Merge branch 'development' of https://github.com/o3de/o3de into Network/olexl/nettransform_local_for_children_cr

This commit is contained in:
AMZN-Olex
2021-09-29 08:31:30 -04:00
190 changed files with 6334 additions and 2928 deletions
@@ -141,6 +141,10 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
connect(
this, &AzAssetBrowserWindow::SizeChangedSignal, m_ui->m_assetBrowserTableViewWidget,
&AzAssetBrowser::AssetBrowserTableView::UpdateSizeSlot);
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main");
}
@@ -164,6 +168,25 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa
return listener;
}
void AzAssetBrowserWindow::resizeEvent(QResizeEvent* resizeEvent)
{
// leftLayout is the parent of the tableView
// rightLayout is the parent of the preview window.
// Workaround: When docking windows this event keeps holding the old size of the widgets instead of the new one
// but the resizeEvent holds the new size of the whole widget
// So we have to save the proportions somehow
const QWidget* leftLayout = m_ui->m_leftLayout;
const QVBoxLayout* rightLayout = m_ui->m_rightLayout;
const float oldLeftLayoutWidth = aznumeric_cast<float>(leftLayout->geometry().width());
const float oldWidth = aznumeric_cast<float>(leftLayout->geometry().width() + rightLayout->geometry().width());
const float newWidth = oldLeftLayoutWidth * aznumeric_cast<float>(resizeEvent->size().width()) / oldWidth;
emit SizeChangedSignal(aznumeric_cast<int>(newWidth));
QWidget::resizeEvent(resizeEvent);
}
void AzAssetBrowserWindow::OnInitViewToggleButton()
{
CreateSwitchViewMenu();
@@ -53,9 +53,17 @@ public:
static QObject* createListenerForShowAssetEditorEvent(QObject* parent);
Q_SIGNALS:
void SizeChangedSignal(int newWidth);
protected:
void resizeEvent(QResizeEvent* resizeEvent) override;
private:
void OnInitViewToggleButton();
void UpdateDisplayInfo();
protected slots:
void CreateSwitchViewMenu();
void SetExpandedAssetBrowserMode();
@@ -9,7 +9,7 @@
#include "QtEditorApplication.h"
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/API/ApplicationAPI_Linux.h>
#include <AzFramework/XcbEventHandler.h>
#endif
namespace Editor
@@ -19,7 +19,7 @@ namespace Editor
if (GetIEditor()->IsInGameMode())
{
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
AzFramework::LinuxXcbEventHandlerBus::Broadcast(&AzFramework::LinuxXcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
#endif
return true;
}
@@ -13,6 +13,7 @@
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Render/IntersectorInterface.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
#include <EditorViewportSettings.h>
namespace SandboxEditor
@@ -94,7 +95,7 @@ namespace SandboxEditor
cameras.AddCamera(m_firstPersonPanCamera);
cameras.AddCamera(m_firstPersonTranslateCamera);
cameras.AddCamera(m_firstPersonScrollCamera);
cameras.AddCamera(m_orbitCamera);
cameras.AddCamera(m_pivotCamera);
});
return controller;
@@ -131,8 +132,8 @@ namespace SandboxEditor
m_firstPersonRotateCamera->SetActivationBeganFn(hideCursor);
m_firstPersonRotateCamera->SetActivationEndedFn(showCursor);
m_firstPersonPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan);
m_firstPersonPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan, AzFramework::TranslatePivot);
m_firstPersonPanCamera->m_panSpeedFn = []
{
@@ -151,8 +152,8 @@ namespace SandboxEditor
const auto translateCameraInputChannelIds = BuildTranslateCameraInputChannelIds();
m_firstPersonTranslateCamera =
AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation, translateCameraInputChannelIds);
m_firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot);
m_firstPersonTranslateCamera->m_translateSpeedFn = []
{
@@ -171,10 +172,10 @@ namespace SandboxEditor
return SandboxEditor::CameraScrollSpeed();
};
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(SandboxEditor::CameraOrbitChannelId());
m_pivotCamera = AZStd::make_shared<AzFramework::PivotCameraInput>(SandboxEditor::CameraPivotChannelId());
m_orbitCamera->SetLookAtFn(
[viewportId = m_viewportId](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
m_pivotCamera->SetPivotFn(
[viewportId = m_viewportId]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
AZStd::optional<AZ::Vector3> lookAtAfterInterpolation;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
@@ -182,109 +183,98 @@ namespace SandboxEditor
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::LookAtAfterInterpolation);
// initially attempt to use the last set look at point after an interpolation has finished
if (lookAtAfterInterpolation.has_value())
// note: ignore this if it is the same location as the camera (e.g. after go to position)
if (lookAtAfterInterpolation.has_value() && !lookAtAfterInterpolation->IsClose(position))
{
return *lookAtAfterInterpolation;
}
const float RayDistance = 1000.0f;
AzFramework::RenderGeometry::RayRequest ray;
ray.m_startWorldPosition = position;
ray.m_endWorldPosition = position + direction * RayDistance;
ray.m_onlyVisible = true;
// otherwise fall back to the selected entity pivot
AZStd::optional<AZ::Transform> entityPivot;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
entityPivot, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
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;
}
// if there is no selection or no intersection, fallback to default camera orbit behavior (ground plane
// intersection)
return {};
// finally just use the identity
return entityPivot.value_or(AZ::Transform::CreateIdentity()).GetTranslation();
});
m_orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraOrbitLookChannelId());
m_pivotRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraPivotLookChannelId());
m_orbitRotateCamera->m_rotateSpeedFn = []
m_pivotRotateCamera->m_rotateSpeedFn = []
{
return SandboxEditor::CameraRotateSpeed();
};
m_orbitRotateCamera->m_invertYawFn = []
m_pivotRotateCamera->m_invertYawFn = []
{
return SandboxEditor::CameraOrbitYawRotationInverted();
return SandboxEditor::CameraPivotYawRotationInverted();
};
m_orbitTranslateCamera =
AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation, translateCameraInputChannelIds);
m_pivotTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffset);
m_orbitTranslateCamera->m_translateSpeedFn = []
m_pivotTranslateCamera->m_translateSpeedFn = []
{
return SandboxEditor::CameraTranslateSpeed();
};
m_orbitTranslateCamera->m_boostMultiplierFn = []
m_pivotTranslateCamera->m_boostMultiplierFn = []
{
return SandboxEditor::CameraBoostMultiplier();
};
m_orbitDollyScrollCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
m_pivotDollyScrollCamera = AZStd::make_shared<AzFramework::PivotDollyScrollCameraInput>();
m_orbitDollyScrollCamera->m_scrollSpeedFn = []
m_pivotDollyScrollCamera->m_scrollSpeedFn = []
{
return SandboxEditor::CameraScrollSpeed();
};
m_orbitDollyMoveCamera =
AZStd::make_shared<AzFramework::OrbitDollyCursorMoveCameraInput>(SandboxEditor::CameraOrbitDollyChannelId());
m_pivotDollyMoveCamera = AZStd::make_shared<AzFramework::PivotDollyMotionCameraInput>(SandboxEditor::CameraPivotDollyChannelId());
m_orbitDollyMoveCamera->m_cursorSpeedFn = []
m_pivotDollyMoveCamera->m_motionSpeedFn = []
{
return SandboxEditor::CameraDollyMotionSpeed();
};
m_orbitPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(SandboxEditor::CameraOrbitPanChannelId(), AzFramework::OrbitPan);
m_pivotPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
SandboxEditor::CameraPivotPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffset);
m_orbitPanCamera->m_panSpeedFn = []
m_pivotPanCamera->m_panSpeedFn = []
{
return SandboxEditor::CameraPanSpeed();
};
m_orbitPanCamera->m_invertPanXFn = []
m_pivotPanCamera->m_invertPanXFn = []
{
return SandboxEditor::CameraPanInvertedX();
};
m_orbitPanCamera->m_invertPanYFn = []
m_pivotPanCamera->m_invertPanYFn = []
{
return SandboxEditor::CameraPanInvertedY();
};
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitRotateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitTranslateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyScrollCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyMoveCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitPanCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotRotateCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotTranslateCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyScrollCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyMoveCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotPanCamera);
}
void EditorModularViewportCameraComposer::OnEditorModularViewportCameraComposerSettingsChanged()
{
const auto translateCameraInputChannelIds = BuildTranslateCameraInputChannelIds();
m_firstPersonTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_orbitTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_firstPersonPanCamera->SetPanInputChannelId(SandboxEditor::CameraFreePanChannelId());
m_orbitPanCamera->SetPanInputChannelId(SandboxEditor::CameraOrbitPanChannelId());
m_firstPersonRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraFreeLookChannelId());
m_orbitRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraOrbitLookChannelId());
m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId());
m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
m_pivotCamera->SetPivotInputChannelId(SandboxEditor::CameraPivotChannelId());
m_pivotTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_pivotPanCamera->SetPanInputChannelId(SandboxEditor::CameraPivotPanChannelId());
m_pivotRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraPivotLookChannelId());
m_pivotDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraPivotDollyChannelId());
}
void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
@@ -295,8 +285,7 @@ namespace SandboxEditor
AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM);
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame,
worldFromLocal);
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, worldFromLocal);
}
else
{
@@ -42,12 +42,12 @@ namespace SandboxEditor
AZStd::shared_ptr<AzFramework::PanCameraInput> m_firstPersonPanCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
AZStd::shared_ptr<AzFramework::ScrollTranslationCameraInput> m_firstPersonScrollCamera;
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_orbitRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_orbitTranslateCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyScrollCameraInput> m_orbitDollyScrollCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyCursorMoveCameraInput> m_orbitDollyMoveCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_orbitPanCamera;
AZStd::shared_ptr<AzFramework::PivotCameraInput> m_pivotCamera;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_pivotRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_pivotTranslateCamera;
AZStd::shared_ptr<AzFramework::PivotDollyScrollCameraInput> m_pivotDollyScrollCamera;
AZStd::shared_ptr<AzFramework::PivotDollyMotionCameraInput> m_pivotDollyMoveCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_pivotPanCamera;
AzFramework::ViewportId m_viewportId;
};
@@ -61,7 +61,7 @@ static AZStd::vector<AZStd::string> GetEditorInputNames()
void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serialize)
{
serialize.Class<CameraMovementSettings>()
->Version(2)
->Version(3)
->Field("TranslateSpeed", &CameraMovementSettings::m_translateSpeed)
->Field("RotateSpeed", &CameraMovementSettings::m_rotateSpeed)
->Field("BoostMultiplier", &CameraMovementSettings::m_boostMultiplier)
@@ -73,12 +73,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Field("TranslateSmoothing", &CameraMovementSettings::m_translateSmoothing)
->Field("TranslateSmoothness", &CameraMovementSettings::m_translateSmoothness)
->Field("CaptureCursorLook", &CameraMovementSettings::m_captureCursorLook)
->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
->Field("PivotYawRotationInverted", &CameraMovementSettings::m_pivotYawRotationInverted)
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY);
serialize.Class<CameraInputSettings>()
->Version(1)
->Version(2)
->Field("TranslateForward", &CameraInputSettings::m_translateForwardChannelId)
->Field("TranslateBackward", &CameraInputSettings::m_translateBackwardChannelId)
->Field("TranslateLeft", &CameraInputSettings::m_translateLeftChannelId)
@@ -86,12 +86,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Field("TranslateUp", &CameraInputSettings::m_translateUpChannelId)
->Field("TranslateDown", &CameraInputSettings::m_translateDownChannelId)
->Field("Boost", &CameraInputSettings::m_boostChannelId)
->Field("Orbit", &CameraInputSettings::m_orbitChannelId)
->Field("Pivot", &CameraInputSettings::m_pivotChannelId)
->Field("FreeLook", &CameraInputSettings::m_freeLookChannelId)
->Field("FreePan", &CameraInputSettings::m_freePanChannelId)
->Field("OrbitLook", &CameraInputSettings::m_orbitLookChannelId)
->Field("OrbitDolly", &CameraInputSettings::m_orbitDollyChannelId)
->Field("OrbitPan", &CameraInputSettings::m_orbitPanChannelId);
->Field("PivotLook", &CameraInputSettings::m_pivotLookChannelId)
->Field("PivotDolly", &CameraInputSettings::m_pivotDollyChannelId)
->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId);
serialize.Class<CEditorPreferencesPage_ViewportCamera>()
->Version(1)
@@ -143,8 +143,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Attribute(AZ::Edit::Attributes::Min, minValue)
->Attribute(AZ::Edit::Attributes::Visibility, &CameraMovementSettings::TranslateSmoothingVisibility)
->DataElement(
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_orbitYawRotationInverted, "Camera Orbit Yaw Inverted",
"Inverted yaw rotation while orbiting")
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_pivotYawRotationInverted, "Camera Pivot Yaw Inverted",
"Inverted yaw rotation while pivoting")
->DataElement(
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_panInvertedX, "Invert Pan X",
"Invert direction of pan in local X axis")
@@ -185,8 +185,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
"Key/button to move the camera more quickly")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitChannelId, "Orbit",
"Key/button to begin the camera orbit behavior")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotChannelId, "Pivot",
"Key/button to begin the camera pivot behavior")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_freeLookChannelId, "Free Look",
@@ -196,16 +196,16 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_freePanChannelId, "Free Pan", "Key/button to begin camera free pan")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitLookChannelId, "Orbit Look",
"Key/button to begin camera orbit look")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotLookChannelId, "Pivot Look",
"Key/button to begin camera pivot look")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitDollyChannelId, "Orbit Dolly",
"Key/button to begin camera orbit dolly")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotDollyChannelId, "Pivot Dolly",
"Key/button to begin camera pivot dolly")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitPanChannelId, "Orbit Pan",
"Key/button to begin camera orbit pan")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotPanChannelId, "Pivot Pan",
"Key/button to begin camera pivot pan")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames);
editContext->Class<CEditorPreferencesPage_ViewportCamera>("Viewport Preferences", "Viewport Preferences")
@@ -264,7 +264,7 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
SandboxEditor::SetCameraTranslateSmoothness(m_cameraMovementSettings.m_translateSmoothness);
SandboxEditor::SetCameraTranslateSmoothingEnabled(m_cameraMovementSettings.m_translateSmoothing);
SandboxEditor::SetCameraCaptureCursorForLook(m_cameraMovementSettings.m_captureCursorLook);
SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
SandboxEditor::SetCameraPivotYawRotationInverted(m_cameraMovementSettings.m_pivotYawRotationInverted);
SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX);
SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY);
@@ -275,12 +275,12 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
SandboxEditor::SetCameraTranslateUpChannelId(m_cameraInputSettings.m_translateUpChannelId);
SandboxEditor::SetCameraTranslateDownChannelId(m_cameraInputSettings.m_translateDownChannelId);
SandboxEditor::SetCameraTranslateBoostChannelId(m_cameraInputSettings.m_boostChannelId);
SandboxEditor::SetCameraOrbitChannelId(m_cameraInputSettings.m_orbitChannelId);
SandboxEditor::SetCameraPivotChannelId(m_cameraInputSettings.m_pivotChannelId);
SandboxEditor::SetCameraFreeLookChannelId(m_cameraInputSettings.m_freeLookChannelId);
SandboxEditor::SetCameraFreePanChannelId(m_cameraInputSettings.m_freePanChannelId);
SandboxEditor::SetCameraOrbitLookChannelId(m_cameraInputSettings.m_orbitLookChannelId);
SandboxEditor::SetCameraOrbitDollyChannelId(m_cameraInputSettings.m_orbitDollyChannelId);
SandboxEditor::SetCameraOrbitPanChannelId(m_cameraInputSettings.m_orbitPanChannelId);
SandboxEditor::SetCameraPivotLookChannelId(m_cameraInputSettings.m_pivotLookChannelId);
SandboxEditor::SetCameraPivotDollyChannelId(m_cameraInputSettings.m_pivotDollyChannelId);
SandboxEditor::SetCameraPivotPanChannelId(m_cameraInputSettings.m_pivotPanChannelId);
SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Broadcast(
&SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Events::OnEditorModularViewportCameraComposerSettingsChanged);
@@ -299,7 +299,7 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
m_cameraMovementSettings.m_translateSmoothness = SandboxEditor::CameraTranslateSmoothness();
m_cameraMovementSettings.m_translateSmoothing = SandboxEditor::CameraTranslateSmoothingEnabled();
m_cameraMovementSettings.m_captureCursorLook = SandboxEditor::CameraCaptureCursorForLook();
m_cameraMovementSettings.m_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted();
m_cameraMovementSettings.m_pivotYawRotationInverted = SandboxEditor::CameraPivotYawRotationInverted();
m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX();
m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY();
@@ -310,10 +310,10 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
m_cameraInputSettings.m_translateUpChannelId = SandboxEditor::CameraTranslateUpChannelId().GetName();
m_cameraInputSettings.m_translateDownChannelId = SandboxEditor::CameraTranslateDownChannelId().GetName();
m_cameraInputSettings.m_boostChannelId = SandboxEditor::CameraTranslateBoostChannelId().GetName();
m_cameraInputSettings.m_orbitChannelId = SandboxEditor::CameraOrbitChannelId().GetName();
m_cameraInputSettings.m_pivotChannelId = SandboxEditor::CameraPivotChannelId().GetName();
m_cameraInputSettings.m_freeLookChannelId = SandboxEditor::CameraFreeLookChannelId().GetName();
m_cameraInputSettings.m_freePanChannelId = SandboxEditor::CameraFreePanChannelId().GetName();
m_cameraInputSettings.m_orbitLookChannelId = SandboxEditor::CameraOrbitLookChannelId().GetName();
m_cameraInputSettings.m_orbitDollyChannelId = SandboxEditor::CameraOrbitDollyChannelId().GetName();
m_cameraInputSettings.m_orbitPanChannelId = SandboxEditor::CameraOrbitPanChannelId().GetName();
m_cameraInputSettings.m_pivotLookChannelId = SandboxEditor::CameraPivotLookChannelId().GetName();
m_cameraInputSettings.m_pivotDollyChannelId = SandboxEditor::CameraPivotDollyChannelId().GetName();
m_cameraInputSettings.m_pivotPanChannelId = SandboxEditor::CameraPivotPanChannelId().GetName();
}
@@ -54,7 +54,7 @@ private:
float m_translateSmoothness;
bool m_translateSmoothing;
bool m_captureCursorLook;
bool m_orbitYawRotationInverted;
bool m_pivotYawRotationInverted;
bool m_panInvertedX;
bool m_panInvertedY;
@@ -80,12 +80,12 @@ private:
AZStd::string m_translateUpChannelId;
AZStd::string m_translateDownChannelId;
AZStd::string m_boostChannelId;
AZStd::string m_orbitChannelId;
AZStd::string m_pivotChannelId;
AZStd::string m_freeLookChannelId;
AZStd::string m_freePanChannelId;
AZStd::string m_orbitLookChannelId;
AZStd::string m_orbitDollyChannelId;
AZStd::string m_orbitPanChannelId;
AZStd::string m_pivotLookChannelId;
AZStd::string m_pivotDollyChannelId;
AZStd::string m_pivotPanChannelId;
};
CameraMovementSettings m_cameraMovementSettings;
+25 -25
View File
@@ -28,7 +28,7 @@ namespace SandboxEditor
constexpr AZStd::string_view CameraRotateSpeedSetting = "/Amazon/Preferences/Editor/Camera/RotateSpeed";
constexpr AZStd::string_view CameraScrollSpeedSetting = "/Amazon/Preferences/Editor/Camera/DollyScrollSpeed";
constexpr AZStd::string_view CameraDollyMotionSpeedSetting = "/Amazon/Preferences/Editor/Camera/DollyMotionSpeed";
constexpr AZStd::string_view CameraOrbitYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted";
constexpr AZStd::string_view CameraPivotYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted";
constexpr AZStd::string_view CameraPanInvertedXSetting = "/Amazon/Preferences/Editor/Camera/PanInvertedX";
constexpr AZStd::string_view CameraPanInvertedYSetting = "/Amazon/Preferences/Editor/Camera/PanInvertedY";
constexpr AZStd::string_view CameraPanSpeedSetting = "/Amazon/Preferences/Editor/Camera/PanSpeed";
@@ -44,12 +44,12 @@ namespace SandboxEditor
constexpr AZStd::string_view CameraTranslateUpIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpId";
constexpr AZStd::string_view CameraTranslateDownIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpDownId";
constexpr AZStd::string_view CameraTranslateBoostIdSetting = "/Amazon/Preferences/Editor/Camera/TranslateBoostId";
constexpr AZStd::string_view CameraOrbitIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitId";
constexpr AZStd::string_view CameraPivotIdSetting = "/Amazon/Preferences/Editor/Camera/PivotId";
constexpr AZStd::string_view CameraFreeLookIdSetting = "/Amazon/Preferences/Editor/Camera/FreeLookId";
constexpr AZStd::string_view CameraFreePanIdSetting = "/Amazon/Preferences/Editor/Camera/FreePanId";
constexpr AZStd::string_view CameraOrbitLookIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitLookId";
constexpr AZStd::string_view CameraOrbitDollyIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitDollyId";
constexpr AZStd::string_view CameraOrbitPanIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitPanId";
constexpr AZStd::string_view CameraPivotLookIdSetting = "/Amazon/Preferences/Editor/Camera/PivotLookId";
constexpr AZStd::string_view CameraPivotDollyIdSetting = "/Amazon/Preferences/Editor/Camera/PivotDollyId";
constexpr AZStd::string_view CameraPivotPanIdSetting = "/Amazon/Preferences/Editor/Camera/PivotPanId";
template<typename T>
void SetRegistry(const AZStd::string_view setting, T&& value)
@@ -239,14 +239,14 @@ namespace SandboxEditor
SetRegistry(CameraDollyMotionSpeedSetting, speed);
}
bool CameraOrbitYawRotationInverted()
bool CameraPivotYawRotationInverted()
{
return GetRegistry(CameraOrbitYawRotationInvertedSetting, false);
return GetRegistry(CameraPivotYawRotationInvertedSetting, false);
}
void SetCameraOrbitYawRotationInverted(const bool inverted)
void SetCameraPivotYawRotationInverted(const bool inverted)
{
SetRegistry(CameraOrbitYawRotationInvertedSetting, inverted);
SetRegistry(CameraPivotYawRotationInvertedSetting, inverted);
}
bool CameraPanInvertedX()
@@ -403,14 +403,14 @@ namespace SandboxEditor
SetRegistry(CameraTranslateBoostIdSetting, cameraTranslateBoostId);
}
AzFramework::InputChannelId CameraOrbitChannelId()
AzFramework::InputChannelId CameraPivotChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraPivotIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
}
void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId)
void SetCameraPivotChannelId(AZStd::string_view cameraPivotId)
{
SetRegistry(CameraOrbitIdSetting, cameraOrbitId);
SetRegistry(CameraPivotIdSetting, cameraPivotId);
}
AzFramework::InputChannelId CameraFreeLookChannelId()
@@ -433,33 +433,33 @@ namespace SandboxEditor
SetRegistry(CameraFreePanIdSetting, cameraFreePanId);
}
AzFramework::InputChannelId CameraOrbitLookChannelId()
AzFramework::InputChannelId CameraPivotLookChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraPivotLookIdSetting, AZStd::string("mouse_button_left")).c_str());
}
void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId)
void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId)
{
SetRegistry(CameraOrbitLookIdSetting, cameraOrbitLookId);
SetRegistry(CameraPivotLookIdSetting, cameraPivotLookId);
}
AzFramework::InputChannelId CameraOrbitDollyChannelId()
AzFramework::InputChannelId CameraPivotDollyChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraPivotDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
}
void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId)
void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId)
{
SetRegistry(CameraOrbitDollyIdSetting, cameraOrbitDollyId);
SetRegistry(CameraPivotDollyIdSetting, cameraPivotDollyId);
}
AzFramework::InputChannelId CameraOrbitPanChannelId()
AzFramework::InputChannelId CameraPivotPanChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraPivotPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
}
void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId)
void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId)
{
SetRegistry(CameraOrbitPanIdSetting, cameraOrbitPanId);
SetRegistry(CameraPivotPanIdSetting, cameraPivotPanId);
}
} // namespace SandboxEditor
+10 -10
View File
@@ -71,8 +71,8 @@ namespace SandboxEditor
SANDBOX_API float CameraDollyMotionSpeed();
SANDBOX_API void SetCameraDollyMotionSpeed(float speed);
SANDBOX_API bool CameraOrbitYawRotationInverted();
SANDBOX_API void SetCameraOrbitYawRotationInverted(bool inverted);
SANDBOX_API bool CameraPivotYawRotationInverted();
SANDBOX_API void SetCameraPivotYawRotationInverted(bool inverted);
SANDBOX_API bool CameraPanInvertedX();
SANDBOX_API void SetCameraPanInvertedX(bool inverted);
@@ -119,8 +119,8 @@ namespace SandboxEditor
SANDBOX_API AzFramework::InputChannelId CameraTranslateBoostChannelId();
SANDBOX_API void SetCameraTranslateBoostChannelId(AZStd::string_view cameraTranslateBoostId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitChannelId();
SANDBOX_API void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId);
SANDBOX_API AzFramework::InputChannelId CameraPivotChannelId();
SANDBOX_API void SetCameraPivotChannelId(AZStd::string_view cameraPivotId);
SANDBOX_API AzFramework::InputChannelId CameraFreeLookChannelId();
SANDBOX_API void SetCameraFreeLookChannelId(AZStd::string_view cameraFreeLookId);
@@ -128,12 +128,12 @@ namespace SandboxEditor
SANDBOX_API AzFramework::InputChannelId CameraFreePanChannelId();
SANDBOX_API void SetCameraFreePanChannelId(AZStd::string_view cameraFreePanId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitLookChannelId();
SANDBOX_API void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId);
SANDBOX_API AzFramework::InputChannelId CameraPivotLookChannelId();
SANDBOX_API void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitDollyChannelId();
SANDBOX_API void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId);
SANDBOX_API AzFramework::InputChannelId CameraPivotDollyChannelId();
SANDBOX_API void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitPanChannelId();
SANDBOX_API void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId);
SANDBOX_API AzFramework::InputChannelId CameraPivotPanChannelId();
SANDBOX_API void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId);
} // namespace SandboxEditor
+1 -1
View File
@@ -2428,7 +2428,7 @@ void EditorViewportWidget::RestoreViewportAfterGameMode()
}
else
{
AZ_Error("CryLegacy", false, "Not restoring the editor viewport camera is currently unsupported");
AZ_Warning("CryLegacy", false, "Not restoring the editor viewport camera is currently unsupported");
SetViewTM(preGameModeViewTM);
}
}
@@ -649,6 +649,16 @@ namespace AZ
m_stateEvent.Signal(oldState, m_state);
}
void Entity::SetSpawnTicketId(u32 spawnTicketId)
{
m_spawnTicketId = spawnTicketId;
}
u32 Entity::GetSpawnTicketId() const
{
return m_spawnTicketId;
}
void Entity::OnNameChanged() const
{
EBUS_EVENT_ID(GetId(), EntityBus, OnEntityNameChanged, m_name);
@@ -133,6 +133,14 @@ namespace AZ
//! @return The state of the entity. For example, the entity has been initialized, the entity is active, and so on.
State GetState() const { return m_state; }
//! Gets the ticket id used to spawn the entity.
//! @return the ticket id used to spawn the entity. If entity is not spawned, the id will be 0.
u32 GetSpawnTicketId() const;
//! Sets the ticket id used to spawn the entity. The ticket id in the entity will remain 0 unless it's set using this function.
//! @param spawnTicketId the ticket id used to spawn the entity.
void SetSpawnTicketId(u32 spawnTicketId);
//! Connects an entity state event handler to the entity.
//! All state changes will be signaled through this event.
//! @param handler reference to the EntityStateEvent handler to attach to the entities state event.
@@ -410,6 +418,8 @@ namespace AZ
//! A user-friendly name for the entity. This makes error messages easier to read.
AZStd::string m_name;
u32 m_spawnTicketId = 0;
//! The state of the entity.
State m_state;
@@ -91,15 +91,6 @@ namespace AzFramework
*/
virtual void DestroyGameEntity(const AZ::EntityId& /*id*/) = 0;
/**
* Destroys an entity only in slice mode (when prefabs are disabled). This request is only added as a stop-gap solution
* to prevent the editor from crashing when prefabs are enabled and must only be called through the BehaviorContext binding
* for 'DestroyGameEntity'. No code should be written to directly call this method. This will be removed soon.
*
* @param id The ID of the entity to destroy.
*/
virtual void DestroyGameEntityOnlyInSliceMode(const AZ::EntityId& /*id*/) = 0;
/**
* Destroys an entity and all of its descendants.
* The entity and its descendants are immediately deactivated and will be
@@ -108,15 +99,6 @@ namespace AzFramework
*/
virtual void DestroyGameEntityAndDescendants(const AZ::EntityId& /*id*/) = 0;
/**
* Destroys an entity and its descendants only in slice mode (when prefabs are disabled). This request is only added as a stop-gap
* solution to prevent the editor from crashing when prefabs are enabled and must only be called through the BehaviorContext
* binding for 'DestroyGameEntityAndDescendants'.No code should be written to directly call this method. This will be removed soon.
*
* @param id The ID of the entity to destroy.
*/
virtual void DestroyGameEntityAndDescendantsOnlyInSliceMode(const AZ::EntityId& /*id*/) = 0;
/**
* Activates the game entity.
* @param id The ID of the entity to activate.
@@ -11,9 +11,10 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include "GameEntityContextComponent.h"
@@ -47,9 +48,9 @@ namespace AzFramework
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Event("CreateGameEntity", &GameEntityContextRequestBus::Events::CreateGameEntityForBehaviorContext)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Event("DestroyGameEntity", &GameEntityContextRequestBus::Events::DestroyGameEntityOnlyInSliceMode)
->Event("DestroyGameEntity", &GameEntityContextRequestBus::Events::DestroyGameEntity)
->Event(
"DestroyGameEntityAndDescendants", &GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendantsOnlyInSliceMode)
"DestroyGameEntityAndDescendants", &GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants)
->Event("ActivateGameEntity", &GameEntityContextRequestBus::Events::ActivateGameEntity)
->Event("DeactivateGameEntity", &GameEntityContextRequestBus::Events::DeactivateGameEntity)
->Attribute(AZ::ScriptCanvasAttributes::DeactivatesInputEntity, true)
@@ -249,23 +250,6 @@ namespace AzFramework
DestroyGameEntityInternal(id, false);
}
void GameEntityContextComponent::DestroyGameEntityOnlyInSliceMode(const AZ::EntityId& id)
{
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (!isPrefabSystemEnabled)
{
DestroyGameEntityInternal(id, false);
}
else
{
AZ_Error(
"GameEntityContextComponent", false,
"Destroying a game entity is temporarily disabled until the Spawnable system can support this.");
}
}
//=========================================================================
// GameEntityContextComponent::DestroyGameEntityAndDescendantsById
//=========================================================================
@@ -274,24 +258,6 @@ namespace AzFramework
DestroyGameEntityInternal(id, true);
}
void GameEntityContextComponent::DestroyGameEntityAndDescendantsOnlyInSliceMode(const AZ::EntityId& id)
{
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (!isPrefabSystemEnabled)
{
DestroyGameEntityInternal(id, true);
}
else
{
AZ_Error(
"GameEntityContextComponent", false,
"Destroying a game entity and its descendants is temporarily disabled until the Spawnable system can support this.");
}
}
//=========================================================================
// GameEntityContextComponent::DestroyGameEntityInternal
//=========================================================================
@@ -319,6 +285,28 @@ namespace AzFramework
EBUS_EVENT_RESULT(currentEntity, AZ::ComponentApplicationBus, FindEntity, *entityIdIter);
if (currentEntity)
{
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabSystemEnabled)
{
if (currentEntity->GetSpawnTicketId() > 0)
{
SpawnableEntitiesDefinition* spawnableEntitiesInterface = SpawnableEntitiesInterface::Get();
AZ_Assert(spawnableEntitiesInterface != nullptr, "SpawnableEntitiesInterface is not found.");
spawnableEntitiesInterface->RetrieveEntitySpawnTicket(
currentEntity->GetSpawnTicketId(),
[spawnableEntitiesInterface, currentEntity](EntitySpawnTicket* entitySpawnTicket)
{
if (entitySpawnTicket != nullptr)
{
spawnableEntitiesInterface->DespawnEntity(currentEntity->GetId(), *entitySpawnTicket);
}
});
return;
}
}
if (currentEntity->GetState() == AZ::Entity::State::Active)
{
// Deactivate the entity, we'll destroy it as soon as it is safe.
@@ -90,11 +90,6 @@ namespace AzFramework
}
private:
//////////////////////////////////////////////////////////////////////////
// GameEntityContextRequestBus
void DestroyGameEntityOnlyInSliceMode(const AZ::EntityId&) override;
void DestroyGameEntityAndDescendantsOnlyInSliceMode(const AZ::EntityId&) override;
/////////////////////////////////////////////////////////////////////////
AzFramework::EntityVisibilityBoundsUnionSystem m_entityVisibilityBoundsUnionSystem;
};
@@ -28,34 +28,10 @@ namespace AzFramework
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId::InputChannelId(const char* name)
: m_crc32(name)
{
memset(m_name, 0, AZ_ARRAY_SIZE(m_name));
azstrncpy(m_name, NAME_BUFFER_SIZE, name, MAX_NAME_LENGTH);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId::InputChannelId(const InputChannelId& other)
: m_crc32(other.m_crc32)
{
memset(m_name, 0, AZ_ARRAY_SIZE(m_name));
azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId& InputChannelId::operator=(const InputChannelId& other)
{
azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name);
m_crc32 = other.m_crc32;
return *this;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const char* InputChannelId::GetName() const
{
return m_name;
return m_name.c_str();
}
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,6 +11,7 @@
#include <AzCore/Math/Crc.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/hash.h>
#include <AzCore/std/string/fixed_string.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
@@ -22,8 +23,7 @@ namespace AzFramework
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Constants
static const int NAME_BUFFER_SIZE = 64;
static const int MAX_NAME_LENGTH = NAME_BUFFER_SIZE - 1;
static constexpr int MAX_NAME_LENGTH = 64;
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
@@ -39,21 +39,28 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] name Name of the input channel (will be truncated if exceeds MAX_NAME_LENGTH)
explicit InputChannelId(const char* name = "");
//! \param[in] name Name of the input channel (will be ignored if exceeds MAX_NAME_LENGTH)
explicit constexpr InputChannelId(AZStd::string_view name = "")
: m_name(name)
, m_crc32(name)
{
}
////////////////////////////////////////////////////////////////////////////////////////////
//! Copy constructor
//! \param[in] other Another instance of the class to copy from
InputChannelId(const InputChannelId& other);
////////////////////////////////////////////////////////////////////////////////////////////
//! Copy assignment operator
//! \param[in] other Another instance of the class to copy from
InputChannelId& operator=(const InputChannelId& other);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
constexpr InputChannelId(const InputChannelId& other) = default;
constexpr InputChannelId(InputChannelId&& other) = default;
constexpr InputChannelId& operator=(const InputChannelId& other)
{
m_name = other.m_name;
m_crc32 = other.m_crc32;
return *this;
}
constexpr InputChannelId& operator=(InputChannelId&& other)
{
m_name = AZStd::move(other.m_name);
m_crc32 = AZStd::move(other.m_crc32);
other.m_crc32 = 0;
return *this;
}
~InputChannelId() = default;
////////////////////////////////////////////////////////////////////////////////////////////
@@ -77,7 +84,7 @@ namespace AzFramework
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
char m_name[NAME_BUFFER_SIZE]; //!< Name of the input channel
AZStd::fixed_string<MAX_NAME_LENGTH> m_name; //!< Name of the input channel
AZ::Crc32 m_crc32; //!< Crc32 of the input channel
};
} // namespace AzFramework
@@ -28,91 +28,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == IdForIndex0.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::Button::A("gamepad_button_a");
const InputChannelId InputDeviceGamepad::Button::B("gamepad_button_b");
const InputChannelId InputDeviceGamepad::Button::X("gamepad_button_x");
const InputChannelId InputDeviceGamepad::Button::Y("gamepad_button_y");
const InputChannelId InputDeviceGamepad::Button::L1("gamepad_button_l1");
const InputChannelId InputDeviceGamepad::Button::R1("gamepad_button_r1");
const InputChannelId InputDeviceGamepad::Button::L3("gamepad_button_l3");
const InputChannelId InputDeviceGamepad::Button::R3("gamepad_button_r3");
const InputChannelId InputDeviceGamepad::Button::DU("gamepad_button_d_up");
const InputChannelId InputDeviceGamepad::Button::DD("gamepad_button_d_down");
const InputChannelId InputDeviceGamepad::Button::DL("gamepad_button_d_left");
const InputChannelId InputDeviceGamepad::Button::DR("gamepad_button_d_right");
const InputChannelId InputDeviceGamepad::Button::Start("gamepad_button_start");
const InputChannelId InputDeviceGamepad::Button::Select("gamepad_button_select");
const AZStd::array<InputChannelId, 14> InputDeviceGamepad::Button::All =
{{
A,
B,
X,
Y,
L1,
R1,
L3,
R3,
DU,
DD,
DL,
DR,
Start,
Select
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::Trigger::L2("gamepad_trigger_l2");
const InputChannelId InputDeviceGamepad::Trigger::R2("gamepad_trigger_r2");
const AZStd::array<InputChannelId, 2> InputDeviceGamepad::Trigger::All =
{{
L2,
R2
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::L("gamepad_thumbstick_l");
const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::R("gamepad_thumbstick_r");
const AZStd::array<InputChannelId, 2> InputDeviceGamepad::ThumbStickAxis2D::All =
{{
L,
R
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LX("gamepad_thumbstick_l_x");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LY("gamepad_thumbstick_l_y");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RX("gamepad_thumbstick_r_x");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RY("gamepad_thumbstick_r_y");
const AZStd::array<InputChannelId, 4> InputDeviceGamepad::ThumbStickAxis1D::All =
{{
LX,
LY,
RX,
RY
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LU("gamepad_thumbstick_l_up");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LD("gamepad_thumbstick_l_down");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LL("gamepad_thumbstick_l_left");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LR("gamepad_thumbstick_l_right");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RU("gamepad_thumbstick_r_up");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RD("gamepad_thumbstick_r_down");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RL("gamepad_thumbstick_r_left");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RR("gamepad_thumbstick_r_right");
const AZStd::array<InputChannelId, 8> InputDeviceGamepad::ThumbStickDirection::All =
{{
LU,
LD,
LL,
LR,
RU,
RD,
RL,
RR
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::Reflect(AZ::ReflectContext* context)
{
@@ -59,75 +59,115 @@ namespace AzFramework
//! All the input channel ids that identify game-pad digital button input
struct Button
{
static const InputChannelId A; //!< The bottom diamond face button
static const InputChannelId B; //!< The right diamond face button
static const InputChannelId X; //!< The left diamond face button
static const InputChannelId Y; //!< The top diamond face button
static const InputChannelId L1; //!< The top-left shoulder bumper button
static const InputChannelId R1; //!< The top-right shoulder bumper button
static const InputChannelId L3; //!< The left thumb-stick click button
static const InputChannelId R3; //!< The right thumb-stick click button
static const InputChannelId DU; //!< The up directional pad button
static const InputChannelId DD; //!< The down directional pad button
static const InputChannelId DL; //!< The left directional pad button
static const InputChannelId DR; //!< The right directional pad button
static const InputChannelId Start; //!< The start/pause/options button
static const InputChannelId Select; //!< The select/back button
static constexpr inline InputChannelId A{"gamepad_button_a"}; //!< The bottom diamond face button
static constexpr inline InputChannelId B{"gamepad_button_b"}; //!< The right diamond face button
static constexpr inline InputChannelId X{"gamepad_button_x"}; //!< The left diamond face button
static constexpr inline InputChannelId Y{"gamepad_button_y"}; //!< The top diamond face button
static constexpr inline InputChannelId L1{"gamepad_button_l1"}; //!< The top-left shoulder bumper button
static constexpr inline InputChannelId R1{"gamepad_button_r1"}; //!< The top-right shoulder bumper button
static constexpr inline InputChannelId L3{"gamepad_button_l3"}; //!< The left thumb-stick click button
static constexpr inline InputChannelId R3{"gamepad_button_r3"}; //!< The right thumb-stick click button
static constexpr inline InputChannelId DU{"gamepad_button_d_up"}; //!< The up directional pad button
static constexpr inline InputChannelId DD{"gamepad_button_d_down"}; //!< The down directional pad button
static constexpr inline InputChannelId DL{"gamepad_button_d_left"}; //!< The left directional pad button
static constexpr inline InputChannelId DR{"gamepad_button_d_right"}; //!< The right directional pad button
static constexpr inline InputChannelId Start{"gamepad_button_start"}; //!< The start/pause/options button
static constexpr inline InputChannelId Select{"gamepad_button_select"}; //!< The select/back button
//!< All digital game-pad button ids
static const AZStd::array<InputChannelId, 14> All;
static constexpr inline AZStd::array All
{
A,
B,
X,
Y,
L1,
R1,
L3,
R3,
DU,
DD,
DL,
DR,
Start,
Select
};
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad analog trigger input
struct Trigger
{
static const InputChannelId L2; //!< The bottom-left shoulder trigger
static const InputChannelId R2; //!< The bottom-right shoulder trigger
static constexpr inline InputChannelId L2{"gamepad_trigger_l2"}; //!< The bottom-left shoulder trigger
static constexpr inline InputChannelId R2{"gamepad_trigger_r2"}; //!< The bottom-right shoulder trigger
//!< All analog game-pad trigger ids
static const AZStd::array<InputChannelId, 2> All;
static constexpr inline AZStd::array All
{
L2,
R2
};
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick 2D axis input
struct ThumbStickAxis2D
{
static const InputChannelId L; //!< The left-hand thumb-stick
static const InputChannelId R; //!< The right-hand thumb-stick
static constexpr inline InputChannelId L{"gamepad_thumbstick_l"}; //!< The left-hand thumb-stick
static constexpr inline InputChannelId R{"gamepad_thumbstick_r"}; //!< The right-hand thumb-stick
//!< All game-pad thumb-stick 2D axis input channel ids
static const AZStd::array<InputChannelId, 2> All;
static constexpr inline AZStd::array All
{
L,
R
};
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick 1D axis input
struct ThumbStickAxis1D
{
static const InputChannelId LX; //!< X-axis of the left-hand thumb-stick
static const InputChannelId LY; //!< Y-axis of the left-hand thumb-stick
static const InputChannelId RX; //!< X-axis of the right-hand thumb-stick
static const InputChannelId RY; //!< Y-axis of the right-hand thumb-stick
static constexpr inline InputChannelId LX{"gamepad_thumbstick_l_x"}; //!< X-axis of the left-hand thumb-stick
static constexpr inline InputChannelId LY{"gamepad_thumbstick_l_y"}; //!< Y-axis of the left-hand thumb-stick
static constexpr inline InputChannelId RX{"gamepad_thumbstick_r_x"}; //!< X-axis of the right-hand thumb-stick
static constexpr inline InputChannelId RY{"gamepad_thumbstick_r_y"}; //!< Y-axis of the right-hand thumb-stick
//!< All game-pad thumb-stick 1D axis input channel ids
static const AZStd::array<InputChannelId, 4> All;
static constexpr inline AZStd::array All
{
LX,
LY,
RX,
RY
};
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick directional input
struct ThumbStickDirection
{
static const InputChannelId LU; //!< Up on the left-hand thumb-stick
static const InputChannelId LD; //!< Down on the left-hand thumb-stick
static const InputChannelId LL; //!< Left on the left-hand thumb-stick
static const InputChannelId LR; //!< Right on the left-hand thumb-stick
static const InputChannelId RU; //!< Up on the left-hand thumb-stick
static const InputChannelId RD; //!< Down on the left-hand thumb-stick
static const InputChannelId RL; //!< Left on the left-hand thumb-stick
static const InputChannelId RR; //!< Right on the left-hand thumb-stick
static constexpr inline InputChannelId LU{"gamepad_thumbstick_l_up"}; //!< Up on the left-hand thumb-stick
static constexpr inline InputChannelId LD{"gamepad_thumbstick_l_down"}; //!< Down on the left-hand thumb-stick
static constexpr inline InputChannelId LL{"gamepad_thumbstick_l_left"}; //!< Left on the left-hand thumb-stick
static constexpr inline InputChannelId LR{"gamepad_thumbstick_l_right"}; //!< Right on the left-hand thumb-stick
static constexpr inline InputChannelId RU{"gamepad_thumbstick_r_up"}; //!< Up on the left-hand thumb-stick
static constexpr inline InputChannelId RD{"gamepad_thumbstick_r_down"}; //!< Down on the left-hand thumb-stick
static constexpr inline InputChannelId RL{"gamepad_thumbstick_r_left"}; //!< Left on the left-hand thumb-stick
static constexpr inline InputChannelId RR{"gamepad_thumbstick_r_right"}; //!< Right on the left-hand thumb-stick
//!< All game-pad thumb-stick directional input channel ids
static const AZStd::array<InputChannelId, 8> All;
static constexpr inline AZStd::array All
{
LU,
LD,
LL,
LR,
RU,
RD,
RL,
RR
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -24,279 +24,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Alphanumeric Keys
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric0("keyboard_key_alphanumeric_0");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric1("keyboard_key_alphanumeric_1");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric2("keyboard_key_alphanumeric_2");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric3("keyboard_key_alphanumeric_3");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric4("keyboard_key_alphanumeric_4");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric5("keyboard_key_alphanumeric_5");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric6("keyboard_key_alphanumeric_6");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric7("keyboard_key_alphanumeric_7");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric8("keyboard_key_alphanumeric_8");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric9("keyboard_key_alphanumeric_9");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericA("keyboard_key_alphanumeric_A");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericB("keyboard_key_alphanumeric_B");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericC("keyboard_key_alphanumeric_C");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericD("keyboard_key_alphanumeric_D");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericE("keyboard_key_alphanumeric_E");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericF("keyboard_key_alphanumeric_F");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericG("keyboard_key_alphanumeric_G");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericH("keyboard_key_alphanumeric_H");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericI("keyboard_key_alphanumeric_I");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericJ("keyboard_key_alphanumeric_J");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericK("keyboard_key_alphanumeric_K");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericL("keyboard_key_alphanumeric_L");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericM("keyboard_key_alphanumeric_M");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericN("keyboard_key_alphanumeric_N");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericO("keyboard_key_alphanumeric_O");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericP("keyboard_key_alphanumeric_P");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericQ("keyboard_key_alphanumeric_Q");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericR("keyboard_key_alphanumeric_R");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericS("keyboard_key_alphanumeric_S");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericT("keyboard_key_alphanumeric_T");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericU("keyboard_key_alphanumeric_U");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericV("keyboard_key_alphanumeric_V");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericW("keyboard_key_alphanumeric_W");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericX("keyboard_key_alphanumeric_X");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericY("keyboard_key_alphanumeric_Y");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericZ("keyboard_key_alphanumeric_Z");
////////////////////////////////////////////////////////////////////////////////////////////////
// Edit (and escape) Keys
const InputChannelId InputDeviceKeyboard::Key::EditBackspace("keyboard_key_edit_backspace");
const InputChannelId InputDeviceKeyboard::Key::EditCapsLock("keyboard_key_edit_capslock");
const InputChannelId InputDeviceKeyboard::Key::EditEnter("keyboard_key_edit_enter");
const InputChannelId InputDeviceKeyboard::Key::EditSpace("keyboard_key_edit_space");
const InputChannelId InputDeviceKeyboard::Key::EditTab("keyboard_key_edit_tab");
const InputChannelId InputDeviceKeyboard::Key::Escape("keyboard_key_escape");
////////////////////////////////////////////////////////////////////////////////////////////////
// Function Keys
const InputChannelId InputDeviceKeyboard::Key::Function01("keyboard_key_function_F01");
const InputChannelId InputDeviceKeyboard::Key::Function02("keyboard_key_function_F02");
const InputChannelId InputDeviceKeyboard::Key::Function03("keyboard_key_function_F03");
const InputChannelId InputDeviceKeyboard::Key::Function04("keyboard_key_function_F04");
const InputChannelId InputDeviceKeyboard::Key::Function05("keyboard_key_function_F05");
const InputChannelId InputDeviceKeyboard::Key::Function06("keyboard_key_function_F06");
const InputChannelId InputDeviceKeyboard::Key::Function07("keyboard_key_function_F07");
const InputChannelId InputDeviceKeyboard::Key::Function08("keyboard_key_function_F08");
const InputChannelId InputDeviceKeyboard::Key::Function09("keyboard_key_function_F09");
const InputChannelId InputDeviceKeyboard::Key::Function10("keyboard_key_function_F10");
const InputChannelId InputDeviceKeyboard::Key::Function11("keyboard_key_function_F11");
const InputChannelId InputDeviceKeyboard::Key::Function12("keyboard_key_function_F12");
const InputChannelId InputDeviceKeyboard::Key::Function13("keyboard_key_function_F13");
const InputChannelId InputDeviceKeyboard::Key::Function14("keyboard_key_function_F14");
const InputChannelId InputDeviceKeyboard::Key::Function15("keyboard_key_function_F15");
const InputChannelId InputDeviceKeyboard::Key::Function16("keyboard_key_function_F16");
const InputChannelId InputDeviceKeyboard::Key::Function17("keyboard_key_function_F17");
const InputChannelId InputDeviceKeyboard::Key::Function18("keyboard_key_function_F18");
const InputChannelId InputDeviceKeyboard::Key::Function19("keyboard_key_function_F19");
const InputChannelId InputDeviceKeyboard::Key::Function20("keyboard_key_function_F20");
////////////////////////////////////////////////////////////////////////////////////////////////
// Modifier Keys
const InputChannelId InputDeviceKeyboard::Key::ModifierAltL("keyboard_key_modifier_alt_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierAltR("keyboard_key_modifier_alt_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlL("keyboard_key_modifier_ctrl_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlR("keyboard_key_modifier_ctrl_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierShiftL("keyboard_key_modifier_shift_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierShiftR("keyboard_key_modifier_shift_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierSuperL("keyboard_key_modifier_super_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierSuperR("keyboard_key_modifier_super_r");
////////////////////////////////////////////////////////////////////////////////////////////////
// Navigation Keys
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowDown("keyboard_key_navigation_arrow_down");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowLeft("keyboard_key_navigation_arrow_left");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowRight("keyboard_key_navigation_arrow_right");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowUp("keyboard_key_navigation_arrow_up");
const InputChannelId InputDeviceKeyboard::Key::NavigationDelete("keyboard_key_navigation_delete");
const InputChannelId InputDeviceKeyboard::Key::NavigationEnd("keyboard_key_navigation_end");
const InputChannelId InputDeviceKeyboard::Key::NavigationHome("keyboard_key_navigation_home");
const InputChannelId InputDeviceKeyboard::Key::NavigationInsert("keyboard_key_navigation_insert");
const InputChannelId InputDeviceKeyboard::Key::NavigationPageDown("keyboard_key_navigation_page_down");
const InputChannelId InputDeviceKeyboard::Key::NavigationPageUp("keyboard_key_navigation_page_up");
////////////////////////////////////////////////////////////////////////////////////////////////
// Numpad Keys
const InputChannelId InputDeviceKeyboard::Key::NumLock("keyboard_key_num_lock");
const InputChannelId InputDeviceKeyboard::Key::NumPad0("keyboard_key_numpad_0");
const InputChannelId InputDeviceKeyboard::Key::NumPad1("keyboard_key_numpad_1");
const InputChannelId InputDeviceKeyboard::Key::NumPad2("keyboard_key_numpad_2");
const InputChannelId InputDeviceKeyboard::Key::NumPad3("keyboard_key_numpad_3");
const InputChannelId InputDeviceKeyboard::Key::NumPad4("keyboard_key_numpad_4");
const InputChannelId InputDeviceKeyboard::Key::NumPad5("keyboard_key_numpad_5");
const InputChannelId InputDeviceKeyboard::Key::NumPad6("keyboard_key_numpad_6");
const InputChannelId InputDeviceKeyboard::Key::NumPad7("keyboard_key_numpad_7");
const InputChannelId InputDeviceKeyboard::Key::NumPad8("keyboard_key_numpad_8");
const InputChannelId InputDeviceKeyboard::Key::NumPad9("keyboard_key_numpad_9");
const InputChannelId InputDeviceKeyboard::Key::NumPadAdd("keyboard_key_numpad_add");
const InputChannelId InputDeviceKeyboard::Key::NumPadDecimal("keyboard_key_numpad_decimal");
const InputChannelId InputDeviceKeyboard::Key::NumPadDivide("keyboard_key_numpad_divide");
const InputChannelId InputDeviceKeyboard::Key::NumPadEnter("keyboard_key_numpad_enter");
const InputChannelId InputDeviceKeyboard::Key::NumPadMultiply("keyboard_key_numpad_multiply");
const InputChannelId InputDeviceKeyboard::Key::NumPadSubtract("keyboard_key_numpad_subtract");
////////////////////////////////////////////////////////////////////////////////////////////////
// Punctuation Keys
const InputChannelId InputDeviceKeyboard::Key::PunctuationApostrophe("keyboard_key_punctuation_apostrophe");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBackslash("keyboard_key_punctuation_backslash");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketL("keyboard_key_punctuation_bracket_l");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketR("keyboard_key_punctuation_bracket_r");
const InputChannelId InputDeviceKeyboard::Key::PunctuationComma("keyboard_key_punctuation_comma");
const InputChannelId InputDeviceKeyboard::Key::PunctuationEquals("keyboard_key_punctuation_equals");
const InputChannelId InputDeviceKeyboard::Key::PunctuationHyphen("keyboard_key_punctuation_hyphen");
const InputChannelId InputDeviceKeyboard::Key::PunctuationPeriod("keyboard_key_punctuation_period");
const InputChannelId InputDeviceKeyboard::Key::PunctuationSemicolon("keyboard_key_punctuation_semicolon");
const InputChannelId InputDeviceKeyboard::Key::PunctuationSlash("keyboard_key_punctuation_slash");
const InputChannelId InputDeviceKeyboard::Key::PunctuationTilde("keyboard_key_punctuation_tilde");
////////////////////////////////////////////////////////////////////////////////////////////////
// Supplementary ISO Key
const InputChannelId InputDeviceKeyboard::Key::SupplementaryISO("keyboard_key_supplementary_iso");
////////////////////////////////////////////////////////////////////////////////////////////////
// Windows System Keys
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPause("keyboard_key_windows_system_pause");
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPrint("keyboard_key_windows_system_print");
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemScrollLock("keyboard_key_windows_system_scroll_lock");
////////////////////////////////////////////////////////////////////////////////////////////////
const AZStd::array<InputChannelId, 112> InputDeviceKeyboard::Key::All =
{{
// Alphanumeric Keys
Alphanumeric0,
Alphanumeric1,
Alphanumeric2,
Alphanumeric3,
Alphanumeric4,
Alphanumeric5,
Alphanumeric6,
Alphanumeric7,
Alphanumeric8,
Alphanumeric9,
AlphanumericA,
AlphanumericB,
AlphanumericC,
AlphanumericD,
AlphanumericE,
AlphanumericF,
AlphanumericG,
AlphanumericH,
AlphanumericI,
AlphanumericJ,
AlphanumericK,
AlphanumericL,
AlphanumericM,
AlphanumericN,
AlphanumericO,
AlphanumericP,
AlphanumericQ,
AlphanumericR,
AlphanumericS,
AlphanumericT,
AlphanumericU,
AlphanumericV,
AlphanumericW,
AlphanumericX,
AlphanumericY,
AlphanumericZ,
// Edit (and escape) Keys
EditBackspace,
EditCapsLock,
EditEnter,
EditSpace,
EditTab,
Escape,
// Function Keys
Function01,
Function02,
Function03,
Function04,
Function05,
Function06,
Function07,
Function08,
Function09,
Function10,
Function11,
Function12,
Function13,
Function14,
Function15,
Function16,
Function17,
Function18,
Function19,
Function20,
// Modifier Keys
ModifierAltL,
ModifierAltR,
ModifierCtrlL,
ModifierCtrlR,
ModifierShiftL,
ModifierShiftR,
ModifierSuperL,
ModifierSuperR,
// Navigation Keys
NavigationArrowDown,
NavigationArrowLeft,
NavigationArrowRight,
NavigationArrowUp,
NavigationDelete,
NavigationEnd,
NavigationHome,
NavigationInsert,
NavigationPageDown,
NavigationPageUp,
// Numpad Keys
NumLock,
NumPad0,
NumPad1,
NumPad2,
NumPad3,
NumPad4,
NumPad5,
NumPad6,
NumPad7,
NumPad8,
NumPad9,
NumPadAdd,
NumPadDecimal,
NumPadDivide,
NumPadEnter,
NumPadMultiply,
NumPadSubtract,
// Punctuation Keys
PunctuationApostrophe,
PunctuationBackslash,
PunctuationBracketL,
PunctuationBracketR,
PunctuationComma,
PunctuationEquals,
PunctuationHyphen,
PunctuationPeriod,
PunctuationSemicolon,
PunctuationSlash,
PunctuationTilde,
// Supplementary ISO Key
SupplementaryISO,
// Windows System Keys
WindowsSystemPause,
WindowsSystemPrint,
WindowsSystemScrollLock
}};
////////////////////////////////////////////////////////////////////////////////////////////////
ModifierKeyMask GetCorrespondingModifierKeyMask(const InputChannelId& channelId)
{
@@ -94,137 +94,268 @@ namespace AzFramework
struct Key
{
// Alphanumeric Keys
static const InputChannelId Alphanumeric0; //!< The 0 key
static const InputChannelId Alphanumeric1; //!< The 1 key
static const InputChannelId Alphanumeric2; //!< The 2 key
static const InputChannelId Alphanumeric3; //!< The 3 key
static const InputChannelId Alphanumeric4; //!< The 4 key
static const InputChannelId Alphanumeric5; //!< The 5 key
static const InputChannelId Alphanumeric6; //!< The 6 key
static const InputChannelId Alphanumeric7; //!< The 7 key
static const InputChannelId Alphanumeric8; //!< The 8 key
static const InputChannelId Alphanumeric9; //!< The 9 key
static const InputChannelId AlphanumericA; //!< The A key
static const InputChannelId AlphanumericB; //!< The B key
static const InputChannelId AlphanumericC; //!< The C key
static const InputChannelId AlphanumericD; //!< The D key
static const InputChannelId AlphanumericE; //!< The E key
static const InputChannelId AlphanumericF; //!< The F key
static const InputChannelId AlphanumericG; //!< The G key
static const InputChannelId AlphanumericH; //!< The H key
static const InputChannelId AlphanumericI; //!< The I key
static const InputChannelId AlphanumericJ; //!< The J key
static const InputChannelId AlphanumericK; //!< The K key
static const InputChannelId AlphanumericL; //!< The L key
static const InputChannelId AlphanumericM; //!< The M key
static const InputChannelId AlphanumericN; //!< The N key
static const InputChannelId AlphanumericO; //!< The O key
static const InputChannelId AlphanumericP; //!< The P key
static const InputChannelId AlphanumericQ; //!< The Q key
static const InputChannelId AlphanumericR; //!< The R key
static const InputChannelId AlphanumericS; //!< The S key
static const InputChannelId AlphanumericT; //!< The T key
static const InputChannelId AlphanumericU; //!< The U key
static const InputChannelId AlphanumericV; //!< The V key
static const InputChannelId AlphanumericW; //!< The W key
static const InputChannelId AlphanumericX; //!< The X key
static const InputChannelId AlphanumericY; //!< The Y key
static const InputChannelId AlphanumericZ; //!< The Z key
static constexpr inline InputChannelId Alphanumeric0{"keyboard_key_alphanumeric_0"}; //!< The 0 key
static constexpr inline InputChannelId Alphanumeric1{"keyboard_key_alphanumeric_1"}; //!< The 1 key
static constexpr inline InputChannelId Alphanumeric2{"keyboard_key_alphanumeric_2"}; //!< The 2 key
static constexpr inline InputChannelId Alphanumeric3{"keyboard_key_alphanumeric_3"}; //!< The 3 key
static constexpr inline InputChannelId Alphanumeric4{"keyboard_key_alphanumeric_4"}; //!< The 4 key
static constexpr inline InputChannelId Alphanumeric5{"keyboard_key_alphanumeric_5"}; //!< The 5 key
static constexpr inline InputChannelId Alphanumeric6{"keyboard_key_alphanumeric_6"}; //!< The 6 key
static constexpr inline InputChannelId Alphanumeric7{"keyboard_key_alphanumeric_7"}; //!< The 7 key
static constexpr inline InputChannelId Alphanumeric8{"keyboard_key_alphanumeric_8"}; //!< The 8 key
static constexpr inline InputChannelId Alphanumeric9{"keyboard_key_alphanumeric_9"}; //!< The 9 key
static constexpr inline InputChannelId AlphanumericA{"keyboard_key_alphanumeric_A"}; //!< The A key
static constexpr inline InputChannelId AlphanumericB{"keyboard_key_alphanumeric_B"}; //!< The B key
static constexpr inline InputChannelId AlphanumericC{"keyboard_key_alphanumeric_C"}; //!< The C key
static constexpr inline InputChannelId AlphanumericD{"keyboard_key_alphanumeric_D"}; //!< The D key
static constexpr inline InputChannelId AlphanumericE{"keyboard_key_alphanumeric_E"}; //!< The E key
static constexpr inline InputChannelId AlphanumericF{"keyboard_key_alphanumeric_F"}; //!< The F key
static constexpr inline InputChannelId AlphanumericG{"keyboard_key_alphanumeric_G"}; //!< The G key
static constexpr inline InputChannelId AlphanumericH{"keyboard_key_alphanumeric_H"}; //!< The H key
static constexpr inline InputChannelId AlphanumericI{"keyboard_key_alphanumeric_I"}; //!< The I key
static constexpr inline InputChannelId AlphanumericJ{"keyboard_key_alphanumeric_J"}; //!< The J key
static constexpr inline InputChannelId AlphanumericK{"keyboard_key_alphanumeric_K"}; //!< The K key
static constexpr inline InputChannelId AlphanumericL{"keyboard_key_alphanumeric_L"}; //!< The L key
static constexpr inline InputChannelId AlphanumericM{"keyboard_key_alphanumeric_M"}; //!< The M key
static constexpr inline InputChannelId AlphanumericN{"keyboard_key_alphanumeric_N"}; //!< The N key
static constexpr inline InputChannelId AlphanumericO{"keyboard_key_alphanumeric_O"}; //!< The O key
static constexpr inline InputChannelId AlphanumericP{"keyboard_key_alphanumeric_P"}; //!< The P key
static constexpr inline InputChannelId AlphanumericQ{"keyboard_key_alphanumeric_Q"}; //!< The Q key
static constexpr inline InputChannelId AlphanumericR{"keyboard_key_alphanumeric_R"}; //!< The R key
static constexpr inline InputChannelId AlphanumericS{"keyboard_key_alphanumeric_S"}; //!< The S key
static constexpr inline InputChannelId AlphanumericT{"keyboard_key_alphanumeric_T"}; //!< The T key
static constexpr inline InputChannelId AlphanumericU{"keyboard_key_alphanumeric_U"}; //!< The U key
static constexpr inline InputChannelId AlphanumericV{"keyboard_key_alphanumeric_V"}; //!< The V key
static constexpr inline InputChannelId AlphanumericW{"keyboard_key_alphanumeric_W"}; //!< The W key
static constexpr inline InputChannelId AlphanumericX{"keyboard_key_alphanumeric_X"}; //!< The X key
static constexpr inline InputChannelId AlphanumericY{"keyboard_key_alphanumeric_Y"}; //!< The Y key
static constexpr inline InputChannelId AlphanumericZ{"keyboard_key_alphanumeric_Z"}; //!< The Z key
// Edit (and escape) Keys
static const InputChannelId EditBackspace; //!< The backspace key
static const InputChannelId EditCapsLock; //!< The caps lock key
static const InputChannelId EditEnter; //!< The enter/return key
static const InputChannelId EditSpace; //!< The spacebar key
static const InputChannelId EditTab; //!< The tab key
static const InputChannelId Escape; //!< The escape key
// Edit {and escape} Keys
static constexpr inline InputChannelId EditBackspace{"keyboard_key_edit_backspace"}; //!< The backspace key
static constexpr inline InputChannelId EditCapsLock{"keyboard_key_edit_capslock"}; //!< The caps lock key
static constexpr inline InputChannelId EditEnter{"keyboard_key_edit_enter"}; //!< The enter/return key
static constexpr inline InputChannelId EditSpace{"keyboard_key_edit_space"}; //!< The spacebar key
static constexpr inline InputChannelId EditTab{"keyboard_key_edit_tab"}; //!< The tab key
static constexpr inline InputChannelId Escape{"keyboard_key_escape"}; //!< The escape key
// Function Keys
static const InputChannelId Function01; //!< The F1 key
static const InputChannelId Function02; //!< The F2 key
static const InputChannelId Function03; //!< The F3 key
static const InputChannelId Function04; //!< The F4 key
static const InputChannelId Function05; //!< The F5 key
static const InputChannelId Function06; //!< The F6 key
static const InputChannelId Function07; //!< The F7 key
static const InputChannelId Function08; //!< The F8 key
static const InputChannelId Function09; //!< The F9 key
static const InputChannelId Function10; //!< The F10 key
static const InputChannelId Function11; //!< The F11 key
static const InputChannelId Function12; //!< The F12 key
static const InputChannelId Function13; //!< The F13 key
static const InputChannelId Function14; //!< The F14 key
static const InputChannelId Function15; //!< The F15 key
static const InputChannelId Function16; //!< The F16 key
static const InputChannelId Function17; //!< The F17 key
static const InputChannelId Function18; //!< The F18 key
static const InputChannelId Function19; //!< The F19 key
static const InputChannelId Function20; //!< The F20 key
static constexpr inline InputChannelId Function01{"keyboard_key_function_F01"}; //!< The F1 key
static constexpr inline InputChannelId Function02{"keyboard_key_function_F02"}; //!< The F2 key
static constexpr inline InputChannelId Function03{"keyboard_key_function_F03"}; //!< The F3 key
static constexpr inline InputChannelId Function04{"keyboard_key_function_F04"}; //!< The F4 key
static constexpr inline InputChannelId Function05{"keyboard_key_function_F05"}; //!< The F5 key
static constexpr inline InputChannelId Function06{"keyboard_key_function_F06"}; //!< The F6 key
static constexpr inline InputChannelId Function07{"keyboard_key_function_F07"}; //!< The F7 key
static constexpr inline InputChannelId Function08{"keyboard_key_function_F08"}; //!< The F8 key
static constexpr inline InputChannelId Function09{"keyboard_key_function_F09"}; //!< The F9 key
static constexpr inline InputChannelId Function10{"keyboard_key_function_F10"}; //!< The F10 key
static constexpr inline InputChannelId Function11{"keyboard_key_function_F11"}; //!< The F11 key
static constexpr inline InputChannelId Function12{"keyboard_key_function_F12"}; //!< The F12 key
static constexpr inline InputChannelId Function13{"keyboard_key_function_F13"}; //!< The F13 key
static constexpr inline InputChannelId Function14{"keyboard_key_function_F14"}; //!< The F14 key
static constexpr inline InputChannelId Function15{"keyboard_key_function_F15"}; //!< The F15 key
static constexpr inline InputChannelId Function16{"keyboard_key_function_F16"}; //!< The F16 key
static constexpr inline InputChannelId Function17{"keyboard_key_function_F17"}; //!< The F17 key
static constexpr inline InputChannelId Function18{"keyboard_key_function_F18"}; //!< The F18 key
static constexpr inline InputChannelId Function19{"keyboard_key_function_F19"}; //!< The F19 key
static constexpr inline InputChannelId Function20{"keyboard_key_function_F20"}; //!< The F20 key
// Modifier Keys
static const InputChannelId ModifierAltL; //!< The left alt/option key
static const InputChannelId ModifierAltR; //!< The right alt/option key
static const InputChannelId ModifierCtrlL; //!< The left control key
static const InputChannelId ModifierCtrlR; //!< The right control key
static const InputChannelId ModifierShiftL; //!< The left shift key
static const InputChannelId ModifierShiftR; //!< The right shift key
static const InputChannelId ModifierSuperL; //!< The left super (windows or apple) key
static const InputChannelId ModifierSuperR; //!< The right super (windows or apple) key
static constexpr inline InputChannelId ModifierAltL{"keyboard_key_modifier_alt_l"}; //!< The left alt/option key
static constexpr inline InputChannelId ModifierAltR{"keyboard_key_modifier_alt_r"}; //!< The right alt/option key
static constexpr inline InputChannelId ModifierCtrlL{"keyboard_key_modifier_ctrl_l"}; //!< The left control key
static constexpr inline InputChannelId ModifierCtrlR{"keyboard_key_modifier_ctrl_r"}; //!< The right control key
static constexpr inline InputChannelId ModifierShiftL{"keyboard_key_modifier_shift_l"}; //!< The left shift key
static constexpr inline InputChannelId ModifierShiftR{"keyboard_key_modifier_shift_r"}; //!< The right shift key
static constexpr inline InputChannelId ModifierSuperL{"keyboard_key_modifier_super_l"}; //!< The left super {windows or apple} key
static constexpr inline InputChannelId ModifierSuperR{"keyboard_key_modifier_super_r"}; //!< The right super {windows or apple} key
// Navigation Keys
static const InputChannelId NavigationArrowDown; //!< The down arrow key
static const InputChannelId NavigationArrowLeft; //!< The left arrow key
static const InputChannelId NavigationArrowRight; //!< The right arrow key
static const InputChannelId NavigationArrowUp; //!< The up arrow key
static const InputChannelId NavigationDelete; //!< The delete key
static const InputChannelId NavigationEnd; //!< The end key
static const InputChannelId NavigationHome; //!< The home key
static const InputChannelId NavigationInsert; //!< The insert key
static const InputChannelId NavigationPageDown; //!< The page down key
static const InputChannelId NavigationPageUp; //!< The page up key
static constexpr inline InputChannelId NavigationArrowDown{"keyboard_key_navigation_arrow_down"}; //!< The down arrow key
static constexpr inline InputChannelId NavigationArrowLeft{"keyboard_key_navigation_arrow_left"}; //!< The left arrow key
static constexpr inline InputChannelId NavigationArrowRight{"keyboard_key_navigation_arrow_right"}; //!< The right arrow key
static constexpr inline InputChannelId NavigationArrowUp{"keyboard_key_navigation_arrow_up"}; //!< The up arrow key
static constexpr inline InputChannelId NavigationDelete{"keyboard_key_navigation_delete"}; //!< The delete key
static constexpr inline InputChannelId NavigationEnd{"keyboard_key_navigation_end"}; //!< The end key
static constexpr inline InputChannelId NavigationHome{"keyboard_key_navigation_home"}; //!< The home key
static constexpr inline InputChannelId NavigationInsert{"keyboard_key_navigation_insert"}; //!< The insert key
static constexpr inline InputChannelId NavigationPageDown{"keyboard_key_navigation_page_down"}; //!< The page down key
static constexpr inline InputChannelId NavigationPageUp{"keyboard_key_navigation_page_up"}; //!< The page up key
// Numpad Keys
static const InputChannelId NumLock; //!< The num lock key (the clear key on apple keyboards)
static const InputChannelId NumPad0; //!< The numpad 0 key
static const InputChannelId NumPad1; //!< The numpad 1 key
static const InputChannelId NumPad2; //!< The numpad 2 key
static const InputChannelId NumPad3; //!< The numpad 3 key
static const InputChannelId NumPad4; //!< The numpad 4 key
static const InputChannelId NumPad5; //!< The numpad 5 key
static const InputChannelId NumPad6; //!< The numpad 6 key
static const InputChannelId NumPad7; //!< The numpad 7 key
static const InputChannelId NumPad8; //!< The numpad 8 key
static const InputChannelId NumPad9; //!< The numpad 9 key
static const InputChannelId NumPadAdd; //!< The numpad add key
static const InputChannelId NumPadDecimal; //!< The numpad decimal key
static const InputChannelId NumPadDivide; //!< The numpad divide key
static const InputChannelId NumPadEnter; //!< The numpad enter key
static const InputChannelId NumPadMultiply; //!< The numpad multiply key
static const InputChannelId NumPadSubtract; //!< The numpad subtract key
static constexpr inline InputChannelId NumLock{"keyboard_key_num_lock"}; //!< The num lock key {the clear key on apple keyboards}
static constexpr inline InputChannelId NumPad0{"keyboard_key_numpad_0"}; //!< The numpad 0 key
static constexpr inline InputChannelId NumPad1{"keyboard_key_numpad_1"}; //!< The numpad 1 key
static constexpr inline InputChannelId NumPad2{"keyboard_key_numpad_2"}; //!< The numpad 2 key
static constexpr inline InputChannelId NumPad3{"keyboard_key_numpad_3"}; //!< The numpad 3 key
static constexpr inline InputChannelId NumPad4{"keyboard_key_numpad_4"}; //!< The numpad 4 key
static constexpr inline InputChannelId NumPad5{"keyboard_key_numpad_5"}; //!< The numpad 5 key
static constexpr inline InputChannelId NumPad6{"keyboard_key_numpad_6"}; //!< The numpad 6 key
static constexpr inline InputChannelId NumPad7{"keyboard_key_numpad_7"}; //!< The numpad 7 key
static constexpr inline InputChannelId NumPad8{"keyboard_key_numpad_8"}; //!< The numpad 8 key
static constexpr inline InputChannelId NumPad9{"keyboard_key_numpad_9"}; //!< The numpad 9 key
static constexpr inline InputChannelId NumPadAdd{"keyboard_key_numpad_add"}; //!< The numpad add key
static constexpr inline InputChannelId NumPadDecimal{"keyboard_key_numpad_decimal"}; //!< The numpad decimal key
static constexpr inline InputChannelId NumPadDivide{"keyboard_key_numpad_divide"}; //!< The numpad divide key
static constexpr inline InputChannelId NumPadEnter{"keyboard_key_numpad_enter"}; //!< The numpad enter key
static constexpr inline InputChannelId NumPadMultiply{"keyboard_key_numpad_multiply"}; //!< The numpad multiply key
static constexpr inline InputChannelId NumPadSubtract{"keyboard_key_numpad_subtract"}; //!< The numpad subtract key
// Punctuation Keys
static const InputChannelId PunctuationApostrophe; //!< The apostrophe key
static const InputChannelId PunctuationBackslash; //!< The backslash key
static const InputChannelId PunctuationBracketL; //!< The left bracket key
static const InputChannelId PunctuationBracketR; //!< The right bracket key
static const InputChannelId PunctuationComma; //!< The comma key
static const InputChannelId PunctuationEquals; //!< The equals key
static const InputChannelId PunctuationHyphen; //!< The hyphen/underscore key
static const InputChannelId PunctuationPeriod; //!< The period key
static const InputChannelId PunctuationSemicolon; //!< The semicolon key
static const InputChannelId PunctuationSlash; //!< The (forward) slash key
static const InputChannelId PunctuationTilde; //!< The tilde/grave key
static constexpr inline InputChannelId PunctuationApostrophe{"keyboard_key_punctuation_apostrophe"}; //!< The apostrophe key
static constexpr inline InputChannelId PunctuationBackslash{"keyboard_key_punctuation_backslash"}; //!< The backslash key
static constexpr inline InputChannelId PunctuationBracketL{"keyboard_key_punctuation_bracket_l"}; //!< The left bracket key
static constexpr inline InputChannelId PunctuationBracketR{"keyboard_key_punctuation_bracket_r"}; //!< The right bracket key
static constexpr inline InputChannelId PunctuationComma{"keyboard_key_punctuation_comma"}; //!< The comma key
static constexpr inline InputChannelId PunctuationEquals{"keyboard_key_punctuation_equals"}; //!< The equals key
static constexpr inline InputChannelId PunctuationHyphen{"keyboard_key_punctuation_hyphen"}; //!< The hyphen/underscore key
static constexpr inline InputChannelId PunctuationPeriod{"keyboard_key_punctuation_period"}; //!< The period key
static constexpr inline InputChannelId PunctuationSemicolon{"keyboard_key_punctuation_semicolon"}; //!< The semicolon key
static constexpr inline InputChannelId PunctuationSlash{"keyboard_key_punctuation_slash"}; //!< The {forward} slash key
static constexpr inline InputChannelId PunctuationTilde{"keyboard_key_punctuation_tilde"}; //!< The tilde/grave key
// Supplementary ISO Key
static const InputChannelId SupplementaryISO; //!< The supplementary ISO layout key
static constexpr inline InputChannelId SupplementaryISO{"keyboard_key_supplementary_iso"}; //!< The supplementary ISO layout key
// Windows System Keys
static const InputChannelId WindowsSystemPause; //!< The windows pause key
static const InputChannelId WindowsSystemPrint; //!< The windows print key
static const InputChannelId WindowsSystemScrollLock; //!< The windows scroll lock key
static constexpr inline InputChannelId WindowsSystemPause{"keyboard_key_windows_system_pause"}; //!< The windows pause key
static constexpr inline InputChannelId WindowsSystemPrint{"keyboard_key_windows_system_print"}; //!< The windows print key
static constexpr inline InputChannelId WindowsSystemScrollLock{"keyboard_key_windows_system_scroll_lock"}; //!< The windows scroll lock key
//!< All keyboard key ids
static const AZStd::array<InputChannelId, 112> All;
static constexpr inline AZStd::array All
{
// Alphanumeric Keys
Alphanumeric0,
Alphanumeric1,
Alphanumeric2,
Alphanumeric3,
Alphanumeric4,
Alphanumeric5,
Alphanumeric6,
Alphanumeric7,
Alphanumeric8,
Alphanumeric9,
AlphanumericA,
AlphanumericB,
AlphanumericC,
AlphanumericD,
AlphanumericE,
AlphanumericF,
AlphanumericG,
AlphanumericH,
AlphanumericI,
AlphanumericJ,
AlphanumericK,
AlphanumericL,
AlphanumericM,
AlphanumericN,
AlphanumericO,
AlphanumericP,
AlphanumericQ,
AlphanumericR,
AlphanumericS,
AlphanumericT,
AlphanumericU,
AlphanumericV,
AlphanumericW,
AlphanumericX,
AlphanumericY,
AlphanumericZ,
// Edit (and escape) Keys
EditBackspace,
EditCapsLock,
EditEnter,
EditSpace,
EditTab,
Escape,
// Function Keys
Function01,
Function02,
Function03,
Function04,
Function05,
Function06,
Function07,
Function08,
Function09,
Function10,
Function11,
Function12,
Function13,
Function14,
Function15,
Function16,
Function17,
Function18,
Function19,
Function20,
// Modifier Keys
ModifierAltL,
ModifierAltR,
ModifierCtrlL,
ModifierCtrlR,
ModifierShiftL,
ModifierShiftR,
ModifierSuperL,
ModifierSuperR,
// Navigation Keys
NavigationArrowDown,
NavigationArrowLeft,
NavigationArrowRight,
NavigationArrowUp,
NavigationDelete,
NavigationEnd,
NavigationHome,
NavigationInsert,
NavigationPageDown,
NavigationPageUp,
// Numpad Keys
NumLock,
NumPad0,
NumPad1,
NumPad2,
NumPad3,
NumPad4,
NumPad5,
NumPad6,
NumPad7,
NumPad8,
NumPad9,
NumPadAdd,
NumPadDecimal,
NumPadDivide,
NumPadEnter,
NumPadMultiply,
NumPadSubtract,
// Punctuation Keys
PunctuationApostrophe,
PunctuationBackslash,
PunctuationBracketL,
PunctuationBracketR,
PunctuationComma,
PunctuationEquals,
PunctuationHyphen,
PunctuationPeriod,
PunctuationSemicolon,
PunctuationSlash,
PunctuationTilde,
// Supplementary ISO Key
SupplementaryISO,
// Windows System Keys
WindowsSystemPause,
WindowsSystemPrint,
WindowsSystemScrollLock
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -23,44 +23,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::Acceleration::Gravity("motion_acceleration_gravity");
const InputChannelId InputDeviceMotion::Acceleration::Raw("motion_acceleration_raw");
const InputChannelId InputDeviceMotion::Acceleration::User("motion_acceleration_user");
const AZStd::array<InputChannelId, 3> InputDeviceMotion::Acceleration::All =
{{
Gravity,
Raw,
User
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::RotationRate::Raw("motion_rotation_rate_raw");
const InputChannelId InputDeviceMotion::RotationRate::Unbiased("motion_rotation_rate_unbiased");
const AZStd::array<InputChannelId, 2> InputDeviceMotion::RotationRate::All =
{{
Raw,
Unbiased
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::MagneticField::North("motion_magnetic_field_north");
const InputChannelId InputDeviceMotion::MagneticField::Raw("motion_magnetic_field_raw");
const InputChannelId InputDeviceMotion::MagneticField::Unbiased("motion_magnetic_field_unbiased");
const AZStd::array<InputChannelId, 3> InputDeviceMotion::MagneticField::All =
{{
North,
Raw,
Unbiased
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::Orientation::Current("motion_orientation_current");
const AZStd::array<InputChannelId, 1> InputDeviceMotion::Orientation::All =
{{
Current
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::Reflect(AZ::ReflectContext* context)
{
@@ -44,12 +44,17 @@ namespace AzFramework
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct Acceleration
{
static const InputChannelId Gravity;
static const InputChannelId Raw;
static const InputChannelId User;
static constexpr inline InputChannelId Gravity{"motion_acceleration_gravity"};
static constexpr inline InputChannelId Raw{"motion_acceleration_raw"};
static constexpr inline InputChannelId User{"motion_acceleration_user"};
//!< All acceleration input channel ids
static const AZStd::array<InputChannelId, 3> All;
static constexpr inline AZStd::array All
{
Gravity,
Raw,
User
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -60,11 +65,15 @@ namespace AzFramework
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct RotationRate
{
static const InputChannelId Raw;
static const InputChannelId Unbiased;
static constexpr inline InputChannelId Raw{"motion_rotation_rate_raw"};
static constexpr inline InputChannelId Unbiased{"motion_rotation_rate_unbiased"};
//!< All rotation rate input channel ids
static const AZStd::array<InputChannelId, 2> All;
static constexpr inline AZStd::array All
{
Raw,
Unbiased
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -75,12 +84,17 @@ namespace AzFramework
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct MagneticField
{
static const InputChannelId North;
static const InputChannelId Raw;
static const InputChannelId Unbiased;
static constexpr inline InputChannelId North{"motion_magnetic_field_north"};
static constexpr inline InputChannelId Raw{"motion_magnetic_field_raw"};
static constexpr inline InputChannelId Unbiased{"motion_magnetic_field_unbiased"};
//!< All magnetic field input channel ids
static const AZStd::array<InputChannelId, 3> All;
static constexpr inline AZStd::array All
{
North,
Raw,
Unbiased
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -91,10 +105,13 @@ namespace AzFramework
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct Orientation
{
static const InputChannelId Current;
static constexpr inline InputChannelId Current{"motion_orientation_current"};
//!< All orientation input channel ids
static const AZStd::array<InputChannelId, 1> All;
static constexpr inline AZStd::array All
{
Current
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,35 +33,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::Button::Left("mouse_button_left");
const InputChannelId InputDeviceMouse::Button::Right("mouse_button_right");
const InputChannelId InputDeviceMouse::Button::Middle("mouse_button_middle");
const InputChannelId InputDeviceMouse::Button::Other1("mouse_button_other1");
const InputChannelId InputDeviceMouse::Button::Other2("mouse_button_other2");
const AZStd::array<InputChannelId, 5> InputDeviceMouse::Button::All =
{{
Left,
Right,
Middle,
Other1,
Other2
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::Movement::X("mouse_delta_x");
const InputChannelId InputDeviceMouse::Movement::Y("mouse_delta_y");
const InputChannelId InputDeviceMouse::Movement::Z("mouse_delta_z");
const AZStd::array<InputChannelId, 3> InputDeviceMouse::Movement::All =
{{
X,
Y,
Z
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::SystemCursorPosition("mouse_system_cursor_position");
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::Reflect(AZ::ReflectContext* context)
{
@@ -66,14 +66,21 @@ namespace AzFramework
//! been implemented for windows simply to provide for backwards compatibility with CryInput.
struct Button
{
static const InputChannelId Left; //!< The left mouse button
static const InputChannelId Right; //!< The right mouse button
static const InputChannelId Middle; //!< The middle mouse button
static const InputChannelId Other1; //!< DEPRECATED: the x1 mouse button
static const InputChannelId Other2; //!< DEPRECATED: the x2 mouse button
static constexpr inline InputChannelId Left{"mouse_button_left"}; //!< The left mouse button
static constexpr inline InputChannelId Right{"mouse_button_right"}; //!< The right mouse button
static constexpr inline InputChannelId Middle{"mouse_button_middle"}; //!< The middle mouse button
static constexpr inline InputChannelId Other1{"mouse_button_other1"}; //!< DEPRECATED: the x1 mouse button
static constexpr inline InputChannelId Other2{"mouse_button_other2"}; //!< DEPRECATED: the x2 mouse button
//!< All mouse button ids
static const AZStd::array<InputChannelId, 5> All;
static constexpr inline AZStd::array All
{
Left,
Right,
Middle,
Other1,
Other2
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -82,12 +89,17 @@ namespace AzFramework
//! directly correlate to the mouse position (which is queried directly from the system).
struct Movement
{
static const InputChannelId X; //!< Raw horizontal mouse movement over the last frame
static const InputChannelId Y; //!< Raw vertical mouse movement over the last frame
static const InputChannelId Z; //!< Raw mouse wheel movement over the last frame
static constexpr inline InputChannelId X{"mouse_delta_x"}; //!< Raw horizontal mouse movement over the last frame
static constexpr inline InputChannelId Y{"mouse_delta_y"}; //!< Raw vertical mouse movement over the last frame
static constexpr inline InputChannelId Z{"mouse_delta_z"}; //!< Raw mouse wheel movement over the last frame
//!< All mouse movement ids
static const AZStd::array<InputChannelId, 3> All;
static constexpr inline AZStd::array All
{
X,
Y,
Z
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -96,7 +108,7 @@ namespace AzFramework
//! the system cursor is hidden or visible. When the system cursor has been constrained to
//! the active window values will be in the [0.0, 1.0] range, but not when unconstrained.
//! See also InputSystemCursorRequests::SetSystemCursorState and GetSystemCursorState.
static const InputChannelId SystemCursorPosition;
static constexpr inline InputChannelId SystemCursorPosition{"mouse_system_cursor_position"};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
@@ -24,31 +24,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceTouch::Touch::Index0("touch_index_0");
const InputChannelId InputDeviceTouch::Touch::Index1("touch_index_1");
const InputChannelId InputDeviceTouch::Touch::Index2("touch_index_2");
const InputChannelId InputDeviceTouch::Touch::Index3("touch_index_3");
const InputChannelId InputDeviceTouch::Touch::Index4("touch_index_4");
const InputChannelId InputDeviceTouch::Touch::Index5("touch_index_5");
const InputChannelId InputDeviceTouch::Touch::Index6("touch_index_6");
const InputChannelId InputDeviceTouch::Touch::Index7("touch_index_7");
const InputChannelId InputDeviceTouch::Touch::Index8("touch_index_8");
const InputChannelId InputDeviceTouch::Touch::Index9("touch_index_9");
const AZStd::array<InputChannelId, 10> InputDeviceTouch::Touch::All =
{{
Index0,
Index1,
Index2,
Index3,
Index4,
Index5,
Index6,
Index7,
Index8,
Index9
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceTouch::Reflect(AZ::ReflectContext* context)
{
@@ -38,19 +38,31 @@ namespace AzFramework
//! track is arbitrary, but ten seems to be more than sufficient for most game applications.
struct Touch
{
static const InputChannelId Index0; //!< Touch index 0
static const InputChannelId Index1; //!< Touch index 1
static const InputChannelId Index2; //!< Touch index 2
static const InputChannelId Index3; //!< Touch index 3
static const InputChannelId Index4; //!< Touch index 4
static const InputChannelId Index5; //!< Touch index 5
static const InputChannelId Index6; //!< Touch index 6
static const InputChannelId Index7; //!< Touch index 7
static const InputChannelId Index8; //!< Touch index 8
static const InputChannelId Index9; //!< Touch index 9
static constexpr inline InputChannelId Index0{"touch_index_0"}; //!< Touch index 0
static constexpr inline InputChannelId Index1{"touch_index_1"}; //!< Touch index 1
static constexpr inline InputChannelId Index2{"touch_index_2"}; //!< Touch index 2
static constexpr inline InputChannelId Index3{"touch_index_3"}; //!< Touch index 3
static constexpr inline InputChannelId Index4{"touch_index_4"}; //!< Touch index 4
static constexpr inline InputChannelId Index5{"touch_index_5"}; //!< Touch index 5
static constexpr inline InputChannelId Index6{"touch_index_6"}; //!< Touch index 6
static constexpr inline InputChannelId Index7{"touch_index_7"}; //!< Touch index 7
static constexpr inline InputChannelId Index8{"touch_index_8"}; //!< Touch index 8
static constexpr inline InputChannelId Index9{"touch_index_9"}; //!< Touch index 9
//!< All touch input channel ids
static const AZStd::array<InputChannelId, 10> All;
static constexpr inline AZStd::array All
{
Index0,
Index1,
Index2,
Index3,
Index4,
Index5,
Index6,
Index7,
Index8,
Index9
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -23,17 +23,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceVirtualKeyboard::Command::EditEnter("virtual_keyboard_edit_enter");
const InputChannelId InputDeviceVirtualKeyboard::Command::EditClear("virtual_keyboard_edit_clear");
const InputChannelId InputDeviceVirtualKeyboard::Command::NavigationBack("virtual_keyboard_navigation_back");
const AZStd::array<InputChannelId, 3> InputDeviceVirtualKeyboard::Command::All =
{{
EditClear,
EditEnter,
NavigationBack
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::Reflect(AZ::ReflectContext* context)
{
@@ -39,17 +39,22 @@ namespace AzFramework
struct Command
{
//!< The clear command used to indicate the user wants to clear the active text field
static const InputChannelId EditClear;
static constexpr inline InputChannelId EditClear{"virtual_keyboard_edit_enter"};
//!< The enter/return/close command used to indicate the user has finished text editing
static const InputChannelId EditEnter;
static constexpr inline InputChannelId EditEnter{"virtual_keyboard_edit_clear"};
//!< The back command used to indicate the user wants to navigate 'backwards'.
//!< This is specific to android devices, and does not have an ios equivalent.
static const InputChannelId NavigationBack;
static constexpr inline InputChannelId NavigationBack{"virtual_keyboard_navigation_back"};
//!< All virtual keyboard command ids
static const AZStd::array<InputChannelId, 3> All;
static constexpr inline AZStd::array All
{
EditClear,
EditEnter,
NavigationBack
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -283,8 +283,12 @@ namespace AzFramework
: m_payload(rhs.m_payload)
, m_id(rhs.m_id)
{
auto manager = SpawnableEntitiesInterface::Get();
AZ_Assert(manager, "SpawnableEntitiesInterface has no implementation.");
rhs.m_payload = nullptr;
rhs.m_id = 0;
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
manager->m_entitySpawnTicketMap.insert_or_assign(rhs.m_id, this);
}
EntitySpawnTicket::EntitySpawnTicket(AZ::Data::Asset<Spawnable> spawnable)
@@ -294,6 +298,8 @@ namespace AzFramework
AZStd::pair<EntitySpawnTicket::Id, void*> result = manager->CreateTicket(AZStd::move(spawnable));
m_id = result.first;
m_payload = result.second;
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
manager->m_entitySpawnTicketMap.insert_or_assign(m_id, this);
}
EntitySpawnTicket::~EntitySpawnTicket()
@@ -304,6 +310,8 @@ namespace AzFramework
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
manager->DestroyTicket(m_payload);
m_payload = nullptr;
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
manager->m_entitySpawnTicketMap.erase(m_id);
m_id = 0;
}
}
@@ -312,17 +320,23 @@ namespace AzFramework
{
if (this != &rhs)
{
auto manager = SpawnableEntitiesInterface::Get();
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
if (m_payload)
{
auto manager = SpawnableEntitiesInterface::Get();
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
manager->DestroyTicket(m_payload);
}
Id previousId = m_id;
m_id = rhs.m_id;
rhs.m_id = 0;
m_payload = rhs.m_payload;
rhs.m_payload = nullptr;
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
manager->m_entitySpawnTicketMap.erase(previousId);
manager->m_entitySpawnTicketMap.insert_or_assign(m_id, this);
}
return *this;
}
@@ -154,7 +154,7 @@ namespace AzFramework
public:
friend class SpawnableEntitiesDefinition;
using Id = uint64_t;
using Id = uint32_t;
EntitySpawnTicket() = default;
EntitySpawnTicket(const EntitySpawnTicket&) = delete;
@@ -176,6 +176,7 @@ namespace AzFramework
using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableEntityContainerView)>;
using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket::Id)>;
using RetrieveEntitySpawnTicketCallback = AZStd::function<void(EntitySpawnTicket*)>;
using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using ListIndicesEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstIndexEntityContainerView)>;
@@ -220,12 +221,21 @@ namespace AzFramework
struct DespawnAllEntitiesOptionalArgs final
{
//! Callback that's called when despawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to despawn. The returned list of entities contains all the newly created entities.
//! made the function call to despawn.
EntityDespawnCallback m_completionCallback;
//! The priority at which this call will be executed.
SpawnablePriority m_priority { SpawnablePriority_Default };
};
struct DespawnEntityOptionalArgs final
{
//! Callback that's called when despawning entity has completed. This can be triggered from a different thread than the one that
//! made the function call to despawn.
EntityDespawnCallback m_completionCallback;
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
};
struct ReloadSpawnableOptionalArgs final
{
//! Callback that's called when respawning entities has completed. This can be triggered from a different thread than the one that
@@ -291,9 +301,17 @@ namespace AzFramework
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Removes all entities in the provided list from the environment.
//! @param ticket The ticket previously used to spawn entities with.
//! @param priority The priority at which this call will be executed.
//! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs.
virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Removes the entity with the provided id from the spawned list of entities.
//! @param entityId the id of entity to despawn.
//! @param ticket The ticket previously used to spawn entities with.
//! @param optionalArgs Optional additional arguments, see DespawnEntityOptionalArgs.
virtual void DespawnEntity(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs = {}) = 0;
//! Gets the EntitySpawnTicket associated with the entitySpawnTicketId.
//! @param entitySpawnTicketId the id of EntitySpawnTicket to get.
//! @param callback The callback to execute upon retrieving the ticket.
virtual void RetrieveEntitySpawnTicket(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback) = 0;
//! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable.
//! @param ticket Holds the information on the entities to reload.
//! @param priority The priority at which this call will be executed.
@@ -361,6 +379,9 @@ namespace AzFramework
{
return reinterpret_cast<const T*>(ticket->m_payload);
}
AZStd::unordered_map<EntitySpawnTicket::Id, EntitySpawnTicket*> m_entitySpawnTicketMap;
AZStd::recursive_mutex m_entitySpawnTicketMapMutex;
};
using SpawnableEntitiesInterface = AZ::Interface<SpawnableEntitiesDefinition>;
@@ -85,6 +85,35 @@ namespace AzFramework
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::DespawnEntity(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs)
{
AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnEntity hasn't been initialized.");
DespawnEntityCommand queueEntry;
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_entityId = entityId;
queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback);
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::RetrieveEntitySpawnTicket(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback)
{
if (entitySpawnTicketId == 0)
{
AZ_Assert(false, "Ticket id provided to RetrieveEntitySpawnTicket is invalid.");
return;
}
AZStd::scoped_lock lock(m_entitySpawnTicketMapMutex);
auto entitySpawnTicketIterator = m_entitySpawnTicketMap.find(entitySpawnTicketId);
if (entitySpawnTicketIterator == m_entitySpawnTicketMap.end())
{
AZ_Assert(false, "The EntitySpawnTicket corresponding to id '%lu' cannot be found", entitySpawnTicketId);
return;
}
callback(entitySpawnTicketIterator->second);
}
void SpawnableEntitiesManager::ReloadSpawnable(
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs)
{
@@ -223,12 +252,13 @@ namespace AzFramework
return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft;
}
AZStd::pair<uint64_t, void*> SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable)
AZStd::pair<EntitySpawnTicket::Id, void*> SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable)
{
static AZStd::atomic_uint64_t idCounter { 1 };
static AZStd::atomic_uint32_t idCounter { 1 };
auto result = aznew Ticket();
result->m_spawnable = AZStd::move(spawnable);
return AZStd::make_pair<EntitySpawnTicket::Id, void*>(idCounter++, result);
}
@@ -339,6 +369,7 @@ namespace AzFramework
// Add to the game context, now the entities are active
for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it)
{
(*it)->SetSpawnTicketId(request.m_ticketId);
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
}
@@ -420,6 +451,7 @@ namespace AzFramework
// Add to the game context, now the entities are active
for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it)
{
(*it)->SetSpawnTicketId(request.m_ticketId);
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
}
@@ -447,8 +479,10 @@ namespace AzFramework
{
if (entity != nullptr)
{
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
entity->SetSpawnTicketId(0);
GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId());
&GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
}
}
@@ -469,6 +503,40 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(DespawnEntityCommand& request)
{
Ticket& ticket = *request.m_ticket;
if (request.m_requestId == ticket.m_currentRequestId)
{
AZStd::vector<AZ::Entity*>& spawnedEntities = request.m_ticket->m_spawnedEntities;
for (auto entityIterator = spawnedEntities.begin(); entityIterator != spawnedEntities.end(); ++entityIterator)
{
if (*entityIterator != nullptr && (*entityIterator)->GetId() == request.m_entityId)
{
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
(*entityIterator)->SetSpawnTicketId(0);
GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntity, (*entityIterator)->GetId());
AZStd::iter_swap(entityIterator, spawnedEntities.rbegin());
spawnedEntities.pop_back();
break;
}
}
if (request.m_completionCallback)
{
request.m_completionCallback(request.m_ticketId);
}
ticket.m_currentRequestId++;
return true;
}
else
{
return false;
}
}
bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request)
{
Ticket& ticket = *request.m_ticket;
@@ -482,8 +550,10 @@ namespace AzFramework
{
if (entity != nullptr)
{
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
entity->SetSpawnTicketId(0);
GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId());
&GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
}
}
@@ -636,8 +706,10 @@ namespace AzFramework
{
if (entity != nullptr)
{
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
entity->SetSpawnTicketId(0);
GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId());
&GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
}
}
delete request.m_ticket;
@@ -57,6 +57,8 @@ namespace AzFramework
void SpawnEntities(
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override;
void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override;
void DespawnEntity(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs = {}) override;
void RetrieveEntitySpawnTicket(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback) override;
void ReloadSpawnable(
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override;
@@ -132,6 +134,14 @@ namespace AzFramework
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct DespawnEntityCommand
{
EntityDespawnCallback m_completionCallback;
Ticket* m_ticket;
AZ::EntityId m_entityId;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct ReloadSpawnableCommand
{
AZ::Data::Asset<Spawnable> m_spawnable;
@@ -176,8 +186,16 @@ namespace AzFramework
};
using Requests = AZStd::variant<
SpawnAllEntitiesCommand, SpawnEntitiesCommand, DespawnAllEntitiesCommand, ReloadSpawnableCommand, ListEntitiesCommand,
ListIndicesEntitiesCommand, ClaimEntitiesCommand, BarrierCommand, DestroyTicketCommand>;
SpawnAllEntitiesCommand,
SpawnEntitiesCommand,
DespawnAllEntitiesCommand,
DespawnEntityCommand,
ReloadSpawnableCommand,
ListEntitiesCommand,
ListIndicesEntitiesCommand,
ClaimEntitiesCommand,
BarrierCommand,
DestroyTicketCommand>;
struct Queue
{
@@ -199,6 +217,7 @@ namespace AzFramework
bool ProcessRequest(SpawnAllEntitiesCommand& request);
bool ProcessRequest(SpawnEntitiesCommand& request);
bool ProcessRequest(DespawnAllEntitiesCommand& request);
bool ProcessRequest(DespawnEntityCommand& request);
bool ProcessRequest(ReloadSpawnableCommand& request);
bool ProcessRequest(ListEntitiesCommand& request);
bool ProcessRequest(ListIndicesEntitiesCommand& request);
@@ -17,15 +17,6 @@
namespace AzFramework
{
AZ_CVAR(
float,
ed_cameraSystemDefaultPlaneHeight,
34.0f,
nullptr,
AZ::ConsoleFunctorFlags::Null,
"The default height of the ground plane to do intersection tests against when orbiting");
AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
bool,
ed_cameraSystemUseCursor,
@@ -135,8 +126,8 @@ namespace AzFramework
camera.m_pitch = eulerAngles.GetX();
camera.m_yaw = eulerAngles.GetZ();
// note: m_lookDist is negative so we must invert it here
camera.m_lookAt = transform.GetTranslation() + (camera.Rotation().GetBasisY() * -camera.m_lookDist);
camera.m_pivot = transform.GetTranslation();
camera.m_offset = AZ::Vector3::CreateZero();
}
bool CameraSystem::HandleEvents(const InputEvent& event)
@@ -320,14 +311,8 @@ namespace AzFramework
nextCamera.m_pitch -= float(cursorDelta.m_y) * rotateSpeed * Invert(m_invertPitchFn());
nextCamera.m_yaw -= float(cursorDelta.m_x) * rotateSpeed * Invert(m_invertYawFn());
const auto clampRotation = [](const float angle)
{
return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi);
};
nextCamera.m_yaw = clampRotation(nextCamera.m_yaw);
// clamp pitch to be +/-90 degrees
nextCamera.m_pitch = AZ::GetClamp(nextCamera.m_pitch, -AZ::Constants::HalfPi, AZ::Constants::HalfPi);
nextCamera.m_yaw = WrapYawRotation(nextCamera.m_yaw);
nextCamera.m_pitch = ClampPitchRotation(nextCamera.m_pitch);
return nextCamera;
}
@@ -337,9 +322,10 @@ namespace AzFramework
m_rotateChannelId = rotateChannelId;
}
PanCameraInput::PanCameraInput(const InputChannelId& panChannelId, PanAxesFn panAxesFn)
PanCameraInput::PanCameraInput(const InputChannelId& panChannelId, PanAxesFn panAxesFn, TranslationDeltaFn translationDeltaFn)
: m_panAxesFn(AZStd::move(panAxesFn))
, m_panChannelId(panChannelId)
, m_translationDeltaFn(translationDeltaFn)
{
m_panSpeedFn = []() constexpr
{
@@ -375,11 +361,11 @@ namespace AzFramework
const auto panAxes = m_panAxesFn(nextCamera);
const float panSpeed = m_panSpeedFn();
const auto deltaPanX = float(cursorDelta.m_x) * panAxes.m_horizontalAxis * panSpeed;
const auto deltaPanY = float(cursorDelta.m_y) * panAxes.m_verticalAxis * panSpeed;
const auto deltaPanX = aznumeric_cast<float>(cursorDelta.m_x) * panAxes.m_horizontalAxis * panSpeed;
const auto deltaPanY = aznumeric_cast<float>(cursorDelta.m_y) * panAxes.m_verticalAxis * panSpeed;
nextCamera.m_lookAt += deltaPanX * Invert(m_invertPanXFn());
nextCamera.m_lookAt += deltaPanY * -Invert(m_invertPanYFn());
m_translationDeltaFn(nextCamera, deltaPanX * Invert(m_invertPanXFn()));
m_translationDeltaFn(nextCamera, deltaPanY * -Invert(m_invertPanYFn()));
return nextCamera;
}
@@ -426,8 +412,11 @@ namespace AzFramework
}
TranslateCameraInput::TranslateCameraInput(
TranslationAxesFn translationAxesFn, const TranslateCameraInputChannelIds& translateCameraInputChannelIds)
const TranslateCameraInputChannelIds& translateCameraInputChannelIds,
TranslationAxesFn translationAxesFn,
TranslationDeltaFn translateDeltaFn)
: m_translationAxesFn(AZStd::move(translationAxesFn))
, m_translateDeltaFn(AZStd::move(translateDeltaFn))
, m_translateCameraInputChannelIds(translateCameraInputChannelIds)
{
m_translateSpeedFn = []() constexpr
@@ -497,32 +486,32 @@ namespace AzFramework
if ((m_translation & TranslationType::Forward) == TranslationType::Forward)
{
nextCamera.m_lookAt += axisY * speed * deltaTime;
m_translateDeltaFn(nextCamera, axisY * speed * deltaTime);
}
if ((m_translation & TranslationType::Backward) == TranslationType::Backward)
{
nextCamera.m_lookAt -= axisY * speed * deltaTime;
m_translateDeltaFn(nextCamera, -axisY * speed * deltaTime);
}
if ((m_translation & TranslationType::Left) == TranslationType::Left)
{
nextCamera.m_lookAt -= axisX * speed * deltaTime;
m_translateDeltaFn(nextCamera, -axisX * speed * deltaTime);
}
if ((m_translation & TranslationType::Right) == TranslationType::Right)
{
nextCamera.m_lookAt += axisX * speed * deltaTime;
m_translateDeltaFn(nextCamera, axisX * speed * deltaTime);
}
if ((m_translation & TranslationType::Up) == TranslationType::Up)
{
nextCamera.m_lookAt += axisZ * speed * deltaTime;
m_translateDeltaFn(nextCamera, axisZ * speed * deltaTime);
}
if ((m_translation & TranslationType::Down) == TranslationType::Down)
{
nextCamera.m_lookAt -= axisZ * speed * deltaTime;
m_translateDeltaFn(nextCamera, -axisZ * speed * deltaTime);
}
if (Ending())
@@ -544,16 +533,20 @@ namespace AzFramework
m_translateCameraInputChannelIds = translateCameraInputChannelIds;
}
OrbitCameraInput::OrbitCameraInput(const InputChannelId& orbitChannelId)
: m_orbitChannelId(orbitChannelId)
PivotCameraInput::PivotCameraInput(const InputChannelId& pivotChannelId)
: m_pivotChannelId(pivotChannelId)
{
m_pivotFn = []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
return AZ::Vector3::CreateZero();
};
}
bool OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
bool PivotCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_orbitChannelId)
if (input->m_channelId == m_pivotChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -568,85 +561,46 @@ namespace AzFramework
if (Active())
{
return m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta);
return m_pivotCameras.HandleEvents(event, cursorDelta, scrollDelta);
}
return !Idle();
}
Camera OrbitCameraInput::StepCamera(
Camera PivotCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime)
{
Camera nextCamera = targetCamera;
if (Beginning())
{
const auto hasLookAt = [&nextCamera, &targetCamera, &lookAtFn = m_lookAtFn]
{
if (lookAtFn)
{
// pass through the camera's position and look vector for use in the lookAt function
if (const auto lookAt = lookAtFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY()))
{
// default to internal look at behavior if the look at point matches the camera translation
if (targetCamera.m_lookAt.IsClose(*lookAt))
{
return false;
}
auto transform = AZ::Transform::CreateLookAt(targetCamera.m_lookAt, *lookAt);
nextCamera.m_lookDist = -lookAt->GetDistance(targetCamera.m_lookAt);
UpdateCameraFromTransform(nextCamera, transform);
return true;
}
}
return false;
}();
if (!hasLookAt)
{
float hit_distance = 0.0f;
AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance);
if (hit_distance > 0.0f)
{
hit_distance = AZStd::min<float>(hit_distance, ed_cameraSystemMaxOrbitDistance);
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -ed_cameraSystemMinOrbitDistance;
nextCamera.m_lookAt =
targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * ed_cameraSystemMinOrbitDistance;
}
}
nextCamera.m_pivot = m_pivotFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY());
nextCamera.m_offset = nextCamera.View().TransformPoint(targetCamera.Translation());
}
if (Active())
{
nextCamera = m_orbitCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
MovePivotDetached(nextCamera, m_pivotFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY()));
nextCamera = m_pivotCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
if (Ending())
{
m_orbitCameras.Reset();
m_pivotCameras.Reset();
nextCamera.m_lookAt = nextCamera.Translation();
nextCamera.m_lookDist = 0.0f;
nextCamera.m_pivot = nextCamera.Translation();
nextCamera.m_offset = AZ::Vector3::CreateZero();
}
return nextCamera;
}
void OrbitCameraInput::SetOrbitInputChannelId(const InputChannelId& orbitChanneId)
void PivotCameraInput::SetPivotInputChannelId(const InputChannelId& pivotChanneId)
{
m_orbitChannelId = orbitChanneId;
m_pivotChannelId = pivotChanneId;
}
OrbitDollyScrollCameraInput::OrbitDollyScrollCameraInput()
PivotDollyScrollCameraInput::PivotDollyScrollCameraInput()
{
m_scrollSpeedFn = []() constexpr
{
@@ -654,7 +608,7 @@ namespace AzFramework
};
}
bool OrbitDollyScrollCameraInput::HandleEvents(
bool PivotDollyScrollCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
@@ -665,46 +619,61 @@ namespace AzFramework
return !Idle();
}
Camera OrbitDollyScrollCameraInput::StepCamera(
static Camera PivotDolly(const Camera& targetCamera, const float delta)
{
Camera nextCamera = targetCamera;
const auto pivotDirection = targetCamera.m_offset.GetNormalized();
nextCamera.m_offset -= pivotDirection * delta;
const auto pivotDot = targetCamera.m_offset.Dot(nextCamera.m_offset);
const auto distance = nextCamera.m_offset.GetLength() * AZ::GetSign(pivotDot);
const auto minDistance = 0.01f;
if (distance < minDistance || pivotDot < 0.0f)
{
nextCamera.m_offset = pivotDirection * minDistance;
}
return nextCamera;
}
Camera PivotDollyScrollCameraInput::StepCamera(
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + scrollDelta * m_scrollSpeedFn(), 0.0f);
const auto nextCamera = PivotDolly(targetCamera, aznumeric_cast<float>(scrollDelta) * m_scrollSpeedFn());
EndActivation();
return nextCamera;
}
OrbitDollyCursorMoveCameraInput::OrbitDollyCursorMoveCameraInput(const InputChannelId& dollyChannelId)
PivotDollyMotionCameraInput::PivotDollyMotionCameraInput(const InputChannelId& dollyChannelId)
: m_dollyChannelId(dollyChannelId)
{
m_cursorSpeedFn = []() constexpr
m_motionSpeedFn = []() constexpr
{
return 0.01f;
};
}
bool OrbitDollyCursorMoveCameraInput::HandleEvents(
bool PivotDollyMotionCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{
HandleActivationEvents(event, m_dollyChannelId, cursorDelta, m_clickDetector, *this);
return CameraInputUpdatingAfterMotion(*this);
}
Camera OrbitDollyCursorMoveCameraInput::StepCamera(
Camera PivotDollyMotionCameraInput::StepCamera(
const Camera& targetCamera,
const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + float(cursorDelta.m_y) * m_cursorSpeedFn(), 0.0f);
return nextCamera;
return PivotDolly(targetCamera, aznumeric_cast<float>(cursorDelta.m_y) * m_motionSpeedFn());
}
void OrbitDollyCursorMoveCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId)
void PivotDollyMotionCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId)
{
m_dollyChannelId = dollyChannelId;
}
@@ -739,7 +708,7 @@ namespace AzFramework
const auto translation_basis = LookTranslation(nextCamera);
const auto axisY = translation_basis.GetBasisY();
nextCamera.m_lookAt += axisY * scrollDelta * m_scrollSpeedFn();
nextCamera.m_pivot += axisY * scrollDelta * m_scrollSpeedFn();
EndActivation();
@@ -790,13 +759,13 @@ namespace AzFramework
{
const float moveRate = AZStd::exp2(cameraProps.m_translateSmoothnessFn());
const float moveTime = AZStd::exp2(-moveRate * deltaTime);
camera.m_lookDist = AZ::Lerp(targetCamera.m_lookDist, currentCamera.m_lookDist, moveTime);
camera.m_lookAt = targetCamera.m_lookAt.Lerp(currentCamera.m_lookAt, moveTime);
camera.m_pivot = targetCamera.m_pivot.Lerp(currentCamera.m_pivot, moveTime);
camera.m_offset = targetCamera.m_offset.Lerp(currentCamera.m_offset, moveTime);
}
else
{
camera.m_lookDist = targetCamera.m_lookDist;
camera.m_lookAt = targetCamera.m_lookAt;
camera.m_pivot = targetCamera.m_pivot;
camera.m_offset = targetCamera.m_offset;
}
return camera;
@@ -29,17 +29,15 @@ namespace AzFramework
//! @note Order of rotation is Z, Y, X.
AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation);
//! A simple camera representation using spherical coordinates as input (pitch, yaw and look distance).
//! A simple camera representation using spherical coordinates as input (pitch, yaw, pivot and offset).
//! The cameras transform and view can be obtained through accessor functions that use the internal
//! spherical coordinates to calculate the position and orientation.
struct Camera
{
AZ::Vector3 m_lookAt = AZ::Vector3::CreateZero(); //!< Position of camera when m_lookDist is zero,
//!< or position of m_lookAt when m_lookDist is greater
//!< than zero.
float m_yaw{ 0.0 }; //!< Yaw rotation of camera (stored in radians) usually clamped to 0-360 degrees (0-2Pi radians).
float m_pitch{ 0.0 }; //!< Pitch rotation of the camera (stored in radians) usually clamped to +/-90 degrees (-Pi/2 - Pi/2 radians).
float m_lookDist{ 0.0 }; //!< Zero gives first person free look, otherwise orbit about m_lookAt
AZ::Vector3 m_pivot = AZ::Vector3::CreateZero(); //!< Pivot point to rotate about (modified in world space).
AZ::Vector3 m_offset = AZ::Vector3::CreateZero(); //!< Offset relative to pivot (modified in camera space).
float m_yaw = 0.0f; //!< Yaw rotation of camera (stored in radians) usually clamped to 0-360 degrees (0-2Pi radians).
float m_pitch = 0.0f; //!< Pitch rotation of the camera (stored in radians) usually clamped to +/-90 degrees (-Pi/2 - Pi/2 radians).
//! View camera transform (V in model-view-projection matrix (MVP)).
AZ::Transform View() const;
@@ -51,6 +49,15 @@ namespace AzFramework
AZ::Vector3 Translation() const;
};
//! Helper to allow the pivot to be positioned without altering the camera's position.
inline void MovePivotDetached(Camera& camera, const AZ::Vector3& pivot)
{
const auto& view = camera.View();
const auto delta = view.TransformPoint(pivot) - view.TransformPoint(camera.m_pivot);
camera.m_offset -= delta;
camera.m_pivot = pivot;
}
inline AZ::Transform Camera::View() const
{
return Transform().GetInverse();
@@ -58,8 +65,8 @@ namespace AzFramework
inline AZ::Transform Camera::Transform() const
{
return AZ::Transform::CreateTranslation(m_lookAt) * AZ::Transform::CreateRotationZ(m_yaw) *
AZ::Transform::CreateRotationX(m_pitch) * AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(m_lookDist));
return AZ::Transform::CreateTranslation(m_pivot) * AZ::Transform::CreateRotationZ(m_yaw) * AZ::Transform::CreateRotationX(m_pitch) *
AZ::Transform::CreateTranslation(m_offset);
}
inline AZ::Matrix3x3 Camera::Rotation() const
@@ -279,21 +286,37 @@ namespace AzFramework
public:
bool HandleEvents(const InputEvent& event);
Camera StepCamera(const Camera& targetCamera, float deltaTime);
bool HandlingEvents() const
{
return m_handlingEvents;
}
bool HandlingEvents() const;
Cameras m_cameras; //!< Represents a collection of camera inputs that together provide a camera controller.
private:
ScreenVector m_motionDelta; //!< The delta used for look/orbit/pan (rotation + translation) - two dimensional.
ScreenVector m_motionDelta; //!< The delta used for look/pivot/pan (rotation + translation) - two dimensional.
CursorState m_cursorState; //!< The current and previous position of the cursor (used to calculate movement delta).
float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional.
bool m_handlingEvents = false; //!< Is the camera system currently handling events (events are consumed and not propagated).
};
//! A camera input to handle motion deltas that can rotate or orbit the camera.
inline bool CameraSystem::HandlingEvents() const
{
return m_handlingEvents;
}
//! Clamps pitch to be +/-90 degrees (-Pi/2, Pi/2).
//! @param pitch Pitch angle in radians.
inline float ClampPitchRotation(const float pitch)
{
return AZ::GetClamp(pitch, -AZ::Constants::HalfPi, AZ::Constants::HalfPi);
}
//! Ensures yaw wraps between 0 and 360 degrees (0, 2Pi).
//! @param yaw Yaw angle in radians.
inline float WrapYawRotation(const float yaw)
{
return AZStd::fmod(yaw + AZ::Constants::TwoPi, AZ::Constants::TwoPi);
}
//! A camera input to handle motion deltas that can rotate or pivot the camera.
class RotateCameraInput : public CameraInput
{
public:
@@ -332,8 +355,8 @@ namespace AzFramework
return { orientation.GetBasisX(), orientation.GetBasisZ() };
}
//! PanAxes to use while in 'orbit' camera behavior.
inline PanAxes OrbitPan(const Camera& camera)
//! PanAxes to use while in 'pivot' camera behavior.
inline PanAxes PivotPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -347,11 +370,23 @@ namespace AzFramework
return { basisX, basisY };
}
using TranslationDeltaFn = AZStd::function<void(Camera& camera, const AZ::Vector3& delta)>;
inline void TranslatePivot(Camera& camera, const AZ::Vector3& delta)
{
camera.m_pivot += delta;
}
inline void TranslateOffset(Camera& camera, const AZ::Vector3& delta)
{
camera.m_offset += camera.View().TransformVector(delta);
}
//! A camera input to handle motion deltas that can pan the camera (translate in two axes).
class PanCameraInput : public CameraInput
{
public:
PanCameraInput(const InputChannelId& panChannelId, PanAxesFn panAxesFn);
PanCameraInput(const InputChannelId& panChannelId, PanAxesFn panAxesFn, TranslationDeltaFn translationDeltaFn);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -365,6 +400,7 @@ namespace AzFramework
private:
PanAxesFn m_panAxesFn; //!< Builder for the particular pan axes (provided in the constructor).
TranslationDeltaFn m_translationDeltaFn; //!< How to apply the translation delta to the camera offset or pivot.
InputChannelId m_panChannelId; //!< Input channel to begin the pan camera input.
ClickDetector m_clickDetector; //!< Used to determine when a sufficient motion delta has occurred after an initial discrete input
//!< event has started (press and move event).
@@ -385,8 +421,8 @@ namespace AzFramework
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
//! TranslationAxes to use while in 'orbit' camera behavior.
inline AZ::Matrix3x3 OrbitTranslation(const Camera& camera)
//! TranslationAxes to use while in 'pivot' camera behavior.
inline AZ::Matrix3x3 PivotTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -417,8 +453,10 @@ namespace AzFramework
class TranslateCameraInput : public CameraInput
{
public:
explicit TranslateCameraInput(
TranslationAxesFn translationAxesFn, const TranslateCameraInputChannelIds& translateCameraInputChannelIds);
TranslateCameraInput(
const TranslateCameraInputChannelIds& translateCameraInputChannelIds,
TranslationAxesFn translationAxesFn,
TranslationDeltaFn translateDeltaFn);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -492,15 +530,16 @@ namespace AzFramework
TranslationType m_translation = TranslationType::Nil; //!< Types of translation the camera input is under.
TranslationAxesFn m_translationAxesFn; //!< Builder for translation axes.
TranslationDeltaFn m_translateDeltaFn; //!< How to apply the translation delta to the camera offset or pivot.
TranslateCameraInputChannelIds m_translateCameraInputChannelIds; //!< Input channel ids that map to internal translation types.
bool m_boost = false; //!< Is the translation speed currently being multiplied/scaled upwards.
};
//! A camera input to handle discrete scroll events that can modify the camera look distance.
class OrbitDollyScrollCameraInput : public CameraInput
//! A camera input to handle discrete scroll events that can modify the camera pivot distance.
class PivotDollyScrollCameraInput : public CameraInput
{
public:
OrbitDollyScrollCameraInput();
PivotDollyScrollCameraInput();
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -509,11 +548,11 @@ namespace AzFramework
AZStd::function<float()> m_scrollSpeedFn;
};
//! A camera input to handle motion deltas that can modify the camera look distance.
class OrbitDollyCursorMoveCameraInput : public CameraInput
//! A camera input to handle motion deltas that can modify the camera pivot distance.
class PivotDollyMotionCameraInput : public CameraInput
{
public:
explicit OrbitDollyCursorMoveCameraInput(const InputChannelId& dollyChannelId);
explicit PivotDollyMotionCameraInput(const InputChannelId& dollyChannelId);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -521,7 +560,7 @@ namespace AzFramework
void SetDollyInputChannelId(const InputChannelId& dollyChannelId);
AZStd::function<float()> m_cursorSpeedFn;
AZStd::function<float()> m_motionSpeedFn;
private:
InputChannelId m_dollyChannelId; //!< Input channel to begin the dolly cursor camera input.
@@ -544,36 +583,36 @@ namespace AzFramework
//! A camera input that doubles as its own set of camera inputs.
//! It is 'exclusive', so does not overlap with other sibling camera inputs - it runs its own set of camera inputs as 'children'.
class OrbitCameraInput : public CameraInput
class PivotCameraInput : public CameraInput
{
public:
using LookAtFn = AZStd::function<AZStd::optional<AZ::Vector3>(const AZ::Vector3& position, const AZ::Vector3& direction)>;
using PivotFn = AZStd::function<AZ::Vector3(const AZ::Vector3& position, const AZ::Vector3& direction)>;
explicit OrbitCameraInput(const InputChannelId& orbitChannelId);
explicit PivotCameraInput(const InputChannelId& pivotChannelId);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override;
void SetOrbitInputChannelId(const InputChannelId& orbitChanneId);
void SetPivotInputChannelId(const InputChannelId& pivotChanneId);
Cameras m_orbitCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive).
Cameras m_pivotCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive).
//! Override the default behavior for how a look-at point is calculated.
void SetLookAtFn(const LookAtFn& lookAtFn);
//! Override the default behavior for how a pivot point is calculated.
void SetPivotFn(PivotFn pivotFn);
private:
InputChannelId m_orbitChannelId; //!< Input channel to begin the orbit camera input.
LookAtFn m_lookAtFn; //!< The look-at behavior to use for this orbit camera (how is the look-at point calculated/retrieved).
InputChannelId m_pivotChannelId; //!< Input channel to begin the pivot camera input.
PivotFn m_pivotFn; //!< The pivot position to use for this pivot camera (how is the pivot point calculated/retrieved).
};
inline void OrbitCameraInput::SetLookAtFn(const LookAtFn& lookAtFn)
inline void PivotCameraInput::SetPivotFn(PivotFn pivotFn)
{
m_lookAtFn = lookAtFn;
m_pivotFn = AZStd::move(pivotFn);
}
inline bool OrbitCameraInput::Exclusive() const
inline bool PivotCameraInput::Exclusive() const
{
return true;
}
@@ -8,11 +8,10 @@
#pragma once
#include <AzCore/base.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/RTTI/TypeInfoSimple.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/RTTI/TypeInfoSimple.h>
#include <AzCore/base.h>
namespace AZ
{
@@ -27,7 +26,11 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenPoint, "{8472B6C2-527F-44FC-87F8-C226B1A57A97}");
ScreenPoint() = default;
ScreenPoint(int x, int y) : m_x(x), m_y(y) {}
ScreenPoint(int x, int y)
: m_x(x)
, m_y(y)
{
}
int m_x; //!< X screen position.
int m_y; //!< Y screen position.
@@ -42,7 +45,11 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenVector, "{1EAA2C62-8FDB-4A28-9FE3-1FA4F1418894}");
ScreenVector() = default;
ScreenVector(int x, int y) : m_x(x), m_y(y) {}
ScreenVector(int x, int y)
: m_x(x)
, m_y(y)
{
}
int m_x; //!< X screen delta.
int m_y; //!< Y screen delta.
@@ -71,14 +78,14 @@ namespace AzFramework
inline const ScreenPoint operator+(const ScreenPoint& lhs, const ScreenVector& rhs)
{
ScreenPoint result{lhs};
ScreenPoint result{ lhs };
result += rhs;
return result;
}
inline const ScreenPoint operator-(const ScreenPoint& lhs, const ScreenVector& rhs)
{
ScreenPoint result{lhs};
ScreenPoint result{ lhs };
result -= rhs;
return result;
}
@@ -99,14 +106,14 @@ namespace AzFramework
inline const ScreenVector operator+(const ScreenVector& lhs, const ScreenVector& rhs)
{
ScreenVector result{lhs};
ScreenVector result{ lhs };
result += rhs;
return result;
}
inline const ScreenVector operator-(const ScreenVector& lhs, const ScreenVector& rhs)
{
ScreenVector result{lhs};
ScreenVector result{ lhs };
result -= rhs;
return result;
}
@@ -131,23 +138,25 @@ namespace AzFramework
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));
lhs.m_y = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_y) * rhs));
return lhs;
}
inline const ScreenVector operator*(const ScreenVector& lhs, const float rhs)
{
ScreenVector 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));
}
inline ScreenPoint ScreenPointFromNDC(const AZ::Vector3& screenNDC, const AZ::Vector2& viewportSize)
{
return ScreenPoint(
aznumeric_caster(AZStd::round(screenNDC.GetX() * viewportSize.GetX())),
aznumeric_caster(AZStd::round((1.0f - screenNDC.GetY()) * viewportSize.GetY())));
}
inline AZ::Vector2 NDCFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize)
{
return AZ::Vector2(aznumeric_cast<float>(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast<float>(screenPoint.m_y)) / viewportSize;
}
//! Return an AZ::Vector2 from a ScreenPoint.
inline AZ::Vector2 Vector2FromScreenPoint(const ScreenPoint& screenPoint)
{
@@ -23,7 +23,7 @@ namespace AzFramework
// y-up, z into the screen, x-left
//
// x -> -x
// y -> z
// y -> z
// z -> y
//
// the same transform can be used to go to/from z-up - the only difference is the order of
@@ -37,15 +37,15 @@ namespace AzFramework
// yaw = AZ::Matrix4x4::CreateRotationZ(AZ::DegToRad(180.0f));
// conversion = pitch * yaw
return AZ::Matrix4x4::CreateFromColumns(
AZ::Vector4(-1.0f, 0.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 1.0f, .0f),
AZ::Vector4(0.0f, 1.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 0.0f, 1.0f));
AZ::Vector4(-1.0f, 0.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 1.0f, 0.0f), AZ::Vector4(0.0f, 1.0f, 0.0f, 0.0f),
AZ::Vector4(0.0f, 0.0f, 0.0f, 1.0f));
}
AZ::Matrix4x4 CameraTransform(const CameraState& cameraState)
{
return AZ::Matrix4x4::CreateFromColumns(
AZ::Vector3ToVector4(cameraState.m_side), AZ::Vector3ToVector4(cameraState.m_forward),
AZ::Vector3ToVector4(cameraState.m_up), AZ::Vector3ToVector4(cameraState.m_position, 1.0f));
AZ::Vector3ToVector4(cameraState.m_side), AZ::Vector3ToVector4(cameraState.m_forward), AZ::Vector3ToVector4(cameraState.m_up),
AZ::Vector3ToVector4(cameraState.m_position, 1.0f));
}
AZ::Matrix4x4 CameraView(const CameraState& cameraState)
@@ -63,8 +63,7 @@ namespace AzFramework
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState)
{
return AZ::Matrix4x4::CreateProjection(
cameraState.VerticalFovRadian(), AspectRatio(cameraState.m_viewportSize), cameraState.m_nearClip,
cameraState.m_farClip);
cameraState.VerticalFovRadian(), AspectRatio(cameraState.m_viewportSize), cameraState.m_nearClip, cameraState.m_farClip);
}
AZ::Matrix4x4 InverseCameraProjection(const CameraState& cameraState)
@@ -93,12 +92,11 @@ namespace AzFramework
const auto cameraWorldTransform = AZ::Transform::CreateFromMatrix3x3AndTranslation(
AZ::Matrix3x3::CreateFromMatrix4x4(worldFromView), worldFromView.GetTranslation());
return AZ::ViewFrustumAttributes(
cameraWorldTransform, AspectRatio(cameraState.m_viewportSize), cameraState.m_fovOrZoom,
cameraState.m_nearClip, cameraState.m_farClip);
cameraWorldTransform, AspectRatio(cameraState.m_viewportSize), cameraState.m_fovOrZoom, cameraState.m_nearClip,
cameraState.m_farClip);
}
AZ::Vector3 WorldToScreenNDC(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection)
AZ::Vector3 WorldToScreenNdc(const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection)
{
// transform the world space position to clip space
const auto clipSpacePosition = cameraProjection * cameraView * AZ::Vector3ToVector4(worldPosition, 1.0f);
@@ -108,25 +106,24 @@ namespace AzFramework
return (AZ::Vector4ToVector3(ndcPosition) + AZ::Vector3::CreateOne()) * 0.5f;
}
ScreenPoint WorldToScreen(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection,
const AZ::Vector3& worldPosition,
const AZ::Matrix4x4& cameraView,
const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize)
{
const auto ndcNormalizedPosition = WorldToScreenNDC(worldPosition, cameraView, cameraProjection);
const auto ndcNormalizedPosition = WorldToScreenNdc(worldPosition, cameraView, cameraProjection);
// scale ndc position by screen dimensions to return screen position
return ScreenPointFromNDC(ndcNormalizedPosition, viewportSize);
return ScreenPointFromNdc(AZ::Vector3ToVector2(ndcNormalizedPosition), viewportSize);
}
ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState)
{
return WorldToScreen(
worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
return WorldToScreen(worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
}
AZ::Vector3 ScreenNDCToWorld(
const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection)
AZ::Vector3 ScreenNdcToWorld(
const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection)
{
// convert screen space coordinates from <0, 1> to <-1,1> range
const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne();
@@ -142,18 +139,19 @@ namespace AzFramework
}
AZ::Vector3 ScreenToWorld(
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize)
const ScreenPoint& screenPosition,
const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection,
const AZ::Vector2& viewportSize)
{
const auto normalizedScreenPosition = NDCFromScreenPoint(screenPosition, viewportSize);
const auto normalizedScreenPosition = NdcFromScreenPoint(screenPosition, viewportSize);
return ScreenNDCToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection);
return ScreenNdcToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection);
}
AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState)
{
return ScreenToWorld(
screenPosition, InverseCameraView(cameraState), InverseCameraProjection(cameraState),
cameraState.m_viewportSize);
screenPosition, InverseCameraView(cameraState), InverseCameraProjection(cameraState), cameraState.m_viewportSize);
}
} // namespace AzFramework
@@ -8,26 +8,42 @@
#pragma once
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AZ
{
class Frustum;
class Matrix4x4;
class Vector3;
struct ViewFrustumAttributes;
} // namespace AZ
namespace AzFramework
{
struct CameraState;
struct ScreenPoint;
struct ViewportInfo;
//! Projects a position in world space to screen space normalized device coordinates for the given camera.
AZ::Vector3 WorldToScreenNDC(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection);
//! Returns a position in screen space (in the range [0-viewportSize.x, 0-viewportSize.y]) from normalized device
//! coordinates (in the range 0.0-1.0).
inline ScreenPoint ScreenPointFromNdc(const AZ::Vector2& screenNdc, const AZ::Vector2& viewportSize)
{
return ScreenPoint(
aznumeric_cast<int>(AZStd::lround(screenNdc.GetX() * viewportSize.GetX())),
aznumeric_cast<int>(AZStd::lround((1.0f - screenNdc.GetY()) * viewportSize.GetY())));
}
//! Returns a position in normalized device coordinates (in the range [0.0-1.0, 0.0-1.0]) from a
//! screen space position (in the range [0-viewportSize.x, 0-viewportSize.y]).
inline AZ::Vector2 NdcFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize)
{
return AZ::Vector2(aznumeric_cast<float>(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast<float>(screenPoint.m_y)) /
viewportSize;
}
//! Projects a position in world space to screen space normalized device coordinates for the given camera.
AZ::Vector3 WorldToScreenNdc(const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection);
//! Projects a position in world space to screen space for the given camera.
ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState);
@@ -35,7 +51,9 @@ namespace AzFramework
//! Overload of WorldToScreen that accepts camera values that can be precomputed if this function
//! is called many times in a loop.
ScreenPoint WorldToScreen(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection,
const AZ::Vector3& worldPosition,
const AZ::Matrix4x4& cameraView,
const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize);
//! Unprojects a position in screen space pixel coordinates to world space.
@@ -45,14 +63,15 @@ namespace AzFramework
//! Overload of ScreenToWorld that accepts camera values that can be precomputed if this function
//! is called many times in a loop.
AZ::Vector3 ScreenToWorld(
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize);
const ScreenPoint& screenPosition,
const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection,
const AZ::Vector2& viewportSize);
//! Unprojects a position in screen space normalized device coordinates to world space.
//! Note: The position returned will be on the near clip plane of the camera in world space.
AZ::Vector3 ScreenNDCToWorld(
const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection);
AZ::Vector3 ScreenNdcToWorld(
const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection);
//! Returns the camera projection for the current camera state.
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState);
+1 -1
View File
@@ -95,4 +95,4 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
endif()
endif()
endif()
@@ -6,29 +6,26 @@
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include "Application_Linux_xcb.h"
#include <AzFramework/XcbApplication.h>
#include <AzFramework/XcbEventHandler.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
////////////////////////////////////////////////////////////////////////////////////////////////
class LinuxXcbConnectionManagerImpl
: public LinuxXcbConnectionManagerBus::Handler
class XcbConnectionManagerImpl
: public XcbConnectionManagerBus::Handler
{
public:
LinuxXcbConnectionManagerImpl()
XcbConnectionManagerImpl()
{
m_xcbConnection = xcb_connect(nullptr, nullptr);
AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
LinuxXcbConnectionManagerBus::Handler::BusConnect();
AZ_Error("Application", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
XcbConnectionManagerBus::Handler::BusConnect();
}
~LinuxXcbConnectionManagerImpl()
~XcbConnectionManagerImpl() override
{
LinuxXcbConnectionManagerBus::Handler::BusDisconnect();
XcbConnectionManagerBus::Handler::BusDisconnect();
xcb_disconnect(m_xcbConnection);
}
@@ -42,53 +39,51 @@ namespace AzFramework
};
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux_xcb::ApplicationLinux_xcb()
XcbApplication::XcbApplication()
{
LinuxLifecycleEvents::Bus::Handler::BusConnect();
m_xcbConnectionManager = AZStd::make_unique<LinuxXcbConnectionManagerImpl>();
if (LinuxXcbConnectionManagerInterface::Get() == nullptr)
m_xcbConnectionManager = AZStd::make_unique<XcbConnectionManagerImpl>();
if (XcbConnectionManagerInterface::Get() == nullptr)
{
LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
XcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux_xcb::~ApplicationLinux_xcb()
XcbApplication::~XcbApplication()
{
if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
if (XcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
{
LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
XcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
}
m_xcbConnectionManager.reset();
LinuxLifecycleEvents::Bus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ApplicationLinux_xcb::PumpSystemEventLoopOnce()
void XcbApplication::PumpSystemEventLoopOnce()
{
if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection())
{
if (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection))
{
LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event);
XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event);
free(event);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ApplicationLinux_xcb::PumpSystemEventLoopUntilEmpty()
void XcbApplication::PumpSystemEventLoopUntilEmpty()
{
if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection())
{
while (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection))
{
LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event);
XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event);
free(event);
}
}
}
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -5,27 +5,24 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/XcbConnectionManager.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
////////////////////////////////////////////////////////////////////////////////////////////////
class ApplicationLinux_xcb
class XcbApplication
: public Application::Implementation
, public LinuxLifecycleEvents::Bus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
AZ_CLASS_ALLOCATOR(ApplicationLinux_xcb, AZ::SystemAllocator, 0);
ApplicationLinux_xcb();
~ApplicationLinux_xcb() override;
AZ_CLASS_ALLOCATOR(XcbApplication, AZ::SystemAllocator, 0);
XcbApplication();
~XcbApplication() override;
////////////////////////////////////////////////////////////////////////////////////////////
// Application::Implementation
@@ -33,9 +30,6 @@ namespace AzFramework
void PumpSystemEventLoopUntilEmpty() override;
private:
AZStd::unique_ptr<LinuxXcbConnectionManager> m_xcbConnectionManager;
AZStd::unique_ptr<XcbConnectionManager> m_xcbConnectionManager;
};
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -0,0 +1,42 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/RTTI/RTTI.h>
#include <xcb/xcb.h>
namespace AzFramework
{
class XcbConnectionManager
{
public:
AZ_RTTI(XcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~XcbConnectionManager() = default;
virtual xcb_connection_t* GetXcbConnection() const = 0;
};
class XcbConnectionManagerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using XcbConnectionManagerBus = AZ::EBus<XcbConnectionManager, XcbConnectionManagerBusTraits>;
using XcbConnectionManagerInterface = AZ::Interface<XcbConnectionManager>;
} // namespace AzFramework
@@ -0,0 +1,40 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/RTTI/RTTI.h>
#include <xcb/xcb.h>
namespace AzFramework
{
class XcbEventHandler
{
public:
AZ_RTTI(XcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~XcbEventHandler() = default;
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
};
class XcbEventHandlerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using XcbEventHandlerBus = AZ::EBus<XcbEventHandler, XcbEventHandlerBusTraits>;
} // namespace AzFramework
@@ -0,0 +1,271 @@
/*
* 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 <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/XcbEventHandler.h>
#include <AzFramework/XcbConnectionManager.h>
#include <AzFramework/XcbInputDeviceKeyboard.h>
#define explicit ExplicitIsACXXKeyword
#include <xcb/xkb.h>
#undef explicit
#include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
namespace AzFramework
{
XcbInputDeviceKeyboard::XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice)
: InputDeviceKeyboard::Implementation(inputDevice)
{
XcbEventHandlerBus::Handler::BusConnect();
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
if (!interface)
{
AZ_Warning("ApplicationLinux", false, "XCB interface not available");
return;
}
auto* connection = interface->GetXcbConnection();
if (!connection)
{
AZ_Warning("ApplicationLinux", false, "XCB connection not available");
return;
}
XcbStdFreePtr<xcb_xkb_use_extension_reply_t> xkbUseExtensionReply{
xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr)
};
if (!xkbUseExtensionReply)
{
AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension");
return;
}
if (!xkbUseExtensionReply->supported)
{
AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension");
return;
}
m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection);
m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS));
m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId));
m_initialized = true;
}
bool XcbInputDeviceKeyboard::IsConnected() const
{
auto* connection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection();
return connection && !xcb_connection_has_error(connection);
}
bool XcbInputDeviceKeyboard::HasTextEntryStarted() const
{
return false;
}
void XcbInputDeviceKeyboard::TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options)
{
}
void XcbInputDeviceKeyboard::TextEntryStop()
{
}
void XcbInputDeviceKeyboard::TickInputDevice()
{
ProcessRawEventQueues();
}
void XcbInputDeviceKeyboard::HandleXcbEvent(xcb_generic_event_t* event)
{
if (!m_initialized)
{
return;
}
switch (event->response_type & ~0x80)
{
case XCB_KEY_PRESS:
{
auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail);
if (key)
{
QueueRawKeyEvent(*key, true);
}
break;
}
case XCB_KEY_RELEASE:
{
auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
if (key)
{
QueueRawKeyEvent(*key, false);
}
break;
}
}
}
[[nodiscard]] const InputChannelId* XcbInputDeviceKeyboard::InputChannelFromKeyEvent(xcb_keycode_t code) const
{
const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code);
switch(keysym)
{
case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0;
case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1;
case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2;
case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3;
case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4;
case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5;
case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6;
case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7;
case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8;
case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9;
case XKB_KEY_A:
case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA;
case XKB_KEY_B:
case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB;
case XKB_KEY_C:
case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC;
case XKB_KEY_D:
case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD;
case XKB_KEY_E:
case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE;
case XKB_KEY_F:
case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF;
case XKB_KEY_G:
case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG;
case XKB_KEY_H:
case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH;
case XKB_KEY_I:
case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI;
case XKB_KEY_J:
case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ;
case XKB_KEY_K:
case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK;
case XKB_KEY_L:
case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL;
case XKB_KEY_M:
case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM;
case XKB_KEY_N:
case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN;
case XKB_KEY_O:
case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO;
case XKB_KEY_P:
case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP;
case XKB_KEY_Q:
case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ;
case XKB_KEY_R:
case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR;
case XKB_KEY_S:
case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS;
case XKB_KEY_T:
case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT;
case XKB_KEY_U:
case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU;
case XKB_KEY_V:
case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV;
case XKB_KEY_W:
case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW;
case XKB_KEY_X:
case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX;
case XKB_KEY_Y:
case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY;
case XKB_KEY_Z:
case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ;
case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace;
case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock;
case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter;
case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace;
case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab;
case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape;
case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01;
case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02;
case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03;
case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04;
case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05;
case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06;
case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07;
case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08;
case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09;
case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10;
case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11;
case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12;
case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13;
case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14;
case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15;
case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16;
case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17;
case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18;
case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19;
case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20;
case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL;
case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR;
case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL;
case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR;
case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL;
case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR;
case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL;
case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR;
case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown;
case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft;
case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight;
case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp;
case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete;
case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd;
case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome;
case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert;
case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown;
case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp;
case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock;
case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0;
case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1;
case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2;
case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3;
case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4;
case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5;
case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6;
case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7;
case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8;
case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9;
case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd;
case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal;
case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide;
case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter;
case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply;
case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract;
case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe;
case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash;
case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL;
case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR;
case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma;
case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals;
case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen;
case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod;
case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon;
case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash;
case XKB_KEY_grave:
case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde;
case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO;
case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause;
case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint;
case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock;
default: return nullptr;
}
}
} // namespace AzFramework
@@ -0,0 +1,46 @@
/*
* 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 <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/XcbEventHandler.h>
#include <AzFramework/XcbInterface.h>
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
namespace AzFramework
{
class XcbInputDeviceKeyboard
: public InputDeviceKeyboard::Implementation
, public XcbEventHandlerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(XcbInputDeviceKeyboard, AZ::SystemAllocator, 0);
using InputDeviceKeyboard::Implementation::Implementation;
XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice);
bool IsConnected() const override;
bool HasTextEntryStarted() const override;
void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override;
void TextEntryStop() override;
void TickInputDevice() override;
void HandleXcbEvent(xcb_generic_event_t* event) override;
private:
[[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const;
XcbUniquePtr<xkb_context, xkb_context_unref> m_xkbContext;
XcbUniquePtr<xkb_keymap, xkb_keymap_unref> m_xkbKeymap;
XcbUniquePtr<xkb_state, xkb_state_unref> m_xkbState;
int m_coreDeviceId{-1};
bool m_initialized{false};
};
} // namespace AzFramework
@@ -0,0 +1,38 @@
/*
* 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
*
*/
#pragma once
#include <xcb/xcb.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AzFramework
{
// @brief Wrap a function pointer in a type
// This serves as a convenient way to wrap a function pointer in a given
// type. That type can then be used in a `unique_ptr` or `shared_ptr`.
// Using a type instead of a function pointer by value prevents the need to
// copy the pointer when copying the smart poiner.
template<auto Callable>
struct XcbDeleterFreeFunctionWrapper
{
using value_type = decltype(Callable);
static constexpr value_type s_value = Callable;
constexpr operator value_type() const noexcept
{
return s_value;
}
};
template<typename T, auto fn>
using XcbUniquePtr = AZStd::unique_ptr<T, XcbDeleterFreeFunctionWrapper<fn>>;
template<typename T>
using XcbStdFreePtr = XcbUniquePtr<T, ::free>;
} // namespace AzFramework
@@ -6,41 +6,37 @@
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <xcb/xcb.h>
#include <AzFramework/XcbNativeWindow.h>
#include <AzFramework/XcbConnectionManager.h>
#include "NativeWindow_Linux_xcb.h"
#include <xcb/xcb.h>
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
[[maybe_unused]] const char LinuxXcbErrorWindow[] = "NativeWindow_Linux_xcb";
[[maybe_unused]] const char XcbErrorWindow[] = "XcbNativeWindow";
static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages
static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified
static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowImpl_Linux_xcb::NativeWindowImpl_Linux_xcb()
XcbNativeWindow::XcbNativeWindow()
: NativeWindow::Implementation()
{
if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get();
if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get();
xcbConnectionManager != nullptr)
{
m_xcbConnection = xcbConnectionManager->GetXcbConnection();
}
AZ_Error(LinuxXcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection");
AZ_Error(XcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection");
}
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowImpl_Linux_xcb::~NativeWindowImpl_Linux_xcb()
{
}
XcbNativeWindow::~XcbNativeWindow() = default;
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::InitWindow(const AZStd::string& title,
void XcbNativeWindow::InitWindow(const AZStd::string& title,
const WindowGeometry& geometry,
const WindowStyleMasks& styleMasks)
{
@@ -98,13 +94,13 @@ namespace AzFramework
xcb_intern_atom_cookie_t cookieProtocol = xcb_intern_atom(m_xcbConnection, 1, strlen(wmProtocolString), wmProtocolString);
xcb_intern_atom_reply_t* replyProtocol = xcb_intern_atom_reply(m_xcbConnection, cookieProtocol, nullptr);
AZ_Error(LinuxXcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString);
AZ_Error(XcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString);
m_xcbAtomProtocols = replyProtocol->atom;
const static char* wmDeleteWindowString = "WM_DELETE_WINDOW";
xcb_intern_atom_cookie_t cookieDeleteWindow = xcb_intern_atom(m_xcbConnection, 0, strlen(wmDeleteWindowString), wmDeleteWindowString);
xcb_intern_atom_reply_t* replyDeleteWindow = xcb_intern_atom_reply(m_xcbConnection, cookieDeleteWindow, nullptr);
AZ_Error(LinuxXcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString);
AZ_Error(XcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString);
m_xcbAtomDeleteWindow = replyDeleteWindow->atom;
xcbCheckResult = xcb_change_property_checked(m_xcbConnection,
@@ -123,9 +119,9 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::Activate()
void XcbNativeWindow::Activate()
{
LinuxXcbEventHandlerBus::Handler::BusConnect();
XcbEventHandlerBus::Handler::BusConnect();
if (!m_activated) // nothing to do if window was already activated
{
@@ -137,7 +133,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::Deactivate()
void XcbNativeWindow::Deactivate()
{
if (m_activated) // nothing to do if window was already deactivated
{
@@ -148,17 +144,17 @@ namespace AzFramework
xcb_unmap_window(m_xcbConnection, m_xcbWindow);
xcb_flush(m_xcbConnection);
}
LinuxXcbEventHandlerBus::Handler::BusDisconnect();
XcbEventHandlerBus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowHandle NativeWindowImpl_Linux_xcb::GetWindowHandle() const
NativeWindowHandle XcbNativeWindow::GetWindowHandle() const
{
return reinterpret_cast<NativeWindowHandle>(m_xcbWindow);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::SetWindowTitle(const AZStd::string& title)
void XcbNativeWindow::SetWindowTitle(const AZStd::string& title)
{
xcb_void_cookie_t xcbCheckResult;
xcbCheckResult = xcb_change_property(m_xcbConnection,
@@ -173,7 +169,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::ResizeClientArea(WindowSize clientAreaSize)
void XcbNativeWindow::ResizeClientArea(WindowSize clientAreaSize)
{
const uint32_t values[] = { clientAreaSize.m_width, clientAreaSize.m_height };
@@ -184,7 +180,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t NativeWindowImpl_Linux_xcb::GetDisplayRefreshRate() const
uint32_t XcbNativeWindow::GetDisplayRefreshRate() const
{
// [GFX TODO][GHI - 2678]
// Using 60 for now until proper support is added
@@ -192,7 +188,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool NativeWindowImpl_Linux_xcb::ValidateXcbResult(xcb_void_cookie_t cookie)
bool XcbNativeWindow::ValidateXcbResult(xcb_void_cookie_t cookie)
{
bool result = true;
if (xcb_generic_error_t* error = xcb_request_check(m_xcbConnection, cookie))
@@ -204,7 +200,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::HandleXcbEvent(xcb_generic_event_t* event)
void XcbNativeWindow::HandleXcbEvent(xcb_generic_event_t* event)
{
switch (event->response_type & s_XcbResponseTypeMask)
{
@@ -233,7 +229,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::WindowSizeChanged(const uint32_t width, const uint32_t height)
void XcbNativeWindow::WindowSizeChanged(const uint32_t width, const uint32_t height)
{
if (m_width != width || m_height != height)
{
@@ -246,7 +242,4 @@ namespace AzFramework
}
}
}
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -5,24 +5,25 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <AzFramework/XcbEventHandler.h>
#include <xcb/xcb.h>
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
class NativeWindowImpl_Linux_xcb final
class XcbNativeWindow final
: public NativeWindow::Implementation
, public LinuxXcbEventHandlerBus::Handler
, public XcbEventHandlerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(NativeWindowImpl_Linux_xcb, AZ::SystemAllocator, 0);
NativeWindowImpl_Linux_xcb();
~NativeWindowImpl_Linux_xcb() override;
AZ_CLASS_ALLOCATOR(XcbNativeWindow, AZ::SystemAllocator, 0);
XcbNativeWindow();
~XcbNativeWindow() override;
////////////////////////////////////////////////////////////////////////////////////////////
// NativeWindow::Implementation
@@ -37,7 +38,7 @@ namespace AzFramework
uint32_t GetDisplayRefreshRate() const override;
////////////////////////////////////////////////////////////////////////////////////////////
// LinuxXcbEventHandlerBus::Handler
// XcbEventHandlerBus::Handler
void HandleXcbEvent(xcb_generic_event_t* event) override;
private:
@@ -49,6 +50,4 @@ namespace AzFramework
xcb_atom_t m_xcbAtomProtocols;
xcb_atom_t m_xcbAtomDeleteWindow;
};
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -0,0 +1,18 @@
#
# 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
#
#
set(FILES
AzFramework/XcbApplication.cpp
AzFramework/XcbApplication.h
AzFramework/XcbConnectionManager.h
AzFramework/XcbInputDeviceKeyboard.cpp
AzFramework/XcbInputDeviceKeyboard.h
AzFramework/XcbInterface.h
AzFramework/XcbNativeWindow.cpp
AzFramework/XcbNativeWindow.h
)
@@ -12,10 +12,6 @@
#include <AzCore/Interface/Interface.h>
#include <AzCore/EBus/EBus.h>
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <xcb/xcb.h>
#endif // LY_COMPILE_DEFINITIONS
namespace AzFramework
{
class LinuxLifecycleEvents
@@ -30,54 +26,4 @@ namespace AzFramework
using Bus = AZ::EBus<LinuxLifecycleEvents>;
};
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
class LinuxXcbConnectionManager
{
public:
AZ_RTTI(LinuxXcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~LinuxXcbConnectionManager() = default;
virtual xcb_connection_t* GetXcbConnection() const = 0;
};
class LinuxXcbConnectionManagerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using LinuxXcbConnectionManagerBus = AZ::EBus<LinuxXcbConnectionManager, LinuxXcbConnectionManagerBusTraits>;
using LinuxXcbConnectionManagerInterface = AZ::Interface<LinuxXcbConnectionManager>;
class LinuxXcbEventHandler
{
public:
AZ_RTTI(LinuxXcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~LinuxXcbEventHandler() = default;
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
};
class LinuxXcbEventHandlerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using LinuxXcbEventHandlerBus = AZ::EBus<LinuxXcbEventHandler, LinuxXcbEventHandlerBusTraits>;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -8,7 +8,9 @@
#include <AzFramework/Application/Application.h>
#include "Application_Linux_xcb.h"
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/XcbApplication.h>
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
@@ -17,7 +19,7 @@ namespace AzFramework
Application::Implementation* Application::Implementation::Create()
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
return aznew ApplicationLinux_xcb();
return aznew XcbApplication();
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
@@ -0,0 +1,27 @@
/*
* 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
*
*/
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/XcbInputDeviceKeyboard.h>
#endif
namespace AzFramework
{
InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice)
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
return aznew XcbInputDeviceKeyboard(inputDevice);
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
#else
#error "Linux Window Manager not recognized."
return nullptr;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
} // namespace AzFramework
@@ -1,293 +0,0 @@
/*
* 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 <AzCore/std/typetraits/integral_constant.h>
#include <AzFramework/API/ApplicationAPI_Linux.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#define explicit ExplicitIsACXXKeyword
#include <xcb/xkb.h>
#undef explicit
#include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
namespace AzFramework
{
class InputDeviceKeyboardXcb
: public InputDeviceKeyboard::Implementation
, public LinuxXcbEventHandlerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(InputDeviceKeyboardXcb, AZ::SystemAllocator, 0);
using InputDeviceKeyboard::Implementation::Implementation;
InputDeviceKeyboardXcb(InputDeviceKeyboard& inputDevice)
: InputDeviceKeyboard::Implementation(inputDevice)
{
LinuxXcbEventHandlerBus::Handler::BusConnect();
auto* interface = AzFramework::LinuxXcbConnectionManagerInterface::Get();
if (!interface)
{
AZ_Warning("ApplicationLinux", false, "XCB interface not available");
return;
}
auto* connection = AzFramework::LinuxXcbConnectionManagerInterface::Get()->GetXcbConnection();
if (!connection)
{
AZ_Warning("ApplicationLinux", false, "XCB connection not available");
return;
}
AZStd::unique_ptr<xcb_xkb_use_extension_reply_t, DeleterForFreeFn<::std::free>> xkbUseExtensionReply{
xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr)
};
if (!xkbUseExtensionReply)
{
AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension");
return;
}
if (!xkbUseExtensionReply->supported)
{
AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension");
return;
}
m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection);
m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS));
m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId));
m_initialized = true;
}
bool IsConnected() const override
{
return m_initialized;
}
bool HasTextEntryStarted() const override
{
return false;
}
void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override
{
}
void TextEntryStop() override
{
}
void TickInputDevice() override
{
ProcessRawEventQueues();
}
void HandleXcbEvent(xcb_generic_event_t* event) override
{
if (!IsConnected())
{
return;
}
switch (event->response_type & ~0x80)
{
case XCB_KEY_PRESS:
{
auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail);
if (key)
{
QueueRawKeyEvent(*key, true);
}
break;
}
case XCB_KEY_RELEASE:
{
auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
if (key)
{
QueueRawKeyEvent(*key, false);
}
break;
}
}
}
private:
[[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const
{
const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code);
switch(keysym)
{
case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0;
case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1;
case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2;
case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3;
case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4;
case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5;
case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6;
case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7;
case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8;
case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9;
case XKB_KEY_A:
case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA;
case XKB_KEY_B:
case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB;
case XKB_KEY_C:
case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC;
case XKB_KEY_D:
case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD;
case XKB_KEY_E:
case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE;
case XKB_KEY_F:
case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF;
case XKB_KEY_G:
case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG;
case XKB_KEY_H:
case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH;
case XKB_KEY_I:
case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI;
case XKB_KEY_J:
case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ;
case XKB_KEY_K:
case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK;
case XKB_KEY_L:
case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL;
case XKB_KEY_M:
case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM;
case XKB_KEY_N:
case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN;
case XKB_KEY_O:
case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO;
case XKB_KEY_P:
case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP;
case XKB_KEY_Q:
case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ;
case XKB_KEY_R:
case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR;
case XKB_KEY_S:
case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS;
case XKB_KEY_T:
case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT;
case XKB_KEY_U:
case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU;
case XKB_KEY_V:
case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV;
case XKB_KEY_W:
case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW;
case XKB_KEY_X:
case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX;
case XKB_KEY_Y:
case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY;
case XKB_KEY_Z:
case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ;
case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace;
case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock;
case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter;
case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace;
case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab;
case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape;
case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01;
case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02;
case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03;
case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04;
case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05;
case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06;
case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07;
case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08;
case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09;
case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10;
case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11;
case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12;
case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13;
case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14;
case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15;
case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16;
case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17;
case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18;
case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19;
case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20;
case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL;
case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR;
case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL;
case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR;
case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL;
case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR;
case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL;
case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR;
case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown;
case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft;
case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight;
case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp;
case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete;
case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd;
case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome;
case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert;
case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown;
case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp;
case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock;
case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0;
case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1;
case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2;
case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3;
case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4;
case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5;
case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6;
case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7;
case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8;
case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9;
case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd;
case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal;
case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide;
case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter;
case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply;
case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract;
case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe;
case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash;
case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL;
case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR;
case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma;
case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals;
case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen;
case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod;
case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon;
case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash;
case XKB_KEY_grave:
case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde;
case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO;
case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause;
case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint;
case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock;
default: return nullptr;
}
}
template<auto freeFn>
using DeleterForFreeFn = AZStd::integral_constant<decltype(freeFn), freeFn>;
AZStd::unique_ptr<xkb_context, DeleterForFreeFn<xkb_context_unref>> m_xkbContext;
AZStd::unique_ptr<xkb_keymap, DeleterForFreeFn<xkb_keymap_unref>> m_xkbKeymap;
AZStd::unique_ptr<xkb_state, DeleterForFreeFn<xkb_state_unref>> m_xkbState;
int m_coreDeviceId{-1};
bool m_initialized{false};
};
InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice)
{
return aznew InputDeviceKeyboardXcb(inputDevice);
}
} // namespace AzFramework
@@ -6,14 +6,16 @@
*
*/
#include "NativeWindow_Linux_xcb.h"
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/XcbNativeWindow.h>
#endif
namespace AzFramework
{
NativeWindow::Implementation* NativeWindow::Implementation::Create()
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
return aznew NativeWindowImpl_Linux_xcb();
return aznew XcbNativeWindow();
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
@@ -22,5 +24,4 @@ namespace AzFramework
return nullptr;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
} // namespace AzFramework
@@ -10,6 +10,14 @@
# Only 'xcb' and 'wayland' are recognized
if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
set(LY_INCLUDE_DIRECTORIES
PUBLIC
Platform/Common/Xcb
)
set(LY_FILES_CMAKE
Platform/Common/Xcb/azframework_xcb_files.cmake
)
set(LY_BUILD_DEPENDENCIES
PRIVATE
3rdParty::X11::xcb
@@ -18,8 +26,6 @@ if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
3rdParty::X11::xkbcommon_X11
)
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland")
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND)
@@ -12,8 +12,6 @@ set(FILES
AzFramework/API/ApplicationAPI_Platform.h
AzFramework/API/ApplicationAPI_Linux.h
AzFramework/Application/Application_Linux.cpp
AzFramework/Application/Application_Linux_xcb.h
AzFramework/Application/Application_Linux_xcb.cpp
AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp
AzFramework/Process/ProcessWatcher_Linux.cpp
AzFramework/Process/ProcessCommon.h
@@ -22,10 +20,8 @@ set(FILES
../Common/Unimplemented/AzFramework/StreamingInstall/StreamingInstall_Unimplemented.cpp
../Common/Default/AzFramework/TargetManagement/TargetManagementComponent_Default.cpp
AzFramework/Windowing/NativeWindow_Linux.cpp
AzFramework/Windowing/NativeWindow_Linux_xcb.h
AzFramework/Windowing/NativeWindow_Linux_xcb.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp
AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp
AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp
@@ -26,7 +26,8 @@ namespace UnitTest
{
constexpr float deltaTime = 0.01666f; // 60fps
const bool consumed = m_cameraSystem->HandleEvents(event);
m_camera = m_cameraSystem->StepCamera(m_targetCamera, deltaTime);
m_targetCamera = m_cameraSystem->StepCamera(m_targetCamera, deltaTime);
m_camera = m_targetCamera; // no smoothing
return consumed;
}
@@ -45,20 +46,38 @@ namespace UnitTest
m_translateCameraInputChannelIds.m_boostChannelId = AzFramework::InputChannelId("keyboard_key_modifier_shift_l");
m_firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Right);
m_firstPersonTranslateCamera =
AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation, m_translateCameraInputChannelIds);
// set rotate speed to be a value that will scale motion delta (pixels moved) by a thousandth.
m_firstPersonRotateCamera->m_rotateSpeedFn = []()
{
return 0.001f;
};
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(m_orbitChannelId);
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
auto orbitTranslateCamera =
AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation, m_translateCameraInputChannelIds);
m_firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
m_translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot);
m_orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
m_pivotCamera = AZStd::make_shared<AzFramework::PivotCameraInput>(m_pivotChannelId);
m_pivotCamera->SetPivotFn(
[this](const AZ::Vector3&, const AZ::Vector3&)
{
return m_pivot;
});
auto pivotRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
// set rotate speed to be a value that will scale motion delta (pixels moved) by a thousandth.
pivotRotateCamera->m_rotateSpeedFn = []()
{
return 0.001f;
};
auto pivotTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
m_translateCameraInputChannelIds, AzFramework::PivotTranslation, AzFramework::TranslateOffset);
m_pivotCamera->m_pivotCameras.AddCamera(pivotRotateCamera);
m_pivotCamera->m_pivotCameras.AddCamera(pivotTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(m_firstPersonRotateCamera);
m_cameraSystem->m_cameras.AddCamera(m_firstPersonTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(m_orbitCamera);
m_cameraSystem->m_cameras.AddCamera(m_pivotCamera);
// these tests rely on using motion delta, not cursor positions (default is true)
AzFramework::ed_cameraSystemUseCursor = false;
@@ -68,7 +87,7 @@ namespace UnitTest
{
AzFramework::ed_cameraSystemUseCursor = true;
m_orbitCamera.reset();
m_pivotCamera.reset();
m_firstPersonRotateCamera.reset();
m_firstPersonTranslateCamera.reset();
@@ -78,24 +97,29 @@ namespace UnitTest
AllocatorsTestFixture::TearDown();
}
AzFramework::InputChannelId m_orbitChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l");
AzFramework::InputChannelId m_pivotChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l");
AzFramework::TranslateCameraInputChannelIds m_translateCameraInputChannelIds;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZStd::shared_ptr<AzFramework::PivotCameraInput> m_pivotCamera;
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;
};
TEST_F(CameraInputFixture, BeginAndEndOrbitCameraInputConsumesCorrectEvents)
TEST_F(CameraInputFixture, BeginAndEndPivotCameraInputConsumesCorrectEvents)
{
// begin orbit camera
// begin pivot camera
const bool consumed1 = HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceKeyboard::Key::ModifierAltL,
AzFramework::InputChannel::State::Began });
// begin listening for orbit rotate (click detector) - event is not consumed
// begin listening for pivot rotate (click detector) - event is not consumed
const bool consumed2 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
// begin orbit rotate (mouse has moved sufficient distance to initiate)
// begin pivot rotate (mouse has moved sufficient distance to initiate)
const bool consumed3 = HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ 5 });
// end orbit (mouse up) - event is not consumed
// end pivot (mouse up) - event is not consumed
const bool consumed4 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Ended });
@@ -236,29 +260,110 @@ namespace UnitTest
EXPECT_TRUE(activationEnded);
}
TEST_F(CameraInputFixture, OrbitCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenOrbiting)
TEST_F(CameraInputFixture, PivotCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenPivoting)
{
// create pathological lookAtFn that just returns the same position as the camera
m_orbitCamera->SetLookAtFn(
m_pivotCamera->SetPivotFn(
[](const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
return position;
});
const auto expectedCameraPosition = AZ::Vector3(10.0f, 10.0f, 10.0f);
AzFramework::UpdateCameraFromTransform(
m_targetCamera,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 10.0f, 10.0f)));
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), expectedCameraPosition));
m_camera = m_targetCamera;
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
// verify the camera yaw has not changed and pivot point matches the expected camera position
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_yaw, FloatNear(AZ::DegToRad(90.0f), 0.001f));
EXPECT_THAT(m_camera.m_pitch, FloatNear(0.0f, 0.001f));
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3::CreateZero()));
EXPECT_THAT(m_camera.m_pivot, IsClose(expectedCameraPosition));
}
// verify the camera yaw has not changed and the look at point
// does not match that of the camera translation
using ::testing::Eq;
using ::testing::Not;
EXPECT_THAT(m_camera.m_yaw, Eq(AZ::DegToRad(90.0f)));
EXPECT_THAT(m_camera.m_lookAt, Not(IsClose(m_camera.Translation())));
TEST_F(CameraInputFixture, FirstPersonRotateCameraInputRotatesYawByNinetyDegreesWithRequiredPixelDelta)
{
const auto cameraStartingPosition = AZ::Vector3::CreateAxisY(-10.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ PixelMotionDelta });
const float expectedYaw = AzFramework::WrapYawRotation(-AZ::Constants::HalfPi);
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_yaw, FloatNear(expectedYaw, 0.001f));
EXPECT_THAT(m_camera.m_pitch, FloatNear(0.0f, 0.001f));
EXPECT_THAT(m_camera.m_pivot, IsClose(cameraStartingPosition));
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3::CreateZero()));
}
TEST_F(CameraInputFixture, FirstPersonRotateCameraInputRotatesPitchByNinetyDegreesWithRequiredPixelDelta)
{
const auto cameraStartingPosition = AZ::Vector3::CreateAxisY(-10.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta });
const float expectedPitch = AzFramework::ClampPitchRotation(-AZ::Constants::HalfPi);
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_yaw, FloatNear(0.0f, 0.001f));
EXPECT_THAT(m_camera.m_pitch, FloatNear(expectedPitch, 0.001f));
EXPECT_THAT(m_camera.m_pivot, IsClose(cameraStartingPosition));
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3::CreateZero()));
}
TEST_F(CameraInputFixture, PivotRotateCameraInputRotatesPitchOffsetByNinetyDegreesWithRequiredPixelDelta)
{
const auto cameraStartingPosition = AZ::Vector3::CreateAxisY(-20.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
m_pivot = AZ::Vector3::CreateAxisY(-10.0f);
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta });
const auto expectedCameraEndingPosition = AZ::Vector3(0.0f, -10.0f, 10.0f);
const float expectedPitch = AzFramework::ClampPitchRotation(-AZ::Constants::HalfPi);
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_yaw, FloatNear(0.0f, 0.001f));
EXPECT_THAT(m_camera.m_pitch, FloatNear(expectedPitch, 0.001f));
EXPECT_THAT(m_camera.m_pivot, IsClose(m_pivot));
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3::CreateAxisY(-10.0f)));
EXPECT_THAT(m_camera.Translation(), IsCloseTolerance(expectedCameraEndingPosition, 0.01f));
}
TEST_F(CameraInputFixture, PivotRotateCameraInputRotatesYawOffsetByNinetyDegreesWithRequiredPixelDelta)
{
const auto cameraStartingPosition = AZ::Vector3(15.0f, -20.0f, 0.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
m_pivot = AZ::Vector3(10.0f, -10.0f, 0.0f);
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ -PixelMotionDelta });
const auto expectedCameraEndingPosition = AZ::Vector3(20.0f, -5.0f, 0.0f);
const float expectedYaw = AzFramework::WrapYawRotation(AZ::Constants::HalfPi);
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_yaw, FloatNear(expectedYaw, 0.001f));
EXPECT_THAT(m_camera.m_pitch, FloatNear(0.0f, 0.001f));
EXPECT_THAT(m_camera.m_pivot, IsClose(m_pivot));
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3(5.0f, -10.0f, 0.0f)));
EXPECT_THAT(m_camera.Translation(), IsCloseTolerance(expectedCameraEndingPosition, 0.01f));
}
} // namespace UnitTest
@@ -40,6 +40,11 @@ namespace AzFramework
MOCK_METHOD2(DespawnAllEntities, void(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs));
MOCK_METHOD3(DespawnEntity, void(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs));
MOCK_METHOD2(
RetrieveEntitySpawnTicket, void(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback));
MOCK_METHOD3(
ReloadSpawnable,
void(EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs));
@@ -151,6 +151,10 @@ namespace AzToolsFramework
m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
&SearchWidget::ClearTypeFilter);
connect(
this, &AssetPickerDialog::SizeChangedSignal, m_ui->m_assetBrowserTableViewWidget,
&AssetBrowserTableView::UpdateSizeSlot);
m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main");
m_tableModel->UpdateTableModelMaps();
}
@@ -206,6 +210,12 @@ namespace AzToolsFramework
}
}
void AssetPickerDialog::resizeEvent(QResizeEvent* resizeEvent)
{
emit SizeChangedSignal(m_ui->verticalLayout_4->geometry().width());
QDialog::resizeEvent(resizeEvent);
}
void AssetPickerDialog::keyPressEvent(QKeyEvent* e)
{
// Until search widget is revised, Return key should not close the dialog,
@@ -46,6 +46,9 @@ namespace AzToolsFramework
explicit AssetPickerDialog(AssetSelectionModel& selection, QWidget* parent = nullptr);
virtual ~AssetPickerDialog();
Q_SIGNALS:
void SizeChangedSignal(int newWidth);
protected:
//////////////////////////////////////////////////////////////////////////
// QDialog
@@ -53,6 +56,7 @@ namespace AzToolsFramework
void accept() override;
void reject() override;
void keyPressEvent(QKeyEvent* e) override;
void resizeEvent(QResizeEvent* resizeEvent) override;
private Q_SLOTS:
void DoubleClickedSlot(const QModelIndex& index);
@@ -117,6 +117,9 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTableView" name="m_assetBrowserTableViewWidget"/>
</item>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="m_assetBrowserTreeViewWidget">
<property name="sizePolicy">
@@ -142,9 +145,6 @@
</property>
</widget>
</item>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTableView" name="m_assetBrowserTableViewWidget"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
@@ -20,13 +20,17 @@ AZ_PUSH_DISABLE_WARNING(
#include <QCoreApplication>
#include <QHeaderView>
#include <QMenu>
#include <QResizeEvent>
#include <QTimer>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
const float MinHeaderResizeProportion = .25f;
const float MaxHeaderResizeProportion = .75f;
const float DefaultHeaderResizeProportion = .5f;
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
: AzQtComponents::TableView(parent)
, m_delegate(new SearchEntryDelegate(this))
@@ -65,8 +69,10 @@ namespace AzToolsFramework
AzQtComponents::TableView::setModel(model);
connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot);
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
header()->setStretchLastSection(true);
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Interactive);
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Interactive);
UpdateSizeSlot(parentWidget()->width());
header()->setSortIndicatorShown(false);
header()->setSectionsClickable(false);
}
@@ -148,8 +154,17 @@ namespace AzToolsFramework
void AssetBrowserTableView::OnAssetBrowserComponentReady()
{
UpdateSizeSlot(parentWidget()->width());
}
void AssetBrowserTableView::UpdateSizeSlot(int newWidth)
{
setColumnWidth(0, aznumeric_cast<int>(newWidth * DefaultHeaderResizeProportion));
header()->setMinimumSectionSize(aznumeric_cast<int>(newWidth * MinHeaderResizeProportion));
header()->setMaximumSectionSize(aznumeric_cast<int>(newWidth * MaxHeaderResizeProportion));
}
void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point)
{
const auto& selectedAssets = GetSelectedAssets();
@@ -59,12 +59,14 @@ namespace AzToolsFramework
void ClearStringFilter();
void ClearTypeFilter();
public Q_SLOTS:
void UpdateSizeSlot(int newWidth);
protected Q_SLOTS:
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override;
void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override;
void layoutChangedSlot(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(),
QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
private:
QString m_name;
QPointer<AssetBrowserTableModel> m_tableModel;
@@ -381,12 +381,22 @@ namespace AzToolsFramework
return;
}
//orphan any children that remain attached to the entity
auto children = entityInfo.GetChildren();
for (auto childId : children)
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
// For slices, orphan any children that remain attached to the entity
// For prefabs, this is an unneeded operation because the prefab system handles the orphans
// and the extra reparenting operation can be problematic for consumers subscribed to entity
// events, such as the entity outliner.
if (!isPrefabSystemEnabled)
{
ReparentChild(childId, AZ::EntityId(), entityId);
m_entityOrphanTable[entityId].insert(childId);
auto children = entityInfo.GetChildren();
for (auto childId : children)
{
ReparentChild(childId, AZ::EntityId(), entityId);
m_entityOrphanTable[entityId].insert(childId);
}
}
m_savedOrderInfo[entityId] = AZStd::make_pair(entityInfo.GetParent(), entityInfo.GetIndexForSorting());
@@ -56,5 +56,7 @@ namespace AzToolsFramework
virtual void StopPlayInEditor() = 0;
virtual void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) = 0;
virtual bool IsRootPrefabAssigned() const = 0;
};
}
@@ -79,6 +79,8 @@ namespace AzToolsFramework
void PrefabEditorEntityOwnershipService::Reset()
{
m_isRootPrefabAssigned = false;
if (m_rootInstance)
{
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
@@ -203,6 +205,7 @@ namespace AzToolsFramework
m_rootInstance->SetTemplateSourcePath(m_loaderInterface->GenerateRelativePath(filename));
m_rootInstance->SetContainerEntityName("Level");
m_prefabSystemComponent->PropagateTemplateChanges(templateId);
m_isRootPrefabAssigned = true;
return true;
}
@@ -302,6 +305,12 @@ namespace AzToolsFramework
}
m_prefabSystemComponent->PropagateTemplateChanges(templateId);
m_isRootPrefabAssigned = true;
}
bool PrefabEditorEntityOwnershipService::IsRootPrefabAssigned() const
{
return m_isRootPrefabAssigned;
}
Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::CreatePrefab(
@@ -167,6 +167,7 @@ namespace AzToolsFramework
void StopPlayInEditor() override;
void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) override;
bool IsRootPrefabAssigned() const override;
protected:
@@ -215,5 +216,6 @@ namespace AzToolsFramework
Prefab::PrefabLoaderInterface* m_loaderInterface;
AzFramework::EntityContextId m_entityContextId;
AZ::SerializeContext m_serializeContext;
bool m_isRootPrefabAssigned = false;
};
}
@@ -33,7 +33,7 @@ namespace AzToolsFramework
virtual AZ::EntityId GetFocusRoot() = 0;
//! Returns whether the entity id provided is part of the focused sub-tree.
virtual bool IsInFocusSubTree(AZ::EntityId entityId) = 0;
virtual bool IsInFocusSubTree(AZ::EntityId entityId) const = 0;
};
} // namespace AzToolsFramework
@@ -79,7 +79,7 @@ namespace AzToolsFramework
return m_focusRoot;
}
bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId)
bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) const
{
if (m_focusRoot == AZ::EntityId())
{
@@ -42,7 +42,7 @@ namespace AzToolsFramework
void SetFocusRoot(AZ::EntityId entityId) override;
void ClearFocusRoot() override;
AZ::EntityId GetFocusRoot() override;
bool IsInFocusSubTree(AZ::EntityId entityId) override;
bool IsInFocusSubTree(AZ::EntityId entityId) const override;
private:
AZ::EntityId m_focusRoot;
@@ -71,6 +71,7 @@ namespace AzToolsFramework
}
m_uniformScaleManipulator->SetVisualOrientationOverride(QuaternionFromTransformNoScaling(localTransform));
m_uniformScaleManipulator->SetLocalPosition(localTransform.GetTranslation());
m_uniformScaleManipulator->SetLocalOrientation(AZ::Quaternion::CreateIdentity());
}
@@ -13,14 +13,14 @@
namespace AzToolsFramework
{
static const float s_surfaceManipulatorTransparency = 0.75f;
static const float s_axisLength = 2.0f;
static const float s_surfaceManipulatorRadius = 0.1f;
static const float SurfaceManipulatorTransparency = 0.75f;
static const float LinearManipulatorAxisLength = 2.0f;
static const float SurfaceManipulatorRadius = 0.1f;
static const AZ::Color s_xAxisColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f);
static const AZ::Color s_yAxisColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f);
static const AZ::Color s_zAxisColor = AZ::Color(0.0f, 0.0f, 1.0f, 1.0f);
static const AZ::Color s_surfaceManipulatorColor = AZ::Color(1.0f, 1.0f, 0.0f, 0.5f);
static const AZ::Color LinearManipulatorXAxisColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f);
static const AZ::Color LinearManipulatorYAxisColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f);
static const AZ::Color LinearManipulatorZAxisColor = AZ::Color(0.0f, 0.0f, 1.0f, 1.0f);
static const AZ::Color SurfaceManipulatorColor = AZ::Color(1.0f, 1.0f, 0.0f, 0.5f);
TranslationManipulators::TranslationManipulators(
const Dimensions dimensions, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale)
@@ -291,7 +291,7 @@ namespace AzToolsFramework
{
const AZ::Color color[2] = {
defaultColor,
Vector3ToVector4(BaseManipulator::s_defaultMouseOverColor.GetAsVector3(), s_surfaceManipulatorTransparency)
Vector3ToVector4(BaseManipulator::s_defaultMouseOverColor.GetAsVector3(), SurfaceManipulatorTransparency)
};
return color[mouseOver];
@@ -325,15 +325,16 @@ namespace AzToolsFramework
void ConfigureTranslationManipulatorAppearance3d(TranslationManipulators* translationManipulators)
{
translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
translationManipulators->ConfigurePlanarView(s_xAxisColor, s_yAxisColor, s_zAxisColor);
translationManipulators->ConfigureLinearView(s_axisLength, s_xAxisColor, s_yAxisColor, s_zAxisColor);
translationManipulators->ConfigureSurfaceView(s_surfaceManipulatorRadius, s_surfaceManipulatorColor);
translationManipulators->ConfigurePlanarView(LinearManipulatorXAxisColor, LinearManipulatorYAxisColor, LinearManipulatorZAxisColor);
translationManipulators->ConfigureLinearView(
LinearManipulatorAxisLength, LinearManipulatorXAxisColor, LinearManipulatorYAxisColor, LinearManipulatorZAxisColor);
translationManipulators->ConfigureSurfaceView(SurfaceManipulatorRadius, SurfaceManipulatorColor);
}
void ConfigureTranslationManipulatorAppearance2d(TranslationManipulators* translationManipulators)
{
translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY());
translationManipulators->ConfigurePlanarView(s_xAxisColor);
translationManipulators->ConfigureLinearView(s_axisLength, s_xAxisColor, s_yAxisColor);
translationManipulators->ConfigurePlanarView(LinearManipulatorXAxisColor);
translationManipulators->ConfigureLinearView(LinearManipulatorAxisLength, LinearManipulatorXAxisColor, LinearManipulatorYAxisColor);
}
} // namespace AzToolsFramework
@@ -35,7 +35,7 @@ namespace AzToolsFramework::Prefab
{
InstanceOptionalReference focusedInstance;
if (entityId == AZ::EntityId())
if (!entityId.IsValid())
{
PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
@@ -89,7 +89,7 @@ namespace AzToolsFramework::Prefab
return false;
}
if (entityId == AZ::EntityId())
if (!entityId.IsValid())
{
return false;
}
@@ -329,6 +329,11 @@ namespace AzToolsFramework
return AZ::Failure(AZStd::string("Could not instantiate prefab - internal error "
"(PrefabEditorEntityOwnershipInterface unavailable)."));
}
if (!prefabEditorEntityOwnershipInterface->IsRootPrefabAssigned())
{
return AZ::Failure(AZStd::string("Could not instantiate prefab - no root prefab assigned. "
"Currently, prefabs can only be instantiated inside a level"));
}
InstanceOptionalReference instanceToParentUnder;
@@ -18,18 +18,19 @@ AzToolsFramework--EntityOutlinerWidget QTreeView
selection-background-color: transparent;
}
/*
* Entity Outliner handles hover and selected state of items via code,
* so we need to override the AzQtComponents::Treeview style.
*/
AzToolsFramework--EntityOutlinerWidget QTreeView::branch:hover
, AzToolsFramework--EntityOutlinerWidget QTreeView::item:hover
{
background: rgba(255, 255, 255, 30);
}
AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected
, AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected
, AzToolsFramework--EntityOutlinerWidget QTreeView::item:selected
, AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected:active
, AzToolsFramework--EntityOutlinerWidget QTreeView::item:selected:active
{
background: rgba(255, 255, 255, 45);
background: transparent;
}
@@ -44,6 +44,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/ToolsComponents/ComponentAssetMimeDataContainer.h>
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
#include <AzToolsFramework/ToolsComponents/EditorEntityIdContainer.h>
@@ -81,6 +82,8 @@ namespace AzToolsFramework
, m_entityExpansionState()
, m_entityFilteredState()
{
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
AZ_Assert(m_focusModeInterface != nullptr, "EntityOutlinerListModel requires a FocusModeInterface instance on construction.");
}
EntityOutlinerListModel::~EntityOutlinerListModel()
@@ -102,10 +105,9 @@ namespace AzToolsFramework
EntityCompositionNotificationBus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
m_editorEntityFrameworkInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(m_editorEntityFrameworkInterface != nullptr,
"EntityOutlinerListModel requires a EditorEntityFrameworkInterface instance on Initialize.");
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(m_editorEntityUiInterface != nullptr,
"EntityOutlinerListModel requires a EditorEntityUiInterface instance on Initialize.");
}
int EntityOutlinerListModel::rowCount(const QModelIndex& parent) const
@@ -279,7 +281,7 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::GetEntityIcon(const AZ::EntityId& id) const
{
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(id);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(id);
QIcon icon;
// Retrieve the icon from the handler
@@ -316,7 +318,7 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::GetEntityTooltip(const AZ::EntityId& id) const
{
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(id);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(id);
QString tooltip;
// Retrieve the tooltip from the handler
@@ -349,7 +351,7 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::dataForVisibility(const QModelIndex& index, int role) const
{
auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
@@ -377,7 +379,7 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::dataForLock(const QModelIndex& index, int role) const
{
auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
@@ -436,7 +438,7 @@ namespace AzToolsFramework
if (value.canConvert<Qt::CheckState>())
{
const auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
@@ -532,6 +534,11 @@ namespace AzToolsFramework
break;
}
if (AZ::EntityId entityId = GetEntityFromIndex(index); !m_focusModeInterface->IsInFocusSubTree(entityId))
{
itemFlags &= !Qt::ItemIsEnabled;
}
return itemFlags;
}
@@ -1337,7 +1344,10 @@ namespace AzToolsFramework
//add/remove operations trigger selection change signals which assert and break undo/redo operations in progress in inspector etc.
//so disallow selection updates until change is complete
emit EnableSelectionUpdates(false);
beginResetModel();
auto parentIndex = GetIndexFromEntity(parentId);
auto childIndex = GetIndexFromEntity(childId);
beginRemoveRows(parentIndex, childIndex.row(), childIndex.row());
}
void EntityOutlinerListModel::OnEntityInfoUpdatedRemoveChildEnd(AZ::EntityId parentId, AZ::EntityId childId)
@@ -1345,7 +1355,7 @@ namespace AzToolsFramework
(void)childId;
AZ_PROFILE_FUNCTION(AzToolsFramework);
endResetModel();
endRemoveRows();
//must refresh partial lock/visibility of parents
m_isFilterDirty = true;
@@ -36,6 +36,7 @@
namespace AzToolsFramework
{
class EditorEntityUiInterface;
class FocusModeInterface;
namespace EntityOutliner
{
@@ -273,7 +274,8 @@ namespace AzToolsFramework
QVariant GetEntityIcon(const AZ::EntityId& id) const;
QVariant GetEntityTooltip(const AZ::EntityId& id) const;
EditorEntityUiInterface* m_editorEntityFrameworkInterface = nullptr;
EditorEntityUiInterface* m_editorEntityUiInterface = nullptr;
FocusModeInterface* m_focusModeInterface = nullptr;
};
class EntityOutlinerCheckBox
@@ -38,6 +38,8 @@ namespace AzToolsFramework
AZ_Assert((m_editorEntityFrameworkInterface != nullptr),
"EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction.");
viewport()->setMouseTracking(true);
}
EntityOutlinerTreeView::~EntityOutlinerTreeView()
@@ -59,6 +61,11 @@ namespace AzToolsFramework
}
}
void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event)
{
m_mousePosition = QPoint();
}
void EntityOutlinerTreeView::mousePressEvent(QMouseEvent* event)
{
//postponing normal mouse pressed logic until mouse is released or dragged
@@ -112,6 +119,8 @@ namespace AzToolsFramework
setSelectionMode(selectionModeBefore);
}
m_mousePosition = event->pos();
//process mouse movement as normal, potentially triggering drag and drop
QTreeView::mouseMoveEvent(event);
}
@@ -172,12 +181,45 @@ namespace AzToolsFramework
void EntityOutlinerTreeView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const
{
const bool isEnabled = (this->model()->flags(index) & Qt::ItemIsEnabled);
const bool isSelected = selectionModel()->isSelected(index);
const bool isHovered = (index == indexAt(m_mousePosition)) && isEnabled;
// Paint the branch Selection/Hover Rect
PaintBranchSelectionHoverRect(painter, rect, isSelected, isHovered);
// Paint the branch background as defined by the entity's handler, or its closes ancestor's.
PaintBranchBackground(painter, rect, index);
QTreeView::drawBranches(painter, rect, index);
}
void EntityOutlinerTreeView::PaintBranchSelectionHoverRect(
QPainter* painter, const QRect& rect, bool isSelected, bool isHovered) const
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
if (isSelected || isHovered)
{
QPainterPath backgroundPath;
QRect backgroundRect(rect);
backgroundPath.addRect(backgroundRect);
QColor backgroundColor = m_hoverColor;
if (isSelected)
{
backgroundColor = m_selectedColor;
}
painter->fillPath(backgroundPath, backgroundColor);
}
painter->restore();
}
void EntityOutlinerTreeView::PaintBranchBackground(QPainter* painter, const QRect& rect, const QModelIndex& index) const
{
// Go through ancestors and add them to the stack
@@ -59,6 +59,7 @@ namespace AzToolsFramework
void startDrag(Qt::DropActions supportedActions) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dropEvent(QDropEvent* event) override;
void leaveEvent(QEvent* event) override;
//! Renders the left side of the item: appropriate background, branch lines, icons.
void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override;
@@ -72,8 +73,10 @@ namespace AzToolsFramework
void StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions) override;
void PaintBranchBackground(QPainter* painter, const QRect& rect, const QModelIndex& index) const;
void PaintBranchSelectionHoverRect(QPainter* painter, const QRect& rect, bool isSelected, bool isHovered) const;
QMouseEvent* m_queuedMouseEvent;
QPoint m_mousePosition;
bool m_draggingUnselectedItem; // This is set when an item is dragged outside its bounding box.
int m_expandOnlyDelay = -1;
@@ -232,6 +232,9 @@ namespace AzToolsFramework
m_gui->m_objectTree->header()->setSortIndicatorShown(false);
m_gui->m_objectTree->header()->setStretchLastSection(false);
// Always expand root entity (level entity) - needed if the widget is re-created while a level is already open.
m_gui->m_objectTree->expand(m_proxyModel->index(0, 0));
// resize the icon columns so that the Visibility and Lock toggle icon columns stay right-justified
m_gui->m_objectTree->header()->setStretchLastSection(false);
m_gui->m_objectTree->header()->setMinimumSectionSize(0);
@@ -13,6 +13,7 @@
#include <AzFramework/Viewport/CameraState.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/API/EditorViewportIconDisplayInterface.h>
#include <AzToolsFramework/ToolsComponents/EditorEntityIconComponentBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
@@ -112,6 +113,17 @@ namespace AzToolsFramework
}
}
EditorHelpers::EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache)
: m_entityDataCache(entityDataCache)
{
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
AZ_Assert(
m_focusModeInterface,
"EditorHelpers - "
"Focus Mode Interface could not be found. "
"Check that it is being correctly initialized.");
}
AZ::EntityId EditorHelpers::HandleMouseInteraction(
const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
@@ -173,6 +185,12 @@ namespace AzToolsFramework
}
}
// Verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor))
{
return AZ::EntityId();
}
return entityIdUnderCursor;
}
@@ -22,6 +22,7 @@ namespace AzFramework
namespace AzToolsFramework
{
class EditorVisibleEntityDataCache;
class FocusModeInterface;
namespace ViewportInteraction
{
@@ -38,10 +39,7 @@ namespace AzToolsFramework
//! An EditorVisibleEntityDataCache must be passed to EditorHelpers to allow it to
//! efficiently read entity data without resorting to EBus calls.
explicit EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache)
: m_entityDataCache(entityDataCache)
{
}
explicit EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache);
EditorHelpers(const EditorHelpers&) = delete;
EditorHelpers& operator=(const EditorHelpers&) = delete;
~EditorHelpers() = default;
@@ -62,5 +60,6 @@ namespace AzToolsFramework
private:
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers.
const FocusModeInterface* m_focusModeInterface = nullptr;
};
} // namespace AzToolsFramework
@@ -700,13 +700,6 @@ namespace AzToolsFramework
switch (referenceFrame)
{
case ReferenceFrame::Local:
// if we have a group selection, always use the pivot override if one
// is set when moving to local space (can't pick individual local space)
if (entityIdMap.size() > 1)
{
pivot.m_worldOrientation = pivotOverrideFrame.m_orientationOverride.value();
}
break;
case ReferenceFrame::Parent:
pivot.m_worldOrientation = pivotOverrideFrame.m_orientationOverride.value();
break;
@@ -784,7 +777,7 @@ namespace AzToolsFramework
SortEntitiesByLocationInHierarchy(sortedEntityIdsOut);
}
static void UpdateInitialRotation(EntityIdManipulators& entityManipulators)
static void UpdateInitialTransform(EntityIdManipulators& entityManipulators)
{
// save new start orientation (if moving rotation axes separate from object
// or switching type of rotation (modifier keys change))
@@ -797,22 +790,17 @@ namespace AzToolsFramework
}
}
// utility function to immediately return the current reference frame
// based on the state of the modifiers
// utility function to immediately return the current reference frame based on the state of the modifiers
static ReferenceFrame ReferenceFrameFromModifiers(const ViewportInteraction::KeyboardModifiers modifiers)
{
if (modifiers.Shift() && !modifiers.Alt())
{
return ReferenceFrame::World;
}
else if (modifiers.Alt() && !modifiers.Shift())
{
return ReferenceFrame::Local;
}
else
{
return ReferenceFrame::Parent;
}
return modifiers.Shift() ? ReferenceFrame::World : ReferenceFrame::Local;
}
// utility function to immediately return the current sphere of influence of the manipulators based on the
// state of the modifiers
static Influence InfluenceFromModifiers(const ViewportInteraction::KeyboardModifiers modifiers)
{
return modifiers.Alt() ? Influence::Individual : Influence::Group;
}
template<typename Action, typename EntityIdContainer>
@@ -838,6 +826,7 @@ namespace AzToolsFramework
else
{
const ReferenceFrame referenceFrame = spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers));
const Influence influence = InfluenceFromModifiers(action.m_modifiers);
// note: used for parent and world depending on the current reference frame
const auto pivotOrientation =
@@ -854,9 +843,9 @@ namespace AzToolsFramework
const AZ::Vector3 worldTranslation = GetWorldTranslation(entityId);
switch (referenceFrame)
switch (influence)
{
case ReferenceFrame::Local:
case Influence::Individual:
{
// move in each entities local space at once
AZ::Quaternion worldOrientation = AZ::Quaternion::CreateIdentity();
@@ -876,10 +865,9 @@ namespace AzToolsFramework
entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, transformChangedInternally);
}
break;
case ReferenceFrame::Parent:
case ReferenceFrame::World:
case Influence::Group:
{
AZ::Quaternion offsetRotation = pivotOrientation.m_worldOrientation *
const AZ::Quaternion offsetRotation = pivotOrientation.m_worldOrientation *
QuaternionFromTransformNoScaling(entityIdManipulators.m_manipulators->GetLocalTransform().GetInverse());
const AZ::Vector3 localOffset = offsetRotation.TransformVector(action.LocalPositionOffset());
@@ -1062,7 +1050,8 @@ namespace AzToolsFramework
{
AZStd::chrono::milliseconds timeNow;
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::BroadcastResult(
timeNow, &AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Events::EditorViewportInputTimeNow);
timeNow,
&AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Events::EditorViewportInputTimeNow);
return timeNow;
});
}
@@ -1263,7 +1252,7 @@ namespace AzToolsFramework
AZStd::unique_ptr<TranslationManipulators> translationManipulators = AZStd::make_unique<TranslationManipulators>(
TranslationManipulators::Dimensions::Three, AZ::Transform::CreateIdentity(), AZ::Vector3::CreateOne());
translationManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth(ViewportUi::DefaultViewportId));
translationManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth());
InitializeManipulators(*translationManipulators);
@@ -1297,7 +1286,7 @@ namespace AzToolsFramework
ViewportInteraction::KeyboardModifiers prevModifiers{};
translationManipulators->InstallLinearManipulatorMouseMoveCallback(
[this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable -> void
[this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable
{
UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
@@ -1331,7 +1320,7 @@ namespace AzToolsFramework
});
translationManipulators->InstallPlanarManipulatorMouseMoveCallback(
[this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable -> void
[this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable
{
UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
@@ -1364,7 +1353,7 @@ namespace AzToolsFramework
});
translationManipulators->InstallSurfaceManipulatorMouseMoveCallback(
[this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable -> void
[this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable
{
UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
@@ -1391,7 +1380,7 @@ namespace AzToolsFramework
AZStd::unique_ptr<RotationManipulators> rotationManipulators =
AZStd::make_unique<RotationManipulators>(AZ::Transform::CreateIdentity());
rotationManipulators->SetCircleBoundWidth(ManipulatorCicleBoundWidth(ViewportUi::DefaultViewportId));
rotationManipulators->SetCircleBoundWidth(ManipulatorCicleBoundWidth());
InitializeManipulators(*rotationManipulators);
@@ -1415,7 +1404,7 @@ namespace AzToolsFramework
AZStd::shared_ptr<SharedRotationState> sharedRotationState = AZStd::make_shared<SharedRotationState>();
rotationManipulators->InstallLeftMouseDownCallback(
[this, sharedRotationState]([[maybe_unused]] const AngularManipulator::Action& action) mutable -> void
[this, sharedRotationState]([[maybe_unused]] const AngularManipulator::Action& action) mutable
{
sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity();
sharedRotationState->m_referenceFrameAtMouseDown = m_referenceFrame;
@@ -1437,11 +1426,13 @@ namespace AzToolsFramework
BeginRecordManipulatorCommand();
});
ViewportInteraction::KeyboardModifiers prevModifiers{};
rotationManipulators->InstallMouseMoveCallback(
[this, prevModifiers, sharedRotationState](const AngularManipulator::Action& action) mutable -> void
[this, prevModifiers = ViewportInteraction::KeyboardModifiers(),
sharedRotationState](const AngularManipulator::Action& action) mutable
{
const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers));
const Influence influence = InfluenceFromModifiers(action.m_modifiers);
const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
// store the pivot override frame when positioning the manipulator manually (ctrl)
// so we don't lose the orientation when adding/removing entities from the selection
@@ -1452,9 +1443,7 @@ namespace AzToolsFramework
// only update the manipulator orientation if we're rotating in a local reference frame or we're
// manually modifying the manipulator orientation independent of the entity by holding ctrl
if ((sharedRotationState->m_referenceFrameAtMouseDown == ReferenceFrame::Local &&
m_entityIdManipulators.m_lookups.size() == 1) ||
action.m_modifiers.Ctrl())
if (sharedRotationState->m_referenceFrameAtMouseDown == ReferenceFrame::Local || action.m_modifiers.Ctrl())
{
m_entityIdManipulators.m_manipulators->SetLocalTransform(AZ::Transform::CreateFromQuaternionAndTranslation(
manipulatorOrientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation()));
@@ -1463,20 +1452,24 @@ namespace AzToolsFramework
// save state if we change the type of rotation we're doing to to prevent snapping
if (prevModifiers != action.m_modifiers)
{
UpdateInitialRotation(m_entityIdManipulators);
UpdateInitialTransform(m_entityIdManipulators);
sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull();
}
// allow the user to modify the orientation without moving the object if ctrl is held
if (action.m_modifiers.Ctrl())
{
UpdateInitialRotation(m_entityIdManipulators);
UpdateInitialTransform(m_entityIdManipulators);
sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull();
}
else
{
const auto pivotOrientation = ETCS::CalculateSelectionPivotOrientation(
m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, ReferenceFrame::Parent);
// only update the pivot override if the orientation is being modified in local space and we have
// more than one entity selected (so rotating a single entity does not set the orientation override)
if (referenceFrame == ReferenceFrame::Local && sharedRotationState->m_entityIds.size() > 1)
{
m_pivotOverrideFrame.m_orientationOverride = manipulatorOrientation;
}
// note: must use sorted entityIds based on hierarchy order when updating transforms
for (AZ::EntityId entityId : sharedRotationState->m_entityIds)
@@ -1492,9 +1485,9 @@ namespace AzToolsFramework
const AZ::Transform offsetRotation =
AZ::Transform::CreateFromQuaternion(sharedRotationState->m_savedOrientation * action.m_current.m_delta);
switch (referenceFrame)
switch (influence)
{
case ReferenceFrame::Local:
case Influence::Individual:
{
const AZ::Quaternion rotation = entityIdLookupIt->second.m_initial.GetRotation().GetNormalized();
const AZ::Vector3 position = entityIdLookupIt->second.m_initial.GetTranslation();
@@ -1510,23 +1503,10 @@ namespace AzToolsFramework
AZ::Transform::CreateTranslation(-centerOffset) * AZ::Transform::CreateUniformScale(scale));
}
break;
case ReferenceFrame::Parent:
case Influence::Group:
{
const AZ::Transform pivotTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
pivotOrientation.m_worldOrientation,
m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation());
const AZ::Transform transformInPivotSpace =
pivotTransform.GetInverse() * entityIdLookupIt->second.m_initial;
SetEntityWorldTransform(entityId, pivotTransform * offsetRotation * transformInPivotSpace);
}
break;
case ReferenceFrame::World:
{
const AZ::Transform pivotTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateIdentity(),
m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation());
manipulatorOrientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation());
const AZ::Transform transformInPivotSpace =
pivotTransform.GetInverse() * entityIdLookupIt->second.m_initial;
@@ -1561,7 +1541,7 @@ namespace AzToolsFramework
AZ_PROFILE_FUNCTION(AzToolsFramework);
AZStd::unique_ptr<ScaleManipulators> scaleManipulators = AZStd::make_unique<ScaleManipulators>(AZ::Transform::CreateIdentity());
scaleManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth(ViewportUi::DefaultViewportId));
scaleManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth());
InitializeManipulators(*scaleManipulators);
@@ -1571,13 +1551,20 @@ namespace AzToolsFramework
scaleManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
scaleManipulators->ConfigureView(2.0f, AZ::Color::CreateOne(), AZ::Color::CreateOne(), AZ::Color::CreateOne());
// lambdas capture shared_ptr by value to increment ref count
auto manipulatorEntityIds = AZStd::make_shared<ManipulatorEntityIds>();
auto uniformLeftMouseDownCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action)
struct SharedScaleState
{
AZ::Vector3 m_savedScaleOffset = AZ::Vector3::CreateZero();
EntityIdList m_entityIds;
};
// lambdas capture shared_ptr by value to increment ref count
auto sharedScaleState = AZStd::make_shared<SharedScaleState>();
auto uniformLeftMouseDownCallback = [this, sharedScaleState]([[maybe_unused]] const LinearManipulator::Action& action)
{
sharedScaleState->m_savedScaleOffset = AZ::Vector3::CreateZero();
// important to sort entityIds based on hierarchy order when updating transforms
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds);
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, sharedScaleState->m_entityIds);
for (auto& entityIdLookup : m_entityIdManipulators.m_lookups)
{
@@ -1591,20 +1578,32 @@ namespace AzToolsFramework
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
};
auto uniformLeftMouseUpCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action)
auto uniformLeftMouseUpCallback = [this, sharedScaleState]([[maybe_unused]] const LinearManipulator::Action& action)
{
AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast(
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged,
manipulatorEntityIds->m_entityIds);
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged, sharedScaleState->m_entityIds);
m_entityIdManipulators.m_manipulators->SetLocalTransform(RecalculateAverageManipulatorTransform(
m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame));
};
auto uniformLeftMouseMoveCallback = [this, manipulatorEntityIds](const LinearManipulator::Action& action)
auto uniformLeftMouseMoveCallback = [this, sharedScaleState, prevModifiers = ViewportInteraction::KeyboardModifiers()](
const LinearManipulator::Action& action) mutable
{
// do nothing to modify the manipulator
if (action.m_modifiers.Ctrl())
{
return;
}
if (prevModifiers != action.m_modifiers)
{
UpdateInitialTransform(m_entityIdManipulators);
sharedScaleState->m_savedScaleOffset = action.LocalScaleOffset();
}
// note: must use sorted entityIds based on hierarchy order when updating transforms
for (AZ::EntityId entityId : manipulatorEntityIds->m_entityIds)
for (AZ::EntityId entityId : sharedScaleState->m_entityIds)
{
auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(entityId);
if (entityIdLookupIt == m_entityIdManipulators.m_lookups.end())
@@ -1620,26 +1619,33 @@ namespace AzToolsFramework
return vec.GetX() + vec.GetY() + vec.GetZ();
};
const float uniformScale = action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset());
const float uniformScale =
action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset() - sharedScaleState->m_savedScaleOffset);
const float scale = AZ::GetClamp(1.0f + uniformScale / initialScale, AZ::MinTransformScale, AZ::MaxTransformScale);
const AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(scale);
if (action.m_modifiers.Alt())
switch (InfluenceFromModifiers(action.m_modifiers))
{
const AZ::Transform pivotTransform = TransformNormalizedScale(entityIdLookupIt->second.m_initial);
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
case Influence::Individual:
{
const AZ::Transform pivotTransform = TransformNormalizedScale(entityIdLookupIt->second.m_initial);
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
}
else
{
const AZ::Transform pivotTransform =
TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform());
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
}
break;
case Influence::Group:
{
const AZ::Transform pivotTransform =
TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform());
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
}
}
}
prevModifiers = action.m_modifiers;
};
scaleManipulators->InstallAxisLeftMouseDownCallback(uniformLeftMouseDownCallback);
@@ -2729,8 +2735,7 @@ namespace AzToolsFramework
if (m_pivotOverrideFrame.m_orientationOverride && m_entityIdManipulators.m_manipulators)
{
m_pivotOverrideFrame.m_orientationOverride =
QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
m_pivotOverrideFrame.m_orientationOverride = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetRotation();
}
if (m_pivotOverrideFrame.m_translationOverride && m_entityIdManipulators.m_manipulators)
@@ -3337,7 +3342,7 @@ namespace AzToolsFramework
display.SetLineWidth(4.0f);
const auto axisFlip = [&transform, &cameraState](const AZ::Vector3& axis) -> float
const auto axisFlip = [&transform, &cameraState](const AZ::Vector3& axis)
{
return ShouldFlipCameraAxis(
AZ::Transform::CreateIdentity(), transform.GetTranslation(), TransformDirectionNoScaling(transform, axis),
@@ -3554,7 +3559,7 @@ namespace AzToolsFramework
// screen space
const auto calculateGizmoAxis = [&cameraView, &cameraProjection, &screenOffset](const AZ::Vector3& axis)
{
auto result = AZ::Vector2(AzFramework::WorldToScreenNDC(axis, cameraView, cameraProjection));
auto result = AZ::Vector2(AzFramework::WorldToScreenNdc(axis, cameraView, cameraProjection));
result.SetY(1.0f - result.GetY());
return result + screenOffset;
};
@@ -96,6 +96,14 @@ namespace AzToolsFramework
AZ::u8 m_pickTypes = PickType::None; //!< What mode(s) were we in when picking an EntityId override.
};
//! How a manipulator should treat an adjustment.
//! @note Determines if a transform is applied to an individual entity or the whole group.
enum class Influence
{
Group,
Individual
};
//! What frame/space is the manipulator currently operating in.
enum class ReferenceFrame
{
@@ -328,7 +336,8 @@ namespace AzToolsFramework
OptionalFrame m_pivotOverrideFrame; //!< Has a pivot override been set.
Mode m_mode = Mode::Translation; //!< Manipulator mode - default to translation.
Pivot m_pivotMode = Pivot::Object; //!< Entity pivot mode - default to object (authored root).
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; //!< What reference frame is the Manipulator currently operating in.
ReferenceFrame m_referenceFrame = ReferenceFrame::Local; //!< What reference frame is the Manipulator currently operating in.
Influence m_influence = Influence::Group; //!< What sphere of influence does the Manipulator have.
Frame m_axisPreview; //!< Axes of entity at the time of mouse down to indicate delta of translation.
bool m_triedToRefresh = false; //!< Did a refresh event occur to recalculate the current Manipulator transform.
//! Was EditorTransformComponentSelection responsible for the most recent entity selection change.
@@ -242,11 +242,11 @@ namespace UnitTest
{
// the initial starting position of the entities
AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity1WorldTranslation));
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity1WorldTranslation));
AZ::TransformBus::Event(
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity2WorldTranslation));
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity2WorldTranslation));
AZ::TransformBus::Event(
m_entityId3, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity3WorldTranslation));
m_entityId3, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity3WorldTranslation));
}
static void PositionCamera(AzFramework::CameraState& cameraState)
@@ -261,9 +261,10 @@ namespace UnitTest
AZ::EntityId m_entityId1;
AZ::EntityId m_entityId2;
AZ::EntityId m_entityId3;
AZ::Vector3 m_entity1WorldTranslation = AZ::Vector3(5.0f, 15.0f, 10.0f);
AZ::Vector3 m_entity2WorldTranslation = AZ::Vector3(5.0f, 14.0f, 10.0f);
AZ::Vector3 m_entity3WorldTranslation = AZ::Vector3(5.0f, 16.0f, 10.0f);
static inline const AZ::Vector3 Entity1WorldTranslation = AZ::Vector3(5.0f, 15.0f, 10.0f);
static inline const AZ::Vector3 Entity2WorldTranslation = AZ::Vector3(5.0f, 14.0f, 10.0f);
static inline const AZ::Vector3 Entity3WorldTranslation = AZ::Vector3(5.0f, 16.0f, 10.0f);
};
void ArrangeIndividualRotatedEntitySelection(const AzToolsFramework::EntityIdList& entityIds, const AZ::Quaternion& orientation)
@@ -371,16 +372,16 @@ namespace UnitTest
// Given
AzToolsFramework::SelectEntity(m_entityId1);
ArrangeIndividualRotatedEntitySelection(m_entityIds, AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
const auto entityTransform = AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
ArrangeIndividualRotatedEntitySelection(m_entityIds, entityTransform.GetRotation());
RefreshManipulators(EditorTransformComponentSelectionRequestBus::Events::RefreshType::All);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
// check preconditions - manipulator transform matches parent/world transform (identity)
EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
EXPECT_THAT(manipulatorTransformBefore.GetBasisZ(), IsClose(AZ::Vector3::CreateAxisZ()));
// check preconditions - manipulator transform matches the entity transform
EXPECT_THAT(manipulatorTransformBefore, IsClose(entityTransform));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -624,7 +625,7 @@ namespace UnitTest
EXPECT_TRUE(selectedEntitiesBefore.empty());
// calculate the position in screen space of the initial entity position
const auto entity1ScreenPosition = AzFramework::WorldToScreen(m_entity1WorldTranslation, m_cameraState);
const auto entity1ScreenPosition = AzFramework::WorldToScreen(Entity1WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
@@ -649,7 +650,7 @@ namespace UnitTest
EXPECT_TRUE(selectedEntitiesBefore.empty());
// calculate the position in screen space of the initial entity position
const auto entity1ScreenPosition = AzFramework::WorldToScreen(m_entity1WorldTranslation, m_cameraState);
const auto entity1ScreenPosition = AzFramework::WorldToScreen(Entity1WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(false)
@@ -728,7 +729,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
@@ -754,7 +755,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(false)
@@ -780,7 +781,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
@@ -806,7 +807,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(false)
@@ -832,7 +833,7 @@ namespace UnitTest
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
@@ -858,7 +859,7 @@ namespace UnitTest
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(false)
@@ -1001,7 +1002,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// single click select entity2
m_actionDispatcher->SetStickySelect(false)
@@ -1035,7 +1036,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// single click select entity2
m_actionDispatcher->SetStickySelect(GetParam())
@@ -1056,7 +1057,7 @@ namespace UnitTest
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation));
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
}
TEST_P(
@@ -1069,7 +1070,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// position in space above the entities
const auto clickOffPositionWorld = AZ::Vector3(5.0f, 15.0f, 12.0f);
@@ -1096,7 +1097,7 @@ namespace UnitTest
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation));
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
})
->MousePosition(clickOffPositionScreen)
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
@@ -1113,11 +1114,560 @@ namespace UnitTest
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
// manipulator transform is reset
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity1WorldTranslation));
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity1WorldTranslation));
}
INSTANTIATE_TEST_CASE_P(All, EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam, testing::Values(true, false));
// create alias for EditorTransformComponentSelectionViewportPickingManipulatorTestFixture to help group tests
using EditorTransformComponentSelectionManipulatorInteractionTestFixture =
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture;
// type to group related inputs and outcomes for parameterized tests (single entity)
struct ManipulatorOptionsSingle
{
AzToolsFramework::ViewportInteraction::KeyboardModifier m_keyboardModifier;
AZ::Transform m_expectedManipulatorTransformAfter;
AZ::Transform m_expectedEntityTransformAfter;
};
class EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsSingle>
{
};
TEST_P(
EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam,
RotatingASingleEntityWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
PositionCamera(m_cameraState);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
AzToolsFramework::SelectEntity(m_entityId1);
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(Entity1WorldTranslation, m_cameraState);
const float manipulatorRadius = 2.0f * screenToWorldMultiplier;
const auto rotationManipulatorStartHoldWorldPosition = Entity1WorldTranslation +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-45.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
const auto rotationManipulatorEndHoldWorldPosition = Entity1WorldTranslation +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-135.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
// calculate screen space positions
const auto rotationManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorStartHoldWorldPosition, m_cameraState);
const auto rotationManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorEndHoldWorldPosition, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(rotationManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(rotationManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntityTransform = GetParam().m_expectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransform = GetManipulatorTransform();
const auto entityTransform = AzToolsFramework::GetWorldTransform(m_entityId1);
EXPECT_THAT(*manipulatorTransform, IsClose(expectedManipulatorTransform));
EXPECT_THAT(entityTransform, IsClose(expectedEntityTransform));
}
static const AZ::Transform ExpectedTransformAfterLocalRotationManipulatorMotion = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)),
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation);
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam,
testing::Values(
// this replicates rotating an entity in local space with no modifiers held
// manipulator and entity rotate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating an entity in local space with the alt modifier held
// manipulator and entity rotate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating an entity in world space with the shift modifier held
// entity rotates, manipulator remains aligned to world
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation),
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating the manipulator in local space with the ctrl modifier held (entity is unchanged)
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalRotationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation) }));
// type to group related inputs and outcomes for parameterized tests (two entities)
struct ManipulatorOptionsMultiple
{
AzToolsFramework::ViewportInteraction::KeyboardModifier m_keyboardModifier;
AZ::Transform m_expectedManipulatorTransformAfter;
AZ::Transform m_firstExpectedEntityTransformAfter;
AZ::Transform m_secondExpectedEntityTransformAfter;
};
class EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
TEST_P(
EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam,
RotatingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
PositionCamera(m_cameraState);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
// manipulator should be centered between the two entities
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier =
AzToolsFramework::CalculateScreenToWorldMultiplier(initialManipulatorTransform->GetTranslation(), m_cameraState);
const float manipulatorRadius = 2.0f * screenToWorldMultiplier;
const auto rotationManipulatorStartHoldWorldPosition = initialManipulatorTransform->GetTranslation() +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-45.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
const auto rotationManipulatorEndHoldWorldPosition = initialManipulatorTransform->GetTranslation() +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-135.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
// calculate screen space positions
const auto rotationManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorStartHoldWorldPosition, m_cameraState);
const auto rotationManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorEndHoldWorldPosition, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(rotationManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(rotationManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsClose(expectedManipulatorTransform));
EXPECT_THAT(entity2Transform, IsClose(expectedEntity2Transform));
EXPECT_THAT(entity3Transform, IsClose(expectedEntity3Transform));
}
// note: The aggregate manipulator position will be the average of entity 2 and 3 combined which
// winds up being the same as entity 1
static const AZ::Vector3 AggregateManipulatorPositionWithEntity2and3Selected =
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation;
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion =
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f))) *
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(-1.0f));
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion =
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f))) *
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(1.0f));
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualRotationManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)));
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualRotationManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates rotating a group of entities in local space with no modifiers held
// manipulator and entity rotate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion },
// this replicates rotating a group of entities in local space with the alt modifier held
// manipulator and entity rotate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedEntity2TransformAfterLocalIndividualRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualRotationManipulatorMotion },
// this replicates rotating a group of entities in world space with the shift modifier held
// entity rotates, manipulator remains aligned to world
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation),
ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion },
// this replicates rotating the manipulator in local space with the ctrl modifier held (entity is unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalRotationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
class EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsSingle>
{
};
static const float LinearManipulatorYAxisMovement = -3.0f;
static const float LinearManipulatorZAxisMovement = 2.0f;
TEST_P(
EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam,
TranslatingASingleEntityWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 14.5, 11.0f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
AzToolsFramework::SelectEntity(m_entityId1);
const auto entity1Transform = AzToolsFramework::GetWorldTransform(m_entityId1);
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation(), m_cameraState);
// calculate positions for two click and drag motions (moving a linear manipulator)
// begin each click in the center of the line of the linear manipulators
const auto translationManipulatorStartHoldWorldPosition1 =
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() + entity1Transform.GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement);
const auto translationManipulatorStartHoldWorldPosition2 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement) - entity1Transform.GetBasisY() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition2 =
translationManipulatorStartHoldWorldPosition2 + AZ::Vector3::CreateAxisY(LinearManipulatorYAxisMovement);
// transform to screen space
const auto translationManipulatorStartHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
const auto translationManipulatorStartHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition2, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition2, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(translationManipulatorStartHoldScreenPosition1)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition1)
->MouseLButtonUp()
->MousePosition(translationManipulatorStartHoldScreenPosition2)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition2)
->MouseLButtonUp();
const auto expectedEntityTransform = GetParam().m_expectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransform = GetManipulatorTransform();
const auto entityTransform = AzToolsFramework::GetWorldTransform(m_entityId1);
EXPECT_THAT(*manipulatorTransform, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entityTransform, IsCloseTolerance(expectedEntityTransform, 0.01f));
}
static const AZ::Transform ExpectedTransformAfterLocalTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
// where the manipulator should end up after the input from TranslatingMultipleEntitiesWithDifferentModifierCombinations
static const AZ::Transform ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
AggregateManipulatorPositionWithEntity2and3Selected +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam,
testing::Values(
// this replicates translating an entity in local space with no modifiers held
// manipulator and entity translate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating an entity in local space with the alt modifier held
// manipulator and entity translate (to the user, equivalent to no modifiers with one entity selected)
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating an entity in world space with the shift modifier held
// manipulator and entity translate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating the manipulator in local space with the ctrl modifier held
// entity is unchanged, manipulator moves
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalTranslationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation) }));
class EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
static const AZ::Transform Entity2RotationForLocalTranslation =
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)));
TEST_P(
EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam,
TranslatingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 14.5, 11.0f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
// give entity 2 a different orientation to entity 3 so when moving in local space their translation vectors will be different
AZ::TransformBus::Event(
m_entityId2, &AZ::TransformBus::Events::SetWorldRotationQuaternion, Entity2RotationForLocalTranslation.GetRotation());
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation(), m_cameraState);
// calculate positions for two click and drag motions (moving a linear manipulator)
// begin each click in the center of the line of the linear manipulators
const auto translationManipulatorStartHoldWorldPosition1 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
initialManipulatorTransform->GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement);
const auto translationManipulatorStartHoldWorldPosition2 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement) - initialManipulatorTransform->GetBasisY() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition2 =
translationManipulatorStartHoldWorldPosition2 + AZ::Vector3::CreateAxisY(LinearManipulatorYAxisMovement);
// transform to screen space
const auto translationManipulatorStartHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
const auto translationManipulatorStartHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition2, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition2, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(translationManipulatorStartHoldScreenPosition1)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition1)
->MouseLButtonUp()
->MousePosition(translationManipulatorStartHoldScreenPosition2)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition2)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entity2Transform, IsCloseTolerance(expectedEntity2Transform, 0.01f));
EXPECT_THAT(entity3Transform, IsCloseTolerance(expectedEntity3Transform, 0.01f));
}
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion =
AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement)) *
Entity2RotationForLocalTranslation;
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
// note: as entity has been rotated by 90 degrees about Z in TranslatingMultipleEntitiesWithDifferentModifierCombinations then
// LinearManipulatorYAxisMovement is now aligned to the world x-axis
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualTranslationManipulatorMotion =
AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation +
AZ::Vector3(-LinearManipulatorYAxisMovement, 0.0f, LinearManipulatorZAxisMovement)) *
Entity2RotationForLocalTranslation;
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates translating a group of entities in local space with no modifiers held (group influence)
// manipulator and entity translate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion },
// this replicates translating a group of entities in local space with the alt modifier held
// entities move in their own local space (individual influence)
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalIndividualTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualTranslationManipulatorMotion },
// this replicates translating a group of entities in world space with the shift modifier held
// entities and manipulator move in world space
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion },
// this replicates translating the manipulator in local space with the ctrl modifier held (entities are unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
Entity2RotationForLocalTranslation,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
class EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
static const float LinearManipulatorZAxisMovementScale = 0.5f;
TEST_P(
EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam,
ScalingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.1f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Scale);
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
// manipulator should be centered between the two entities
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier =
AzToolsFramework::CalculateScreenToWorldMultiplier(initialManipulatorTransform->GetTranslation(), m_cameraState);
const auto translationManipulatorStartHoldWorldPosition1 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
initialManipulatorTransform->GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovementScale);
// calculate screen space positions
const auto scaleManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto scaleManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(scaleManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(scaleManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entity2Transform, IsCloseTolerance(expectedEntity2Transform, 0.01f));
EXPECT_THAT(entity3Transform, IsCloseTolerance(expectedEntity3Transform, 0.01f));
}
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, -1.0f, 0.0f)) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 1.0f, 0.0f)) * AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates scaling a group of entities in local space with no modifiers held
// entities scale relative to manipulator pivot
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion },
// this replicates scaling a group of entities in local space with the alt modifier held
// entities scale about their own pivot
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalIndividualScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualScaleManipulatorMotion },
// this replicates scaling a group of entities in world space with the shift modifier held
// entities scale relative to manipulator pivot in world space
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion },
// this has no effect (entities and manipulator are unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
using EditorTransformComponentSelectionManipulatorTestFixture =
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>;
@@ -1661,7 +2211,7 @@ namespace UnitTest
All,
EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
testing::Values(
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Local, PivotOverrideLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Parent, PivotOverrideLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
@@ -0,0 +1,128 @@
/*
* 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 <AzTest/AzTest.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
namespace AzToolsFramework
{
class EditorFocusModeTests
: public ::testing::Test
{
protected:
void SetUp() override
{
m_app.Start(m_descriptor);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
GenerateTestHierarchy();
}
void GenerateTestHierarchy()
{
/*
* City
* |_ Street
* |_ Car
* | |_ Passenger
* |_ SportsCar
* |_ Passenger
*/
m_entityMap["cityId"] = CreateEditorEntity("City", AZ::EntityId());
m_entityMap["streetId"] = CreateEditorEntity("Street", m_entityMap["cityId"]);
m_entityMap["carId"] = CreateEditorEntity("Car", m_entityMap["streetId"]);
m_entityMap["passengerId1"] = CreateEditorEntity("Passenger", m_entityMap["carId"]);
m_entityMap["sportsCarId"] = CreateEditorEntity("SportsCar", m_entityMap["streetId"]);
m_entityMap["passengerId2"] = CreateEditorEntity("Passenger", m_entityMap["sportsCarId"]);
}
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId)
{
AZ::Entity* entity = nullptr;
UnitTest::CreateDefaultEditorEntity(name, &entity);
// Parent
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId);
return entity->GetId();
}
void TearDown() override
{
m_app.Stop();
}
UnitTest::ToolsTestApplication m_app{ "EditorFocusModeTests" };
AZ::ComponentApplication::Descriptor m_descriptor;
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
};
TEST_F(EditorFocusModeTests, EditorFocusModeTests_SetFocus)
{
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
EXPECT_TRUE(focusModeInterface != nullptr);
focusModeInterface->SetFocusRoot(m_entityMap["carId"]);
EXPECT_EQ(focusModeInterface->GetFocusRoot(), m_entityMap["carId"]);
focusModeInterface->ClearFocusRoot();
EXPECT_EQ(focusModeInterface->GetFocusRoot(), AZ::EntityId());
}
TEST_F(EditorFocusModeTests, EditorFocusModeTests_IsInFocusSubTree)
{
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
EXPECT_TRUE(focusModeInterface != nullptr);
focusModeInterface->ClearFocusRoot();
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
focusModeInterface->SetFocusRoot(m_entityMap["streetId"]);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
focusModeInterface->SetFocusRoot(m_entityMap["carId"]);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), false);
focusModeInterface->SetFocusRoot(m_entityMap["passengerId2"]);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
focusModeInterface->ClearFocusRoot();
}
}
@@ -0,0 +1,173 @@
/*
* 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 <AzCore/Component/EntityId.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <Prefab/PrefabTestFixture.h>
namespace UnitTest
{
class PrefabFocusTests
: public PrefabTestFixture
{
protected:
void GenerateTestHierarchy()
{
/*
* City (Prefab Container)
* |_ City
* |_ Street (Prefab Container)
* |_ Car (Prefab Container)
* | |_ Passenger
* |_ SportsCar (Prefab Container)
* |_ Passenger
*/
m_entityMap["passenger1"] = CreateEntity("Passenger1");
m_entityMap["passenger2"] = CreateEntity("Passenger2");
m_entityMap["city"] = CreateEntity("City");
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded,
AzToolsFramework::EntityList{ m_entityMap["passenger1"], m_entityMap["passenger2"], m_entityMap["city"] });
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> carInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger1"] }, {}, "test/car");
ASSERT_TRUE(carInstance);
m_instanceMap["car"] = carInstance.get();
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sportsCarInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger2"] }, {}, "test/sportsCar");
ASSERT_TRUE(sportsCarInstance);
m_instanceMap["sportsCar"] = sportsCarInstance.get();
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> streetInstance =
m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(carInstance), AZStd::move(sportsCarInstance) ), "test/street");
ASSERT_TRUE(streetInstance);
m_instanceMap["street"] = streetInstance.get();
m_rootInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["city"] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city");
ASSERT_TRUE(m_rootInstance);
m_instanceMap["city"] = m_rootInstance.get();
}
AZStd::unordered_map<AZStd::string, AZ::Entity*> m_entityMap;
AZStd::unordered_map<AZStd::string, Instance*> m_instanceMap;
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> m_rootInstance;
};
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab)
{
GenerateTestHierarchy();
PrefabFocusInterface* prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
EXPECT_TRUE(prefabFocusInterface != nullptr);
// Verify FocusOnOwningPrefab works when passing the container entity of the root prefab.
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["city"]);
}
// Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab.
{
prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["city"]->GetId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["city"]);
}
// Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab.
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["car"]->GetContainerEntityId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["car"]);
}
// Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab.
{
prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["passenger1"]->GetId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["car"]);
}
// Verify FocusOnOwningPrefab points to the root prefab when the focus is cleared.
{
AzToolsFramework::PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
AzToolsFramework::Prefab::InstanceOptionalReference rootPrefabInstance =
prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
EXPECT_TRUE(rootPrefabInstance.has_value());
prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), rootPrefabInstance->get().GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), &rootPrefabInstance->get());
}
m_rootInstance.release();
}
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused)
{
GenerateTestHierarchy();
PrefabFocusInterface* prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
EXPECT_TRUE(prefabFocusInterface != nullptr);
// Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested)
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId());
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId()));
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId()));
}
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants)
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["street"]->GetContainerEntityId());
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["street"]->GetContainerEntityId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId()));
}
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings)
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["sportsCar"]->GetContainerEntityId());
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["sportsCar"]->GetContainerEntityId()));
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger2"]->GetId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId()));
}
m_rootInstance.release();
}
}
@@ -6,8 +6,8 @@
*
*/
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/VectorConversions.h>
@@ -20,18 +20,17 @@
namespace UnitTest
{
// transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates
AZ::Vector2 ScreenNDCToWorldToScreenNDC(
const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
// transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates
AZ::Vector2 ScreenNdcToWorldToScreenNdc(const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
{
const auto worldResult = AzFramework::ScreenNDCToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
const auto ndcResult = AzFramework::WorldToScreenNDC(worldResult, CameraView(cameraState), CameraProjection(cameraState));
const auto worldResult =
AzFramework::ScreenNdcToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
const auto ndcResult = AzFramework::WorldToScreenNdc(worldResult, CameraView(cameraState), CameraProjection(cameraState));
return AZ::Vector3ToVector2(ndcResult);
}
// transform a point from screen space to world space, and then from world space back to screen space
AzFramework::ScreenPoint ScreenToWorldToScreen(
const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
AzFramework::ScreenPoint ScreenToWorldToScreen(const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
{
const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState);
return AzFramework::WorldToScreen(worldResult, cameraState);
@@ -47,25 +46,25 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
{
const auto expectedScreenPoint = ScreenPoint{600, 450};
const auto expectedScreenPoint = ScreenPoint{ 600, 450 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
{
const auto expectedScreenPoint = ScreenPoint{400, 300};
const auto expectedScreenPoint = ScreenPoint{ 400, 300 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
{
const auto expectedScreenPoint = ScreenPoint{0, 0};
const auto expectedScreenPoint = ScreenPoint{ 0, 0 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
{
const auto expectedScreenPoint = ScreenPoint{800, 600};
const auto expectedScreenPoint = ScreenPoint{ 800, 600 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
@@ -81,7 +80,7 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto expectedScreenPoint = ScreenPoint{200, 300};
const auto expectedScreenPoint = ScreenPoint{ 200, 300 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
@@ -93,46 +92,46 @@ namespace UnitTest
using AzFramework::ScreenPoint;
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) *
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraTransform =
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{400, 300}, cameraState);
const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{ 400, 300 }, cameraState);
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f)));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// NDC tests
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
{
using NdcPoint = AZ::Vector2;
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f);
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
{
const auto expectedNdcPoint = NdcPoint{0.75f, 0.75f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 0.75f, 0.75f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
{
const auto expectedNdcPoint = NdcPoint{0.5f, 0.5f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 0.5f, 0.5f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
{
const auto expectedNdcPoint = NdcPoint{0.0f, 0.0f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 0.0f, 0.0f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
{
const auto expectedNdcPoint = NdcPoint{1.0f, 1.0f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 1.0f, 1.0f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
}
@@ -147,8 +146,8 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto expectedNdcPoint = NdcPoint{0.25f, 0.5f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 0.25f, 0.5f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
@@ -159,12 +158,13 @@ namespace UnitTest
using NdcPoint = AZ::Vector2;
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) *
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraTransform =
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto worldResult = AzFramework::ScreenNDCToWorld(NdcPoint{0.5f, 0.5f}, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
const auto worldResult =
AzFramework::ScreenNdcToWorld(NdcPoint{ 0.5f, 0.5f }, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f)));
}
@@ -175,7 +175,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenPoint{100, 200} - ScreenPoint{10, 20};
const ScreenVector screenVector = ScreenPoint{ 100, 200 } - ScreenPoint{ 10, 20 };
EXPECT_EQ(screenVector, ScreenVector(90, 180));
}
@@ -184,7 +184,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{100, 200} + ScreenVector{50, 25};
const ScreenPoint screenPoint = ScreenPoint{ 100, 200 } + ScreenVector{ 50, 25 };
EXPECT_EQ(screenPoint, ScreenPoint(150, 225));
}
@@ -193,7 +193,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{120, 200} - ScreenVector{50, 20};
const ScreenPoint screenPoint = ScreenPoint{ 120, 200 } - ScreenVector{ 50, 20 };
EXPECT_EQ(screenPoint, ScreenPoint(70, 180));
}
@@ -202,7 +202,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenVector{100, 200} + ScreenVector{50, 25};
const ScreenVector screenVector = ScreenVector{ 100, 200 } + ScreenVector{ 50, 25 };
EXPECT_EQ(screenVector, ScreenVector(150, 225));
}
@@ -211,7 +211,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenVector{100, 200} - ScreenVector{50, 25};
const ScreenVector screenVector = ScreenVector{ 100, 200 } - ScreenVector{ 50, 25 };
EXPECT_EQ(screenVector, ScreenVector(50, 175));
}
@@ -220,8 +220,8 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{100, 200};
const ScreenVector screenVector = ScreenVector{50, 25};
const ScreenPoint screenPoint = ScreenPoint{ 100, 200 };
const ScreenVector screenVector = ScreenVector{ 50, 25 };
const AZ::Vector2 fromScreenPoint = AzFramework::Vector2FromScreenPoint(screenPoint);
const AZ::Vector2 fromScreenVector = AzFramework::Vector2FromScreenVector(screenVector);
@@ -295,6 +295,58 @@ namespace UnitTest
EXPECT_NEAR(AzFramework::ScreenVectorLength(ScreenVector(12, 15)), 19.20937f, 0.001f);
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarUpwards)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(5, 10);
auto scaledScreenVector = screenVector * 2.0f;
EXPECT_EQ(scaledScreenVector, ScreenVector(10, 20));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarWithRounding)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(1, 6);
auto scaledScreenVector = screenVector * 0.1f;
// value less than 0.5 rounds down, greater than or equal to 0.5 rounds up
EXPECT_EQ(scaledScreenVector, ScreenVector(0, 1));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarWithRoundingAtHalfwayBoundary)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(5, 10);
auto scaledScreenVector = screenVector * 0.1f;
// value less than 0.5 rounds down, greater than or equal to 0.5 rounds up
EXPECT_EQ(scaledScreenVector, ScreenVector(1, 1));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarDownwards)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(6, 12);
auto scaledScreenVector = screenVector * 0.5f;
EXPECT_EQ(scaledScreenVector, ScreenVector(3, 6));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarInplace)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(13, 37);
screenVector *= 10.0f;
EXPECT_EQ(screenVector, ScreenVector(130, 370));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// Other tests
TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack)
@@ -34,6 +34,7 @@ set(FILES
EntityTestbed.h
FileFunc.cpp
FingerprintingTests.cpp
FocusMode/EditorFocusModeTests.cpp
GenericComponentWrapperTest.cpp
InstanceDataHierarchy.cpp
IntegerPrimtitiveTestConfig.h
@@ -50,6 +51,7 @@ set(FILES
Prefab/Benchmark/PrefabLoadBenchmarks.cpp
Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp
Prefab/Benchmark/SpawnableCreateBenchmarks.cpp
Prefab/PrefabFocus/PrefabFocusTests.cpp
Prefab/MockPrefabFileIOActionValidator.cpp
Prefab/MockPrefabFileIOActionValidator.h
Prefab/PrefabDuplicateTests.cpp
@@ -358,8 +358,9 @@ namespace O3DE::ProjectManager
painter.drawPixmap(backgroundRect, m_background);
// Draw a semi-transparent overlay to darken down the colors.
painter.setCompositionMode (QPainter::CompositionMode_DestinationIn);
const float overlayTransparency = 0.7f;
// Use SourceOver, DestinationIn will make background transparent on Mac
painter.setCompositionMode (QPainter::CompositionMode_SourceOver);
const float overlayTransparency = 0.3f;
painter.fillRect(backgroundRect, QColor(0, 0, 0, static_cast<int>(255.0f * overlayTransparency)));
}
@@ -122,6 +122,9 @@ class BatchAnalytics:
)
]
for named_query in self._named_queries:
named_query.node.add_dependency(self._athena_work_group)
@property
def athena_work_group_name(self) -> athena.CfnWorkGroup.name:
return self._athena_work_group.name
@@ -12,6 +12,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <Atom/Feature/RenderCommon.h>
#include <Atom/Feature/CoreLights/EsmShadowmapsPassData.h>
#include <Atom/Feature/CoreLights/PhotometricValue.h>
#include <Atom/Feature/CoreLights/ShadowConstants.h>
@@ -33,8 +34,6 @@
#include <CoreLights/ShadowmapPass.h>
#include <CoreLights/ProjectedShadowmapsPass.h>
#include <RenderCommon.h>
namespace AZ
{
namespace Render
@@ -18,6 +18,7 @@
#include <Atom/Utils/DdsFile.h>
#include <Atom/Utils/PpmFile.h>
#include <Atom/Utils/PngFile.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzCore/Jobs/JobFunction.h>
@@ -34,21 +35,12 @@
#include <AzCore/Preprocessor/EnumReflectUtils.h>
#include <AzCore/Console/Console.h>
#if defined(OPEN_IMAGE_IO_ENABLED)
// OpenImageIO/fmath.h(2271,5): error C4777: 'fprintf' : format string '%zd' requires an argument of type 'unsigned __int64', but variadic
// argument 5 has type 'OpenImageIO_v2_1::span_strided<const float,-1>::index_type'
AZ_PUSH_DISABLE_WARNING(4777, "-Wunknown-warning-option")
#include <OpenImageIO/imageio.h>
AZ_POP_DISABLE_WARNING
#endif
namespace AZ
{
namespace Render
{
AZ_ENUM_DEFINE_REFLECT_UTILITIES(FrameCaptureResult);
#if defined(OPEN_IMAGE_IO_ENABLED)
AZ_CVAR(unsigned int,
r_pngCompressionLevel,
3, // A compression level of 3 seems like the best default in terms of file size and saving speeds
@@ -97,28 +89,22 @@ namespace AZ
jobCompletion.StartAndWaitForCompletion();
}
using namespace OIIO;
AZStd::unique_ptr<ImageOutput> out = ImageOutput::create(outputFilePath.c_str());
if (out)
{
ImageSpec spec(
readbackResult.m_imageDescriptor.m_size.m_width,
readbackResult.m_imageDescriptor.m_size.m_height,
numChannels
);
spec.attribute("png:compressionLevel", r_pngCompressionLevel);
Utils::PngFile image = Utils::PngFile::Create(readbackResult.m_imageDescriptor.m_size, readbackResult.m_imageDescriptor.m_format, *buffer);
if (out->open(outputFilePath.c_str(), spec))
{
out->write_image(TypeDesc::UINT8, buffer->data());
out->close();
return FrameCaptureOutputResult{FrameCaptureResult::Success, AZStd::nullopt};
}
Utils::PngFile::SaveSettings saveSettings;
saveSettings.m_compressionLevel = r_pngCompressionLevel;
// We should probably strip alpha to save space, especially for automated test screenshots. Alpha is left in to maintain
// prior behavior, changing this is out of scope for the current task. Note, it would have bit of a cascade effect where
// AtomSampleViewer's ScriptReporter assumes an RGBA image.
saveSettings.m_stripAlpha = false;
if(image && image.Save(outputFilePath.c_str(), saveSettings))
{
return FrameCaptureOutputResult{FrameCaptureResult::Success, AZStd::nullopt};
}
return FrameCaptureOutputResult{FrameCaptureResult::InternalError, "Unable to save frame capture output to " + outputFilePath};
return FrameCaptureOutputResult{FrameCaptureResult::InternalError, "Unable to save frame capture output to '" + outputFilePath + "'"};
}
#endif
FrameCaptureOutputResult DdsFrameCaptureOutput(
const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
@@ -502,7 +488,6 @@ namespace AZ
m_result = ddsFrameCapture.m_result;
m_latestCaptureInfo = ddsFrameCapture.m_errorMessage.value_or("");
}
#if defined(OPEN_IMAGE_IO_ENABLED)
else if (extension == "png")
{
if (readbackResult.m_imageDescriptor.m_format == RHI::Format::R8G8B8A8_UNORM ||
@@ -523,7 +508,6 @@ namespace AZ
m_result = FrameCaptureResult::UnsupportedFormat;
}
}
#endif
else
{
m_latestCaptureInfo = AZStd::string::format("Only supports saving image to ppm or dds files");
@@ -6,10 +6,9 @@
*
*/
#include <RenderCommon.h>
#include <Atom/RHI/RHIUtils.h>
#include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
#include <Atom/Feature/RenderCommon.h>
#include <Atom/Feature/Mesh/MeshFeatureProcessor.h>
#include <Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h>
#include <Atom/RPI.Public/Model/ModelLodUtils.h>
@@ -85,7 +84,6 @@ namespace AZ
{
const auto jobLambda = [&]() -> void
{
AZ_PROFILE_SCOPE(AzRender, "MeshFP::Simulate() Lambda");
for (auto meshDataIter = iteratorRange.first; meshDataIter != iteratorRange.second; ++meshDataIter)
{
if (!meshDataIter->m_model)
@@ -12,12 +12,5 @@ endif()
set(LY_BUILD_DEPENDENCIES
PRIVATE
3rdParty::OpenImageIO
3rdParty::ilmbase
)
# [GFX-TODO] Add macro defintion in OpenImageIO 3rd party find cmake file
set(LY_COMPILE_DEFINITIONS
PRIVATE
OPEN_IMAGE_IO_ENABLED
)
@@ -9,7 +9,7 @@
#pragma once
#include <AzCore/Math/Transform.h>
#include <RenderCommon.h>
#include <Atom/Feature/RenderCommon.h>
#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/Model/Model.h>
@@ -7,6 +7,7 @@
#
set(FILES
Include/Atom/Feature/RenderCommon.h
Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h
Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl
Include/Atom/Feature/Utils/EditorLightingPreset.h
@@ -16,7 +17,6 @@ set(FILES
Source/EditorCommonSystemComponent.cpp
Source/EditorCommonSystemComponent.h
Source/CommonModule.cpp
Source/RenderCommon.h
Source/Material/ConvertEmissiveUnitFunctorSourceData.cpp
Source/Material/ConvertEmissiveUnitFunctorSourceData.h
Source/Material/MaterialConverterSystemComponent.cpp
@@ -8,6 +8,7 @@
set(FILES
3rdParty/ACES/ACES/Aces.h
Include/Atom/Feature/RenderCommon.h
Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h
Include/Atom/Feature/Automation/AtomAutomationBus.h
Include/Atom/Feature/AuxGeom/AuxGeomFeatureProcessor.h
@@ -52,7 +53,6 @@ set(FILES
Source/FrameCaptureSystemComponent.h
Source/ProfilingCaptureSystemComponent.cpp
Source/ProfilingCaptureSystemComponent.h
Source/RenderCommon.h
3rdParty/ACES/ACES/Aces.cpp
Source/ACES/AcesDisplayMapperFeatureProcessor.cpp
Source/AuxGeom/AuxGeomBase.h
@@ -28,25 +28,19 @@ namespace AZ
size_t m_accumulatedInBytes = 0;
};
/**
* Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints:
*
* 1) Reserved <= Budget (unless the budget is 0).
* 2) Resident <= Reserved.
*/
//! Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints:
//! 1) Reserved <= Budget (unless the budget is 0).
//! 2) Resident <= Reserved.
struct HeapMemoryUsage
{
HeapMemoryUsage() = default;
HeapMemoryUsage(const HeapMemoryUsage&);
HeapMemoryUsage& operator=(const HeapMemoryUsage&);
/**
* This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely
* reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected.
*
* @param sizeInBytes The amount of bytes to reserve.
* @return Whether the reservation was successful.
*/
//! This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely
//! reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected.
//! @param sizeInBytes The amount of bytes to reserve.
//! @return Whether the reservation was successful.
bool TryReserveMemory(size_t sizeInBytes)
{
const size_t reservationInBytes = (m_reservedInBytes += sizeInBytes);
@@ -60,45 +54,41 @@ namespace AZ
return true;
}
/**
* Helper function to validate sizes
*/
//! Helper function to validate sizes
void Validate()
{
if (Validation::IsEnabled())
{
AZ_Assert(m_budgetInBytes >= m_reservedInBytes, "Reserved memory is larger than memory budget");
AZ_Assert(m_reservedInBytes >= m_residentInBytes, "Resident memory is larger than reserved memory");
AZ_Assert(
m_budgetInBytes >= m_reservedInBytes,
"Reserved memory is larger than memory budget. Memory budget %zu Reserved %zu", m_budgetInBytes, m_reservedInBytes.load());
AZ_Assert(
m_reservedInBytes >= m_residentInBytes,
"Resident memory is larger than reserved memory. Reserved Memory %zu Resident memory %zu", m_reservedInBytes.load(),
m_residentInBytes.load());
}
}
/**
* The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests
* once the budget is exceeded. A zero budget effectively disables this check. On certain platforms,
* it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain
* heaps.
*/
// The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests
// once the budget is exceeded. A zero budget effectively disables this check. On certain platforms,
// it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain
// heaps.
size_t m_budgetInBytes = 0;
/**
* Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for
* the platform. It is validated against the budget and may not exceed it.
*/
// Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for
// the platform. It is validated against the budget and may not exceed it.
AZStd::atomic_size_t m_reservedInBytes{ 0 };
/**
* Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms
* may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver.
*/
// Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms
// may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver.
AZStd::atomic_size_t m_residentInBytes{ 0 };
};
/**
* Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single
* device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms
* may not require one or the other. In this case, the memory usage / budget will report empty values for
* that heap type.
*/
//!
//! Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single
//! device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms
//! may not require one or the other. In this case, the memory usage / budget will report empty values for
//! that heap type.
struct PoolMemoryUsage
{
PoolMemoryUsage() = default;

Some files were not shown because too many files have changed in this diff Show More