Merge pull request #5752 from aws-lumberyard-dev/puvvadar/gitflow_211118_o3de
Merge stabilization/2110
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
+2
-2
@@ -41,8 +41,8 @@ namespace AzManipulatorTestFramework
|
||||
// ViewportInteractionRequestBus overrides ...
|
||||
AzFramework::CameraState GetCameraState() override;
|
||||
AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) override;
|
||||
AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override;
|
||||
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportScreenToWorldRay(
|
||||
AZ::Vector3 ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition) override;
|
||||
AzToolsFramework::ViewportInteraction::ProjectedViewportRay ViewportScreenToWorldRay(
|
||||
const AzFramework::ScreenPoint& screenPosition) override;
|
||||
float DeviceScalingFactor() override;
|
||||
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ namespace AzManipulatorTestFramework
|
||||
|
||||
AzToolsFramework::ViewportInteraction::MousePick mousePick;
|
||||
mousePick.m_screenCoordinates = screenPoint;
|
||||
mousePick.m_rayOrigin = cameraState.m_position;
|
||||
mousePick.m_rayOrigin = nearPlaneWorldPosition;
|
||||
mousePick.m_rayDirection = (nearPlaneWorldPosition - cameraState.m_position).GetNormalized();
|
||||
|
||||
return mousePick;
|
||||
|
||||
@@ -69,8 +69,6 @@ namespace AzManipulatorTestFramework
|
||||
void ImmediateModeActionDispatcher::CameraStateImpl(const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
m_viewportManipulatorInteraction.GetViewportInteraction().SetCameraState(cameraState);
|
||||
GetMouseInteractionEvent()->m_mouseInteraction.m_mousePick.m_rayOrigin = cameraState.m_position;
|
||||
GetMouseInteractionEvent()->m_mouseInteraction.m_mousePick.m_rayDirection = cameraState.m_forward;
|
||||
}
|
||||
|
||||
void ImmediateModeActionDispatcher::MouseLButtonDownImpl()
|
||||
|
||||
+2
-1
@@ -20,7 +20,8 @@ namespace AzManipulatorTestFramework
|
||||
{
|
||||
public:
|
||||
IndirectCallManipulatorManager(ViewportInteractionInterface& viewportInteraction);
|
||||
// ManipulatorManagerInterface ...
|
||||
|
||||
// ManipulatorManagerInterface overrides ...
|
||||
void ConsumeMouseInteractionEvent(const MouseInteractionEvent& event) override;
|
||||
AzToolsFramework::ManipulatorManagerId GetId() const override;
|
||||
bool ManipulatorBeingInteracted() const override;
|
||||
|
||||
@@ -140,13 +140,12 @@ namespace AzManipulatorTestFramework
|
||||
return m_viewportId;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::Vector3> ViewportInteraction::ViewportScreenToWorld(
|
||||
[[maybe_unused]] const AzFramework::ScreenPoint& screenPosition, [[maybe_unused]] float depth)
|
||||
AZ::Vector3 ViewportInteraction::ViewportScreenToWorld([[maybe_unused]] const AzFramework::ScreenPoint& screenPosition)
|
||||
{
|
||||
return {};
|
||||
return AZ::Vector3::CreateZero();
|
||||
}
|
||||
|
||||
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportInteraction::ViewportScreenToWorldRay(
|
||||
AzToolsFramework::ViewportInteraction::ProjectedViewportRay ViewportInteraction::ViewportScreenToWorldRay(
|
||||
[[maybe_unused]] const AzFramework::ScreenPoint& screenPosition)
|
||||
{
|
||||
return {};
|
||||
|
||||
@@ -140,8 +140,8 @@ namespace UnitTest
|
||||
// given a left mouse down ray in world space
|
||||
// consume the mouse move event
|
||||
state.m_actionDispatcher->CameraState(m_cameraState)
|
||||
->MouseLButtonDown()
|
||||
->MousePosition(AzManipulatorTestFramework::GetCameraStateViewportCenter(m_cameraState))
|
||||
->MouseLButtonDown()
|
||||
->ExpectTrue(state.m_linearManipulator->PerformingAction())
|
||||
->ExpectManipulatorBeingInteracted()
|
||||
->MouseLButtonUp()
|
||||
|
||||
@@ -1198,14 +1198,25 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId ToolsApplication::GetCurrentLevelEntityId()
|
||||
{
|
||||
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
AZ::SliceComponent* rootSliceComponent = nullptr;
|
||||
AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceComponent, editorEntityContextId,
|
||||
&AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
|
||||
if (rootSliceComponent && rootSliceComponent->GetMetadataEntity())
|
||||
if (IsPrefabSystemEnabled())
|
||||
{
|
||||
return rootSliceComponent->GetMetadataEntity()->GetId();
|
||||
if (auto prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get())
|
||||
{
|
||||
return prefabPublicInterface->GetLevelInstanceContainerEntityId();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
AZ::SliceComponent* rootSliceComponent = nullptr;
|
||||
AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(
|
||||
rootSliceComponent, editorEntityContextId, &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
|
||||
if (rootSliceComponent && rootSliceComponent->GetMetadataEntity())
|
||||
{
|
||||
return rootSliceComponent->GetMetadataEntity()->GetId();
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::EntityId();
|
||||
|
||||
+5
-1
@@ -448,7 +448,11 @@ namespace AzToolsFramework
|
||||
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder,
|
||||
componentMode.m_componentMode->GetComponentModeName().c_str());
|
||||
componentMode.m_componentMode->GetComponentModeName().c_str(),
|
||||
[]
|
||||
{
|
||||
ComponentModeSystemRequestBus::Broadcast(&ComponentModeSystemRequests::EndComponentMode);
|
||||
});
|
||||
}
|
||||
|
||||
RefreshActions();
|
||||
|
||||
+5
-2
@@ -55,8 +55,11 @@ namespace AzToolsFramework
|
||||
GetEntityComponentIdPair(), elementIdsToDisplay);
|
||||
// create the component mode border with the specific name for this component mode
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder,
|
||||
GetComponentModeName());
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, GetComponentModeName(),
|
||||
[]
|
||||
{
|
||||
ComponentModeSystemRequestBus::Broadcast(&ComponentModeSystemRequests::EndComponentMode);
|
||||
});
|
||||
// set the EntityComponentId for this ComponentMode to active in the ComponentModeViewportUi system
|
||||
ComponentModeViewportUiRequestBus::Event(
|
||||
GetComponentType(), &ComponentModeViewportUiRequestBus::Events::SetViewportUiActiveEntityComponentId,
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace AzToolsFramework::Prefab
|
||||
}
|
||||
|
||||
// Retrieve parent of currently focused prefab.
|
||||
InstanceOptionalReference parentInstance = m_instanceFocusHierarchy[hierarchySize - 2];
|
||||
InstanceOptionalReference parentInstance = GetReferenceFromContainerEntityId(m_instanceFocusHierarchy[hierarchySize - 2]);
|
||||
|
||||
// Use container entity of parent Instance for focus operations.
|
||||
AZ::EntityId entityId = parentInstance->get().GetContainerEntityId();
|
||||
@@ -132,7 +132,7 @@ namespace AzToolsFramework::Prefab
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusHierarchy[index];
|
||||
InstanceOptionalReference focusedInstance = GetReferenceFromContainerEntityId(m_instanceFocusHierarchy[index]);
|
||||
|
||||
return FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId());
|
||||
}
|
||||
@@ -172,7 +172,8 @@ namespace AzToolsFramework::Prefab
|
||||
// Close all container entities in the old path.
|
||||
CloseInstanceContainers(m_instanceFocusHierarchy);
|
||||
|
||||
m_focusedInstance = focusedInstance;
|
||||
// Do not store the container for the root instance, use an invalid EntityId instead.
|
||||
m_focusedInstanceContainerEntityId = focusedInstance->get().GetParentInstance().has_value() ? focusedInstance->get().GetContainerEntityId() : AZ::EntityId();
|
||||
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
|
||||
|
||||
// Focus on the descendants of the container entity in the Editor, if the interface is initialized.
|
||||
@@ -206,56 +207,55 @@ namespace AzToolsFramework::Prefab
|
||||
InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance(
|
||||
[[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
return m_focusedInstance;
|
||||
return GetReferenceFromContainerEntityId(m_focusedInstanceContainerEntityId);
|
||||
}
|
||||
|
||||
AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return AZ::EntityId();
|
||||
}
|
||||
|
||||
return m_focusedInstance->get().GetContainerEntityId();
|
||||
return m_focusedInstanceContainerEntityId;
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entityId.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
if (!instance.has_value())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return instance.has_value() && (&instance->get() == &m_focusedInstance->get());
|
||||
// If this is owned by the root instance, that corresponds to an invalid m_focusedInstanceContainerEntityId.
|
||||
if (!instance->get().GetParentInstance().has_value())
|
||||
{
|
||||
return !m_focusedInstanceContainerEntityId.IsValid();
|
||||
}
|
||||
|
||||
return (instance->get().GetContainerEntityId() == m_focusedInstanceContainerEntityId);
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabInFocusHierarchy(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entityId.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the focus is on the root, m_focusedInstanceContainerEntityId will be the invalid id.
|
||||
// In those case all entities are in the focus hierarchy and should return true.
|
||||
if (!m_focusedInstanceContainerEntityId.IsValid())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
while (instance.has_value())
|
||||
{
|
||||
if (&instance->get() == &m_focusedInstance->get())
|
||||
if (instance->get().GetContainerEntityId() == m_focusedInstanceContainerEntityId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -290,8 +290,9 @@ namespace AzToolsFramework::Prefab
|
||||
// Determine if the entityId is the container for any of the instances in the vector.
|
||||
auto result = AZStd::find_if(
|
||||
m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(),
|
||||
[entityId](const InstanceOptionalReference& instance)
|
||||
[&, entityId](const AZ::EntityId& containerEntityId)
|
||||
{
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
return (instance->get().GetContainerEntityId() == entityId);
|
||||
}
|
||||
);
|
||||
@@ -316,8 +317,9 @@ namespace AzToolsFramework::Prefab
|
||||
// Determine if the templateId matches any of the instances in the vector.
|
||||
auto result = AZStd::find_if(
|
||||
m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(),
|
||||
[templateId](const InstanceOptionalReference& instance)
|
||||
[&, templateId](const AZ::EntityId& containerEntityId)
|
||||
{
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
return (instance->get().GetTemplateId() == templateId);
|
||||
}
|
||||
);
|
||||
@@ -336,10 +338,17 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
AZStd::list<InstanceOptionalReference> instanceFocusList;
|
||||
|
||||
InstanceOptionalReference currentInstance = m_focusedInstance;
|
||||
InstanceOptionalReference currentInstance = GetReferenceFromContainerEntityId(m_focusedInstanceContainerEntityId);
|
||||
while (currentInstance.has_value())
|
||||
{
|
||||
m_instanceFocusHierarchy.emplace_back(currentInstance);
|
||||
if (currentInstance->get().GetParentInstance().has_value())
|
||||
{
|
||||
m_instanceFocusHierarchy.emplace_back(currentInstance->get().GetContainerEntityId());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_instanceFocusHierarchy.emplace_back(AZ::EntityId());
|
||||
}
|
||||
|
||||
currentInstance = currentInstance->get().GetParentInstance();
|
||||
}
|
||||
@@ -357,42 +366,48 @@ namespace AzToolsFramework::Prefab
|
||||
size_t index = 0;
|
||||
size_t maxIndex = m_instanceFocusHierarchy.size() - 1;
|
||||
|
||||
for (const InstanceOptionalReference& instance : m_instanceFocusHierarchy)
|
||||
for (const AZ::EntityId containerEntityId : m_instanceFocusHierarchy)
|
||||
{
|
||||
AZStd::string prefabName;
|
||||
|
||||
if (index < maxIndex)
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
if (instance.has_value())
|
||||
{
|
||||
// Get the filename without the extension (stem).
|
||||
prefabName = instance->get().GetTemplateSourcePath().Stem().Native();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the full filename.
|
||||
prefabName = instance->get().GetTemplateSourcePath().Filename().Native();
|
||||
}
|
||||
AZStd::string prefabName;
|
||||
|
||||
if (prefabSystemComponentInterface->IsTemplateDirty(instance->get().GetTemplateId()))
|
||||
{
|
||||
prefabName += "*";
|
||||
}
|
||||
if (index < maxIndex)
|
||||
{
|
||||
// Get the filename without the extension (stem).
|
||||
prefabName = instance->get().GetTemplateSourcePath().Stem().Native();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the full filename.
|
||||
prefabName = instance->get().GetTemplateSourcePath().Filename().Native();
|
||||
}
|
||||
|
||||
m_instanceFocusPath.Append(prefabName);
|
||||
if (prefabSystemComponentInterface->IsTemplateDirty(instance->get().GetTemplateId()))
|
||||
{
|
||||
prefabName += "*";
|
||||
}
|
||||
|
||||
m_instanceFocusPath.Append(prefabName);
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const
|
||||
void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector<AZ::EntityId>& instances) const
|
||||
{
|
||||
// If this is called outside the Editor, this interface won't be initialized.
|
||||
if (!m_containerEntityInterface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const InstanceOptionalReference& instance : instances)
|
||||
|
||||
for (const AZ::EntityId containerEntityId : instances)
|
||||
{
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
|
||||
if (instance.has_value())
|
||||
{
|
||||
m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), true);
|
||||
@@ -400,7 +415,7 @@ namespace AzToolsFramework::Prefab
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const
|
||||
void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector<AZ::EntityId>& instances) const
|
||||
{
|
||||
// If this is called outside the Editor, this interface won't be initialized.
|
||||
if (!m_containerEntityInterface)
|
||||
@@ -408,8 +423,10 @@ namespace AzToolsFramework::Prefab
|
||||
return;
|
||||
}
|
||||
|
||||
for (const InstanceOptionalReference& instance : instances)
|
||||
for (const AZ::EntityId containerEntityId : instances)
|
||||
{
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
|
||||
if (instance.has_value())
|
||||
{
|
||||
m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), false);
|
||||
@@ -417,4 +434,22 @@ namespace AzToolsFramework::Prefab
|
||||
}
|
||||
}
|
||||
|
||||
InstanceOptionalReference PrefabFocusHandler::GetReferenceFromContainerEntityId(AZ::EntityId containerEntityId) const
|
||||
{
|
||||
if (!containerEntityId.IsValid())
|
||||
{
|
||||
PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
|
||||
AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
|
||||
if (!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
return prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
}
|
||||
|
||||
return m_instanceEntityMapperInterface->FindOwningInstance(containerEntityId);
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
@@ -73,16 +73,19 @@ namespace AzToolsFramework::Prefab
|
||||
void RefreshInstanceFocusList();
|
||||
void RefreshInstanceFocusPath();
|
||||
|
||||
void OpenInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const;
|
||||
void CloseInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const;
|
||||
void OpenInstanceContainers(const AZStd::vector<AZ::EntityId>& instances) const;
|
||||
void CloseInstanceContainers(const AZStd::vector<AZ::EntityId>& instances) const;
|
||||
|
||||
//! The instance the editor is currently focusing on.
|
||||
InstanceOptionalReference m_focusedInstance;
|
||||
InstanceOptionalReference GetReferenceFromContainerEntityId(AZ::EntityId containerEntityId) const;
|
||||
|
||||
//! The EntityId of the prefab container entity for the instance the editor is currently focusing on.
|
||||
AZ::EntityId m_focusedInstanceContainerEntityId = AZ::EntityId();
|
||||
//! The templateId of the focused instance.
|
||||
TemplateId m_focusedTemplateId;
|
||||
//! The list of instances going from the root (index 0) to the focused instance.
|
||||
AZStd::vector<InstanceOptionalReference> m_instanceFocusHierarchy;
|
||||
//! A path containing the names of the containers in the instance focus hierarchy, separated with a /.
|
||||
//! The list of instances going from the root (index 0) to the focused instance,
|
||||
//! referenced by their prefab container's EntityId.
|
||||
AZStd::vector<AZ::EntityId> m_instanceFocusHierarchy;
|
||||
//! A path containing the filenames of the instances in the focus hierarchy, separated with a /.
|
||||
AZ::IO::Path m_instanceFocusPath;
|
||||
|
||||
ContainerEntityInterface* m_containerEntityInterface = nullptr;
|
||||
|
||||
+28
-5
@@ -527,8 +527,8 @@ namespace AzToolsFramework
|
||||
m_errorButton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::UpdateErrorButton(const AZStd::string& errorLog)
|
||||
|
||||
void PropertyAssetCtrl::UpdateErrorButton()
|
||||
{
|
||||
if (m_errorButton)
|
||||
{
|
||||
@@ -543,12 +543,17 @@ namespace AzToolsFramework
|
||||
m_errorButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
m_errorButton->setFixedSize(QSize(16, 16));
|
||||
m_errorButton->setMouseTracking(true);
|
||||
m_errorButton->setIcon(QIcon("Icons/PropertyEditor/error_icon.png"));
|
||||
m_errorButton->setIcon(QIcon(":/PropertyEditor/Resources/error_icon.png"));
|
||||
m_errorButton->setToolTip("Show Errors");
|
||||
|
||||
// Insert the error button after the asset label
|
||||
qobject_cast<QHBoxLayout*>(layout())->insertWidget(1, m_errorButton);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::UpdateErrorButtonWithLog(const AZStd::string& errorLog)
|
||||
{
|
||||
UpdateErrorButton();
|
||||
|
||||
// Connect pressed to opening the error dialog
|
||||
// Must capture this for call to QObject::connect
|
||||
@@ -587,6 +592,21 @@ namespace AzToolsFramework
|
||||
logDialog->show();
|
||||
});
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::UpdateErrorButtonWithMessage(const AZStd::string& message)
|
||||
{
|
||||
UpdateErrorButton();
|
||||
|
||||
connect(m_errorButton, &QPushButton::clicked, this, [this, message]() {
|
||||
QMessageBox::critical(nullptr, "Error", message.c_str());
|
||||
|
||||
// Without this, the error button would maintain focus after clicking, which left the red error icon in a blue-highlighted state
|
||||
if (parentWidget())
|
||||
{
|
||||
parentWidget()->setFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::ClearAssetInternal()
|
||||
{
|
||||
@@ -960,7 +980,6 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
const AZ::Data::AssetId assetID = GetCurrentAssetID();
|
||||
m_currentAssetHint = "";
|
||||
|
||||
AZ::Outcome<AssetSystem::JobInfoContainer> jobOutcome = AZ::Failure();
|
||||
AssetSystemJobRequestBus::BroadcastResult(jobOutcome, &AssetSystemJobRequestBus::Events::GetAssetJobsInfoByAssetID, assetID, false, false);
|
||||
@@ -1018,7 +1037,7 @@ namespace AzToolsFramework
|
||||
// In case of failure, render failure icon
|
||||
case AssetSystem::JobStatus::Failed:
|
||||
{
|
||||
UpdateErrorButton(errorLog);
|
||||
UpdateErrorButtonWithLog(errorLog);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1043,6 +1062,10 @@ namespace AzToolsFramework
|
||||
m_currentAssetHint = assetPath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateErrorButtonWithMessage(AZStd::string::format("Asset is missing.\n\nID: %s\nHint:%s", assetID.ToString<AZStd::string>().c_str(), GetCurrentAssetHint().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the asset file name
|
||||
|
||||
+3
-1
@@ -168,7 +168,9 @@ namespace AzToolsFramework
|
||||
|
||||
bool IsCorrectMimeData(const QMimeData* pData, AZ::Data::AssetId* pAssetId = nullptr, AZ::Data::AssetType* pAssetType = nullptr) const;
|
||||
void ClearErrorButton();
|
||||
void UpdateErrorButton(const AZStd::string& errorLog);
|
||||
void UpdateErrorButton();
|
||||
void UpdateErrorButtonWithLog(const AZStd::string& errorLog);
|
||||
void UpdateErrorButtonWithMessage(const AZStd::string& message);
|
||||
virtual const AZStd::string GetFolderSelection() const { return AZStd::string(); }
|
||||
virtual void SetFolderSelection(const AZStd::string& /* folderPath */) {}
|
||||
virtual void ClearAssetInternal();
|
||||
|
||||
+4
-1
@@ -158,7 +158,10 @@ namespace UnitTest
|
||||
{
|
||||
// Create & Start a new ToolsApplication if there's no existing one
|
||||
m_app = CreateTestApplication();
|
||||
m_app->Start(AzFramework::Application::Descriptor());
|
||||
AZ::ComponentApplication::StartupParameters startupParameters;
|
||||
startupParameters.m_loadAssetCatalog = false;
|
||||
|
||||
m_app->Start(AzFramework::Application::Descriptor(), startupParameters);
|
||||
}
|
||||
|
||||
// without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzFramework/Render/IntersectorInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -62,4 +63,33 @@ namespace AzToolsFramework
|
||||
|
||||
return circleBoundWidth;
|
||||
}
|
||||
|
||||
AZ::Vector3 FindClosestPickIntersection(
|
||||
AzFramework::ViewportId viewportId, const AzFramework::ScreenPoint& screenPoint, const float rayLength, const float defaultDistance)
|
||||
{
|
||||
using AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus;
|
||||
AzToolsFramework::ViewportInteraction::ProjectedViewportRay viewportRay{};
|
||||
ViewportInteractionRequestBus::EventResult(
|
||||
viewportRay, viewportId, &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPoint);
|
||||
|
||||
AzFramework::RenderGeometry::RayRequest ray;
|
||||
ray.m_startWorldPosition = viewportRay.origin;
|
||||
ray.m_endWorldPosition = viewportRay.origin + viewportRay.direction * rayLength;
|
||||
ray.m_onlyVisible = true;
|
||||
|
||||
AzFramework::RenderGeometry::RayResult renderGeometryIntersectionResult;
|
||||
AzFramework::RenderGeometry::IntersectorBus::EventResult(
|
||||
renderGeometryIntersectionResult, AzToolsFramework::GetEntityContextId(),
|
||||
&AzFramework::RenderGeometry::IntersectorBus::Events::RayIntersect, ray);
|
||||
|
||||
// attempt a ray intersection with any visible mesh and return the intersection position if successful
|
||||
if (renderGeometryIntersectionResult)
|
||||
{
|
||||
return renderGeometryIntersectionResult.m_worldPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
return viewportRay.origin + viewportRay.direction * defaultDistance;
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -162,12 +162,11 @@ namespace AzToolsFramework
|
||||
//! Multiply by DeviceScalingFactor to get the position in viewport pixel space.
|
||||
virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0;
|
||||
//! Transforms a point from Qt widget screen space to world space based on the given clip space depth.
|
||||
//! Depth specifies a relative camera depth to project in the range of [0.f, 1.f].
|
||||
//! Returns the world space position if successful.
|
||||
virtual AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0;
|
||||
virtual AZ::Vector3 ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition) = 0;
|
||||
//! Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane.
|
||||
//! Returns a ray containing the ray's origin and a direction normal, if successful.
|
||||
virtual AZStd::optional<ProjectedViewportRay> ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0;
|
||||
virtual ProjectedViewportRay ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0;
|
||||
//! Gets the DPI scaling factor that translates Qt widget space into viewport pixel space.
|
||||
virtual float DeviceScalingFactor() = 0;
|
||||
|
||||
@@ -229,9 +228,6 @@ namespace AzToolsFramework
|
||||
class MainEditorViewportInteractionRequests
|
||||
{
|
||||
public:
|
||||
//! Given a point in screen space, return the picked entity (if any).
|
||||
//! Picked EntityId will be returned, InvalidEntityId will be returned on failure.
|
||||
virtual AZ::EntityId PickEntity(const AzFramework::ScreenPoint& point) = 0;
|
||||
//! Given a point in screen space, return the terrain position in world space.
|
||||
virtual AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) = 0;
|
||||
//! Return the terrain height given a world position in 2d (xy plane).
|
||||
@@ -266,7 +262,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
|
||||
//! Returns the current state of the keyboard modifier keys.
|
||||
virtual KeyboardModifiers QueryKeyboardModifiers() = 0;
|
||||
@@ -290,7 +285,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
|
||||
//! Returns the current time in seconds.
|
||||
//! This interface can be overridden for the purposes of testing to simplify viewport input requests.
|
||||
@@ -340,6 +334,12 @@ namespace AzToolsFramework
|
||||
return entityContextId;
|
||||
}
|
||||
|
||||
//! Performs an intersection test against meshes in the scene, if there is a hit (the ray intersects
|
||||
//! a mesh), that position is returned, otherwise a point projected defaultDistance from the
|
||||
//! origin of the ray will be returned.
|
||||
AZ::Vector3 FindClosestPickIntersection(
|
||||
AzFramework::ViewportId viewportId, const AzFramework::ScreenPoint& screenPoint, float rayLength, float defaultDistance);
|
||||
|
||||
//! Maps a mouse interaction event to a ClickDetector event.
|
||||
//! @note Function only cares about up or down events, all other events are mapped to Nil (ignored).
|
||||
AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
|
||||
|
||||
+5
-3
@@ -148,7 +148,6 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
EditorHelpers::EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache)
|
||||
: m_entityDataCache(entityDataCache)
|
||||
{
|
||||
@@ -190,7 +189,10 @@ namespace AzToolsFramework
|
||||
if (helpersVisible)
|
||||
{
|
||||
// some components choose to hide their icons (e.g. meshes)
|
||||
if (!m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex))
|
||||
// we also do not want to test against icons that may not be showing as they're inside a 'closed' entity container
|
||||
// (these icons only become visible when it is opened for editing)
|
||||
if (!m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) &&
|
||||
m_entityDataCache->IsVisibleEntityIndividuallySelectableInViewport(entityCacheIndex))
|
||||
{
|
||||
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
|
||||
|
||||
@@ -235,7 +237,7 @@ namespace AzToolsFramework
|
||||
viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::SetOverrideCursor,
|
||||
ViewportInteraction::CursorStyleOverride::Forbidden);
|
||||
}
|
||||
|
||||
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down ||
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick)
|
||||
|
||||
-3
@@ -18,9 +18,6 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
// default ray length for picking in the viewport
|
||||
static const float EditorPickRayLength = 1000.0f;
|
||||
|
||||
AZ::Vector3 CalculateCenterOffset(const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot)
|
||||
{
|
||||
if (Centered(pivot))
|
||||
|
||||
+3
@@ -26,6 +26,9 @@ namespace AzFramework
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! Default ray length for picking in the viewport.
|
||||
inline constexpr float EditorPickRayLength = 1000.0f;
|
||||
|
||||
//! Is the pivot at the center of the object (middle of extents) or at the
|
||||
//! exported authored object root position.
|
||||
inline bool Centered(const EditorTransformComponentSelectionRequests::Pivot pivot)
|
||||
|
||||
+17
-4
@@ -27,6 +27,7 @@
|
||||
#include <AzToolsFramework/Manipulators/ScaleManipulators.h>
|
||||
#include <AzToolsFramework/Manipulators/TranslationManipulators.h>
|
||||
#include <AzToolsFramework/Maths/TransformUtils.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
|
||||
@@ -409,7 +410,7 @@ namespace AzToolsFramework
|
||||
const AzFramework::CameraState cameraState = GetCameraState(viewportId);
|
||||
for (size_t entityCacheIndex = 0; entityCacheIndex < entityDataCache.VisibleEntityDataCount(); ++entityCacheIndex)
|
||||
{
|
||||
if (!entityDataCache.IsVisibleEntitySelectableInViewport(entityCacheIndex))
|
||||
if (!entityDataCache.IsVisibleEntityIndividuallySelectableInViewport(entityCacheIndex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -983,7 +984,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (auto entityIndex = entityDataCache.GetVisibleEntityIndexFromId(entityId))
|
||||
{
|
||||
if (entityDataCache.IsVisibleEntitySelectableInViewport(*entityIndex))
|
||||
if (entityDataCache.IsVisibleEntityIndividuallySelectableInViewport(*entityIndex))
|
||||
{
|
||||
return *entityIndex;
|
||||
}
|
||||
@@ -1014,6 +1015,15 @@ namespace AzToolsFramework
|
||||
ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
|
||||
}
|
||||
|
||||
// leaves focus mode by focusing on the parent of the current perfab in the entity outliner
|
||||
static void LeaveFocusMode()
|
||||
{
|
||||
if (auto prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get())
|
||||
{
|
||||
prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(GetEntityContextId());
|
||||
}
|
||||
}
|
||||
|
||||
EditorTransformComponentSelection::EditorTransformComponentSelection(const EditorVisibleEntityDataCache* entityDataCache)
|
||||
: m_entityDataCache(entityDataCache)
|
||||
{
|
||||
@@ -3674,7 +3684,8 @@ namespace AzToolsFramework
|
||||
case ViewportEditorMode::Focus:
|
||||
{
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode");
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode",
|
||||
LeaveFocusMode);
|
||||
}
|
||||
break;
|
||||
case ViewportEditorMode::Default:
|
||||
@@ -3703,12 +3714,14 @@ namespace AzToolsFramework
|
||||
if (editorModeState.IsModeActive(ViewportEditorMode::Focus))
|
||||
{
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode");
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode",
|
||||
LeaveFocusMode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewportEditorMode::Focus:
|
||||
{
|
||||
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RemoveViewportBorder);
|
||||
}
|
||||
|
||||
+3
-5
@@ -293,12 +293,10 @@ namespace AzToolsFramework
|
||||
return m_impl->m_visibleEntityDatas[index].m_iconHidden;
|
||||
}
|
||||
|
||||
bool EditorVisibleEntityDataCache::IsVisibleEntitySelectableInViewport(size_t index) const
|
||||
bool EditorVisibleEntityDataCache::IsVisibleEntityIndividuallySelectableInViewport(const size_t index) const
|
||||
{
|
||||
return m_impl->m_visibleEntityDatas[index].m_visible
|
||||
&& !m_impl->m_visibleEntityDatas[index].m_locked
|
||||
&& m_impl->m_visibleEntityDatas[index].m_inFocus
|
||||
&& !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer;
|
||||
return m_impl->m_visibleEntityDatas[index].m_visible && !m_impl->m_visibleEntityDatas[index].m_locked &&
|
||||
m_impl->m_visibleEntityDatas[index].m_inFocus && !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer;
|
||||
}
|
||||
|
||||
AZStd::optional<size_t> EditorVisibleEntityDataCache::GetVisibleEntityIndexFromId(const AZ::EntityId entityId) const
|
||||
|
||||
+4
-1
@@ -55,7 +55,10 @@ namespace AzToolsFramework
|
||||
bool IsVisibleEntityVisible(size_t index) const;
|
||||
bool IsVisibleEntitySelected(size_t index) const;
|
||||
bool IsVisibleEntityIconHidden(size_t index) const;
|
||||
bool IsVisibleEntitySelectableInViewport(size_t index) const;
|
||||
//! Returns true if the entity is individually selectable (none of its ancestors are a closed container entity).
|
||||
//! @note It may still be desirable to be able to 'click' an entity that is a descendant of a closed container
|
||||
//! to select the container itself, not the individual entity.
|
||||
bool IsVisibleEntityIndividuallySelectableInViewport(size_t index) const;
|
||||
|
||||
AZStd::optional<size_t> GetVisibleEntityIndexFromId(AZ::EntityId entityId) const;
|
||||
|
||||
|
||||
@@ -62,9 +62,6 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
return;
|
||||
}
|
||||
|
||||
// set hover to true by default
|
||||
action->setProperty("IconHasHoverEffect", true);
|
||||
|
||||
// add the action
|
||||
addAction(action);
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
namespace AzToolsFramework::ViewportUi::Internal
|
||||
{
|
||||
const static int HighlightBorderSize = 5;
|
||||
const static char* HighlightBorderColor = "#4A90E2";
|
||||
const static char* const HighlightBorderColor = "#4A90E2";
|
||||
const static int HighlightBorderBackButtonIconSize = 20;
|
||||
const static char* const HighlightBorderBackButtonIconFile = "X_axis.svg";
|
||||
|
||||
static void UnparentWidgets(ViewportUiElementIdInfoLookup& viewportUiElementIdInfoLookup)
|
||||
{
|
||||
@@ -62,6 +64,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
, m_fullScreenLayout(&m_uiOverlay)
|
||||
, m_uiOverlayLayout()
|
||||
, m_viewportBorderText(&m_uiOverlay)
|
||||
, m_viewportBorderBackButton(&m_uiOverlay)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -254,7 +257,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
auto viewportUiMapElement = m_viewportUiElements.find(elementId);
|
||||
if (viewportUiMapElement != m_viewportUiElements.end())
|
||||
{
|
||||
viewportUiMapElement->second.m_widget->setVisible(false);
|
||||
viewportUiMapElement->second.m_widget->hide();
|
||||
viewportUiMapElement->second.m_widget->setParent(nullptr);
|
||||
m_viewportUiElements.erase(viewportUiMapElement);
|
||||
}
|
||||
@@ -269,7 +272,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
{
|
||||
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId); element.m_widget)
|
||||
{
|
||||
element.m_widget->setVisible(true);
|
||||
element.m_widget->show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +280,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
{
|
||||
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId); element.m_widget)
|
||||
{
|
||||
element.m_widget->setVisible(false);
|
||||
element.m_widget->hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,27 +294,34 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
return false;
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::CreateViewportBorder(const AZStd::string& borderTitle)
|
||||
void ViewportUiDisplay::CreateViewportBorder(
|
||||
const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback)
|
||||
{
|
||||
const AZStd::string styleSheet = AZStd::string::format(
|
||||
"border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, ViewportUiTopBorderSize,
|
||||
HighlightBorderColor);
|
||||
m_uiOverlay.setStyleSheet(styleSheet.c_str());
|
||||
m_uiOverlay.setStyleSheet(QString("border: %1px solid %2; border-top: %3px solid %4;")
|
||||
.arg(
|
||||
QString::number(HighlightBorderSize), HighlightBorderColor,
|
||||
QString::number(ViewportUiTopBorderSize), HighlightBorderColor));
|
||||
m_uiOverlayLayout.setContentsMargins(
|
||||
HighlightBorderSize + ViewportUiOverlayMargin, ViewportUiTopBorderSize + ViewportUiOverlayMargin,
|
||||
HighlightBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin);
|
||||
m_viewportBorderText.setVisible(true);
|
||||
m_viewportBorderText.show();
|
||||
m_viewportBorderText.setText(borderTitle.c_str());
|
||||
UpdateUiOverlayGeometry();
|
||||
|
||||
// only display the back button if a callback was provided
|
||||
m_viewportBorderBackButtonCallback = backButtonCallback;
|
||||
m_viewportBorderBackButton.setVisible(m_viewportBorderBackButtonCallback.has_value());
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::RemoveViewportBorder()
|
||||
{
|
||||
m_viewportBorderText.setVisible(false);
|
||||
m_viewportBorderText.hide();
|
||||
m_uiOverlay.setStyleSheet("border: none;");
|
||||
m_uiOverlayLayout.setContentsMargins(
|
||||
ViewportUiOverlayMargin, ViewportUiOverlayMargin + ViewportUiOverlayTopMarginPadding, ViewportUiOverlayMargin,
|
||||
ViewportUiOverlayMargin);
|
||||
m_viewportBorderBackButtonCallback.reset();
|
||||
m_viewportBorderBackButton.hide();
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::PositionViewportUiElementFromWorldSpace(ViewportUiElementId elementId, const AZ::Vector3& pos)
|
||||
@@ -350,23 +360,46 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
{
|
||||
m_uiMainWindow.setObjectName(QString("ViewportUiWindow"));
|
||||
ConfigureWindowForViewportUi(&m_uiMainWindow);
|
||||
m_uiMainWindow.setVisible(false);
|
||||
m_uiMainWindow.hide();
|
||||
|
||||
m_uiOverlay.setObjectName(QString("ViewportUiOverlay"));
|
||||
m_uiMainWindow.setCentralWidget(&m_uiOverlay);
|
||||
m_uiOverlay.setVisible(false);
|
||||
m_uiOverlay.hide();
|
||||
|
||||
// remove any spacing and margins from the UI Overlay Layout
|
||||
m_fullScreenLayout.setSpacing(0);
|
||||
m_fullScreenLayout.setContentsMargins(0, 0, 0, 0);
|
||||
m_fullScreenLayout.addLayout(&m_uiOverlayLayout, 0, 0, 1, 1);
|
||||
|
||||
// format the label which will appear on top of the highlight border
|
||||
AZStd::string styleSheet = AZStd::string::format("background-color: %s; border: none;", HighlightBorderColor);
|
||||
m_viewportBorderText.setStyleSheet(styleSheet.c_str());
|
||||
// style the label which will appear on top of the highlight border
|
||||
m_viewportBorderText.setStyleSheet(QString("background-color: %1; border: none").arg(HighlightBorderColor));
|
||||
m_viewportBorderText.setFixedHeight(ViewportUiTopBorderSize);
|
||||
m_viewportBorderText.setVisible(false);
|
||||
m_viewportBorderText.hide();
|
||||
m_fullScreenLayout.addWidget(&m_viewportBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter);
|
||||
|
||||
m_viewportBorderBackButton.setAutoRaise(true); // hover highlight
|
||||
m_viewportBorderBackButton.hide();
|
||||
|
||||
QIcon backButtonIcon(QString(":/stylesheet/img/UI20/toolbar/%1").arg(HighlightBorderBackButtonIconFile));
|
||||
m_viewportBorderBackButton.setIcon(backButtonIcon);
|
||||
m_viewportBorderBackButton.setIconSize(QSize(HighlightBorderBackButtonIconSize, HighlightBorderBackButtonIconSize));
|
||||
|
||||
// setup the handler for the back button to call the user provided callback (if any)
|
||||
QObject::connect(
|
||||
&m_viewportBorderBackButton, &QToolButton::clicked,
|
||||
[this]
|
||||
{
|
||||
if (m_viewportBorderBackButtonCallback.has_value())
|
||||
{
|
||||
// we need to swap out the existing back button callback because it will be reset in RemoveViewportBorder()
|
||||
// so preserve the lifetime with this temporary callback until after the call to RemoveViewportBorder()
|
||||
AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback;
|
||||
m_viewportBorderBackButtonCallback.swap(backButtonCallback);
|
||||
RemoveViewportBorder();
|
||||
(*backButtonCallback)();
|
||||
}
|
||||
});
|
||||
m_fullScreenLayout.addWidget(&m_viewportBorderBackButton, 0, 0, Qt::AlignTop | Qt::AlignRight);
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::PrepareWidgetForViewportUi(QPointer<QWidget> widget)
|
||||
@@ -414,16 +447,9 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
region += m_uiOverlay.childrenRegion();
|
||||
|
||||
// set viewport ui visibility depending on if elements are present
|
||||
if (region.isEmpty() || !UiDisplayEnabled())
|
||||
{
|
||||
m_uiMainWindow.setVisible(false);
|
||||
m_uiOverlay.setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_uiMainWindow.setVisible(true);
|
||||
m_uiOverlay.setVisible(true);
|
||||
}
|
||||
const bool visible = !region.isEmpty() && UiDisplayEnabled();
|
||||
m_uiMainWindow.setVisible(visible);
|
||||
m_uiOverlay.setVisible(visible);
|
||||
|
||||
m_uiMainWindow.setMask(region);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
#include <QPointer>
|
||||
#include <QToolButton>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
|
||||
#include <QGridLayout>
|
||||
@@ -89,7 +90,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
AZStd::shared_ptr<QWidget> GetViewportUiElement(ViewportUiElementId elementId);
|
||||
bool IsViewportUiElementVisible(ViewportUiElementId elementId);
|
||||
|
||||
void CreateViewportBorder(const AZStd::string& borderTitle);
|
||||
void CreateViewportBorder(const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback);
|
||||
void RemoveViewportBorder();
|
||||
|
||||
private:
|
||||
@@ -113,7 +114,10 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
QWidget m_uiOverlay; //!< The UI Overlay which displays Viewport UI Elements.
|
||||
QGridLayout m_fullScreenLayout; //!< The layout which extends across the full screen.
|
||||
ViewportUiDisplayLayout m_uiOverlayLayout; //!< The layout used for optionally anchoring Viewport UI Elements.
|
||||
QLabel m_viewportBorderText; //!< The text used for the viewport border.
|
||||
QLabel m_viewportBorderText; //!< The text used for the viewport highlight border.
|
||||
QToolButton m_viewportBorderBackButton; //!< The button to return from the viewport highlight border (only displayed if callback provided).
|
||||
//! The optional callback for when the viewport highlight border back button is pressed.
|
||||
AZStd::optional<ViewportUiBackButtonCallback> m_viewportBorderBackButtonCallback;
|
||||
|
||||
QWidget* m_renderOverlay;
|
||||
QPointer<QWidget> m_fullScreenWidget; //!< Reference to the widget attached to m_fullScreenLayout if any.
|
||||
|
||||
@@ -240,9 +240,10 @@ namespace AzToolsFramework::ViewportUi
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportUiManager::CreateViewportBorder(const AZStd::string& borderTitle)
|
||||
void ViewportUiManager::CreateViewportBorder(
|
||||
const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback)
|
||||
{
|
||||
m_viewportUi->CreateViewportBorder(borderTitle);
|
||||
m_viewportUi->CreateViewportBorder(borderTitle, backButtonCallback);
|
||||
}
|
||||
|
||||
void ViewportUiManager::RemoveViewportBorder()
|
||||
|
||||
@@ -50,7 +50,8 @@ namespace AzToolsFramework::ViewportUi
|
||||
void RegisterTextFieldCallback(TextFieldId textFieldId, AZ::Event<AZStd::string>::Handler& handler) override;
|
||||
void RemoveTextField(TextFieldId textFieldId) override;
|
||||
void SetTextFieldVisible(TextFieldId textFieldId, bool visible) override;
|
||||
void CreateViewportBorder(const AZStd::string& borderTitle) override;
|
||||
void CreateViewportBorder(
|
||||
const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback) override;
|
||||
void RemoveViewportBorder() override;
|
||||
void PressButton(ClusterId clusterId, ButtonId buttonId) override;
|
||||
void PressButton(SwitcherId switcherId, ButtonId buttonId) override;
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace AzToolsFramework::ViewportUi
|
||||
using SwitcherId = IdType<struct SwitcherIdType>;
|
||||
using TextFieldId = IdType<struct TextFieldIdType>;
|
||||
|
||||
//! Callback function for viewport UI back button.
|
||||
using ViewportUiBackButtonCallback = AZStd::function<void()>;
|
||||
|
||||
inline const ViewportUiElementId InvalidViewportUiElementId = ViewportUiElementId(0);
|
||||
inline const ButtonId InvalidButtonId = ButtonId(0);
|
||||
inline const ClusterId InvalidClusterId = ClusterId(0);
|
||||
@@ -95,9 +98,9 @@ namespace AzToolsFramework::ViewportUi
|
||||
virtual void RemoveTextField(TextFieldId textFieldId) = 0;
|
||||
//! Sets the visibility of the text field.
|
||||
virtual void SetTextFieldVisible(TextFieldId textFieldId, bool visible) = 0;
|
||||
//! Create the highlight border for Component Mode.
|
||||
virtual void CreateViewportBorder(const AZStd::string& borderTitle) = 0;
|
||||
//! Remove the highlight border for Component Mode.
|
||||
//! Create the highlight border with optional back button to exit the given editor mode.
|
||||
virtual void CreateViewportBorder(const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback) = 0;
|
||||
//! Remove the highlight border.
|
||||
virtual void RemoveViewportBorder() = 0;
|
||||
//! Invoke a button press on a cluster.
|
||||
virtual void PressButton(ClusterId clusterId, ButtonId buttonId) = 0;
|
||||
|
||||
@@ -22,8 +22,6 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
|
||||
// Add am empty active button (is set in the call to SetActiveMode)
|
||||
m_activeButton = new QToolButton();
|
||||
// No hover effect for the main button as it's not clickable
|
||||
m_activeButton->setProperty("IconHasHoverEffect", false);
|
||||
m_activeButton->setCheckable(false);
|
||||
m_activeButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
addWidget(m_activeButton);
|
||||
@@ -56,9 +54,6 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
return;
|
||||
}
|
||||
|
||||
// set hover to true by default
|
||||
action->setProperty("IconHasHoverEffect", true);
|
||||
|
||||
// add the action
|
||||
addAction(action);
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ namespace UnitTest
|
||||
{
|
||||
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
|
||||
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
// default local bounds to unit cube
|
||||
m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Deactivate()
|
||||
@@ -57,7 +60,6 @@ namespace UnitTest
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetLocalBounds()
|
||||
{
|
||||
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
|
||||
return m_localBounds;
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -41,5 +41,7 @@ namespace UnitTest
|
||||
// BoundsRequestBus overrides ...
|
||||
AZ::Aabb GetWorldBounds() override;
|
||||
AZ::Aabb GetLocalBounds() override;
|
||||
|
||||
AZ::Aabb m_localBounds; //!< Local bounds that can be modified for certain tests (defaults to unit cube).
|
||||
};
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
|
||||
|
||||
#include<Tests/BoundsTestComponent.h>
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -493,12 +493,8 @@ namespace UnitTest
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Then
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
|
||||
AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
|
||||
|
||||
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
|
||||
const AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
@@ -527,12 +523,8 @@ namespace UnitTest
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Then
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
|
||||
AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId1, entity2, entity3, entity4 };
|
||||
|
||||
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
|
||||
const AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId1, entity2, entity3, entity4 };
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
@@ -946,6 +938,42 @@ namespace UnitTest
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, BoundsBetweenCameraAndNearClipPlaneDoesNotIntersectMouseRay)
|
||||
{
|
||||
// move camera to 10 units along the y-axis
|
||||
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
|
||||
// send a very narrow bounds for entity1
|
||||
AZ::Entity* entity1 = AzToolsFramework::GetEntityById(m_entityId1);
|
||||
auto* boundTestComponent = entity1->FindComponent<BoundsTestComponent>();
|
||||
boundTestComponent->m_localBounds =
|
||||
AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f, -0.0025f, -0.5f), AZ::Vector3(0.5f, 0.0025f, 0.5f));
|
||||
|
||||
// move entity1 in front of the camera between it and the near clip plane
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.05f)));
|
||||
// move entity2 behind entity1
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(15.0f)));
|
||||
|
||||
const auto entity2ScreenPosition = AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(m_entityId2), m_cameraState);
|
||||
|
||||
// click the entity in the viewport
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
->CameraState(m_cameraState)
|
||||
->MousePosition(entity2ScreenPosition)
|
||||
->CameraState(m_cameraState)
|
||||
->MouseLButtonDown()
|
||||
->MouseLButtonUp();
|
||||
|
||||
// ensure entity1 is not selected as it is before the near clip plane
|
||||
using ::testing::UnorderedElementsAreArray;
|
||||
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
|
||||
const AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId2 };
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
}
|
||||
|
||||
class EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam
|
||||
: public EditorTransformComponentSelectionViewportPickingManipulatorTestFixture
|
||||
, public ::testing::WithParamInterface<bool>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <Tests/Utils/Printers.h>
|
||||
|
||||
using namespace AzToolsFramework;
|
||||
|
||||
|
||||
@@ -106,7 +106,9 @@ namespace UnitTest
|
||||
inline static const char* Passenger2EntityName = "Passenger2";
|
||||
};
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootContainer)
|
||||
// Test was disabled because the implementation of GetFocusedPrefabInstance now relies on the Prefab EOS,
|
||||
// which is not used by our test environment. This can be restored once Instance handles are implemented.
|
||||
TEST_F(PrefabFocusTests, DISABLED_PrefabFocus_FocusOnOwningPrefab_RootContainer)
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing the container entity of the root prefab.
|
||||
{
|
||||
@@ -121,7 +123,9 @@ namespace UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootEntity)
|
||||
// Test was disabled because the implementation of GetFocusedPrefabInstance now relies on the Prefab EOS,
|
||||
// which is not used by our test environment. This can be restored once Instance handles are implemented.
|
||||
TEST_F(PrefabFocusTests, DISABLED_PrefabFocus_FocusOnOwningPrefab_RootEntity)
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab.
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportTypes.h>
|
||||
#include <Tests/Utils/Printers.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
@@ -35,6 +36,7 @@ namespace UnitTest
|
||||
const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState);
|
||||
return AzFramework::WorldToScreen(worldResult, cameraState);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ScreenPoint tests
|
||||
TEST(ViewportScreen, WorldToScreenAndScreenToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
|
||||
@@ -102,8 +104,8 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NDC tests
|
||||
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
|
||||
// Ndc tests
|
||||
TEST(ViewportScreen, WorldToScreenNdcAndScreenNdcToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
|
||||
{
|
||||
using NdcPoint = AZ::Vector2;
|
||||
|
||||
@@ -136,7 +138,7 @@ namespace UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueOrientatedCamera)
|
||||
TEST(ViewportScreen, WorldToScreenNdcAndScreenNdcToWorldReturnsTheSameValueOrientatedCamera)
|
||||
{
|
||||
using NdcPoint = AZ::Vector2;
|
||||
|
||||
@@ -153,7 +155,7 @@ namespace UnitTest
|
||||
|
||||
// note: nearClip is 0.1 - the world space value returned will be aligned to the near clip
|
||||
// plane of the camera so use that to confirm the mapping to/from is correct
|
||||
TEST(ViewportScreen, ScreenNDCToWorldReturnsPositionOnNearClipPlaneInWorldSpace)
|
||||
TEST(ViewportScreen, ScreenNdcToWorldReturnsPositionOnNearClipPlaneInWorldSpace)
|
||||
{
|
||||
using NdcPoint = AZ::Vector2;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user