diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg new file mode 100644 index 0000000000..f1d36d3e41 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg new file mode 100644 index 0000000000..9fc9fe52c2 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg new file mode 100644 index 0000000000..7a61db38e0 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg new file mode 100644 index 0000000000..c6fb977c52 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Assets/Editor/Translation/scriptcanvas_en_us.ts b/Assets/Editor/Translation/scriptcanvas_en_us.ts index 937f6a4d96..7b8361c879 100644 --- a/Assets/Editor/Translation/scriptcanvas_en_us.ts +++ b/Assets/Editor/Translation/scriptcanvas_en_us.ts @@ -8098,7 +8098,7 @@ COLOR_FROMVALUES_PARAM0_TOOLTIP - The Red value of hte Color [0, 255] + The Red value of the Color [0.0-1.0] COLOR_FROMVALUES_PARAM1_NAME @@ -8107,7 +8107,7 @@ COLOR_FROMVALUES_PARAM1_TOOLTIP - The Green value of the Color [0, 255] + The Green value of the Color [0.0-1.0] COLOR_FROMVALUES_PARAM2_NAME @@ -8116,7 +8116,7 @@ COLOR_FROMVALUES_PARAM2_TOOLTIP - The Blue value of the Color [0, 255] + The Blue value of the Color [0.0-1.0] COLOR_FROMVALUES_PARAM3_NAME @@ -8125,7 +8125,7 @@ COLOR_FROMVALUES_PARAM3_TOOLTIP - The Alpha value of the Color [0, 255] + The Alpha value of the Color [0.0-1.0] diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt index 4a26500ee2..52682e70bb 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt @@ -17,5 +17,14 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AssetProcessorBatch AZ::AssetProcessor ) + + ly_add_pytest( + NAME AssetPipelineTests.Fbx_Tests + PATH ${CMAKE_CURRENT_LIST_DIR}/fbx_test/fbx_test.py + TEST_SUITE sandbox + RUNTIME_DEPENDENCIES + AZ::AssetProcessorBatch + AZ::AssetProcessor + ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py index 2b90f53fc3..5cb61da68e 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py @@ -34,11 +34,13 @@ logger = logging.getLogger(__name__) targetProjects = ["AutomatedTesting"] @pytest.fixture +@pytest.mark.SUITE_sandbox def local_resources(request, workspace, ap_setup_fixture): ap_setup_fixture["tests_dir"] = os.path.dirname(os.path.realpath(__file__)) @dataclass +@pytest.mark.SUITE_sandbox class BlackboxAssetTest: test_name: str asset_folder: str @@ -338,9 +340,11 @@ blackbox_fbx_special_tests = [ @pytest.mark.usefixtures("local_resources") @pytest.mark.parametrize("project", targetProjects) @pytest.mark.assetpipeline +@pytest.mark.SUITE_sandbox class TestsFBX_AllPlatforms(object): @pytest.mark.BAT + @pytest.mark.SUITE_sandbox @pytest.mark.parametrize("blackbox_param", blackbox_fbx_tests) def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace, ap_setup_fixture, asset_processor, project, @@ -359,6 +363,7 @@ class TestsFBX_AllPlatforms(object): asset_processor, project, blackbox_param) @pytest.mark.BAT + @pytest.mark.SUITE_sandbox @pytest.mark.parametrize("blackbox_param", blackbox_fbx_special_tests) def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(self, workspace, ap_setup_fixture, diff --git a/Code/Editor/AssetEditor/AssetEditorRequestsHandler.h b/Code/Editor/AssetEditor/AssetEditorRequestsHandler.h index ebfa84249d..bbe55e216a 100644 --- a/Code/Editor/AssetEditor/AssetEditorRequestsHandler.h +++ b/Code/Editor/AssetEditor/AssetEditorRequestsHandler.h @@ -7,7 +7,6 @@ */ #pragma once -#include #include #include diff --git a/Code/Editor/AssetEditor/AssetEditorWindow.h b/Code/Editor/AssetEditor/AssetEditorWindow.h index 2cf73ac282..63347d6ff3 100644 --- a/Code/Editor/AssetEditor/AssetEditorWindow.h +++ b/Code/Editor/AssetEditor/AssetEditorWindow.h @@ -9,7 +9,6 @@ #if !defined(Q_MOC_RUN) #include -#include #include #include #include diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 18946c7874..9fe4cfd0d2 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -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(leftLayout->geometry().width()); + const float oldWidth = aznumeric_cast(leftLayout->geometry().width() + rightLayout->geometry().width()); + + const float newWidth = oldLeftLayoutWidth * aznumeric_cast(resizeEvent->size().width()) / oldWidth; + + emit SizeChangedSignal(aznumeric_cast(newWidth)); + QWidget::resizeEvent(resizeEvent); +} + void AzAssetBrowserWindow::OnInitViewToggleButton() { CreateSwitchViewMenu(); diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index bf742b0cf2..753f000300 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -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(); diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.cpp b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.cpp index 40ea71f577..1151137bfc 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.cpp +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.cpp @@ -26,6 +26,9 @@ #include #include +//AzCore +#include + // Editor #include "Clipboard.h" diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.h b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.h index 64a4341096..ed5d1915ac 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.h +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.h @@ -12,7 +12,6 @@ #if !defined(Q_MOC_RUN) #include -#include #include #include "Include/EditorCoreAPI.h" #include "ReflectedPropertyItem.h" diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVar.h b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVar.h index a15f8326d1..7c38465c5e 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVar.h +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVar.h @@ -10,10 +10,10 @@ #define CRYINCLUDE_EDITOR_UTILS_REFLECTEDVAR_H #pragma once -#include #include #include #include "Util/VariablePropertyType.h" +#include #include #include #include diff --git a/Code/Editor/Core/QtEditorApplication.cpp b/Code/Editor/Core/QtEditorApplication.cpp index 66ed42af8f..a4aab24be4 100644 --- a/Code/Editor/Core/QtEditorApplication.cpp +++ b/Code/Editor/Core/QtEditorApplication.cpp @@ -44,7 +44,7 @@ enum { // in milliseconds GameModeIdleFrequency = 0, - EditorModeIdleFrequency = 0, + EditorModeIdleFrequency = 1, InactiveModeFrequency = 10, UninitializedFrequency = 9999, }; diff --git a/Code/Editor/Core/QtEditorApplication_linux.cpp b/Code/Editor/Core/QtEditorApplication_linux.cpp index 175bef0238..2fd4ef629e 100644 --- a/Code/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Core/QtEditorApplication_linux.cpp @@ -9,7 +9,7 @@ #include "QtEditorApplication.h" #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB -#include +#include #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(message)); + AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast(message)); #endif return true; } diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index e375e98fb6..29cf348ab5 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include 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(SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan); + m_firstPersonPanCamera = AZStd::make_shared( + 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::LookTranslation, translateCameraInputChannelIds); + m_firstPersonTranslateCamera = AZStd::make_shared( + translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot); m_firstPersonTranslateCamera->m_translateSpeedFn = [] { @@ -171,120 +172,97 @@ namespace SandboxEditor return SandboxEditor::CameraScrollSpeed(); }; - m_orbitCamera = AZStd::make_shared(SandboxEditor::CameraOrbitChannelId()); + m_pivotCamera = AZStd::make_shared(SandboxEditor::CameraPivotChannelId()); - m_orbitCamera->SetLookAtFn( - [viewportId = m_viewportId](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional + m_pivotCamera->SetPivotFn( + []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) { - AZStd::optional lookAtAfterInterpolation; - AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( - lookAtAfterInterpolation, viewportId, - &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::LookAtAfterInterpolation); - - // initially attempt to use the last set look at point after an interpolation has finished - if (lookAtAfterInterpolation.has_value()) - { - 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; - - 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 {}; + // use the manipulator transform as the pivot point + AZStd::optional entityPivot; + AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( + entityPivot, AzToolsFramework::GetEntityContextId(), + &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); + + // otherwise just use the identity + return entityPivot.value_or(AZ::Transform::CreateIdentity()).GetTranslation(); }); - m_orbitRotateCamera = AZStd::make_shared(SandboxEditor::CameraOrbitLookChannelId()); + m_pivotRotateCamera = AZStd::make_shared(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::OrbitTranslation, translateCameraInputChannelIds); + m_pivotTranslateCamera = AZStd::make_shared( + 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(); + m_pivotDollyScrollCamera = AZStd::make_shared(); - m_orbitDollyScrollCamera->m_scrollSpeedFn = [] + m_pivotDollyScrollCamera->m_scrollSpeedFn = [] { return SandboxEditor::CameraScrollSpeed(); }; - m_orbitDollyMoveCamera = - AZStd::make_shared(SandboxEditor::CameraOrbitDollyChannelId()); + m_pivotDollyMoveCamera = AZStd::make_shared(SandboxEditor::CameraPivotDollyChannelId()); - m_orbitDollyMoveCamera->m_cursorSpeedFn = [] + m_pivotDollyMoveCamera->m_motionSpeedFn = [] { return SandboxEditor::CameraDollyMotionSpeed(); }; - m_orbitPanCamera = AZStd::make_shared(SandboxEditor::CameraOrbitPanChannelId(), AzFramework::OrbitPan); + m_pivotPanCamera = AZStd::make_shared( + 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 +273,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 { diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index e6e71c976c..e691ca1c89 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -42,12 +42,12 @@ namespace SandboxEditor AZStd::shared_ptr m_firstPersonPanCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; AZStd::shared_ptr m_firstPersonScrollCamera; - AZStd::shared_ptr m_orbitCamera; - AZStd::shared_ptr m_orbitRotateCamera; - AZStd::shared_ptr m_orbitTranslateCamera; - AZStd::shared_ptr m_orbitDollyScrollCamera; - AZStd::shared_ptr m_orbitDollyMoveCamera; - AZStd::shared_ptr m_orbitPanCamera; + AZStd::shared_ptr m_pivotCamera; + AZStd::shared_ptr m_pivotRotateCamera; + AZStd::shared_ptr m_pivotTranslateCamera; + AZStd::shared_ptr m_pivotDollyScrollCamera; + AZStd::shared_ptr m_pivotDollyMoveCamera; + AZStd::shared_ptr m_pivotPanCamera; AzFramework::ViewportId m_viewportId; }; diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.cpp b/Code/Editor/EditorPreferencesPageViewportCamera.cpp index 368d2769f8..55b631e1f6 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.cpp +++ b/Code/Editor/EditorPreferencesPageViewportCamera.cpp @@ -61,7 +61,7 @@ static AZStd::vector GetEditorInputNames() void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serialize) { serialize.Class() - ->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() - ->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() ->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("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(); } diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.h b/Code/Editor/EditorPreferencesPageViewportCamera.h index 31a728599f..01dc4664f6 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.h +++ b/Code/Editor/EditorPreferencesPageViewportCamera.h @@ -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; diff --git a/Code/Editor/EditorPreferencesTreeWidgetItem.cpp b/Code/Editor/EditorPreferencesTreeWidgetItem.cpp index 7adc298c2c..09578e72ee 100644 --- a/Code/Editor/EditorPreferencesTreeWidgetItem.cpp +++ b/Code/Editor/EditorPreferencesTreeWidgetItem.cpp @@ -9,6 +9,9 @@ #include "EditorPreferencesTreeWidgetItem.h" +// AzCore +#include + // AzToolsFramework #include diff --git a/Code/Editor/EditorViewportCamera.cpp b/Code/Editor/EditorViewportCamera.cpp index 0c7a1559d1..b25e7072ff 100644 --- a/Code/Editor/EditorViewportCamera.cpp +++ b/Code/Editor/EditorViewportCamera.cpp @@ -48,7 +48,7 @@ namespace SandboxEditor { AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( viewportContext->GetId(), &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, - AZ::Transform::CreateFromQuaternionAndTranslation(CameraRotation(pitch, yaw), position), 0.0f); + AZ::Transform::CreateFromQuaternionAndTranslation(CameraRotation(pitch, yaw), position)); } } diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index 063ec125ba..fe8efecd17 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -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 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 diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index 20d397a29e..d1271017c6 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -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 diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index 5b5b52d28f..dad6734428 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -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); } } diff --git a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp index affe5794cc..1a44d43370 100644 --- a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp +++ b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp @@ -167,7 +167,7 @@ namespace UnitTest AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f)); AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, - transformToInterpolateTo, 0.0f); + transformToInterpolateTo); // simulate interpolation m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); @@ -193,7 +193,7 @@ namespace UnitTest // When AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, - transformToInterpolateTo, 0.0f); + transformToInterpolateTo); // simulate interpolation m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); diff --git a/Code/Editor/MainWindow.cpp b/Code/Editor/MainWindow.cpp index beb338b732..05477bb0c7 100644 --- a/Code/Editor/MainWindow.cpp +++ b/Code/Editor/MainWindow.cpp @@ -27,10 +27,11 @@ AZ_POP_DISABLE_WARNING #endif // AzCore -#include #include -#include #include +#include +#include +#include #include // AzFramework diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index 47bd214b8d..666700a874 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -1739,8 +1739,7 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework:: AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( viewportContext->GetId(), - &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform, - distanceToLookAt); + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform); } } } diff --git a/Code/Editor/PythonEditorFuncs.cpp b/Code/Editor/PythonEditorFuncs.cpp index 455cdfa17d..1b731a3d5f 100644 --- a/Code/Editor/PythonEditorFuncs.cpp +++ b/Code/Editor/PythonEditorFuncs.cpp @@ -18,6 +18,7 @@ #include // AzToolsFramework +#include #include #include diff --git a/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp b/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp index 6b3f1c2633..b510315995 100644 --- a/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp +++ b/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp @@ -36,9 +36,6 @@ #include "CryEdit.h" #include "Viewport.h" -// Atom Renderer -#include - AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING #include AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING @@ -1237,13 +1234,6 @@ void CSequenceBatchRenderDialog::OnKickIdleTimout() { componentApplication->TickSystem(); } - - // Directly tick the renderer, as it's no longer part of the system tick - if (auto rpiSystem = AZ::RPI::RPISystemInterface::Get()) - { - rpiSystem->SimulationTick(); - rpiSystem->RenderTick(); - } } } diff --git a/Code/Editor/TrackView/TrackViewPythonFuncs.cpp b/Code/Editor/TrackView/TrackViewPythonFuncs.cpp index 72da1e0b18..dc55411f80 100644 --- a/Code/Editor/TrackView/TrackViewPythonFuncs.cpp +++ b/Code/Editor/TrackView/TrackViewPythonFuncs.cpp @@ -19,6 +19,7 @@ // Editor #include "AnimationContext.h" +#include namespace { diff --git a/Code/Editor/ViewportTitleDlg.cpp b/Code/Editor/ViewportTitleDlg.cpp index 65d6de8944..75d16e9a40 100644 --- a/Code/Editor/ViewportTitleDlg.cpp +++ b/Code/Editor/ViewportTitleDlg.cpp @@ -15,6 +15,7 @@ #include "ViewportTitleDlg.h" // Qt +#include #include #include @@ -36,8 +37,10 @@ #include "MathConversion.h" #include "EditorViewportSettings.h" -#include +#include #include +#include +#include #include #include @@ -145,6 +148,11 @@ CViewportTitleDlg::~CViewportTitleDlg() AZ::VR::VREventBus::Handler::BusDisconnect(); GetISystem()->GetISystemEventDispatcher()->RemoveListener(this); GetIEditor()->UnregisterNotifyListener(this); + + if (m_prefabViewportFocusPathHandler) + { + delete m_prefabViewportFocusPathHandler; + } } void CViewportTitleDlg::SetupCameraDropdownMenu() @@ -292,8 +300,6 @@ void CViewportTitleDlg::SetViewPane(CLayoutViewPane* pViewPane) ////////////////////////////////////////////////////////////////////////// void CViewportTitleDlg::OnInitDialog() { - m_ui->m_titleBtn->setText(m_title); - // Add a child parented to us that listens for r_displayInfo changes. auto displayInfoHelper = new CViewportTitleDlgDisplayInfoHelper(this); connect(displayInfoHelper, &CViewportTitleDlgDisplayInfoHelper::ViewportInfoStatusUpdated, this, &CViewportTitleDlg::UpdateDisplayInfo); @@ -314,13 +320,28 @@ void CViewportTitleDlg::OnInitDialog() m_cameraSpeed->setFixedWidth(width); + bool isPrefabSystemEnabled = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); + + if (isPrefabSystemEnabled) + { + m_prefabViewportFocusPathHandler = new AzToolsFramework::Prefab::PrefabViewportFocusPathHandler(); + m_prefabViewportFocusPathHandler->Initialize(m_ui->m_prefabFocusPath, m_ui->m_prefabFocusBackButton); + } + else + { + m_ui->m_prefabFocusPath->setEnabled(false); + m_ui->m_prefabFocusBackButton->setEnabled(false); + m_ui->m_prefabFocusPath->hide(); + m_ui->m_prefabFocusBackButton->hide(); + } + } ////////////////////////////////////////////////////////////////////////// void CViewportTitleDlg::SetTitle(const QString& title) { m_title = title; - m_ui->m_titleBtn->setText(m_title); } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/ViewportTitleDlg.h b/Code/Editor/ViewportTitleDlg.h index 4a2a454907..6996fe7750 100644 --- a/Code/Editor/ViewportTitleDlg.h +++ b/Code/Editor/ViewportTitleDlg.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -176,6 +177,8 @@ protected: QWidgetAction* m_gridSizeActionWidget = nullptr; QWidgetAction* m_angleSizeActionWidget = nullptr; + AzToolsFramework::Prefab::PrefabViewportFocusPathHandler* m_prefabViewportFocusPathHandler = nullptr; + QScopedPointer m_ui; }; diff --git a/Code/Editor/ViewportTitleDlg.ui b/Code/Editor/ViewportTitleDlg.ui index f0679dd2a1..7ba6361cf9 100644 --- a/Code/Editor/ViewportTitleDlg.ui +++ b/Code/Editor/ViewportTitleDlg.ui @@ -28,7 +28,7 @@ 29 - + 10 @@ -42,102 +42,112 @@ 0 - + + + Up one level + + + + :/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg:/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg + + + + + 0 0 - - Qt::NoContextMenu + + + + + + Qt::Horizontal + + + + 40 + 20 + - - Static + + + + + + Camera settings - - 11 + + + :/Menu/camera.svg:/Menu/camera.svg - - - - :/Menu/camera.svg:/Menu/camera.svg - - - - Camera settings - - - - - - - Debug information - - - - :/Menu/debug.svg:/Menu/debug.svg - - - - true - - - + + + Debug information + + + + :/Menu/debug.svg:/Menu/debug.svg + + + true + + + - - - Toggle viewport helpers - - - - :/Menu/helpers.svg:/Menu/helpers.svg - - - - true - - - + + + Toggle viewport helpers + + + + :/Menu/helpers.svg:/Menu/helpers.svg + + + true + + + - - - Viewport resolution - - - - :/Menu/resolution.svg:/Menu/resolution.svg - - - + + + Viewport resolution + + + + :/Menu/resolution.svg:/Menu/resolution.svg + + - - - Other settings - - - - :/Menu/menu.svg:/Menu/menu.svg - - - + + + Other settings + + + + :/Menu/menu.svg:/Menu/menu.svg + + - AzQtComponents::ButtonDivider + AzQtComponents::BreadCrumbs QWidget -
AzQtComponents/Components/ButtonDivider.h
+
AzQtComponents/Components/Widgets/BreadCrumbs.h
1
- - - - + + + + diff --git a/Code/Framework/AtomCore/AtomCore/Instance/InstanceData.h b/Code/Framework/AtomCore/AtomCore/Instance/InstanceData.h index 33fc93c0f5..6b07d12e9c 100644 --- a/Code/Framework/AtomCore/AtomCore/Instance/InstanceData.h +++ b/Code/Framework/AtomCore/AtomCore/Instance/InstanceData.h @@ -89,6 +89,9 @@ namespace AZ // Tracks the asset type used to create the instance. AssetType m_assetType; + + // Boolean to indicate if the instance has been orphaned from the instance database + bool m_isOrphaned = false; }; /// @cond EXCLUDE_DOCS diff --git a/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h b/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h index ac97af3629..4b4ad572c2 100644 --- a/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h +++ b/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h @@ -203,6 +203,16 @@ namespace AZ //! Calls FindOrCreate using a random InstanceId Data::Instance Create(const Asset& asset, const AZStd::any* param = nullptr); + /** + * Removes the instance data from the database. Does not release it. + * References to existing instances will remain valid, but new calls to Create/FindOrCreate will create a new instance + * This function is temporary, to provide functionality needed for Model hot-reloading, but will be removed + * once the Model class does not need it anymore. + * + * @param id The id of the instance to remove + */ + void TEMPOrphan(const InstanceId& id); + private: InstanceDatabase(const AssetType& assetType); ~InstanceDatabase(); @@ -356,6 +366,20 @@ namespace AZ return FindOrCreate(Data::InstanceId::CreateRandom(), asset, param); } + template + void InstanceDatabase::TEMPOrphan(const InstanceId& id) + { + AZStd::scoped_lock lock(m_databaseMutex); + // Check if the instance is still in the database, in case it was orphaned twice + auto instanceItr = m_database.find(id); + if (instanceItr != m_database.end()) + { + // Mark the instance as orphaned, and remove it from the database + instanceItr->second->m_isOrphaned = true; + m_database.erase(instanceItr); + } + } + template void InstanceDatabase::ReleaseInstance(InstanceData* instance, const InstanceId& instanceId) { @@ -374,6 +398,12 @@ namespace AZ m_database.erase(instance->GetId()); m_instanceHandler.m_deleteFunction(static_cast(instance)); } + else if (instance->m_isOrphaned && instance->m_useCount.compare_exchange_strong(expectedRefCount, -1)) + { + // If the instance was orphaned, it has already been removed from the database, + // but still needs to be deleted when the refcount drops to 0 + m_instanceHandler.m_deleteFunction(static_cast(instance)); + } } template diff --git a/Code/Framework/AtomCore/Tests/InstanceDatabase.cpp b/Code/Framework/AtomCore/Tests/InstanceDatabase.cpp index 5d1edc5a09..6a5c65ac7a 100644 --- a/Code/Framework/AtomCore/Tests/InstanceDatabase.cpp +++ b/Code/Framework/AtomCore/Tests/InstanceDatabase.cpp @@ -181,7 +181,76 @@ namespace UnitTest EXPECT_EQ(instance, instance3); } - void ParallelInstanceCreateHelper(size_t threadCountMax, size_t assetIdCount, size_t durationSeconds) + TEST_F(InstanceDatabaseTest, InstanceOrphan) + { + auto& assetManager = AssetManager::Instance(); + auto& instanceDatabase = InstanceDatabase::Instance(); + + Asset someAsset = assetManager.CreateAsset(s_assetId0, AZ::Data::AssetLoadBehavior::Default); + + Instance orphanedInstance = instanceDatabase.FindOrCreate(s_instanceId0, someAsset); + EXPECT_NE(orphanedInstance, nullptr); + + instanceDatabase.TEMPOrphan(s_instanceId0); + // After orphan, the instance should not be found in the database, but it should still be valid + EXPECT_EQ(instanceDatabase.Find(s_instanceId0), nullptr); + EXPECT_NE(orphanedInstance, nullptr); + + instanceDatabase.TEMPOrphan(s_instanceId0); + // Orphaning twice should be a no-op + EXPECT_EQ(instanceDatabase.Find(s_instanceId0), nullptr); + EXPECT_NE(orphanedInstance, nullptr); + + Instance instance2 = instanceDatabase.FindOrCreate(s_instanceId0, someAsset); + // Creating another instance with the same id should return a different instance than the one that was orphaned + EXPECT_NE(orphanedInstance, instance2); + } + + enum class ParallelInstanceTestCases + { + Create, + CreateAndDeferRemoval, + CreateAndOrphan, + CreateDeferRemovalAndOrphan + }; + + enum class ParralleInstanceCurrentAction + { + Create, + DeferredRemoval, + Orphan + }; + + ParralleInstanceCurrentAction ParallelInstanceGetCurrentAction(ParallelInstanceTestCases testCase) + { + switch (testCase) + { + case ParallelInstanceTestCases::CreateAndDeferRemoval: + switch (rand() % 2) + { + case 0: return ParralleInstanceCurrentAction::Create; + case 1: return ParralleInstanceCurrentAction::DeferredRemoval; + } + case ParallelInstanceTestCases::CreateAndOrphan: + switch (rand() % 2) + { + case 0: return ParralleInstanceCurrentAction::Create; + case 1: return ParralleInstanceCurrentAction::Orphan; + } + case ParallelInstanceTestCases::CreateDeferRemovalAndOrphan: + switch (rand() % 3) + { + case 0: return ParralleInstanceCurrentAction::Create; + case 1: return ParralleInstanceCurrentAction::DeferredRemoval; + case 2: return ParralleInstanceCurrentAction::Orphan; + } + case ParallelInstanceTestCases::Create: + default: + return ParralleInstanceCurrentAction::Create; + } + } + + void ParallelInstanceCreateHelper(size_t threadCountMax, size_t assetIdCount, float durationSeconds, ParallelInstanceTestCases testCase) { printf("Testing threads=%zu assetIds=%zu ... ", threadCountMax, assetIdCount); @@ -192,6 +261,7 @@ namespace UnitTest auto& instanceManager = InstanceDatabase::Instance(); AZStd::vector guids; + AZStd::vector> instances; AZStd::vector> assets; for (size_t i = 0; i < assetIdCount; ++i) @@ -199,6 +269,7 @@ namespace UnitTest Uuid guid = Uuid::CreateRandom(); guids.emplace_back(guid); + instances.emplace_back(nullptr); // Pre-create asset so we don't attempt to load it from the catalog. assets.emplace_back(assetManager.CreateAsset(guid, AZ::Data::AssetLoadBehavior::Default)); @@ -206,6 +277,7 @@ namespace UnitTest AZStd::vector threads; AZStd::mutex mutex; + AZStd::mutex referenceTableMutex; AZStd::atomic threadCount((int)threadCountMax); AZStd::condition_variable cv; AZStd::atomic_bool keepDispatching(true); @@ -225,11 +297,15 @@ namespace UnitTest for (size_t i = 0; i < threadCountMax; ++i) { threads.emplace_back( - [&instanceManager, &threadCount, &cv, &guids, &assets, &durationSeconds]() + [&instanceManager, &threadCount, &cv, &guids, &instances, &assets, &durationSeconds, &testCase, &referenceTableMutex]() { AZ::Debug::Timer timer; timer.Stamp(); + bool deferRemoval = testCase == ParallelInstanceTestCases::CreateAndDeferRemoval || + testCase == ParallelInstanceTestCases::CreateDeferRemovalAndOrphan + ? true : false; + while (timer.GetDeltaTimeInSeconds() < durationSeconds) { const size_t index = rand() % guids.size(); @@ -237,11 +313,36 @@ namespace UnitTest const InstanceId instanceId{ uuid }; const AssetId assetId{ uuid }; - Instance instance = - instanceManager.FindOrCreate(instanceId, Asset(assetId, azrtti_typeid())); - EXPECT_NE(instance, nullptr); - EXPECT_EQ(instance->GetId(), instanceId); - EXPECT_EQ(instance->m_asset, assets[index]); + ParralleInstanceCurrentAction currentAction = ParallelInstanceGetCurrentAction(testCase); + + if (currentAction == ParralleInstanceCurrentAction::Orphan) + { + // Orphan the instance, but don't decrease its refcount + instanceManager.TEMPOrphan(instanceId); + } + else if (currentAction == ParralleInstanceCurrentAction::DeferredRemoval) + { + // Drop the refcount to zero so the instance will be released + referenceTableMutex.lock(); + instances[index] = nullptr; + referenceTableMutex.unlock(); + } + else + { + // Otherwise, add a new instance + Instance instance = instanceManager.FindOrCreate(instanceId, assets[index]); + EXPECT_NE(instance, nullptr); + EXPECT_EQ(instance->GetId(), instanceId); + EXPECT_EQ(instance->m_asset, assets[index]); + + if (deferRemoval) + { + // Keep a reference to the instance alive so it can be removed later + referenceTableMutex.lock(); + instances[index] = instance; + referenceTableMutex.unlock(); + } + } } threadCount--; @@ -254,10 +355,12 @@ namespace UnitTest // Used to detect a deadlock. If we wait for more than 10 seconds, it's likely a deadlock has occurred while (threadCount > 0 && !timedOut) { + size_t durationSecondsRoundedUp = static_cast(std::ceil(durationSeconds)); + AZStd::unique_lock lock(mutex); timedOut = (AZStd::cv_status::timeout == - cv.wait_until(lock, AZStd::chrono::system_clock::now() + AZStd::chrono::seconds(durationSeconds * 2))); + cv.wait_until(lock, AZStd::chrono::system_clock::now() + AZStd::chrono::seconds(durationSecondsRoundedUp * 2))); } EXPECT_TRUE(threadCount == 0) << "One or more threads appear to be deadlocked at " << timer.GetDeltaTimeInSeconds() << " seconds"; @@ -273,11 +376,11 @@ namespace UnitTest printf("Took %f seconds\n", timer.GetDeltaTimeInSeconds()); } - TEST_F(InstanceDatabaseTest, ParallelInstanceCreate) + void ParallelCreateTest(ParallelInstanceTestCases testCase) { // This is the original test scenario from when InstanceDatabase was first implemented // threads, AssetIds, seconds - ParallelInstanceCreateHelper(8, 100, 5); + ParallelInstanceCreateHelper(8, 100, 5, testCase); // This value is checked in as 1 so this test doesn't take too much time, but can be increased locally to soak the test. const size_t attempts = 1; @@ -289,11 +392,11 @@ namespace UnitTest // The idea behind this series of tests is that there are two threads sharing one Instance, and both threads try to // create or release that instance at the same time. // At the time, this set of scenarios has something like a 10% failure rate. - const size_t duration = 2; + const float duration = 2.0f; // threads, AssetIds, seconds - ParallelInstanceCreateHelper(2, 1, duration); - ParallelInstanceCreateHelper(4, 1, duration); - ParallelInstanceCreateHelper(8, 1, duration); + ParallelInstanceCreateHelper(2, 1, duration, testCase); + ParallelInstanceCreateHelper(4, 1, duration, testCase); + ParallelInstanceCreateHelper(8, 1, duration, testCase); } for (size_t i = 0; i < attempts; ++i) @@ -301,19 +404,39 @@ namespace UnitTest printf("Attempt %zu of %zu... \n", i, attempts); // Here we try a bunch of different threadCount:assetCount ratios to be thorough - const size_t duration = 2; + const float duration = 2.0f; // threads, AssetIds, seconds - ParallelInstanceCreateHelper(2, 1, duration); - ParallelInstanceCreateHelper(4, 1, duration); - ParallelInstanceCreateHelper(4, 2, duration); - ParallelInstanceCreateHelper(4, 4, duration); - ParallelInstanceCreateHelper(8, 1, duration); - ParallelInstanceCreateHelper(8, 2, duration); - ParallelInstanceCreateHelper(8, 3, duration); - ParallelInstanceCreateHelper(8, 4, duration); + ParallelInstanceCreateHelper(2, 1, duration, testCase); + ParallelInstanceCreateHelper(4, 1, duration, testCase); + ParallelInstanceCreateHelper(4, 2, duration, testCase); + ParallelInstanceCreateHelper(4, 4, duration, testCase); + ParallelInstanceCreateHelper(8, 1, duration, testCase); + ParallelInstanceCreateHelper(8, 2, duration, testCase); + ParallelInstanceCreateHelper(8, 3, duration, testCase); + ParallelInstanceCreateHelper(8, 4, duration, testCase); } } + TEST_F(InstanceDatabaseTest, ParallelInstanceCreate) + { + ParallelCreateTest(ParallelInstanceTestCases::Create); + } + + TEST_F(InstanceDatabaseTest, ParallelInstanceCreateAndDeferRemoval) + { + ParallelCreateTest(ParallelInstanceTestCases::CreateAndDeferRemoval); + } + + TEST_F(InstanceDatabaseTest, ParallelInstanceCreateAndOrphan) + { + ParallelCreateTest(ParallelInstanceTestCases::CreateAndOrphan); + } + + TEST_F(InstanceDatabaseTest, ParallelInstanceCreateDeferRemovalAndOrphan) + { + ParallelCreateTest(ParallelInstanceTestCases::CreateDeferRemovalAndOrphan); + } + TEST_F(InstanceDatabaseTest, InstanceCreateNoDatabase) { bool m_deleted = false; diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp index 98182a9568..8eb620f69e 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp @@ -340,6 +340,14 @@ namespace AZ // (Load jobs will attempt to reuse blocked threads before spinning off new job threads) ProcessLoadJob(); } + + // Pump the AssetBus function queue once more after the load has completed in case additional + // functions have been queued between the last call to DispatchEvents and the completion + // of the current load job + if (m_shouldDispatchEvents) + { + AssetManager::Instance().DispatchEvents(); + } } void Finish() diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManagerComponent.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetManagerComponent.cpp index 93315c9c81..b40c36bc6b 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetManagerComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetManagerComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetSerializer.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetSerializer.cpp index ee594c585b..b5cc6c5df4 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetSerializer.cpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace AZ { diff --git a/Code/Framework/AzCore/AzCore/Component/Component.h b/Code/Framework/AzCore/AzCore/Component/Component.h index 3cbb9b5a86..677517d896 100644 --- a/Code/Framework/AzCore/AzCore/Component/Component.h +++ b/Code/Framework/AzCore/AzCore/Component/Component.h @@ -22,6 +22,7 @@ #include #include // Used as the allocator for most components. #include +#include namespace AZ { diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index a13f11c007..168807cd97 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -74,8 +74,6 @@ #include #include -AZ_CVAR(float, g_simulation_tick_rate, 0, nullptr, AZ::ConsoleFunctorFlags::Null, "The rate at which the game simulation tick loop runs, or 0 for as fast as possible"); - static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments) { if (arguments.empty()) @@ -1396,23 +1394,6 @@ namespace AZ AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick"); EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now)); } - - // If tick rate limiting is on, ensure (1 / g_simulation_tick_rate) ms has elapsed since the last frame, - // sleeping if there's still time remaining. - if (g_simulation_tick_rate > 0.f) - { - now = AZStd::chrono::system_clock::now(); - - // Work in microsecond durations here as that's the native measurement time for time_point - constexpr float microsecondsPerSecond = 1000.f * 1000.f; - const AZStd::chrono::microseconds timeBudgetPerTick(static_cast(microsecondsPerSecond / g_simulation_tick_rate)); - AZStd::chrono::microseconds timeUntilNextTick = m_currentTime + timeBudgetPerTick - now; - - if (timeUntilNextTick.count() > 0) - { - AZStd::this_thread::sleep_for(timeUntilNextTick); - } - } } } diff --git a/Code/Framework/AzCore/AzCore/Component/Entity.cpp b/Code/Framework/AzCore/AzCore/Component/Entity.cpp index 2e92873238..0fe201d448 100644 --- a/Code/Framework/AzCore/AzCore/Component/Entity.cpp +++ b/Code/Framework/AzCore/AzCore/Component/Entity.cpp @@ -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); diff --git a/Code/Framework/AzCore/AzCore/Component/Entity.h b/Code/Framework/AzCore/AzCore/Component/Entity.h index 356533f268..c2bde375bb 100644 --- a/Code/Framework/AzCore/AzCore/Component/Entity.h +++ b/Code/Framework/AzCore/AzCore/Component/Entity.h @@ -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; diff --git a/Code/Framework/AzCore/AzCore/Component/EntityUtils.h b/Code/Framework/AzCore/AzCore/Component/EntityUtils.h index ce258bc637..5c02a3fe74 100644 --- a/Code/Framework/AzCore/AzCore/Component/EntityUtils.h +++ b/Code/Framework/AzCore/AzCore/Component/EntityUtils.h @@ -5,8 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZCORE_ENTITY_UTILS_H -#define AZCORE_ENTITY_UTILS_H +#pragma once #include #include @@ -217,6 +216,3 @@ namespace AZ } // namespace EntityUtils } // namespace AZ - -#endif // AZCORE_ENTITY_UTILS_H -#pragma once diff --git a/Code/Framework/AzCore/AzCore/Component/TickBus.h b/Code/Framework/AzCore/AzCore/Component/TickBus.h index 966a3c303e..e65efb93f2 100644 --- a/Code/Framework/AzCore/AzCore/Component/TickBus.h +++ b/Code/Framework/AzCore/AzCore/Component/TickBus.h @@ -46,8 +46,6 @@ namespace AZ TICK_PRE_RENDER = 750, ///< Suggested tick handler position to update render-related data. - TICK_RENDER = 800, ///< Suggested tick handler position for rendering. - TICK_DEFAULT = 1000, ///< Default tick handler position when the handler is constructed. TICK_UI = 2000, ///< Suggested tick handler position for UI components. diff --git a/Code/Framework/AzCore/AzCore/Debug/AssetTracking.h b/Code/Framework/AzCore/AzCore/Debug/AssetTracking.h index 5c3c835271..615634c05a 100644 --- a/Code/Framework/AzCore/AzCore/Debug/AssetTracking.h +++ b/Code/Framework/AzCore/AzCore/Debug/AssetTracking.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include diff --git a/Code/Framework/AzCore/AzCore/EBus/EBus.h b/Code/Framework/AzCore/AzCore/EBus/EBus.h index 58754ff9b8..ff8966e8e0 100644 --- a/Code/Framework/AzCore/AzCore/EBus/EBus.h +++ b/Code/Framework/AzCore/AzCore/EBus/EBus.h @@ -19,14 +19,11 @@ #pragma once #include +#include #include #include - // Included for backwards compatibility purposes -#include -#include #include -// End backwards compat #include #include @@ -90,14 +87,14 @@ namespace AZ * For available settings, see AZ::EBusHandlerPolicy. * By default, an EBus supports any number of handlers. */ - static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Multiple; + static constexpr EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Multiple; /** * Defines how many addresses exist on the EBus. * For available settings, see AZ::EBusAddressPolicy. * By default, an EBus uses a single address. */ - static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::Single; + static constexpr EBusAddressPolicy AddressPolicy = EBusAddressPolicy::Single; /** * The type of ID that is used to address the EBus. @@ -152,14 +149,14 @@ namespace AZ * `::ExecuteQueuedEvents()`. * By default, the event queue is disabled. */ - static const bool EnableEventQueue = false; + static constexpr bool EnableEventQueue = false; /** * Specifies whether the bus should accept queued messages by default or not. * If set to false, Bus::AllowFunctionQueuing(true) must be called before events are accepted. * Used only when #EnableEventQueue is true. */ - static const bool EventQueueingActiveByDefault = true; + static constexpr bool EventQueueingActiveByDefault = true; /** * Specifies whether the EBus supports queueing functions which take reference @@ -168,7 +165,7 @@ namespace AZ * You should only use this if you know that the data being passed as arguments will * outlive the dispatch of the queued event. */ - static const bool EnableQueuedReferences = false; + static constexpr bool EnableQueuedReferences = false; /** * Locking primitive that is used when adding and removing @@ -197,7 +194,7 @@ namespace AZ * to do. * By default, the standard policy is used, which locks around all dispatches */ - static const bool LocklessDispatch = false; + static constexpr bool LocklessDispatch = false; /** * Specifies where EBus data is stored. diff --git a/Code/Framework/AzCore/AzCore/EBus/IEventScheduler.h b/Code/Framework/AzCore/AzCore/EBus/IEventScheduler.h index 5c1bbf6dab..021e8edfab 100644 --- a/Code/Framework/AzCore/AzCore/EBus/IEventScheduler.h +++ b/Code/Framework/AzCore/AzCore/EBus/IEventScheduler.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace AZ { diff --git a/Code/Framework/AzCore/AzCore/EBus/Policies.h b/Code/Framework/AzCore/AzCore/EBus/Policies.h index db11043ef8..86cbe5d02f 100644 --- a/Code/Framework/AzCore/AzCore/EBus/Policies.h +++ b/Code/Framework/AzCore/AzCore/EBus/Policies.h @@ -18,9 +18,8 @@ #include #include #include +#include -#include -#include namespace AZ { @@ -251,29 +250,21 @@ namespace AZ void Execute() { AZ_Warning("System", m_isActive, "You are calling execute queued functions on a bus which has not activated its function queuing! Call YourBus::AllowFunctionQueuing(true)!"); - while (true) + + MessageQueueType localMessages; + + // Swap the current list of queue functions with a local instance + { + AZStd::scoped_lock lock(m_messagesMutex); + AZStd::swap(localMessages, m_messages); + } + + // Execute the queue functions safely now that are owned by the function + while (!localMessages.empty()) { - BusMessageCall invoke; - - ////////////////////////////////////////////////////////////////////////// - // Pop element from the queue. - { - AZStd::lock_guard lock(m_messagesMutex); - size_t numMessages = m_messages.size(); - if (numMessages == 0) - { - break; - } - AZStd::swap(invoke, m_messages.front()); - m_messages.pop(); - if (numMessages == 1) - { - m_messages = {}; - } - } - ////////////////////////////////////////////////////////////////////////// - - invoke(); + const BusMessageCall& localMessage = localMessages.front(); + localMessage(); + localMessages.pop(); } } diff --git a/Code/Framework/AzCore/AzCore/IO/FileReader.cpp b/Code/Framework/AzCore/AzCore/IO/FileReader.cpp new file mode 100644 index 0000000000..94118cdfe4 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/IO/FileReader.cpp @@ -0,0 +1,200 @@ +/* + * 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 +#include +#include + +namespace AZ::IO +{ + FileReader::FileReader() = default; + + FileReader::FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath) + { + Open(fileIoBase, filePath); + } + + FileReader::~FileReader() + { + Close(); + } + + FileReader::FileReader(FileReader&& other) + { + AZStd::swap(m_file, other.m_file); + AZStd::swap(m_fileIoBase, other.m_fileIoBase); + } + + FileReader& FileReader::operator=(FileReader&& other) + { + // Close the current file and take over other file + Close(); + m_file = AZStd::move(other.m_file); + m_fileIoBase = AZStd::move(other.m_fileIoBase); + other.m_file = AZStd::monostate{}; + other.m_fileIoBase = {}; + + return *this; + } + + bool FileReader::Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath) + { + // Close file if the FileReader has an instance open + Close(); + + if (fileIoBase != nullptr) + { + AZ::IO::HandleType fileHandle; + if (fileIoBase->Open(filePath, IO::OpenMode::ModeRead, fileHandle)) + { + m_file = fileHandle; + m_fileIoBase = fileIoBase; + return true; + } + } + else + { + AZ::IO::SystemFile file; + if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY)) + { + m_file = AZStd::move(file); + return true; + } + } + + return false; + } + + bool FileReader::IsOpen() const + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + return *fileHandle != AZ::IO::InvalidHandle; + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->IsOpen(); + } + + return false; + } + + void FileReader::Close() + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + if (AZ::IO::FileIOBase* fileIo = m_fileIoBase; fileIo != nullptr) + { + fileIo->Close(*fileHandle); + } + } + + m_file = AZStd::monostate{}; + m_fileIoBase = {}; + } + + auto FileReader::Length() const -> SizeType + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + if (SizeType fileSize{}; m_fileIoBase->Size(*fileHandle, fileSize)) + { + return fileSize; + } + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->Length(); + } + + return 0; + } + + auto FileReader::Read(SizeType byteSize, void* buffer) -> SizeType + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + if (SizeType bytesRead{}; m_fileIoBase->Read(*fileHandle, buffer, byteSize, false, &bytesRead)) + { + return bytesRead; + } + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->Read(byteSize, buffer); + } + + return 0; + } + + auto FileReader::Tell() const -> SizeType + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + if (SizeType fileOffset{}; m_fileIoBase->Tell(*fileHandle, fileOffset)) + { + return fileOffset; + } + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->Tell(); + } + + return 0; + } + + bool FileReader::Seek(AZ::s64 offset, SeekType type) + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + return m_fileIoBase->Seek(*fileHandle, offset, type); + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + systemFile->Seek(offset, static_cast(type)); + return true; + } + + return false; + } + + bool FileReader::Eof() const + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + return m_fileIoBase->Eof(*fileHandle); + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->Eof(); + } + + return false; + } + + bool FileReader::GetFilePath(AZ::IO::FixedMaxPath& filePath) const + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + AZ::IO::FixedMaxPathString& pathStringRef = filePath.Native(); + if (m_fileIoBase->GetFilename(*fileHandle, pathStringRef.data(), pathStringRef.capacity())) + { + pathStringRef.resize_no_construct(AZStd::char_traits::length(pathStringRef.data())); + return true; + } + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + filePath = systemFile->Name(); + return true; + } + + return false; + } +} diff --git a/Code/Framework/AzCore/AzCore/IO/FileReader.h b/Code/Framework/AzCore/AzCore/IO/FileReader.h new file mode 100644 index 0000000000..4fdb18b2b2 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/IO/FileReader.h @@ -0,0 +1,92 @@ +/* + * 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 +#include +#include + +namespace AZ::IO +{ + class FileIOBase; + enum class SeekType : AZ::u32; + + //! Structure which encapsulates delegates File Read operations + //! to either the FileIOBase or SystemFile classes based if a FileIOBase* instance has been supplied + //! to the FileSystemReader class + //! the SettingsRegistry option to use FileIO + class FileReader + { + using HandleType = AZ::u32; + using FileHandleType = AZStd::variant; + public: + using SizeType = AZ::u64; + + //! Creates FileReader instance in the default state with no file opend + FileReader(); + ~FileReader(); + + //! Creates a new FileReader instance and attempts to open the file at the supplied path + //! Uses the FileIOBase instance if supplied + //! @param fileIOBase pointer to fileIOBase instance + //! @param null-terminated filePath to open + FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath); + + //! Takes ownership of the supplied FileReader handle + FileReader(FileReader&& other); + + //! Moves ownership of FileReader handle to this instance + FileReader& operator=(FileReader&& other); + + //! Opens a File using the FileIOBase instance if non-nullptr + //! Otherwise fall back to use SystemFile + //! @param fileIOBase pointer to fileIOBase instance + //! @param null-terminated filePath to open + //! @return true if the File is opened successfully + bool Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath); + + //! Returns true if a file is currently open + //! @return true if the file is open + bool IsOpen() const; + + //! Closes the File + void Close(); + + //! Retrieve the length of the OpenFile + SizeType Length() const; + + //! Attempts to read up to byte size bytes into the supplied buffer + //! @param byteSize - Maximum number of bytes to read + //! @param buffer - Buffer to read bytes into + //! @returns the number of bytes read if the file is open, otherwise 0 + SizeType Read(SizeType byteSize, void* buffer); + + //! Returns the current file offset + //! @returns file offset if the file is open, otherwise 0 + SizeType Tell() const; + + //! Seeks within the open file to the offset supplied + //! @param offset File offset to seek to + //! @param type parameter to indicate the reference point to start the seek from + //! @returns true if the file is open and the seek succeeded + bool Seek(AZ::s64 offset, SeekType type); + + //! Returns true if the file is open and in the EOF state + bool Eof() const; + + //! Store the file path of the open file into the output file path parameter + //! The filePath reference is left unmodified, if the path was not stored + //! @return true if the filePath was stored + bool GetFilePath(AZ::IO::FixedMaxPath& filePath) const; + + private: + + FileHandleType m_file; + AZ::IO::FileIOBase* m_fileIoBase{}; + }; +} diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp index 5bff79b422..651abb89fe 100644 --- a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp +++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp @@ -160,12 +160,12 @@ void SystemFile::Seek(SeekSizeType offset, SeekMode mode) Platform::Seek(m_handle, this, offset, mode); } -SystemFile::SizeType SystemFile::Tell() +SystemFile::SizeType SystemFile::Tell() const { return Platform::Tell(m_handle, this); } -bool SystemFile::Eof() +bool SystemFile::Eof() const { return Platform::Eof(m_handle, this); } diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.h b/Code/Framework/AzCore/AzCore/IO/SystemFile.h index 8a5b2b2521..551ce89ce7 100644 --- a/Code/Framework/AzCore/AzCore/IO/SystemFile.h +++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.h @@ -72,9 +72,9 @@ namespace AZ /// Seek in current file. void Seek(SeekSizeType offset, SeekMode mode); /// Get the cursor position in the current file. - SizeType Tell(); + SizeType Tell() const; /// Is the cursor at the end of the file? - bool Eof(); + bool Eof() const; /// Get the time the file was last modified. AZ::u64 ModificationTime(); /// Read data from a file synchronous. Return number of bytes actually read in the buffer. diff --git a/Code/Framework/AzCore/AzCore/Name/NameSerializer.cpp b/Code/Framework/AzCore/AzCore/Name/NameSerializer.cpp index bf3cad7178..581e11ff04 100644 --- a/Code/Framework/AzCore/AzCore/Name/NameSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Name/NameSerializer.cpp @@ -7,6 +7,7 @@ */ #include +#include namespace AZ { diff --git a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.h b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.h index 0a1af21213..7f48f301aa 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.h +++ b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptProperty.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptProperty.cpp index dbccda4de2..578bbcf271 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptProperty.cpp +++ b/Code/Framework/AzCore/AzCore/Script/ScriptProperty.cpp @@ -5,11 +5,13 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ +#include #include #include #include #include #include +#include namespace AZ { diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptProperty.h b/Code/Framework/AzCore/AzCore/Script/ScriptProperty.h index f38931127d..6b7275dfeb 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptProperty.h +++ b/Code/Framework/AzCore/AzCore/Script/ScriptProperty.h @@ -5,8 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZCORE_SCRIPT_SCRIPTPROPERTY_H -#define AZCORE_SCRIPT_SCRIPTPROPERTY_H +#pragma once #include #include @@ -490,5 +489,4 @@ namespace AZ }; } -#endif diff --git a/Code/Framework/AzCore/AzCore/Serialization/DataPatch.cpp b/Code/Framework/AzCore/AzCore/Serialization/DataPatch.cpp index 01a98667fa..6f1635148c 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/DataPatch.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/DataPatch.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp index 324df7c141..a741294544 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp @@ -9,6 +9,7 @@ #include "AzCore/RTTI/TypeInfo.h" #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp index f06fb36be8..8b6c1d154c 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp @@ -6,7 +6,9 @@ * */ +#include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp index 7978b8104b..196ce28216 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Serialization/ObjectStream.cpp b/Code/Framework/AzCore/AzCore/Serialization/ObjectStream.cpp index 6251a2e6de..746c80f3ea 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/ObjectStream.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/ObjectStream.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Serialization/ObjectStream.h b/Code/Framework/AzCore/AzCore/Serialization/ObjectStream.h index 5d920045f8..9f9b3fc9f0 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/ObjectStream.h +++ b/Code/Framework/AzCore/AzCore/Serialization/ObjectStream.h @@ -37,7 +37,8 @@ namespace AZ class GenericStream; } - namespace ObjectStreamInternal { + namespace ObjectStreamInternal + { class ObjectStreamImpl; } diff --git a/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.cpp b/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.cpp index b74039c876..81546f4f28 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.cpp @@ -7,6 +7,8 @@ */ #include + +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h b/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h index 3d7f3c00f8..a37780029e 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h @@ -5,8 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZCORE_SERIALIZE_CONTEXT_H -#define AZCORE_SERIALIZE_CONTEXT_H +#pragma once #include @@ -43,6 +42,12 @@ namespace AZ { + namespace Data + { + template + class Asset; + } + class EditContext; class ObjectStream; @@ -2562,11 +2567,13 @@ namespace AZ #include #include -/// include asset generics -#include +// Forward declare asset serialization helper specialization +namespace AZ +{ + template + struct SerializeGenericTypeInfo< Data::Asset >; +} /// include implementation of SerializeContext::EnumBuilder #include -#endif // AZCORE_SERIALIZE_CONTEXT_H -#pragma once diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp index 7ef1fa661d..92f546815e 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1116,118 +1117,6 @@ namespace AZ } } - //! Structure which encapsulates Commands to either the FileIOBase or SystemFile classes based on - //! the SettingsRegistry option to use FileIO - struct SettingsRegistryFileReader - { - using FileHandleType = AZStd::variant; - - SettingsRegistryFileReader() = default; - SettingsRegistryFileReader(bool useFileIo, const char* filePath) - { - Open(useFileIo, filePath); - } - - ~SettingsRegistryFileReader() - { - if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) - { - if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr) - { - fileIo->Close(*fileHandle); - } - } - } - - bool Open(bool useFileIo, const char* filePath) - { - Close(); - if (AZ::IO::FileIOBase* fileIo = useFileIo ? AZ::IO::FileIOBase::GetInstance() : nullptr; fileIo != nullptr) - { - AZ::IO::HandleType fileHandle; - if (fileIo->Open(filePath, IO::OpenMode::ModeRead, fileHandle)) - { - m_file = fileHandle; - return true; - } - } - else - { - AZ::IO::SystemFile file; - if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY)) - { - m_file = AZStd::move(file); - return true; - } - } - - return false; - } - - bool IsOpen() const - { - if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) - { - return *fileHandle != AZ::IO::InvalidHandle; - } - else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) - { - return systemFile->IsOpen(); - } - - return false; - } - - void Close() - { - if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) - { - if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr) - { - fileIo->Close(*fileHandle); - } - } - - m_file = AZStd::monostate{}; - } - - u64 Length() const - { - if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) - { - if (u64 fileSize{}; AZ::IO::FileIOBase::GetInstance()->Size(*fileHandle, fileSize)) - { - return fileSize; - } - } - else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) - { - return systemFile->Length(); - } - - return 0; - } - - AZ::IO::SizeType Read(AZ::IO::SizeType byteSize, void* buffer) - { - if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) - { - if (AZ::u64 bytesRead{}; AZ::IO::FileIOBase::GetInstance()->Read(*fileHandle, buffer, byteSize, false, &bytesRead)) - { - return bytesRead; - } - } - else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) - { - return systemFile->Read(byteSize, buffer); - } - - return 0; - } - - FileHandleType m_file; - }; - bool SettingsRegistryImpl::MergeSettingsFileInternal(const char* path, Format format, AZStd::string_view rootKey, AZStd::vector& scratchBuffer) { @@ -1236,7 +1125,7 @@ namespace AZ Pointer pointer(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/-"); - SettingsRegistryFileReader fileReader(m_useFileIo, path); + FileReader fileReader(m_useFileIo ? AZ::IO::FileIOBase::GetInstance(): nullptr, path); if (!fileReader.IsOpen()) { AZ_Error("Settings Registry", false, R"(Unable to open registry file "%s".)", path); diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 5290c9b02a..da7110e36e 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -6,6 +6,8 @@ * */ +#include +#include #include #include #include @@ -388,8 +390,36 @@ namespace AZ::SettingsRegistryMergeUtils const ConfigParserSettings& configParserSettings) { auto configPath = FindEngineRoot(registry) / filePath; - IO::SystemFile configFile; - if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY)) + IO::FileReader configFile; + bool configFileOpened{}; + switch (configParserSettings.m_fileReaderClass) + { + case ConfigParserSettings::FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile: + { + auto fileIo = AZ::IO::FileIOBase::GetInstance(); + configFileOpened = configFile.Open(fileIo, configPath.c_str()); + break; + } + case ConfigParserSettings::FileReaderClass::UseSystemFileOnly: + { + configFileOpened = configFile.Open(nullptr, configPath.c_str()); + break; + } + case ConfigParserSettings::FileReaderClass::UseFileIOOnly: + { + auto fileIo = AZ::IO::FileIOBase::GetInstance(); + if (fileIo == nullptr) + { + return false; + } + configFileOpened = configFile.Open(fileIo, configPath.c_str()); + break; + } + default: + AZ_Error("SettingsRegistryMergeUtils", false, "An Invalid FileReaderClass enum value has been supplied"); + return false; + } + if (!configFileOpened) { AZ_Warning("SettingsRegistryMergeUtils", false, R"(Unable to open file "%s")", configPath.c_str()); return false; @@ -480,7 +510,7 @@ namespace AZ::SettingsRegistryMergeUtils AZ_Error("SettingsRegistryMergeUtils", false, R"(The config file "%s" contains a line which is longer than the max line length of %zu.)" "\n" R"(Parsing will halt. The line content so far is:)" "\n" - R"("%.*s")" "\n", configFile.Name(), configBuffer.max_size(), + R"("%.*s")" "\n", configPath.c_str(), configBuffer.max_size(), aznumeric_cast(configBuffer.size()), configBuffer.data()); configFileParsed = false; break; diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h index 02346c2ba1..daa64c0343 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h @@ -155,6 +155,15 @@ namespace AZ::SettingsRegistryMergeUtils //! structure which is forwarded to the SettingsRegistryInterface MergeCommandLineArgument function //! The structure contains a functor which returns true if a character is a valid delimiter SettingsRegistryInterface::CommandLineArgumentSettings m_commandLineSettings; + + //! enumeration to indicate if AZ::IO::FileIOBase should be used to open the config file over AZ::IO::SystemFile + enum class FileReaderClass + { + UseFileIOIfAvailableFallbackToSystemFile, + UseSystemFileOnly, + UseFileIOOnly + }; + FileReaderClass m_fileReaderClass = FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile; }; //! Loads basic configuration files which have structures similar to Windows INI files //! It is inspired by the Python configparser module: https://docs.python.org/3.10/library/configparser.html diff --git a/Code/Framework/AzCore/AzCore/Slice/SliceComponent.cpp b/Code/Framework/AzCore/AzCore/Slice/SliceComponent.cpp index 82a9175cf9..0b410369f0 100644 --- a/Code/Framework/AzCore/AzCore/Slice/SliceComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Slice/SliceComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Statistics/StatisticalProfiler.h b/Code/Framework/AzCore/AzCore/Statistics/StatisticalProfiler.h index 2d8823c6e8..681635cd1c 100644 --- a/Code/Framework/AzCore/AzCore/Statistics/StatisticalProfiler.h +++ b/Code/Framework/AzCore/AzCore/Statistics/StatisticalProfiler.h @@ -10,6 +10,7 @@ #include //Just to get AZ::NullMutex #include #include +#include #include namespace AZ @@ -243,7 +244,7 @@ namespace AZ //! This one is needed because running statistics are collected many times across //! several frames. This value is used to calculate a per frame sample for @m_totalTimePerFrameStat, - //! by subtracting @m_prevAccumulatedSums from the accumulated sum in @m_statisticsManager. + //! by subtracting @m_prevAccumulatedSums from the accumulated sum in @m_statisticsManager. double m_prevAccumulatedSums; }; diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 6c498c3335..aa07959997 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -166,6 +166,8 @@ set(FILES IO/FileIO.cpp IO/FileIO.h IO/FileIOEventBus.h + IO/FileReader.cpp + IO/FileReader.h IO/IOUtils.h IO/IOUtils.cpp IO/IStreamer.h diff --git a/Code/Framework/AzCore/Tests/AZStd/VariantSerialization.cpp b/Code/Framework/AzCore/Tests/AZStd/VariantSerialization.cpp index a87aa70d8c..a4f2d1f1e4 100644 --- a/Code/Framework/AzCore/Tests/AZStd/VariantSerialization.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/VariantSerialization.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp b/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp index 8ce3747620..3e6376323c 100644 --- a/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp +++ b/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp @@ -6,6 +6,7 @@ * */ #include +#include #include #include #include @@ -365,6 +366,42 @@ namespace UnitTest }; + static constexpr AZStd::chrono::seconds MaxDispatchTimeoutSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds * 12; + + template + bool DispatchEventsUntilCondition(AZ::Data::AssetManager& assetManager, Pred&& conditionPredicate, + AZStd::chrono::seconds logIntervalSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds, + AZStd::chrono::seconds maxTimeoutSeconds = MaxDispatchTimeoutSeconds) + { + // If the Max Timeout is hit the test will be marked as a failure + + AZStd::chrono::time_point dispatchEventTimeStart = AZStd::chrono::system_clock::now(); + AZStd::chrono::seconds dispatchEventNextLogTime = logIntervalSeconds; + + while (!conditionPredicate()) + { + AZStd::chrono::time_point currentTime = AZStd::chrono::system_clock::now(); + if (AZStd::chrono::seconds elapsedTime{ currentTime - dispatchEventTimeStart }; + elapsedTime >= dispatchEventNextLogTime) + { + const testing::TestInfo* test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + AZ_Printf("AssetManagerLoadingTest", "The DispatchEventsUntiTimeout function has been waiting for %llu seconds" + " in test %s.%s", elapsedTime.count(), test_info->test_case_name(), test_info->name()); + // Update the next log time to be the next multiple of DefaultTimeout Seconds + // after current elapsed time + dispatchEventNextLogTime = elapsedTime + logIntervalSeconds - ((elapsedTime + logIntervalSeconds) % logIntervalSeconds); + if (elapsedTime >= maxTimeoutSeconds) + { + return false; + } + } + assetManager.DispatchEvents(); + AZStd::this_thread::yield(); + } + + return true; + } + #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_ASSET_MANAGER_FLOOD_TEST TEST_F(AssetJobsFloodTest, DISABLED_FloodTest) #else @@ -1357,42 +1394,74 @@ namespace UnitTest m_assetHandlerAndCatalog->m_numCreations = 0; m_assetHandlerAndCatalog->m_numDestructions = 0; { + ContainerReadyListener containerLoadingCompleteListener(NoLoadAssetId); OnAssetReadyListener readyListener(NoLoadAssetId, azrtti_typeid()); - OnAssetReadyListener depenencyListener(MyAsset2Id, azrtti_typeid()); + OnAssetReadyListener dependencyListener(MyAsset2Id, azrtti_typeid()); + + SCOPED_TRACE("LoadDependencies_BehaviorObeyed"); + + auto AssetOnlyReady = [&readyListener]() -> bool + { + return readyListener.m_ready; + }; + auto AssetAndDependencyReady = [&readyListener, &dependencyListener]() -> bool + { + return readyListener.m_ready && dependencyListener.m_ready; + }; + auto AssetContainerReady = [&containerLoadingCompleteListener]() -> bool + { + return containerLoadingCompleteListener.m_ready; + }; auto noLoadRef = m_testAssetManager->GetAsset(NoLoadAssetId, azrtti_typeid(), AZ::Data::AssetLoadBehavior::Default); - auto maxTimeout = AZStd::chrono::system_clock::now() + DefaultTimeoutSeconds; + // Dispatch AssetBus events until the NoLoadAssetId has signaled an OnAssetReady + // event or the timeout has been reached + EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetOnlyReady)) + << "The DispatchEventsUntiTimeout function has not completed in " + << MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n"; + + // Dispatch AssetBus events until the asset container used to load + // NoLoadAssetId has signaled an OnAssetContainerReady event + // or the timeout has been reached + // Wait until the current asset container has finished loading the NoLoadAssetId + // before trigger another load + // If the wait does not occur here, most likely what would occur is + // the AssetManager::m_ownedAssetContainers object is still loading the NoLoadAssetId + // using the default AssetLoadParameters + // If a call to GetAsset occurs at this point while the Asset is still loading + // it will ignore the new loadParams below and instead just re-use the existing + // AssetContainerReader instance, resulting in the dependent MyAsset2Id not + // being loaded + // The function that can return an existing AssetContainer instance is the + // AssetManager::GetAssetContainer. Since it can be in the middle of a load, + // updating the AssetLoadParams would have an effect on the current in progress + // load + EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetContainerReady)) + << "The DispatchEventsUntiTimeout function has not completed in " + << MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n"; + + // Reset the ContainerLoadingComplete ready status back to 0 + containerLoadingCompleteListener.m_ready = 0; - while (!readyListener.m_ready) - { - m_testAssetManager->DispatchEvents(); - if (AZStd::chrono::system_clock::now() > maxTimeout) - { - break; - } - AZStd::this_thread::yield(); - } - EXPECT_EQ(readyListener.m_ready, 1); - EXPECT_EQ(depenencyListener.m_ready, 0); - AZ::Data::AssetLoadParameters loadParams(nullptr, AZ::Data::AssetDependencyLoadRules::LoadAll); loadParams.m_reloadMissingDependencies = true; auto loadDependencyRef = m_testAssetManager->GetAsset(NoLoadAssetId, azrtti_typeid(), AZ::Data::AssetLoadBehavior::Default, loadParams); - while (!depenencyListener.m_ready || !readyListener.m_ready) - { - m_testAssetManager->DispatchEvents(); - if (AZStd::chrono::system_clock::now() > maxTimeout) - { - break; - } - AZStd::this_thread::yield(); - } + // Dispatch AssetBus events until the NoLoadAssetId and the MyAsset2Id has signaled + // an OnAssetReady event or the timeout has been reached + EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetAndDependencyReady)) + << "The DispatchEventsUntiTimeout function has not completed in " + << MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n"; + EXPECT_EQ(readyListener.m_ready, 1); - EXPECT_EQ(depenencyListener.m_ready, 1); + EXPECT_EQ(dependencyListener.m_ready, 1); + + EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetContainerReady)) + << "The DispatchEventsUntiTimeout function has not completed in " + << MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n"; } CheckFinishedCreationsAndDestructions(); diff --git a/Code/Framework/AzCore/Tests/Asset/AssetManagerStreamingTests.cpp b/Code/Framework/AzCore/Tests/Asset/AssetManagerStreamingTests.cpp index a683f9630e..3e9ea42f22 100644 --- a/Code/Framework/AzCore/Tests/Asset/AssetManagerStreamingTests.cpp +++ b/Code/Framework/AzCore/Tests/Asset/AssetManagerStreamingTests.cpp @@ -6,6 +6,7 @@ * */ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/Asset/BaseAssetManagerTest.cpp b/Code/Framework/AzCore/Tests/Asset/BaseAssetManagerTest.cpp index ba47064998..4dbd3c0e1e 100644 --- a/Code/Framework/AzCore/Tests/Asset/BaseAssetManagerTest.cpp +++ b/Code/Framework/AzCore/Tests/Asset/BaseAssetManagerTest.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp index e968ee28fc..e04c5190f9 100644 --- a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/AssetManager.cpp b/Code/Framework/AzCore/Tests/AssetManager.cpp index c5f249cf21..1e875d61dc 100644 --- a/Code/Framework/AzCore/Tests/AssetManager.cpp +++ b/Code/Framework/AzCore/Tests/AssetManager.cpp @@ -6,6 +6,7 @@ * */ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp b/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp new file mode 100644 index 0000000000..691b3f2821 --- /dev/null +++ b/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp @@ -0,0 +1,72 @@ +/* + * 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 +#include +#include + +namespace UnitTest +{ + template + class FileReaderTestFixture + : public ScopedAllocatorSetupFixture + { + public: + void SetUp() override + { + if constexpr (AZStd::is_same_v) + { + m_fileIo = AZStd::make_unique(); + } + } + + void TearDown() override + { + m_fileIo.reset(); + } + + protected: + AZStd::unique_ptr m_fileIo{}; + }; + + using FileIOTypes = ::testing::Types; + + TYPED_TEST_CASE(FileReaderTestFixture, FileIOTypes); + + TYPED_TEST(FileReaderTestFixture, ConstructorWithFilePath_OpensFileSuccessfully) + { + AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename()); + EXPECT_TRUE(fileReader.IsOpen()); + } + + TYPED_TEST(FileReaderTestFixture, Open_OpensFileSucessfully) + { + AZ::IO::FileReader fileReader; + fileReader.Open(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename()); + EXPECT_TRUE(fileReader.IsOpen()); + } + + TYPED_TEST(FileReaderTestFixture, Eof_OnNULDeviceFile_Succeeds) + { + AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename()); + EXPECT_TRUE(fileReader.Eof()); + } + + TYPED_TEST(FileReaderTestFixture, GetFilePath_ReturnsNULDeviceFilename_Succeeds) + { + AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename()); + AZ::IO::FixedMaxPath filePath; + EXPECT_TRUE(fileReader.GetFilePath(filePath)); + AZ::IO::FixedMaxPath nulFilename{ AZ::IO::SystemFile::GetNullFilename() }; + if (this->m_fileIo) + { + EXPECT_TRUE(this->m_fileIo->ResolvePath(nulFilename, nulFilename)); + } + EXPECT_EQ(nulFilename, filePath); + } + +} // namespace UnitTest diff --git a/Code/Framework/AzCore/Tests/Serialization.cpp b/Code/Framework/AzCore/Tests/Serialization.cpp index e554945b55..f1d5edc490 100644 --- a/Code/Framework/AzCore/Tests/Serialization.cpp +++ b/Code/Framework/AzCore/Tests/Serialization.cpp @@ -9,6 +9,7 @@ #include "FileIOBaseTestTypes.h" #include +#include #include #include diff --git a/Code/Framework/AzCore/Tests/UUIDTests.cpp b/Code/Framework/AzCore/Tests/UUIDTests.cpp index 5d4fb7a711..a18dc33c4f 100644 --- a/Code/Framework/AzCore/Tests/UUIDTests.cpp +++ b/Code/Framework/AzCore/Tests/UUIDTests.cpp @@ -7,6 +7,7 @@ */ #include #include +#include using namespace AZ; diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index d4d107f094..c36d37d874 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -37,6 +37,7 @@ set(FILES FileIOBaseTestTypes.h Geometry2DUtils.cpp Interface.cpp + IO/FileReaderTests.cpp IO/Path/PathTests.cpp IPC.cpp Jobs.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.h b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.h index 646410f8db..ae1e3dfa9c 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetBundleManifest.h b/Code/Framework/AzFramework/AzFramework/Asset/AssetBundleManifest.h index 9e05263c57..9760482231 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetBundleManifest.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetBundleManifest.h @@ -8,7 +8,10 @@ #pragma once -#include +#include +#include +#include +#include namespace AZ { diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetSeedList.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetSeedList.cpp index d16f4bf459..b95efc4dff 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetSeedList.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetSeedList.cpp @@ -8,6 +8,7 @@ */ #include +#include namespace AzFramework { diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetSeedList.h b/Code/Framework/AzFramework/AzFramework/Asset/AssetSeedList.h index 2ad895596b..e982cb54e1 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetSeedList.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetSeedList.h @@ -8,7 +8,6 @@ */ #pragma once -#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkAsset.cpp b/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkAsset.cpp index cea1cb1d8e..351eb97245 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkAsset.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkAsset.cpp @@ -7,6 +7,7 @@ */ #include +#include namespace AzFramework { diff --git a/Code/Framework/AzFramework/AzFramework/Asset/SimpleAsset.cpp b/Code/Framework/AzFramework/AzFramework/Asset/SimpleAsset.cpp index 87e603015a..29cfdc5ca7 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/SimpleAsset.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/SimpleAsset.cpp @@ -7,6 +7,7 @@ */ #include +#include #include namespace AzFramework @@ -36,4 +37,36 @@ namespace AzFramework return ""; } + void SimpleAssetReferenceBase::Reflect(AZ::ReflectContext *context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("AssetPath", &SimpleAssetReferenceBase::m_assetPath); + + AZ::EditContext* edit = serializeContext->GetEditContext(); + if (edit) + { + edit->Class("Asset path", "Asset reference as a project-relative path") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide) + ; + } + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Asset") + ->Attribute(AZ::Script::Attributes::Module, "asset") + ->Property("assetPath", &SimpleAssetReferenceBase::GetAssetPath, nullptr) + ->Property("assetType", &SimpleAssetReferenceBase::GetAssetType, nullptr) + ->Property("fileFilter", &SimpleAssetReferenceBase::GetFileFilter, nullptr) + ->Method("SetAssetPath", &SimpleAssetReferenceBase::SetAssetPath) + ->Attribute(AZ::Script::Attributes::Alias, "set_asset_path") + ; + } + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Asset/SimpleAsset.h b/Code/Framework/AzFramework/AzFramework/Asset/SimpleAsset.h index 7c7a1b4fb2..a7525e90bd 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/SimpleAsset.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/SimpleAsset.h @@ -42,7 +42,6 @@ */ #include -#include #include #include #include @@ -74,38 +73,7 @@ namespace AzFramework virtual AZ::Data::AssetType GetAssetType() const = 0; virtual const char* GetFileFilter() const = 0; - static void Reflect(AZ::ReflectContext* context) - { - if (auto serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1) - ->Field("AssetPath", &SimpleAssetReferenceBase::m_assetPath); - - AZ::EditContext* edit = serializeContext->GetEditContext(); - if (edit) - { - edit->Class("Asset path", "Asset reference as a project-relative path") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide) - ; - } - } - - if (auto behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Category, "Asset") - ->Attribute(AZ::Script::Attributes::Module, "asset") - ->Property("assetPath", &SimpleAssetReferenceBase::GetAssetPath, nullptr) - ->Property("assetType", &SimpleAssetReferenceBase::GetAssetType, nullptr) - ->Property("fileFilter", &SimpleAssetReferenceBase::GetFileFilter, nullptr) - ->Method("SetAssetPath", &SimpleAssetReferenceBase::SetAssetPath) - ->Attribute(AZ::Script::Attributes::Alias, "set_asset_path") - ; - } - } + static void Reflect(AZ::ReflectContext* context); protected: diff --git a/Code/Framework/AzFramework/AzFramework/Asset/XmlSchemaAsset.h b/Code/Framework/AzFramework/AzFramework/Asset/XmlSchemaAsset.h index 81338d3843..45004fd7cf 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/XmlSchemaAsset.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/XmlSchemaAsset.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Entity/EntityOwnershipServiceBus.h b/Code/Framework/AzFramework/AzFramework/Entity/EntityOwnershipServiceBus.h index a8c0779529..08200afb9e 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/EntityOwnershipServiceBus.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/EntityOwnershipServiceBus.h @@ -10,6 +10,11 @@ #include +namespace AZ +{ + class Entity; +} + namespace AzFramework { using EntityContextId = AZ::Uuid; diff --git a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextBus.h b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextBus.h index b5ca37df6e..f8e4dfa98b 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextBus.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextBus.h @@ -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. diff --git a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.cpp b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.cpp index a97281b9c1..aa7e03577a 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.cpp @@ -11,9 +11,10 @@ #include #include #include +#include #include #include -#include +#include #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. diff --git a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h index b9a4ea0da1..f837bed14e 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h @@ -90,11 +90,6 @@ namespace AzFramework } private: - ////////////////////////////////////////////////////////////////////////// - // GameEntityContextRequestBus - void DestroyGameEntityOnlyInSliceMode(const AZ::EntityId&) override; - void DestroyGameEntityAndDescendantsOnlyInSliceMode(const AZ::EntityId&) override; - ///////////////////////////////////////////////////////////////////////// AzFramework::EntityVisibilityBoundsUnionSystem m_entityVisibilityBoundsUnionSystem; }; diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp index cc6154f20c..496d89d767 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp @@ -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(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h index 5c1c58d6b1..a4d8ac83eb 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h @@ -11,6 +11,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// 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 = ""); - - //////////////////////////////////////////////////////////////////////////////////////////// - //! 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); + //! \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) + { + } - //////////////////////////////////////////////////////////////////////////////////////////// - //! 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 m_name; //!< Name of the input channel AZ::Crc32 m_crc32; //!< Crc32 of the input channel }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp index 95886c6871..51aa008519 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp @@ -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 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 InputDeviceGamepad::Trigger::All = - {{ - L2, - R2 - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::L("gamepad_thumbstick_l"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::R("gamepad_thumbstick_r"); - const AZStd::array 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 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 InputDeviceGamepad::ThumbStickDirection::All = - {{ - LU, - LD, - LL, - LR, - RU, - RD, - RL, - RR - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceGamepad::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h index f4fddbb24e..286b4f0df3 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h @@ -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 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 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 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 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 All; + static constexpr inline AZStd::array All + { + LU, + LD, + LL, + LR, + RU, + RD, + RL, + RR + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp index dd84fd9633..d1117ea895 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp @@ -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 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) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h index 5588f94d69..e3c21ec326 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h @@ -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 - - // 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 + 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 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 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 + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp index 34c72f20d2..c144f63d2c 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp @@ -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 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 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 InputDeviceMotion::MagneticField::All = - {{ - North, - Raw, - Unbiased - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::Orientation::Current("motion_orientation_current"); - const AZStd::array InputDeviceMotion::Orientation::All = - {{ - Current - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceMotion::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h index 1e020a6e02..872bd1cfa0 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h @@ -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 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 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 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 All; + static constexpr inline AZStd::array All + { + Current + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp index 2cc4573bce..e9af4cc4ce 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp @@ -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 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 InputDeviceMouse::Movement::All = - {{ - X, - Y, - Z - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMouse::SystemCursorPosition("mouse_system_cursor_position"); - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceMouse::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h index 99fe5b820c..3b35e03f1b 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h @@ -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 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 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 diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp index 07d10090a3..be850e9289 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp @@ -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 InputDeviceTouch::Touch::All = - {{ - Index0, - Index1, - Index2, - Index3, - Index4, - Index5, - Index6, - Index7, - Index8, - Index9 - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceTouch::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h index 6e069c44ba..d21834c22a 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h @@ -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 All; + static constexpr inline AZStd::array All + { + Index0, + Index1, + Index2, + Index3, + Index4, + Index5, + Index6, + Index7, + Index8, + Index9 + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp index bc23826ebc..f3024f11ab 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp @@ -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 InputDeviceVirtualKeyboard::Command::All = - {{ - EditClear, - EditEnter, - NavigationBack - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceVirtualKeyboard::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h index 43e2815875..6f62c3ec61 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h @@ -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 All; + static constexpr inline AZStd::array All + { + EditClear, + EditEnter, + NavigationBack + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Logging/MissingAssetLogger.h b/Code/Framework/AzFramework/AzFramework/Logging/MissingAssetLogger.h index 9434ca80ae..b49609d820 100644 --- a/Code/Framework/AzFramework/AzFramework/Logging/MissingAssetLogger.h +++ b/Code/Framework/AzFramework/AzFramework/Logging/MissingAssetLogger.h @@ -8,6 +8,7 @@ #pragma once +#include #include namespace AzFramework { class LogFile; } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ClassConverters.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ClassConverters.cpp index 9c1a8c83ca..7e918ee364 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ClassConverters.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ClassConverters.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionEvents.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionEvents.cpp index e98bbcc21a..9c1646fa08 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionEvents.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionEvents.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionGroups.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionGroups.cpp index 2100b0c394..13f7dbc672 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionGroups.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionGroups.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSceneQueries.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSceneQueries.cpp index ff79f097de..28e3762223 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSceneQueries.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSceneQueries.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp index f55c430a2e..4a2a7f99af 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp index 4ac97cf041..222bc48dda 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp @@ -5,6 +5,8 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp index 62a3916e9e..a5ebdd04c6 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace AzPhysics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp index 2fda459e20..2ff4780c9e 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp @@ -8,6 +8,8 @@ #include +#include + namespace AzPhysics { void SystemInterface::Reflect(AZ::ReflectContext* context) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Shape.h b/Code/Framework/AzFramework/AzFramework/Physics/Shape.h index 6549269c77..0a766473e1 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Shape.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Shape.h @@ -9,7 +9,6 @@ #pragma once #include -#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp index ab5cb90616..e90c9d4eed 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp @@ -7,9 +7,11 @@ */ #include +#include #include #include #include +#include namespace Physics { diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h index 07845e95b9..b40a8b1edd 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp index 81af7b5840..3dcb37c541 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp @@ -10,12 +10,14 @@ #include "Utils.h" #include "Material.h" #include "Shape.h" + +#include +#include #include #include #include #include #include -#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Render/GeometryIntersectionStructures.h b/Code/Framework/AzFramework/AzFramework/Render/GeometryIntersectionStructures.h index 11c3fedb2b..9d6cd48102 100644 --- a/Code/Framework/AzFramework/AzFramework/Render/GeometryIntersectionStructures.h +++ b/Code/Framework/AzFramework/AzFramework/Render/GeometryIntersectionStructures.h @@ -11,6 +11,7 @@ #include #include #include +#include #include //! Common structures for Render geometry queries diff --git a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp index e11dc6dace..bfd7c5ac4c 100644 --- a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp index 75fca39574..171d626b27 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp @@ -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) @@ -294,6 +298,8 @@ namespace AzFramework AZStd::pair 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; } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 6901fc7116..74a17020df 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -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; using EntityPreInsertionCallback = AZStd::function; using EntityDespawnCallback = AZStd::function; + using RetrieveEntitySpawnTicketCallback = AZStd::function; using ReloadSpawnableCallback = AZStd::function; using ListEntitiesCallback = AZStd::function; using ListIndicesEntitiesCallback = AZStd::function; @@ -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 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(ticket->m_payload); } + + AZStd::unordered_map m_entitySpawnTicketMap; + AZStd::recursive_mutex m_entitySpawnTicketMapMutex; }; using SpawnableEntitiesInterface = AZ::Interface; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 5fa072a451..ef7351aabb 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -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, ReloadSpawnableOptionalArgs optionalArgs) { @@ -223,12 +252,13 @@ namespace AzFramework return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft; } - AZStd::pair SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset&& spawnable) + AZStd::pair SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset&& 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(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& 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; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index 69985f8aa9..c3de5be003 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -57,6 +57,8 @@ namespace AzFramework void SpawnEntities( EntitySpawnTicket& ticket, AZStd::vector 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, 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 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); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index cb1e657edd..f6130c9e31 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/StreamingInstall/StreamingInstall.cpp b/Code/Framework/AzFramework/AzFramework/StreamingInstall/StreamingInstall.cpp index e1e193abfd..e90dcc17a1 100644 --- a/Code/Framework/AzFramework/AzFramework/StreamingInstall/StreamingInstall.cpp +++ b/Code/Framework/AzFramework/AzFramework/StreamingInstall/StreamingInstall.cpp @@ -7,6 +7,7 @@ */ #include +#include #include "StreamingInstall.h" namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/StreamingInstall/StreamingInstallRequests.h b/Code/Framework/AzFramework/AzFramework/StreamingInstall/StreamingInstallRequests.h index dba2a78f3c..201f9007c5 100644 --- a/Code/Framework/AzFramework/AzFramework/StreamingInstall/StreamingInstallRequests.h +++ b/Code/Framework/AzFramework/AzFramework/StreamingInstall/StreamingInstallRequests.h @@ -9,10 +9,8 @@ #pragma once #include -#include #include #include -#include namespace AzFramework { diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index 18667ac153..a3eb4d8329 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -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(cursorDelta.m_x) * panAxes.m_horizontalAxis * panSpeed; + const auto deltaPanY = aznumeric_cast(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(&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(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(&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(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(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; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index 69e8b66434..c44d951292 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -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; + + 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 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 m_cursorSpeedFn; + AZStd::function 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(const AZ::Vector3& position, const AZ::Vector3& direction)>; + using PivotFn = AZStd::function; - 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; } diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ScreenGeometry.h b/Code/Framework/AzFramework/AzFramework/Viewport/ScreenGeometry.h index c0fa1ad631..1529d760e5 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ScreenGeometry.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ScreenGeometry.h @@ -8,11 +8,10 @@ #pragma once -#include +#include #include -#include #include -#include +#include 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,21 +138,23 @@ namespace AzFramework return !operator==(lhs, rhs); } - inline float ScreenVectorLength(const ScreenVector& screenVector) + inline ScreenVector& operator*=(ScreenVector& lhs, const float rhs) { - return aznumeric_cast(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y)); + lhs.m_x = aznumeric_cast(AZStd::lround(aznumeric_cast(lhs.m_x) * rhs)); + lhs.m_y = aznumeric_cast(AZStd::lround(aznumeric_cast(lhs.m_y) * rhs)); + return lhs; } - inline ScreenPoint ScreenPointFromNDC(const AZ::Vector3& screenNDC, const AZ::Vector2& viewportSize) + inline const ScreenVector operator*(const ScreenVector& lhs, const float rhs) { - return ScreenPoint( - aznumeric_caster(AZStd::round(screenNDC.GetX() * viewportSize.GetX())), - aznumeric_caster(AZStd::round((1.0f - screenNDC.GetY()) * viewportSize.GetY()))); + ScreenVector result{ lhs }; + result *= rhs; + return result; } - inline AZ::Vector2 NDCFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize) + inline float ScreenVectorLength(const ScreenVector& screenVector) { - return AZ::Vector2(aznumeric_cast(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast(screenPoint.m_y)) / viewportSize; + return aznumeric_cast(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y)); } //! Return an AZ::Vector2 from a ScreenPoint. diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp index a94b806ad3..afd16a6c23 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp @@ -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 diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h index 87bad66eab..8a6c0249b2 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h @@ -8,26 +8,42 @@ #pragma once +#include #include +#include +#include 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(AZStd::lround(screenNdc.GetX() * viewportSize.GetX())), + aznumeric_cast(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(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast(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); diff --git a/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.h b/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.h index 7479b0d1e1..0eb699475f 100644 --- a/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.h +++ b/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include diff --git a/Code/Framework/AzFramework/CMakeLists.txt b/Code/Framework/AzFramework/CMakeLists.txt index 6f6e47855e..59393d90a1 100644 --- a/Code/Framework/AzFramework/CMakeLists.txt +++ b/Code/Framework/AzFramework/CMakeLists.txt @@ -76,6 +76,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAMESPACE AZ FILES_CMAKE Tests/frameworktests_files.cmake + ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake INCLUDE_DIRECTORIES PRIVATE Tests @@ -93,6 +94,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAME AZ::AzFramework.Tests ) + include(${pal_dir}/platform_specific_test_targets.cmake) + endif() -endif() \ No newline at end of file +endif() diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp new file mode 100644 index 0000000000..f57f4a89ac --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp @@ -0,0 +1,87 @@ +/* + * 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 +#include +#include + +namespace AzFramework +{ + //////////////////////////////////////////////////////////////////////////////////////////////// + class XcbConnectionManagerImpl + : public XcbConnectionManagerBus::Handler + { + public: + XcbConnectionManagerImpl() + : m_xcbConnection(xcb_connect(nullptr, nullptr)) + { + AZ_Error("Application", m_xcbConnection != nullptr, "Unable to connect to X11 Server."); + XcbConnectionManagerBus::Handler::BusConnect(); + } + + ~XcbConnectionManagerImpl() override + { + XcbConnectionManagerBus::Handler::BusDisconnect(); + } + + xcb_connection_t* GetXcbConnection() const override + { + return m_xcbConnection.get(); + } + + private: + XcbUniquePtr m_xcbConnection = nullptr; + }; + + //////////////////////////////////////////////////////////////////////////////////////////////// + XcbApplication::XcbApplication() + { + LinuxLifecycleEvents::Bus::Handler::BusConnect(); + m_xcbConnectionManager = AZStd::make_unique(); + if (XcbConnectionManagerInterface::Get() == nullptr) + { + XcbConnectionManagerInterface::Register(m_xcbConnectionManager.get()); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + XcbApplication::~XcbApplication() + { + if (XcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get()) + { + XcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get()); + } + m_xcbConnectionManager.reset(); + LinuxLifecycleEvents::Bus::Handler::BusDisconnect(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void XcbApplication::PumpSystemEventLoopOnce() + { + if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection()) + { + if (auto event = XcbStdFreePtr{xcb_poll_for_event(xcbConnection)}) + { + XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event.get()); + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void XcbApplication::PumpSystemEventLoopUntilEmpty() + { + if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection()) + { + while (auto event = XcbStdFreePtr{xcb_poll_for_event(xcbConnection)}) + { + XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event.get()); + } + } + } + +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.h similarity index 61% rename from Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h rename to Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.h index fbd7f165d3..f27b5dd680 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.h @@ -5,27 +5,24 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include #include +#include -//////////////////////////////////////////////////////////////////////////////////////////////////// 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 m_xcbConnectionManager; + AZStd::unique_ptr m_xcbConnectionManager; }; - -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h new file mode 100644 index 0000000000..daa5bf35af --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h @@ -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 +#include +#include + +#include + +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; + using XcbConnectionManagerInterface = AZ::Interface; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h new file mode 100644 index 0000000000..c6307755a7 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h @@ -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 +#include + +#include + +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; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp new file mode 100644 index 0000000000..c971c3b793 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp @@ -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 +#include +#include +#include + +#define explicit ExplicitIsACXXKeyword +#include +#undef explicit +#include +#include +#include + +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 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(event); + + const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail); + if (key) + { + QueueRawKeyEvent(*key, true); + } + break; + } + case XCB_KEY_RELEASE: + { + auto* keyRelease = reinterpret_cast(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 diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h new file mode 100644 index 0000000000..de65941a67 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h @@ -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 +#include +#include + +#include +#include + +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 m_xkbContext; + XcbUniquePtr m_xkbKeymap; + XcbUniquePtr m_xkbState; + int m_coreDeviceId{-1}; + bool m_initialized{false}; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInterface.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInterface.h new file mode 100644 index 0000000000..838048b174 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInterface.h @@ -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 + +#include + +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 + struct XcbDeleterFreeFunctionWrapper + { + using value_type = decltype(Callable); + static constexpr value_type s_value = Callable; + constexpr operator value_type() const noexcept + { + return s_value; + } + }; + + template + using XcbUniquePtr = AZStd::unique_ptr>; + + template + using XcbStdFreePtr = XcbUniquePtr; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp similarity index 85% rename from Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp rename to Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp index cfc0222214..6ab97fd616 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp @@ -6,41 +6,37 @@ * */ -#include #include #include -#include +#include +#include -#include "NativeWindow_Linux_xcb.h" +#include 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(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 diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h similarity index 78% rename from Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h rename to Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h index 40181395e4..36737e24e8 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h @@ -5,24 +5,25 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include #include #include +#include + #include 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 diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake b/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake new file mode 100644 index 0000000000..68de710fa1 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake @@ -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 +) diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h b/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h index 833f4019f8..adf3ee0bdd 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h @@ -12,10 +12,6 @@ #include #include -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB -#include -#endif // LY_COMPILE_DEFINITIONS - namespace AzFramework { class LinuxLifecycleEvents @@ -30,54 +26,4 @@ namespace AzFramework using Bus = AZ::EBus; }; - -#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; - using LinuxXcbConnectionManagerInterface = AZ::Interface; - - 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; - -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp index 59602fe788..158240210e 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp @@ -8,7 +8,9 @@ #include -#include "Application_Linux_xcb.h" +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#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; diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp deleted file mode 100644 index aaab67b2a1..0000000000 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp +++ /dev/null @@ -1,94 +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 -#include -#include "Application_Linux_xcb.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -namespace AzFramework -{ -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - //////////////////////////////////////////////////////////////////////////////////////////////// - class LinuxXcbConnectionManagerImpl - : public LinuxXcbConnectionManagerBus::Handler - { - public: - LinuxXcbConnectionManagerImpl() - { - m_xcbConnection = xcb_connect(nullptr, nullptr); - AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server."); - LinuxXcbConnectionManagerBus::Handler::BusConnect(); - } - - ~LinuxXcbConnectionManagerImpl() - { - LinuxXcbConnectionManagerBus::Handler::BusDisconnect(); - xcb_disconnect(m_xcbConnection); - } - - xcb_connection_t* GetXcbConnection() const override - { - return m_xcbConnection; - } - - private: - xcb_connection_t* m_xcbConnection = nullptr; - }; - - //////////////////////////////////////////////////////////////////////////////////////////////// - ApplicationLinux_xcb::ApplicationLinux_xcb() - { - LinuxLifecycleEvents::Bus::Handler::BusConnect(); - m_xcbConnectionManager = AZStd::make_unique(); - if (LinuxXcbConnectionManagerInterface::Get() == nullptr) - { - LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get()); - } - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - ApplicationLinux_xcb::~ApplicationLinux_xcb() - { - if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get()) - { - LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get()); - } - m_xcbConnectionManager.reset(); - LinuxLifecycleEvents::Bus::Handler::BusDisconnect(); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - void ApplicationLinux_xcb::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); - free(event); - } - } - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - void ApplicationLinux_xcb::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); - free(event); - } - } - } - -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - -} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp new file mode 100644 index 0000000000..d1f90c3052 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp @@ -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 +#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 diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp deleted file mode 100644 index 8ff4c72d42..0000000000 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp +++ /dev/null @@ -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 -#include -#include - -#define explicit ExplicitIsACXXKeyword -#include -#undef explicit -#include -#include -#include - -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> 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(event); - - const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail); - if (key) - { - QueueRawKeyEvent(*key, true); - } - break; - } - case XCB_KEY_RELEASE: - { - auto* keyRelease = reinterpret_cast(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 - using DeleterForFreeFn = AZStd::integral_constant; - - AZStd::unique_ptr> m_xkbContext; - AZStd::unique_ptr> m_xkbKeymap; - AZStd::unique_ptr> m_xkbState; - int m_coreDeviceId{-1}; - bool m_initialized{false}; - }; - - InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice) - { - return aznew InputDeviceKeyboardXcb(inputDevice); - } -} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp index 2950f8dcfc..ba3e761974 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp @@ -6,14 +6,16 @@ * */ -#include "NativeWindow_Linux_xcb.h" +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#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 diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake index 5c8ca8392f..b08c3b310d 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake @@ -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) diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake index 93767322b2..4f06aa5c57 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake @@ -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 diff --git a/Code/Framework/AzFramework/Tests/CameraInputTests.cpp b/Code/Framework/AzFramework/Tests/CameraInputTests.cpp index df7d22f7ca..1e0c805587 100644 --- a/Code/Framework/AzFramework/Tests/CameraInputTests.cpp +++ b/Code/Framework/AzFramework/Tests/CameraInputTests.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::InputDeviceMouse::Button::Right); - m_firstPersonTranslateCamera = - AZStd::make_shared(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_firstPersonTranslateCamera = AZStd::make_shared( + m_translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot); + + m_pivotCamera = AZStd::make_shared(m_pivotChannelId); + m_pivotCamera->SetPivotFn( + [this](const AZ::Vector3&, const AZ::Vector3&) + { + return m_pivot; + }); + + auto pivotRotateCamera = AZStd::make_shared(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; + }; - m_orbitCamera = AZStd::make_shared(m_orbitChannelId); - auto orbitRotateCamera = AZStd::make_shared(AzFramework::InputDeviceMouse::Button::Left); - auto orbitTranslateCamera = - AZStd::make_shared(AzFramework::OrbitTranslation, m_translateCameraInputChannelIds); + auto pivotTranslateCamera = AZStd::make_shared( + m_translateCameraInputChannelIds, AzFramework::PivotTranslation, AzFramework::TranslateOffset); - m_orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera); - m_orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera); + 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 m_firstPersonRotateCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; - AZStd::shared_ptr m_orbitCamera; + AZStd::shared_ptr 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)); + + HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, 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)); + } + + 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); - m_camera = m_targetCamera; + 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 }); - HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began }); + const auto expectedCameraEndingPosition = AZ::Vector3(20.0f, -5.0f, 0.0f); + const float expectedYaw = AzFramework::WrapYawRotation(AZ::Constants::HalfPi); - // 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()))); + 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 diff --git a/Code/Framework/AzFramework/Tests/Main.cpp b/Code/Framework/AzFramework/Tests/Main.cpp new file mode 100644 index 0000000000..c88648458a --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Main.cpp @@ -0,0 +1,9 @@ +/* + * 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 +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h b/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h index a5df7af4c3..a437545adf 100644 --- a/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h @@ -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, ReloadSpawnableOptionalArgs optionalArgs)); diff --git a/Code/Framework/AzFramework/Tests/Platform/Android/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/Android/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Android/platform_specific_test_targets.cmake @@ -0,0 +1,7 @@ +# +# 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 +# +# diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h new file mode 100644 index 0000000000..1650ff4f8d --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h @@ -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 + * + */ + +#pragma once + +#include +#include + +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_1_VALUE_PARAMS(p0)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_2_VALUE_PARAMS(p0, p1)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1 }; + return value; +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Main.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Main.cpp new file mode 100644 index 0000000000..c88648458a --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Main.cpp @@ -0,0 +1,9 @@ +/* + * 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 +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp new file mode 100644 index 0000000000..64c3967fdc --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp @@ -0,0 +1,81 @@ +/* + * 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 "MockXcbInterface.h" + +// The functions defined in this file will take precedence over those defined +// in the real libxcb.so, allowing the test code to use a mock implementation + +extern "C" +{ + +// ---------------------------------------------------------------------------- +// xcb +xcb_connection_t* xcb_connect(const char* displayname, int* screenp) +{ + return MockXcbInterface::Instance()->xcb_connect(displayname, screenp); +} +void xcb_disconnect(xcb_connection_t* c) +{ + return MockXcbInterface::Instance()->xcb_disconnect(c); +} +xcb_generic_event_t* xcb_poll_for_event(xcb_connection_t* c) +{ + return MockXcbInterface::Instance()->xcb_poll_for_event(c); +} + +// ---------------------------------------------------------------------------- +// xcb-xkb +xcb_xkb_use_extension_cookie_t xcb_xkb_use_extension(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor) +{ + return MockXcbInterface::Instance()->xcb_xkb_use_extension(c, wantedMajor, wantedMinor); +} +xcb_xkb_use_extension_reply_t* xcb_xkb_use_extension_reply(xcb_connection_t* c, xcb_xkb_use_extension_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_xkb_use_extension_reply(c, cookie, e); +} + +// ---------------------------------------------------------------------------- +// xkb-x11 +int32_t xkb_x11_get_core_keyboard_device_id(xcb_connection_t* connection) +{ + return MockXcbInterface::Instance()->xkb_x11_get_core_keyboard_device_id(connection); +} +struct xkb_keymap* xkb_x11_keymap_new_from_device(struct xkb_context* context, xcb_connection_t* connection, int32_t device_id, enum xkb_keymap_compile_flags flags) +{ + return MockXcbInterface::Instance()->xkb_x11_keymap_new_from_device(context, connection, device_id, flags); +} +xkb_state* xkb_x11_state_new_from_device(xkb_keymap* keymap, xcb_connection_t* connection, int32_t device_id) +{ + return MockXcbInterface::Instance()->xkb_x11_state_new_from_device(keymap, connection, device_id); +} + +// ---------------------------------------------------------------------------- +// xkbcommon +xkb_context* xkb_context_new(enum xkb_context_flags flags) +{ + return MockXcbInterface::Instance()->xkb_context_new(flags); +} +void xkb_context_unref(xkb_context *context) +{ + return MockXcbInterface::Instance()->xkb_context_unref(context); +} +void xkb_keymap_unref(xkb_keymap *keymap) +{ + return MockXcbInterface::Instance()->xkb_keymap_unref(keymap); +} +void xkb_state_unref(xkb_state *state) +{ + return MockXcbInterface::Instance()->xkb_state_unref(state); +} +xkb_keysym_t xkb_state_key_get_one_sym(xkb_state *state, xkb_keycode_t key) +{ + return MockXcbInterface::Instance()->xkb_state_key_get_one_sym(state, key); +} + +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h new file mode 100644 index 0000000000..ecda005830 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h @@ -0,0 +1,81 @@ +/* + * 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 +#include + +#include + +#define explicit ExplicitIsACXXKeyword +#include +#undef explicit +#include + +#include "Printers.h" + +// xcb / xkb do not provide definitions of these structs, but the tests need some concrete value for them. +struct xcb_connection_t +{ +}; + +struct xkb_context +{ +}; + +struct xkb_keymap +{ +}; + +struct xkb_state +{ +}; + +class MockXcbInterface +{ +public: + MockXcbInterface() + { + self = this; + } + MockXcbInterface(const MockXcbInterface&) = delete; + MockXcbInterface(MockXcbInterface&&) = delete; + MockXcbInterface& operator=(const MockXcbInterface&) = delete; + MockXcbInterface& operator=(MockXcbInterface&&) = delete; + ~MockXcbInterface() + { + self = nullptr; + } + + static MockXcbInterface* Instance() { return self; } + + // xcb + MOCK_CONST_METHOD2(xcb_connect, xcb_connection_t*(const char* displayname, int* screenp)); + MOCK_CONST_METHOD1(xcb_disconnect, void(xcb_connection_t* c)); + MOCK_CONST_METHOD1(xcb_poll_for_event, xcb_generic_event_t*(xcb_connection_t* c)); + + // xcb-xkb + MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor)); + MOCK_CONST_METHOD3(xcb_xkb_use_extension_reply, xcb_xkb_use_extension_reply_t*(xcb_connection_t* c, xcb_xkb_use_extension_cookie_t cookie, xcb_generic_error_t** e)); + + // xkb-x11 + MOCK_CONST_METHOD1(xkb_x11_get_core_keyboard_device_id, int32_t(xcb_connection_t* connection)); + MOCK_CONST_METHOD4(xkb_x11_keymap_new_from_device, xkb_keymap*(xkb_context* context, xcb_connection_t* connection, int32_t device_id, xkb_keymap_compile_flags flags)); + MOCK_CONST_METHOD3(xkb_x11_state_new_from_device, xkb_state*(xkb_keymap* keymap, xcb_connection_t* connection, int32_t device_id)); + + // xkbcommon + MOCK_CONST_METHOD1(xkb_context_new, xkb_context*(xkb_context_flags flags)); + MOCK_CONST_METHOD1(xkb_context_unref, void(xkb_context* context)); + MOCK_CONST_METHOD1(xkb_keymap_unref, void(xkb_keymap* keymap)); + MOCK_CONST_METHOD1(xkb_state_unref, void(xkb_state* state)); + MOCK_CONST_METHOD2(xkb_state_key_get_one_sym, xkb_keysym_t(xkb_state *state, xkb_keycode_t key)); + +private: + static inline MockXcbInterface* self = nullptr; +}; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.cpp new file mode 100644 index 0000000000..9de17bd776 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "Printers.h" +#include +#include + +namespace AzFramework +{ + void PrintTo(const InputChannel::State& state, std::ostream* os) + { + switch(state) + { + case InputChannel::State::Began: + *os << "Began"; + break; + case InputChannel::State::Ended: + *os << "Ended"; + break; + case InputChannel::State::Idle: + *os << "Idle"; + break; + case InputChannel::State::Updated: + *os << "Updated"; + break; + } + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.h new file mode 100644 index 0000000000..0659ec6eb1 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.h @@ -0,0 +1,17 @@ +/* + * 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 +#include + +namespace AzFramework +{ + void PrintTo(const InputChannel::State& state, std::ostream* os); +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp new file mode 100644 index 0000000000..1b494c9525 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp @@ -0,0 +1,145 @@ +/* + * 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 +#include + +#include + +#include +#include +#include "MockXcbInterface.h" +#include "Actions.h" + +template +xcb_generic_event_t MakeEvent(T event) +{ + return *reinterpret_cast(&event); +} + +namespace AzFramework +{ + TEST(XcbInputDeviceKeyboard, InputChannelsUpdateStateFromXcbEvents) + { + using testing::Return; + using testing::Eq; + using testing::_; + MockXcbInterface interface; + + xcb_connection_t connection{}; + xkb_context xkbContext{}; + xkb_keymap xkbKeymap{}; + xkb_state xkbState{}; + const int32_t coreDeviceId{1}; + + constexpr xcb_keycode_t keycodeForAKey = 38; + + const AZStd::array events + { + MakeEvent(xcb_key_press_event_t{ + /*.response_type = */ XCB_KEY_PRESS, + /*.detail = */ keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_key_release_event_t{ + /*.response_type = */ XCB_KEY_RELEASE, + /*.detail = */ keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + }; + + EXPECT_CALL(interface, xcb_connect(_, _)) + .WillOnce(Return(&connection)); + EXPECT_CALL(interface, xcb_disconnect(&connection)) + .Times(1); + + EXPECT_CALL(interface, xkb_context_new(XKB_CONTEXT_NO_FLAGS)) + .WillOnce(Return(&xkbContext)); + EXPECT_CALL(interface, xkb_context_unref(&xkbContext)) + .Times(1); + + EXPECT_CALL(interface, xkb_x11_keymap_new_from_device(&xkbContext, &connection, coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS)) + .WillOnce(Return(&xkbKeymap)); + EXPECT_CALL(interface, xkb_keymap_unref(&xkbKeymap)) + .Times(1); + + EXPECT_CALL(interface, xkb_x11_state_new_from_device(&xkbKeymap, &connection, coreDeviceId)) + .WillOnce(Return(&xkbState)); + EXPECT_CALL(interface, xkb_state_unref(&xkbState)) + .Times(1); + + EXPECT_CALL(interface, xcb_xkb_use_extension(&connection, 1, 0)); + EXPECT_CALL(interface, xcb_xkb_use_extension_reply(&connection, _, _)) + .WillOnce(ReturnMalloc( + /* .response_type =*/static_cast(XCB_XKB_USE_EXTENSION), + /* .supported =*/ static_cast(1)) + ); + EXPECT_CALL(interface, xkb_x11_get_core_keyboard_device_id(&connection)) + .WillRepeatedly(Return(coreDeviceId)); + + // Set the expectations for the events that will be generated + // nullptr entries represent when the event queue is empty, and will cause + // PumpSystemEventLoopUntilEmpty to return + // event pointers are freed by the calling code, so we malloc new copies + // here + EXPECT_CALL(interface, xcb_poll_for_event(&connection)) + .WillOnce(ReturnMalloc(events[0])) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(events[1])) + .WillOnce(Return(nullptr)) + ; + + EXPECT_CALL(interface, xkb_state_key_get_one_sym(&xkbState, keycodeForAKey)) + .WillOnce(Return(XKB_KEY_a)) + .WillOnce(Return(XKB_KEY_a)) + ; + + Application application; + application.Start({}, {}); + + const InputChannel* inputChannel = InputChannelRequests::FindInputChannel(InputDeviceKeyboard::Key::AlphanumericA); + ASSERT_TRUE(inputChannel); + EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Idle)); + + application.PumpSystemEventLoopUntilEmpty(); + application.TickSystem(); + application.Tick(); + + EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Began)); + + application.PumpSystemEventLoopUntilEmpty(); + application.TickSystem(); + application.Tick(); + + EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Ended)); + + application.Stop(); + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake new file mode 100644 index 0000000000..aebd28222b --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake @@ -0,0 +1,17 @@ +# +# 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 + Actions.h + Main.cpp + MockXcbInterface.cpp + MockXcbInterface.h + Printers.cpp + Printers.h + XcbInputDeviceKeyboardTests.cpp +) diff --git a/Code/Framework/AzFramework/Tests/Platform/Linux/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/Linux/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..e443831462 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Linux/platform_specific_test_targets.cmake @@ -0,0 +1,33 @@ +# +# 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} STREQUAL "xcb") + # This library defines local implementations of all the used xcb functions, + # in order to allow tests to run in the absence of a running X server. + # These have to be in a separate library, so that the normal AzFramework + # tests do not need to set up a mock Xcb interface. + ly_add_target( + NAME AzFramework.Xcb.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE AZ + FILES_CMAKE + Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + ${pal_dir} + BUILD_DEPENDENCIES + PRIVATE + AZ::AzFramework + AZ::AzTest + AZ::AzTestShared + AZ::AzFrameworkTestShared + ) + ly_add_googletest( + NAME AZ::AzFramework.Xcb.Tests + ) +endif() diff --git a/Code/Framework/AzFramework/Tests/Platform/Mac/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/Mac/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Mac/platform_specific_test_targets.cmake @@ -0,0 +1,7 @@ +# +# 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 +# +# diff --git a/Code/Framework/AzFramework/Tests/Platform/Windows/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/Windows/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Windows/platform_specific_test_targets.cmake @@ -0,0 +1,7 @@ +# +# 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 +# +# diff --git a/Code/Framework/AzFramework/Tests/Platform/iOS/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/iOS/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/iOS/platform_specific_test_targets.cmake @@ -0,0 +1,7 @@ +# +# 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 +# +# diff --git a/Code/Framework/AzFramework/Tests/frameworktests_files.cmake b/Code/Framework/AzFramework/Tests/frameworktests_files.cmake index 680155d49d..6c4f611352 100644 --- a/Code/Framework/AzFramework/Tests/frameworktests_files.cmake +++ b/Code/Framework/AzFramework/Tests/frameworktests_files.cmake @@ -7,7 +7,7 @@ # set(FILES - ../../AzCore/Tests/Main.cpp + Main.cpp Spawnable/SpawnableEntitiesInterfaceTests.cpp Spawnable/SpawnableEntitiesManagerTests.cpp ArchiveCompressionTests.cpp diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp index f759857505..475a7d5504 100644 --- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp +++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp @@ -32,17 +32,14 @@ namespace AzGameFramework // at the Assets alias, otherwise to attempting to mount the engine pak // from the Cache folder AZ::IO::FixedMaxPath enginePakPath = AZ::Utils::GetExecutableDirectory(); - enginePakPath /= "Engine.pak"; - if (m_archiveFileIO->Exists(enginePakPath.c_str())) + enginePakPath /= "engine.pak"; + if (!m_archive->OpenPack("@assets@", enginePakPath.Native())) { - m_archive->OpenPack("@assets@", enginePakPath.Native()); - } - else if (enginePakPath.clear(); m_settingsRegistry->Get(enginePakPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) - { - // fall back to checking if there is an Engine.pak in the Asset Cache - enginePakPath /= "Engine.pak"; - if (m_archiveFileIO->Exists(enginePakPath.c_str())) + enginePakPath.clear(); + if (m_settingsRegistry->Get(enginePakPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) { + // fall back to checking Project Cache Root. + enginePakPath /= "engine.pak"; m_archive->OpenPack("@assets@", enginePakPath.Native()); } } diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeBitsetView.inl b/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeBitsetView.inl index 8cbdd27e09..6192cfa878 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeBitsetView.inl +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeBitsetView.inl @@ -15,12 +15,12 @@ namespace AzNetworking , m_startOffset(startOffset) , m_count(startOffset < bitset.GetValidBitCount() && startOffset + count <= bitset.GetValidBitCount() ? count : 0) { - AZ_Assert(startOffset + count <= bitset.GetValidBitCount(), "Out of bounds setup in BitsetSubset. Defaulting to 0 bit count."); + AZ_Warning("FixedSizeBitsetView", startOffset + count <= bitset.GetValidBitCount(), "Out of bounds setup in BitsetSubset. Defaulting to 0 bit count."); } inline void FixedSizeBitsetView::SetBit(uint32_t index, bool value) { - AZ_Assert(index < m_count, "Out of bounds access in BitsetSubset (requested %u, count %u)", index, m_count); + AZ_Warning("FixedSizeBitsetView", index < m_count, "Out of bounds access in BitsetSubset (requested %u, count %u)", index, m_count); if (m_count) { m_bitset.SetBit(m_startOffset + index, value); @@ -29,7 +29,7 @@ namespace AzNetworking inline bool FixedSizeBitsetView::GetBit(uint32_t index) const { - AZ_Assert(index < m_count, "Out of bounds access in BitsetSubset (requested %u, count %u)", index, m_count); + AZ_Warning("FixedSizeBitsetView", index < m_count, "Out of bounds access in BitsetSubset (requested %u, count %u)", index, m_count); if (m_count) { return m_bitset.GetBit(m_startOffset + index); diff --git a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp index 83588f4acb..16e4e26f67 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace AzNetworking diff --git a/Code/Framework/AzNetworking/AzNetworking/Serialization/ISerializer.inl b/Code/Framework/AzNetworking/AzNetworking/Serialization/ISerializer.inl index 0d705629f3..56cbb52049 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Serialization/ISerializer.inl +++ b/Code/Framework/AzNetworking/AzNetworking/Serialization/ISerializer.inl @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnectionSet.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnectionSet.h index 8594bf87db..7fa66b0470 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnectionSet.h +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnectionSet.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace AzNetworking { diff --git a/Code/Framework/AzNetworking/Tests/DataStructures/FixedSizeBitsetTests.cpp b/Code/Framework/AzNetworking/Tests/DataStructures/FixedSizeBitsetTests.cpp index f22e815526..12e242c86c 100644 --- a/Code/Framework/AzNetworking/Tests/DataStructures/FixedSizeBitsetTests.cpp +++ b/Code/Framework/AzNetworking/Tests/DataStructures/FixedSizeBitsetTests.cpp @@ -55,5 +55,8 @@ namespace UnitTest unusedBitTest.SetBit(i, false); } EXPECT_FALSE(unusedBitTest.AnySet()); + + unusedBitTest.SetBit(0, true); + EXPECT_TRUE(unusedBitTest.AnySet()); } } diff --git a/Code/Framework/AzNetworking/Tests/DataStructures/FixedSizeBitsetViewTests.cpp b/Code/Framework/AzNetworking/Tests/DataStructures/FixedSizeBitsetViewTests.cpp index 6f32eacda9..8ac3b63f1b 100644 --- a/Code/Framework/AzNetworking/Tests/DataStructures/FixedSizeBitsetViewTests.cpp +++ b/Code/Framework/AzNetworking/Tests/DataStructures/FixedSizeBitsetViewTests.cpp @@ -42,4 +42,29 @@ namespace UnitTest EXPECT_FALSE(view.GetBit(0)); } } + + TEST(FixedSizeBitsetView, EmptyBitset) + { + AzNetworking::FixedSizeBitset<32> bitset; + AzNetworking::FixedSizeBitsetView view(bitset, 10, 0); + EXPECT_FALSE(view.GetBit(0)); + } + + TEST(FixedSizeBitsetView, TestAnySet) + { + const uint32_t VIEW_SIZE = 5; + + AzNetworking::FixedSizeBitset<9> unusedBitTest(true); + AzNetworking::FixedSizeBitsetView view(unusedBitTest, 0, VIEW_SIZE); + for (uint32_t i = 0; i < VIEW_SIZE; ++i) + { + view.SetBit(i, false); + } + EXPECT_FALSE(view.AnySet()); + + view.SetBit(0, true); + EXPECT_TRUE(view.AnySet()); + + EXPECT_EQ(view.GetValidBitCount(), VIEW_SIZE); + } } diff --git a/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp b/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp index bc9ecab2cb..3031f66774 100644 --- a/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp +++ b/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp @@ -148,6 +148,11 @@ namespace UnitTest EXPECT_EQ(testServer.m_serverNetworkInterface->GetConnectionSet().GetConnectionCount(), 1); EXPECT_EQ(testClient.m_clientNetworkInterface->GetConnectionSet().GetConnectionCount(), 1); + + testClient.m_clientNetworkInterface->SetTimeoutEnabled(true); + EXPECT_TRUE(testClient.m_clientNetworkInterface->IsTimeoutEnabled()); + + EXPECT_TRUE(testServer.m_serverNetworkInterface->StopListening()); } #if AZ_TRAIT_DISABLE_FAILED_NETWORKING_TESTS diff --git a/Code/Framework/AzNetworking/Tests/UdpTransport/UdpTransportTests.cpp b/Code/Framework/AzNetworking/Tests/UdpTransport/UdpTransportTests.cpp index c4de3fc6dd..91db3b4549 100644 --- a/Code/Framework/AzNetworking/Tests/UdpTransport/UdpTransportTests.cpp +++ b/Code/Framework/AzNetworking/Tests/UdpTransport/UdpTransportTests.cpp @@ -125,6 +125,18 @@ namespace UnitTest AzNetworking::NetworkingSystemComponent* m_networkingSystemComponent; }; + TEST_F(UdpTransportTests, PacketIdWrap) + { + const uint32_t SEQUENCE_BOUNDARY = 0xFFFF; + UdpPacketTracker tracker; + + for (uint32_t i = 0; i < SEQUENCE_BOUNDARY; ++i) + { + tracker.GetNextPacketId(); + } + EXPECT_EQ(tracker.GetNextPacketId(), PacketId(SEQUENCE_BOUNDARY + 1)); + } + TEST_F(UdpTransportTests, AckReplication) { static const SequenceId TestReliableSequenceId = InvalidSequenceId; @@ -266,6 +278,15 @@ namespace UnitTest EXPECT_EQ(testServer.m_serverNetworkInterface->GetConnectionSet().GetConnectionCount(), 1); EXPECT_EQ(testClient.m_clientNetworkInterface->GetConnectionSet().GetConnectionCount(), 1); + + testClient.m_clientNetworkInterface->SetTimeoutEnabled(true); + EXPECT_TRUE(testClient.m_clientNetworkInterface->IsTimeoutEnabled()); + + EXPECT_FALSE(dynamic_cast(testClient.m_clientNetworkInterface)->IsEncrypted()); + + EXPECT_TRUE(testServer.m_serverNetworkInterface->StopListening()); + EXPECT_FALSE(testServer.m_serverNetworkInterface->StopListening()); + EXPECT_FALSE(dynamic_cast(testServer.m_serverNetworkInterface)->IsOpen()); } TEST_F(UdpTransportTests, TestMultipleClients) diff --git a/Code/Framework/AzNetworking/Tests/Utilities/IpAddressTests.cpp b/Code/Framework/AzNetworking/Tests/Utilities/IpAddressTests.cpp index 435ebb48e0..c143556670 100644 --- a/Code/Framework/AzNetworking/Tests/Utilities/IpAddressTests.cpp +++ b/Code/Framework/AzNetworking/Tests/Utilities/IpAddressTests.cpp @@ -11,4 +11,16 @@ namespace UnitTest { + TEST(IpAddressTests, TestIpQuads) + { + const AzNetworking::IpAddress ip = AzNetworking::IpAddress(127, 0, 0, 1, 12345); + + EXPECT_EQ(ip.GetQuadA(), 127); + EXPECT_EQ(ip.GetQuadB(), 0); + EXPECT_EQ(ip.GetQuadC(), 0); + EXPECT_EQ(ip.GetQuadD(), 1); + + EXPECT_EQ(ip.GetString(), "127.0.0.1:12345"); + EXPECT_EQ(ip.GetIpString(), "127.0.0.1"); + } } diff --git a/Code/Framework/AzNetworking/Tests/Utilities/QuantizedValuesTests.cpp b/Code/Framework/AzNetworking/Tests/Utilities/QuantizedValuesTests.cpp index 13bf9bdb0a..0f0bcac54d 100644 --- a/Code/Framework/AzNetworking/Tests/Utilities/QuantizedValuesTests.cpp +++ b/Code/Framework/AzNetworking/Tests/Utilities/QuantizedValuesTests.cpp @@ -79,13 +79,12 @@ namespace UnitTest template void TestQuantizedValuesHelper01() { - AzNetworking::QuantizedValues testIn, testOut; // Transmits float values between 0 and 1 using NUM_BYTES + AzNetworking::QuantizedValues testIn(ValueFromFloat::Construct(0.0f)), testOut; // Transmits float values between 0 and 1 using NUM_BYTES AZStd::array buffer; AzNetworking::NetworkInputSerializer inputSerializer(buffer.data(), static_cast(buffer.size())); AzNetworking::NetworkOutputSerializer outputSerializer(buffer.data(), static_cast(buffer.size())); - testIn = ValueFromFloat::Construct(0.0f); EXPECT_EQ(static_cast::ValueType>(testIn), ValueFromFloat::Construct(0.0f)); testIn.Serialize(inputSerializer); EXPECT_EQ(inputSerializer.GetSize(), NUM_BYTES * NUM_ELEMENTS); @@ -95,6 +94,8 @@ namespace UnitTest testIn = ValueFromFloat::Construct(1.0f); EXPECT_EQ(static_cast::ValueType>(testIn), ValueFromFloat::Construct(1.0f)); testIn.Serialize(inputSerializer); + EXPECT_NE(testIn, testOut); + EXPECT_NE(testIn.GetQuantizedIntegralValues()[0], testOut.GetQuantizedIntegralValues()[0]); testOut.Serialize(outputSerializer); EXPECT_EQ(testIn, testOut); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/Damping.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/Damping.svg new file mode 100644 index 0000000000..b12640bd59 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/Damping.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/MaxForce.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/MaxForce.svg new file mode 100644 index 0000000000..b1b09104b1 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/MaxForce.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/MaxTorque.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/MaxTorque.svg new file mode 100644 index 0000000000..3c40cb82b2 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/MaxTorque.svg @@ -0,0 +1,15 @@ + + + + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SnapPosition.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SnapPosition.svg new file mode 100644 index 0000000000..a9c5811d7d --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SnapPosition.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SnapRotation.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SnapRotation.svg new file mode 100644 index 0000000000..684c32a08d --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SnapRotation.svg @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/Stiffness.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/Stiffness.svg new file mode 100644 index 0000000000..456785f1bc --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/Stiffness.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SwingLimits.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SwingLimits.svg new file mode 100644 index 0000000000..ec3d1114bb --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/SwingLimits.svg @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/TwistLimits.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/TwistLimits.svg new file mode 100644 index 0000000000..98f56f547d --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/joints/TwistLimits.svg @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc index 7321128b57..16d55f1b8c 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc @@ -390,6 +390,14 @@ img/UI20/toolbar/Y_axis.svg img/UI20/toolbar/Z_axis.svg img/UI20/toolbar/XY2_copy.svg + img/UI20/toolbar/joints/Damping.svg + img/UI20/toolbar/joints/MaxForce.svg + img/UI20/toolbar/joints/MaxTorque.svg + img/UI20/toolbar/joints/SnapPosition.svg + img/UI20/toolbar/joints/SnapRotation.svg + img/UI20/toolbar/joints/Stiffness.svg + img/UI20/toolbar/joints/SwingLimits.svg + img/UI20/toolbar/joints/TwistLimits.svg img/triangle0.png img/triangle0_highlighted.png img/line.png diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Gallery/ReflectedPropertyEditorPage.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Gallery/ReflectedPropertyEditorPage.cpp index 7655a2076c..2be0428d63 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Gallery/ReflectedPropertyEditorPage.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Gallery/ReflectedPropertyEditorPage.cpp @@ -9,6 +9,7 @@ #include "ReflectedPropertyEditorPage.h" #include +#include #include #include #include diff --git a/Code/Framework/AzTest/AzTest/Platform/Common/Unimplemented/Platform_Unimplemented.cpp b/Code/Framework/AzTest/AzTest/Platform/Common/Unimplemented/Platform_Unimplemented.cpp index ea6c6bc5b8..afa75d8333 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Common/Unimplemented/Platform_Unimplemented.cpp +++ b/Code/Framework/AzTest/AzTest/Platform/Common/Unimplemented/Platform_Unimplemented.cpp @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include "Platform.h" +#include #include class ModuleHandle diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorEntityAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorEntityAPI.h index f2808eb0e7..3b5e5c9dc3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorEntityAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorEntityAPI.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index 91a916ae70..717e0c6f8a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp index 5a1e491b01..cc3153ed8e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include namespace AzToolsFramework { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index acf935e6dc..c6772ea2d7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -20,7 +20,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") AZ_POP_DISABLE_WARNING AZ_CVAR( - bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, + bool, ed_useNewAssetBrowserTableView, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Use the new AssetBrowser TableView for searching assets."); namespace AzToolsFramework { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 469a235b9f..ec709aef8a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -138,7 +138,13 @@ namespace AzToolsFramework m_indexMap[row] = index; m_rowMap[index] = row; ++row; - ++m_displayedItemsCounter; + + // We only want to increase the displayed counter if it is a parent (Source) + // so we don't cut children entries. + if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source) + { + ++m_displayedItemsCounter; + } } if (model->hasChildren(index)) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.cpp index 76b634b51e..4ebeb03a71 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.cpp @@ -29,7 +29,7 @@ AZ_PUSH_DISABLE_WARNING(4251 4244, "-Wunknown-warning-option") // disable warnin AZ_POP_DISABLE_WARNING AZ_CVAR( - bool, ed_hideAssetPickerPathColumn, false, nullptr, AZ::ConsoleFunctorFlags::Null, + bool, ed_hideAssetPickerPathColumn, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Hide AssetPicker path column for a clearer view."); AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView); @@ -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, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.h index bab265c134..f8ec6077ce 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.h @@ -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); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.ui b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.ui index b11ffb3990..df7d27d3c8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.ui +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.ui @@ -117,6 +117,9 @@ 0 + + + @@ -142,9 +145,6 @@ - - -
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h index e2929c0d3e..7b94108a91 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h index d765d4e1e5..685770dd20 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h @@ -7,6 +7,7 @@ */ #pragma once +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index 37091c11e2..feab9a9e5a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -20,29 +20,33 @@ AZ_PUSH_DISABLE_WARNING( #include #include #include - +#include #include 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 EntryDelegate(this)) + , m_delegate(new SearchEntryDelegate(this)) { - setSortingEnabled(true); + setSortingEnabled(false); setItemDelegate(m_delegate); setRootIsDecorated(false); //Styling the header aligning text to the left and using a bold font. header()->setDefaultAlignment(Qt::AlignLeft); - header()->setStyleSheet("QHeaderView { font-weight: bold; }"); + header()->setStyleSheet("QHeaderView { font-weight: bold; };"); + setContextMenuPolicy(Qt::CustomContextMenu); setMouseTracking(true); - setSortingEnabled(false); setSelectionMode(QAbstractItemView::SingleSelection); connect(this, &AzQtComponents::TableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); @@ -65,8 +69,12 @@ 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); } void AssetBrowserTableView::SetName(const QString& name) @@ -146,8 +154,17 @@ namespace AzToolsFramework void AssetBrowserTableView::OnAssetBrowserComponentReady() { + UpdateSizeSlot(parentWidget()->width()); } + void AssetBrowserTableView::UpdateSizeSlot(int newWidth) + { + setColumnWidth(0, aznumeric_cast(newWidth * DefaultHeaderResizeProportion)); + header()->setMinimumSectionSize(aznumeric_cast(newWidth * MinHeaderResizeProportion)); + header()->setMaximumSectionSize(aznumeric_cast(newWidth * MaxHeaderResizeProportion)); + } + + void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point) { const auto& selectedAssets = GetSelectedAssets(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index 94e9bdc4e4..b93500ba37 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -26,7 +26,7 @@ namespace AzToolsFramework class AssetBrowserEntry; class AssetBrowserTableModel; class AssetBrowserFilterModel; - class EntryDelegate; + class SearchEntryDelegate; class AssetBrowserTableView //! Table view that displays the asset browser entries in a list. : public AzQtComponents::TableView @@ -59,17 +59,19 @@ 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 &parents = QList(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); - private: QString m_name; - QPointer m_tableModel = nullptr; - QPointer m_sourceFilterModel = nullptr; - EntryDelegate* m_delegate = nullptr; + QPointer m_tableModel; + QPointer m_sourceFilterModel; + SearchEntryDelegate* m_delegate = nullptr; private Q_SLOTS: void OnContextMenu(const QPoint& point); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index 29324c612c..755c59b55b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -11,12 +11,13 @@ #include #include #include - +#include #include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer' needs to have dll-interface to be used by clients of class 'QBrush' // 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning) +#include #include AZ_POP_DISABLE_WARNING @@ -24,8 +25,13 @@ namespace AzToolsFramework { namespace AssetBrowser { - const int ENTRY_SPACING_LEFT_PIXELS = 8; - const int ENTRY_ICON_MARGIN_LEFT_PIXELS = 2; + static constexpr const char* TreeIconPathFirst = "Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg"; + static constexpr const char* TreeIconPathMiddle = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg"; + static constexpr const char* TreeIconPathLast = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg"; + static constexpr const char* TreeIconPathOneChild = "Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg"; + + const int EntrySpacingLeftPixels = 8; + const int EntryIconMarginLeftPixels = 2; EntryDelegate::EntryDelegate(QWidget* parent) : QStyledItemDelegate(parent) @@ -62,7 +68,7 @@ namespace AzToolsFramework // Draw main entry thumbnail. QRect remainingRect(option.rect); - remainingRect.adjust(ENTRY_ICON_MARGIN_LEFT_PIXELS, 0, 0, 0); // bump it rightwards to give some margin to the icon. + remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon. QSize iconSize(m_iconSize, m_iconSize); // Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size @@ -89,7 +95,7 @@ namespace AzToolsFramework } remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail - remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing. + remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing. } QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) @@ -148,7 +154,162 @@ namespace AzToolsFramework return m_iconSize; } - } // namespace Thumbnailer + SearchEntryDelegate::SearchEntryDelegate(QWidget* parent) + : EntryDelegate(parent) + { + LoadBranchPixMaps(); + } + + void SearchEntryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const + { + auto data = index.data(AssetBrowserModel::Roles::EntryRole); + if (data.canConvert()) + { + bool isEnabled = (option.state & QStyle::State_Enabled) != 0; + bool isSelected = (option.state & QStyle::State_Selected) != 0; + + QStyle* style = option.widget ? option.widget->style() : QApplication::style(); + + // draw the background + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget); + + // Draw main entry thumbnail. + QRect remainingRect(option.rect); + + QSize iconSize(m_iconSize, m_iconSize); + // Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size + // so it needs to center vertically with padding in that case: + QPoint iconTopLeft; + QPoint branchIconTopLeft = QPoint(); + + auto entry = qvariant_cast(data); + auto sourceEntry = azrtti_cast(entry); + + //If it is a SourceEntry or it is not the column name we don't want to add space for the branch Icon + if (sourceEntry || index.column() != aznumeric_cast(AssetBrowserEntry::Column::Name)) + { + remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon. + iconTopLeft = QPoint(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + } + else + { + remainingRect.adjust(EntryIconMarginLeftPixels + m_iconSize, 0, 0, 0); // bump it rightwards to give some margin to the icon. + iconTopLeft = QPoint(remainingRect.x() / 2 + m_iconSize, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + branchIconTopLeft = QPoint((remainingRect.x() / 2) - 2, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + } + + QPalette actualPalette(option.palette); + + if (index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)) + { + int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey()); + if (sourceEntry) + { + if (m_showSourceControl) + { + DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey()); + } + // sources with no children should be greyed out. + if (sourceEntry->GetChildCount() == 0) + { + isEnabled = false; // draw in disabled style. + actualPalette.setCurrentColorGroup(QPalette::Disabled); + } + } + else + { + //Get the indexes above and below our entry to see what type are they. + QAbstractItemView* view = qobject_cast(option.styleObject); + const QAbstractItemModel* viewModel = view->model(); + + const QModelIndex indexBelow = viewModel->index(index.row() + 1, index.column()); + const QModelIndex indexAbove = viewModel->index(index.row() - 1, index.column()); + + auto aboveEntry = qvariant_cast(indexBelow.data(AssetBrowserModel::Roles::EntryRole)); + auto belowEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole)); + + auto aboveSourceEntry = azrtti_cast(aboveEntry); + auto belowSourceEntry = azrtti_cast(belowEntry); + + // if current index is the last entry in the view + // or the index above it is a Source Entry and + // the index below is invalid or is valid but it is also a source entry + // then the current index is the only child. + if (index.row() == viewModel->rowCount() - 1 || + (indexBelow.isValid() && aboveSourceEntry && + (!indexAbove.isValid() || (indexAbove.isValid() && belowSourceEntry)))) + { + DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); // Draw One Child Icon + } + else if (indexBelow.isValid() && aboveSourceEntry) // The index above is a source entry + { + DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); // Draw First child Icon + } + else if (indexAbove.isValid() && belowSourceEntry) // The index below is a source entry + { + DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw Last Child Icon + } + else //the index above and below are also child entries + { + DrawBranchPixMap(EntryBranchType::Middle, painter, branchIconTopLeft, iconSize); // Draw Default child Icon. + } + } + + remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail + remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing. + } + QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) + ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) + : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path))); + + style->drawItemText( + painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, displayString, + isSelected ? QPalette::HighlightedText : QPalette::Text); + } + } + + void SearchEntryDelegate::LoadBranchPixMaps() + { + AZ::IO::BasicPath absoluteIconPath; + for (int branchType = EntryBranchType::First; branchType != EntryBranchType::Count; ++branchType) + { + QPixmap pixmap; + switch (branchType) + { + case AzToolsFramework::AssetBrowser::EntryBranchType::First: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathFirst; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::Middle: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathMiddle; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::Last: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathLast; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::OneChild: + default: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild; + break; + } + bool pixmapLoadedSuccess = pixmap.load(absoluteIconPath.c_str()); + AZ_Assert(pixmapLoadedSuccess, "Error loading Branch Icons in SearchEntryDelegate"); + + m_branchIcons[static_cast(branchType)] = pixmap; + + } + } + + void SearchEntryDelegate::DrawBranchPixMap( + EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const + { + const QPixmap& pixmap = m_branchIcons[branchType]; + + pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + const QSize sizeDelta = size - pixmap.size(); + const QPoint pointDelta = QPoint(sizeDelta.width() / 2, sizeDelta.height() / 2); + painter->drawPixmap(point + pointDelta, pixmap); + } + + } // namespace AssetBrowser } // namespace AzToolsFramework #include "AssetBrowser/Views/moc_EntryDelegate.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h index 643bedec7c..ac68c19248 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h @@ -27,9 +27,19 @@ namespace AzToolsFramework { namespace AssetBrowser { + //! Type of branch icon the delegate should paint. + enum EntryBranchType + { + First, + Middle, + Last, + OneChild, + Count + }; + class AssetBrowserFilterModel; - //! EntryDelegate draws a single item in AssetBrowser + //! EntryDelegate draws a single item in AssetBrowser. class EntryDelegate : public QStyledItemDelegate { @@ -52,5 +62,23 @@ namespace AzToolsFramework //! Draw a thumbnail and return its width int DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const; }; + + //! SearchEntryDelegate draws a single item in AssetBrowserTableView. + class SearchEntryDelegate + : public EntryDelegate + { + Q_OBJECT + public: + explicit SearchEntryDelegate(QWidget* parent = nullptr); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + private: + void LoadBranchPixMaps(); + void DrawBranchPixMap(EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const; + + private: + QMap m_branchIcons; + }; } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBundle/AssetBundleComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBundle/AssetBundleComponent.cpp index ee3669a7ba..7083737cf7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBundle/AssetBundleComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBundle/AssetBundleComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorBus.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorBus.cpp new file mode 100644 index 0000000000..43c950cd00 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorBus.cpp @@ -0,0 +1,24 @@ +/* + * 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 "AssetEditorBus.h" +#include +#include + +namespace AzToolsFramework::AssetEditor +{ + void AssetEditorWindowSettings::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Field("m_openAssets", &AssetEditorWindowSettings::m_openAssets) + ; + } + } +} // namespace AzToolsFramework::AssetEditor diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorBus.h index 4d14e5c1bf..9b12ea27d4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorBus.h @@ -8,12 +8,14 @@ #pragma once #include +#include #include -#include -#include -#include +#include -namespace AZ { namespace Data { class AssetData; } } +namespace AZ::Data +{ + class AssetData; +} namespace AZStd { @@ -45,15 +47,7 @@ namespace AzToolsFramework static constexpr const char* s_name = "AssetEditorWindowSettings"; - static void Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Field("m_openAssets", &AssetEditorWindowSettings::m_openAssets) - ; - } - } + static void Reflect(AZ::ReflectContext* context); }; // External interaction with Asset Editor diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp index 95dc0f36c4..e09dd183f8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp @@ -22,6 +22,7 @@ AZ_POP_DISABLE_WARNING #include #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp index 0259142135..4d849466ee 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp @@ -8,8 +8,9 @@ #include "ComponentModeCollection.h" -#include #include +#include +#include namespace AzToolsFramework { @@ -17,7 +18,7 @@ namespace AzToolsFramework { AZ_CLASS_ALLOCATOR_IMPL(ComponentModeCollection, AZ::SystemAllocator, 0) - static const char* const s_nextActiveComponentModeTitle = "Edit Next"; + static const char* const s_nextActiveComponentModeTitle = "Edit Next"; static const char* const s_previousActiveComponentModeTitle = "Edit Previous"; static const char* const s_nextActiveComponentModeDesc = "Move to the next component"; static const char* const s_prevActiveComponentModeDesc = "Move to the previous component"; @@ -119,6 +120,11 @@ namespace AzToolsFramework } }; + ComponentModeCollection::ComponentModeCollection(ViewportEditorModeTrackerInterface* viewportEditorModeTracker) + : m_viewportEditorModeTracker(viewportEditorModeTracker) + { + } + void ComponentModeCollection::AddComponentMode( const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid componentType, const ComponentModeFactoryFunction& componentModeBuilder) @@ -209,6 +215,11 @@ namespace AzToolsFramework GetEntityContextId(), &EditorComponentModeNotifications::EnteredComponentMode, m_activeComponentTypes); + // this call to activate the component mode editor state should eventually replace the bus call in + // ComponentModeCollection::BeginComponentMode() to EditorComponentModeNotifications::EnteredComponentMode + // such that all of the notifications for activating/deactivating the different editor modes are in a central location + m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Component); + // enable actions for the first/primary ComponentMode // note: if multiple ComponentModes are activated at the same time, actions // are not available together, the 'active' mode will bind its actions one at a time @@ -282,6 +293,10 @@ namespace AzToolsFramework &EditorComponentModeNotifications::LeftComponentMode, m_activeComponentTypes); + // this call to deactivate the component mode editor state should eventually replace the bus call in + // ComponentModeCollection::EndComponentMode() to EditorComponentModeNotifications::LeftComponentMode + // such that all of the notifications for activating/deactivating the different editor modes are in a central location + m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Component); // clear stored modes and builders for this ComponentMode // TLDR: avoid 'use after free' error diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.h index 7dd97dc0c9..9e299d2323 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.h @@ -15,6 +15,7 @@ namespace AzToolsFramework { class EditorMetricsEventsBusTraits; + class ViewportEditorModeTrackerInterface; namespace ComponentModeFramework { @@ -25,7 +26,7 @@ namespace AzToolsFramework AZ_CLASS_ALLOCATOR_DECL /// @cond - ComponentModeCollection() = default; + explicit ComponentModeCollection(ViewportEditorModeTrackerInterface* viewportEditorModeTracker); ~ComponentModeCollection() = default; ComponentModeCollection(const ComponentModeCollection&) = delete; ComponentModeCollection& operator=(const ComponentModeCollection&) = delete; @@ -101,6 +102,7 @@ namespace AzToolsFramework size_t m_selectedComponentModeIndex = 0; ///< Index into the array of active ComponentModes, current index is 'selected' ComponentMode. bool m_adding = false; ///< Are we currently adding individual ComponentModes to the Editor wide ComponentMode. bool m_componentMode = false; ///< Editor (global) ComponentMode flag - is ComponentMode active or not. + ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes. }; } // namespace ComponentModeFramework } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp index 6ed133c830..7789070209 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp index 8d96cc42fe..ce1bf84db0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp @@ -381,12 +381,22 @@ namespace AzToolsFramework return; } - //orphan any children that remain attached to the entity - auto children = entityInfo.GetChildren(); - for (auto childId : children) - { - ReparentChild(childId, AZ::EntityId(), entityId); - m_entityOrphanTable[entityId].insert(childId); + 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) + { + 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()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h index feb2fc12bf..3bd6e656ed 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h @@ -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; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index 0f2b896b40..e5daf2674d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -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( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h index a98fce8059..a172e9550c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h @@ -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; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index 6d3a7599de..75e3bab60f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -8,9 +8,12 @@ #pragma once +#include #include #include +#include + namespace AzToolsFramework { //! FocusModeInterface @@ -26,14 +29,14 @@ namespace AzToolsFramework virtual void SetFocusRoot(AZ::EntityId entityId) = 0; //! Clears the Editor focus, allowing the user to select the whole level again. - virtual void ClearFocusRoot() = 0; + virtual void ClearFocusRoot(AzFramework::EntityContextId entityContextId) = 0; //! Returns the entity id of the root of the current Editor focus. //! @return The entity id of the root of the Editor focus, or an invalid entity id if no focus is set. - virtual AZ::EntityId GetFocusRoot() = 0; + virtual AZ::EntityId GetFocusRoot(AzFramework::EntityContextId entityContextId) = 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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h new file mode 100644 index 0000000000..ab8629ac85 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h @@ -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 +#include + +#include + +namespace AzToolsFramework +{ + //! Used to notify when the editor focus changes. + class FocusModeNotifications + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = AzFramework::EntityContextId; + ////////////////////////////////////////////////////////////////////////// + + //! Triggered when the editor focus is changed to a different entity. + //! @param entityId The entity the focus has been moved to. + virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0; + + protected: + ~FocusModeNotifications() = default; + }; + + using FocusModeNotificationBus = AZ::EBus; + +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index e08191032d..d1c4d37821 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace AzToolsFramework @@ -64,22 +65,28 @@ namespace AzToolsFramework void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId) { + if (m_focusRoot == entityId) + { + return; + } + m_focusRoot = entityId; + FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot); // TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode } - void FocusModeSystemComponent::ClearFocusRoot() + void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId) { SetFocusRoot(AZ::EntityId()); } - AZ::EntityId FocusModeSystemComponent::GetFocusRoot() + AZ::EntityId FocusModeSystemComponent::GetFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId) { return m_focusRoot; } - bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) + bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) const { if (m_focusRoot == AZ::EntityId()) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index 697a4ff998..dabaa6aaf4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -40,9 +40,9 @@ namespace AzToolsFramework // FocusModeInterface overrides ... void SetFocusRoot(AZ::EntityId entityId) override; - void ClearFocusRoot() override; - AZ::EntityId GetFocusRoot() override; - bool IsInFocusSubTree(AZ::EntityId entityId) override; + void ClearFocusRoot(AzFramework::EntityContextId entityContextId) override; + AZ::EntityId GetFocusRoot(AzFramework::EntityContextId entityContextId) override; + bool IsInFocusSubTree(AZ::EntityId entityId) const override; private: AZ::EntityId m_focusRoot; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h index a2b004763b..06e9c82e55 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp index 1867f346ec..d8c43ace9e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp @@ -71,6 +71,7 @@ namespace AzToolsFramework } m_uniformScaleManipulator->SetVisualOrientationOverride(QuaternionFromTransformNoScaling(localTransform)); + m_uniformScaleManipulator->SetLocalPosition(localTransform.GetTranslation()); m_uniformScaleManipulator->SetLocalOrientation(AZ::Quaternion::CreateIdentity()); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp index 049cab4a13..0efe310250 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp @@ -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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.cpp index 7aa5967a44..d95a704e84 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.h index 75b68c859d..8873787bd3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.h @@ -8,7 +8,6 @@ #pragma once #include -#include namespace AzToolsFramework { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 4fd202a73d..4e58bbdf05 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -8,9 +8,11 @@ #include +#include #include #include #include +#include namespace AzToolsFramework::Prefab { @@ -23,19 +25,21 @@ namespace AzToolsFramework::Prefab "Instance Entity Mapper Interface could not be found. " "Check that it is being correctly initialized."); + EditorEntityContextNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); } PrefabFocusHandler::~PrefabFocusHandler() { AZ::Interface::Unregister(this); + EditorEntityContextNotificationBus::Handler::BusDisconnect(); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) { InstanceOptionalReference focusedInstance; - if (entityId == AZ::EntityId()) + if (!entityId.IsValid()) { PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); @@ -53,35 +57,76 @@ namespace AzToolsFramework::Prefab focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); } - if (!focusedInstance.has_value()) + return FocusOnPrefabInstance(focusedInstance); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) + { + if (index < 0 || index >= m_instanceFocusVector.size()) { - return AZ::Failure(AZStd::string( - "Prefab Focus Handler: Couldn't find owning instance of entityId provided.")); + return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); } - m_focusedInstance = focusedInstance; - m_focusedTemplateId = focusedInstance->get().GetTemplateId(); + InstanceOptionalReference focusedInstance = m_instanceFocusVector[index]; + + return FocusOnPrefabInstance(focusedInstance); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance) + { + if (!focusedInstance.has_value()) + { + return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on.")); + } - FocusModeInterface* focusModeInterface = AZ::Interface::Get(); - if (focusModeInterface) + if (!m_focusedInstance.has_value() || &m_focusedInstance->get() != &focusedInstance->get()) { - focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + m_focusedInstance = focusedInstance; + m_focusedTemplateId = focusedInstance->get().GetTemplateId(); + + AZ::EntityId containerEntityId; + + if (focusedInstance->get().GetParentInstance() != AZStd::nullopt) + { + containerEntityId = focusedInstance->get().GetContainerEntityId(); + + // Select the container entity + AzToolsFramework::SelectEntity(containerEntityId); + } + else + { + containerEntityId = AZ::EntityId(); + + // Clear the selection + AzToolsFramework::SelectEntities({}); + + } + + // Focus on the descendants of the container entity + if (FocusModeInterface* focusModeInterface = AZ::Interface::Get()) + { + focusModeInterface->SetFocusRoot(containerEntityId); + } + + RefreshInstanceFocusList(); + PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); } return AZ::Success(); } - TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() + TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_focusedTemplateId; } - InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance() + InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance( + [[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_focusedInstance; } - bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) + bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const { if (!m_focusedInstance.has_value()) { @@ -89,7 +134,7 @@ namespace AzToolsFramework::Prefab return false; } - if (entityId == AZ::EntityId()) + if (!entityId.IsValid()) { return false; } @@ -99,4 +144,44 @@ namespace AzToolsFramework::Prefab return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); } + const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath([[maybe_unused]] AzFramework::EntityContextId entityContextId) const + { + return m_instanceFocusPath; + } + + const int PrefabFocusHandler::GetPrefabFocusPathLength([[maybe_unused]] AzFramework::EntityContextId entityContextId) const + { + return aznumeric_cast(m_instanceFocusVector.size()); + } + + void PrefabFocusHandler::OnEntityStreamLoadSuccess() + { + // Focus on the root prefab (AZ::EntityId() will default to it) + FocusOnOwningPrefab(AZ::EntityId()); + } + + void PrefabFocusHandler::RefreshInstanceFocusList() + { + m_instanceFocusVector.clear(); + m_instanceFocusPath.clear(); + + AZStd::list instanceFocusList; + + // Use a support list to easily push front while traversing the prefab hierarchy + InstanceOptionalReference currentInstance = m_focusedInstance; + while (currentInstance.has_value()) + { + instanceFocusList.push_front(currentInstance); + + currentInstance = currentInstance->get().GetParentInstance(); + } + + // Populate internals using the support list + for (auto& instance : instanceFocusList) + { + m_instanceFocusPath.Append(instance->get().GetContainerEntity()->get().GetName()); + m_instanceFocusVector.emplace_back(instance); + } + } + } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index fa7727894d..2ccec36882 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -21,6 +22,7 @@ namespace AzToolsFramework::Prefab //! Handles Prefab Focus mode, determining which prefab file entity changes will target. class PrefabFocusHandler final : private PrefabFocusInterface + , private EditorEntityContextNotificationBus::Handler { public: AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0); @@ -28,15 +30,26 @@ namespace AzToolsFramework::Prefab PrefabFocusHandler(); ~PrefabFocusHandler(); - // PrefabFocusInterface override ... + // PrefabFocusInterface overrides ... PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; - TemplateId GetFocusedPrefabTemplateId() override; - InstanceOptionalReference GetFocusedPrefabInstance() override; - bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override; + PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override; + TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override; + InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override; + bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override; + const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override; + const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override; + + // EditorEntityContextNotificationBus overrides ... + void OnEntityStreamLoadSuccess() override; private: + PrefabFocusOperationResult FocusOnPrefabInstance(InstanceOptionalReference focusedInstance); + void RefreshInstanceFocusList(); + InstanceOptionalReference m_focusedInstance; TemplateId m_focusedTemplateId; + AZStd::vector m_instanceFocusVector; + AZ::IO::Path m_instanceFocusPath; InstanceEntityMapperInterface* m_instanceEntityMapperInterface; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 833a5ef7c8..1c0f4f85e9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -11,6 +11,8 @@ #include #include +#include + #include #include @@ -28,16 +30,27 @@ namespace AzToolsFramework::Prefab //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; + //! Set the focused prefab instance to the instance at position index of the current path. + //! @param index The index of the instance in the current path that we want the prefab system to focus on. + virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0; + //! Returns the template id of the instance the prefab system is focusing on. - virtual TemplateId GetFocusedPrefabTemplateId() = 0; + virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0; //! Returns a reference to the instance the prefab system is focusing on. - virtual InstanceOptionalReference GetFocusedPrefabInstance() = 0; + virtual InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const = 0; //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. //! @param entityId The entityId of the queried entity. //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. - virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0; + virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0; + + //! Returns the path from the root instance to the currently focused instance. + //! @return A path composed from the names of the container entities for the instance path. + virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0; + + //! Returns the size of the path to the currently focused instance. + virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h new file mode 100644 index 0000000000..31bab719e2 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h @@ -0,0 +1,37 @@ +/* + * 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 +#include + +namespace AzToolsFramework::Prefab +{ + //! Used to notify when the editor focus changes. + class PrefabFocusNotifications + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = AzFramework::EntityContextId; + ////////////////////////////////////////////////////////////////////////// + + //! Triggered when the editor focus is changed to a different prefab. + virtual void OnPrefabFocusChanged() = 0; + + protected: + ~PrefabFocusNotifications() = default; + }; + + using PrefabFocusNotificationBus = AZ::EBus; + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 63f042be85..41538d54e8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -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; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp index ad171dc6f4..0aaf81c4c9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp @@ -10,6 +10,8 @@ #include +#include + namespace AzToolsFramework { namespace Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 0d2696f14b..6ef2d756fb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -818,7 +818,14 @@ namespace AzToolsFramework auto linkIterator = m_linkIdMap.find(linkId); if (linkIterator != m_linkIdMap.end()) { - return AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId()); + if (AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId())) + { + return true; + } + else + { + continue; + } } } return false; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp index 8e0d7fcd42..ac3ada7557 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/PropertyTreeEditor/PropertyTreeEditor.h b/Code/Framework/AzToolsFramework/AzToolsFramework/PropertyTreeEditor/PropertyTreeEditor.h index 7e416c3cc0..648a39fe76 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/PropertyTreeEditor/PropertyTreeEditor.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/PropertyTreeEditor/PropertyTreeEditor.h @@ -12,6 +12,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/SourceControlAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/SourceControlAPI.h index df9b136752..5eb4a31ffb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/SourceControlAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/SourceControlAPI.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp index cca0071a4d..f58f273f11 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp @@ -7,6 +7,7 @@ */ #include "EditorLayerComponent.h" #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ScriptEditorComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ScriptEditorComponent.cpp index cc350c8107..be29af5c0f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ScriptEditorComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ScriptEditorComponent.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutliner.qss b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutliner.qss index 15ff8b1b67..b3c5882334 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutliner.qss +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutliner.qss @@ -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; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index d9f3ff8bb8..2872c1bce3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,8 @@ namespace AzToolsFramework , m_entityExpansionState() , m_entityFilteredState() { + m_focusModeInterface = AZ::Interface::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::Get(); - - AZ_Assert(m_editorEntityFrameworkInterface != nullptr, - "EntityOutlinerListModel requires a EditorEntityFrameworkInterface instance on Initialize."); + m_editorEntityUiInterface = AZ::Interface::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()) { 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; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx index 25e4276dd4..4b13aa6d27 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx @@ -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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index 0549c600d3..4364ae1efc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -38,10 +38,21 @@ namespace AzToolsFramework AZ_Assert((m_editorEntityFrameworkInterface != nullptr), "EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction."); + + + AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + + FocusModeNotificationBus::Handler::BusConnect(editorEntityContextId); + + viewport()->setMouseTracking(true); } EntityOutlinerTreeView::~EntityOutlinerTreeView() { + FocusModeNotificationBus::Handler::BusDisconnect(); + ClearQueuedMouseEvent(); } @@ -59,6 +70,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 +128,8 @@ namespace AzToolsFramework setSelectionMode(selectionModeBefore); } + m_mousePosition = event->pos(); + //process mouse movement as normal, potentially triggering drag and drop QTreeView::mouseMoveEvent(event); } @@ -172,12 +190,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 @@ -261,6 +312,11 @@ namespace AzToolsFramework StyledTreeView::StartCustomDrag(indexListSorted, supportedActions); } + + void EntityOutlinerTreeView::OnEditorFocusChanged([[maybe_unused]] AZ::EntityId entityId) + { + viewport()->repaint(); + } } #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx index 66cd082407..2ddbaaafa9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx @@ -15,6 +15,7 @@ #include #include +#include #include #endif @@ -35,6 +36,7 @@ namespace AzToolsFramework //! of other entities. If the selection updates instantly, this would never be possible. class EntityOutlinerTreeView : public AzQtComponents::StyledTreeView + , private FocusModeNotificationBus::Handler { Q_OBJECT; public: @@ -59,6 +61,10 @@ namespace AzToolsFramework void startDrag(Qt::DropActions supportedActions) override; void dragMoveEvent(QDragMoveEvent* event) override; void dropEvent(QDropEvent* event) override; + void leaveEvent(QEvent* event) override; + + // FocusModeNotificationBus overrides ... + void OnEditorFocusChanged(AZ::EntityId entityId) 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 +78,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; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index 4db235d073..689e5b5dc4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -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); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp new file mode 100644 index 0000000000..6b0de5dc53 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -0,0 +1,71 @@ +/* + * 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 + +#include + +namespace AzToolsFramework::Prefab +{ + PrefabViewportFocusPathHandler::PrefabViewportFocusPathHandler() + { + // Get default EntityContextId + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + + // Connect to Prefab Focus Notifications + PrefabFocusNotificationBus::Handler::BusConnect(m_editorEntityContextId); + } + + PrefabViewportFocusPathHandler::~PrefabViewportFocusPathHandler() + { + // Disconnect from Prefab Focus Notifications + PrefabFocusNotificationBus::Handler::BusDisconnect(); + } + + void PrefabViewportFocusPathHandler::Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton) + { + // Get reference to the PrefabFocusInterface handler + m_prefabFocusInterface = AZ::Interface::Get(); + if (m_prefabFocusInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabViewportFocusPathHandler construction."); + return; + } + + // Initialize Widgets + m_breadcrumbsWidget = breadcrumbsWidget; + m_backButton = backButton; + + // If a part of the path is clicked, focus on that instance + connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this, + [&](const QString&, int linkIndex) + { + m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex); + } + ); + + // The back button will allow user to go one level up + connect(m_backButton, &QToolButton::clicked, this, + [&]() + { + if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1) + { + m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2); + } + } + ); + } + + void PrefabViewportFocusPathHandler::OnPrefabFocusChanged() + { + // Push new Path + m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str()); + } + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h new file mode 100644 index 0000000000..ce7744fb1b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h @@ -0,0 +1,45 @@ +/* + * 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 + +#include + +#include + +#include +#include + +namespace AzToolsFramework::Prefab +{ + class PrefabFocusInterface; + + class PrefabViewportFocusPathHandler + : public PrefabFocusNotificationBus::Handler + , private QObject + { + public: + PrefabViewportFocusPathHandler(); + ~PrefabViewportFocusPathHandler(); + + void Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton); + + // PrefabFocusNotificationBus overrides ... + void OnPrefabFocusChanged() override; + + private: + AzQtComponents::BreadCrumbs* m_breadcrumbsWidget = nullptr; + QToolButton* m_backButton = nullptr; + + AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + + PrefabFocusInterface* m_prefabFocusInterface = nullptr; + }; +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index c07191fda2..ba84e662c1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -33,6 +33,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx index 33619f9f62..cc4aff5649 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx @@ -16,6 +16,7 @@ #include #include "PropertyEditorAPI.h" #include +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEntityIdCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEntityIdCtrl.cpp index f98638fd69..7a9a5a4d7a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEntityIdCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEntityIdCtrl.cpp @@ -119,11 +119,12 @@ namespace AzToolsFramework // replace the default input handler with one specific for dealing with // entity selection in the viewport + EditorInteractionSystemViewportSelectionRequestBus::Event( GetEntityContextId(), &EditorInteractionSystemViewportSelection::SetHandler, - [](const EditorVisibleEntityDataCache* entityDataCache) + [](const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker) { - return AZStd::make_unique(entityDataCache); + return AZStd::make_unique(entityDataCache, viewportEditorModeTracker); }); if (!pickModeEntityContextId.IsNull()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h index b3a660d0f2..c4aefecce5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -166,11 +167,12 @@ namespace UnitTest m_editorActions.Connect(); const auto viewportHandlerBuilder = - [this](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache) + [this](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache, + [[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker) { // create the default viewport (handles ComponentMode) AZStd::unique_ptr defaultSelection = - AZStd::make_unique(entityDataCache); + AZStd::make_unique(entityDataCache, viewportEditorModeTracker); // override the phantom widget so we can use out custom test widget defaultSelection->SetOverridePhantomWidget(&m_editorActions.m_componentModeWidget); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp index 7903668409..30958cfbc1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp @@ -9,6 +9,7 @@ #include "EditorDefaultSelection.h" #include +#include #include #include #include @@ -19,21 +20,26 @@ namespace AzToolsFramework { AZ_CLASS_ALLOCATOR_IMPL(EditorDefaultSelection, AZ::SystemAllocator, 0) - EditorDefaultSelection::EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache) + EditorDefaultSelection::EditorDefaultSelection( + const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker) : m_phantomWidget(nullptr) , m_entityDataCache(entityDataCache) + , m_viewportEditorModeTracker(viewportEditorModeTracker) + , m_componentModeCollection(viewportEditorModeTracker) { ActionOverrideRequestBus::Handler::BusConnect(GetEntityContextId()); ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusConnect(); m_manipulatorManager = AZStd::make_shared(AzToolsFramework::g_mainManipulatorManagerId); m_transformComponentSelection = AZStd::make_unique(entityDataCache); + m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Default); } EditorDefaultSelection::~EditorDefaultSelection() { ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusDisconnect(); ActionOverrideRequestBus::Handler::BusDisconnect(); + m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Default); } void EditorDefaultSelection::SetOverridePhantomWidget(QWidget* phantomOverrideWidget) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.h index 5763bf227c..e4c794e99e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.h @@ -15,6 +15,8 @@ namespace AzToolsFramework { + class ViewportEditorModeTrackerInterface; + //! The default selection/input handler for the editor (includes handling ComponentMode). class EditorDefaultSelection : public ViewportInteraction::InternalViewportSelectionRequests @@ -25,7 +27,7 @@ namespace AzToolsFramework AZ_CLASS_ALLOCATOR_DECL //! @cond - explicit EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache); + EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker); EditorDefaultSelection(const EditorDefaultSelection&) = delete; EditorDefaultSelection& operator=(const EditorDefaultSelection&) = delete; virtual ~EditorDefaultSelection(); @@ -110,5 +112,7 @@ namespace AzToolsFramework AZStd::shared_ptr m_manipulatorManager; //!< The default manipulator manager. ViewportInteraction::MouseInteraction m_currentInteraction; //!< Current mouse interaction to be used for drawing manipulators. + ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes. + }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 25b10463dc..57e6c9ff7a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,17 @@ namespace AzToolsFramework } } + EditorHelpers::EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache) + : m_entityDataCache(entityDataCache) + { + m_focusModeInterface = AZ::Interface::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; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h index 0dbb1d0f6c..a6a78a4e61 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h @@ -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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp index 7002436d13..5d03231aab 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp @@ -10,9 +10,24 @@ #include #include +#include namespace AzToolsFramework { + EditorInteractionSystemComponent::EditorInteractionSystemComponent() + : m_viewportEditorMode(AZStd::make_unique()) + { + AZ_Assert(AZ::Interface::Get() == nullptr, "Unexpected registration of viewport editor mode tracker.") + AZ::Interface::Register(m_viewportEditorMode.get()); + } + + EditorInteractionSystemComponent::~EditorInteractionSystemComponent() + { + m_interactionRequests.reset(); + AZ_Assert(AZ::Interface::Get() != nullptr, "Unexpected unregistration of viewport editor mode tracker.") + AZ::Interface::Unregister(m_viewportEditorMode.get()); + } + void EditorInteractionSystemComponent::Activate() { EditorInteractionSystemViewportSelectionRequestBus::Handler::BusConnect(GetEntityContextId()); @@ -41,7 +56,8 @@ namespace AzToolsFramework return m_interactionRequests->InternalHandleMouseManipulatorInteraction(mouseInteraction); } - void EditorInteractionSystemComponent::SetHandler(const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder) + void EditorInteractionSystemComponent::SetHandler( + const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder) { // when setting a handler, make sure we're connected to the ViewportDebugDisplayEventBus so we // can forward calls to the specific type implementing ViewportSelectionRequests @@ -59,7 +75,7 @@ namespace AzToolsFramework m_entityDataCache = AZStd::make_unique(); m_interactionRequests.reset(); // BusConnect/Disconnect in constructor/destructor, // so have to reset before assigning the new one - m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get()); + m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get(), m_viewportEditorMode.get()); } EditorInteractionSystemViewportSelectionRequestBus::Handler::BusConnect(GetEntityContextId()); @@ -68,9 +84,9 @@ namespace AzToolsFramework void EditorInteractionSystemComponent::SetDefaultHandler() { SetHandler( - [](const EditorVisibleEntityDataCache* entityDataCache) + [](const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker) { - return AZStd::make_unique(entityDataCache); + return AZStd::make_unique(entityDataCache, viewportEditorModeTracker); }); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h index 17521eab14..856fd2e326 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h @@ -14,6 +14,8 @@ namespace AzToolsFramework { + class ViewportEditorModeTracker; + //! System Component to wrap active input handler. //! EditorInteractionSystemComponent is notified of viewport mouse events from RenderViewport //! and forwards them to a concrete implementation of ViewportSelectionRequests. @@ -26,6 +28,9 @@ namespace AzToolsFramework public: AZ_COMPONENT(EditorInteractionSystemComponent, "{146D0317-AF42-45AB-A953-F54198525DD5}") + EditorInteractionSystemComponent(); + ~EditorInteractionSystemComponent(); + static void Reflect(AZ::ReflectContext* context); // EditorInteractionSystemViewportSelectionRequestBus @@ -54,5 +59,7 @@ namespace AzToolsFramework AZStd::unique_ptr m_interactionRequests; //!< Hold a concrete implementation of //!< ViewportSelectionRequests to handle viewport //!< input and drawing for the Editor. + + AZStd::unique_ptr m_viewportEditorMode; //!< Editor mode tracker for each viewport. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h index ee3f83f74d..3579460ca0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h @@ -17,6 +17,7 @@ namespace AzToolsFramework { class EditorVisibleEntityDataCache; + class ViewportEditorModeTrackerInterface; //! Bus to handle all mouse events originating from the viewport. //! Coordinated by the EditorInteractionSystemComponent @@ -32,8 +33,8 @@ namespace AzToolsFramework }; //! Alias for factory function to create a new type implementing the ViewportSelectionRequests interface. - using ViewportSelectionRequestsBuilderFn = - AZStd::function(const EditorVisibleEntityDataCache*)>; + using ViewportSelectionRequestsBuilderFn = AZStd::function( + const EditorVisibleEntityDataCache*, ViewportEditorModeTrackerInterface*)>; //! Interface for system component implementing the ViewportSelectionRequests interface. //! This interface also includes a setter to set a custom handler also implementing diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.cpp index ea1bc73056..18eda140a0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.cpp @@ -8,6 +8,7 @@ #include "EditorPickEntitySelection.h" +#include #include #include @@ -15,9 +16,12 @@ namespace AzToolsFramework { AZ_CLASS_ALLOCATOR_IMPL(EditorPickEntitySelection, AZ::SystemAllocator, 0) - EditorPickEntitySelection::EditorPickEntitySelection(const EditorVisibleEntityDataCache* entityDataCache) + EditorPickEntitySelection::EditorPickEntitySelection( + const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker) : m_editorHelpers(AZStd::make_unique(entityDataCache)) + , m_viewportEditorModeTracker(viewportEditorModeTracker) { + m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Pick); } EditorPickEntitySelection::~EditorPickEntitySelection() @@ -26,6 +30,8 @@ namespace AzToolsFramework { ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, m_hoveredEntityId, false); } + + m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Pick); } // note: entityIdUnderCursor is the authoritative entityId we get each frame by querying diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h index e8d83af932..62fa4161b7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h @@ -13,6 +13,8 @@ namespace AzToolsFramework { + class ViewportEditorModeTrackerInterface; + //! Viewport interaction that will handle assigning an entity in the viewport to //! an entity field in the entity inspector. class EditorPickEntitySelection : public ViewportInteraction::InternalViewportSelectionRequests @@ -20,7 +22,8 @@ namespace AzToolsFramework public: AZ_CLASS_ALLOCATOR_DECL - EditorPickEntitySelection(const EditorVisibleEntityDataCache* entityDataCache); + EditorPickEntitySelection( + const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker); ~EditorPickEntitySelection(); private: @@ -32,5 +35,6 @@ namespace AzToolsFramework AZStd::unique_ptr m_editorHelpers; //!< Editor visualization of entities (icons, shapes, debug visuals etc). AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any). AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display. + ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 8157d369d2..ccb72e1d50 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -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 @@ -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 = AZStd::make_unique( 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 = AZStd::make_unique(AZ::Transform::CreateIdentity()); - rotationManipulators->SetCircleBoundWidth(ManipulatorCicleBoundWidth(ViewportUi::DefaultViewportId)); + rotationManipulators->SetCircleBoundWidth(ManipulatorCicleBoundWidth()); InitializeManipulators(*rotationManipulators); @@ -1415,7 +1404,7 @@ namespace AzToolsFramework AZStd::shared_ptr sharedRotationState = AZStd::make_shared(); 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: - { - 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: + case Influence::Group: { 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 = AZStd::make_unique(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()); + struct SharedScaleState + { + AZ::Vector3 m_savedScaleOffset = AZ::Vector3::CreateZero(); + EntityIdList m_entityIds; + }; + // lambdas capture shared_ptr by value to increment ref count - auto manipulatorEntityIds = AZStd::make_shared(); + auto sharedScaleState = AZStd::make_shared(); - auto uniformLeftMouseDownCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) + 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; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 6cc89b2d10..a0e87d9d6f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -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. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.cpp index 4adddb02e1..105712c789 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.cpp @@ -45,22 +45,6 @@ namespace AzToolsFramework return m_editorModes[static_cast(mode)]; } - void ViewportEditorModeTracker::RegisterInterface() - { - if (AZ::Interface::Get() == nullptr) - { - AZ::Interface::Register(this); - } - } - - void ViewportEditorModeTracker::UnregisterInterface() - { - if (AZ::Interface::Get() != nullptr) - { - AZ::Interface::Unregister(this); - } - } - AZ::Outcome ViewportEditorModeTracker::ActivateMode( const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h index 6ae68b39b2..5b382c44e7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h @@ -41,12 +41,6 @@ namespace AzToolsFramework : public ViewportEditorModeTrackerInterface { public: - //! Registers this object with the AZ::Interface. - void RegisterInterface(); - - //! Unregisters this object with the AZ::Interface. - void UnregisterInterface(); - // ViewportEditorModeTrackerInterface overrides ... AZ::Outcome ActivateMode(const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) override; AZ::Outcome DeactivateMode(const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ButtonGroup.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ButtonGroup.cpp index e933940d95..372587c110 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ButtonGroup.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ButtonGroup.cpp @@ -29,13 +29,29 @@ namespace AzToolsFramework::ViewportUi::Internal void ButtonGroup::SetHighlightedButton(ButtonId buttonId) { + if (buttonId == m_highlightedButtonId) // the requested button is highlighted, so do nothing. + { + return; + } + if (auto buttonEntry = m_buttons.find(buttonId); buttonEntry != m_buttons.end()) { - for (auto& button : m_buttons) - { - button.second->m_state = Button::State::Deselected; - } + ClearHighlightedButton(); buttonEntry->second->m_state = Button::State::Selected; + m_highlightedButtonId = buttonId; + } + } + + void ButtonGroup::ClearHighlightedButton() + { + if (m_highlightedButtonId == InvalidButtonId) + { + return; + } + if (auto buttonEntry = m_buttons.find(m_highlightedButtonId); buttonEntry != m_buttons.end()) + { + buttonEntry->second->m_state = Button::State::Deselected; + m_highlightedButtonId = InvalidButtonId; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ButtonGroup.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ButtonGroup.h index 0500a80c36..7b6864a71b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ButtonGroup.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ButtonGroup.h @@ -8,6 +8,7 @@ #pragma once +#include #include namespace AzToolsFramework::ViewportUi::Internal @@ -24,6 +25,7 @@ namespace AzToolsFramework::ViewportUi::Internal ~ButtonGroup() = default; void SetHighlightedButton(ButtonId buttonId); + void ClearHighlightedButton(); void SetViewportUiElementId(ViewportUiElementId id); ViewportUiElementId GetViewportUiElementId() const; @@ -39,5 +41,6 @@ namespace AzToolsFramework::ViewportUi::Internal AZ::Event m_buttonTriggeredEvent; ViewportUiElementId m_viewportUiId; AZStd::unordered_map> m_buttons; + ButtonId m_highlightedButtonId = InvalidButtonId; }; } // namespace AzToolsFramework::ViewportUi::Internal diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp index cee4a311e8..255d11f561 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp @@ -50,6 +50,16 @@ namespace AzToolsFramework::ViewportUi } } + void ViewportUiManager::ClearClusterActiveButton(ClusterId clusterId) + { + if (auto clusterIt = m_clusterButtonGroups.find(clusterId); clusterIt != m_clusterButtonGroups.end()) + { + auto cluster = clusterIt->second; + cluster->ClearHighlightedButton(); + UpdateButtonGroupUi(cluster.get()); + } + } + void ViewportUiManager::SetSwitcherActiveButton(const SwitcherId switcherId, const ButtonId buttonId) { if (auto switcherIt = m_switcherButtonGroups.find(switcherId); switcherIt != m_switcherButtonGroups.end()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h index c5ecf7aee6..9ec6648451 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h @@ -30,6 +30,7 @@ namespace AzToolsFramework::ViewportUi const ClusterId CreateCluster(Alignment align) override; const SwitcherId CreateSwitcher(Alignment align) override; void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override; + void ClearClusterActiveButton(ClusterId clusterId) override; void SetSwitcherActiveButton(SwitcherId switcherId, ButtonId buttonId) override; void SetClusterButtonLocked(ClusterId clusterId, ButtonId buttonId, bool isLocked) override; void SetClusterButtonTooltip(ClusterId clusterId, ButtonId buttonId, const AZStd::string& tooltip) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h index 649186c7e7..108d23950a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h @@ -59,6 +59,8 @@ namespace AzToolsFramework::ViewportUi virtual const SwitcherId CreateSwitcher(Alignment align) = 0; //! Sets the active button of the cluster. This is the button which will display as highlighted. virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0; + //! Clears the active button of the cluster if one is active. The button will no longer display as highlighted. + virtual void ClearClusterActiveButton(ClusterId clusterId) = 0; //! Sets the active button of the switcher. This is the button which has a text label. virtual void SetSwitcherActiveButton(SwitcherId clusterId, ButtonId buttonId) = 0; //! Adds a locked overlay to the cluster button's icon. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 88febef8c2..f09d4f9b72 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -7,6 +7,7 @@ # set(FILES + AssetEditor/AssetEditorBus.cpp AssetEditor/AssetEditorBus.h AssetEditor/AssetEditorToolbar.ui AssetEditor/AssetEditorStatusBar.ui @@ -150,6 +151,7 @@ set(FILES Fingerprinting/TypeFingerprinter.h Fingerprinting/TypeFingerprinter.cpp FocusMode/FocusModeInterface.h + FocusMode/FocusModeNotificationBus.h FocusMode/FocusModeSystemComponent.h FocusMode/FocusModeSystemComponent.cpp Logger/TraceLogger.cpp @@ -635,6 +637,7 @@ set(FILES Prefab/PrefabFocusHandler.h Prefab/PrefabFocusHandler.cpp Prefab/PrefabFocusInterface.h + Prefab/PrefabFocusNotificationBus.h Prefab/PrefabIdTypes.h Prefab/PrefabLoader.h Prefab/PrefabLoader.cpp @@ -733,6 +736,8 @@ set(FILES UI/Prefab/PrefabIntegrationInterface.h UI/Prefab/PrefabUiHandler.h UI/Prefab/PrefabUiHandler.cpp + UI/Prefab/PrefabViewportFocusPathHandler.h + UI/Prefab/PrefabViewportFocusPathHandler.cpp PythonTerminal/ScriptHelpDialog.cpp PythonTerminal/ScriptHelpDialog.h PythonTerminal/ScriptHelpDialog.ui diff --git a/Code/Framework/AzToolsFramework/Tests/BoundsTestComponent.cpp b/Code/Framework/AzToolsFramework/Tests/BoundsTestComponent.cpp new file mode 100644 index 0000000000..11a077f53c --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/BoundsTestComponent.cpp @@ -0,0 +1,60 @@ +/* + * 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 + +#include + +namespace UnitTest +{ + AZ::Aabb BoundsTestComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) + { + return GetWorldBounds(); + } + + bool BoundsTestComponent::EditorSelectionIntersectRayViewport( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) + { + return AzToolsFramework::AabbIntersectRay(src, dir, GetWorldBounds(), distance); + } + + bool BoundsTestComponent::SupportsEditorRayIntersect() + { + return true; + } + + void BoundsTestComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context) + { + // noop + } + + void BoundsTestComponent::Activate() + { + AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId()); + AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId()); + } + + void BoundsTestComponent::Deactivate() + { + AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect(); + AzFramework::BoundsRequestBus::Handler::BusDisconnect(); + } + + AZ::Aabb BoundsTestComponent::GetWorldBounds() + { + AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); + return GetLocalBounds().GetTransformedAabb(worldFromLocal); + } + + AZ::Aabb BoundsTestComponent::GetLocalBounds() + { + return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f)); + } + +} // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/BoundsTestComponent.h b/Code/Framework/AzToolsFramework/Tests/BoundsTestComponent.h new file mode 100644 index 0000000000..1aabcfcd64 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/BoundsTestComponent.h @@ -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 + * + */ + +#pragma once + +#include +#include +#include + +namespace UnitTest +{ + //! Basic component that implements BoundsRequestBus and EditorComponentSelectionRequestsBus to be compatible + //! with the Editor visibility system. + //! Note: Used for simulating selection (picking) in the viewport. + class BoundsTestComponent + : public AzToolsFramework::Components::EditorComponentBase + , public AzFramework::BoundsRequestBus::Handler + , public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler + { + public: + AZ_EDITOR_COMPONENT( + BoundsTestComponent, "{E6312E9D-8489-4677-9980-C93C328BC92C}", AzToolsFramework::Components::EditorComponentBase); + + static void Reflect(AZ::ReflectContext* context); + + // AZ::Component overrides ... + void Activate() override; + void Deactivate() override; + + // EditorComponentSelectionRequestsBus overrides ... + AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override; + bool EditorSelectionIntersectRayViewport( + const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override; + bool SupportsEditorRayIntersect() override; + + // BoundsRequestBus overrides ... + AZ::Aabb GetWorldBounds() override; + AZ::Aabb GetLocalBounds() override; + }; + +} // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp index 449c4549be..81909ac511 100644 --- a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -22,12 +21,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include @@ -41,6 +38,8 @@ #include #include +#include + namespace AZ { std::ostream& operator<<(std::ostream& os, const EntityId entityId) @@ -123,80 +122,6 @@ namespace UnitTest EXPECT_FALSE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value())); } - //! Basic component that implements BoundsRequestBus and EditorComponentSelectionRequestsBus to be compatible - //! with the Editor visibility system. - //! Note: Used for simulating selection (picking) in the viewport. - class BoundsTestComponent - : public AzToolsFramework::Components::EditorComponentBase - , public AzFramework::BoundsRequestBus::Handler - , public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler - { - public: - AZ_EDITOR_COMPONENT( - BoundsTestComponent, "{E6312E9D-8489-4677-9980-C93C328BC92C}", AzToolsFramework::Components::EditorComponentBase); - - static void Reflect(AZ::ReflectContext* context); - - // AZ::Component overrides ... - void Activate() override; - void Deactivate() override; - - // EditorComponentSelectionRequestsBus overrides ... - AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override; - bool EditorSelectionIntersectRayViewport( - const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override; - bool SupportsEditorRayIntersect() override; - - // BoundsRequestBus overrides ... - AZ::Aabb GetWorldBounds() override; - AZ::Aabb GetLocalBounds() override; - }; - - AZ::Aabb BoundsTestComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) - { - return GetWorldBounds(); - } - - bool BoundsTestComponent::EditorSelectionIntersectRayViewport( - [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) - { - return AzToolsFramework::AabbIntersectRay(src, dir, GetWorldBounds(), distance); - } - - bool BoundsTestComponent::SupportsEditorRayIntersect() - { - return true; - } - - void BoundsTestComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context) - { - // noop - } - - void BoundsTestComponent::Activate() - { - AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId()); - AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId()); - } - - void BoundsTestComponent::Deactivate() - { - AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect(); - AzFramework::BoundsRequestBus::Handler::BusDisconnect(); - } - - AZ::Aabb BoundsTestComponent::GetWorldBounds() - { - AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); - return GetLocalBounds().GetTransformedAabb(worldFromLocal); - } - - AZ::Aabb BoundsTestComponent::GetLocalBounds() - { - return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f)); - } - // Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection. class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture { @@ -242,11 +167,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 +186,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) @@ -343,9 +269,10 @@ namespace UnitTest using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus; EditorInteractionSystemViewportSelectionRequestBus::Event( AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler, - [](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache) + [](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache, + [[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker) { - return AZStd::make_unique(entityDataCache); + return AZStd::make_unique(entityDataCache, viewportEditorModeTracker); }); // When @@ -371,16 +298,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 +551,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 +576,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 +655,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 +681,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 +707,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 +733,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 +759,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 +785,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 +928,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 +962,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 +983,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 +996,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 +1023,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 +1040,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 + { + }; + + 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 + { + }; + + 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 + { + }; + + 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 + { + }; + + 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 + { + }; + + 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; @@ -1661,7 +2137,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() })); diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp new file mode 100644 index 0000000000..c309261a27 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp @@ -0,0 +1,81 @@ +/* + * 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 + +#include + +#include + +namespace AzToolsFramework +{ + void EditorFocusModeFixture::SetUpEditorFixtureImpl() + { + // 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); + + m_focusModeInterface = AZ::Interface::Get(); + ASSERT_TRUE(m_focusModeInterface != nullptr); + + // register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus + GetApplication()->RegisterComponentDescriptor(UnitTest::BoundsTestComponent::CreateDescriptor()); + + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + + GenerateTestHierarchy(); + } + + void EditorFocusModeFixture::GenerateTestHierarchy() + { + /* + * City + * |_ Street + * |_ Car + * | |_ Passenger + * |_ SportsCar + * |_ Passenger + */ + + m_entityMap[CityEntityName] = CreateEditorEntity(CityEntityName, AZ::EntityId()); + m_entityMap[StreetEntityName] = CreateEditorEntity(StreetEntityName, m_entityMap[CityEntityName]); + m_entityMap[CarEntityName] = CreateEditorEntity(CarEntityName, m_entityMap[StreetEntityName]); + m_entityMap[Passenger1EntityName] = CreateEditorEntity(Passenger1EntityName, m_entityMap[CarEntityName]); + m_entityMap[SportsCarEntityName] = CreateEditorEntity(SportsCarEntityName, m_entityMap[StreetEntityName]); + m_entityMap[Passenger2EntityName] = CreateEditorEntity(Passenger2EntityName, m_entityMap[SportsCarEntityName]); + + // Add a BoundsTestComponent to the Car entity. + AZ::Entity* entity = GetEntityById(m_entityMap[CarEntityName]); + + entity->Deactivate(); + entity->CreateComponent(); + entity->Activate(); + + // Move the CarEntity so it's out of the way. + AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, CarEntityPosition); + + // Setup the camera so the Car entity is in view. + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 0.0f)), CameraPosition)); + } + + AZ::EntityId EditorFocusModeFixture::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(); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h new file mode 100644 index 0000000000..f038408012 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h @@ -0,0 +1,50 @@ +/* + * 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 +#include +#include + +#include + +#include +#include + +namespace AzToolsFramework +{ + class EditorFocusModeFixture + : public UnitTest::ToolsApplicationFixture + { + protected: + void SetUpEditorFixtureImpl() override; + + void GenerateTestHierarchy(); + AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId); + + AZStd::unordered_map m_entityMap; + FocusModeInterface* m_focusModeInterface = nullptr; + + public: + AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + + AzFramework::CameraState m_cameraState; + + inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f); + + inline static const char* CityEntityName = "City"; + inline static const char* StreetEntityName = "Street"; + inline static const char* CarEntityName = "Car"; + inline static const char* SportsCarEntityName = "SportsCar"; + inline static const char* Passenger1EntityName = "Passenger1"; + inline static const char* Passenger2EntityName = "Passenger2"; + + inline static AZ::Vector3 CarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f); + }; +} diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionTests.cpp new file mode 100644 index 0000000000..968bb3f293 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionTests.cpp @@ -0,0 +1,147 @@ +/* + * 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 + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +namespace AzToolsFramework +{ + class EditorFocusModeSelectionFixture + : public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin + { + public: + void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition) + { + // Calculate the world position in screen space + const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState); + + // Click the entity in the viewport + m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp(); + } + }; + + void ClearSelectedEntities() + { + AzToolsFramework::ToolsApplicationRequestBus::Broadcast( + &AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList()); + } + + AzToolsFramework::EntityIdList GetSelectedEntities() + { + AzToolsFramework::EntityIdList selectedEntities; + AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult( + selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities); + return selectedEntities; + } + + TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnLevel) + { + // Clear the focus, disabling focus mode + m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); + // Clear selection + ClearSelectedEntities(); + + // Click on Car Entity + ClickAtWorldPositionOnViewport(CarEntityPosition); + + // Verify entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 1); + EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]); + } + + TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnAncestor) + { + // Set the focus on the Street Entity (parent of the test entity) + m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); + // Clear selection + ClearSelectedEntities(); + + // Click on Car Entity + ClickAtWorldPositionOnViewport(CarEntityPosition); + + // Verify entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 1); + EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]); + + // Clear the focus, disabling focus mode + m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); + } + + TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnItself) + { + // Set the focus on the Car Entity (test entity) + m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]); + // Clear selection + ClearSelectedEntities(); + + // Click on Car Entity + ClickAtWorldPositionOnViewport(CarEntityPosition); + + // Verify entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 1); + EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]); + + // Clear the focus, disabling focus mode + m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); + } + + TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnSibling) + { + // Set the focus on the SportsCar Entity (sibling of the test entity) + m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]); + // Clear selection + ClearSelectedEntities(); + + // Click on Car Entity + ClickAtWorldPositionOnViewport(CarEntityPosition); + + // Verify entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 0); + + // Clear the focus, disabling focus mode + m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); + } + + TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnDescendant) + { + // Set the focus on the Passenger1 Entity (child of the entity) + m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]); + // Clear selection + ClearSelectedEntities(); + + // Click on Car Entity + ClickAtWorldPositionOnViewport(CarEntityPosition); + + // Verify entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 0); + + // Clear the focus, disabling focus mode + m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp new file mode 100644 index 0000000000..22bc7a494c --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp @@ -0,0 +1,104 @@ +/* + * 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 + +namespace AzToolsFramework +{ + TEST_F(EditorFocusModeFixture, EditorFocusModeTests_SetFocus) + { + // When an entity is set as the focus root, GetFocusRoot should return its EntityId. + m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]); + EXPECT_EQ(m_focusModeInterface->GetFocusRoot(m_editorEntityContextId), m_entityMap[CarEntityName]); + + // Restore default expected focus. + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + } + + TEST_F(EditorFocusModeFixture, EditorFocusModeTests_ClearFocus) + { + // Change the value from the default. + m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]); + + // Calling ClearFocusRoot restores the default focus root (which is an invalid EntityId). + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + EXPECT_EQ(m_focusModeInterface->GetFocusRoot(m_editorEntityContextId), AZ::EntityId()); + } + + TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants) + { + // When the focus is set to an entity, all its descendants are in the focus subtree while the ancestors aren't. + { + m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); + + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); + } + + // Restore default expected focus. + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + } + + TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Siblings) + { + // If the root entity has siblings, they are also outside of the focus subtree. + { + m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]); + + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false); + } + + // Restore default expected focus. + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + } + + TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Leaf) + { + // If the root is a leaf, then the focus subtree will consists of just that entity. + { + m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]); + + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); + } + + // Restore default expected focus. + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + } + + TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Clear) + { + // Change the value from the default. + m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); + + // When the focus is cleared, the whole level is in the focus subtree; so we expect all entities to return true. + { + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); + } + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp new file mode 100644 index 0000000000..14eee84b7d --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp @@ -0,0 +1,220 @@ +/* + * 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 +#include +#include +#include +#include + +namespace UnitTest +{ + class PrefabFocusTests + : public PrefabTestFixture + { + protected: + void GenerateTestHierarchy() + { + /* + * City (Prefab Container) + * |_ City + * |_ Street (Prefab Container) + * |_ Car (Prefab Container) + * | |_ Passenger + * |_ SportsCar (Prefab Container) + * |_ Passenger + */ + + // Create loose entities + m_entityMap[Passenger1EntityName] = CreateEntity(Passenger1EntityName); + m_entityMap[Passenger2EntityName] = CreateEntity(Passenger2EntityName); + m_entityMap[CityEntityName] = CreateEntity(CityEntityName); + + // Call HandleEntitiesAdded to the loose entities to register them with the Prefab EOS + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, + AzToolsFramework::EntityList{ m_entityMap[Passenger1EntityName], m_entityMap[Passenger2EntityName], m_entityMap[CityEntityName] }); + + // Create a car prefab from the passenger1 entity. The container entity will be created as part of the process. + AZStd::unique_ptr carInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap[Passenger1EntityName] }, {}, "test/car"); + ASSERT_TRUE(carInstance); + m_instanceMap[CarEntityName] = carInstance.get(); + + // Create a sportscar prefab from the passenger2 entity. The container entity will be created as part of the process. + AZStd::unique_ptr sportsCarInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap[Passenger2EntityName] }, {}, "test/sportsCar"); + ASSERT_TRUE(sportsCarInstance); + m_instanceMap[SportsCarEntityName] = sportsCarInstance.get(); + + // Create a street prefab that nests the car and sportscar instances created above. The container entity will be created as part of the process. + AZStd::unique_ptr streetInstance = + m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(carInstance), AZStd::move(sportsCarInstance) ), "test/street"); + ASSERT_TRUE(streetInstance); + m_instanceMap[StreetEntityName] = streetInstance.get(); + + // Create a city prefab that nests the street instances created above and the city entity. The container entity will be created as part of the process. + m_rootInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap[CityEntityName] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city"); + ASSERT_TRUE(m_rootInstance); + m_instanceMap[CityEntityName] = m_rootInstance.get(); + } + + void SetUpEditorFixtureImpl() override + { + PrefabTestFixture::SetUpEditorFixtureImpl(); + + m_prefabFocusInterface = AZ::Interface::Get(); + ASSERT_TRUE(m_prefabFocusInterface != nullptr); + + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + + GenerateTestHierarchy(); + } + + void TearDownEditorFixtureImpl() override + { + m_rootInstance.release(); + + PrefabTestFixture::TearDownEditorFixtureImpl(); + } + + AZStd::unordered_map m_entityMap; + AZStd::unordered_map m_instanceMap; + + AZStd::unique_ptr m_rootInstance; + + PrefabFocusInterface* m_prefabFocusInterface = nullptr; + AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + + inline static const char* CityEntityName = "City"; + inline static const char* StreetEntityName = "Street"; + inline static const char* CarEntityName = "Car"; + inline static const char* SportsCarEntityName = "SportsCar"; + inline static const char* Passenger1EntityName = "Passenger1"; + inline static const char* Passenger2EntityName = "Passenger2"; + }; + + TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootContainer) + { + // Verify FocusOnOwningPrefab works when passing the container entity of the root prefab. + { + m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); + EXPECT_EQ( + m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), + m_instanceMap[CityEntityName]->GetTemplateId()); + + auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap[CityEntityName]); + } + } + + TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootEntity) + { + // Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab. + { + m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId()); + EXPECT_EQ( + m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), + m_instanceMap[CityEntityName]->GetTemplateId()); + + auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap[CityEntityName]); + } + } + + TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_NestedContainer) + { + // Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab. + { + m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId()); + EXPECT_EQ( + m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId()); + + auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap[CarEntityName]); + } + } + + TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_NestedEntity) + { + // Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab. + { + m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId()); + EXPECT_EQ( + m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId()); + + auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap[CarEntityName]); + } + } + + TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_Clear) + { + // Verify FocusOnOwningPrefab points to the root prefab when the focus is cleared. + { + AzToolsFramework::PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = + AZ::Interface::Get(); + AzToolsFramework::Prefab::InstanceOptionalReference rootPrefabInstance = + prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); + EXPECT_TRUE(rootPrefabInstance.has_value()); + + m_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId()); + EXPECT_EQ( + m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), rootPrefabInstance->get().GetTemplateId()); + + auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), &rootPrefabInstance->get()); + } + } + + TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_Content) + { + // Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested) + { + m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); + + EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); + EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); + } + } + + TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_AncestorsDescendants) + { + // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants) + { + m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId()); + + EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); + EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); + } + } + + TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_Siblings) + { + // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings) + { + m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()); + + EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId())); + EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId())); + EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); + } + } + +} diff --git a/Code/Framework/AzToolsFramework/Tests/PropertyTreeEditorTests.cpp b/Code/Framework/AzToolsFramework/Tests/PropertyTreeEditorTests.cpp index 91e53f1650..5def9e6bad 100644 --- a/Code/Framework/AzToolsFramework/Tests/PropertyTreeEditorTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/PropertyTreeEditorTests.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp index 3954ef6dc6..866d88b7ba 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp @@ -7,7 +7,9 @@ */ #include +#include #include +#include #include namespace UnitTest @@ -18,6 +20,7 @@ namespace UnitTest using ViewportEditorModeInfo = AzToolsFramework::ViewportEditorModeInfo; using ViewportId = ViewportEditorModeInfo::IdType; using ViewportEditorModesInterface = AzToolsFramework::ViewportEditorModesInterface; + using ViewportEditorModeTrackerInterface = AzToolsFramework::ViewportEditorModeTrackerInterface; void ActivateModeAndExpectSuccess(ViewportEditorModes& editorModeState, ViewportEditorMode mode) { @@ -47,6 +50,26 @@ namespace UnitTest } } + void ExpectOnlyModeActive(const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) + { + for (auto modeIndex = 0; modeIndex < ViewportEditorModes::NumEditorModes; modeIndex++) + { + const auto currentMode = static_cast(modeIndex); + const bool expectedActive = (mode == currentMode); + EXPECT_EQ(editorModeState.IsModeActive(currentMode), expectedActive); + } + } + + void ExpectOnlyModeInactive(const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) + { + for (auto modeIndex = 0; modeIndex < ViewportEditorModes::NumEditorModes; modeIndex++) + { + const auto currentMode = static_cast(modeIndex); + const bool expectedActive = (mode != currentMode); + EXPECT_EQ(editorModeState.IsModeActive(currentMode), expectedActive); + } + } + // Fixture for testing editor mode states class ViewportEditorModesTestsFixture : public ::testing::Test @@ -116,7 +139,7 @@ namespace UnitTest m_editorModes[mode].m_onEnter = true; } - virtual void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override + void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override { m_editorModes[mode].m_onExit = true; } @@ -152,6 +175,22 @@ namespace UnitTest AZStd::array, ViewportEditorModes::NumEditorModes> m_editorModeHandlers; }; + // Fixture for testing the integration of viewport editor mode state tracker + class ViewportEditorModeTrackerIntegrationTestFixture + : public ToolsApplicationFixture + { + public: + void SetUpEditorFixtureImpl() override + { + m_viewportEditorModeTracker = AZ::Interface::Get(); + ASSERT_NE(m_viewportEditorModeTracker, nullptr); + m_viewportEditorModes = m_viewportEditorModeTracker->GetViewportEditorModes({}); + } + + ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; + const ViewportEditorModesInterface* m_viewportEditorModes = nullptr; + }; + TEST_F(ViewportEditorModesTestsFixture, NumberOfEditorModesIsEqualTo4) { EXPECT_EQ(ViewportEditorModes::NumEditorModes, 4); @@ -168,38 +207,14 @@ namespace UnitTest TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingModeActiveActivatesOnlyThatMode) { ActivateModeAndExpectSuccess(m_editorModes, m_selectedEditorMode); - - for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++) - { - const auto editorMode = static_cast(mode); - if (editorMode == m_selectedEditorMode) - { - EXPECT_TRUE(m_editorModes.IsModeActive(static_cast(editorMode))); - } - else - { - EXPECT_FALSE(m_editorModes.IsModeActive(static_cast(editorMode))); - } - } + ExpectOnlyModeActive(m_editorModes, m_selectedEditorMode); } TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingModeInactiveInactivatesOnlyThatMode) { SetAllModesActive(m_editorModes); DeactivateModeAndExpectSuccess(m_editorModes, m_selectedEditorMode); - - for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++) - { - const auto editorMode = static_cast(mode); - if (editorMode == m_selectedEditorMode) - { - EXPECT_FALSE(m_editorModes.IsModeActive(editorMode)); - } - else - { - EXPECT_TRUE(m_editorModes.IsModeActive(editorMode)); - } - } + ExpectOnlyModeInactive(m_editorModes, m_selectedEditorMode); } TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingMultipleModesActiveActivatesAllThoseModesNonMutuallyExclusively) @@ -298,7 +313,7 @@ namespace UnitTest EXPECT_EQ(m_viewportEditorModeTracker.GetTrackedViewportCount(), 0); } - TEST_F(ViewportEditorModeTrackerTestFixture, RegisteringViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatId) + TEST_F(ViewportEditorModeTrackerTestFixture, ActivatingViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatId) { // Given a viewport not currently being tracked const ViewportId viewportid = 0; @@ -318,7 +333,7 @@ namespace UnitTest EXPECT_TRUE(viewportEditorModeState->IsModeActive(editorMode)); } - TEST_F(ViewportEditorModeTrackerTestFixture, UnregisteringViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatIdButReturnsError) + TEST_F(ViewportEditorModeTrackerTestFixture, DeactivatingViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatIdButReturnsError) { // Given a viewport not currently being tracked const ViewportId viewportid = 0; @@ -351,7 +366,7 @@ namespace UnitTest EXPECT_EQ(m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid }), nullptr); } - TEST_F(ViewportEditorModeTrackerTestFixture, RegisteringViewportEditorModesForExistingIdInThatStateReturnsError) + TEST_F(ViewportEditorModeTrackerTestFixture, ActivatingViewportEditorModesForExistingIdInThatStateReturnsError) { // Given a viewport not currently tracked const ViewportId viewportid = 0; @@ -390,7 +405,7 @@ namespace UnitTest } } - TEST_F(ViewportEditorModeTrackerTestFixture, UnregisteringViewportEditorModesForExistingIdNotInThatStateReturnssError) + TEST_F(ViewportEditorModeTrackerTestFixture, DeactivatingViewportEditorModesForExistingIdNotInThatStateReturnssError) { // Given a viewport not currently tracked const ViewportId viewportid = 0; @@ -432,7 +447,7 @@ namespace UnitTest TEST_F( ViewportEditorModePublisherTestFixture, - RegisteringViewportEditorModesForExistingIdPublishesOnViewportEditorModeRegisterEventForAllSubscribers) + ActivatingViewportEditorModesForExistingIdPublishesOnViewportEditorModeActivateEventForAllSubscribers) { // Given a set of subscribers tracking the editor modes for their exclusive viewport for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++) @@ -465,7 +480,7 @@ namespace UnitTest TEST_F( ViewportEditorModePublisherTestFixture, - UnregisteringViewportEditorModesForExistingIdPublishesOnViewportEditorModeUnregisterEventForAllSubscribers) + DeactivatingViewportEditorModesForExistingIdPublishesOnViewportEditorModeDeactivatingEventForAllSubscribers) { // Given a set of subscribers tracking the editor modes for their exclusive viewport for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++) @@ -495,4 +510,54 @@ namespace UnitTest EXPECT_TRUE(expectedEditorModeSet->second.m_onExit); } } + + TEST_F(ViewportEditorModeTrackerIntegrationTestFixture, InitialViewportEditorModeIsDefault) + { + ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default); + } + + TEST_F( + ViewportEditorModeTrackerIntegrationTestFixture, EnteringComponentModeAfterInitialStateHasViewportEditorModesDefaultAndComponentModeActive) + { + // When component mode is entered + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, + AZStd::vector{}); + + bool inComponentMode = false; + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( + inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); + + // Expect to be in component mode + EXPECT_TRUE(inComponentMode); + + // Expect the default and component viewport editor modes to be active + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component)); + + // Do not expect the pick and focus viewport editor modes to be active + EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick)); + EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus)); + } + + TEST_F( + ViewportEditorModeTrackerIntegrationTestFixture, + EnteringEditorPickEntitySelectionAfterInitialStateHasOnlyViewportEditorModePickModeActive) + { + // When entering pick mode + using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus; + EditorInteractionSystemViewportSelectionRequestBus::Event( + AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler, + [](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache, + [[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker) + { + return AZStd::make_unique(entityDataCache, viewportEditorModeTracker); + }); + + // Expect only the pick viewport editor mode to be active + ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Pick); + } + + // FocusMode integration tests will follow (LYN-6995) + } // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp index 77058038e9..e8fce6653f 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp @@ -6,8 +6,8 @@ * */ -#include #include +#include #include #include #include @@ -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) diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportUiManagerTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportUiManagerTests.cpp index 676b9c5e35..daf4b9c29a 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportUiManagerTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportUiManagerTests.cpp @@ -127,6 +127,25 @@ namespace UnitTest EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Selected); } + TEST_F(ViewportUiManagerTestFixture, ClearClusterActiveButtonSetsButtonStateToDeselected) + { + // setup + auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft); + auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, ""); + + auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId); + auto button = clusterEntry->second->GetButton(buttonId); + + // first set a button to active + m_viewportManagerWrapper.GetViewportManager()->SetClusterActiveButton(clusterId, buttonId); + EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Selected); + + // clear the active button on the cluster + m_viewportManagerWrapper.GetViewportManager()->ClearClusterActiveButton(clusterId); + // the button should now be deselected + EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Deselected); + } + TEST_F(ViewportUiManagerTestFixture, RegisterClusterEventHandlerConnectsHandlerToClusterEvent) { auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft); diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 764afce266..11ea5a0b38 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -12,6 +12,8 @@ set(FILES AssetFileInfoListComparison.cpp AssetSeedManager.cpp AssetSystemMocks.h + BoundsTestComponent.cpp + BoundsTestComponent.h ComponentAdapterTests.cpp ComponentAddRemove.cpp ComponentModeTestDoubles.cpp @@ -34,6 +36,10 @@ set(FILES EntityTestbed.h FileFunc.cpp FingerprintingTests.cpp + FocusMode/EditorFocusModeFixture.cpp + FocusMode/EditorFocusModeFixture.h + FocusMode/EditorFocusModeSelectionTests.cpp + FocusMode/EditorFocusModeTests.cpp GenericComponentWrapperTest.cpp InstanceDataHierarchy.cpp IntegerPrimtitiveTestConfig.h @@ -50,6 +56,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 diff --git a/Code/Legacy/CryCommon/Maestro/Bus/SequenceComponentBus.h b/Code/Legacy/CryCommon/Maestro/Bus/SequenceComponentBus.h index ba3d7bdcb6..9a3619b89e 100644 --- a/Code/Legacy/CryCommon/Maestro/Bus/SequenceComponentBus.h +++ b/Code/Legacy/CryCommon/Maestro/Bus/SequenceComponentBus.h @@ -7,6 +7,7 @@ */ #pragma once +#include #include #include #include diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index 2c70717e8f..3187095094 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -808,7 +808,7 @@ void CSystem::OpenBasicPaks() const char* const assetsDir = "@assets@"; // After game paks to have same search order as with files on disk - m_env.pCryPak->OpenPack(assetsDir, "Engine.pak"); + m_env.pCryPak->OpenPack(assetsDir, "engine.pak"); #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_15 @@ -1261,7 +1261,7 @@ AZ_POP_DISABLE_WARNING InlineInitializationProcessing("CSystem::Init Create console"); // Need to load the engine.pak that includes the config files needed during initialization - m_env.pCryPak->OpenPack("@assets@", "Engine.pak"); + m_env.pCryPak->OpenPack("@assets@", "engine.pak"); InitFileSystem_LoadEngineFolders(startupParams); diff --git a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp index d6c4661c15..62b063c83b 100644 --- a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp @@ -20,6 +20,7 @@ #include #include #include // For slice asset sub ids +#include ////////////////////////////////////////////////////////////////////////// namespace AssetBuilderSDK diff --git a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/SerializationDependencies.cpp b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/SerializationDependencies.cpp index 9e826faabb..09c6fc7cb4 100644 --- a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/SerializationDependencies.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/SerializationDependencies.cpp @@ -7,6 +7,7 @@ */ #include +#include namespace AssetBuilderSDK { diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp index f570a1ccb4..6eee3d6f2b 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp @@ -8,6 +8,7 @@ #include "native/AssetManager/AssetCatalog.h" +#include #include #include #include diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp b/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp index 60873ae1af..c3530c768a 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp @@ -8,9 +8,10 @@ #include "AssetRequestHandler.h" +#include +#include #include #include -#include using namespace AssetProcessor; diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp index e6d9894116..c65ab24aed 100644 --- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/SerializationDependenciesTests.cpp b/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/SerializationDependenciesTests.cpp index 86c363075d..71d11b19eb 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/SerializationDependenciesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/SerializationDependenciesTests.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include diff --git a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorServerUnitTests.h b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorServerUnitTests.h index 209897fd6e..8358bc3d2e 100644 --- a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorServerUnitTests.h +++ b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorServerUnitTests.h @@ -11,6 +11,7 @@ #if !defined(Q_MOC_RUN) #include "UnitTestRunner.h" #include "native/utilities/IniConfiguration.h" +#include #include #endif diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 957b2b4fa6..eeed316cbc 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -568,17 +568,20 @@ QProgressBar::chunk { font-size: 12px; } +#gemRepoNoReposLabel { + font-size: 16px; +} + #gemRepoHeaderRefreshButton { background-color: transparent; qproperty-flat: true; qproperty-iconSize: 14px; } -#gemRepoHeaderAddButton { +#gemRepoAddButton { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #888888, stop: 1.0 #555555); qproperty-flat: true; - margin-right:30px; min-width:120px; max-width:120px; min-height:24px; @@ -588,11 +591,11 @@ QProgressBar::chunk { font-size:12px; font-weight:600; } -#gemRepoHeaderAddButton:hover { +#gemRepoAddButton:hover { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #999999, stop: 1.0 #666666); } -#gemRepoHeaderAddButton:pressed { +#gemRepoAddButton:pressed { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #555555, stop: 1.0 #777777); } @@ -610,6 +613,14 @@ QProgressBar::chunk { background: #444444; } +#gemRepoAddDialogInstructionTitleLabel { + font-size:14px; +} + +#addGemRepoDialog #formFrame { + margin-left:0px; +} + /************** Gem Repo Inspector **************/ #gemRepoInspectorNameLabel { diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp new file mode 100644 index 0000000000..1839948e80 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp @@ -0,0 +1,65 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemRepoAddDialog::GemRepoAddDialog(QWidget* parent) + : QDialog(parent) + { + setWindowTitle(tr("Add a User Repository")); + setModal(true); + setObjectName("addGemRepoDialog"); + + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setContentsMargins(30, 30, 25, 10); + vLayout->setSpacing(0); + setLayout(vLayout); + + QLabel* instructionTitleLabel = new QLabel(tr("Enter a valid path to add a new user repository")); + instructionTitleLabel->setObjectName("gemRepoAddDialogInstructionTitleLabel"); + instructionTitleLabel->setAlignment(Qt::AlignLeft); + vLayout->addWidget(instructionTitleLabel); + + vLayout->addSpacing(10); + + QLabel* instructionContextLabel = new QLabel(tr("The path can be a Repository URL or a Local Path in your directory.")); + instructionContextLabel->setAlignment(Qt::AlignLeft); + vLayout->addWidget(instructionContextLabel); + + m_repoPath = new FormLineEditWidget(tr("Repository Path"), "", this); + m_repoPath->setFixedWidth(600); + vLayout->addWidget(m_repoPath); + + vLayout->addSpacing(40); + + QDialogButtonBox* dialogButtons = new QDialogButtonBox(); + dialogButtons->setObjectName("footer"); + vLayout->addWidget(dialogButtons); + + QPushButton* cancelButton = dialogButtons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); + cancelButton->setProperty("secondary", true); + QPushButton* applyButton = dialogButtons->addButton(tr("Add"), QDialogButtonBox::ApplyRole); + + connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject); + connect(applyButton, &QPushButton::clicked, this, &QDialog::accept); + } + + QString GemRepoAddDialog::GetRepoPath() + { + return m_repoPath->lineEdit()->text(); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.h new file mode 100644 index 0000000000..4ca469098e --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.h @@ -0,0 +1,31 @@ +/* + * 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 + +#if !defined(Q_MOC_RUN) +#include +#endif + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(FormLineEditWidget) + + class GemRepoAddDialog + : public QDialog + { + public: + explicit GemRepoAddDialog(QWidget* parent = nullptr); + ~GemRepoAddDialog() = default; + + QString GetRepoPath(); + + private: + FormLineEditWidget* m_repoPath = nullptr; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp index c0b17904f8..9c432884e6 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,8 @@ #include #include #include +#include +#include namespace O3DE::ProjectManager { @@ -34,11 +37,135 @@ namespace O3DE::ProjectManager vLayout->setSpacing(0); setLayout(vLayout); + m_contentStack = new QStackedWidget(this); + + m_noRepoContent = CreateNoReposContent(); + m_contentStack->addWidget(m_noRepoContent); + + m_repoContent = CreateReposContent(); + m_contentStack->addWidget(m_repoContent); + + vLayout->addWidget(m_contentStack); + + Reinit(); + } + + void GemRepoScreen::Reinit() + { + m_gemRepoModel->clear(); + FillModel(); + + // If model contains any data show the repos + if (m_gemRepoModel->rowCount()) + { + m_contentStack->setCurrentWidget(m_repoContent); + } + else + { + m_contentStack->setCurrentWidget(m_noRepoContent); + } + + // Select the first entry after everything got correctly sized + QTimer::singleShot(200, [=]{ + QModelIndex firstModelIndex = m_gemRepoListView->model()->index(0,0); + m_gemRepoListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); + }); + } + + void GemRepoScreen::HandleAddRepoButton() + { + GemRepoAddDialog* repoAddDialog = new GemRepoAddDialog(this); + + if (repoAddDialog->exec() == QDialog::DialogCode::Accepted) + { + QString repoUrl = repoAddDialog->GetRepoPath(); + if (repoUrl.isEmpty()) + { + return; + } + + AZ::Outcome addGemRepoResult = PythonBindingsInterface::Get()->AddGemRepo(repoUrl); + if (addGemRepoResult.IsSuccess()) + { + Reinit(); + } + else + { + QMessageBox::critical(this, tr("Operation failed"), + QString("Failed to add gem repo: %1.
Error:
%2").arg(repoUrl, addGemRepoResult.GetError().c_str())); + } + } + } + + void GemRepoScreen::FillModel() + { + AZ::Outcome, AZStd::string> allGemRepoInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoInfos(); + if (allGemRepoInfosResult.IsSuccess()) + { + // Add all available repos to the model + const QVector allGemRepoInfos = allGemRepoInfosResult.GetValue(); + for (const GemRepoInfo& gemRepoInfo : allGemRepoInfos) + { + m_gemRepoModel->AddGemRepo(gemRepoInfo); + } + } + else + { + QMessageBox::critical(this, tr("Operation failed"), QString("Cannot retrieve gem repos for engine.
Error:
%2").arg(allGemRepoInfosResult.GetError().c_str())); + } + } + + QFrame* GemRepoScreen::CreateNoReposContent() + { + QFrame* contentFrame = new QFrame(this); + + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setAlignment(Qt::AlignHCenter); + vLayout->setMargin(0); + vLayout->setSpacing(0); + contentFrame->setLayout(vLayout); + + vLayout->addStretch(); + + QLabel* noRepoLabel = new QLabel(tr("No repositories have been added yet."), this); + noRepoLabel->setObjectName("gemRepoNoReposLabel"); + vLayout->addWidget(noRepoLabel); + vLayout->setAlignment(noRepoLabel, Qt::AlignHCenter); + + vLayout->addSpacing(20); + + // Size hint for button is wrong so horizontal layout with stretch is used to center it QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); hLayout->setSpacing(0); + + hLayout->addStretch(); + + QPushButton* addRepoButton = new QPushButton(tr("Add Repository"), this); + addRepoButton->setObjectName("gemRepoAddButton"); + addRepoButton->setMinimumWidth(120); + hLayout->addWidget(addRepoButton); + + connect(addRepoButton, &QPushButton::clicked, this, &GemRepoScreen::HandleAddRepoButton); + + hLayout->addStretch(); + vLayout->addLayout(hLayout); + vLayout->addStretch(); + + return contentFrame; + } + + QFrame* GemRepoScreen::CreateReposContent() + { + QFrame* contentFrame = new QFrame(this); + + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setMargin(0); + hLayout->setSpacing(0); + contentFrame->setLayout(hLayout); + hLayout->addSpacing(60); QVBoxLayout* middleVLayout = new QVBoxLayout(); @@ -63,9 +190,13 @@ namespace O3DE::ProjectManager topMiddleHLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); - m_AddRepoButton = new QPushButton(tr("Add Repository"), this); - m_AddRepoButton->setObjectName("gemRepoHeaderAddButton"); - topMiddleHLayout->addWidget(m_AddRepoButton); + QPushButton* addRepoButton = new QPushButton(tr("Add Repository"), this); + addRepoButton->setObjectName("gemRepoAddButton"); + topMiddleHLayout->addWidget(addRepoButton); + + connect(addRepoButton, &QPushButton::clicked, this, &GemRepoScreen::HandleAddRepoButton); + + topMiddleHLayout->addSpacing(30); middleVLayout->addLayout(topMiddleHLayout); @@ -105,37 +236,7 @@ namespace O3DE::ProjectManager m_gemRepoInspector->setFixedWidth(240); hLayout->addWidget(m_gemRepoInspector); - Reinit(); - } - - void GemRepoScreen::Reinit() - { - m_gemRepoModel->clear(); - FillModel(); - - // Select the first entry after everything got correctly sized - QTimer::singleShot(200, [=]{ - QModelIndex firstModelIndex = m_gemRepoListView->model()->index(0,0); - m_gemRepoListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); - }); - } - - void GemRepoScreen::FillModel() - { - AZ::Outcome, AZStd::string> allGemRepoInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoInfos(); - if (allGemRepoInfosResult.IsSuccess()) - { - // Add all available repos to the model - const QVector allGemRepoInfos = allGemRepoInfosResult.GetValue(); - for (const GemRepoInfo& gemRepoInfo : allGemRepoInfos) - { - m_gemRepoModel->AddGemRepo(gemRepoInfo); - } - } - else - { - QMessageBox::critical(this, tr("Operation failed"), tr("Cannot retrieve gem repos for engine.\n\nError:\n%2").arg(allGemRepoInfosResult.GetError().c_str())); - } + return contentFrame; } ProjectManagerScreen GemRepoScreen::GetScreenEnum() diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h index f7d943fc2a..284118b978 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h @@ -16,6 +16,8 @@ QT_FORWARD_DECLARE_CLASS(QLabel) QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QHeaderView) QT_FORWARD_DECLARE_CLASS(QTableWidget) +QT_FORWARD_DECLARE_CLASS(QFrame) +QT_FORWARD_DECLARE_CLASS(QStackedWidget) namespace O3DE::ProjectManager { @@ -35,8 +37,17 @@ namespace O3DE::ProjectManager GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; } + public slots: + void HandleAddRepoButton(); + private: void FillModel(); + QFrame* CreateNoReposContent(); + QFrame* CreateReposContent(); + + QStackedWidget* m_contentStack = nullptr; + QFrame* m_noRepoContent; + QFrame* m_repoContent; QTableWidget* m_gemRepoHeaderTable = nullptr; QHeaderView* m_gemRepoListHeader = nullptr; @@ -46,6 +57,5 @@ namespace O3DE::ProjectManager QLabel* m_lastAllUpdateLabel; QPushButton* m_AllUpdateButton; - QPushButton* m_AddRepoButton; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index 81d4047db5..b313c05230 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.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(255.0f * overlayTransparency))); } diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 284ed9dcec..b9369b5bb0 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -921,6 +922,13 @@ namespace O3DE::ProjectManager } } + AZ::Outcome PythonBindings::AddGemRepo(const QString& repoUri) + { + // o3de scripts need method added + (void)repoUri; + return AZ::Failure("Adding Gem Repo not implemented yet in o3de scripts."); + } + GemRepoInfo PythonBindings::GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath) { /* Placeholder Logic */ diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 3b766c3797..42f04ed6e6 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -57,6 +57,7 @@ namespace O3DE::ProjectManager AZ::Outcome> GetProjectTemplates(const QString& projectPath = {}) override; // Gem Repos + AZ::Outcome AddGemRepo(const QString& repoUri) override; AZ::Outcome, AZStd::string> GetAllGemRepoInfos() override; private: diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index ccf217d25b..92139f3df5 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -160,6 +160,13 @@ namespace O3DE::ProjectManager // Gem Repos + /** + * A gem repo to engine. Registers this gem repo with the current engine. + * @param repoUri the absolute filesystem path or url to the gem repo manifest file. + * @return An outcome with the success flag as well as an error message in case of a failure. + */ + virtual AZ::Outcome AddGemRepo(const QString& repoUri) = 0; + /** * Get all available gem repo infos. Gathers all repos registered with the engine. * @return A list of gem repo infos. diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index f71ae290e7..544fa2537b 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -104,6 +104,8 @@ set(FILES Source/GemCatalog/GemSortFilterProxyModel.cpp Source/GemRepo/GemRepoScreen.h Source/GemRepo/GemRepoScreen.cpp + Source/GemRepo/GemRepoAddDialog.h + Source/GemRepo/GemRepoAddDialog.cpp Source/GemRepo/GemRepoInfo.h Source/GemRepo/GemRepoInfo.cpp Source/GemRepo/GemRepoInspector.h diff --git a/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpBlendShapeImporter.cpp b/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpBlendShapeImporter.cpp index 8e447f9d79..2dd7fff8e2 100644 --- a/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpBlendShapeImporter.cpp +++ b/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpBlendShapeImporter.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.h b/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.h index 5b6178a63d..ded12e4051 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.h +++ b/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.h @@ -12,6 +12,10 @@ namespace AZ { + struct BehaviorParameter; + struct BehaviorValueParameter; + class BehaviorClass; + namespace Python { class PythonBehaviorInfo; diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp index d68b6c52d4..664c5f1272 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Tools/SceneAPI/SceneCore/DllMain.cpp b/Code/Tools/SceneAPI/SceneCore/DllMain.cpp index 408b516f84..ad875f5538 100644 --- a/Code/Tools/SceneAPI/SceneCore/DllMain.cpp +++ b/Code/Tools/SceneAPI/SceneCore/DllMain.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp index 8924f10115..2e6503d195 100644 --- a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp +++ b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp b/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp index fa81ea2826..066129f974 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp +++ b/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace AZ { diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MaterialData.cpp b/Code/Tools/SceneAPI/SceneData/GraphData/MaterialData.cpp index 117a1196f8..b6b87fecc7 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/MaterialData.cpp +++ b/Code/Tools/SceneAPI/SceneData/GraphData/MaterialData.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Code/Tools/Standalone/Source/LUA/LUAEditorStyleMessages.h b/Code/Tools/Standalone/Source/LUA/LUAEditorStyleMessages.h index 6ddc462e94..cf27245682 100644 --- a/Code/Tools/Standalone/Source/LUA/LUAEditorStyleMessages.h +++ b/Code/Tools/Standalone/Source/LUA/LUAEditorStyleMessages.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/Gems/AWSClientAuth/Code/Include/Private/AWSClientAuthBus.h b/Gems/AWSClientAuth/Code/Include/Private/AWSClientAuthBus.h index 4e625a563c..63d5bb2a83 100644 --- a/Gems/AWSClientAuth/Code/Include/Private/AWSClientAuthBus.h +++ b/Gems/AWSClientAuth/Code/Include/Private/AWSClientAuthBus.h @@ -9,6 +9,8 @@ #include +#include + namespace Aws { namespace CognitoIdentityProvider diff --git a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h index f0b9b45aff..39e96517a7 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h @@ -9,6 +9,7 @@ #include #include +#include #include diff --git a/Gems/AWSCore/Code/Source/AWSCoreSystemComponent.cpp b/Gems/AWSCore/Code/Source/AWSCoreSystemComponent.cpp index 92a5aa493f..294462e654 100644 --- a/Gems/AWSCore/Code/Source/AWSCoreSystemComponent.cpp +++ b/Gems/AWSCore/Code/Source/AWSCoreSystemComponent.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include diff --git a/Gems/AWSCore/Code/Tests/AWSCoreSystemComponentTest.cpp b/Gems/AWSCore/Code/Tests/AWSCoreSystemComponentTest.cpp index 730fc97f49..f9fff631f4 100644 --- a/Gems/AWSCore/Code/Tests/AWSCoreSystemComponentTest.cpp +++ b/Gems/AWSCore/Code/Tests/AWSCoreSystemComponentTest.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorSystemComponentTest.cpp b/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorSystemComponentTest.cpp index 07a6c8ae08..0cd01b4245 100644 --- a/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorSystemComponentTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorSystemComponentTest.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftCreateSessionOnQueueRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftCreateSessionOnQueueRequest.cpp index 4f178f4655..a6cb024ba8 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftCreateSessionOnQueueRequest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftCreateSessionOnQueueRequest.cpp @@ -6,11 +6,12 @@ * */ +#include + +#include #include #include -#include - namespace AWSGameLift { void AWSGameLiftCreateSessionOnQueueRequest::Reflect(AZ::ReflectContext* context) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftCreateSessionRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftCreateSessionRequest.cpp index 4e21c0e711..1c9669e474 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftCreateSessionRequest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftCreateSessionRequest.cpp @@ -6,11 +6,12 @@ * */ +#include + +#include #include #include -#include - namespace AWSGameLift { void AWSGameLiftCreateSessionRequest::Reflect(AZ::ReflectContext* context) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftJoinSessionRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftJoinSessionRequest.cpp index 4007093175..7d23a4b581 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftJoinSessionRequest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftJoinSessionRequest.cpp @@ -5,12 +5,12 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ +#include +#include #include #include -#include - namespace AWSGameLift { void AWSGameLiftJoinSessionRequest::Reflect(AZ::ReflectContext* context) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftSearchSessionsRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftSearchSessionsRequest.cpp index f484a0b357..b78320dc32 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftSearchSessionsRequest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftSearchSessionsRequest.cpp @@ -6,13 +6,14 @@ * */ +#include +#include + +#include #include #include #include -#include -#include - namespace AWSGameLift { void AWSGameLiftSearchSessionsRequest::Reflect(AZ::ReflectContext* context) diff --git a/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py b/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py index eb36d8a5da..709335f9ce 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py @@ -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 diff --git a/Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer.cpp b/Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer.cpp index f111b972e4..798da14aa1 100644 --- a/Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer.cpp +++ b/Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include /////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapNotificationBus.h b/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapNotificationBus.h index f4ce51fc02..e40bf39923 100644 --- a/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapNotificationBus.h +++ b/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapNotificationBus.h @@ -56,8 +56,7 @@ namespace AZ ////////////////////////////////////////////////////////////////////////// - virtual void OnBootstrapSceneReady([[maybe_unused]]AZ::RPI::Scene* bootstrapScene){} - virtual void OnFrameRateLimitChanged([[maybe_unused]]float fpsLimit){} + virtual void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) = 0; }; using NotificationBus = AZ::EBus; } // namespace Bootstrap diff --git a/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapRequestBus.h b/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapRequestBus.h index fbf3bad935..21cc91420b 100644 --- a/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapRequestBus.h +++ b/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapRequestBus.h @@ -23,8 +23,6 @@ namespace AZ::Render::Bootstrap virtual AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) = 0; virtual bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) = 0; - virtual float GetFrameRateLimit() const = 0; - virtual void SetFrameRateLimit(float fpsLimit) = 0; protected: ~Request() = default; diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index 8a6b7db1ca..1349bc34f9 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -42,14 +42,7 @@ #include #include -static void OnFrameRateLimitChanged(const float& fpsLimit) -{ - AZ::Render::Bootstrap::RequestBus::Broadcast( - &AZ::Render::Bootstrap::RequestBus::Events::SetFrameRateLimit, fpsLimit); -} - AZ_CVAR(AZ::CVarFixedString, r_default_pipeline_name, AZ_TRAIT_BOOTSTRAPSYSTEMCOMPONENT_PIPELINE_NAME, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default Render pipeline name"); -AZ_CVAR(float, r_fps_limit, 0, OnFrameRateLimitChanged, AZ::ConsoleFunctorFlags::Null, "The maximum framerate to render at, or 0 for unlimited"); namespace AZ { @@ -358,22 +351,6 @@ namespace AZ return true; } - float BootstrapSystemComponent::GetFrameRateLimit() const - { - return r_fps_limit; - } - - void BootstrapSystemComponent::SetFrameRateLimit(float fpsLimit) - { - r_fps_limit = fpsLimit; - if (m_viewportContext) - { - m_viewportContext->SetFpsLimit(r_fps_limit); - } - Render::Bootstrap::NotificationBus::Broadcast( - &Render::Bootstrap::NotificationBus::Events::OnFrameRateLimitChanged, fpsLimit); - } - void BootstrapSystemComponent::CreateDefaultRenderPipeline() { EnsureDefaultRenderPipelineInstalledForScene(m_defaultScene, m_viewportContext); @@ -413,11 +390,23 @@ namespace AZ } void BootstrapSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) - { } + { + // Temp: When running in the launcher without the legacy renderer + // we need to call RenderTick on the viewport context each frame. + if (m_viewportContext) + { + AZ::ApplicationTypeQuery appType; + ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType); + if (appType.IsGame()) + { + m_viewportContext->RenderTick(); + } + } + } int BootstrapSystemComponent::GetTickOrder() { - return TICK_PRE_RENDER; + return TICK_LAST; } void BootstrapSystemComponent::OnWindowClosed() diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h index 438c6cb236..566d19b1a4 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h @@ -69,8 +69,6 @@ namespace AZ // Render::Bootstrap::RequestBus::Handler overrides ... AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) override; bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) override; - float GetFrameRateLimit() const override; - void SetFrameRateLimit(float fpsLimit) override; protected: // Component overrides ... diff --git a/Gems/Atom/Component/DebugCamera/Code/Source/ArcBallControllerComponent.cpp b/Gems/Atom/Component/DebugCamera/Code/Source/ArcBallControllerComponent.cpp index 040338ab6f..904d6e0c2f 100644 --- a/Gems/Atom/Component/DebugCamera/Code/Source/ArcBallControllerComponent.cpp +++ b/Gems/Atom/Component/DebugCamera/Code/Source/ArcBallControllerComponent.cpp @@ -9,6 +9,7 @@ #include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype index 9a2edc9fca..17209771e5 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype @@ -5,7 +5,7 @@ "properties": { "general": [ { - "id": "texcoord", + "name": "texcoord", "displayName": "Texture Coordinate Stream", "description": "Which UV channel to use when sampling textures.", "type": "Int", @@ -14,75 +14,75 @@ "max": 8 }, { - "id": "enableShadows", + "name": "enableShadows", "displayName": "Enable Shadows", "description": "Whether to use the shadow maps.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_enableShadows" + "name": "o_enableShadows" } }, { - "id": "enableDirectionalLights", + "name": "enableDirectionalLights", "displayName": "Enable Directional Lights", "description": "Whether to use directional lights.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_enableDirectionalLights" + "name": "o_enableDirectionalLights" } }, { - "id": "enablePunctualLights", + "name": "enablePunctualLights", "displayName": "Enable Punctual Lights", "description": "Whether to use punctual lights.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_enablePunctualLights" + "name": "o_enablePunctualLights" } }, { - "id": "enableAreaLights", + "name": "enableAreaLights", "displayName": "Enable Area Lights", "description": "Whether to use area lights.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_enableAreaLights" + "name": "o_enableAreaLights" } }, { - "id": "enableIBL", + "name": "enableIBL", "displayName": "Enable IBL", "description": "Whether to use Image Based Lighting (IBL).", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableIBL" + "name": "o_enableIBL" } } ], "baseColor": [ { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_baseColor" + "name": "m_baseColor" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", @@ -91,31 +91,31 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_baseColorFactor" + "name": "m_baseColorFactor" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture map.", "type": "Bool", "defaultValue": true }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture Map", "description": "Base color texture map", "type": "Image", "defaultValue": "Textures/Default/default_basecolor.tif", "connection": { "type": "ShaderInput", - "id": "m_baseColorMap" + "name": "m_baseColorMap" } } ], "metallic": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "This value is linear, black is non-metal and white means raw metal.", "type": "Float", @@ -124,30 +124,30 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_metallicFactor" + "name": "m_metallicFactor" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture map, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture Map", "description": "", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_metallicMap" + "name": "m_metallicMap" } } ], "roughness": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the values", "type": "Float", @@ -156,31 +156,31 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessFactor" + "name": "m_roughnessFactor" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture map, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture Map", "description": "Texture map for defining surface roughness.", "type": "Image", "defaultValue": "Textures/Default/default_roughness.tif", "connection": { "type": "ShaderInput", - "id": "m_roughnessMap" + "name": "m_roughnessMap" } } ], "specularF0": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.", "type": "Float", @@ -189,51 +189,51 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_specularF0Factor" + "name": "m_specularF0Factor" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture map, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture Map", "description": "Texture map for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_specularF0Map" + "name": "m_specularF0Map" } }, { - "id": "applySpecularAA", + "name": "applySpecularAA", "displayName": "Apply Specular AA", "description": "Whether to apply specular anti-aliasing in the shader.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_applySpecularAA" + "name": "o_applySpecularAA" } }, { - "id": "enableMultiScatterCompensation", + "name": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", "description": "Whether to enable multiple scattering compensation.", "type": "Bool", "connection": { "type": "ShaderOption", - "id": "o_specularF0_enableMultiScatterCompensation" + "name": "o_specularF0_enableMultiScatterCompensation" } } ], "normal": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the values", "type": "Float", @@ -243,85 +243,85 @@ "max": 2.0, "connection": { "type": "ShaderInput", - "id": "m_normalFactor" + "name": "m_normalFactor" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture map, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture Map", "description": "Texture map for defining surface normal direction.", "type": "Image", "defaultValue": "Textures/Default/default_normal.tif", "connection": { "type": "ShaderInput", - "id": "m_normalMap" + "name": "m_normalMap" } }, { - "id": "flipX", + "name": "flipX", "displayName": "Flip X Channel", "description": "Flip tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_flipNormalX" + "name": "m_flipNormalX" } }, { - "id": "flipY", + "name": "flipY", "displayName": "Flip Y Channel", "description": "Flip bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_flipNormalY" + "name": "m_flipNormalY" } } ], "opacity": [ { - "id": "mode", + "name": "mode", "displayName": "Opacity Mode", "description": "Opacity mode for this texture. 0: Opaque, 1: Cutout, 2:Blended", "type": "Uint", "defaultValue": 0, "connection": { "type": "ShaderOption", - "id": "o_opacity_mode" + "name": "o_opacity_mode" } }, { - "id": "alphaSource", + "name": "alphaSource", "displayName": "Alpha Source", "description": "Source texture of alpha value. 0:Packed, 1:Split, 2:None", "type": "Uint", "defaultValue": 0, "connection": { "type": "ShaderOption", - "id": "o_opacity_source" + "name": "o_opacity_source" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture Map", "description": "Texture map for defining surface opacity.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_opacityMap" + "name": "m_opacityMap" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Factor for cutout threshold and blending", "type": "Float", @@ -330,11 +330,11 @@ "defaultValue": 0.5, "connection": { "type": "ShaderInput", - "id": "m_opacityFactor" + "name": "m_opacityFactor" } }, { - "id": "doubleSided", + "name": "doubleSided", "displayName": "Double-sided", "description": "Whether to render back-faces or just front-faces.", "type": "Bool" @@ -342,14 +342,14 @@ ], "uv": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", "defaultValue": [0.0, 0.0] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in V.", "type": "float", @@ -357,7 +357,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -365,7 +365,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -374,7 +374,7 @@ "max": 1.0 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -383,7 +383,7 @@ "max": 1.0 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -393,7 +393,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", @@ -403,29 +403,29 @@ ], "emissive": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable the emissive group", "type":"Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_emissiveEnabled" + "name": "o_emissiveEnabled" } }, { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_emissiveColor" + "name": "m_emissiveColor" } }, { - "id": "intensity", + "name": "intensity", "displayName": "Intensity", "description": "The amount of energy emitted, in EV100 unit", "type": "Float", @@ -434,33 +434,33 @@ "max": 5 }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture map.", "type": "Bool", "defaultValue": false }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture Map", "description": "Texture map for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_emissiveMap" + "name": "m_emissiveMap" } } ], "parallax": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Whether to enable the parallax feature.", "type": "Bool", "defaultValue": false }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the depth values", "type": "Float", @@ -469,39 +469,39 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_heightmapScale" + "name": "m_heightmapScale" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture Map", "description": "Depthmap to create parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_heightmap" + "name": "m_heightmap" } }, { - "id": "algorithm", + "name": "algorithm", "displayName": "Algorithm", "description": "Select the algorithm to use for parallax mapping. 0: Basic, 1:Steep, 2:POM, 3:Relief, 4:Contact refinement", "type": "Uint", "defaultValue": 0, "connection":{ "type": "ShaderOption", - "id": "o_parallax_algorithm" + "name": "o_parallax_algorithm" } }, { - "id": "quality", + "name": "quality", "displayName": "Quality", "description": "Quality of parallax mapping. 0:Low, 1:Medium, 2:High, 3:Ultra", "type": "Uint", "defaultValue": 0, "connection":{ "type": "ShaderOption", - "id": "o_parallax_quality" + "name": "o_parallax_quality" } } ] diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.materialtype index 59b7af8439..74246f85db 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.materialtype @@ -5,7 +5,7 @@ "properties": { "settings": [ { - "id": "opacity", + "name": "opacity", "displayName": "Opacity", "description": "Opacity of the shadow effect.", "type": "Float", @@ -14,17 +14,17 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_opacity" + "name": "m_opacity" } }, { - "id": "shadeAll", + "name": "shadeAll", "displayName": "Shade All", "description": "Shades the entire geometry with the shadow color, not just what's in shadow. For debugging.", "type": "Bool", "connection": { "type": "ShaderOption", - "id": "o_shadeAll" + "name": "o_shadeAll" } } ] diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index 9b2c465352..bed3b69c4c 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -4,88 +4,88 @@ "version": 3, "groups": [ { - "id": "baseColor", + "name": "baseColor", "displayName": "Base Color", "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." }, { - "id": "metallic", + "name": "metallic", "displayName": "Metallic", "description": "Properties for configuring whether the surface is metallic or not." }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "description": "Properties for configuring how rough the surface appears." }, { - "id": "specularF0", + "name": "specularF0", "displayName": "Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, { - "id": "normal", + "name": "normal", "displayName": "Normal", "description": "Properties related to configuring surface normal." }, { - "id": "detailLayerGroup", + "name": "detailLayerGroup", "displayName": "Detail Layer", "description": "Properties for Fine Details Layer." }, { - "id": "detailUV", + "name": "detailUV", "displayName": "Detail Layer UV", "description": "Properties for modifying detail layer UV." }, { - "id": "anisotropy", + "name": "anisotropy", "displayName": "Anisotropic Material Response", "description": "How much is this material response anisotropic." }, { - "id": "occlusion", + "name": "occlusion", "displayName": "Occlusion", "description": "Properties for baked textures that represent geometric occlusion of light." }, { - "id": "emissive", + "name": "emissive", "displayName": "Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, { - "id": "subsurfaceScattering", + "name": "subsurfaceScattering", "displayName": "Subsurface Scattering", "description": "Properties for configuring subsurface scattering effects." }, { - "id": "clearCoat", + "name": "clearCoat", "displayName": "Clear Coat", "description": "Properties for configuring gloss clear coat" }, { - "id": "parallax", + "name": "parallax", "displayName": "Displacement", "description": "Properties for parallax effect produced by a height map." }, { - "id": "opacity", + "name": "opacity", "displayName": "Opacity", "description": "Properties for configuring the materials transparency." }, { - "id": "uv", + "name": "uv", "displayName": "UVs", "description": "Properties for configuring UV transforms." }, { // Note: this property group is used in the DiffuseGlobalIllumination pass and not by the main forward shader - "id": "irradiance", + "name": "irradiance", "displayName": "Irradiance", "description": "Properties for configuring the irradiance used in global illumination." }, { - "id": "general", + "name": "general", "displayName": "General Settings", "description": "General settings." } @@ -93,97 +93,97 @@ "properties": { "general": [ { - "id": "applySpecularAA", + "name": "applySpecularAA", "displayName": "Apply Specular AA", "description": "Whether to apply specular anti-aliasing in the shader.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_applySpecularAA" + "name": "o_applySpecularAA" } }, { - "id": "enableShadows", + "name": "enableShadows", "displayName": "Enable Shadows", "description": "Whether to use the shadow maps.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableShadows" + "name": "o_enableShadows" } }, { - "id": "enableDirectionalLights", + "name": "enableDirectionalLights", "displayName": "Enable Directional Lights", "description": "Whether to use directional lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableDirectionalLights" + "name": "o_enableDirectionalLights" } }, { - "id": "enablePunctualLights", + "name": "enablePunctualLights", "displayName": "Enable Punctual Lights", "description": "Whether to use punctual lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enablePunctualLights" + "name": "o_enablePunctualLights" } }, { - "id": "enableAreaLights", + "name": "enableAreaLights", "displayName": "Enable Area Lights", "description": "Whether to use area lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableAreaLights" + "name": "o_enableAreaLights" } }, { - "id": "enableIBL", + "name": "enableIBL", "displayName": "Enable IBL", "description": "Whether to use Image Based Lighting (IBL).", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableIBL" + "name": "o_enableIBL" } }, { - "id": "forwardPassIBLSpecular", + "name": "forwardPassIBLSpecular", "displayName": "Forward Pass IBL Specular", "description": "Whether to apply IBL specular in the forward pass.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_materialUseForwardPassIBLSpecular" + "name": "o_materialUseForwardPassIBLSpecular" } } ], "baseColor": [ { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_baseColor" + "name": "m_baseColor" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", @@ -192,28 +192,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_baseColorFactor" + "name": "m_baseColorFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_baseColorMap" + "name": "m_baseColorMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Base color map UV set", "type": "Enum", @@ -221,11 +221,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_baseColorMapUvIndex" + "name": "m_baseColorMapUvIndex" } }, { - "id": "textureBlendMode", + "name": "textureBlendMode", "displayName": "Texture Blend Mode", "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", @@ -233,13 +233,13 @@ "defaultValue": "Multiply", "connection": { "type": "ShaderOption", - "id": "o_baseColorTextureBlendMode" + "name": "o_baseColorTextureBlendMode" } } ], "metallic": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "This value is linear, black is non-metal and white means raw metal.", "type": "Float", @@ -248,28 +248,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_metallicFactor" + "name": "m_metallicFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_metallicMap" + "name": "m_metallicMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Metallic map UV set", "type": "Enum", @@ -277,30 +277,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_metallicMapUvIndex" + "name": "m_metallicMapUvIndex" } } ], "roughness": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_roughnessMap" + "name": "m_roughnessMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Roughness map UV set", "type": "Enum", @@ -308,12 +308,12 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_roughnessMapUvIndex" + "name": "m_roughnessMapUvIndex" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "lowerBound", + "name": "lowerBound", "displayName": "Lower Bound", "description": "The roughness value that corresponds to black in the texture.", "type": "Float", @@ -322,12 +322,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessLowerBound" + "name": "m_roughnessLowerBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "upperBound", + "name": "upperBound", "displayName": "Upper Bound", "description": "The roughness value that corresponds to white in the texture.", "type": "Float", @@ -336,12 +336,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessUpperBound" + "name": "m_roughnessUpperBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Controls the roughness value", "type": "Float", @@ -350,24 +350,24 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessFactor" + "name": "m_roughnessFactor" } } ], "anisotropy": [ { - "id": "enableAnisotropy", + "name": "enableAnisotropy", "displayName": "Enable Anisotropy", "description": "Enable anisotropic surface response for non uniform reflection along the axis", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_enableAnisotropy" + "name": "o_enableAnisotropy" } }, { - "id": "factor", + "name": "factor", "displayName": "Anisotropy Factor", "description": "Strength factor for the anisotropy: negative = along v, positive = along u", "type": "Float", @@ -376,11 +376,11 @@ "max": 0.95, "connection": { "type": "ShaderInput", - "id": "m_anisotropicFactor" + "name": "m_anisotropicFactor" } }, { - "id": "anisotropyAngle", + "name": "anisotropyAngle", "displayName": "Anisotropy Angle", "description": "Anisotropy direction of major reflection axis: 0 = 0 degrees, 1.0 = 180 degrees", "type": "Float", @@ -389,13 +389,13 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_anisotropicAngle" + "name": "m_anisotropicAngle" } } ], "specularF0": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.", "type": "Float", @@ -404,28 +404,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_specularF0Factor" + "name": "m_specularF0Factor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_specularF0Map" + "name": "m_specularF0Map" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Specular reflection map UV set", "type": "Enum", @@ -433,31 +433,31 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_specularF0MapUvIndex" + "name": "m_specularF0MapUvIndex" } }, // Consider moving this to the "general" group to be consistent with StandardMultilayerPBR { - "id": "enableMultiScatterCompensation", + "name": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", "description": "Whether to enable multiple scattering compensation.", "type": "Bool", "connection": { "type": "ShaderOption", - "id": "o_specularF0_enableMultiScatterCompensation" + "name": "o_specularF0_enableMultiScatterCompensation" } } ], "clearCoat": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable clear coat", "type": "Bool", "defaultValue": false }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the percentage of effect applied", "type": "Float", @@ -466,28 +466,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_clearCoatFactor" + "name": "m_clearCoatFactor" } }, { - "id": "influenceMap", + "name": "influenceMap", "displayName": " Influence Map", "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_clearCoatInfluenceMap" + "name": "m_clearCoatInfluenceMap" } }, { - "id": "useInfluenceMap", + "name": "useInfluenceMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "influenceMapUv", + "name": "influenceMapUv", "displayName": " UV", "description": "Strength factor map UV set", "type": "Enum", @@ -495,11 +495,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_clearCoatInfluenceMapUvIndex" + "name": "m_clearCoatInfluenceMapUvIndex" } }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "description": "Clear coat layer roughness", "type": "Float", @@ -508,28 +508,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_clearCoatRoughness" + "name": "m_clearCoatRoughness" } }, { - "id": "roughnessMap", + "name": "roughnessMap", "displayName": " Roughness Map", "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_clearCoatRoughnessMap" + "name": "m_clearCoatRoughnessMap" } }, { - "id": "useRoughnessMap", + "name": "useRoughnessMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { - "id": "roughnessMapUv", + "name": "roughnessMapUv", "displayName": " UV", "description": "Roughness map UV set", "type": "Enum", @@ -537,11 +537,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_clearCoatRoughnessMapUvIndex" + "name": "m_clearCoatRoughnessMapUvIndex" } }, { - "id": "normalStrength", + "name": "normalStrength", "displayName": "Normal Strength", "description": "Scales the impact of the clear coat normal map", "type": "Float", @@ -550,28 +550,28 @@ "max": 2.0, "connection": { "type": "ShaderInput", - "id": "m_clearCoatNormalStrength" + "name": "m_clearCoatNormalStrength" } }, { - "id": "normalMap", + "name": "normalMap", "displayName": "Normal Map", "description": "Normal map for clear coat layer, as top layer material clear coat doesn't affect by base layer normal map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_clearCoatNormalMap" + "name": "m_clearCoatNormalMap" } }, { - "id": "useNormalMap", + "name": "useNormalMap", "displayName": " Use Texture", "description": "Whether to use the normal map", "type": "Bool", "defaultValue": true }, { - "id": "normalMapUv", + "name": "normalMapUv", "displayName": " UV", "description": "Normal map UV set", "type": "Enum", @@ -579,30 +579,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_clearCoatNormalMapUvIndex" + "name": "m_clearCoatNormalMapUvIndex" } } ], "normal": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_normalMap" + "name": "m_normalMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Normal map UV set", "type": "Enum", @@ -610,33 +610,33 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_normalMapUvIndex" + "name": "m_normalMapUvIndex" } }, { - "id": "flipX", + "name": "flipX", "displayName": "Flip X Channel", "description": "Flip tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_flipNormalX" + "name": "m_flipNormalX" } }, { - "id": "flipY", + "name": "flipY", "displayName": "Flip Y Channel", "description": "Flip bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_flipNormalY" + "name": "m_flipNormalY" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the values", "type": "Float", @@ -645,13 +645,13 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_normalFactor" + "name": "m_normalFactor" } } ], "opacity": [ { - "id": "mode", + "name": "mode", "displayName": "Opacity Mode", "description": "Indicates the general approach how transparency is to be applied.", "type": "Enum", @@ -659,11 +659,11 @@ "defaultValue": "Opaque", "connection": { "type": "ShaderOption", - "id": "o_opacity_mode" + "name": "o_opacity_mode" } }, { - "id": "alphaSource", + "name": "alphaSource", "displayName": "Alpha Source", "description": "Indicates whether to get the opacity texture from the Base Color map (Packed) or from a separate greyscale texture (Split).", "type": "Enum", @@ -671,21 +671,21 @@ "defaultValue": "Packed", "connection": { "type": "ShaderOption", - "id": "o_opacity_source" + "name": "o_opacity_source" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface opacity.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_opacityMap" + "name": "m_opacityMap" } }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Opacity map UV set", "type": "Enum", @@ -693,11 +693,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_opacityMapUvIndex" + "name": "m_opacityMapUvIndex" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Factor for cutout threshold and blending", "type": "Float", @@ -706,17 +706,17 @@ "defaultValue": 0.5, "connection": { "type": "ShaderInput", - "id": "m_opacityFactor" + "name": "m_opacityFactor" } }, { - "id": "doubleSided", + "name": "doubleSided", "displayName": "Double-sided", "description": "Whether to render back-faces or just front-faces.", "type": "Bool" }, { - "id": "alphaAffectsSpecular", + "name": "alphaAffectsSpecular", "displayName": "Alpha affects specular", "description": "How much the alpha value should also affect specular reflection. This should be 0.0 for materials where light can transmit through their physical surface (like glass), but 1.0 when alpha determines the very presence of a surface (like hair or grass)", "type": "float", @@ -725,13 +725,13 @@ "defaultValue": 0.0, "connection": { "type": "ShaderInput", - "id": "m_opacityAffectsSpecularFactor" + "name": "m_opacityAffectsSpecularFactor" } } ], "uv": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", @@ -739,7 +739,7 @@ "defaultValue": [ 0.5, 0.5 ] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in U.", "type": "float", @@ -747,7 +747,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -755,7 +755,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -764,7 +764,7 @@ "max": 1.0 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -773,7 +773,7 @@ "max": 1.0 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -783,7 +783,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", @@ -793,24 +793,24 @@ ], "occlusion": [ { - "id": "diffuseTextureMap", + "name": "diffuseTextureMap", "displayName": "Diffuse AO", "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionMap" + "name": "m_diffuseOcclusionMap" } }, { - "id": "diffuseUseTexture", + "name": "diffuseUseTexture", "displayName": " Use Texture", "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { - "id": "diffuseTextureMapUv", + "name": "diffuseTextureMapUv", "displayName": " UV", "description": "Diffuse AO map UV set.", "type": "Enum", @@ -818,11 +818,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionMapUvIndex" + "name": "m_diffuseOcclusionMapUvIndex" } }, { - "id": "diffuseFactor", + "name": "diffuseFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Diffuse AO", "type": "Float", @@ -831,28 +831,28 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionFactor" + "name": "m_diffuseOcclusionFactor" } }, { - "id": "specularTextureMap", + "name": "specularTextureMap", "displayName": "Specular Cavity", "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionMap" + "name": "m_specularOcclusionMap" } }, { - "id": "specularUseTexture", + "name": "specularUseTexture", "displayName": " Use Texture", "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { - "id": "specularTextureMapUv", + "name": "specularTextureMapUv", "displayName": " UV", "description": "Specular Cavity map UV set.", "type": "Enum", @@ -860,11 +860,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionMapUvIndex" + "name": "m_specularOcclusionMapUvIndex" } }, { - "id": "specularFactor", + "name": "specularFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Specular Cavity", "type": "Float", @@ -873,20 +873,20 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionFactor" + "name": "m_specularOcclusionFactor" } } ], "emissive": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable the emissive group", "type": "Bool", "defaultValue": false }, { - "id": "unit", + "name": "unit", "displayName": "Units", "description": "The photometric units of the Intensity property.", "type": "Enum", @@ -894,18 +894,18 @@ "defaultValue": "Ev100" }, { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_emissiveColor" + "name": "m_emissiveColor" } }, { - "id": "intensity", + "name": "intensity", "displayName": "Intensity", "description": "The amount of energy emitted.", "type": "Float", @@ -916,24 +916,24 @@ "softMax": 16 }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_emissiveMap" + "name": "m_emissiveMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Emissive map UV set", "type": "Enum", @@ -941,30 +941,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_emissiveMapUvIndex" + "name": "m_emissiveMapUvIndex" } } ], "parallax": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Height Map", "description": "Displacement height map to create parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_heightmap" + "name": "m_heightmap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Height map UV set", "type": "Enum", @@ -972,11 +972,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_parallaxUvIndex" + "name": "m_parallaxUvIndex" } }, { - "id": "factor", + "name": "factor", "displayName": "Height Map Scale", "description": "The total height of the height map in local model units.", "type": "Float", @@ -985,11 +985,11 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_heightmapScale" + "name": "m_heightmapScale" } }, { - "id": "offset", + "name": "offset", "displayName": "Offset", "description": "Adjusts the overall displacement amount in local model units.", "type": "Float", @@ -998,11 +998,11 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_heightmapOffset" + "name": "m_heightmapOffset" } }, { - "id": "algorithm", + "name": "algorithm", "displayName": "Algorithm", "description": "Select the algorithm to use for parallax mapping.", "type": "Enum", @@ -1010,11 +1010,11 @@ "defaultValue": "POM", "connection": { "type": "ShaderOption", - "id": "o_parallax_algorithm" + "name": "o_parallax_algorithm" } }, { - "id": "quality", + "name": "quality", "displayName": "Quality", "description": "Quality of parallax mapping.", "type": "Enum", @@ -1022,46 +1022,46 @@ "defaultValue": "Low", "connection": { "type": "ShaderOption", - "id": "o_parallax_quality" + "name": "o_parallax_quality" } }, { - "id": "pdo", + "name": "pdo", "displayName": "Pixel Depth Offset", "description": "Enable PDO to offset the original pixel depths. This will affect any shaders using depth, for example, when receiving shadows.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_parallax_enablePixelDepthOffset" + "name": "o_parallax_enablePixelDepthOffset" } }, { - "id": "showClipping", + "name": "showClipping", "displayName": "Show Clipping", "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_parallax_highlightClipping" + "name": "o_parallax_highlightClipping" } } ], "subsurfaceScattering": [ { - "id": "enableSubsurfaceScattering", + "name": "enableSubsurfaceScattering", "displayName": "Subsurface Scattering", "description": "Enable subsurface scattering feature, this will disable metallic and parallax mapping property due to incompatibility", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_enableSubsurfaceScattering" + "name": "o_enableSubsurfaceScattering" } }, { - "id": "subsurfaceScatterFactor", + "name": "subsurfaceScatterFactor", "displayName": " Factor", "description": "Strength factor for scaling percentage of subsurface scattering effect applied", "type": "float", @@ -1070,28 +1070,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_subsurfaceScatteringFactor" + "name": "m_subsurfaceScatteringFactor" } }, { - "id": "influenceMap", + "name": "influenceMap", "displayName": " Influence Map", "description": "Texture for controlling the strength of subsurface scattering", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_subsurfaceScatteringInfluenceMap" + "name": "m_subsurfaceScatteringInfluenceMap" } }, { - "id": "useInfluenceMap", + "name": "useInfluenceMap", "displayName": " Use Influence Map", "description": "Whether to use the influence map.", "type": "Bool", "defaultValue": true }, { - "id": "influenceMapUv", + "name": "influenceMapUv", "displayName": " UV", "description": "Influence map UV set", "type": "Enum", @@ -1099,18 +1099,18 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_subsurfaceScatteringInfluenceMapUvIndex" + "name": "m_subsurfaceScatteringInfluenceMapUvIndex" } }, { - "id": "scatterColor", + "name": "scatterColor", "displayName": " Scatter color", "description": "Color of volume light traveled through", "type": "Color", "defaultValue": [ 1.0, 0.27, 0.13 ] }, { - "id": "scatterDistance", + "name": "scatterDistance", "displayName": " Scatter distance", "description": "How far light traveled inside the volume", "type": "float", @@ -1119,7 +1119,7 @@ "softMax": 20.0 }, { - "id": "quality", + "name": "quality", "displayName": " Quality", "description": "How much percent of sample will be used for each pixel, more samples improve quality and reduce artifacts, especially when the scatter distance is relatively large, but slow down computation time, 1.0 = full set 200 samples per pixel", "type": "float", @@ -1128,11 +1128,11 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_subsurfaceScatteringQuality" + "name": "m_subsurfaceScatteringQuality" } }, { - "id": "transmissionMode", + "name": "transmissionMode", "displayName": "Transmission", "description": "Algorithm used for calculating transmission", "type": "Enum", @@ -1140,11 +1140,11 @@ "defaultValue": "None", "connection": { "type": "ShaderOption", - "id": "o_transmission_mode" + "name": "o_transmission_mode" } }, { - "id": "thickness", + "name": "thickness", "displayName": " Thickness", "description": "Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel", "type": "float", @@ -1153,24 +1153,24 @@ "max": 1.0 }, { - "id": "thicknessMap", + "name": "thicknessMap", "displayName": " Thickness Map", "description": "Texture for controlling per pixel thickness", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_transmissionThicknessMap" + "name": "m_transmissionThicknessMap" } }, { - "id": "useThicknessMap", + "name": "useThicknessMap", "displayName": " Use Thickness Map", "description": "Whether to use the thickness map", "type": "Bool", "defaultValue": true }, { - "id": "thicknessMapUv", + "name": "thicknessMapUv", "displayName": " UV", "description": "Thickness map UV set", "type": "Enum", @@ -1178,18 +1178,18 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_transmissionThicknessMapUvIndex" + "name": "m_transmissionThicknessMapUvIndex" } }, { - "id": "transmissionTint", + "name": "transmissionTint", "displayName": " Transmission Tint", "description": "Color of the volume light traveling through", "type": "Color", "defaultValue": [ 1.0, 0.8, 0.6 ] }, { - "id": "transmissionPower", + "name": "transmissionPower", "displayName": " Power", "description": "How much transmitted light scatter radially ", "type": "float", @@ -1198,7 +1198,7 @@ "softMax": 20.0 }, { - "id": "transmissionDistortion", + "name": "transmissionDistortion", "displayName": " Distortion", "description": "How much light direction distorted towards surface normal", "type": "float", @@ -1207,7 +1207,7 @@ "max": 1.0 }, { - "id": "transmissionAttenuation", + "name": "transmissionAttenuation", "displayName": " Attenuation", "description": "How fast transmitted light fade with thickness", "type": "float", @@ -1216,7 +1216,7 @@ "softMax": 20.0 }, { - "id": "transmissionScale", + "name": "transmissionScale", "displayName": " Scale", "description": "Strength of transmission", "type": "float", @@ -1227,14 +1227,14 @@ ], "detailLayerGroup": [ { - "id": "enableDetailLayer", + "name": "enableDetailLayer", "displayName": "Enable Detail Layer", "description": "Enable detail layer for fine details and scratches", "type": "Bool", "defaultValue": false }, { - "id": "blendDetailFactor", + "name": "blendDetailFactor", "displayName": "Blend Factor", "description": "Scales the overall impact of the detail layer.", "type": "Float", @@ -1243,28 +1243,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_detail_blendFactor" + "name": "m_detail_blendFactor" } }, { - "id": "blendDetailMask", + "name": "blendDetailMask", "displayName": "Blend Mask", "description": "Detailed blend mask for application of the detail maps.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_detail_blendMask_texture" + "name": "m_detail_blendMask_texture" } }, { - "id": "enableDetailMaskTexture", + "name": "enableDetailMaskTexture", "displayName": " Use Texture", "description": "Enable detail blend mask", "type": "Bool", "defaultValue": true }, { - "id": "blendDetailMaskUv", + "name": "blendDetailMaskUv", "displayName": " Blend Mask UV", "description": "Which UV set to use for sampling the detail blend mask", "type": "Enum", @@ -1272,11 +1272,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_detail_blendMask_uvIndex" + "name": "m_detail_blendMask_uvIndex" } }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "Detail Map UVs", "description": "Which UV set to use for detail map sampling", "type": "Enum", @@ -1284,28 +1284,28 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_detail_allMapsUvIndex" + "name": "m_detail_allMapsUvIndex" } }, { - "id": "enableBaseColor", + "name": "enableBaseColor", "displayName": "Enable Base Color", "description": "Enable detail blending for base color", "type": "Bool", "defaultValue": false }, { - "id": "baseColorDetailMap", + "name": "baseColorDetailMap", "displayName": " Texture", "description": "Detailed Base Color Texture", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_detail_baseColor_texture" + "name": "m_detail_baseColor_texture" } }, { - "id": "baseColorDetailBlend", + "name": "baseColorDetailBlend", "displayName": " Blend Factor", "description": "How much to blend the detail layer into the base color.", "type": "Float", @@ -1314,18 +1314,18 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_detail_baseColor_factor" + "name": "m_detail_baseColor_factor" } }, { - "id": "enableNormals", + "name": "enableNormals", "displayName": "Enable Normal", "description": "Enable detail normal map to be used for fine detail normal such as scratches and small dents", "type": "Bool", "defaultValue": false }, { - "id": "normalDetailStrength", + "name": "normalDetailStrength", "displayName": " Factor", "description": "Strength factor for scaling the Detail Normal", "type": "Float", @@ -1334,45 +1334,45 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_detail_normal_factor" + "name": "m_detail_normal_factor" } }, { - "id": "normalDetailMap", + "name": "normalDetailMap", "displayName": " Texture", "description": "Detailed Normal map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_detail_normal_texture" + "name": "m_detail_normal_texture" } }, { - "id": "normalDetailFlipX", + "name": "normalDetailFlipX", "displayName": " Flip X Channel", "description": "Flip Detail tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_detail_normal_flipX" + "name": "m_detail_normal_flipX" } }, { - "id": "normalDetailFlipY", + "name": "normalDetailFlipY", "displayName": " Flip Y Channel", "description": "Flip Detail bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_detail_normal_flipY" + "name": "m_detail_normal_flipY" } } ], "detailUV": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", @@ -1380,7 +1380,7 @@ "defaultValue": [ 0.5, 0.5 ] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in V.", "type": "float", @@ -1388,7 +1388,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -1396,7 +1396,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -1405,7 +1405,7 @@ "max": 1.0 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -1414,7 +1414,7 @@ "max": 1.0 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -1424,7 +1424,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", @@ -1435,14 +1435,14 @@ "irradiance": [ // Note: this property group is used in the DiffuseGlobalIllumination pass and not by the main forward shader { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ] }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the irradiance color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index fe86576cf8..0969cc36e8 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -4,52 +4,52 @@ "version": 3, "groups": [ { - "id": "baseColor", + "name": "baseColor", "displayName": "Base Color", "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "description": "Properties for configuring how rough the surface appears." }, { - "id": "specularF0", + "name": "specularF0", "displayName": "Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, { - "id": "normal", + "name": "normal", "displayName": "Normal", "description": "Properties related to configuring surface normal." }, { - "id": "detailLayerGroup", + "name": "detailLayerGroup", "displayName": "Detail Layer", "description": "Properties for Fine Details Layer." }, { - "id": "detailUV", + "name": "detailUV", "displayName": "Detail Layer UV", "description": "Properties for modifying detail layer UV." }, { - "id": "occlusion", + "name": "occlusion", "displayName": "Occlusion", "description": "Properties for baked textures that represent geometric occlusion of light." }, { - "id": "subsurfaceScattering", + "name": "subsurfaceScattering", "displayName": "Subsurface Scattering", "description": "Properties for configuring subsurface scattering effects." }, { - "id": "wrinkleLayers", + "name": "wrinkleLayers", "displayName": "Wrinkle Layers", "description": "Properties for wrinkle maps to support morph animation, using vertex color blend weights." }, { - "id": "general", + "name": "general", "displayName": "General Settings", "description": "General settings." } @@ -57,86 +57,86 @@ "properties": { "general": [ { - "id": "applySpecularAA", + "name": "applySpecularAA", "displayName": "Apply Specular AA", "description": "Whether to apply specular anti-aliasing in the shader.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_applySpecularAA" + "name": "o_applySpecularAA" } }, { - "id": "enableShadows", + "name": "enableShadows", "displayName": "Enable Shadows", "description": "Whether to use the shadow maps.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableShadows" + "name": "o_enableShadows" } }, { - "id": "enableDirectionalLights", + "name": "enableDirectionalLights", "displayName": "Enable Directional Lights", "description": "Whether to use directional lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableDirectionalLights" + "name": "o_enableDirectionalLights" } }, { - "id": "enablePunctualLights", + "name": "enablePunctualLights", "displayName": "Enable Punctual Lights", "description": "Whether to use punctual lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enablePunctualLights" + "name": "o_enablePunctualLights" } }, { - "id": "enableAreaLights", + "name": "enableAreaLights", "displayName": "Enable Area Lights", "description": "Whether to use area lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableAreaLights" + "name": "o_enableAreaLights" } }, { - "id": "enableIBL", + "name": "enableIBL", "displayName": "Enable IBL", "description": "Whether to use Image Based Lighting (IBL).", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableIBL" + "name": "o_enableIBL" } } ], "baseColor": [ { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_baseColor" + "name": "m_baseColor" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", @@ -145,28 +145,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_baseColorFactor" + "name": "m_baseColorFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_baseColorMap" + "name": "m_baseColorMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Base color map UV set", "type": "Enum", @@ -174,11 +174,11 @@ "defaultValue": "Unwrapped", "connection": { "type": "ShaderInput", - "id": "m_baseColorMapUvIndex" + "name": "m_baseColorMapUvIndex" } }, { - "id": "textureBlendMode", + "name": "textureBlendMode", "displayName": "Texture Blend Mode", "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", @@ -186,30 +186,30 @@ "defaultValue": "Multiply", "connection": { "type": "ShaderOption", - "id": "o_baseColorTextureBlendMode" + "name": "o_baseColorTextureBlendMode" } } ], "roughness": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_roughnessMap" + "name": "m_roughnessMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Roughness map UV set", "type": "Enum", @@ -217,12 +217,12 @@ "defaultValue": "Unwrapped", "connection": { "type": "ShaderInput", - "id": "m_roughnessMapUvIndex" + "name": "m_roughnessMapUvIndex" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "lowerBound", + "name": "lowerBound", "displayName": "Lower Bound", "description": "The roughness value that corresponds to black in the texture.", "type": "Float", @@ -231,12 +231,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessLowerBound" + "name": "m_roughnessLowerBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "upperBound", + "name": "upperBound", "displayName": "Upper Bound", "description": "The roughness value that corresponds to white in the texture.", "type": "Float", @@ -245,12 +245,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessUpperBound" + "name": "m_roughnessUpperBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Controls the roughness value", "type": "Float", @@ -259,13 +259,13 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessFactor" + "name": "m_roughnessFactor" } } ], "specularF0": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.", "type": "Float", @@ -274,28 +274,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_specularF0Factor" + "name": "m_specularF0Factor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_specularF0Map" + "name": "m_specularF0Map" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Specular reflection map UV set", "type": "Enum", @@ -303,41 +303,41 @@ "defaultValue": "Unwrapped", "connection": { "type": "ShaderInput", - "id": "m_specularF0MapUvIndex" + "name": "m_specularF0MapUvIndex" } }, // Consider moving this to the "general" group to be consistent with StandardMultilayerPBR { - "id": "enableMultiScatterCompensation", + "name": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", "description": "Whether to enable multiple scattering compensation.", "type": "Bool", "connection": { "type": "ShaderOption", - "id": "o_specularF0_enableMultiScatterCompensation" + "name": "o_specularF0_enableMultiScatterCompensation" } } ], "normal": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_normalMap" + "name": "m_normalMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Normal map UV set", "type": "Enum", @@ -345,33 +345,33 @@ "defaultValue": "Unwrapped", "connection": { "type": "ShaderInput", - "id": "m_normalMapUvIndex" + "name": "m_normalMapUvIndex" } }, { - "id": "flipX", + "name": "flipX", "displayName": "Flip X Channel", "description": "Flip tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_flipNormalX" + "name": "m_flipNormalX" } }, { - "id": "flipY", + "name": "flipY", "displayName": "Flip Y Channel", "description": "Flip bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_flipNormalY" + "name": "m_flipNormalY" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the values", "type": "Float", @@ -380,30 +380,30 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_normalFactor" + "name": "m_normalFactor" } } ], "occlusion": [ { - "id": "diffuseTextureMap", + "name": "diffuseTextureMap", "displayName": "Diffuse AO", "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionMap" + "name": "m_diffuseOcclusionMap" } }, { - "id": "diffuseUseTexture", + "name": "diffuseUseTexture", "displayName": " Use Texture", "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { - "id": "diffuseTextureMapUv", + "name": "diffuseTextureMapUv", "displayName": " UV", "description": "Diffuse AO map UV set.", "type": "Enum", @@ -411,11 +411,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionMapUvIndex" + "name": "m_diffuseOcclusionMapUvIndex" } }, { - "id": "diffuseFactor", + "name": "diffuseFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Diffuse AO", "type": "Float", @@ -424,28 +424,28 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionFactor" + "name": "m_diffuseOcclusionFactor" } }, { - "id": "specularTextureMap", + "name": "specularTextureMap", "displayName": "Specular Cavity", "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionMap" + "name": "m_specularOcclusionMap" } }, { - "id": "specularUseTexture", + "name": "specularUseTexture", "displayName": " Use Texture", "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { - "id": "specularTextureMapUv", + "name": "specularTextureMapUv", "displayName": " UV", "description": "Specular Cavity map UV set.", "type": "Enum", @@ -453,11 +453,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionMapUvIndex" + "name": "m_specularOcclusionMapUvIndex" } }, { - "id": "specularFactor", + "name": "specularFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Specular Cavity", "type": "Float", @@ -466,24 +466,24 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionFactor" + "name": "m_specularOcclusionFactor" } } ], "subsurfaceScattering": [ { - "id": "enableSubsurfaceScattering", + "name": "enableSubsurfaceScattering", "displayName": "Subsurface Scattering", "description": "Enable subsurface scattering feature, this will disable metallic and parallax mapping property due to incompatibility", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_enableSubsurfaceScattering" + "name": "o_enableSubsurfaceScattering" } }, { - "id": "subsurfaceScatterFactor", + "name": "subsurfaceScatterFactor", "displayName": " Factor", "description": "Strength factor for scaling percentage of subsurface scattering effect applied", "type": "float", @@ -492,28 +492,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_subsurfaceScatteringFactor" + "name": "m_subsurfaceScatteringFactor" } }, { - "id": "influenceMap", + "name": "influenceMap", "displayName": " Influence Map", "description": "Texture for controlling the strength of subsurface scattering", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_subsurfaceScatteringInfluenceMap" + "name": "m_subsurfaceScatteringInfluenceMap" } }, { - "id": "useInfluenceMap", + "name": "useInfluenceMap", "displayName": " Use Influence Map", "description": "Whether to use the influence map.", "type": "Bool", "defaultValue": true }, { - "id": "influenceMapUv", + "name": "influenceMapUv", "displayName": " UV", "description": "Influence map UV set", "type": "Enum", @@ -521,18 +521,18 @@ "defaultValue": "Unwrapped", "connection": { "type": "ShaderInput", - "id": "m_subsurfaceScatteringInfluenceMapUvIndex" + "name": "m_subsurfaceScatteringInfluenceMapUvIndex" } }, { - "id": "scatterColor", + "name": "scatterColor", "displayName": " Scatter color", "description": "Color of volume light traveled through", "type": "Color", "defaultValue": [ 1.0, 0.27, 0.13 ] }, { - "id": "scatterDistance", + "name": "scatterDistance", "displayName": " Scatter distance", "description": "How far light traveled inside the volume", "type": "float", @@ -541,7 +541,7 @@ "softMax": 20.0 }, { - "id": "quality", + "name": "quality", "displayName": " Quality", "description": "How much percent of sample will be used for each pixel, more samples improve quality and reduce artifacts, especially when the scatter distance is relatively large, but slow down computation time, 1.0 = full set 200 samples per pixel", "type": "float", @@ -550,11 +550,11 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_subsurfaceScatteringQuality" + "name": "m_subsurfaceScatteringQuality" } }, { - "id": "transmissionMode", + "name": "transmissionMode", "displayName": "Transmission", "description": "Algorithm used for calculating transmission", "type": "Enum", @@ -562,11 +562,11 @@ "defaultValue": "None", "connection": { "type": "ShaderOption", - "id": "o_transmission_mode" + "name": "o_transmission_mode" } }, { - "id": "thickness", + "name": "thickness", "displayName": " Thickness", "description": "Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel", "type": "float", @@ -575,24 +575,24 @@ "max": 1.0 }, { - "id": "thicknessMap", + "name": "thicknessMap", "displayName": " Thickness Map", "description": "Texture for controlling per pixel thickness", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_transmissionThicknessMap" + "name": "m_transmissionThicknessMap" } }, { - "id": "useThicknessMap", + "name": "useThicknessMap", "displayName": " Use Thickness Map", "description": "Whether to use the thickness map", "type": "Bool", "defaultValue": true }, { - "id": "thicknessMapUv", + "name": "thicknessMapUv", "displayName": " UV", "description": "Thickness map UV set", "type": "Enum", @@ -600,18 +600,18 @@ "defaultValue": "Unwrapped", "connection": { "type": "ShaderInput", - "id": "m_transmissionThicknessMapUvIndex" + "name": "m_transmissionThicknessMapUvIndex" } }, { - "id": "transmissionTint", + "name": "transmissionTint", "displayName": " Transmission Tint", "description": "Color of the volume light traveling through", "type": "Color", "defaultValue": [ 1.0, 0.8, 0.6 ] }, { - "id": "transmissionPower", + "name": "transmissionPower", "displayName": " Power", "description": "How much transmitted light scatter radially ", "type": "float", @@ -620,7 +620,7 @@ "softMax": 20.0 }, { - "id": "transmissionDistortion", + "name": "transmissionDistortion", "displayName": " Distortion", "description": "How much light direction distorted towards surface normal", "type": "float", @@ -629,7 +629,7 @@ "max": 1.0 }, { - "id": "transmissionAttenuation", + "name": "transmissionAttenuation", "displayName": " Attenuation", "description": "How fast transmitted light fade with thickness", "type": "float", @@ -638,7 +638,7 @@ "softMax": 20.0 }, { - "id": "transmissionScale", + "name": "transmissionScale", "displayName": " Scale", "description": "Strength of transmission", "type": "float", @@ -649,14 +649,14 @@ ], "wrinkleLayers": [ { - "id": "enable", + "name": "enable", "displayName": "Enable Wrinkle Layers", "description": "Enable wrinkle layers for morph animations, using vertex color blend weights.", "type": "Bool", "defaultValue": false }, { - "id": "count", + "name": "count", "displayName": "Number Of Layers", "description": "The number of wrinkle map layers to use. The blend values come from the 'COLOR0' vertex stream, where R/G/B/A correspond to wrinkle layers 1/2/3/4 respectively.", "type": "UInt", @@ -665,109 +665,109 @@ "max": 4 }, { - "id": "showBlendValues", + "name": "showBlendValues", "displayName": "Show Blend Values", "description": "Enable a debug mode that draws the blend values as red, green, blue, and white overlays.", "type": "Bool", "defaultValue": false }, { - "id": "enableBaseColor", + "name": "enableBaseColor", "displayName": "Enable Base Color Maps", "description": "Enable support for blending the base color according to morph animations.", "type": "Bool", "defaultValue": false }, { - "id": "baseColorMap1", + "name": "baseColorMap1", "displayName": " Base Color 1", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_wrinkle_baseColor_texture1" + "name": "m_wrinkle_baseColor_texture1" } }, { - "id": "baseColorMap2", + "name": "baseColorMap2", "displayName": " Base Color 2", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_wrinkle_baseColor_texture2" + "name": "m_wrinkle_baseColor_texture2" } }, { - "id": "baseColorMap3", + "name": "baseColorMap3", "displayName": " Base Color 3", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_wrinkle_baseColor_texture3" + "name": "m_wrinkle_baseColor_texture3" } }, { - "id": "baseColorMap4", + "name": "baseColorMap4", "displayName": " Base Color 4", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_wrinkle_baseColor_texture4" + "name": "m_wrinkle_baseColor_texture4" } }, { - "id": "enableNormal", + "name": "enableNormal", "displayName": "Enable Normal Maps", "description": "Enable support for blending the normal maps according to morph animations.", "type": "Bool", "defaultValue": false }, { - "id": "normalMap1", + "name": "normalMap1", "displayName": " Normals 1", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_wrinkle_normal_texture1" + "name": "m_wrinkle_normal_texture1" } }, { - "id": "normalMap2", + "name": "normalMap2", "displayName": " Normals 2", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_wrinkle_normal_texture2" + "name": "m_wrinkle_normal_texture2" } }, { - "id": "normalMap3", + "name": "normalMap3", "displayName": " Normals 3", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_wrinkle_normal_texture3" + "name": "m_wrinkle_normal_texture3" } }, { - "id": "normalMap4", + "name": "normalMap4", "displayName": " Normals 4", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_wrinkle_normal_texture4" + "name": "m_wrinkle_normal_texture4" } } ], "detailLayerGroup": [ { - "id": "enableDetailLayer", + "name": "enableDetailLayer", "displayName": "Enable Detail Layer", "description": "Enable detail layer for fine details and scratches", "type": "Bool", "defaultValue": false }, { - "id": "blendDetailFactor", + "name": "blendDetailFactor", "displayName": "Blend Factor", "description": "Scales the overall impact of the detail layer.", "type": "Float", @@ -776,28 +776,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_detail_blendFactor" + "name": "m_detail_blendFactor" } }, { - "id": "blendDetailMask", + "name": "blendDetailMask", "displayName": "Blend Mask", "description": "Detailed blend mask for application of the detail maps.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_detail_blendMask_texture" + "name": "m_detail_blendMask_texture" } }, { - "id": "enableDetailMaskTexture", + "name": "enableDetailMaskTexture", "displayName": " Use Texture", "description": "Enable detail blend mask", "type": "Bool", "defaultValue": true }, { - "id": "blendDetailMaskUv", + "name": "blendDetailMaskUv", "displayName": " Blend Mask UV", "description": "Which UV set to use for sampling the detail blend mask", "type": "Enum", @@ -805,11 +805,11 @@ "defaultValue": "Unwrapped", "connection": { "type": "ShaderInput", - "id": "m_detail_blendMask_uvIndex" + "name": "m_detail_blendMask_uvIndex" } }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "Detail Map UVs", "description": "Which UV set to use for detail map sampling", "type": "Enum", @@ -817,28 +817,28 @@ "defaultValue": "Unwrapped", "connection": { "type": "ShaderInput", - "id": "m_detail_allMapsUvIndex" + "name": "m_detail_allMapsUvIndex" } }, { - "id": "enableBaseColor", + "name": "enableBaseColor", "displayName": "Enable Base Color", "description": "Enable detail blending for base color", "type": "Bool", "defaultValue": false }, { - "id": "baseColorDetailMap", + "name": "baseColorDetailMap", "displayName": " Texture", "description": "Detailed Base Color Texture", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_detail_baseColor_texture" + "name": "m_detail_baseColor_texture" } }, { - "id": "baseColorDetailBlend", + "name": "baseColorDetailBlend", "displayName": " Blend Factor", "description": "How much to blend the detail layer into the base color.", "type": "Float", @@ -847,18 +847,18 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_detail_baseColor_factor" + "name": "m_detail_baseColor_factor" } }, { - "id": "enableNormals", + "name": "enableNormals", "displayName": "Enable Normal", "description": "Enable detail normal map to be used for fine detail normal such as scratches and small dents", "type": "Bool", "defaultValue": false }, { - "id": "normalDetailStrength", + "name": "normalDetailStrength", "displayName": " Factor", "description": "Strength factor for scaling the Detail Normal", "type": "Float", @@ -867,45 +867,45 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_detail_normal_factor" + "name": "m_detail_normal_factor" } }, { - "id": "normalDetailMap", + "name": "normalDetailMap", "displayName": " Texture", "description": "Detailed Normal map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_detail_normal_texture" + "name": "m_detail_normal_texture" } }, { - "id": "normalDetailFlipX", + "name": "normalDetailFlipX", "displayName": " Flip X Channel", "description": "Flip Detail tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_detail_normal_flipX" + "name": "m_detail_normal_flipX" } }, { - "id": "normalDetailFlipY", + "name": "normalDetailFlipY", "displayName": " Flip Y Channel", "description": "Flip Detail bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_detail_normal_flipY" + "name": "m_detail_normal_flipY" } } ], "detailUV": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", @@ -913,7 +913,7 @@ "defaultValue": [ 0.5, 0.5 ] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in V.", "type": "float", @@ -921,7 +921,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -929,7 +929,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -938,7 +938,7 @@ "max": 1.0 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -947,7 +947,7 @@ "max": 1.0 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -957,7 +957,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index bccb530eb4..5fa0dcb217 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -4,28 +4,28 @@ "version": 3, "groups": [ { - "id": "blend", + "name": "blend", "displayName": "Blend Settings", "description": "Properties for configuring how layers are blended together." }, { - "id": "parallax", + "name": "parallax", "displayName": "Parallax Settings", "description": "Properties for configuring the parallax effect, applied to all layers." }, { - "id": "uv", + "name": "uv", "displayName": "UVs", "description": "Properties for configuring UV transforms for the entire material, including the blend masks." }, { // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader - "id": "irradiance", + "name": "irradiance", "displayName": "Irradiance", "description": "Properties for configuring the irradiance used in global illumination." }, { - "id": "general", + "name": "general", "displayName": "General Settings", "description": "General settings." }, @@ -33,52 +33,52 @@ // Layer 1 Groups //############################################################################################## { - "id": "layer1_baseColor", + "name": "layer1_baseColor", "displayName": "Layer 1: Base Color", "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." }, { - "id": "layer1_metallic", + "name": "layer1_metallic", "displayName": "Layer 1: Metallic", "description": "Properties for configuring whether the surface is metallic or not." }, { - "id": "layer1_roughness", + "name": "layer1_roughness", "displayName": "Layer 1: Roughness", "description": "Properties for configuring how rough the surface appears." }, { - "id": "layer1_specularF0", + "name": "layer1_specularF0", "displayName": "Layer 1: Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, { - "id": "layer1_normal", + "name": "layer1_normal", "displayName": "Layer 1: Normal", "description": "Properties related to configuring surface normal." }, { - "id": "layer1_occlusion", + "name": "layer1_occlusion", "displayName": "Layer 1: Occlusion", "description": "Properties for baked textures for diffuse and specular occlusion of ambient lighting." }, { - "id": "layer1_emissive", + "name": "layer1_emissive", "displayName": "Layer 1: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, { - "id": "layer1_clearCoat", + "name": "layer1_clearCoat", "displayName": "Layer 1: Clear Coat", "description": "Properties for configuring gloss clear coat" }, { - "id": "layer1_parallax", + "name": "layer1_parallax", "displayName": "Layer 1: Displacement", "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { - "id": "layer1_uv", + "name": "layer1_uv", "displayName": "Layer 1: UVs", "description": "Properties for configuring UV transforms." }, @@ -86,52 +86,52 @@ // Layer 2 Groups //############################################################################################## { - "id": "layer2_baseColor", + "name": "layer2_baseColor", "displayName": "Layer 2: Base Color", "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." }, { - "id": "layer2_metallic", + "name": "layer2_metallic", "displayName": "Layer 2: Metallic", "description": "Properties for configuring whether the surface is metallic or not." }, { - "id": "layer2_roughness", + "name": "layer2_roughness", "displayName": "Layer 2: Roughness", "description": "Properties for configuring how rough the surface appears." }, { - "id": "layer2_specularF0", + "name": "layer2_specularF0", "displayName": "Layer 2: Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, { - "id": "layer2_normal", + "name": "layer2_normal", "displayName": "Layer 2: Normal", "description": "Properties related to configuring surface normal." }, { - "id": "layer2_occlusion", + "name": "layer2_occlusion", "displayName": "Layer 2: Occlusion", "description": "Properties for baked textures for diffuse and specular occlusion of ambient lighting." }, { - "id": "layer2_emissive", + "name": "layer2_emissive", "displayName": "Layer 2: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, { - "id": "layer2_clearCoat", + "name": "layer2_clearCoat", "displayName": "Layer 2: Clear Coat", "description": "Properties for configuring gloss clear coat" }, { - "id": "layer2_parallax", + "name": "layer2_parallax", "displayName": "Layer 2: Displacement", "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { - "id": "layer2_uv", + "name": "layer2_uv", "displayName": "Layer 2: UVs", "description": "Properties for configuring UV transforms." }, @@ -139,52 +139,52 @@ // Layer 3 Groups //############################################################################################## { - "id": "layer3_baseColor", + "name": "layer3_baseColor", "displayName": "Layer 3: Base Color", "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." }, { - "id": "layer3_metallic", + "name": "layer3_metallic", "displayName": "Layer 3: Metallic", "description": "Properties for configuring whether the surface is metallic or not." }, { - "id": "layer3_roughness", + "name": "layer3_roughness", "displayName": "Layer 3: Roughness", "description": "Properties for configuring how rough the surface appears." }, { - "id": "layer3_specularF0", + "name": "layer3_specularF0", "displayName": "Layer 3: Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, { - "id": "layer3_normal", + "name": "layer3_normal", "displayName": "Layer 3: Normal", "description": "Properties related to configuring surface normal." }, { - "id": "layer3_occlusion", + "name": "layer3_occlusion", "displayName": "Layer 3: Occlusion", "description": "Properties for baked textures for diffuse and specular occlusion of ambient lighting." }, { - "id": "layer3_emissive", + "name": "layer3_emissive", "displayName": "Layer 3: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, { - "id": "layer3_clearCoat", + "name": "layer3_clearCoat", "displayName": "Layer 3: Clear Coat", "description": "Properties for configuring gloss clear coat" }, { - "id": "layer3_parallax", + "name": "layer3_parallax", "displayName": "Layer 3: Displacement", "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { - "id": "layer3_uv", + "name": "layer3_uv", "displayName": "Layer 3: UVs", "description": "Properties for configuring UV transforms." } @@ -195,118 +195,118 @@ //############################################################################################## "general": [ { - "id": "applySpecularAA", + "name": "applySpecularAA", "displayName": "Apply Specular AA", "description": "Whether to apply specular anti-aliasing in the shader.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_applySpecularAA" + "name": "o_applySpecularAA" } }, { - "id": "enableMultiScatterCompensation", + "name": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", "description": "Whether to enable multiple scattering compensation.", "type": "Bool", "connection": { "type": "ShaderOption", - "id": "o_specularF0_enableMultiScatterCompensation" + "name": "o_specularF0_enableMultiScatterCompensation" } }, { - "id": "enableShadows", + "name": "enableShadows", "displayName": "Enable Shadows", "description": "Whether to use the shadow maps.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableShadows" + "name": "o_enableShadows" } }, { - "id": "enableDirectionalLights", + "name": "enableDirectionalLights", "displayName": "Enable Directional Lights", "description": "Whether to use directional lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableDirectionalLights" + "name": "o_enableDirectionalLights" } }, { - "id": "enablePunctualLights", + "name": "enablePunctualLights", "displayName": "Enable Punctual Lights", "description": "Whether to use punctual lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enablePunctualLights" + "name": "o_enablePunctualLights" } }, { - "id": "enableAreaLights", + "name": "enableAreaLights", "displayName": "Enable Area Lights", "description": "Whether to use area lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableAreaLights" + "name": "o_enableAreaLights" } }, { - "id": "enableIBL", + "name": "enableIBL", "displayName": "Enable IBL", "description": "Whether to use Image Based Lighting (IBL).", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableIBL" + "name": "o_enableIBL" } }, { - "id": "forwardPassIBLSpecular", + "name": "forwardPassIBLSpecular", "displayName": "Forward Pass IBL Specular", "description": "Whether to apply IBL specular in the forward pass.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_materialUseForwardPassIBLSpecular" + "name": "o_materialUseForwardPassIBLSpecular" } } ], "blend": [ { - "id": "enableLayer2", + "name": "enableLayer2", "displayName": "Enable Layer 2", "description": "Whether to enable layer 2.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_layer2_enabled" + "name": "o_layer2_enabled" } }, { - "id": "enableLayer3", + "name": "enableLayer3", "displayName": "Enable Layer 3", "description": "Whether to enable layer 3.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_layer3_enabled" + "name": "o_layer3_enabled" } }, { - "id": "blendSource", + "name": "blendSource", "displayName": "Blend Source", "description": "The source to use for defining the blend mask. Note VertexColors mode will still use the texture as a fallback if the mesh does not have a COLOR0 stream.", "type": "Enum", @@ -314,22 +314,22 @@ "defaultValue": "BlendMaskTexture", "connection": { "type": "ShaderOption", - "id": "o_layerBlendSource" + "name": "o_layerBlendSource" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Blend Mask Texture", "description": "RGB image where each channel is the blend mask for one of the three available layers.", "type": "Image", "defaultValue": "Textures/DefaultBlendMask_layers.png", "connection": { "type": "ShaderInput", - "id": "m_blendMaskTexture" + "name": "m_blendMaskTexture" } }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "Blend Mask UV", "description": "Blend Mask UV set.", "type": "Enum", @@ -337,11 +337,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_blendMaskUvIndex" + "name": "m_blendMaskUvIndex" } }, { - "id": "displacementBlendDistance", + "name": "displacementBlendDistance", "displayName": "Blend Distance", "description": "Adjusts how smoothly to transition between layers when displacement blending is enabled.", "type": "Float", @@ -351,11 +351,11 @@ "step": 0.001, "connection": { "type": "ShaderInput", - "id": "m_displacementBlendDistance" + "name": "m_displacementBlendDistance" } }, { - "id": "debugDrawMode", + "name": "debugDrawMode", "displayName": "Debug Draw Mode", "description": "Enables various debug view features.", "type": "Enum", @@ -363,7 +363,7 @@ "defaultValue": "None", "connection": { "type": "ShaderOption", - "id": "o_debugDrawMode" + "name": "o_debugDrawMode" } } ], @@ -372,14 +372,14 @@ // Note parallax is enabled by default so that as soon as a user hooks up displacement settings they will see some parallax applied. // The functor that controls parallax will set o_parallax_feature_enabled=false when all the individual layers have no displacement, so // a default value of true here will not have any initial impact on performance. - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Whether to enable the parallax feature for this material.", "type": "Bool", "defaultValue": true }, { - "id": "parallaxUv", + "name": "parallaxUv", "displayName": "UV", "description": "UV set that supports parallax mapping.", "type": "Enum", @@ -387,11 +387,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_parallaxUvIndex" + "name": "m_parallaxUvIndex" } }, { - "id": "algorithm", + "name": "algorithm", "displayName": "Algorithm", "description": "Select the algorithm to use for parallax mapping.", "type": "Enum", @@ -399,11 +399,11 @@ "defaultValue": "POM", "connection": { "type": "ShaderOption", - "id": "o_parallax_algorithm" + "name": "o_parallax_algorithm" } }, { - "id": "quality", + "name": "quality", "displayName": "Quality", "description": "Quality of parallax mapping.", "type": "Enum", @@ -411,35 +411,35 @@ "defaultValue": "Low", "connection": { "type": "ShaderOption", - "id": "o_parallax_quality" + "name": "o_parallax_quality" } }, { - "id": "pdo", + "name": "pdo", "displayName": "Pixel Depth Offset", "description": "Enable PDO to offset the original pixel depths. This will affect any shaders using depth, for example, when receiving shadows.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_parallax_enablePixelDepthOffset" + "name": "o_parallax_enablePixelDepthOffset" } }, { - "id": "showClipping", + "name": "showClipping", "displayName": "Show Clipping", "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_parallax_highlightClipping" + "name": "o_parallax_highlightClipping" } } ], "uv": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", @@ -447,7 +447,7 @@ "defaultValue": [ 0.5, 0.5 ] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in V.", "type": "float", @@ -455,7 +455,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -463,7 +463,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -473,7 +473,7 @@ "step": 0.001 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -483,7 +483,7 @@ "step": 0.001 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -493,7 +493,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", @@ -504,14 +504,14 @@ "irradiance": [ // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ] }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the irradiance color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", @@ -525,18 +525,18 @@ //############################################################################################## "layer1_baseColor": [ { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_layer1_m_baseColor" + "name": "m_layer1_m_baseColor" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", @@ -545,28 +545,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_baseColorFactor" + "name": "m_layer1_m_baseColorFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_baseColorMap" + "name": "m_layer1_m_baseColorMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Base color map UV set", "type": "Enum", @@ -574,11 +574,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_baseColorMapUvIndex" + "name": "m_layer1_m_baseColorMapUvIndex" } }, { - "id": "textureBlendMode", + "name": "textureBlendMode", "displayName": "Texture Blend Mode", "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", @@ -586,13 +586,13 @@ "defaultValue": "Multiply", "connection": { "type": "ShaderOption", - "id": "o_layer1_o_baseColorTextureBlendMode" + "name": "o_layer1_o_baseColorTextureBlendMode" } } ], "layer1_metallic": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "This value is linear, black is non-metal and white means raw metal.", "type": "Float", @@ -601,28 +601,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_metallicFactor" + "name": "m_layer1_m_metallicFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_metallicMap" + "name": "m_layer1_m_metallicMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Metallic map UV set", "type": "Enum", @@ -630,30 +630,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_metallicMapUvIndex" + "name": "m_layer1_m_metallicMapUvIndex" } } ], "layer1_roughness": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_roughnessMap" + "name": "m_layer1_m_roughnessMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Roughness map UV set", "type": "Enum", @@ -661,12 +661,12 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_roughnessMapUvIndex" + "name": "m_layer1_m_roughnessMapUvIndex" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "lowerBound", + "name": "lowerBound", "displayName": "Lower Bound", "description": "The roughness value that corresponds to black in the texture.", "type": "Float", @@ -675,12 +675,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_roughnessLowerBound" + "name": "m_layer1_m_roughnessLowerBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "upperBound", + "name": "upperBound", "displayName": "Upper Bound", "description": "The roughness value that corresponds to white in the texture.", "type": "Float", @@ -689,12 +689,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_roughnessUpperBound" + "name": "m_layer1_m_roughnessUpperBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Controls the roughness value", "type": "Float", @@ -703,13 +703,13 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_roughnessFactor" + "name": "m_layer1_m_roughnessFactor" } } ], "layer1_specularF0": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.", "type": "Float", @@ -718,28 +718,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_specularF0Factor" + "name": "m_layer1_m_specularF0Factor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_specularF0Map" + "name": "m_layer1_m_specularF0Map" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Specular reflection map UV set", "type": "Enum", @@ -747,30 +747,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_specularF0MapUvIndex" + "name": "m_layer1_m_specularF0MapUvIndex" } } ], "layer1_normal": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_normalMap" + "name": "m_layer1_m_normalMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Normal map UV set", "type": "Enum", @@ -778,33 +778,33 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_normalMapUvIndex" + "name": "m_layer1_m_normalMapUvIndex" } }, { - "id": "flipX", + "name": "flipX", "displayName": "Flip X Channel", "description": "Flip tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_flipNormalX" + "name": "m_layer1_m_flipNormalX" } }, { - "id": "flipY", + "name": "flipY", "displayName": "Flip Y Channel", "description": "Flip bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_flipNormalY" + "name": "m_layer1_m_flipNormalY" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the values", "type": "Float", @@ -813,20 +813,20 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_normalFactor" + "name": "m_layer1_m_normalFactor" } } ], "layer1_clearCoat": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable clear coat", "type": "Bool", "defaultValue": false }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the percentage of effect applied", "type": "Float", @@ -835,28 +835,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatFactor" + "name": "m_layer1_m_clearCoatFactor" } }, { - "id": "influenceMap", + "name": "influenceMap", "displayName": " Influence Map", "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatInfluenceMap" + "name": "m_layer1_m_clearCoatInfluenceMap" } }, { - "id": "useInfluenceMap", + "name": "useInfluenceMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "influenceMapUv", + "name": "influenceMapUv", "displayName": " UV", "description": "Strength factor map UV set", "type": "Enum", @@ -864,11 +864,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatInfluenceMapUvIndex" + "name": "m_layer1_m_clearCoatInfluenceMapUvIndex" } }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "description": "Clear coat layer roughness", "type": "Float", @@ -877,28 +877,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatRoughness" + "name": "m_layer1_m_clearCoatRoughness" } }, { - "id": "roughnessMap", + "name": "roughnessMap", "displayName": " Roughness Map", "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatRoughnessMap" + "name": "m_layer1_m_clearCoatRoughnessMap" } }, { - "id": "useRoughnessMap", + "name": "useRoughnessMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { - "id": "roughnessMapUv", + "name": "roughnessMapUv", "displayName": " UV", "description": "Roughness map UV set", "type": "Enum", @@ -906,11 +906,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatRoughnessMapUvIndex" + "name": "m_layer1_m_clearCoatRoughnessMapUvIndex" } }, { - "id": "normalStrength", + "name": "normalStrength", "displayName": "Normal Strength", "description": "Scales the impact of the clear coat normal map", "type": "Float", @@ -919,28 +919,28 @@ "max": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatNormalStrength" + "name": "m_layer1_m_clearCoatNormalStrength" } }, { - "id": "normalMap", + "name": "normalMap", "displayName": "Normal Map", "description": "Normal map for clear coat layer, as top layer material clear coat doesn't affect by base layer normal map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatNormalMap" + "name": "m_layer1_m_clearCoatNormalMap" } }, { - "id": "useNormalMap", + "name": "useNormalMap", "displayName": " Use Texture", "description": "Whether to use the normal map", "type": "Bool", "defaultValue": true }, { - "id": "normalMapUv", + "name": "normalMapUv", "displayName": " UV", "description": "Normal map UV set", "type": "Enum", @@ -948,30 +948,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_clearCoatNormalMapUvIndex" + "name": "m_layer1_m_clearCoatNormalMapUvIndex" } } ], "layer1_occlusion": [ { - "id": "diffuseTextureMap", + "name": "diffuseTextureMap", "displayName": "Diffuse AO", "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_diffuseOcclusionMap" + "name": "m_layer1_m_diffuseOcclusionMap" } }, { - "id": "diffuseUseTexture", + "name": "diffuseUseTexture", "displayName": " Use Texture", "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { - "id": "diffuseTextureMapUv", + "name": "diffuseTextureMapUv", "displayName": " UV", "description": "Diffuse AO map UV set.", "type": "Enum", @@ -979,11 +979,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_diffuseOcclusionMapUvIndex" + "name": "m_layer1_m_diffuseOcclusionMapUvIndex" } }, { - "id": "diffuseFactor", + "name": "diffuseFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Diffuse AO", "type": "Float", @@ -992,28 +992,28 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_diffuseOcclusionFactor" + "name": "m_layer1_m_diffuseOcclusionFactor" } }, { - "id": "specularTextureMap", + "name": "specularTextureMap", "displayName": "Specular Cavity", "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_specularOcclusionMap" + "name": "m_layer1_m_specularOcclusionMap" } }, { - "id": "specularUseTexture", + "name": "specularUseTexture", "displayName": " Use Texture", "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { - "id": "specularTextureMapUv", + "name": "specularTextureMapUv", "displayName": " UV", "description": "Specular Cavity map UV set.", "type": "Enum", @@ -1021,11 +1021,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_specularOcclusionMapUvIndex" + "name": "m_layer1_m_specularOcclusionMapUvIndex" } }, { - "id": "specularFactor", + "name": "specularFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Specular Cavity", "type": "Float", @@ -1034,20 +1034,20 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_specularOcclusionFactor" + "name": "m_layer1_m_specularOcclusionFactor" } } ], "layer1_emissive": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable the emissive group", "type": "Bool", "defaultValue": false }, { - "id": "unit", + "name": "unit", "displayName": "Units", "description": "The photometric units of the Intensity property.", "type": "Enum", @@ -1055,18 +1055,18 @@ "defaultValue": "Ev100" }, { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_layer1_m_emissiveColor" + "name": "m_layer1_m_emissiveColor" } }, { - "id": "intensity", + "name": "intensity", "displayName": "Intensity", "description": "The amount of energy emitted.", "type": "Float", @@ -1077,24 +1077,24 @@ "softMax": 16 }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_emissiveMap" + "name": "m_layer1_m_emissiveMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Emissive map UV set", "type": "Enum", @@ -1102,30 +1102,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_emissiveMapUvIndex" + "name": "m_layer1_m_emissiveMapUvIndex" } } ], "layer1_parallax": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Height Map", "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_heightmap" + "name": "m_layer1_m_heightmap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { - "id": "factor", + "name": "factor", "displayName": "Scale", "description": "The total height of the height map in local model units.", "type": "Float", @@ -1134,11 +1134,11 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_heightmapScale" + "name": "m_layer1_m_heightmapScale" } }, { - "id": "offset", + "name": "offset", "displayName": "Offset", "description": "Adjusts the overall displacement amount in local model units.", "type": "Float", @@ -1147,13 +1147,13 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_heightmapOffset" + "name": "m_layer1_m_heightmapOffset" } } ], "layer1_uv": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", @@ -1161,7 +1161,7 @@ "defaultValue": [ 0.5, 0.5 ] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in V.", "type": "float", @@ -1169,7 +1169,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -1177,7 +1177,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -1187,7 +1187,7 @@ "step": 0.001 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -1197,7 +1197,7 @@ "step": 0.001 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -1207,7 +1207,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", @@ -1220,18 +1220,18 @@ //############################################################################################## "layer2_baseColor": [ { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_layer2_m_baseColor" + "name": "m_layer2_m_baseColor" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", @@ -1240,28 +1240,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_baseColorFactor" + "name": "m_layer2_m_baseColorFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_baseColorMap" + "name": "m_layer2_m_baseColorMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Base color map UV set", "type": "Enum", @@ -1269,11 +1269,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_baseColorMapUvIndex" + "name": "m_layer2_m_baseColorMapUvIndex" } }, { - "id": "textureBlendMode", + "name": "textureBlendMode", "displayName": "Texture Blend Mode", "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", @@ -1281,13 +1281,13 @@ "defaultValue": "Multiply", "connection": { "type": "ShaderOption", - "id": "o_layer2_o_baseColorTextureBlendMode" + "name": "o_layer2_o_baseColorTextureBlendMode" } } ], "layer2_metallic": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "This value is linear, black is non-metal and white means raw metal.", "type": "Float", @@ -1296,28 +1296,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_metallicFactor" + "name": "m_layer2_m_metallicFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_metallicMap" + "name": "m_layer2_m_metallicMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Metallic map UV set", "type": "Enum", @@ -1325,30 +1325,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_metallicMapUvIndex" + "name": "m_layer2_m_metallicMapUvIndex" } } ], "layer2_roughness": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_roughnessMap" + "name": "m_layer2_m_roughnessMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Roughness map UV set", "type": "Enum", @@ -1356,12 +1356,12 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_roughnessMapUvIndex" + "name": "m_layer2_m_roughnessMapUvIndex" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "lowerBound", + "name": "lowerBound", "displayName": "Lower Bound", "description": "The roughness value that corresponds to black in the texture.", "type": "Float", @@ -1370,12 +1370,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_roughnessLowerBound" + "name": "m_layer2_m_roughnessLowerBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "upperBound", + "name": "upperBound", "displayName": "Upper Bound", "description": "The roughness value that corresponds to white in the texture.", "type": "Float", @@ -1384,12 +1384,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_roughnessUpperBound" + "name": "m_layer2_m_roughnessUpperBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Controls the roughness value", "type": "Float", @@ -1398,13 +1398,13 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_roughnessFactor" + "name": "m_layer2_m_roughnessFactor" } } ], "layer2_specularF0": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.", "type": "Float", @@ -1413,28 +1413,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_specularF0Factor" + "name": "m_layer2_m_specularF0Factor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_specularF0Map" + "name": "m_layer2_m_specularF0Map" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Specular reflection map UV set", "type": "Enum", @@ -1442,30 +1442,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_specularF0MapUvIndex" + "name": "m_layer2_m_specularF0MapUvIndex" } } ], "layer2_normal": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_normalMap" + "name": "m_layer2_m_normalMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Normal map UV set", "type": "Enum", @@ -1473,33 +1473,33 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_normalMapUvIndex" + "name": "m_layer2_m_normalMapUvIndex" } }, { - "id": "flipX", + "name": "flipX", "displayName": "Flip X Channel", "description": "Flip tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_flipNormalX" + "name": "m_layer2_m_flipNormalX" } }, { - "id": "flipY", + "name": "flipY", "displayName": "Flip Y Channel", "description": "Flip bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_flipNormalY" + "name": "m_layer2_m_flipNormalY" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the values", "type": "Float", @@ -1508,20 +1508,20 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_normalFactor" + "name": "m_layer2_m_normalFactor" } } ], "layer2_clearCoat": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable clear coat", "type": "Bool", "defaultValue": false }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the percentage of effect applied", "type": "Float", @@ -1530,28 +1530,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatFactor" + "name": "m_layer2_m_clearCoatFactor" } }, { - "id": "influenceMap", + "name": "influenceMap", "displayName": " Influence Map", "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatInfluenceMap" + "name": "m_layer2_m_clearCoatInfluenceMap" } }, { - "id": "useInfluenceMap", + "name": "useInfluenceMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "influenceMapUv", + "name": "influenceMapUv", "displayName": " UV", "description": "Strength factor map UV set", "type": "Enum", @@ -1559,11 +1559,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatInfluenceMapUvIndex" + "name": "m_layer2_m_clearCoatInfluenceMapUvIndex" } }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "description": "Clear coat layer roughness", "type": "Float", @@ -1572,28 +1572,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatRoughness" + "name": "m_layer2_m_clearCoatRoughness" } }, { - "id": "roughnessMap", + "name": "roughnessMap", "displayName": " Roughness Map", "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatRoughnessMap" + "name": "m_layer2_m_clearCoatRoughnessMap" } }, { - "id": "useRoughnessMap", + "name": "useRoughnessMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { - "id": "roughnessMapUv", + "name": "roughnessMapUv", "displayName": " UV", "description": "Roughness map UV set", "type": "Enum", @@ -1601,11 +1601,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatRoughnessMapUvIndex" + "name": "m_layer2_m_clearCoatRoughnessMapUvIndex" } }, { - "id": "normalStrength", + "name": "normalStrength", "displayName": "Normal Strength", "description": "Scales the impact of the clear coat normal map", "type": "Float", @@ -1614,28 +1614,28 @@ "max": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatNormalStrength" + "name": "m_layer2_m_clearCoatNormalStrength" } }, { - "id": "normalMap", + "name": "normalMap", "displayName": "Normal Map", "description": "Normal map for clear coat layer, as top layer material clear coat doesn't affect by base layer normal map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatNormalMap" + "name": "m_layer2_m_clearCoatNormalMap" } }, { - "id": "useNormalMap", + "name": "useNormalMap", "displayName": " Use Texture", "description": "Whether to use the normal map", "type": "Bool", "defaultValue": true }, { - "id": "normalMapUv", + "name": "normalMapUv", "displayName": " UV", "description": "Normal map UV set", "type": "Enum", @@ -1643,30 +1643,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_clearCoatNormalMapUvIndex" + "name": "m_layer2_m_clearCoatNormalMapUvIndex" } } ], "layer2_occlusion": [ { - "id": "diffuseTextureMap", + "name": "diffuseTextureMap", "displayName": "Diffuse AO", "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_diffuseOcclusionMap" + "name": "m_layer2_m_diffuseOcclusionMap" } }, { - "id": "diffuseUseTexture", + "name": "diffuseUseTexture", "displayName": " Use Texture", "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { - "id": "diffuseTextureMapUv", + "name": "diffuseTextureMapUv", "displayName": " UV", "description": "Diffuse AO map UV set.", "type": "Enum", @@ -1674,11 +1674,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_diffuseOcclusionMapUvIndex" + "name": "m_layer2_m_diffuseOcclusionMapUvIndex" } }, { - "id": "diffuseFactor", + "name": "diffuseFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Diffuse AO", "type": "Float", @@ -1687,28 +1687,28 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_diffuseOcclusionFactor" + "name": "m_layer2_m_diffuseOcclusionFactor" } }, { - "id": "specularTextureMap", + "name": "specularTextureMap", "displayName": "Specular Cavity", "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_specularOcclusionMap" + "name": "m_layer2_m_specularOcclusionMap" } }, { - "id": "specularUseTexture", + "name": "specularUseTexture", "displayName": " Use Texture", "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { - "id": "specularTextureMapUv", + "name": "specularTextureMapUv", "displayName": " UV", "description": "Specular Cavity map UV set.", "type": "Enum", @@ -1716,11 +1716,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_specularOcclusionMapUvIndex" + "name": "m_layer2_m_specularOcclusionMapUvIndex" } }, { - "id": "specularFactor", + "name": "specularFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Specular Cavity", "type": "Float", @@ -1729,20 +1729,20 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_specularOcclusionFactor" + "name": "m_layer2_m_specularOcclusionFactor" } } ], "layer2_emissive": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable the emissive group", "type": "Bool", "defaultValue": false }, { - "id": "unit", + "name": "unit", "displayName": "Units", "description": "The photometric units of the Intensity property.", "type": "Enum", @@ -1750,18 +1750,18 @@ "defaultValue": "Ev100" }, { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_layer2_m_emissiveColor" + "name": "m_layer2_m_emissiveColor" } }, { - "id": "intensity", + "name": "intensity", "displayName": "Intensity", "description": "The amount of energy emitted.", "type": "Float", @@ -1772,24 +1772,24 @@ "softMax": 16 }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_emissiveMap" + "name": "m_layer2_m_emissiveMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Emissive map UV set", "type": "Enum", @@ -1797,30 +1797,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_emissiveMapUvIndex" + "name": "m_layer2_m_emissiveMapUvIndex" } } ], "layer2_parallax": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Height Map", "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_heightmap" + "name": "m_layer2_m_heightmap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { - "id": "factor", + "name": "factor", "displayName": "Scale", "description": "The total height of the height map in local model units.", "type": "Float", @@ -1829,11 +1829,11 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_heightmapScale" + "name": "m_layer2_m_heightmapScale" } }, { - "id": "offset", + "name": "offset", "displayName": "Offset", "description": "Adjusts the overall displacement amount in local model units.", "type": "Float", @@ -1842,13 +1842,13 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_heightmapOffset" + "name": "m_layer2_m_heightmapOffset" } } ], "layer2_uv": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", @@ -1856,7 +1856,7 @@ "defaultValue": [ 0.5, 0.5 ] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in V.", "type": "float", @@ -1864,7 +1864,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -1872,7 +1872,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -1882,7 +1882,7 @@ "step": 0.001 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -1892,7 +1892,7 @@ "step": 0.001 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -1902,7 +1902,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", @@ -1915,18 +1915,18 @@ //############################################################################################## "layer3_baseColor": [ { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_layer3_m_baseColor" + "name": "m_layer3_m_baseColor" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", @@ -1935,28 +1935,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_baseColorFactor" + "name": "m_layer3_m_baseColorFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_baseColorMap" + "name": "m_layer3_m_baseColorMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Base color map UV set", "type": "Enum", @@ -1964,11 +1964,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_baseColorMapUvIndex" + "name": "m_layer3_m_baseColorMapUvIndex" } }, { - "id": "textureBlendMode", + "name": "textureBlendMode", "displayName": "Texture Blend Mode", "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", @@ -1976,13 +1976,13 @@ "defaultValue": "Multiply", "connection": { "type": "ShaderOption", - "id": "o_layer3_o_baseColorTextureBlendMode" + "name": "o_layer3_o_baseColorTextureBlendMode" } } ], "layer3_metallic": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "This value is linear, black is non-metal and white means raw metal.", "type": "Float", @@ -1991,28 +1991,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_metallicFactor" + "name": "m_layer3_m_metallicFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_metallicMap" + "name": "m_layer3_m_metallicMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Metallic map UV set", "type": "Enum", @@ -2020,30 +2020,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_metallicMapUvIndex" + "name": "m_layer3_m_metallicMapUvIndex" } } ], "layer3_roughness": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_roughnessMap" + "name": "m_layer3_m_roughnessMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Roughness map UV set", "type": "Enum", @@ -2051,12 +2051,12 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_roughnessMapUvIndex" + "name": "m_layer3_m_roughnessMapUvIndex" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "lowerBound", + "name": "lowerBound", "displayName": "Lower Bound", "description": "The roughness value that corresponds to black in the texture.", "type": "Float", @@ -2065,12 +2065,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_roughnessLowerBound" + "name": "m_layer3_m_roughnessLowerBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "upperBound", + "name": "upperBound", "displayName": "Upper Bound", "description": "The roughness value that corresponds to white in the texture.", "type": "Float", @@ -2079,12 +2079,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_roughnessUpperBound" + "name": "m_layer3_m_roughnessUpperBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Controls the roughness value", "type": "Float", @@ -2093,13 +2093,13 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_roughnessFactor" + "name": "m_layer3_m_roughnessFactor" } } ], "layer3_specularF0": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.", "type": "Float", @@ -2108,28 +2108,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_specularF0Factor" + "name": "m_layer3_m_specularF0Factor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_specularF0Map" + "name": "m_layer3_m_specularF0Map" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Specular reflection map UV set", "type": "Enum", @@ -2137,30 +2137,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_specularF0MapUvIndex" + "name": "m_layer3_m_specularF0MapUvIndex" } } ], "layer3_normal": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_normalMap" + "name": "m_layer3_m_normalMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Normal map UV set", "type": "Enum", @@ -2168,33 +2168,33 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_normalMapUvIndex" + "name": "m_layer3_m_normalMapUvIndex" } }, { - "id": "flipX", + "name": "flipX", "displayName": "Flip X Channel", "description": "Flip tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_flipNormalX" + "name": "m_layer3_m_flipNormalX" } }, { - "id": "flipY", + "name": "flipY", "displayName": "Flip Y Channel", "description": "Flip bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_flipNormalY" + "name": "m_layer3_m_flipNormalY" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the values", "type": "Float", @@ -2203,20 +2203,20 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_normalFactor" + "name": "m_layer3_m_normalFactor" } } ], "layer3_clearCoat": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable clear coat", "type": "Bool", "defaultValue": false }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the percentage of effect applied", "type": "Float", @@ -2225,28 +2225,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatFactor" + "name": "m_layer3_m_clearCoatFactor" } }, { - "id": "influenceMap", + "name": "influenceMap", "displayName": " Influence Map", "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatInfluenceMap" + "name": "m_layer3_m_clearCoatInfluenceMap" } }, { - "id": "useInfluenceMap", + "name": "useInfluenceMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "influenceMapUv", + "name": "influenceMapUv", "displayName": " UV", "description": "Strength factor map UV set", "type": "Enum", @@ -2254,11 +2254,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatInfluenceMapUvIndex" + "name": "m_layer3_m_clearCoatInfluenceMapUvIndex" } }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "description": "Clear coat layer roughness", "type": "Float", @@ -2267,28 +2267,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatRoughness" + "name": "m_layer3_m_clearCoatRoughness" } }, { - "id": "roughnessMap", + "name": "roughnessMap", "displayName": " Roughness Map", "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatRoughnessMap" + "name": "m_layer3_m_clearCoatRoughnessMap" } }, { - "id": "useRoughnessMap", + "name": "useRoughnessMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { - "id": "roughnessMapUv", + "name": "roughnessMapUv", "displayName": " UV", "description": "Roughness map UV set", "type": "Enum", @@ -2296,11 +2296,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatRoughnessMapUvIndex" + "name": "m_layer3_m_clearCoatRoughnessMapUvIndex" } }, { - "id": "normalStrength", + "name": "normalStrength", "displayName": "Normal Strength", "description": "Scales the impact of the clear coat normal map", "type": "Float", @@ -2309,28 +2309,28 @@ "max": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatNormalStrength" + "name": "m_layer3_m_clearCoatNormalStrength" } }, { - "id": "normalMap", + "name": "normalMap", "displayName": "Normal Map", "description": "Normal map for clear coat layer, as top layer material clear coat doesn't affect by base layer normal map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatNormalMap" + "name": "m_layer3_m_clearCoatNormalMap" } }, { - "id": "useNormalMap", + "name": "useNormalMap", "displayName": " Use Texture", "description": "Whether to use the normal map", "type": "Bool", "defaultValue": true }, { - "id": "normalMapUv", + "name": "normalMapUv", "displayName": " UV", "description": "Normal map UV set", "type": "Enum", @@ -2338,30 +2338,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_clearCoatNormalMapUvIndex" + "name": "m_layer3_m_clearCoatNormalMapUvIndex" } } ], "layer3_occlusion": [ { - "id": "diffuseTextureMap", + "name": "diffuseTextureMap", "displayName": "Diffuse AO", "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_diffuseOcclusionMap" + "name": "m_layer3_m_diffuseOcclusionMap" } }, { - "id": "diffuseUseTexture", + "name": "diffuseUseTexture", "displayName": " Use Texture", "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { - "id": "diffuseTextureMapUv", + "name": "diffuseTextureMapUv", "displayName": " UV", "description": "Diffuse AO map UV set.", "type": "Enum", @@ -2369,11 +2369,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_diffuseOcclusionMapUvIndex" + "name": "m_layer3_m_diffuseOcclusionMapUvIndex" } }, { - "id": "diffuseFactor", + "name": "diffuseFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Diffuse AO", "type": "Float", @@ -2382,28 +2382,28 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_diffuseOcclusionFactor" + "name": "m_layer3_m_diffuseOcclusionFactor" } }, { - "id": "specularTextureMap", + "name": "specularTextureMap", "displayName": "Specular Cavity", "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_specularOcclusionMap" + "name": "m_layer3_m_specularOcclusionMap" } }, { - "id": "specularUseTexture", + "name": "specularUseTexture", "displayName": " Use Texture", "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { - "id": "specularTextureMapUv", + "name": "specularTextureMapUv", "displayName": " UV", "description": "Specular Cavity map UV set.", "type": "Enum", @@ -2411,11 +2411,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_specularOcclusionMapUvIndex" + "name": "m_layer3_m_specularOcclusionMapUvIndex" } }, { - "id": "specularFactor", + "name": "specularFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Specular Cavity", "type": "Float", @@ -2424,20 +2424,20 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_specularOcclusionFactor" + "name": "m_layer3_m_specularOcclusionFactor" } } ], "layer3_emissive": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable the emissive group", "type": "Bool", "defaultValue": false }, { - "id": "unit", + "name": "unit", "displayName": "Units", "description": "The photometric units of the Intensity property.", "type": "Enum", @@ -2445,18 +2445,18 @@ "defaultValue": "Ev100" }, { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_layer3_m_emissiveColor" + "name": "m_layer3_m_emissiveColor" } }, { - "id": "intensity", + "name": "intensity", "displayName": "Intensity", "description": "The amount of energy emitted.", "type": "Float", @@ -2467,24 +2467,24 @@ "softMax": 16 }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_emissiveMap" + "name": "m_layer3_m_emissiveMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Emissive map UV set", "type": "Enum", @@ -2492,30 +2492,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_emissiveMapUvIndex" + "name": "m_layer3_m_emissiveMapUvIndex" } } ], "layer3_parallax": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Height Map", "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_heightmap" + "name": "m_layer3_m_heightmap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { - "id": "factor", + "name": "factor", "displayName": "Scale", "description": "The total height of the height map in local model units.", "type": "Float", @@ -2524,11 +2524,11 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_heightmapScale" + "name": "m_layer3_m_heightmapScale" } }, { - "id": "offset", + "name": "offset", "displayName": "Offset", "description": "Adjusts the overall displacement amount in local model units.", "type": "Float", @@ -2537,13 +2537,13 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_heightmapOffset" + "name": "m_layer3_m_heightmapOffset" } } ], "layer3_uv": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", @@ -2551,7 +2551,7 @@ "defaultValue": [ 0.5, 0.5 ] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in V.", "type": "float", @@ -2559,7 +2559,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -2567,7 +2567,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -2577,7 +2577,7 @@ "step": 0.001 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -2587,7 +2587,7 @@ "step": 0.001 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -2597,7 +2597,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index 8b7e4b1c7e..6eb82b85ae 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -4,68 +4,68 @@ "version": 3, "groups": [ { - "id": "baseColor", + "name": "baseColor", "displayName": "Base Color", "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." }, { - "id": "metallic", + "name": "metallic", "displayName": "Metallic", "description": "Properties for configuring whether the surface is metallic or not." }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "description": "Properties for configuring how rough the surface appears." }, { - "id": "specularF0", + "name": "specularF0", "displayName": "Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, { - "id": "normal", + "name": "normal", "displayName": "Normal", "description": "Properties related to configuring surface normal." }, { - "id": "occlusion", + "name": "occlusion", "displayName": "Occlusion", "description": "Properties for baked textures that represent geometric occlusion of light." }, { - "id": "emissive", + "name": "emissive", "displayName": "Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, { - "id": "clearCoat", + "name": "clearCoat", "displayName": "Clear Coat", "description": "Properties for configuring gloss clear coat" }, { - "id": "parallax", + "name": "parallax", "displayName": "Displacement", "description": "Properties for parallax effect produced by a height map." }, { - "id": "opacity", + "name": "opacity", "displayName": "Opacity", "description": "Properties for configuring the materials transparency." }, { - "id": "uv", + "name": "uv", "displayName": "UVs", "description": "Properties for configuring UV transforms." }, { // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader - "id": "irradiance", + "name": "irradiance", "displayName": "Irradiance", "description": "Properties for configuring the irradiance used in global illumination." }, { - "id": "general", + "name": "general", "displayName": "General Settings", "description": "General settings." } @@ -73,97 +73,97 @@ "properties": { "general": [ { - "id": "applySpecularAA", + "name": "applySpecularAA", "displayName": "Apply Specular AA", "description": "Whether to apply specular anti-aliasing in the shader.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_applySpecularAA" + "name": "o_applySpecularAA" } }, { - "id": "enableShadows", + "name": "enableShadows", "displayName": "Enable Shadows", "description": "Whether to use the shadow maps.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableShadows" + "name": "o_enableShadows" } }, { - "id": "enableDirectionalLights", + "name": "enableDirectionalLights", "displayName": "Enable Directional Lights", "description": "Whether to use directional lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableDirectionalLights" + "name": "o_enableDirectionalLights" } }, { - "id": "enablePunctualLights", + "name": "enablePunctualLights", "displayName": "Enable Punctual Lights", "description": "Whether to use punctual lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enablePunctualLights" + "name": "o_enablePunctualLights" } }, { - "id": "enableAreaLights", + "name": "enableAreaLights", "displayName": "Enable Area Lights", "description": "Whether to use area lights.", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableAreaLights" + "name": "o_enableAreaLights" } }, { - "id": "enableIBL", + "name": "enableIBL", "displayName": "Enable IBL", "description": "Whether to use Image Based Lighting (IBL).", "type": "Bool", "defaultValue": true, "connection": { "type": "ShaderOption", - "id": "o_enableIBL" + "name": "o_enableIBL" } }, { - "id": "forwardPassIBLSpecular", + "name": "forwardPassIBLSpecular", "displayName": "Forward Pass IBL Specular", "description": "Whether to apply IBL specular in the forward pass.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_materialUseForwardPassIBLSpecular" + "name": "o_materialUseForwardPassIBLSpecular" } } ], "baseColor": [ { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_baseColor" + "name": "m_baseColor" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", @@ -172,28 +172,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_baseColorFactor" + "name": "m_baseColorFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_baseColorMap" + "name": "m_baseColorMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Base color map UV set", "type": "Enum", @@ -201,11 +201,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_baseColorMapUvIndex" + "name": "m_baseColorMapUvIndex" } }, { - "id": "textureBlendMode", + "name": "textureBlendMode", "displayName": "Texture Blend Mode", "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", @@ -213,13 +213,13 @@ "defaultValue": "Multiply", "connection": { "type": "ShaderOption", - "id": "o_baseColorTextureBlendMode" + "name": "o_baseColorTextureBlendMode" } } ], "metallic": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "This value is linear, black is non-metal and white means raw metal.", "type": "Float", @@ -228,28 +228,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_metallicFactor" + "name": "m_metallicFactor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_metallicMap" + "name": "m_metallicMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Metallic map UV set", "type": "Enum", @@ -257,30 +257,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_metallicMapUvIndex" + "name": "m_metallicMapUvIndex" } } ], "roughness": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_roughnessMap" + "name": "m_roughnessMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Roughness map UV set", "type": "Enum", @@ -288,12 +288,12 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_roughnessMapUvIndex" + "name": "m_roughnessMapUvIndex" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "lowerBound", + "name": "lowerBound", "displayName": "Lower Bound", "description": "The roughness value that corresponds to black in the texture.", "type": "Float", @@ -302,12 +302,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessLowerBound" + "name": "m_roughnessLowerBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "upperBound", + "name": "upperBound", "displayName": "Upper Bound", "description": "The roughness value that corresponds to white in the texture.", "type": "Float", @@ -316,12 +316,12 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessUpperBound" + "name": "m_roughnessUpperBound" } }, { // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Controls the roughness value", "type": "Float", @@ -330,13 +330,13 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughnessFactor" + "name": "m_roughnessFactor" } } ], "specularF0": [ { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.", "type": "Float", @@ -345,28 +345,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_specularF0Factor" + "name": "m_specularF0Factor" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_specularF0Map" + "name": "m_specularF0Map" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Specular reflection map UV set", "type": "Enum", @@ -374,31 +374,31 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_specularF0MapUvIndex" + "name": "m_specularF0MapUvIndex" } }, // Consider moving this to the "general" group to be consistent with StandardMultilayerPBR { - "id": "enableMultiScatterCompensation", + "name": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", "description": "Whether to enable multiple scattering compensation.", "type": "Bool", "connection": { "type": "ShaderOption", - "id": "o_specularF0_enableMultiScatterCompensation" + "name": "o_specularF0_enableMultiScatterCompensation" } } ], "clearCoat": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable clear coat", "type": "Bool", "defaultValue": false }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the percentage of effect applied", "type": "Float", @@ -407,28 +407,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_clearCoatFactor" + "name": "m_clearCoatFactor" } }, { - "id": "influenceMap", + "name": "influenceMap", "displayName": " Influence Map", "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_clearCoatInfluenceMap" + "name": "m_clearCoatInfluenceMap" } }, { - "id": "useInfluenceMap", + "name": "useInfluenceMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { - "id": "influenceMapUv", + "name": "influenceMapUv", "displayName": " UV", "description": "Strength factor map UV set", "type": "Enum", @@ -436,11 +436,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_clearCoatInfluenceMapUvIndex" + "name": "m_clearCoatInfluenceMapUvIndex" } }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "description": "Clear coat layer roughness", "type": "Float", @@ -449,28 +449,28 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_clearCoatRoughness" + "name": "m_clearCoatRoughness" } }, { - "id": "roughnessMap", + "name": "roughnessMap", "displayName": " Roughness Map", "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_clearCoatRoughnessMap" + "name": "m_clearCoatRoughnessMap" } }, { - "id": "useRoughnessMap", + "name": "useRoughnessMap", "displayName": " Use Texture", "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { - "id": "roughnessMapUv", + "name": "roughnessMapUv", "displayName": " UV", "description": "Roughness map UV set", "type": "Enum", @@ -478,11 +478,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_clearCoatRoughnessMapUvIndex" + "name": "m_clearCoatRoughnessMapUvIndex" } }, { - "id": "normalStrength", + "name": "normalStrength", "displayName": "Normal Strength", "description": "Scales the impact of the clear coat normal map", "type": "Float", @@ -491,28 +491,28 @@ "max": 2.0, "connection": { "type": "ShaderInput", - "id": "m_clearCoatNormalStrength" + "name": "m_clearCoatNormalStrength" } }, { - "id": "normalMap", + "name": "normalMap", "displayName": "Normal Map", "description": "Normal map for clear coat layer, as top layer material clear coat doesn't affect by base layer normal map", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_clearCoatNormalMap" + "name": "m_clearCoatNormalMap" } }, { - "id": "useNormalMap", + "name": "useNormalMap", "displayName": " Use Texture", "description": "Whether to use the normal map", "type": "Bool", "defaultValue": true }, { - "id": "normalMapUv", + "name": "normalMapUv", "displayName": " UV", "description": "Normal map UV set", "type": "Enum", @@ -520,30 +520,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_clearCoatNormalMapUvIndex" + "name": "m_clearCoatNormalMapUvIndex" } } ], "normal": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_normalMap" + "name": "m_normalMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Normal map UV set", "type": "Enum", @@ -551,33 +551,33 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_normalMapUvIndex" + "name": "m_normalMapUvIndex" } }, { - "id": "flipX", + "name": "flipX", "displayName": "Flip X Channel", "description": "Flip tangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_flipNormalX" + "name": "m_flipNormalX" } }, { - "id": "flipY", + "name": "flipY", "displayName": "Flip Y Channel", "description": "Flip bitangent direction for this normal map.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderInput", - "id": "m_flipNormalY" + "name": "m_flipNormalY" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the values", "type": "Float", @@ -586,13 +586,13 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_normalFactor" + "name": "m_normalFactor" } } ], "opacity": [ { - "id": "mode", + "name": "mode", "displayName": "Opacity Mode", "description": "Indicates the general approach how transparency is to be applied.", "type": "Enum", @@ -600,11 +600,11 @@ "defaultValue": "Opaque", "connection": { "type": "ShaderOption", - "id": "o_opacity_mode" + "name": "o_opacity_mode" } }, { - "id": "alphaSource", + "name": "alphaSource", "displayName": "Alpha Source", "description": "Indicates whether to get the opacity texture from the Base Color map (Packed) or from a separate greyscale texture (Split).", "type": "Enum", @@ -612,21 +612,21 @@ "defaultValue": "Packed", "connection": { "type": "ShaderOption", - "id": "o_opacity_source" + "name": "o_opacity_source" } }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining surface opacity.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_opacityMap" + "name": "m_opacityMap" } }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Opacity map UV set", "type": "Enum", @@ -634,11 +634,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_opacityMapUvIndex" + "name": "m_opacityMapUvIndex" } }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Factor for cutout threshold and blending", "type": "Float", @@ -647,17 +647,17 @@ "defaultValue": 0.5, "connection": { "type": "ShaderInput", - "id": "m_opacityFactor" + "name": "m_opacityFactor" } }, { - "id": "doubleSided", + "name": "doubleSided", "displayName": "Double-sided", "description": "Whether to render back-faces or just front-faces.", "type": "Bool" }, { - "id": "alphaAffectsSpecular", + "name": "alphaAffectsSpecular", "displayName": "Alpha affects specular", "description": "How much the alpha value should also affect specular reflection. This should be 0.0 for materials where light can transmit through their physical surface (like glass), but 1.0 when alpha determines the very presence of a surface (like hair or grass)", "type": "float", @@ -666,13 +666,13 @@ "defaultValue": 0.0, "connection": { "type": "ShaderInput", - "id": "m_opacityAffectsSpecularFactor" + "name": "m_opacityAffectsSpecularFactor" } } ], "uv": [ { - "id": "center", + "name": "center", "displayName": "Center", "description": "Center point for scaling and rotation transformations.", "type": "vector2", @@ -680,7 +680,7 @@ "defaultValue": [ 0.5, 0.5 ] }, { - "id": "tileU", + "name": "tileU", "displayName": "Tile U", "description": "Scales texture coordinates in U.", "type": "float", @@ -688,7 +688,7 @@ "step": 0.1 }, { - "id": "tileV", + "name": "tileV", "displayName": "Tile V", "description": "Scales texture coordinates in V.", "type": "float", @@ -696,7 +696,7 @@ "step": 0.1 }, { - "id": "offsetU", + "name": "offsetU", "displayName": "Offset U", "description": "Offsets texture coordinates in the U direction.", "type": "float", @@ -705,7 +705,7 @@ "max": 1.0 }, { - "id": "offsetV", + "name": "offsetV", "displayName": "Offset V", "description": "Offsets texture coordinates in the V direction.", "type": "float", @@ -714,7 +714,7 @@ "max": 1.0 }, { - "id": "rotateDegrees", + "name": "rotateDegrees", "displayName": "Rotate", "description": "Rotates the texture coordinates (degrees).", "type": "float", @@ -724,7 +724,7 @@ "step": 1.0 }, { - "id": "scale", + "name": "scale", "displayName": "Scale", "description": "Scales texture coordinates in both U and V.", "type": "float", @@ -734,24 +734,24 @@ ], "occlusion": [ { - "id": "diffuseTextureMap", + "name": "diffuseTextureMap", "displayName": "Diffuse AO", "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionMap" + "name": "m_diffuseOcclusionMap" } }, { - "id": "diffuseUseTexture", + "name": "diffuseUseTexture", "displayName": " Use Texture", "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { - "id": "diffuseTextureMapUv", + "name": "diffuseTextureMapUv", "displayName": " UV", "description": "Diffuse AO map UV set.", "type": "Enum", @@ -759,11 +759,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionMapUvIndex" + "name": "m_diffuseOcclusionMapUvIndex" } }, { - "id": "diffuseFactor", + "name": "diffuseFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Diffuse AO", "type": "Float", @@ -772,28 +772,28 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_diffuseOcclusionFactor" + "name": "m_diffuseOcclusionFactor" } }, { - "id": "specularTextureMap", + "name": "specularTextureMap", "displayName": "Specular Cavity", "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionMap" + "name": "m_specularOcclusionMap" } }, { - "id": "specularUseTexture", + "name": "specularUseTexture", "displayName": " Use Texture", "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { - "id": "specularTextureMapUv", + "name": "specularTextureMapUv", "displayName": " UV", "description": "Specular Cavity map UV set.", "type": "Enum", @@ -801,11 +801,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionMapUvIndex" + "name": "m_specularOcclusionMapUvIndex" } }, { - "id": "specularFactor", + "name": "specularFactor", "displayName": " Factor", "description": "Strength factor for scaling the values of Specular Cavity", "type": "Float", @@ -814,20 +814,20 @@ "softMax": 2.0, "connection": { "type": "ShaderInput", - "id": "m_specularOcclusionFactor" + "name": "m_specularOcclusionFactor" } } ], "emissive": [ { - "id": "enable", + "name": "enable", "displayName": "Enable", "description": "Enable the emissive group", "type": "Bool", "defaultValue": false }, { - "id": "unit", + "name": "unit", "displayName": "Units", "description": "The photometric units of the Intensity property.", "type": "Enum", @@ -835,18 +835,18 @@ "defaultValue": "Ev100" }, { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_emissiveColor" + "name": "m_emissiveColor" } }, { - "id": "intensity", + "name": "intensity", "displayName": "Intensity", "description": "The amount of energy emitted.", "type": "Float", @@ -857,24 +857,24 @@ "softMax": 16 }, { - "id": "textureMap", + "name": "textureMap", "displayName": "Texture", "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_emissiveMap" + "name": "m_emissiveMap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Emissive map UV set", "type": "Enum", @@ -882,30 +882,30 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_emissiveMapUvIndex" + "name": "m_emissiveMapUvIndex" } } ], "parallax": [ { - "id": "textureMap", + "name": "textureMap", "displayName": "Height Map", "description": "Displacement height map to create parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_heightmap" + "name": "m_heightmap" } }, { - "id": "useTexture", + "name": "useTexture", "displayName": "Use Texture", "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { - "id": "textureMapUv", + "name": "textureMapUv", "displayName": "UV", "description": "Height map UV set", "type": "Enum", @@ -913,11 +913,11 @@ "defaultValue": "Tiled", "connection": { "type": "ShaderInput", - "id": "m_parallaxUvIndex" + "name": "m_parallaxUvIndex" } }, { - "id": "factor", + "name": "factor", "displayName": "Height Map Scale", "description": "The total height of the height map in local model units.", "type": "Float", @@ -926,11 +926,11 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_heightmapScale" + "name": "m_heightmapScale" } }, { - "id": "offset", + "name": "offset", "displayName": "Offset", "description": "Adjusts the overall displacement amount in local model units.", "type": "Float", @@ -939,11 +939,11 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_heightmapOffset" + "name": "m_heightmapOffset" } }, { - "id": "algorithm", + "name": "algorithm", "displayName": "Algorithm", "description": "Select the algorithm to use for parallax mapping.", "type": "Enum", @@ -951,11 +951,11 @@ "defaultValue": "POM", "connection": { "type": "ShaderOption", - "id": "o_parallax_algorithm" + "name": "o_parallax_algorithm" } }, { - "id": "quality", + "name": "quality", "displayName": "Quality", "description": "Quality of parallax mapping.", "type": "Enum", @@ -963,43 +963,43 @@ "defaultValue": "Low", "connection": { "type": "ShaderOption", - "id": "o_parallax_quality" + "name": "o_parallax_quality" } }, { - "id": "pdo", + "name": "pdo", "displayName": "Pixel Depth Offset", "description": "Enable PDO to offset the original pixel depths. This will affect any shaders using depth, for example, when receiving shadows.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_parallax_enablePixelDepthOffset" + "name": "o_parallax_enablePixelDepthOffset" } }, { - "id": "showClipping", + "name": "showClipping", "displayName": "Show Clipping", "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_parallax_highlightClipping" + "name": "o_parallax_highlightClipping" } } ], "irradiance": [ // Note: this property group is used in the DiffuseGlobalIllumination pass and not by the main forward shader { - "id": "color", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ] }, { - "id": "factor", + "name": "factor", "displayName": "Factor", "description": "Strength factor for scaling the irradiance color values. Zero (0.0) is black, white (1.0) is full color.", "type": "Float", diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h index 987e78ae0f..40555bae00 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h @@ -39,13 +39,24 @@ namespace AZ //! Otherwise an attempt will be made to find or create a shared instance. void RebuildInstance(); + //! Release asset and instance references + void Release(); + + //! Return true if contained assets have not been loaded + bool RequiresLoading() const; + + //! Applies property overrides to material instance + bool ApplyProperties(); + //! Returns a string composed of the asset path. AZStd::string ToString() const; Data::Asset m_materialAsset; + Data::Asset m_defaultMaterialAsset; Data::Instance m_materialInstance; MaterialPropertyOverrideMap m_propertyOverrides; RPI::MaterialModelUvOverrideMap m_matModUvOverrides; + bool m_materialInstancePreCreated = false; }; using MaterialAssignmentMap = AZStd::unordered_map; diff --git a/Gems/Atom/Feature/Common/Code/Source/RenderCommon.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/RenderCommon.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/RenderCommon.h rename to Gems/Atom/Feature/Common/Code/Include/Atom/Feature/RenderCommon.h diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/SkinnedMesh/SkinnedMeshVertexStreams.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/SkinnedMesh/SkinnedMeshVertexStreams.h index 5be3bece5e..aedfb73396 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/SkinnedMesh/SkinnedMeshVertexStreams.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/SkinnedMesh/SkinnedMeshVertexStreams.h @@ -8,7 +8,6 @@ #pragma once -#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CoreLightsSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CoreLightsSystemComponent.cpp index a2c6b8a67e..947028610c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CoreLightsSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CoreLightsSystemComponent.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -33,8 +34,6 @@ #include #include -#include - namespace AZ { namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp index a9ee46dd2c..9e29070e71 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp @@ -7,6 +7,7 @@ */ +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 9f6d2a343d..51e18b8e31 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -34,21 +35,12 @@ #include #include -#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::index_type' -AZ_PUSH_DISABLE_WARNING(4777, "-Wunknown-warning-option") -#include -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 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"); diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp index 4d437be244..8984ec160b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp @@ -7,6 +7,8 @@ */ #include +#include +#include #include #include #include @@ -69,9 +71,9 @@ namespace AZ } MaterialAssignment::MaterialAssignment(const AZ::Data::AssetId& materialAssetId) - : m_materialInstance() + : m_materialAsset(materialAssetId, AZ::AzTypeInfo::Uuid()) + , m_materialInstance() { - m_materialAsset.Create(materialAssetId); } MaterialAssignment::MaterialAssignment(const Data::Asset& asset) @@ -88,12 +90,70 @@ namespace AZ void MaterialAssignment::RebuildInstance() { + if (m_materialInstancePreCreated) + { + return; + } + if (m_materialAsset.IsReady()) { - m_materialInstance = - m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset); + m_materialInstance = m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset); AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized"); } + else if (m_defaultMaterialAsset.IsReady()) + { + m_materialInstance = m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_defaultMaterialAsset) : RPI::Material::Create(m_defaultMaterialAsset); + AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized"); + } + } + + void MaterialAssignment::Release() + { + if (!m_materialInstancePreCreated) + { + m_materialInstance = nullptr; + } + m_materialAsset.Release(); + m_defaultMaterialAsset.Release(); + } + + bool MaterialAssignment::RequiresLoading() const + { + return + !m_materialInstancePreCreated && + !m_materialAsset.IsReady() && + !m_materialAsset.IsLoading() && + !m_defaultMaterialAsset.IsReady() && + !m_defaultMaterialAsset.IsLoading(); + } + + bool MaterialAssignment::ApplyProperties() + { + // if there is no instance or no properties there's nothing to apply + if (!m_materialInstance || m_propertyOverrides.empty()) + { + return true; + } + + if (m_materialInstance->CanCompile()) + { + for (const auto& propertyPair : m_propertyOverrides) + { + if (!propertyPair.second.empty()) + { + const auto& materialPropertyIndex = m_materialInstance->FindPropertyIndex(propertyPair.first); + if (!materialPropertyIndex.IsNull()) + { + m_materialInstance->SetPropertyValue( + materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second)); + } + } + } + + return m_materialInstance->Compile(); + } + + return false; } AZStd::string MaterialAssignment::ToString() const diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 82568126df..cced38c3a7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -6,10 +6,9 @@ * */ -#include - #include #include +#include #include #include #include @@ -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) diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake index ecdc7d59ab..7c594fc945 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake +++ b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake @@ -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 -) diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h index 5b56b70ec8..bee304c5b9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h @@ -9,7 +9,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/Utils/LightingPreset.cpp b/Gems/Atom/Feature/Common/Code/Source/Utils/LightingPreset.cpp index 36c604c931..8984747b74 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Utils/LightingPreset.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Utils/LightingPreset.cpp @@ -9,6 +9,7 @@ #undef RC_INVOKED #include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/Utils/ModelPreset.cpp b/Gems/Atom/Feature/Common/Code/Source/Utils/ModelPreset.cpp index 156fca3b7e..c684b3cc89 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Utils/ModelPreset.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Utils/ModelPreset.cpp @@ -9,8 +9,11 @@ #undef RC_INVOKED #include +#include #include #include +#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake index 59a29f04b6..8262500ca1 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake @@ -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 diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 92f96a591b..9372d6594a 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -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 diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/Handle.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/Handle.h index 01e1fab7cc..3e0c3335a0 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/Handle.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/Handle.h @@ -9,6 +9,7 @@ #include #include +#include namespace AZ { diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h index 36511ebccf..b6f6e0df11 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.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; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h index 0bd0056a0b..dc95078f80 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h @@ -13,13 +13,12 @@ namespace AZ { namespace RHI { - /** - * A virtual address which may be relative to a base resource. This means - * 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value). - * To account for this, VirtualAddress::Null is used instead. Check validity of the address - * using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized - * to Null, so returning the default constructor is sufficient to represent an invalid address. - */ + + //! A virtual address which may be relative to a base resource. This means + //! 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value). + //! To account for this, VirtualAddress::Null is used instead. Check validity of the address + //! using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized + //! to Null, so returning the default constructor is sufficient to represent an invalid address. class VirtualAddress { static const VirtualAddress Null; @@ -29,13 +28,13 @@ namespace AZ static VirtualAddress CreateNull(); - /// Creates a valid address with a zero offset. + //! Creates a valid address with a zero offset. static VirtualAddress CreateZero(); - /// Creates an address from a pointer. + //! Creates an address from a pointer. static VirtualAddress CreateFromPointer(void* ptr); - /// Creates an address from an offset from a base pointer. + //! Creates an address from an offset from a base pointer. static VirtualAddress CreateFromOffset(uint64_t offset); inline bool IsValid() const @@ -51,15 +50,13 @@ namespace AZ uintptr_t m_ptr; }; - /** - * An allocator interface used for external GPU allocations. The allocator - * does not manage the host memory. Instead, the user specifies a base address - * (which may be 0, in order to allocate offsets from a base resource). The allocator - * interface also provides an API for garbage collection. If used to manage GPU resources, - * these are often deferred-released after N frames. The user may provide a garbage collection - * latency, which controls the number of GarbageCollect calls that must occur before an allocation - * is actually reclaimed. The intended use case is to garbage collect at the end of each frame. - */ + //! An allocator interface used for external GPU allocations. The allocator + //! does not manage the host memory. Instead, the user specifies a base address + //! (which may be 0, in order to allocate offsets from a base resource). The allocator + //! interface also provides an API for garbage collection. If used to manage GPU resources, + //! these are often deferred-released after N frames. The user may provide a garbage collection + //! latency, which controls the number of GarbageCollect calls that must occur before an allocation + //! is actually reclaimed. The intended use case is to garbage collect at the end of each frame. class Allocator { public: @@ -86,44 +83,42 @@ namespace AZ virtual void Shutdown() = 0; - /** - * Allocates a virtual address relative to the base address provided at initialization time. - * @param byteCount The number of bytes to allocate. - * @param byteAlignement The alignment used to align the allocation. - */ + //! Allocates a virtual address relative to the base address provided at initialization time. + //! @param byteCount The number of bytes to allocate. + //! @param byteAlignement The alignment used to align the allocation. virtual VirtualAddress Allocate(size_t byteCount, size_t byteAlignment) = 0; - /** - * Deallocates an allocation. The memory is not reclaimed until garbage collect is called. - * Depending on the garbage collection latency, it may take several garbage collection cycles - * before the memory is reclaimed. - */ + //! Deallocates an allocation. The memory is not reclaimed until garbage collect is called. + //! Depending on the garbage collection latency, it may take several garbage collection cycles + //! before the memory is reclaimed. virtual void DeAllocate(VirtualAddress offset) = 0; - /// Allocations are deferred-released until a specific number of GC cycles have occurred. This - /// is useful for allocations actively being consumed by the GPU. + //! Allocations are deferred-released until a specific number of GC cycles have occurred. This + //! is useful for allocations actively being consumed by the GPU. virtual void GarbageCollect() = 0; - /// Forces garbage collection of all allocations, regardless of the GC latency. + //! Forces garbage collection of all allocations, regardless of the GC latency. virtual void GarbageCollectForce() = 0; - /** - * Returns the number of allocations active for this allocator. This includes - * allocations that are pending garbage collection. - */ + //! Returns the number of allocations active for this allocator. This includes + //! allocations that are pending garbage collection. virtual size_t GetAllocationCount() const { return 0; } - /** - * Returns the number of bytes used by the allocator. This includes - * allocations that are pending garbage collection. - */ + //! Returns the number of bytes used by the allocator. This includes + //! allocations that are pending garbage collection. virtual size_t GetAllocatedByteCount() const { return 0; } - /// Returns the descriptor used to initialize the allocator. + //! Returns the descriptor used to initialize the allocator. virtual const Descriptor& GetDescriptor() const = 0; - /// Helper for converting agnostic VirtualAddress type to pointer type. Will convert - /// VirtualAddress::Null to nullptr. + //! Clone the current allocator to the new allocator passed in + virtual void Clone([[maybe_unused]] RHI::Allocator* newAllocator) + { + AZ_Assert(false, "Not Implemented"); + }; + + //! Helper for converting agnostic VirtualAddress type to pointer type. Will convert + //! VirtualAddress::Null to nullptr. template T* AllocateAs(size_t byteCount, size_t byteAlignment) { diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h index f9df29cf74..fb4082bb25 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h @@ -139,6 +139,12 @@ namespace AZ //! Notifies after all objects currently in the platform release queue are released virtual void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) = 0; + //! Allows the back-ends to compact SRG related memory if applicable + virtual RHI::ResultCode CompactSRGMemory() + { + return RHI::ResultCode::Success; + }; + protected: DeviceFeatures m_features; DeviceLimits m_limits; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h index 992cc0e79a..71f9f1605b 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h @@ -112,6 +112,9 @@ namespace AZ //! Returns true if Pix dll is loaded static bool IsPixModuleLoaded(); + //! Returns true if Warp is enabled + static bool UsingWarpDevice(); + //! Returns the name of the Factory. virtual Name GetName() = 0; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h index ee1cb7c7f4..ce20f9cbe5 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h @@ -55,6 +55,7 @@ namespace AZ size_t GetAllocationCount() const override; size_t GetAllocatedByteCount() const override; const Descriptor& GetDescriptor() const override; + void Clone(RHI::Allocator* newAllocator) override; ////////////////////////////////////////////////////////////////////////// private: diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ThreadLocalContext.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ThreadLocalContext.h index dbea45c54a..4d3534b845 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ThreadLocalContext.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ThreadLocalContext.h @@ -11,6 +11,7 @@ #include #include +#include namespace AZ { diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp index ab45d2239b..ef330e7902 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp @@ -155,11 +155,6 @@ namespace AZ { arguments += " -Zpr"; } - if (m_dxcGenerateDebugInfo) - { - arguments += " -Zi"; // Generate debug information - arguments += " -Zss"; // Compute Shader Hash considering source information - } // strip spaces at both sides AZStd::string dxcAdditionalFreeArguments = m_dxcAdditionalFreeArguments; AzFramework::StringFunc::TrimWhiteSpace(dxcAdditionalFreeArguments, true, true); diff --git a/Gems/Atom/RHI/Code/Source/RHI.Reflect/ShaderSemantic.cpp b/Gems/Atom/RHI/Code/Source/RHI.Reflect/ShaderSemantic.cpp index 3cb4bd05a5..718604d750 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Reflect/ShaderSemantic.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Reflect/ShaderSemantic.cpp @@ -7,6 +7,8 @@ */ #include + +#include #include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp index 48b64c17c0..82b0a13c86 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp @@ -8,12 +8,12 @@ #include #include +#include #include #include #if defined(USE_RENDERDOC) || defined(USE_PIX) #include -#include #include #endif @@ -28,6 +28,8 @@ static AZStd::unique_ptr s_pixModule; static bool s_isPixGpuCaptureDllLoaded = false; #endif +static bool s_usingWarpDevice = false; + namespace AZ { namespace RHI @@ -55,6 +57,8 @@ namespace AZ Factory::Factory() { + AZStd::string preferredUserAdapterName = RHI::GetCommandLineValue("forceAdapter"); + s_usingWarpDevice = preferredUserAdapterName == "Microsoft Basic Render Driver"; #if defined(USE_RENDERDOC) // If RenderDoc is requested, we need to load the library as early as possible (before device queries/factories are made) bool enableRenderDoc = RHI::QueryCommandLineOption("enableRenderDoc"); @@ -197,5 +201,10 @@ namespace AZ return false; #endif } + + bool Factory::UsingWarpDevice() + { + return s_usingWarpDevice; + } } } diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index df887379fe..5d2feb1e34 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -314,6 +314,11 @@ namespace AZ resourcePoolDatabase.ForEachShaderResourceGroupPool(compileAllLambda); } + + //It is possible for certain back ends to run out of SRG memory (due to fragmentation) in which case + //we try to compact and re-compile SRGs. + RHI::ResultCode resultCode = m_device->CompactSRGMemory(); + AZ_Assert(resultCode == RHI::ResultCode::Success, "SRG compaction failed and this can lead to a gpu crash."); } void FrameScheduler::BuildRayTracingShaderTables() diff --git a/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp b/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp index c646700495..77877b0020 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp @@ -344,5 +344,17 @@ namespace AZ handle = node.m_nextFree; } } + + void FreeListAllocator::Clone(RHI::Allocator* newAllocator) + { + FreeListAllocator* newFreeListAllocator = static_cast(newAllocator); + newFreeListAllocator->m_headHandle = m_headHandle; + newFreeListAllocator->m_nodeFreeList = m_nodeFreeList; + newFreeListAllocator->m_nodes = m_nodes; + newFreeListAllocator->m_allocations = m_allocations; + newFreeListAllocator->m_garbage = m_garbage; + newFreeListAllocator->m_garbageCollectCycle = m_garbageCollectCycle; + newFreeListAllocator->m_byteCountTotal = m_byteCountTotal; + } } } diff --git a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h index 1f22599b38..63c2d69ea2 100644 --- a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h +++ b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h @@ -64,6 +64,12 @@ namespace AZ //! int array: Max count for descriptors AZStd::unordered_map> m_descriptorHeapLimits; + // Number of max static handles for shader visible srv/uav/cbv views + uint32_t m_numShaderVisibleCbvSrvUavStaticHandles = 2000; + + //Bool to indicate allowing compaction of shader visible srv/uav/cbv heap in case of fragmentation + bool m_allowDescriptorHeapCompaction = false; + FrameGraphExecuterData m_frameGraphExecuterData; void LoadPlatformLimitsDescriptor(const char* rhiName) override; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 89ae3ede46..f30fc72ace 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -255,6 +255,11 @@ namespace AZ // Compilation parameters AZStd::string params = shaderCompilerArguments.MakeAdditionalDxcCommandLineString(); + if (BuildHasDebugInfo(shaderCompilerArguments)) + { + params += " -Zi"; // Generate debug information + params += " -Zss"; // Compute Shader Hash considering source information + } // Enable half precision types when shader model >= 6.2 int shaderModelMajor = 0; @@ -281,12 +286,11 @@ namespace AZ AZStd::string symbolDatabaseFileCliArgument{" "}; // when not debug: still insert a space between 5.dxil and 7.hlsl-in if (BuildHasDebugInfo(shaderCompilerArguments)) { - // prepare .ldd filename: + // prepare .pdb filename: AZStd::string md5hex = RHI::ByteToHexString(md5); AZStd::string symbolDatabaseFilePath = dxcInputFile.c_str(); // mutate from source - AZStd::string lldFileName = md5hex // lld is like pdb but it's the default symbol database extension in dxc - + "-" + profileIt->second; // concatenate the shader profile to disambiguate vs/ps... - AzFramework::StringFunc::Path::ReplaceFullName(symbolDatabaseFilePath, lldFileName.c_str(), "lld"); + AZStd::string pdbFileName = md5hex + "-" + profileIt->second; // concatenate the shader profile to disambiguate vs/ps... + AzFramework::StringFunc::Path::ReplaceFullName(symbolDatabaseFilePath, pdbFileName.c_str(), "pdb"); // it is possible that another activated platform/profile, already exported that file. (since it's hashed on the source file) // dxc returns an error in such case. we get less surprising effets by just not mentionning an -Fd argument if (AZ::IO::SystemFile::Exists(symbolDatabaseFilePath.c_str())) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp index 77b41d6ffc..fd9ad84511 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp @@ -19,8 +19,10 @@ namespace AZ if (SerializeContext* serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(0) + ->Version(1) ->Field("DescriptorHeapLimits", &PlatformLimitsDescriptor::m_descriptorHeapLimits) + ->Field("NumShaderVisibleCbvSrvUavStaticHandles", &PlatformLimitsDescriptor::m_numShaderVisibleCbvSrvUavStaticHandles) + ->Field("AllowDescriptorHeapCompaction", &PlatformLimitsDescriptor::m_allowDescriptorHeapCompaction) ->Field("FrameGraphExecuterData", &PlatformLimitsDescriptor::m_frameGraphExecuterData) ; } @@ -54,7 +56,7 @@ namespace AZ // Map default value must be initialized after attempting to serialize (and result in failure). // Otherwise, serialization won't overwrite the default values. m_descriptorHeapLimits = AZStd::unordered_map>({ - { AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 1000000, 1000000 } }, + { AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 100000, 1000000 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_SAMPLER"), { 2048, 2048 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_RTV"), { 2048, 0 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_DSV"), { 2048, 0 } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h index 443ab3e73a..1472cdc80e 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h @@ -438,7 +438,7 @@ namespace AZ switch (pipelineType) { case RHI::PipelineStateType::Draw: - if (binding.m_resourceTable.IsValid()) + if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr) { GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle); } @@ -448,14 +448,15 @@ namespace AZ GetCommandList()->SetGraphicsRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress); } - if (binding.m_samplerTable.IsValid()) + if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr) { GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle); } for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex) { - if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid()) + if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() && + compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr) { GetCommandList()->SetGraphicsRootDescriptorTable( binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(), @@ -465,7 +466,7 @@ namespace AZ break; case RHI::PipelineStateType::Dispatch: - if (binding.m_resourceTable.IsValid()) + if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr) { GetCommandList()->SetComputeRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle); } @@ -475,14 +476,15 @@ namespace AZ GetCommandList()->SetComputeRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress); } - if (binding.m_samplerTable.IsValid()) + if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr) { GetCommandList()->SetComputeRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle); } for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex) { - if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid()) + if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() && + compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr) { GetCommandList()->SetComputeRootDescriptorTable( binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(), diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp index efa9518ee1..9733045179 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp @@ -50,7 +50,7 @@ namespace AZ void CommandListBase::SetNameInternal(const AZStd::string_view& name) { - AZStd::wstring wname; + AZStd::fixed_wstring<256> wname; AZStd::to_wstring(wname, name.data()); GetCommandList()->SetName(wname.data()); } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp index 9bf25d8bc6..012784d3fc 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp @@ -39,7 +39,7 @@ namespace AZ { Device& device = static_cast(deviceBase); m_currentFrameIndex = 0; - m_frameFences.resize(RHI::Limits::Device::FrameCountMax - 1); + m_frameFences.resize(RHI::Limits::Device::FrameCountMax); for (FenceSet& fences : m_frameFences) { fences.Init(device.GetDevice(), RHI::FenceState::Signaled); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp index 635c66f1cd..1b0df5846c 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include namespace AZ { @@ -40,7 +42,7 @@ namespace AZ for (D3D12_SRV_DIMENSION dimension : validSRVDimensions) { - DescriptorHandle srvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle srvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_SHADER_RESOURCE_VIEW_DESC desc = {}; desc.Format = DXGI_FORMAT_R32_UINT; @@ -62,7 +64,7 @@ namespace AZ for (D3D12_UAV_DIMENSION dimension : UAVDimensions) { - DescriptorHandle uavDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle uavDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {}; desc.Format = DXGI_FORMAT_R32_UINT; @@ -75,14 +77,14 @@ namespace AZ void DescriptorContext::CreateNullDescriptorsCBV() { D3D12_CONSTANT_BUFFER_VIEW_DESC constantBufferDesc = {}; - DescriptorHandle cbvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle cbvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); m_device->CreateConstantBufferView(&constantBufferDesc, GetCpuPlatformHandle(cbvDescriptorHandle)); m_nullDescriptorCBV = cbvDescriptorHandle; } void DescriptorContext::CreateNullDescriptorsSampler() { - m_nullSamplerDescriptor = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + m_nullSamplerDescriptor = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_SAMPLER_DESC samplerDesc = {}; samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; @@ -102,7 +104,7 @@ namespace AZ AZ_Assert(platformLimitsDescriptor.get(), "Platform limits information is missing"); m_platformLimitsDescriptor = platformLimitsDescriptor; - + m_allowDescriptorHeapCompaction = m_platformLimitsDescriptor->m_allowDescriptorHeapCompaction; for (const auto& itr : platformLimitsDescriptor->m_descriptorHeapLimits) { for (uint32_t shaderVisibleIdx = 0; shaderVisibleIdx < PlatformLimitsDescriptor::NumHeapFlags; ++shaderVisibleIdx) @@ -114,11 +116,33 @@ namespace AZ if (descriptorCountMax) { - GetPool(static_cast(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax); + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flags)) + { + //Init the two heaps to help support compaction after fragmentation + m_shaderVisibleCbvSrvUavPools[0].Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, + descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + + m_shaderVisibleCbvSrvUavPools[1].Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, + descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + } + else + { + GetPool(static_cast(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax, descriptorCountMax); + } } } } - + + if (m_allowDescriptorHeapCompaction) + { + m_backupStaticHandles.Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, + platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles, + platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + } + CreateNullDescriptors(); } @@ -129,7 +153,7 @@ namespace AZ { if (constantBufferView.IsNull()) { - constantBufferView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + constantBufferView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(constantBufferView); @@ -145,7 +169,7 @@ namespace AZ { if (shaderResourceView.IsNull()) { - shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView); @@ -165,7 +189,7 @@ namespace AZ { if (unorderedAccessView.IsNull()) { - unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView); @@ -176,7 +200,24 @@ namespace AZ // Copy the UAV descriptor into the GPU-visible version for clearing. if (unorderedAccessViewClear.IsNull()) { - unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset(); + unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1); + + if (unorderedAccessViewClear.IsNull()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory for static handles. Please consider increasing the value of NumShaderVisibleCbvSrvUavStaticHandles" + "within platformlimits.azasset file for dx12."); + return; + } + + if (m_allowDescriptorHeapCompaction) + { + //We make a copy of static handles in case we need to compact and recreate the shader visible heap + m_device->CopyDescriptorsSimple( + 1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor, + unorderedAccessViewClear.m_type); + } } CopyDescriptor(unorderedAccessViewClear, unorderedAccessView); } @@ -188,7 +229,7 @@ namespace AZ { if (shaderResourceView.IsNull()) { - shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView); @@ -205,7 +246,7 @@ namespace AZ { if (unorderedAccessView.IsNull()) { - unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView); @@ -216,7 +257,24 @@ namespace AZ // Copy the UAV descriptor into the GPU-visible version for clearing. if (unorderedAccessViewClear.IsNull()) { - unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset(); + unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1); + + if (unorderedAccessViewClear.IsNull()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory for static handles. Please consider increasing the value of " + "NumShaderVisibleCbvSrvUavStaticHandles within platformlimits.azasset file for dx12."); + return; + } + + if (m_allowDescriptorHeapCompaction) + { + // We make a copy of static handles in case we need to compact and recreate the shader visible heap + m_device->CopyDescriptorsSimple( + 1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor, + unorderedAccessViewClear.m_type); + } } CopyDescriptor(unorderedAccessViewClear, unorderedAccessView); } @@ -228,7 +286,7 @@ namespace AZ { if (renderTargetView.IsNull()) { - renderTargetView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + renderTargetView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor = GetCpuPlatformHandle(renderTargetView); @@ -245,13 +303,13 @@ namespace AZ { if (depthStencilView.IsNull()) { - depthStencilView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + depthStencilView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE depthStencilDescriptor = GetCpuPlatformHandle(depthStencilView); if (depthStencilReadView.IsNull()) { - depthStencilReadView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + depthStencilReadView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE depthStencilReadDescriptor = GetCpuPlatformHandle(depthStencilReadView); @@ -274,7 +332,7 @@ namespace AZ { if (samplerHandle.IsNull()) { - samplerHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + samplerHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_SAMPLER_DESC samplerDesc; @@ -286,17 +344,49 @@ namespace AZ { if (!descriptorHandle.IsNull()) { - ReleaseDescriptorTable(DescriptorTable(descriptorHandle, 1)); + GetPool(descriptorHandle.m_type, descriptorHandle.m_flags).ReleaseHandle(descriptorHandle); } } DescriptorTable DescriptorContext::CreateDescriptorTable( - D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, - uint32_t descriptorCount) + D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg) { - return Allocate(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, descriptorCount); + if (m_allowDescriptorHeapCompaction && !m_compactionInProgress) + { + // Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap + AZStd::scoped_lock lock{ m_srgMapMutex }; + auto iter = m_srgAllocations.find(srg); + if (iter == m_srgAllocations.end()) + { + m_srgAllocations.emplace(srg, 1); + } + else + { + m_srgAllocations[srg]++; + } + } + + return GetPool(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE).AllocateTable(descriptorCount); } + void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table, ShaderResourceGroup* srg) + { + if (m_allowDescriptorHeapCompaction && !m_compactionInProgress) + { + //Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap + AZStd::scoped_lock lock{ m_srgMapMutex }; + auto iter = m_srgAllocations.find(srg); + AZ_Assert(iter != m_srgAllocations.end(), "Srg entry not found"); + m_srgAllocations[srg]--; + if (m_srgAllocations[srg] == 0) + { + m_srgAllocations.erase(srg); + } + } + + GetPool(table.GetType(), table.GetFlags()).ReleaseTable(table); + } + void DescriptorContext::UpdateDescriptorTableRange( DescriptorTable gpuDestinationTable, const DescriptorHandle* cpuSourceDescriptors, @@ -313,14 +403,12 @@ namespace AZ } // Resolve destination descriptor to platform handle. - D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandle(gpuDestinationTable.GetOffset()); + D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandleForTable(gpuDestinationTable); // An array of descriptor sizes for each range. We just want N ranges with 1 descriptor each. AZStd::vector rangeCounts(DescriptorCount, 1); - /** - * We are gathering N source descriptors into a contiguous destination table. - */ + //We are gathering N source descriptors into a contiguous destination table. m_device->CopyDescriptors( 1, // Number of destination ranges. &gpuDestinationHandle, // Destination range array. @@ -353,19 +441,24 @@ namespace AZ } } } + + if (m_allowDescriptorHeapCompaction) + { + m_backupStaticHandles.GarbageCollect(); + } } - DescriptorTable DescriptorContext::Allocate( + DescriptorTable DescriptorContext::AllocateTable( D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count) { - return GetPool(type, flags).Allocate(count); + return GetPool(type, flags).AllocateTable(count); } - void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table) + DescriptorHandle DescriptorContext::AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count) { - GetPool(table.GetType(), table.GetFlags()).Release(table); + return GetPool(type, flags).AllocateHandle(count); } D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandle(DescriptorHandle handle) const @@ -378,6 +471,16 @@ namespace AZ return GetPool(handle.m_type, handle.m_flags).GetGpuPlatformHandle(handle); } + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetCpuPlatformHandleForTable(descTable); + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorContext::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetGpuPlatformHandleForTable(descTable); + } + DescriptorHandle DescriptorContext::GetNullHandleSRV(D3D12_SRV_DIMENSION dimension) const { auto iter = m_nullDescriptorsSRV.find(dimension); @@ -431,14 +534,88 @@ namespace AZ { AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type); AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag); - return m_pools[type][flag]; + + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag)) + { + return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex]; + } + else + { + return m_pools[type][flag]; + } } const DescriptorPool& DescriptorContext::GetPool(uint32_t type, uint32_t flag) const { AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type); AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag); - return m_pools[type][flag]; + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag)) + { + return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex]; + } + else + { + return m_pools[type][flag]; + } + } + + RHI::ResultCode DescriptorContext::CompactDescriptorHeap() + { + //Check if heap compaction is enabled by the user. Since there is an overhead associated with heap compaction it is not enabled by default + if(!m_allowDescriptorHeapCompaction) + { + AZ_Assert( + false, + "Descriptor heap Compaction not allowed. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV or enabling AllowDescriptorHeapCompaction within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + //We need to ping-pong between two heaps as we cannot compact the active heap without updating it and that is not allowed as + //we need to keep that gpu memory untouched until GPU is finished consuming which can take up to 3 frames. + m_compactionInProgress = true; + DescriptorPool& srcPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + + //Update the currently active heap index + m_currentHeapIndex = !m_currentHeapIndex; + DescriptorPool& destPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + + //Copy over all the static handles first + for (size_t i = 0; i < m_platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles; i++) + { + DescriptorHandle srcHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, static_cast(i)); + DescriptorHandle destHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, static_cast(i)); + m_device->CopyDescriptorsSimple(1, destPool.GetCpuPlatformHandle(destHandle), m_backupStaticHandles.GetCpuPlatformHandle(srcHandle), destHandle.m_type); + } + + //Clone the allocator of the source pool into the destination pool + srcPool.CloneAllocator(destPool.GetAllocator()); + + { + //The mutex is here 'just in case' Compaction is called from more than one thread. + AZStd::scoped_lock lock{ m_srgMapMutex }; + //Re-update all the descriptor tables associated with active SRGs + for (const auto& [srg, numAllocations] : m_srgAllocations) + { + RHI::ResultCode resultCode = static_cast(srg->GetPool())->UpdateDescriptorTableAfterCompaction(*srg, srg->GetData()); + if (resultCode != RHI::ResultCode::Success) + { + return resultCode; + } + } + } + + //Clear the allocator of the source pool + srcPool.ClearAllocator(); + + m_compactionInProgress = false; + + return RHI::ResultCode::Success; + } + + bool DescriptorContext::IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const + { + return type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV && flag == D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h index d4b6479fb2..88d2781329 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h @@ -15,6 +15,8 @@ #include #include #include +#include +#include namespace AZ { @@ -82,12 +84,14 @@ namespace AZ //! Creates a GPU-visible descriptor table. //! @param descriptorHeapType The descriptor heap to allocate from. //! @param descriptorCount The number of descriptors to allocate. + //! @param srg Shader resource group with which the descriptor table is associated with DescriptorTable CreateDescriptorTable( - D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, - uint32_t descriptorCount); - - void ReleaseDescriptorTable(DescriptorTable descriptorTable); + D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg); + //! Releases a GPU-visible descriptor table. + //! @param descriptorHeapType The descriptor heap to allocate from. + //! @param srg Shader resource group with which the descriptor table is associated with + void ReleaseDescriptorTable(DescriptorTable descriptorTable, ShaderResourceGroup* srg); //! Performs a gather of disjoint CPU-side descriptors and copies to a contiguous GPU-side descriptor table. //! @param gpuDestinationTable The destination descriptor table that the descriptors will be uploaded to. @@ -110,6 +114,8 @@ namespace AZ D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const; D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const; + D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable descTable) const; + D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable descTable) const; void SetDescriptorHeaps(ID3D12GraphicsCommandList* commandList) const; @@ -117,6 +123,12 @@ namespace AZ ID3D12DeviceX* GetDevice(); + //! Since we are only allowed one shader visible CbvSrvUav heap of a limited size in certain hardware, it is possible that + //! it can get fragmented by constant alloc/de-alloc of descriptor tables related to direct views or unbounded resource views within a SRG. We use two + //! heaps to ping pong during compaction as fragmentation can occur many times. It copies static handles directly and for all the + //! dynamic handles we re-update the new heap by copying over the handles from the 'non-shader visible' heap. + RHI::ResultCode CompactDescriptorHeap(); + private: void CopyDescriptor(DescriptorHandle dst, DescriptorHandle src); @@ -129,10 +141,13 @@ namespace AZ DescriptorPool& GetPool(uint32_t type, uint32_t flag); const DescriptorPool& GetPool(uint32_t type, uint32_t flag) const; - DescriptorTable Allocate( - D3D12_DESCRIPTOR_HEAP_TYPE type, - D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t count); + //! Allocates a Descriptor table which describes a contiguous range of descriptor handles + DescriptorTable AllocateTable(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count); + + //! Allocates a single descriptor handle + DescriptorHandle AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count); + + bool IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const; static const uint32_t NumHeapFlags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE + 1; static const uint32_t s_descriptorCountMax[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES][NumHeapFlags]; @@ -147,6 +162,25 @@ namespace AZ DescriptorHandle m_nullSamplerDescriptor; RHI::ConstPtr m_platformLimitsDescriptor; + + // Use 2 heaps below in order to ping-pong between shader visible CbvSrvUav heap when one of them fragments and run out of memory. + static const uint32_t MaxShaderVisibleCbvSrvUavHeaps = 2; + DescriptorPoolShaderVisibleCbvSrvUav m_shaderVisibleCbvSrvUavPools[MaxShaderVisibleCbvSrvUavHeaps]; + //This pool stores a copy of static handles that can later be used to recreate the compacted shader visible CbvSrvUav heap. + DescriptorPool m_backupStaticHandles; + + //Boolean to dictate when compaction was in progress + bool m_compactionInProgress = false; + + //Boolean to dictate if we should support compaction for shader visible CbvSrvUav heap + bool m_allowDescriptorHeapCompaction = false; + + //Map to store active SRGs and the number of associated descriptor tables. This is used to recreate the new compacted heap when we switch heaps + AZStd::unordered_map m_srgAllocations; + AZStd::mutex m_srgMapMutex; + + //Index that holds the currently active shader visible CbvSrvUav heap + uint32_t m_currentHeapIndex = 0; }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp index 7a6d96a519..96a3979f41 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp @@ -18,34 +18,38 @@ namespace AZ ID3D12DeviceX* device, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t descriptorCount) + uint32_t descriptorCountForHeap, + uint32_t descriptorCountForAllocator) { - m_Desc.Type = type; - m_Desc.Flags = flags; - m_Desc.NumDescriptors = descriptorCount; - m_Desc.NodeMask = 1; + m_desc.Type = type; + m_desc.Flags = flags; + m_desc.NumDescriptors = descriptorCountForHeap; + m_desc.NodeMask = 1; ID3D12DescriptorHeap* heap; - DX12::AssertSuccess(device->CreateDescriptorHeap(&m_Desc, IID_GRAPHICS_PPV_ARGS(&heap))); + DX12::AssertSuccess(device->CreateDescriptorHeap(&m_desc, IID_GRAPHICS_PPV_ARGS(&heap))); heap->SetName(L"DescriptorHeap"); - m_DescriptorHeap.Attach(heap); - m_Stride = device->GetDescriptorHandleIncrementSize(m_Desc.Type); + m_descriptorHeap.Attach(heap); + m_stride = device->GetDescriptorHandleIncrementSize(m_desc.Type); - m_CpuStart = heap->GetCPUDescriptorHandleForHeapStart(); - m_GpuStart = {}; + m_cpuStart = heap->GetCPUDescriptorHandleForHeapStart(); + m_gpuStart = {}; - if (RHI::CheckBitsAny(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)) + const bool isGpuVisible = RHI::CheckBitsAll(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + if (isGpuVisible) { - m_GpuStart = heap->GetGPUDescriptorHandleForHeapStart(); + m_gpuStart = heap->GetGPUDescriptorHandleForHeapStart(); } - const bool isGpuVisible = RHI::CheckBitsAll(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); if (isGpuVisible) { RHI::FreeListAllocator::Descriptor descriptor; descriptor.m_alignmentInBytes = 1; - descriptor.m_capacityInBytes = descriptorCount; + + //It is possible for descriptorCountForAllocator to not match descriptorCountForHeap for DescriptorPoolShaderVisibleCbvSrvUav + //heaps in which case descriptorCountForAllocator defines the number of static handles + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCountForAllocator); descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax; RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator(); @@ -56,10 +60,11 @@ namespace AZ { // Non-shader-visible heaps don't require contiguous descriptors. Therefore, we can allocate // them using a block allocator. + RHI::PoolAllocator::Descriptor descriptor; descriptor.m_alignmentInBytes = 1; descriptor.m_elementSize = 1; - descriptor.m_capacityInBytes = descriptorCount; + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCountForAllocator); descriptor.m_garbageCollectLatency = 0; RHI::PoolAllocator* allocator = aznew RHI::PoolAllocator(); @@ -68,7 +73,7 @@ namespace AZ } } - DescriptorTable DescriptorPool::Allocate(uint32_t count) + DescriptorHandle DescriptorPool::AllocateHandle(uint32_t count) { RHI::VirtualAddress address; { @@ -78,24 +83,34 @@ namespace AZ if (address.IsValid()) { - DescriptorHandle handle(m_Desc.Type, m_Desc.Flags, static_cast(address.m_ptr)); - return DescriptorTable(handle, static_cast(count)); + DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast(address.m_ptr)); + return handle; } else { - return DescriptorTable{}; + return DescriptorHandle{}; } } - void DescriptorPool::Release(DescriptorTable table) + void DescriptorPool::ReleaseHandle(DescriptorHandle handle) { - if (table.IsNull()) + if (handle.IsNull()) { return; } AZStd::lock_guard lock(m_mutex); - m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index)); + m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(handle.m_index)); + } + + DescriptorTable DescriptorPool::AllocateTable(uint32_t count) + { + return DescriptorTable(AllocateHandle(count), static_cast(count)); + } + + void DescriptorPool::ReleaseTable(DescriptorTable table) + { + ReleaseHandle(table.GetOffset()); } void DescriptorPool::GarbageCollect() @@ -106,20 +121,134 @@ namespace AZ ID3D12DescriptorHeap* DescriptorPool::GetPlatformHeap() const { - return m_DescriptorHeap.Get(); + return m_descriptorHeap.Get(); } D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandle(DescriptorHandle handle) const { AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); - return D3D12_CPU_DESCRIPTOR_HANDLE{ m_CpuStart.ptr + handle.m_index * m_Stride }; + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride }; } D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandle(DescriptorHandle handle) const { AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); - return D3D12_GPU_DESCRIPTOR_HANDLE{ m_GpuStart.ptr + handle.m_index * m_Stride }; + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) }; + } + + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride }; + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) }; + } + + void DescriptorPool::CloneAllocator(RHI::Allocator* newAllocator) + { + m_allocator->Clone(newAllocator); + } + + void DescriptorPool::ClearAllocator() + { + AZ_Assert(m_gpuStart.ptr, "Clearing the allocator is only supported for the gpu visible heap as only this heap can be compacted"); + static_cast(m_allocator.get()) + ->Init(static_cast(m_allocator.get())->GetDescriptor()); + } + + RHI::Allocator* DescriptorPool::GetAllocator() const + { + return m_allocator.get(); + } + + void DescriptorPoolShaderVisibleCbvSrvUav::Init( + ID3D12DeviceX* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + uint32_t descriptorCount, + uint32_t staticHandlesCount) + { + //This pool manages two allocators. The allocator in the base class manages static handles + Base::Init(device, type, flags, descriptorCount, staticHandlesCount); + + //This allocator manages dynamic handles associated with descriptor tables. This allows us to + //reconstruct the full heap in a compact manner if it ever fragments. + RHI::FreeListAllocator::Descriptor descriptor; + descriptor.m_alignmentInBytes = 1; + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCount - staticHandlesCount); + descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax; + + RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator(); + allocator->Init(descriptor); + m_unboundedArrayAllocator.reset(allocator); + + //Cache the starting point of the dynamic section of the heap + m_startingHandleIndex = staticHandlesCount; + } + + DescriptorTable DescriptorPoolShaderVisibleCbvSrvUav::AllocateTable(uint32_t count) + { + RHI::VirtualAddress address; + { + AZStd::lock_guard lock(m_mutex); + address = m_unboundedArrayAllocator->Allocate(count, 1); + } + + if (address.IsValid()) + { + DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast(address.m_ptr)); + return DescriptorTable(handle, static_cast(count)); + } + else + { + return DescriptorTable{}; + } + } + + void DescriptorPoolShaderVisibleCbvSrvUav::ReleaseTable(DescriptorTable table) + { + if (table.IsNull()) + { + return; + } + + AZStd::lock_guard lock(m_mutex); + m_unboundedArrayAllocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index)); + } + + void DescriptorPoolShaderVisibleCbvSrvUav::GarbageCollect() + { + Base::GarbageCollect(); + m_unboundedArrayAllocator->GarbageCollect(); + } + + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) }; + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) }; + } + + void DescriptorPoolShaderVisibleCbvSrvUav::ClearAllocator() + { + Base::ClearAllocator(); + static_cast(m_unboundedArrayAllocator.get())->Init(static_cast(m_unboundedArrayAllocator.get())->GetDescriptor()); } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h index cd90d1aba3..2237841ea7 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h @@ -18,37 +18,91 @@ namespace AZ { namespace DX12 { + //! This class defines a Descriptor pool which manages all the descriptors used for binding resources class DescriptorPool { public: DescriptorPool() = default; + virtual ~DescriptorPool() = default; - void Init( + //! Initialize the native heap as well as init the allocators tracking the memory for descriptor handles + virtual void Init( ID3D12DeviceX* device, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t descriptorCount); + uint32_t descriptorCountForHeap, + uint32_t descriptorCountForAllocator); ID3D12DescriptorHeap* GetPlatformHeap() const; - DescriptorTable Allocate(uint32_t count = 1); - - void Release(DescriptorTable table); + //! Allocate a Descriptor handles + DescriptorHandle AllocateHandle(uint32_t count = 1); + //! Release a descriptor handle + void ReleaseHandle(DescriptorHandle table); + //! Allocate a range contiguous handles (i.e Descriptor table) + virtual DescriptorTable AllocateTable(uint32_t count = 1); + //! Release a range contiguous handles (i.e Descriptor table) + virtual void ReleaseTable(DescriptorTable table); + //! Garbage collection for freed handles or tables + virtual void GarbageCollect(); + //Get native pointers from the heap + virtual D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const; + virtual D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const; + //Clear the tracking allocator + virtual void ClearAllocator(); - void GarbageCollect(); D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const; D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const; - private: - D3D12_CPU_DESCRIPTOR_HANDLE m_CpuStart = {}; - D3D12_GPU_DESCRIPTOR_HANDLE m_GpuStart = {}; - D3D12_CPU_DESCRIPTOR_HANDLE m_NullDescriptor = {}; - uint32_t m_Stride = 0; - D3D12_DESCRIPTOR_HEAP_DESC m_Desc; - Microsoft::WRL::ComPtr m_DescriptorHeap; + //Clone the tracking allocator + void CloneAllocator(RHI::Allocator* newAllocator); + RHI::Allocator* GetAllocator() const; + + protected: + D3D12_DESCRIPTOR_HEAP_DESC m_desc; AZStd::mutex m_mutex; + D3D12_CPU_DESCRIPTOR_HANDLE m_cpuStart = {}; + D3D12_GPU_DESCRIPTOR_HANDLE m_gpuStart = {}; + uint32_t m_stride = 0; + private: + + // Native heap + Microsoft::WRL::ComPtr m_descriptorHeap; + + // Allocator used to manage the whole native heap. In the case of DescriptorPoolShaderVisibleCbvSrvUav this allocator + // is used to manage the part of the heap that only manages static handles. AZStd::unique_ptr m_allocator; }; + + //! A specialized pool that specifically handles Descriptor tables for Cbv/Srv/Uav views and allows for Compaction + //! Specifically this pool handles the dynamic part of the heap + class DescriptorPoolShaderVisibleCbvSrvUav : public DescriptorPool + { + using Base = DescriptorPool; + + public: + void Init( + ID3D12DeviceX* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + uint32_t descriptorCount, + uint32_t staticHandlesCount); + + DescriptorTable AllocateTable(uint32_t count = 1) override; + void ReleaseTable(DescriptorTable table) override; + void GarbageCollect() override; + + D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const override; + D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const override; + void ClearAllocator() override; + + private: + + // A separate allocator that handles descriptor tables which are dynamic in nature and may fragment and require compaction + AZStd::unique_ptr m_unboundedArrayAllocator; + //Starting index of the dynamic part of the heap + uint32_t m_startingHandleIndex = 0; + }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp index 82e4aba57c..6f614499d2 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp @@ -625,5 +625,20 @@ namespace AZ { return m_isAftermathInitialized; } + + RHI::ResultCode Device::CompactSRGMemory() + { + if (m_isDescriptorHeapCompactionNeeded) + { + m_isDescriptorHeapCompactionNeeded = false; + return m_descriptorContext->CompactDescriptorHeap(); + } + return RHI::ResultCode::Success; + } + + void Device::DescriptorHeapCompactionNeeded() + { + m_isDescriptorHeapCompactionNeeded = true; + } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h index 7004900ec5..40e26db891 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h @@ -98,39 +98,27 @@ namespace AZ D3D12_RESOURCE_STATES initialState, ImageTileLayout& imageTilingInfo); - /** - * Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed - * through the GPU. - */ + //! Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed + //! through the GPU. void QueueForRelease(RHI::Ptr dx12Object); - /** - * Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the - * current frame has flushed through the GPU. The reference on the MemoryView itself is not released. - */ + //! Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the + //! current frame has flushed through the GPU. The reference on the MemoryView itself is not released. void QueueForRelease(const MemoryView& memoryView); - /** - * Allocates host memory from the internal frame allocator that is suitable for staging - * uploads to the GPU for the current frame. The memory is valid for the lifetime of - * the frame and is automatically reclaimed after the frame has completed on the GPU. - */ + //! Allocates host memory from the internal frame allocator that is suitable for staging + //! uploads to the GPU for the current frame. The memory is valid for the lifetime of + //! the frame and is automatically reclaimed after the frame has completed on the GPU. MemoryView AcquireStagingMemory(size_t size, size_t alignment); - /** - * Acquires a pipeline layout from the internal cache. - */ + //! Acquires a pipeline layout from the internal cache. RHI::ConstPtr AcquirePipelineLayout(const RHI::PipelineLayoutDescriptor& descriptor); - /** - * Acquires a new command list for the frame given the hardware queue class. The command list is - * automatically reclaimed after the current frame has flushed through the GPU. - */ + //! Acquires a new command list for the frame given the hardware queue class. The command list is + //! automatically reclaimed after the current frame has flushed through the GPU. CommandList* AcquireCommandList(RHI::HardwareQueueClass hardwareQueueClass); - /** - * Acquires a sampler from the internal cache. - */ + //! Acquires a sampler from the internal cache. RHI::ConstPtr AcquireSampler(const RHI::SamplerState& state); const PhysicalDevice& GetPhysicalDevice() const; @@ -146,6 +134,10 @@ namespace AZ AsyncUploadQueue& GetAsyncUploadQueue(); bool IsAftermathInitialized() const; + + //! Indicate that we need to compact the shader visible srv/uav/cbv shader visible heap. + void DescriptorHeapCompactionNeeded(); + private: Device(); @@ -167,6 +159,7 @@ namespace AZ RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::ImageDescriptor & descriptor) override; RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::BufferDescriptor & descriptor) override; void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override; + RHI::ResultCode CompactSRGMemory() override; ////////////////////////////////////////////////////////////////////////// RHI::ResultCode InitSubPlatform(RHI::PhysicalDevice& physicalDevice); @@ -198,6 +191,9 @@ namespace AZ AZStd::mutex m_samplerCacheMutex; bool m_isAftermathInitialized = false; + + // Boolean used to compact the view specific shader visible heap + bool m_isDescriptorHeapCompactionNeeded = false; }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp index 66b3f9623a..63625fde72 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp @@ -49,9 +49,11 @@ namespace AZ AZStd::array_view bytes; bool shouldCreateLibFromSerializedData = true; - if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded()) + if (RHI::Factory::Get().IsRenderDocModuleLoaded() || + RHI::Factory::Get().IsPixModuleLoaded() || + RHI::Factory::Get().UsingWarpDevice()) { - // CreatePipelineLibrary api does not function properly if Renderdoc or Pix is enabled + // CreatePipelineLibrary api does not function properly if Renderdoc, Pix or Warp is enabled shouldCreateLibFromSerializedData = false; } @@ -215,9 +217,11 @@ namespace AZ RHI::ResultCode PipelineLibrary::MergeIntoInternal([[maybe_unused]] AZStd::array_view pipelineLibraries) { - if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded()) + if (RHI::Factory::Get().IsRenderDocModuleLoaded() || + RHI::Factory::Get().IsPixModuleLoaded() || + RHI::Factory::Get().UsingWarpDevice()) { - // StorePipeline api does not function properly if RenderDoc or Pix is enabled + // StorePipeline api does not function properly if RenderDoc, Pix or Warp is enabled return RHI::ResultCode::Fail; } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h index 4c686fe525..0a7185ccd5 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h @@ -51,6 +51,7 @@ namespace AZ ShaderResourceGroup() = default; friend class ShaderResourceGroupPool; + friend class DescriptorContext; /// The current index into the compiled data array. uint32_t m_compiledDataIndex = 0; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp index d51fdd3495..bd648cf535 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp @@ -132,33 +132,17 @@ namespace AZ compiledData.m_cpuConstantAddress = cpuAddress + m_constantBufferSize * i; } } - - if (m_viewsDescriptorTableSize) - { - group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize); - - if (!group.m_viewsDescriptorTable.IsValid()) - { - AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate view descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12"); - return RHI::ResultCode::OutOfMemory; - } - - for (uint32_t i = 0; i < copyCount; ++i) - { - const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i; - - ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; - compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle); - } - } - + if (m_samplersDescriptorTableSize) { - group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize); + group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize, &group); if (!group.m_samplersDescriptorTable.IsValid()) { - AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate sampler descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12."); + AZ_Error( + "ShaderResourceGroupPool", false, + "Descriptor context failed to allocate sampler descriptor table. Please consider increasing number of handles " + "allowed for the second value of DESCRIPTOR_HEAP_TYPE_SAMPLER within platformlimits.azasset file for dx12."); return RHI::ResultCode::OutOfMemory; } @@ -167,7 +151,7 @@ namespace AZ const DescriptorHandle descriptorHandle = group.m_samplersDescriptorTable.GetOffset() + m_samplersDescriptorTableSize * i; ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; - compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle); + compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable(DescriptorTable(descriptorHandle, static_cast(m_samplersDescriptorTableSize))); } } @@ -186,19 +170,25 @@ namespace AZ if (m_viewsDescriptorTableSize) { - m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable); + if (group.m_viewsDescriptorTable.IsValid()) + { + m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable, &group); + } } if (m_samplersDescriptorTableSize) { - m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable); + if (group.m_viewsDescriptorTable.IsValid()) + { + m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable, &group); + } } for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex) { if (group.m_unboundedDescriptorTables[unboundedArrayindex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex], &group); } } @@ -213,6 +203,7 @@ namespace AZ const RHI::ShaderResourceGroupData& groupData) { ShaderResourceGroup& group = static_cast(groupBase); + auto& device = static_cast(GetDevice()); group.m_compiledDataIndex = (group.m_compiledDataIndex + 1) % RHI::Limits::Device::FrameCountMax; if (m_constantBufferSize) @@ -222,6 +213,22 @@ namespace AZ if (m_viewsDescriptorTableSize) { + //Lazy initialization for cbv/srv/uav Descriptor Tables + if (!group.m_viewsDescriptorTable.IsValid()) + { + group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group); + + if (!group.m_viewsDescriptorTable.IsValid()) + { + //We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that. + device.DescriptorHeapCompactionNeeded(); + return RHI::ResultCode::Success; + } + + CacheGpuHandlesForViews(group); + } + const DescriptorTable descriptorTable( group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize, static_cast(m_viewsDescriptorTableSize)); @@ -246,6 +253,18 @@ namespace AZ return RHI::ResultCode::Success; } + void ShaderResourceGroupPool::CacheGpuHandlesForViews(ShaderResourceGroup& group) + { + for (uint32_t i = 0; i < RHI::Limits::Device::FrameCountMax; ++i) + { + const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i; + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; + compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable( + DescriptorTable(descriptorHandle, static_cast(m_viewsDescriptorTableSize))); + } + } + void ShaderResourceGroupPool::UpdateViewsDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData) { const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); @@ -261,27 +280,27 @@ namespace AZ AZStd::vector descriptorHandles; switch (descriptorRangeType) { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: - { - descriptorHandles = GetCBVsFromBufferViews(bufferViews); - break; - } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); - break; + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: + { + descriptorHandles = GetCBVsFromBufferViews(bufferViews); + break; + } + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; } - UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex); + UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex); ++shaderInputIndex; } @@ -297,23 +316,24 @@ namespace AZ AZStd::vector descriptorHandles; switch (descriptorRangeType) { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImage.m_type)); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImage.m_type)); - break; - } - default: + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = + GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImage.m_type)); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = + GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImage.m_type)); + break; + } + default: AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); break; } UpdateDescriptorTableRange(descriptorTable, descriptorHandles, imageInputIndex); - ++shaderInputIndex; } } @@ -334,7 +354,7 @@ namespace AZ void ShaderResourceGroupPool::UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData) { const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); - + auto& device = static_cast(GetDevice()); uint32_t shaderInputIndex = 0; // process buffer unbounded arrays @@ -350,50 +370,30 @@ namespace AZ { if (group.m_unboundedDescriptorTables[tableIndex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group); group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{}; } if (!bufferViews.empty()) { - group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size())); - AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory."); + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + // We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that. + device.DescriptorHeapCompactionNeeded(); + return; + } ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; - compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset()); + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); } } - - ++shaderInputIndex; - - if (bufferViews.empty()) - { - // we don't need to update descriptors since the buffer list is empty - continue; - } - - D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(shaderInputBufferUnboundedArray.m_access); - - AZStd::vector descriptorHandles; - switch (descriptorRangeType) - { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews(bufferViews, D3D12_SRV_DIMENSION_BUFFER); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); - break; - } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); - break; - } - + const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(bufferViews.size())); - m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access); + ++shaderInputIndex; } // process image unbounded arrays @@ -407,53 +407,221 @@ namespace AZ // resize the descriptor table allocation if necessary if (group.m_unboundedDescriptorTables[tableIndex].GetSize() != imageViews.size()) { + if (group.m_unboundedDescriptorTables[tableIndex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group); group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{}; } if (!imageViews.empty()) { - group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size())); - AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory."); + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + // We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that + device.DescriptorHeapCompactionNeeded(); + return; + } ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; - compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset()); + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); } } + const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size())); + UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type); ++shaderInputIndex; + } + } - if (imageViews.empty()) - { - // we don't need to update descriptors since the image list is empty - continue; - } + void ShaderResourceGroupPool::UpdateUnboundedBuffersDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputBufferAccess bufferAccess) + { + const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> bufferViews = + groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex); - D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(shaderInputImageUnboundedArray.m_access); + if (bufferViews.empty()) + { + // we don't need to update descriptors since the buffer list is empty + return; + } - AZStd::vector descriptorHandles; - switch (descriptorRangeType) + D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(bufferAccess); + + AZStd::vector descriptorHandles; + switch (descriptorRangeType) + { + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + descriptorHandles = GetSRVsFromImageViews(bufferViews, D3D12_SRV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: { - descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImageUnboundedArray.m_type)); + descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); break; } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; + } + + m_descriptorContext->UpdateDescriptorTableRange( + descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + void ShaderResourceGroupPool::UpdateUnboundedImagesDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputImageAccess imageAccess, + RHI::ShaderInputImageType imageType) + { + const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> imageViews = + groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex); + + if (imageViews.empty()) + { + // we don't need to update descriptors since the image list is empty + return; + } + + D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(imageAccess); + + AZStd::vector descriptorHandles; + switch (descriptorRangeType) + { + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: { - descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImageUnboundedArray.m_type)); + descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(imageType)); break; } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(imageType)); break; } + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; + } - const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size())); - m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + m_descriptorContext->UpdateDescriptorTableRange( + descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + RHI::ResultCode ShaderResourceGroupPool::UpdateDescriptorTableAfterCompaction( + RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData) + { + // Since we are trying to compact we will re-create all the descriptor tables and re-update them all + ShaderResourceGroup& group = static_cast(groupBase); + + if (m_viewsDescriptorTableSize) + { + group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group); + + if (!group.m_viewsDescriptorTable.IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + CacheGpuHandlesForViews(group); + + const DescriptorTable descriptorTable( + group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize, + static_cast(m_viewsDescriptorTableSize)); + + UpdateViewsDescriptorTable(descriptorTable, groupData); + } + + if (m_unboundedArrayCount) + { + //Reset all the old descriptor tables as the previous heap is gone. + for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex) + { + group.m_unboundedDescriptorTables[unboundedArrayindex] = DescriptorTable{}; + } + + const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); + uint32_t shaderInputIndex = 0; + + // process buffer unbounded arrays + for (const RHI::ShaderInputBufferUnboundedArrayDescriptor& shaderInputBufferUnboundedArray : groupLayout.GetShaderInputListForBufferUnboundedArrays()) + { + const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> bufferViews = groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex); + + uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex; + if (!bufferViews.empty()) + { + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); + + const DescriptorTable descriptorTable( + group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(bufferViews.size())); + UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access); + } + shaderInputIndex++; + } + + // process image unbounded arrays + for (const RHI::ShaderInputImageUnboundedArrayDescriptor& shaderInputImageUnboundedArray : + groupLayout.GetShaderInputListForImageUnboundedArrays()) + { + const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> imageViews = + groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex); + + uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex; + if (!imageViews.empty()) + { + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); + + const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size())); + UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type); + } + shaderInputIndex++; + } } + return RHI::ResultCode::Success; } void ShaderResourceGroupPool::OnFrameEnd() diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h index bbe7e6596d..e1b2145097 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h @@ -30,6 +30,9 @@ namespace AZ static RHI::Ptr Create(); + //! Re-Update the descriptor tables for all the cbv/srv/uav views (direct and via unbounded array) + RHI::ResultCode UpdateDescriptorTableAfterCompaction(RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData); + private: ShaderResourceGroupPool() = default; @@ -51,6 +54,21 @@ namespace AZ void UpdateSamplersDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData); void UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData); + //! Update all the buffer views for the unbounded array + void UpdateUnboundedBuffersDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputBufferAccess bufferAccess); + + //! Update all the image views for the unbounded array + void UpdateUnboundedImagesDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputImageAccess imageAccess, + RHI::ShaderInputImageType imageType); + void UpdateDescriptorTableRange( DescriptorTable descriptorTable, const AZStd::vector& descriptors, @@ -66,6 +84,9 @@ namespace AZ RHI::ShaderInputSamplerIndex samplerIndex, AZStd::array_view samplerStates); + //Cache all the gpu handles for the Descriptor tables related to all the views + void CacheGpuHandlesForViews(ShaderResourceGroup& group); + DescriptorTable GetBufferTable(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const; DescriptorTable GetBufferTableUnbounded(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const; DescriptorTable GetImageTable(DescriptorTable descriptorTable, RHI::ShaderInputImageIndex imageIndex) const; @@ -79,7 +100,6 @@ namespace AZ AZStd::vector GetCBVsFromBufferViews(const AZStd::array_view>& bufferViews); - AZStd::mutex m_constantAllocatorMutex; MemoryPoolSubAllocator m_constantAllocator; DescriptorContext* m_descriptorContext = nullptr; uint32_t m_constantBufferSize = 0; diff --git a/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg b/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg index 240f8b041c..22b7d1bf29 100644 --- a/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg +++ b/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg @@ -21,11 +21,13 @@ "$type": "AZ::DX12::PlatformLimitsDescriptor", "DescriptorHeapLimits": { - "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [1000000, 1000000], + "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [100000, 1000000], "DESCRIPTOR_HEAP_TYPE_SAMPLER": [2048, 2048], "DESCRIPTOR_HEAP_TYPE_RTV": [2048, 0], "DESCRIPTOR_HEAP_TYPE_DSV": [2048, 0] - } + }, + "NumShaderVisibleCbvSrvUavStaticHandles": 2000, + "AllowDescriptorHeapCompaction": false }, "vulkan": { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp index a818599b0d..7d70d0245f 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp @@ -10,6 +10,10 @@ #include #include +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif + namespace AZ { namespace Vulkan @@ -21,7 +25,7 @@ namespace AZ #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB xcb_connection_t* xcb_connection = nullptr; - if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get(); + if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get(); xcbConnectionManager != nullptr) { xcb_connection = xcbConnectionManager->GetXcbConnection(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyConnectionSerializer.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyConnectionSerializer.h new file mode 100644 index 0000000000..44b8805667 --- /dev/null +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyConnectionSerializer.h @@ -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 +#include + +namespace AZ +{ + class ReflectContext; + + namespace RPI + { + //! The property connection itself is rather simple, but we need this custom serializer to provide backward compatibility + //! for when the "id" key was changed to "name". If the JSON serialization system is ever updated to provide built-in + //! support for versioning, then we can probably remove this class. + class JsonMaterialPropertyConnectionSerializer + : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonMaterialPropertyConnectionSerializer, "{2B7F00CF-51F7-4409-9C0E-914E59696FB9}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + + JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, + const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) override; + }; + + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyGroupSerializer.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyGroupSerializer.h new file mode 100644 index 0000000000..138dace86a --- /dev/null +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyGroupSerializer.h @@ -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 +#include + +namespace AZ +{ + class ReflectContext; + + namespace RPI + { + //! The property group itself is rather simple, but we need this custom serializer to provide backward compatibility + //! for when the "id" key was changed to "name". If the JSON serialization system is ever updated to provide built-in + //! support for versioning, then we can probably remove this class. + class JsonMaterialPropertyGroupSerializer + : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonMaterialPropertyGroupSerializer, "{74C56BBC-2084-46AF-9393-04C2FBDF6B20}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + + JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, + const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) override; + }; + + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h index 7019289466..04b6222404 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h @@ -40,12 +40,12 @@ namespace AZ AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::PropertyConnection, "{C2F37C26-D7EF-4142-A650-EF50BB18610F}"); PropertyConnection() = default; - PropertyConnection(MaterialPropertyOutputType type, AZStd::string_view nameId, int32_t shaderIndex = -1); + PropertyConnection(MaterialPropertyOutputType type, AZStd::string_view fieldName, int32_t shaderIndex = -1); MaterialPropertyOutputType m_type = MaterialPropertyOutputType::Invalid; //! The name of a specific shader setting. This will either be a ShaderResourceGroup input or a ShaderOption, depending on m_type - AZStd::string m_nameId; + AZStd::string m_fieldName; //! For m_type==ShaderOption, this is either the index of a specific shader in m_shaderCollection, or -1 which means every shader in m_shaderCollection. //! For m_type==ShaderInput, this field is not used. @@ -58,8 +58,8 @@ namespace AZ { AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::GroupDefinition, "{B2D0FC5C-72A3-435E-A194-1BFDABAC253D}"); - //! The unique name of the property group. A property's full ID will be groupNameId.propertyNameId. - AZStd::string m_nameId; + //! The unique name of the property group. The full property ID will be groupName.propertyName + AZStd::string m_name; // Editor metadata ... AZStd::string m_displayName; @@ -74,7 +74,7 @@ namespace AZ static const float DefaultMax; static const float DefaultStep; - AZStd::string m_nameId; //!< The name of the property within the property group. The full ID will be groupNameId.propertyNameId. + AZStd::string m_name; //!< The name of the property within the property group. The full property ID will be groupName.propertyName. MaterialPropertyVisibility m_visibility = MaterialPropertyVisibility::Default; @@ -130,7 +130,7 @@ namespace AZ AZStd::vector m_groups; //! Collection of all available user-facing properties - AZStd::map m_properties; + AZStd::map m_properties; }; AZStd::string m_description; @@ -151,9 +151,9 @@ namespace AZ //! Copy over UV custom names to the properties enum values. void ResolveUvEnums(); - const GroupDefinition* FindGroup(AZStd::string_view groupNameId) const; + const GroupDefinition* FindGroup(AZStd::string_view groupName) const; - const PropertyDefinition* FindProperty(AZStd::string_view groupNameId, AZStd::string_view propertyNameId) const; + const PropertyDefinition* FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName) const; //! Construct a complete list of group definitions, including implicit groups, arranged in the same order as the source data //! Groups with the same name will be consolidated into a single entry diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialUtils.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialUtils.h index 7801e0c3a7..d12e848a02 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialUtils.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialUtils.h @@ -15,6 +15,13 @@ namespace AZ { + class JsonDeserializerContext; + + namespace JsonSerializationResult + { + union ResultCode; + } + namespace RPI { class MaterialTypeSourceData; @@ -35,6 +42,17 @@ namespace AZ //! @param filePath a relative path if document is provided, an absolute path if document is not provided. //! @param document the loaded json document. AZ::Outcome LoadMaterialTypeSourceData(const AZStd::string& filePath, const rapidjson::Value* document = nullptr); + + //! Utility function for custom JSON serializers to report results as "Skipped" when encountering keys that aren't recognized + //! as part of the custom format. + //! @param acceptedFieldNames an array of names that are recognized by the custom format + //! @param acceptedFieldNameCount the number of elements in @acceptedFieldNames + //! @param object the JSON object being loaded + //! @param context the common JsonDeserializerContext that is central to the serialization process + //! @param result the ResultCode that well be updated with the Outcomes "Skipped" if an unrecognized field is encountered + void CheckForUnrecognizedJsonFields( + const AZStd::string_view* acceptedFieldNames, uint32_t acceptedFieldNameCount, + const rapidjson::Value& object, JsonDeserializerContext& context, JsonSerializationResult::ResultCode& result); } } } diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h index 3976ef989b..d8a0cf0e7d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h @@ -70,8 +70,8 @@ namespace AZ virtual ~Material(); - //! Finds the material property index from the material property name - MaterialPropertyIndex FindPropertyIndex(const Name& name) const; + //! Finds the material property index from the material property ID + MaterialPropertyIndex FindPropertyIndex(const Name& propertyId) const; //! Sets the value of a material property. The template data type must match the property's data type. //! @return true if property value was changed diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h index a19c985f2d..b94d2169bf 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h @@ -24,6 +24,8 @@ namespace AZ { namespace RPI { + class ModelAsset; + class Model final : public Data::InstanceData { diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h index cc25aa17c4..90389687de 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h @@ -161,25 +161,19 @@ namespace AZ //! Add this RenderPipeline to RPI system's RenderTick and it will be rendered whenever //! the RPI system's RenderTick is called. - //! The RenderPipeline is rendered per RenderTick by default. + //! The RenderPipeline is rendered per RenderTick by default unless AddToRenderTickOnce() was called. void AddToRenderTick(); - //! Add this RenderPipeline to RPI system's RenderTick and it will be rendered every RenderTick - //! after the specified interval has elapsed since the last rendered frame. - //! @param renderInterval The desired time between rendered frames, in seconds. - void AddToRenderTickAtInterval(AZStd::chrono::duration renderInterval); - //! Disable render for this RenderPipeline void RemoveFromRenderTick(); ~RenderPipeline(); - + enum class RenderMode : uint8_t { - RenderEveryTick, //!< Render at each RPI system render tick. - RenderAtTargetRate, //!< Render on RPI system render tick after a target refresh rate interval has passed. - RenderOnce, //!< Render once in next RPI system render tick. - NoRender //!< Render disabled. + RenderEveryTick, // Render at each RPI system render tick + RenderOnce, // Render once in next RPI system render tick + NoRender // Render disabled. }; //! Get current render mode @@ -191,12 +185,6 @@ namespace AZ //! Get draw filter mask RHI::DrawFilterMask GetDrawFilterMask() const; - using FrameNotificationEvent = AZ::Event<>; - //! Notifies a listener when a frame is about to be prepared for render, before SRGs are bound. - void ConnectPrepareFrameHandler(FrameNotificationEvent::Handler& handler); - //! Notifies a listener when the rendering of a frame has finished - void ConnectEndFrameHandler(FrameNotificationEvent::Handler& handler); - private: RenderPipeline() = default; @@ -214,11 +202,8 @@ namespace AZ void OnAddedToScene(Scene* scene); void OnRemovedFromScene(Scene* scene); - // Called before this pipeline is about to be rendered and before SRGs are bound. - void OnPrepareFrame(); - // Called when this pipeline is about to be rendered - void OnStartFrame(); + void OnStartFrame(const TickTimeInfo& tick); // Called when the rendering of current frame is finished. void OnFrameEnd(); @@ -243,14 +228,8 @@ namespace AZ PipelineViewMap m_pipelineViewsByTag; - // The system time when the last time this pipeline render was started - AZStd::chrono::system_clock::time_point m_lastRenderStartTime; - - // The current system time, as of OnPrepareFrame's execution. - AZStd::chrono::system_clock::time_point m_lastRenderRequestTime; - - // The target time between renders when m_renderMode is RenderMode::RenderAtTargetRate - AZStd::chrono::duration m_targetRefreshRate; + /// The system time when the last time this pipeline render was started + float m_lastRenderStartTime = 0; // RenderPipeline's name id, it will be used to identify the render pipeline when it's added to a Scene RenderPipelineId m_nameId; @@ -280,11 +259,7 @@ namespace AZ RHI::DrawFilterTag m_drawFilterTag; // A mask to filter draw items submitted by passes of this render pipeline. // This mask is created from the value of m_drawFilterTag. - RHI::DrawFilterMask m_drawFilterMask = 0; - - // Events for notification on render state - FrameNotificationEvent m_prepareFrameEvent; - FrameNotificationEvent m_endFrameEvent; + RHI::DrawFilterMask m_drawFilterMask = 0; }; } // namespace RPI diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/SceneBus.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/SceneBus.h index 748c470edf..ca531d2de6 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/SceneBus.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/SceneBus.h @@ -67,9 +67,6 @@ namespace AZ //! Notifies when the PrepareRender phase is ending virtual void OnEndPrepareRender() {} - - //! Notifies when the render tick for a given frame has finished. - virtual void OnFrameEnd() {} }; using SceneNotificationBus = AZ::EBus; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h index feed24a80d..966e6b3016 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h @@ -51,21 +51,9 @@ namespace AZ //! Sets the root scene associated with this viewport. //! This does not provide a default render pipeline, one must be provided to enable rendering. void SetRenderScene(ScenePtr scene); - - //! Gets the maximum frame rate this viewport context's pipeline can render at, 0 for unlimited. - //! The target framerate for the pipeline will be determined by this frame limit and the - //! vsync settings for the current window. - float GetFpsLimit() const; - - //! Sets the maximum frame rate this viewport context's pipeline can render at, 0 for unlimited. - //! The target framerate for the pipeline will be determined by this frame limit and the - //! vsync settings for the current window. - void SetFpsLimit(float fpsLimit); - - //! Gets the target frame rate for this viewport context. - //! This returns the lowest of either the current VSync refresh rate - //! or 0 for an unlimited frame rate (if there's no FPS limit and vsync is off). - float GetTargetFrameRate() const; + //! Runs one simulation and render tick and renders a frame to this viewport's window. + //! @note This is likely to be replaced by a tick management system in the RPI. + void RenderTick(); //! Gets the current name of this ViewportContext. //! This name is used to tie this ViewportContext to its View stack, and ViewportContexts may be @@ -86,28 +74,19 @@ namespace AZ //! \see AzFramework::WindowRequests::GetDpiScaleFactor float GetDpiScalingFactor() const; - //! Gets the current vsync interval, as a divisor of the current refresh rate. - //! A value of 0 indicates that vsync is disabled. - uint32_t GetVsyncInterval() const; - - //! Gets the current display refresh rate, in frames per second. - uint32_t GetRefreshRate() const; - // SceneNotificationBus interface overrides... //! Ensures our default view remains set when our scene's render pipelines are modified. void OnRenderPipelineAdded(RenderPipelinePtr pipeline) override; //! Ensures our default view remains set when our scene's render pipelines are modified. void OnRenderPipelineRemoved(RenderPipeline* pipeline) override; + //! OnBeginPrepareRender is forwarded to our RenderTick notification to allow subscribers to do rendering. + void OnBeginPrepareRender() override; // WindowNotificationBus interface overrides... //! Used to fire a notification when our window resizes. void OnWindowResized(uint32_t width, uint32_t height) override; //! Used to fire a notification when our window DPI changes. void OnDpiScaleFactorChanged(float dpiScaleFactor) override; - //! Used to fire a notification when our vsync interval changes. - void OnVsyncIntervalChanged(uint32_t interval) override; - //! Used to fire a notification when our refresh rate changes. - void OnRefreshRateChanged(uint32_t refreshRate) override; using SizeChangedEvent = AZ::Event; //! Notifies consumers when the viewport size has changed. @@ -119,12 +98,6 @@ namespace AZ //! Alternatively, connect to ViewportContextNotificationsBus and listen to ViewportContextNotifications::OnViewportDpiScalingChanged. void ConnectDpiScalingFactorChangedHandler(ScalarChangedEvent::Handler& handler); - using UintChangedEvent = AZ::Event; - //! Notifies consumers when the vsync interval has changed. - void ConnectVsyncIntervalChangedHandler(UintChangedEvent::Handler& handler); - //! Notifies consumers when the refresh rate has changed. - void ConnectRefreshRateChangedHandler(UintChangedEvent::Handler& handler); - using MatrixChangedEvent = AZ::Event; //! Notifies consumers when the view matrix has changed. void ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler); @@ -166,24 +139,15 @@ namespace AZ void SetDefaultView(ViewPtr view); // Ensures our render pipeline's default camera matches ours. void UpdatePipelineView(); - // Ensures our render pipeline refresh rate matches our refresh rate. - void UpdatePipelineRefreshRate(); - // Resets the current pipeline reference and ensures pipeline events are disconnected. - void ResetCurrentPipeline(); ScenePtr m_rootScene; WindowContextSharedPtr m_windowContext; ViewPtr m_defaultView; AzFramework::WindowSize m_viewportSize; float m_viewportDpiScaleFactor = 1.0f; - uint32_t m_vsyncInterval = 1; - uint32_t m_refreshRate = 60; - float m_fpsLimit = 0.f; SizeChangedEvent m_sizeChangedEvent; ScalarChangedEvent m_dpiScalingFactorChangedEvent; - UintChangedEvent m_vsyncIntervalChangedEvent; - UintChangedEvent m_refreshRateChangedEvent; MatrixChangedEvent m_viewMatrixChangedEvent; MatrixChangedEvent::Handler m_onViewMatrixChangedHandler; MatrixChangedEvent m_projectionMatrixChangedEvent; @@ -193,9 +157,6 @@ namespace AZ ViewChangedEvent m_defaultViewChangedEvent; ViewportIdEvent m_aboutToBeDestroyedEvent; - AZ::Event<>::Handler m_prepareFrameHandler; - AZ::Event<>::Handler m_endFrameHandler; - ViewportContextManager* m_manager; RenderPipelinePtr m_currentPipeline; Name m_name; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextBus.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextBus.h index fa13a3987a..0b53172ba2 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextBus.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextBus.h @@ -110,13 +110,11 @@ namespace AZ virtual void OnViewportSizeChanged(AzFramework::WindowSize size){AZ_UNUSED(size);} //! Called when the window DPI scaling changes for a given viewport context. virtual void OnViewportDpiScalingChanged(float dpiScale){AZ_UNUSED(dpiScale);} - //! Called when the active view changes for a given viewport context. + //! Called when the active view for a given viewport context name changes. virtual void OnViewportDefaultViewChanged(AZ::RPI::ViewPtr view){AZ_UNUSED(view);} //! Called when the viewport is to be rendered. - //! Add draws to this function if they only need to be rendered to this viewport. - virtual void OnRenderTick(){} - //! Called when the viewport finishes rendering a frame. - virtual void OnFrameEnd(){} + //! Add draws to this functions if they only need to be rendered to this viewport. + virtual void OnRenderTick(){}; protected: ~ViewportContextNotifications() = default; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h index 45c8f0dc30..bc020b8129 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h @@ -35,7 +35,7 @@ namespace AZ AZ_DISABLE_COPY_MOVE(MaterialPropertiesLayout); size_t GetPropertyCount() const; - MaterialPropertyIndex FindPropertyIndex(const Name& propertyName) const; + MaterialPropertyIndex FindPropertyIndex(const Name& propertyId) const; const MaterialPropertyDescriptor* GetPropertyDescriptor(MaterialPropertyIndex index) const; private: diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h index 1462490884..66bbd7b188 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h @@ -40,7 +40,7 @@ namespace AZ //! by ShaderVariantAssetBuilder this is 1+. static uint32_t MakeAssetProductSubId( uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, ShaderVariantStableId variantStableId, - uint32_t subProductType = ShaderVariantAssetSubProductType); + uint32_t subProductType = 0); ShaderVariantAsset() = default; ~ShaderVariantAsset() = default; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index ef0678046c..86ea8ab688 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -47,7 +47,7 @@ namespace AZ { AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor; materialBuilderDescriptor.m_name = JobKey; - materialBuilderDescriptor.m_version = 108; // Set materialtype dependency to OrderOnce + materialBuilderDescriptor.m_version = 109; // Changed "id" to "name" in serialization materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_busId = azrtti_typeid(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyConnectionSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyConnectionSerializer.cpp new file mode 100644 index 0000000000..a0be7b9dca --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyConnectionSerializer.cpp @@ -0,0 +1,126 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +namespace AZ +{ + namespace RPI + { + namespace JsonMaterialPropertyConnectionSerializerInternal + { + namespace Field + { + static constexpr const char type[] = "type"; + static constexpr const char name[] = "name"; + static constexpr const char id[] = "id"; // For backward compatibility + static constexpr const char shaderIndex[] = "shaderIndex"; + } + + static const AZStd::string_view AcceptedFields[] = + { + Field::type, + Field::name, + Field::id, + Field::shaderIndex + }; + } + + AZ_CLASS_ALLOCATOR_IMPL(JsonMaterialPropertyConnectionSerializer, SystemAllocator, 0); + + JsonSerializationResult::Result JsonMaterialPropertyConnectionSerializer::Load(void* outputValue, const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertyConnectionSerializerInternal; + + AZ_Assert(azrtti_typeid() == outputValueTypeId, + "Unable to deserialize material property connection to json because the provided type is %s", + outputValueTypeId.ToString().c_str()); + AZ_UNUSED(outputValueTypeId); + + MaterialTypeSourceData::PropertyConnection* propertyConnection = reinterpret_cast(outputValue); + AZ_Assert(propertyConnection, "Output value for JsonMaterialPropertyConnectionSerializer can't be null."); + + JSR::ResultCode result(JSR::Tasks::ReadField); + + if (!inputValue.IsObject()) + { + return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Unsupported, "Property connection must be a JSON object."); + } + + MaterialUtils::CheckForUnrecognizedJsonFields(AcceptedFields, AZ_ARRAY_SIZE(AcceptedFields), inputValue, context, result); + + result.Combine(ContinueLoadingFromJsonObjectField(&propertyConnection->m_type, azrtti_typeid(), inputValue, Field::type, context)); + + JsonSerializationResult::ResultCode nameResult = ContinueLoadingFromJsonObjectField(&propertyConnection->m_fieldName, azrtti_typeid(), inputValue, Field::name, context); + if (nameResult.GetOutcome() == JsonSerializationResult::Outcomes::DefaultsUsed) + { + // This "id" key is for backward compatibility. + result.Combine(ContinueLoadingFromJsonObjectField(&propertyConnection->m_fieldName, azrtti_typeid(), inputValue, Field::id, context)); + } + else + { + result.Combine(nameResult); + } + + result.Combine(ContinueLoadingFromJsonObjectField(&propertyConnection->m_shaderIndex, azrtti_typeid(), inputValue, Field::shaderIndex, context)); + + if (result.GetProcessing() == JsonSerializationResult::Processing::Completed) + { + return context.Report(result, "Successfully loaded property connection."); + } + else + { + return context.Report(result, "Partially loaded property connection."); + } + } + + + JsonSerializationResult::Result JsonMaterialPropertyConnectionSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, + [[maybe_unused]] const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) + { + namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertyConnectionSerializerInternal; + + AZ_Assert(azrtti_typeid() == valueTypeId, + "Unable to serialize material property connection to json because the provided type is %s", + valueTypeId.ToString().c_str()); + AZ_UNUSED(valueTypeId); + + const MaterialTypeSourceData::PropertyConnection* propertyConnection = reinterpret_cast(inputValue); + AZ_Assert(propertyConnection, "Input value for JsonMaterialPropertyConnectionSerializer can't be null."); + + JSR::ResultCode result(JSR::Tasks::WriteValue); + + outputValue.SetObject(); + + MaterialTypeSourceData::PropertyConnection defaultConnection; + + result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::type, &propertyConnection->m_type, &defaultConnection.m_type, azrtti_typeid(), context)); + result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::name, &propertyConnection->m_fieldName, &defaultConnection.m_fieldName, azrtti_typeid(), context)); + result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::shaderIndex, &propertyConnection->m_shaderIndex, &defaultConnection.m_shaderIndex, azrtti_typeid(), context)); + + if (result.GetProcessing() == JsonSerializationResult::Processing::Completed) + { + return context.Report(result, "Successfully stored property connection."); + } + else + { + return context.Report(result, "Partially stored property connection."); + } + } + + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyGroupSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyGroupSerializer.cpp new file mode 100644 index 0000000000..b54ba012d4 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyGroupSerializer.cpp @@ -0,0 +1,125 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +namespace AZ +{ + namespace RPI + { + namespace JsonMaterialPropertyGroupSerializerInternal + { + namespace Field + { + static constexpr const char name[] = "name"; + static constexpr const char id[] = "id"; // For backward compatibility + static constexpr const char displayName[] = "displayName"; + static constexpr const char description[] = "description"; + } + + static const AZStd::string_view AcceptedFields[] = + { + Field::name, + Field::id, + Field::displayName, + Field::description + }; + } + + AZ_CLASS_ALLOCATOR_IMPL(JsonMaterialPropertyGroupSerializer, SystemAllocator, 0); + + JsonSerializationResult::Result JsonMaterialPropertyGroupSerializer::Load(void* outputValue, const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertyGroupSerializerInternal; + + AZ_Assert(azrtti_typeid() == outputValueTypeId, + "Unable to deserialize material property group to json because the provided type is %s", + outputValueTypeId.ToString().c_str()); + AZ_UNUSED(outputValueTypeId); + + MaterialTypeSourceData::GroupDefinition* propertyGroup = reinterpret_cast(outputValue); + AZ_Assert(propertyGroup, "Output value for JsonMaterialPropertyGroupSerializer can't be null."); + + JSR::ResultCode result(JSR::Tasks::ReadField); + + if (!inputValue.IsObject()) + { + return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Unsupported, "Property group must be a JSON object."); + } + + MaterialUtils::CheckForUnrecognizedJsonFields(AcceptedFields, AZ_ARRAY_SIZE(AcceptedFields), inputValue, context, result); + + JsonSerializationResult::ResultCode nameResult = ContinueLoadingFromJsonObjectField(&propertyGroup->m_name, azrtti_typeid(), inputValue, Field::name, context); + if (nameResult.GetOutcome() == JsonSerializationResult::Outcomes::DefaultsUsed) + { + // This "id" key is for backward compatibility. + result.Combine(ContinueLoadingFromJsonObjectField(&propertyGroup->m_name, azrtti_typeid(), inputValue, Field::id, context)); + } + else + { + result.Combine(nameResult); + } + + result.Combine(ContinueLoadingFromJsonObjectField(&propertyGroup->m_displayName, azrtti_typeid(), inputValue, Field::displayName, context)); + result.Combine(ContinueLoadingFromJsonObjectField(&propertyGroup->m_description, azrtti_typeid(), inputValue, Field::description, context)); + + if (result.GetProcessing() == JsonSerializationResult::Processing::Completed) + { + return context.Report(result, "Successfully loaded property group."); + } + else + { + return context.Report(result, "Partially loaded property group."); + } + } + + + JsonSerializationResult::Result JsonMaterialPropertyGroupSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, + [[maybe_unused]] const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) + { + namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertyGroupSerializerInternal; + + AZ_Assert(azrtti_typeid() == valueTypeId, + "Unable to serialize material property group to json because the provided type is %s", + valueTypeId.ToString().c_str()); + AZ_UNUSED(valueTypeId); + + const MaterialTypeSourceData::GroupDefinition* propertyGroup = reinterpret_cast(inputValue); + AZ_Assert(propertyGroup, "Input value for JsonMaterialPropertyGroupSerializer can't be null."); + + JSR::ResultCode result(JSR::Tasks::WriteValue); + + outputValue.SetObject(); + + AZStd::string defaultEmpty; + + result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::name, &propertyGroup->m_name, &defaultEmpty, azrtti_typeid(), context)); + result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::displayName, &propertyGroup->m_displayName, &defaultEmpty, azrtti_typeid(), context)); + result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::description, &propertyGroup->m_description, &defaultEmpty, azrtti_typeid(), context)); + + if (result.GetProcessing() == JsonSerializationResult::Processing::Completed) + { + return context.Report(result, "Successfully stored property group."); + } + else + { + return context.Report(result, "Partially stored property group."); + } + } + + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertySerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertySerializer.cpp index f3d499ac78..4c66f753f2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertySerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertySerializer.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -23,11 +24,12 @@ namespace AZ { namespace RPI { - namespace // Avoid conflicts in uber builds + namespace JsonMaterialPropertySerializerInternal { namespace Field { - static constexpr const char id[] = "id"; + static constexpr const char name[] = "name"; + static constexpr const char id[] = "id"; // For backward compatibility static constexpr const char displayName[] = "displayName"; static constexpr const char description[] = "description"; static constexpr const char type[] = "type"; @@ -46,6 +48,7 @@ namespace AZ static const AZStd::string_view AcceptedFields[] = { + Field::name, Field::id, Field::displayName, Field::description, @@ -103,6 +106,7 @@ namespace AZ JsonDeserializerContext& context) { namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertySerializerInternal; JSR::ResultCode result(JSR::Tasks::ReadField); @@ -160,6 +164,7 @@ namespace AZ JsonDeserializerContext& context) { namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertySerializerInternal; JSR::ResultCode result(JSR::Tasks::ReadField); @@ -181,6 +186,7 @@ namespace AZ const rapidjson::Value& inputValue, JsonDeserializerContext& context) { namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertySerializerInternal; AZ_Assert(azrtti_typeid() == outputValueTypeId, "Unable to deserialize material property to json because the provided type is %s", @@ -197,28 +203,19 @@ namespace AZ return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Unsupported, "Property definition must be a JSON object."); } - // First check for unexpected fields - for (auto iter = inputValue.MemberBegin(); iter != inputValue.MemberEnd(); ++iter) - { - bool matched = false; - - for (int i = 0; i < AZ_ARRAY_SIZE(AcceptedFields); ++i) - { - if (iter->name.GetString() == AcceptedFields[i]) - { - matched = true; - break; - } - } + MaterialUtils::CheckForUnrecognizedJsonFields(AcceptedFields, AZ_ARRAY_SIZE(AcceptedFields), inputValue, context, result); - if (!matched) - { - ScopedContextPath subPath{context, iter->name.GetString()}; - result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Skipped, "Skipping unrecognized field")); - } + JsonSerializationResult::ResultCode nameResult = ContinueLoadingFromJsonObjectField(&property->m_name, azrtti_typeid(), inputValue, Field::name, context); + if (nameResult.GetOutcome() == JsonSerializationResult::Outcomes::DefaultsUsed) + { + // This "id" key is for backward compatibility. + result.Combine(ContinueLoadingFromJsonObjectField(&property->m_name, azrtti_typeid(), inputValue, Field::id, context)); + } + else + { + result.Combine(nameResult); } - result.Combine(ContinueLoadingFromJsonObjectField(&property->m_nameId, azrtti_typeid(), inputValue, Field::id, context)); result.Combine(ContinueLoadingFromJsonObjectField(&property->m_displayName, azrtti_typeid(), inputValue, Field::displayName, context)); result.Combine(ContinueLoadingFromJsonObjectField(&property->m_description, azrtti_typeid(), inputValue, Field::description, context)); result.Combine(ContinueLoadingFromJsonObjectField(&property->m_dataType, azrtti_typeid(), inputValue, Field::type, context)); @@ -302,6 +299,8 @@ namespace AZ JsonSerializerContext& context) { namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertySerializerInternal; + JSR::ResultCode result(JSR::Tasks::WriteValue); if (property->m_value.Is()) @@ -345,6 +344,8 @@ namespace AZ JsonSerializerContext& context) { namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertySerializerInternal; + JsonSerializationResult::ResultCode result(JSR::Tasks::WriteValue); if (property->m_value.Is()) @@ -360,6 +361,7 @@ namespace AZ [[maybe_unused]] const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) { namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertySerializerInternal; AZ_Assert(azrtti_typeid() == valueTypeId, "Unable to serialize material property to json because the provided type is %s", @@ -374,7 +376,7 @@ namespace AZ outputValue.SetObject(); const AZStd::string emptyString; - result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::id, &property->m_nameId, &emptyString, azrtti_typeid(), context)); + result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::name, &property->m_name, &emptyString, azrtti_typeid(), context)); result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::displayName, &property->m_displayName, &emptyString, azrtti_typeid(), context)); result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::description, &property->m_description, &emptyString, azrtti_typeid(), context)); @@ -452,6 +454,8 @@ namespace AZ const rapidjson::Value& inputValue, JsonDeserializerContext& context) { namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertySerializerInternal; + JSR::ResultCode result(JSR::Tasks::ReadField); if (inputValue.HasMember(Field::vectorLabels)) @@ -467,6 +471,8 @@ namespace AZ { AZStd::string emptyString; namespace JSR = JsonSerializationResult; + using namespace JsonMaterialPropertySerializerInternal; + JsonSerializationResult::ResultCode result(JSR::Tasks::WriteValue); if (!property->m_vectorLabels.empty()) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp index 060a563c53..3b2d36451a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp @@ -62,18 +62,18 @@ namespace AZ return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Catastrophic, "Material type reference not found."); } - // Construct the full property name (groupId.propertyId) by parsing it from the JSON path string. - size_t startPropertyNameId = context.GetPath().Get().rfind('/'); - size_t startGroupNameId = context.GetPath().Get().rfind('/', startPropertyNameId-1); - AZStd::string_view groupNameId = context.GetPath().Get().substr(startGroupNameId + 1, startPropertyNameId - startGroupNameId - 1); - AZStd::string_view propertyNameId = context.GetPath().Get().substr(startPropertyNameId + 1); + // Construct the full property name (groupName.propertyName) by parsing it from the JSON path string. + size_t startPropertyName = context.GetPath().Get().rfind('/'); + size_t startGroupName = context.GetPath().Get().rfind('/', startPropertyName-1); + AZStd::string_view groupName = context.GetPath().Get().substr(startGroupName + 1, startPropertyName - startGroupName - 1); + AZStd::string_view propertyName = context.GetPath().Get().substr(startPropertyName + 1); JSR::ResultCode result(JSR::Tasks::ReadField); - auto propertyDefinition = materialType->FindProperty(groupNameId, propertyNameId); + auto propertyDefinition = materialType->FindProperty(groupName, propertyName); if (!propertyDefinition) { - AZStd::string message = AZStd::string::format("Property '%.*s.%.*s' not found in material type.", AZ_STRING_ARG(groupNameId), AZ_STRING_ARG(propertyNameId)); + AZStd::string message = AZStd::string::format("Property '%.*s.%.*s' not found in material type.", AZ_STRING_ARG(groupName), AZ_STRING_ARG(propertyName)); return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Unsupported, message); } else diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 5a95cb35a6..1cc57f4d47 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -46,29 +48,17 @@ namespace AZ if (JsonRegistrationContext* jsonContext = azrtti_cast(context)) { jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer()->HandlesType(); } else if (auto* serializeContext = azrtti_cast(context)) { - serializeContext->Class() - ->Version(1) - ->Field("type", &PropertyConnection::m_type) - ->Field("id", &PropertyConnection::m_nameId) - ->Field("shaderIndex", &PropertyConnection::m_shaderIndex) - ; + serializeContext->Class()->Version(3); + serializeContext->Class()->Version(4); + serializeContext->Class()->Version(1); serializeContext->RegisterGenericType(); - serializeContext->Class() - ->Version(1) - ->Field("id", &GroupDefinition::m_nameId) - ->Field("displayName", &GroupDefinition::m_displayName) - ->Field("description", &GroupDefinition::m_description) - ; - - serializeContext->Class() - ->Version(1) - ; - serializeContext->Class() ->Version(2) ->Field("file", &ShaderVariantReferenceData::m_shaderFilePath) @@ -96,9 +86,9 @@ namespace AZ } } - MaterialTypeSourceData::PropertyConnection::PropertyConnection(MaterialPropertyOutputType type, AZStd::string_view nameId, int32_t shaderIndex) + MaterialTypeSourceData::PropertyConnection::PropertyConnection(MaterialPropertyOutputType type, AZStd::string_view fieldName, int32_t shaderIndex) : m_type(type) - , m_nameId(nameId) + , m_fieldName(fieldName) , m_shaderIndex(shaderIndex) { } @@ -107,11 +97,11 @@ namespace AZ const float MaterialTypeSourceData::PropertyDefinition::DefaultMax = std::numeric_limits::max(); const float MaterialTypeSourceData::PropertyDefinition::DefaultStep = 0.1f; - const MaterialTypeSourceData::GroupDefinition* MaterialTypeSourceData::FindGroup(AZStd::string_view groupNameId) const + const MaterialTypeSourceData::GroupDefinition* MaterialTypeSourceData::FindGroup(AZStd::string_view groupName) const { for (const GroupDefinition& group : m_propertyLayout.m_groups) { - if (group.m_nameId == groupNameId) + if (group.m_name == groupName) { return &group; } @@ -120,9 +110,9 @@ namespace AZ return nullptr; } - const MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::FindProperty(AZStd::string_view groupNameId, AZStd::string_view propertyNameId) const + const MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName) const { - auto groupIter = m_propertyLayout.m_properties.find(groupNameId); + auto groupIter = m_propertyLayout.m_properties.find(groupName); if (groupIter == m_propertyLayout.m_properties.end()) { return nullptr; @@ -130,7 +120,7 @@ namespace AZ for (const PropertyDefinition& property : groupIter->second) { - if (property.m_nameId == propertyNameId) + if (property.m_name == propertyName) { return &property; } @@ -169,24 +159,24 @@ namespace AZ AZStd::unordered_set foundGroups; for (const auto& groupDefinition : m_propertyLayout.m_groups) { - if (foundGroups.insert(groupDefinition.m_nameId).second) + if (foundGroups.insert(groupDefinition.m_name).second) { groupDefinitions.push_back(groupDefinition); } else { - AZ_Warning("Material source data", false, "Duplicate group '%s' found.", groupDefinition.m_nameId.c_str()); + AZ_Warning("Material source data", false, "Duplicate group '%s' found.", groupDefinition.m_name.c_str()); } } // Some groups are defined implicitly, in the "properties" section where a group name is used but not explicitly defined in the "groups" section. for (const auto& propertyListPair : m_propertyLayout.m_properties) { - const AZStd::string& groupNameId = propertyListPair.first; - if (foundGroups.insert(groupNameId).second) + const AZStd::string& groupName = propertyListPair.first; + if (foundGroups.insert(groupName).second) { MaterialTypeSourceData::GroupDefinition groupDefinition; - groupDefinition.m_nameId = groupNameId; + groupDefinition.m_name = groupName; groupDefinitions.push_back(groupDefinition); } } @@ -203,12 +193,12 @@ namespace AZ for (const auto& propertyListPair : m_propertyLayout.m_properties) { - const AZStd::string& groupNameId = propertyListPair.first; + const AZStd::string& groupName = propertyListPair.first; const auto& propertyList = propertyListPair.second; for (const auto& propertyDefinition : propertyList) { - const AZStd::string& propertyNameId = propertyDefinition.m_nameId; - if (!callback(groupNameId, propertyNameId, propertyDefinition)) + const AZStd::string& propertyName = propertyDefinition.m_name; + if (!callback(groupName, propertyName, propertyDefinition)) { return; } @@ -225,15 +215,15 @@ namespace AZ for (const auto& groupDefinition : GetGroupDefinitionsInDisplayOrder()) { - const AZStd::string& groupNameId = groupDefinition.m_nameId; - const auto propertyListItr = m_propertyLayout.m_properties.find(groupNameId); + const AZStd::string& groupName = groupDefinition.m_name; + const auto propertyListItr = m_propertyLayout.m_properties.find(groupName); if (propertyListItr != m_propertyLayout.m_properties.end()) { const auto& propertyList = propertyListItr->second; for (const auto& propertyDefinition : propertyList) { - const AZStd::string& propertyNameId = propertyDefinition.m_nameId; - if (!callback(groupNameId, propertyNameId, propertyDefinition)) + const AZStd::string& propertyName = propertyDefinition.m_name; + if (!callback(groupName, propertyName, propertyDefinition)) { return; } @@ -249,7 +239,7 @@ namespace AZ const uint32_t index = propertyValue.GetValue(); if (index >= propertyDefinition.m_enumValues.size()) { - AZ_Error("Material source data", false, "Invalid value for material enum property: '%s'.", propertyDefinition.m_nameId.c_str()); + AZ_Error("Material source data", false, "Invalid value for material enum property: '%s'.", propertyDefinition.m_name.c_str()); return false; } @@ -272,7 +262,7 @@ namespace AZ imageAsset.GetId(), imageAsset.GetType(), platformName, imageAssetInfo, rootFilePath); if (!result) { - AZ_Error("Material source data", false, "Image asset could not be found for property: '%s'.", propertyDefinition.m_nameId.c_str()); + AZ_Error("Material source data", false, "Image asset could not be found for property: '%s'.", propertyDefinition.m_name.c_str()); return false; } } @@ -340,13 +330,13 @@ namespace AZ for (auto& groupIter : m_propertyLayout.m_properties) { - const AZStd::string& groupNameId = groupIter.first; + const AZStd::string& groupName = groupIter.first; for (const PropertyDefinition& property : groupIter.second) { // Register the property... - MaterialPropertyId propertyId{ groupNameId, property.m_nameId }; + MaterialPropertyId propertyId{ groupName, property.m_name }; if (!propertyId.IsValid()) { @@ -366,16 +356,16 @@ namespace AZ switch (output.m_type) { case MaterialPropertyOutputType::ShaderInput: - materialTypeAssetCreator.ConnectMaterialPropertyToShaderInput(Name{ output.m_nameId.data() }); + materialTypeAssetCreator.ConnectMaterialPropertyToShaderInput(Name{ output.m_fieldName.data() }); break; case MaterialPropertyOutputType::ShaderOption: if (output.m_shaderIndex >= 0) { - materialTypeAssetCreator.ConnectMaterialPropertyToShaderOption(Name{ output.m_nameId.data() }, output.m_shaderIndex); + materialTypeAssetCreator.ConnectMaterialPropertyToShaderOption(Name{ output.m_fieldName.data() }, output.m_shaderIndex); } else { - materialTypeAssetCreator.ConnectMaterialPropertyToShaderOptions(Name{ output.m_nameId.data() }); + materialTypeAssetCreator.ConnectMaterialPropertyToShaderOptions(Name{ output.m_fieldName.data() }); } break; case MaterialPropertyOutputType::Invalid: diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialUtils.cpp index 62f4f02e3c..90ce9e66ce 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialUtils.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include @@ -99,6 +101,29 @@ namespace AZ return AZ::Success(AZStd::move(materialType)); } } + + void CheckForUnrecognizedJsonFields(const AZStd::string_view* acceptedFieldNames, uint32_t acceptedFieldNameCount, const rapidjson::Value& object, JsonDeserializerContext& context, JsonSerializationResult::ResultCode &result) + { + for (auto iter = object.MemberBegin(); iter != object.MemberEnd(); ++iter) + { + bool matched = false; + + for (uint32_t i = 0; i < acceptedFieldNameCount; ++i) + { + if (iter->name.GetString() == acceptedFieldNames[i]) + { + matched = true; + break; + } + } + + if (!matched) + { + ScopedContextPath subPath{context, iter->name.GetString()}; + result.Combine(context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Skipped, "Skipping unrecognized field")); + } + } + } } } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderVariantListSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderVariantListSourceData.cpp index b6a2132b89..6cd38f65bc 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderVariantListSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderVariantListSourceData.cpp @@ -8,9 +8,10 @@ #include #include - +#include #include + namespace AZ { namespace RPI diff --git a/Gems/Atom/RPI/Code/Source/RPI.Private/RPISystemComponent.cpp b/Gems/Atom/RPI/Code/Source/RPI.Private/RPISystemComponent.cpp index 789d43712e..2567d221e5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Private/RPISystemComponent.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Private/RPISystemComponent.cpp @@ -14,9 +14,6 @@ #include #include -#include -#include -#include #include #include @@ -96,29 +93,20 @@ namespace AZ } m_rpiSystem.Initialize(m_rpiDescriptor); - AZ::TickBus::Handler::BusConnect(); + AZ::SystemTickBus::Handler::BusConnect(); } void RPISystemComponent::Deactivate() { - AZ::TickBus::Handler::BusDisconnect(); + AZ::SystemTickBus::Handler::BusDisconnect(); m_rpiSystem.Shutdown(); } - void RPISystemComponent::OnTick([[maybe_unused]]float deltaTime, [[maybe_unused]]ScriptTimePoint time) + void RPISystemComponent::OnSystemTick() { - if (deltaTime == 0.f) - { - return; - } - m_rpiSystem.SimulationTick(); m_rpiSystem.RenderTick(); } - int RPISystemComponent::GetTickOrder() - { - return AZ::ComponentTickBus::TICK_RENDER; - } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Private/RPISystemComponent.h b/Gems/Atom/RPI/Code/Source/RPI.Private/RPISystemComponent.h index 7933e1581c..e0a128c3f1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Private/RPISystemComponent.h +++ b/Gems/Atom/RPI/Code/Source/RPI.Private/RPISystemComponent.h @@ -32,7 +32,7 @@ namespace AZ */ class RPISystemComponent final : public AZ::Component - , private AZ::TickBus::Handler + , public AZ::SystemTickBus::Handler { public: AZ_COMPONENT(RPISystemComponent, "{83E301F3-7A0C-4099-B530-9342B91B1BC0}"); @@ -50,9 +50,8 @@ namespace AZ private: RPISystemComponent(const RPISystemComponent&) = delete; - // TickBus overrides... - void OnTick(float deltaTime, ScriptTimePoint time) override; - int GetTickOrder() override; + // SystemTickBus overrides... + void OnSystemTick() override; RPISystem m_rpiSystem; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index d44ed40c7b..c87646e17d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -386,9 +386,9 @@ namespace AZ return m_currentChangeId; } - MaterialPropertyIndex Material::FindPropertyIndex(const Name& name) const + MaterialPropertyIndex Material::FindPropertyIndex(const Name& propertyId) const { - return m_layout->FindPropertyIndex(name); + return m_layout->FindPropertyIndex(propertyId); } template diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp index 50da470ec4..1ba17deb91 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp @@ -7,6 +7,7 @@ */ #include +#include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp index a1651defbc..3cbb20b9e9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp @@ -10,7 +10,9 @@ #include #include +#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelSystem.cpp index 1ed81f8e16..775d02f7ab 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelSystem.cpp @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index c5e871697b..a8d94e9a91 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -93,11 +93,8 @@ namespace AZ void Pass::SetEnabled(bool enabled) { - if (m_flags.m_enabled != enabled) - { - m_flags.m_enabled = enabled; - OnHierarchyChange(); - } + m_flags.m_enabled = enabled; + OnHierarchyChange(); } bool Pass::IsEnabled() const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp index 409a084e4f..1b99abae4e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp @@ -301,26 +301,6 @@ namespace AZ m_drawFilterMask = 0; } - void RenderPipeline::OnPrepareFrame() - { - m_lastRenderRequestTime = AZStd::chrono::system_clock::now(); - - // If we're attempting to render at a target interval, check to see if we're within - // 1ms of that interval, enabling rendering only if we are. - if (m_renderMode == RenderMode::RenderAtTargetRate) - { - constexpr AZStd::chrono::duration updateThresholdMs(0.001f); - const bool shouldRender = - m_lastRenderRequestTime - m_lastRenderStartTime + updateThresholdMs >= m_targetRefreshRate; - m_rootPass->SetEnabled(shouldRender); - } - - if (NeedsRender()) - { - m_prepareFrameEvent.Signal(); - } - } - void RenderPipeline::OnPassModified() { if (m_needsPassRecreate) @@ -395,11 +375,11 @@ namespace AZ m_scene->RemoveRenderPipeline(m_nameId); } - void RenderPipeline::OnStartFrame() + void RenderPipeline::OnStartFrame(const TickTimeInfo& tick) { AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); - m_lastRenderStartTime = m_lastRenderRequestTime; + m_lastRenderStartTime = tick.m_currentGameTime; OnPassModified(); @@ -427,7 +407,6 @@ namespace AZ { RemoveFromRenderTick(); } - m_endFrameEvent.Signal(); } void RenderPipeline::CollectPersistentViews(AZStd::map& outViewMasks) const @@ -510,13 +489,6 @@ namespace AZ m_renderMode = RenderMode::RenderEveryTick; } - void RenderPipeline::AddToRenderTickAtInterval(AZStd::chrono::duration renderInterval) - { - m_rootPass->SetEnabled(false); - m_renderMode = RenderMode::RenderAtTargetRate; - m_targetRefreshRate = renderInterval; - } - void RenderPipeline::RemoveFromRenderTick() { m_renderMode = RenderMode::NoRender; @@ -530,7 +502,7 @@ namespace AZ bool RenderPipeline::NeedsRender() const { - return m_rootPass->IsEnabled(); + return m_renderMode != RenderMode::NoRender; } RHI::DrawFilterTag RenderPipeline::GetDrawFilterTag() const @@ -543,16 +515,6 @@ namespace AZ return m_drawFilterMask; } - void RenderPipeline::ConnectPrepareFrameHandler(FrameNotificationEvent::Handler& handler) - { - handler.Connect(m_prepareFrameEvent); - } - - void RenderPipeline::ConnectEndFrameHandler(FrameNotificationEvent::Handler& handler) - { - handler.Connect(m_endFrameEvent); - } - void RenderPipeline::SetDrawFilterTag(RHI::DrawFilterTag tag) { m_drawFilterTag = tag; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index a7ecf089c0..646cba1999 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -418,7 +418,7 @@ namespace AZ } } - void Scene::PrepareRender([[maybe_unused]]const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) + void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) { AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender"); @@ -429,27 +429,20 @@ namespace AZ SceneNotificationBus::Event(GetId(), &SceneNotification::OnBeginPrepareRender); - // Get active pipelines which need to be rendered and notify them of an impending frame. + // Get active pipelines which need to be rendered and notify them frame started AZStd::vector activePipelines; { - AZ_PROFILE_SCOPE(RPI, "Scene: OnPrepareFrame"); + AZ_PROFILE_SCOPE(RPI, "Scene: OnStartFrame"); for (auto& pipeline : m_pipelines) { - pipeline->OnPrepareFrame(); if (pipeline->NeedsRender()) { activePipelines.push_back(pipeline); + pipeline->OnStartFrame(tickInfo); } } } - // Get active pipelines which need to be rendered and notify them frame started - for (const auto& pipeline : activePipelines) - { - AZ_PROFILE_SCOPE(RPI, "Scene: OnStartFrame"); - pipeline->OnStartFrame(); - } - // Return if there is no active render pipeline if (activePipelines.empty()) { @@ -589,12 +582,10 @@ namespace AZ void Scene::OnFrameEnd() { AZ_PROFILE_SCOPE(RPI, "Scene: OnFrameEnd"); - bool didRender = false; for (auto& pipeline : m_pipelines) { if (pipeline->NeedsRender()) { - didRender = true; pipeline->OnFrameEnd(); } } @@ -602,10 +593,6 @@ namespace AZ { fp->OnRenderEnd(); } - if (didRender) - { - SceneNotificationBus::Event(GetId(), &SceneNotification::OnFrameEnd); - } } void Scene::UpdateSrgs() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp index 528d32e217..e11624a921 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp @@ -23,11 +23,11 @@ namespace AZ { namespace RPI { - AZStd::string GetMetricsFilePath() + AZ::IO::FixedMaxPath GetMetricsFilePath() { - char shaderMetricPath[AZ_MAX_PATH_LEN]; - AZ::Utils::GetExecutableDirectory(shaderMetricPath, AZ_MAX_PATH_LEN); - return AZStd::string(shaderMetricPath) + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "ShaderMetrics.json"; + AZ::IO::FixedMaxPath resolvedPath; + AZ::IO::LocalFileIO::GetInstance()->ResolvePath(resolvedPath, "@user@/ShaderMetrics.json"); + return resolvedPath; } ShaderMetricsSystemInterface* ShaderMetricsSystemInterface::Get() @@ -64,7 +64,7 @@ namespace AZ void ShaderMetricsSystem::ReadLog() { - const AZStd::string metricsFilePath = GetMetricsFilePath(); + const AZ::IO::FixedMaxPath metricsFilePath = GetMetricsFilePath(); if (AZ::IO::LocalFileIO::GetInstance()->Exists(metricsFilePath.c_str())) { @@ -80,7 +80,7 @@ namespace AZ void ShaderMetricsSystem::WriteLog() { - const AZStd::string metricsFilePath = GetMetricsFilePath(); + const AZ::IO::FixedMaxPath metricsFilePath = GetMetricsFilePath(); auto saveResult = AZ::JsonSerializationUtils::SaveObjectToFile(&m_metrics, metricsFilePath.c_str()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index c2356cea45..8c1e38ba58 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -241,10 +241,7 @@ namespace AZ { AZ_PROFILE_SCOPE(RPI, "View: FinalizeDrawLists"); m_drawListContext.FinalizeLists(); - if (m_passesByDrawList) - { - SortFinalizedDrawLists(); - } + SortFinalizedDrawLists(); } void View::SortFinalizedDrawLists() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp index 3dcbae2fb9..77114e5cf5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp @@ -25,13 +25,14 @@ namespace AZ , m_viewportSize(1, 1) { m_windowContext->Initialize(device, nativeWindow); - AzFramework::WindowRequestBus::Event(nativeWindow, [this](AzFramework::WindowRequestBus::Events* window) - { - m_viewportSize = window->GetClientAreaSize(); - m_viewportDpiScaleFactor = window->GetDpiScaleFactor(); - m_vsyncInterval = window->GetSyncInterval(); - m_refreshRate = window->GetDisplayRefreshRate(); - }); + AzFramework::WindowRequestBus::EventResult( + m_viewportSize, + nativeWindow, + &AzFramework::WindowRequestBus::Events::GetClientAreaSize); + AzFramework::WindowRequestBus::EventResult( + m_viewportDpiScaleFactor, + nativeWindow, + &AzFramework::WindowRequestBus::Events::GetDpiScaleFactor); AzFramework::WindowNotificationBus::Handler::BusConnect(nativeWindow); AzFramework::ViewportRequestBus::Handler::BusConnect(id); @@ -45,20 +46,6 @@ namespace AZ m_viewMatrixChangedEvent.Signal(matrix); }); - m_prepareFrameHandler = RenderPipeline::FrameNotificationEvent::Handler( - [this]() - { - ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnRenderTick); - ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnRenderTick); - }); - - m_endFrameHandler = RenderPipeline::FrameNotificationEvent::Handler( - [this]() - { - ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnFrameEnd); - ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnFrameEnd); - }); - SetRenderScene(renderScene); } @@ -124,38 +111,26 @@ namespace AZ { SceneNotificationBus::Handler::BusConnect(m_rootScene->GetId()); } - ResetCurrentPipeline(); + m_currentPipeline.reset(); UpdatePipelineView(); - UpdatePipelineRefreshRate(); } m_sceneChangedEvent.Signal(scene); } - float ViewportContext::GetFpsLimit() const - { - return m_fpsLimit; - } - - void ViewportContext::SetFpsLimit(float fpsLimit) + void ViewportContext::RenderTick() { - m_fpsLimit = fpsLimit; - UpdatePipelineRefreshRate(); + // add the current pipeline to next render tick if it's not already added. + if (m_currentPipeline && m_currentPipeline->GetRenderMode() != RenderPipeline::RenderMode::RenderOnce) + { + m_currentPipeline->AddToRenderTickOnce(); + } } - float ViewportContext::GetTargetFrameRate() const + void ViewportContext::OnBeginPrepareRender() { - float targetFrameRate = GetFpsLimit(); - const AZ::u32 vsyncInterval = GetVsyncInterval(); - if (vsyncInterval != 0) - { - const float vsyncFrameRate = static_cast(GetRefreshRate()) / static_cast(vsyncInterval); - if (targetFrameRate == 0.f || vsyncFrameRate < targetFrameRate) - { - targetFrameRate = vsyncFrameRate; - } - } - return targetFrameRate; + ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnRenderTick); + ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnRenderTick); } AZ::Name ViewportContext::GetName() const @@ -183,16 +158,6 @@ namespace AZ return m_viewportDpiScaleFactor; } - uint32_t ViewportContext::GetVsyncInterval() const - { - return m_vsyncInterval; - } - - uint32_t ViewportContext::GetRefreshRate() const - { - return m_refreshRate; - } - void ViewportContext::ConnectSizeChangedHandler(SizeChangedEvent::Handler& handler) { handler.Connect(m_sizeChangedEvent); @@ -203,16 +168,6 @@ namespace AZ handler.Connect(m_dpiScalingFactorChangedEvent); } - void ViewportContext::ConnectVsyncIntervalChangedHandler(UintChangedEvent::Handler& handler) - { - handler.Connect(m_vsyncIntervalChangedEvent); - } - - void ViewportContext::ConnectRefreshRateChangedHandler(UintChangedEvent::Handler& handler) - { - handler.Connect(m_refreshRateChangedEvent); - } - void ViewportContext::ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler) { handler.Connect(m_viewMatrixChangedEvent); @@ -308,43 +263,12 @@ namespace AZ m_currentPipelineChangedEvent.Signal(m_currentPipeline); } - if (m_currentPipeline) + if (auto pipeline = GetCurrentPipeline()) { - if (!m_prepareFrameHandler.IsConnected()) - { - m_currentPipeline->ConnectPrepareFrameHandler(m_prepareFrameHandler); - m_currentPipeline->ConnectEndFrameHandler(m_endFrameHandler); - } - m_currentPipeline->SetDefaultView(m_defaultView); - } - } - - void ViewportContext::UpdatePipelineRefreshRate() - { - if (!m_currentPipeline) - { - return; - } - - const float refreshRate = GetTargetFrameRate(); - // If we have a truly unlimited framerate, just render every tick - if (refreshRate == 0.f) - { - m_currentPipeline->AddToRenderTick(); - } - else - { - m_currentPipeline->AddToRenderTickAtInterval(AZStd::chrono::duration(1.f / refreshRate)); + pipeline->SetDefaultView(m_defaultView); } } - void ViewportContext::ResetCurrentPipeline() - { - m_prepareFrameHandler.Disconnect(); - m_endFrameHandler.Disconnect(); - m_currentPipeline.reset(); - } - RenderPipelinePtr ViewportContext::GetCurrentPipeline() { return m_currentPipeline; @@ -357,9 +281,8 @@ namespace AZ // in the event prioritization is added later if (pipeline->GetWindowHandle() == m_windowContext->GetWindowHandle()) { - ResetCurrentPipeline(); + m_currentPipeline.reset(); UpdatePipelineView(); - UpdatePipelineRefreshRate(); } } @@ -367,9 +290,8 @@ namespace AZ { if (m_currentPipeline.get() == pipeline) { - ResetCurrentPipeline(); + m_currentPipeline.reset(); UpdatePipelineView(); - UpdatePipelineRefreshRate(); } } @@ -383,30 +305,10 @@ namespace AZ } } - void ViewportContext::OnRefreshRateChanged(uint32_t refreshRate) - { - if (m_refreshRate != refreshRate) - { - m_refreshRate = refreshRate; - m_refreshRateChangedEvent.Signal(m_refreshRate); - UpdatePipelineRefreshRate(); - } - } - void ViewportContext::OnDpiScaleFactorChanged(float dpiScaleFactor) { m_viewportDpiScaleFactor = dpiScaleFactor; m_dpiScalingFactorChangedEvent.Signal(dpiScaleFactor); } - - void ViewportContext::OnVsyncIntervalChanged(uint32_t interval) - { - if (m_vsyncInterval != interval) - { - m_vsyncInterval = interval; - m_vsyncIntervalChangedEvent.Signal(m_vsyncInterval); - UpdatePipelineRefreshRate(); - } - } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp index 180ce6d5a7..95f47d7393 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp @@ -8,6 +8,7 @@ #include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetView.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetView.cpp index a88d4f10e4..2b3aabdba2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetView.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetView.cpp @@ -7,6 +7,7 @@ */ #include +#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/StreamingImageAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/StreamingImageAsset.cpp index 1caba9dba8..59f359e474 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/StreamingImageAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/StreamingImageAsset.cpp @@ -8,6 +8,7 @@ #include +#include #include namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/StreamingImagePoolAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/StreamingImagePoolAsset.cpp index fda4c31b08..056002f527 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/StreamingImagePoolAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/StreamingImagePoolAsset.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp index 9338d52cb2..c33409f687 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index c3e0221192..5c7af601fb 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertiesLayout.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertiesLayout.cpp index 273ea321a6..1c5142031d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertiesLayout.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertiesLayout.cpp @@ -34,9 +34,9 @@ namespace AZ return m_materialPropertyDescriptors.size(); } - MaterialPropertyIndex MaterialPropertiesLayout::FindPropertyIndex(const Name& propertyName) const + MaterialPropertyIndex MaterialPropertiesLayout::FindPropertyIndex(const Name& propertyId) const { - return m_materialPropertyIndexes.Find(propertyName); + return m_materialPropertyIndexes.Find(propertyId); } const MaterialPropertyDescriptor* MaterialPropertiesLayout::GetPropertyDescriptor(MaterialPropertyIndex index) const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyValue.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyValue.cpp index 13ecea52dc..d1017139fc 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyValue.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyValue.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp index 7b3b6b7a28..87076891dd 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp index 8e2d1bdd7e..9a432643d7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelMaterialSlot.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelMaterialSlot.cpp index b9d9200aaf..4485359cf9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelMaterialSlot.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelMaterialSlot.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/MorphTargetMetaAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/MorphTargetMetaAsset.cpp index 228ef67025..bcdc91abda 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/MorphTargetMetaAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/MorphTargetMetaAsset.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp index 49ef457311..692087d93b 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp index ce842d385d..9afc05a237 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp @@ -46,7 +46,7 @@ namespace JsonSerializationTests AZStd::shared_ptr CreatePartialDefaultInstance() override { auto result = AZStd::make_shared(); - result->m_nameId = "testProperty"; + result->m_name = "testProperty"; result->m_dataType = AZ::RPI::MaterialPropertyDataType::Float; result->m_step = 1.0f; result->m_value = 0.0f; @@ -57,7 +57,7 @@ namespace JsonSerializationTests { return R"( { - "id": "testProperty", + "name": "testProperty", "type": "Float", "step": 1.0 })"; @@ -66,7 +66,7 @@ namespace JsonSerializationTests AZStd::shared_ptr CreateFullySetInstance() override { auto result = AZStd::make_shared(); - result->m_nameId = "testProperty"; + result->m_name = "testProperty"; result->m_description = "description"; result->m_displayName = "display_name"; result->m_dataType = AZ::RPI::MaterialPropertyDataType::Float; @@ -87,7 +87,7 @@ namespace JsonSerializationTests { return R"( { - "id": "testProperty", + "name": "testProperty", "displayName": "display_name", "description": "description", "type": "Float", @@ -101,7 +101,7 @@ namespace JsonSerializationTests "connection": { "type": "ShaderOption", - "id": "o_foo", + "name": "o_foo", "shaderIndex": 2 }, "enumIsUv": true @@ -135,7 +135,7 @@ namespace JsonSerializationTests const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& lhs, const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& rhs) override { - if (lhs.m_nameId != rhs.m_nameId) { return false; } + if (lhs.m_name != rhs.m_name) { return false; } if (lhs.m_description != rhs.m_description) { return false; } if (lhs.m_displayName != rhs.m_displayName) { return false; } if (lhs.m_dataType != rhs.m_dataType) { return false; } @@ -153,7 +153,7 @@ namespace JsonSerializationTests auto& leftConnection = lhs.m_outputConnections[i]; auto& rightConnection = rhs.m_outputConnections[i]; if (leftConnection.m_type != rightConnection.m_type) { return false; } - if (leftConnection.m_nameId != rightConnection.m_nameId) { return false; } + if (leftConnection.m_fieldName != rightConnection.m_fieldName) { return false; } if (leftConnection.m_shaderIndex != rightConnection.m_shaderIndex) { return false; } } return true; @@ -202,7 +202,7 @@ namespace UnitTest { const AZStd::string inputJson = R"( { - "id": "testProperty", + "name": "testProperty", "displayName": "Test Property", "description": "This is a property description", "type": "Float" @@ -216,12 +216,12 @@ namespace UnitTest EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing()); EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialDefaults, loadResult.m_jsonResultCode.GetOutcome()); - EXPECT_EQ("testProperty", propertyData.m_nameId); + EXPECT_EQ("testProperty", propertyData.m_name); EXPECT_EQ("Test Property", propertyData.m_displayName); EXPECT_EQ("This is a property description", propertyData.m_description); EXPECT_EQ(MaterialPropertyDataType::Float, propertyData.m_dataType); - EXPECT_TRUE(loadResult.ContainsMessage("/id", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage("/name", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/displayName", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/description", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/type", "Success")); @@ -237,7 +237,7 @@ namespace UnitTest // Note we are keeping id and type because they are required fields const AZStd::string inputJson = R"( { - "id": "testProperty", + "name": "testProperty", "type": "Float" } )"; @@ -252,7 +252,7 @@ namespace UnitTest EXPECT_TRUE(propertyData.m_displayName.empty()); EXPECT_TRUE(propertyData.m_description.empty()); - EXPECT_TRUE(loadResult.ContainsMessage("/id", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage("/name", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/type", "Success")); EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped)); @@ -280,7 +280,7 @@ namespace UnitTest { const AZStd::string inputJson = R"( { - "id": "testProperty", + "name": "testProperty", "type": "foo" } )"; @@ -294,7 +294,7 @@ namespace UnitTest EXPECT_EQ(AZ::RPI::MaterialPropertyDataType::Invalid, propertyData.m_dataType); - EXPECT_TRUE(loadResult.ContainsMessage("/id", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage("/name", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/type", "Enum value could not read")); } @@ -303,7 +303,7 @@ namespace UnitTest const AZStd::string inputJson = R"( [ { - "id": "testProperty1", + "name": "testProperty1", "type": "Float", "defaultValue": 0.5, "min": 0.1, @@ -313,7 +313,7 @@ namespace UnitTest "step": 0.05 }, { - "id": "testProperty2", + "name": "testProperty2", "type": "Int", "defaultValue": -1, "min": -5, @@ -323,7 +323,7 @@ namespace UnitTest "step": 1 }, { - "id": "testProperty3", + "name": "testProperty3", "type": "UInt", "defaultValue": 4294901761, "min": 4294901760, @@ -368,7 +368,7 @@ namespace UnitTest for (int i = 0; i < propertyData.size(); ++i) { AZStd::string prefix = AZStd::string::format("/%d", i); - EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/id", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/name", "Success")); EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/type", "Success")); EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/defaultValue", "Success")); EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/min", "Success")); @@ -388,19 +388,19 @@ namespace UnitTest const AZStd::string inputJson = R"( [ { - "id": "testProperty1", + "name": "testProperty1", "displayName": "Test Property 1", "description": "Test", "type": "Float" }, { - "id": "testProperty2", + "name": "testProperty2", "displayName": "Test Property 2", "description": "Test", "type": "Int" }, { - "id": "testProperty3", + "name": "testProperty3", "displayName": "Test Property 3", "description": "Test", "type": "UInt" @@ -443,13 +443,13 @@ namespace UnitTest const AZStd::string inputJson = R"( [ { - "id": "testProperty1", + "name": "testProperty1", "type": "Vector2", "vectorLabels": ["U", "V"], "defaultValue": [0.6, 0.5] }, { - "id": "testProperty2", + "name": "testProperty2", "type": "Vector4", "vectorLabels": ["A", "B", "C", "D"], "defaultValue": [0.3, 0.4, 0.5, 0.6] @@ -485,21 +485,21 @@ namespace UnitTest const AZStd::string inputJson = R"( [ { - "id": "visibilityIsDefault", + "name": "visibilityIsDefault", "type": "Float" }, { - "id": "visibilityIsEditable", + "name": "visibilityIsEditable", "type": "Float", "visibility": "Enabled" }, { - "id": "visibilityIsDisabled", + "name": "visibilityIsDisabled", "type": "Float", "visibility": "Disabled" }, { - "id": "visibilityIsHidden", + "name": "visibilityIsHidden", "type": "Float", "visibility": "Hidden" } @@ -509,20 +509,20 @@ namespace UnitTest const AZStd::string expectedOutputJson = R"( [ { - "id": "visibilityIsDefault", + "name": "visibilityIsDefault", "type": "Float" }, { - "id": "visibilityIsEditable", + "name": "visibilityIsEditable", "type": "Float" }, { - "id": "visibilityIsDisabled", + "name": "visibilityIsDisabled", "type": "Float", "visibility": "Disabled" }, { - "id": "visibilityIsHidden", + "name": "visibilityIsHidden", "type": "Float", "visibility": "Hidden" } @@ -552,7 +552,7 @@ namespace UnitTest const AZStd::string inputJson = R"( [ { - "id": "testProperty1", + "name": "testProperty1", "type": "Float", "defaultValue": true, "min": -1, @@ -560,7 +560,7 @@ namespace UnitTest "step": "1" }, { - "id": "testProperty2", + "name": "testProperty2", "type": "Int", "defaultValue": true, "min": -1.5, @@ -568,7 +568,7 @@ namespace UnitTest "step": "1" }, { - "id": "testProperty3", + "name": "testProperty3", "type": "UInt", "defaultValue": "4294963200", "min": true, @@ -610,32 +610,32 @@ namespace UnitTest const AZStd::string inputJson = R"( [ { - "id": "testProperty1", + "name": "testProperty1", "type": "Bool", "defaultValue": true }, { - "id": "testProperty2", + "name": "testProperty2", "type": "Vector2", "defaultValue": [0.1, 0.2] }, { - "id": "testProperty3", + "name": "testProperty3", "type": "Vector3", "defaultValue": [0.3, 0.4, 0.5] }, { - "id": "testProperty4", + "name": "testProperty4", "type": "Vector4", "defaultValue": [0.6, 0.5, 0.8, 0.4] }, { - "id": "testProperty5", + "name": "testProperty5", "type": "Color", "defaultValue": [0.1, 0.2, 0.3] }, { - "id": "testProperty6", + "name": "testProperty6", "type": "Image", "defaultValue": "Default.png" } @@ -669,7 +669,7 @@ namespace UnitTest for (int i = 0; i < propertyData.size(); ++i) { AZStd::string prefix = AZStd::string::format("/%d", i); - EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/id", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/name", "Success")); EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/type", "Success")); EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/defaultValue", "Success")); } @@ -684,27 +684,27 @@ namespace UnitTest const AZStd::string inputJson = R"( [ { - "id": "testProperty1", + "name": "testProperty1", "type": "Bool" }, { - "id": "testProperty2", + "name": "testProperty2", "type": "Vector2" }, { - "id": "testProperty3", + "name": "testProperty3", "type": "Vector3" }, { - "id": "testProperty4", + "name": "testProperty4", "type": "Vector4" }, { - "id": "testProperty5", + "name": "testProperty5", "type": "Color" }, { - "id": "testProperty6", + "name": "testProperty6", "type": "Image" } ] @@ -745,27 +745,27 @@ namespace UnitTest const AZStd::string inputJson = R"( [ { - "id": "testProperty1", + "name": "testProperty1", "type": "Bool", "defaultValue": 1 }, { - "id": "testProperty2", + "name": "testProperty2", "type": "Vector2", "defaultValue": { "x": 0.4, "y": 0.1 } }, { - "id": "testProperty3", + "name": "testProperty3", "type": "Vector3", "defaultValue": { "x": 0.4, "y": 0.1, "z": 0.5 } }, { - "id": "testProperty4", + "name": "testProperty4", "type": "Vector4", "defaultValue": { "x": 0.4, "y": 0.1, "z": 0.5, "w": 0.6 } }, { - "id": "testProperty5", + "name": "testProperty5", "type": "Color", "defaultValue": { "hex": "FF00FF" } } @@ -798,6 +798,41 @@ namespace UnitTest TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_OneConnection) { + const AZStd::string inputJson = R"( + { + "name": "testProperty", + "type": "Float", + "connection": { + "type": "ShaderOption", + "name": "o_foo", + "shaderIndex": 2 + } + } + )"; + + MaterialTypeSourceData::PropertyDefinition propertyData; + JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson); + + EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask()); + EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing()); + + EXPECT_EQ(1, propertyData.m_outputConnections.size()); + EXPECT_EQ(MaterialPropertyOutputType::ShaderOption, propertyData.m_outputConnections[0].m_type); + EXPECT_EQ("o_foo", propertyData.m_outputConnections[0].m_fieldName); + EXPECT_EQ(2, propertyData.m_outputConnections[0].m_shaderIndex); + + EXPECT_TRUE(loadResult.ContainsMessage("/connection/type", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage("/connection/name", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage("/connection/shaderIndex", "Success")); + EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped)); + + TestStoreToJson(propertyData, inputJson); + } + + TEST_F(MaterialPropertySerializerTests, LoadUsingOldFormat) + { + // Tests backward compatibility for when "id" was the key instead of "name", for both the property and its connections. + const AZStd::string inputJson = R"( { "id": "testProperty", @@ -815,35 +850,35 @@ namespace UnitTest EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask()); EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing()); + + EXPECT_EQ("testProperty", propertyData.m_name); EXPECT_EQ(1, propertyData.m_outputConnections.size()); EXPECT_EQ(MaterialPropertyOutputType::ShaderOption, propertyData.m_outputConnections[0].m_type); - EXPECT_EQ("o_foo", propertyData.m_outputConnections[0].m_nameId); + EXPECT_EQ("o_foo", propertyData.m_outputConnections[0].m_fieldName); EXPECT_EQ(2, propertyData.m_outputConnections[0].m_shaderIndex); EXPECT_TRUE(loadResult.ContainsMessage("/connection/type", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/connection/id", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/connection/shaderIndex", "Success")); EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped)); - - TestStoreToJson(propertyData, inputJson); } TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_MultipleConnections) { const AZStd::string inputJson = R"( { - "id": "testProperty", + "name": "testProperty", "type": "Float", "connection": [ { "type": "ShaderInput", - "id": "o_foo", + "name": "o_foo", "shaderIndex": 2 }, { "type": "ShaderOption", - "id": "o_bar", + "name": "o_bar", "shaderIndex": 1 } ] @@ -858,18 +893,18 @@ namespace UnitTest EXPECT_EQ(2, propertyData.m_outputConnections.size()); EXPECT_EQ(MaterialPropertyOutputType::ShaderInput, propertyData.m_outputConnections[0].m_type); - EXPECT_EQ("o_foo", propertyData.m_outputConnections[0].m_nameId); + EXPECT_EQ("o_foo", propertyData.m_outputConnections[0].m_fieldName); EXPECT_EQ(2, propertyData.m_outputConnections[0].m_shaderIndex); EXPECT_EQ(MaterialPropertyOutputType::ShaderOption, propertyData.m_outputConnections[1].m_type); - EXPECT_EQ("o_bar", propertyData.m_outputConnections[1].m_nameId); + EXPECT_EQ("o_bar", propertyData.m_outputConnections[1].m_fieldName); EXPECT_EQ(1, propertyData.m_outputConnections[1].m_shaderIndex); EXPECT_TRUE(loadResult.ContainsMessage("/connection/0/type", "Success")); - EXPECT_TRUE(loadResult.ContainsMessage("/connection/0/id", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage("/connection/0/name", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/connection/0/shaderIndex", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/connection/1/type", "Success")); - EXPECT_TRUE(loadResult.ContainsMessage("/connection/1/id", "Success")); + EXPECT_TRUE(loadResult.ContainsMessage("/connection/1/name", "Success")); EXPECT_TRUE(loadResult.ContainsMessage("/connection/1/shaderIndex", "Success")); EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped)); @@ -881,12 +916,12 @@ namespace UnitTest // "conection" is misspelled const AZStd::string inputJson = R"( { - "id": "testProperty", + "name": "testProperty", "type": "Float", "conection": [ { "type": "ShaderInput", - "id": "o_foo", + "name": "o_foo", "shaderIndex": 2 } ] @@ -899,7 +934,7 @@ namespace UnitTest EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask()); EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing()); - EXPECT_EQ(propertyData.m_nameId, "testProperty"); + EXPECT_EQ(propertyData.m_name, "testProperty"); EXPECT_EQ(propertyData.m_dataType, MaterialPropertyDataType::Float); EXPECT_EQ(propertyData.m_outputConnections.size(), 0); @@ -911,13 +946,13 @@ namespace UnitTest // "shadrIndex" is misspelled const AZStd::string inputJson = R"( { - "id": "testProperty", + "name": "testProperty", "type": "Float", "connection": [ { "type": "ShaderInput", "shadrIndex": 2, - "id": "o_foo" + "name": "o_foo" } ] } @@ -929,10 +964,10 @@ namespace UnitTest EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask()); EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing()); - EXPECT_EQ(propertyData.m_nameId, "testProperty"); + EXPECT_EQ(propertyData.m_name, "testProperty"); EXPECT_EQ(propertyData.m_dataType, MaterialPropertyDataType::Float); EXPECT_EQ(propertyData.m_outputConnections.size(), 1); - EXPECT_EQ(propertyData.m_outputConnections[0].m_nameId, "o_foo"); + EXPECT_EQ(propertyData.m_outputConnections[0].m_fieldName, "o_foo"); EXPECT_EQ(propertyData.m_outputConnections[0].m_type, MaterialPropertyOutputType::ShaderInput); EXPECT_EQ(propertyData.m_outputConnections[0].m_shaderIndex, -1); diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp index 553c3243e1..dd3b3b2711 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp @@ -89,14 +89,14 @@ namespace UnitTest } }; - void AddPropertyGroup(MaterialSourceData& material, AZStd::string_view groupNameId) + void AddPropertyGroup(MaterialSourceData& material, AZStd::string_view groupName) { - material.m_properties.insert(groupNameId); + material.m_properties.insert(groupName); } - void AddProperty(MaterialSourceData& material, AZStd::string_view groupNameId, AZStd::string_view propertyNameId, const MaterialPropertyValue& anyValue) + void AddProperty(MaterialSourceData& material, AZStd::string_view groupName, AZStd::string_view propertyName, const MaterialPropertyValue& anyValue) { - material.m_properties[groupNameId][propertyNameId].m_value = anyValue; + material.m_properties[groupName][propertyName].m_value = anyValue; } TEST_F(MaterialSourceDataTests, CreateMaterialAsset_BasicProperties) @@ -205,25 +205,25 @@ namespace UnitTest " \"propertyLayout\": { \n" " \"version\": 1, \n" " \"groups\": [ \n" - " { \"id\": \"groupA\" }, \n" - " { \"id\": \"groupB\" }, \n" - " { \"id\": \"groupC\" } \n" + " { \"name\": \"groupA\" }, \n" + " { \"name\": \"groupB\" }, \n" + " { \"name\": \"groupC\" } \n" " ], \n" " \"properties\": { \n" " \"groupA\": [ \n" - " {\"id\": \"MyBool\", \"type\": \"bool\"}, \n" - " {\"id\": \"MyInt\", \"type\": \"int\"}, \n" - " {\"id\": \"MyUInt\", \"type\": \"uint\"} \n" + " {\"name\": \"MyBool\", \"type\": \"bool\"}, \n" + " {\"name\": \"MyInt\", \"type\": \"int\"}, \n" + " {\"name\": \"MyUInt\", \"type\": \"uint\"} \n" " ], \n" " \"groupB\": [ \n" - " {\"id\": \"MyFloat\", \"type\": \"float\"}, \n" - " {\"id\": \"MyFloat2\", \"type\": \"vector2\"}, \n" - " {\"id\": \"MyFloat3\", \"type\": \"vector3\"} \n" + " {\"name\": \"MyFloat\", \"type\": \"float\"}, \n" + " {\"name\": \"MyFloat2\", \"type\": \"vector2\"}, \n" + " {\"name\": \"MyFloat3\", \"type\": \"vector3\"} \n" " ], \n" " \"groupC\": [ \n" - " {\"id\": \"MyFloat4\", \"type\": \"vector4\"}, \n" - " {\"id\": \"MyColor\", \"type\": \"color\"}, \n" - " {\"id\": \"MyImage\", \"type\": \"image\"} \n" + " {\"name\": \"MyFloat4\", \"type\": \"vector4\"}, \n" + " {\"name\": \"MyColor\", \"type\": \"color\"}, \n" + " {\"name\": \"MyImage\", \"type\": \"image\"} \n" " ] \n" " } \n" " } \n" @@ -271,7 +271,7 @@ namespace UnitTest "properties": { "general": [ { - "id": "testColor", + "name": "testColor", "type": "color" } ] @@ -381,7 +381,7 @@ namespace UnitTest "properties": { "general": [ { - "id": "testColor", + "name": "testColor", "type": "color" } ] @@ -427,7 +427,7 @@ namespace UnitTest "properties": { "general": [ { - "id": "testColor", + "name": "testColor", "type": "color" } ] diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp index 179dc7c966..ba4a58b9ff 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp @@ -510,7 +510,7 @@ namespace UnitTest sourceData.m_shaderCollection.push_back(MaterialTypeSourceData::ShaderVariantReferenceData{ TestShaderFilename }); MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "MyBool"; + propertySource.m_name = "MyBool"; propertySource.m_displayName = "My Bool"; propertySource.m_description = "This is a bool"; propertySource.m_dataType = MaterialPropertyDataType::Bool; @@ -536,7 +536,7 @@ namespace UnitTest sourceData.m_shaderCollection.push_back(MaterialTypeSourceData::ShaderVariantReferenceData{ TestShaderFilename }); MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "MyFloat"; + propertySource.m_name = "MyFloat"; propertySource.m_displayName = "My Float"; propertySource.m_description = "This is a float"; propertySource.m_min = 0.0f; @@ -566,7 +566,7 @@ namespace UnitTest sourceData.m_shaderCollection.push_back(MaterialTypeSourceData::ShaderVariantReferenceData{ TestShaderFilename }); MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "MyImage"; + propertySource.m_name = "MyImage"; propertySource.m_displayName = "My Image"; propertySource.m_description = "This is an image"; propertySource.m_dataType = MaterialPropertyDataType::Image; @@ -591,7 +591,7 @@ namespace UnitTest sourceData.m_shaderCollection.push_back(MaterialTypeSourceData::ShaderVariantReferenceData{TestShaderFilename}); MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "MyInt"; + propertySource.m_name = "MyInt"; propertySource.m_displayName = "My Integer"; propertySource.m_dataType = MaterialPropertyDataType::Int; propertySource.m_outputConnections.push_back(MaterialTypeSourceData::PropertyConnection{MaterialPropertyOutputType::ShaderOption, AZStd::string("o_foo"), 0}); @@ -614,7 +614,7 @@ namespace UnitTest sourceData.m_shaderCollection.push_back(MaterialTypeSourceData::ShaderVariantReferenceData{TestShaderFilename}); MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "MyInt"; + propertySource.m_name = "MyInt"; propertySource.m_dataType = MaterialPropertyDataType::Int; propertySource.m_outputConnections.push_back(MaterialTypeSourceData::PropertyConnection{MaterialPropertyOutputType::ShaderOption, AZStd::string("DoesNotExist"), 0}); sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource); @@ -633,7 +633,7 @@ namespace UnitTest MaterialTypeSourceData::PropertyDefinition propertySource; propertySource.m_dataType = MaterialPropertyDataType::Int; - propertySource.m_nameId = "a"; + propertySource.m_name = "a"; sourceData.m_propertyLayout.m_properties["not a valid name because it has spaces"].push_back(propertySource); // Expected errors: @@ -654,7 +654,7 @@ namespace UnitTest MaterialTypeSourceData::PropertyDefinition propertySource; propertySource.m_dataType = MaterialPropertyDataType::Int; - propertySource.m_nameId = "not a valid name because it has spaces"; + propertySource.m_name = "not a valid name because it has spaces"; sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource); // Expected errors: @@ -674,7 +674,7 @@ namespace UnitTest MaterialTypeSourceData::PropertyDefinition propertySource; propertySource.m_dataType = MaterialPropertyDataType::Int; - propertySource.m_nameId = "a"; + propertySource.m_name = "a"; sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource); sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource); @@ -738,7 +738,7 @@ namespace UnitTest sourceData.m_shaderCollection.push_back(MaterialTypeSourceData::ShaderVariantReferenceData{ "shaderC.shader" }); MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "MyInt"; + propertySource.m_name = "MyInt"; propertySource.m_displayName = "Integer"; propertySource.m_description = "Integer property that is connected to multiple shader settings"; propertySource.m_dataType = MaterialPropertyDataType::Int; @@ -797,7 +797,7 @@ namespace UnitTest MaterialTypeSourceData sourceData; MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "NonAliasFloat"; + propertySource.m_name = "NonAliasFloat"; propertySource.m_displayName = "Non-Alias Float"; propertySource.m_description = "This float is processed by a functor, not with a direct alias"; propertySource.m_dataType = MaterialPropertyDataType::Float; @@ -842,13 +842,13 @@ namespace UnitTest sourceData.m_shaderCollection.push_back(MaterialTypeSourceData::ShaderVariantReferenceData{TestShaderFilename}); MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "EnableSpecialPassA"; + propertySource.m_name = "EnableSpecialPassA"; propertySource.m_displayName = "Enable Special Pass"; propertySource.m_description = "This is a bool to enable an extra shader/pass"; propertySource.m_dataType = MaterialPropertyDataType::Bool; // Note that we don't fill propertySource.m_outputConnections because this is not a direct-connected property sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource); - propertySource.m_nameId = "EnableSpecialPassB"; + propertySource.m_name = "EnableSpecialPassB"; sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource); sourceData.m_materialFunctorSourceData.push_back( @@ -902,7 +902,7 @@ namespace UnitTest sourceData.m_shaderCollection.push_back(MaterialTypeSourceData::ShaderVariantReferenceData{TestShaderFilename}); MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = "MyProperty"; + propertySource.m_name = "MyProperty"; propertySource.m_dataType = MaterialPropertyDataType::Bool; // Note that we don't fill propertySource.m_outputConnections because this is not a direct-connected property sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource); @@ -938,7 +938,7 @@ namespace UnitTest auto addProperty = [&sourceData](MaterialPropertyDataType dateType, const char* propertyName, const char* srgConstantName, const AZ::RPI::MaterialPropertyValue& value) { MaterialTypeSourceData::PropertyDefinition propertySource; - propertySource.m_nameId = propertyName; + propertySource.m_name = propertyName; propertySource.m_dataType = dateType; propertySource.m_outputConnections.push_back(MaterialTypeSourceData::PropertyConnection{ MaterialPropertyOutputType::ShaderInput, AZStd::string(srgConstantName) }); propertySource.m_value = value; @@ -975,6 +975,152 @@ namespace UnitTest // Note that serialization of individual fields within material properties is thoroughly tested in // MaterialPropertySerializerTests, so the sample property data used here is cursory. + const AZStd::string inputJson = R"( + { + "description": "This is a general description about the material", + "propertyLayout": { + "version": 2, + "groups": [ + { + "name": "groupA", + "displayName": "Property Group A", + "description": "Description of property group A" + }, + { + "name": "groupB", + "displayName": "Property Group B", + "description": "Description of property group B" + } + ], + "properties": { + "groupA": [ + { + "name": "foo", + "type": "Bool", + "defaultValue": true + }, + { + "name": "bar", + "type": "Image", + "defaultValue": "Default.png", + "visibility": "Hidden" + } + ], + "groupB": [ + { + "name": "foo", + "type": "Float", + "defaultValue": 0.5 + }, + { + "name": "bar", + "type": "Color", + "defaultValue": [0.5, 0.5, 0.5], + "visibility": "Disabled" + } + ] + } + }, + "shaders": [ + { + "file": "ForwardPass.shader", + "tag": "ForwardPass", + "options": { + "o_optionA": "False", + "o_optionB": "True" + } + }, + { + "file": "DepthPass.shader", + "options": { + "o_optionC": "1", + "o_optionD": "2" + } + } + ], + "functors": [ + { + "type": "EnableShader", + "args": { + "enablePassProperty": "groupA.foo", + "shaderIndex": 1 + } + }, + { + "type": "Splat3", + "args": { + "floatPropertyInput": "groupB.foo", + "float3ShaderSettingOutput": "m_someFloat3" + } + } + ] + } + )"; + + MaterialTypeSourceData material; + JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson); + + EXPECT_EQ(material.m_description, "This is a general description about the material"); + + EXPECT_EQ(material.m_propertyLayout.m_version, 2); + + EXPECT_EQ(material.m_propertyLayout.m_groups.size(), 2); + EXPECT_TRUE(material.FindGroup("groupA") != nullptr); + EXPECT_TRUE(material.FindGroup("groupB") != nullptr); + EXPECT_EQ(material.FindGroup("groupA")->m_displayName, "Property Group A"); + EXPECT_EQ(material.FindGroup("groupB")->m_displayName, "Property Group B"); + EXPECT_EQ(material.FindGroup("groupA")->m_description, "Description of property group A"); + EXPECT_EQ(material.FindGroup("groupB")->m_description, "Description of property group B"); + + EXPECT_EQ(material.m_propertyLayout.m_properties.size(), 2); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"].size(), 2); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"].size(), 2); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][0].m_name, "foo"); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][1].m_name, "bar"); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][0].m_name, "foo"); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][1].m_name, "bar"); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][0].m_dataType, MaterialPropertyDataType::Bool); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][1].m_dataType, MaterialPropertyDataType::Image); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][0].m_dataType, MaterialPropertyDataType::Float); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][1].m_dataType, MaterialPropertyDataType::Color); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][0].m_visibility, MaterialPropertyVisibility::Enabled); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][1].m_visibility, MaterialPropertyVisibility::Hidden); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][0].m_visibility, MaterialPropertyVisibility::Enabled); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][1].m_visibility, MaterialPropertyVisibility::Disabled); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][0].m_value, true); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][1].m_value, AZStd::string{"Default.png"}); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][0].m_value, 0.5f); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][1].m_value, AZ::Color(0.5f, 0.5f, 0.5f, 1.0f)); + + EXPECT_EQ(material.m_shaderCollection.size(), 2); + EXPECT_EQ(material.m_shaderCollection[0].m_shaderFilePath, "ForwardPass.shader"); + EXPECT_EQ(material.m_shaderCollection[1].m_shaderFilePath, "DepthPass.shader"); + EXPECT_EQ(material.m_shaderCollection[0].m_shaderOptionValues.size(), 2); + EXPECT_EQ(material.m_shaderCollection[1].m_shaderOptionValues.size(), 2); + EXPECT_EQ(material.m_shaderCollection[0].m_shaderOptionValues[Name{"o_optionA"}], Name{"False"}); + EXPECT_EQ(material.m_shaderCollection[0].m_shaderOptionValues[Name{"o_optionB"}], Name{"True"}); + EXPECT_EQ(material.m_shaderCollection[1].m_shaderOptionValues[Name{"o_optionC"}], Name{"1"}); + EXPECT_EQ(material.m_shaderCollection[1].m_shaderOptionValues[Name{"o_optionD"}], Name{"2"}); + EXPECT_EQ(material.m_shaderCollection[0].m_shaderTag, Name{"ForwardPass"}); + + EXPECT_EQ(material.m_materialFunctorSourceData.size(), 2); + EXPECT_TRUE(azrtti_cast(material.m_materialFunctorSourceData[0]->GetActualSourceData().get())); + EXPECT_EQ(azrtti_cast(material.m_materialFunctorSourceData[0]->GetActualSourceData().get())->m_enablePassPropertyId, "groupA.foo"); + EXPECT_EQ(azrtti_cast(material.m_materialFunctorSourceData[0]->GetActualSourceData().get())->m_shaderIndex, 1); + EXPECT_TRUE(azrtti_cast(material.m_materialFunctorSourceData[1]->GetActualSourceData().get())); + EXPECT_EQ(azrtti_cast(material.m_materialFunctorSourceData[1]->GetActualSourceData().get())->m_floatPropertyInputId, "groupB.foo"); + EXPECT_EQ(azrtti_cast(material.m_materialFunctorSourceData[1]->GetActualSourceData().get())->m_float3ShaderSettingOutputId, "m_someFloat3"); + + AZStd::string outputJson; + JsonTestResult storeResult = StoreTestDataToJson(material, outputJson); + ExpectSimilarJson(inputJson, outputJson); + } + + TEST_F(MaterialTypeSourceDataTests, LoadAllFieldsUsingOldFormat) + { + // The content of this test was copied from LoadAndStoreJson_AllFields to prove backward compatibility. + // (The "store" part of the test was not included because the saved data will be the new format). + const AZStd::string inputJson = R"( { "description": "This is a general description about the material", @@ -1075,10 +1221,10 @@ namespace UnitTest EXPECT_EQ(material.m_propertyLayout.m_properties.size(), 2); EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"].size(), 2); EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"].size(), 2); - EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][0].m_nameId, "foo"); - EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][1].m_nameId, "bar"); - EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][0].m_nameId, "foo"); - EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][1].m_nameId, "bar"); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][0].m_name, "foo"); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][1].m_name, "bar"); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][0].m_name, "foo"); + EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][1].m_name, "bar"); EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][0].m_dataType, MaterialPropertyDataType::Bool); EXPECT_EQ(material.m_propertyLayout.m_properties["groupA"][1].m_dataType, MaterialPropertyDataType::Image); EXPECT_EQ(material.m_propertyLayout.m_properties["groupB"][0].m_dataType, MaterialPropertyDataType::Float); @@ -1110,10 +1256,6 @@ namespace UnitTest EXPECT_TRUE(azrtti_cast(material.m_materialFunctorSourceData[1]->GetActualSourceData().get())); EXPECT_EQ(azrtti_cast(material.m_materialFunctorSourceData[1]->GetActualSourceData().get())->m_floatPropertyInputId, "groupB.foo"); EXPECT_EQ(azrtti_cast(material.m_materialFunctorSourceData[1]->GetActualSourceData().get())->m_float3ShaderSettingOutputId, "m_someFloat3"); - - AZStd::string outputJson; - JsonTestResult storeResult = StoreTestDataToJson(material, outputJson); - ExpectSimilarJson(inputJson, outputJson); } TEST_F(MaterialTypeSourceDataTests, CreateMaterialTypeAsset_PropertyImagePath) @@ -1127,7 +1269,7 @@ namespace UnitTest "version": 2, "groups": [ { - "id": "general", + "name": "general", "displayName": "General", "description": "" } @@ -1135,12 +1277,12 @@ namespace UnitTest "properties": { "general": [ { - "id": "absolute", + "name": "absolute", "type": "Image", "defaultValue": "%s" }, { - "id": "relative", + "name": "relative", "type": "Image", "defaultValue": "%s" } diff --git a/Gems/Atom/RPI/Code/atom_rpi_edit_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_edit_files.cmake index c7a459f217..e32d19ffd7 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_edit_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_edit_files.cmake @@ -18,6 +18,8 @@ set(FILES Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h Include/Atom/RPI.Edit/Material/MaterialConverterBus.h Include/Atom/RPI.Edit/Material/MaterialPropertyId.h + Include/Atom/RPI.Edit/Material/MaterialPropertyConnectionSerializer.h + Include/Atom/RPI.Edit/Material/MaterialPropertyGroupSerializer.h Include/Atom/RPI.Edit/Material/MaterialPropertySerializer.h Include/Atom/RPI.Edit/Material/MaterialPropertyValueSerializer.h Include/Atom/RPI.Edit/Material/MaterialPropertyValueSourceData.h @@ -36,6 +38,8 @@ set(FILES Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp Source/RPI.Edit/Material/MaterialTypeSourceData.cpp Source/RPI.Edit/Material/MaterialPropertyId.cpp + Source/RPI.Edit/Material/MaterialPropertyGroupSerializer.cpp + Source/RPI.Edit/Material/MaterialPropertyConnectionSerializer.cpp Source/RPI.Edit/Material/MaterialPropertySerializer.cpp Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp Source/RPI.Edit/Material/MaterialPropertyValueSourceData.cpp diff --git a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick.materialtype b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick.materialtype index 0b62638c5a..00f11663f7 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick.materialtype +++ b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick.materialtype @@ -4,12 +4,12 @@ "version": 3, "groups": [ { - "id": "shape", + "name": "shape", "displayName": "Shape", "description": "Properties for configuring size, shape, and position of the bricks." }, { - "id": "appearance", + "name": "appearance", "displayName": "Appearance", "description": "Properties for configuring the appearance of the bricks and grout lines." } @@ -17,7 +17,7 @@ "properties": { "shape": [ { - "id": "brickWidth", + "name": "brickWidth", "displayName": "Brick Width", "description": "The width of each brick.", "type": "Float", @@ -27,11 +27,11 @@ "step": 0.001, "connection": { "type": "ShaderInput", - "id": "m_brickWidth" + "name": "m_brickWidth" } }, { - "id": "brickHeight", + "name": "brickHeight", "displayName": "Brick Height", "description": "The height of each brick.", "type": "Float", @@ -41,11 +41,11 @@ "step": 0.001, "connection": { "type": "ShaderInput", - "id": "m_brickHeight" + "name": "m_brickHeight" } }, { - "id": "brickOffset", + "name": "brickOffset", "displayName": "Offset", "description": "The offset of each stack of bricks as a percentage of brick width.", "type": "Float", @@ -54,11 +54,11 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_brickOffset" + "name": "m_brickOffset" } }, { - "id": "lineWidth", + "name": "lineWidth", "displayName": "Line Width", "description": "The width of the grout lines.", "type": "Float", @@ -68,11 +68,11 @@ "step": 0.0001, "connection": { "type": "ShaderInput", - "id": "m_lineWidth" + "name": "m_lineWidth" } }, { - "id": "lineDepth", + "name": "lineDepth", "displayName": "Line Depth", "description": "The depth of the grout lines.", "type": "Float", @@ -81,34 +81,34 @@ "softMax": 0.02, "connection": { "type": "ShaderInput", - "id": "m_lineDepth" + "name": "m_lineDepth" } } ], "appearance": [ { - "id": "noiseTexture", + "name": "noiseTexture", "type": "Image", "defaultValue": "TestData/Textures/noise512.png", "visibility": "Hidden", "connection": { "type": "ShaderInput", - "id": "m_noise" + "name": "m_noise" } }, { - "id": "brickColor", + "name": "brickColor", "displayName": "Brick Color", "description": "The color of the bricks.", "type": "Color", "defaultValue": [1.0,1.0,1.0], "connection": { "type": "ShaderInput", - "id": "m_brickColor" + "name": "m_brickColor" } }, { - "id": "brickColorNoise", + "name": "brickColorNoise", "displayName": "Brick Color Noise", "description": "Scale the variation of brick color.", "type": "Float", @@ -117,22 +117,22 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_brickNoiseFactor" + "name": "m_brickNoiseFactor" } }, { - "id": "lineColor", + "name": "lineColor", "displayName": "Line Color", "description": "The color of the grout lines.", "type": "Color", "defaultValue": [0.5,0.5,0.5], "connection": { "type": "ShaderInput", - "id": "m_lineColor" + "name": "m_lineColor" } }, { - "id": "lineColorNoise", + "name": "lineColorNoise", "displayName": "Line Color Noise", "description": "Scale the variation of grout line color.", "type": "Float", @@ -141,11 +141,11 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_lineNoiseFactor" + "name": "m_lineNoiseFactor" } }, { - "id": "brickColorBleed", + "name": "brickColorBleed", "displayName": "Brick Color Bleed", "description": "Distance into the grout line that the brick color will continue.", "type": "Float", @@ -154,11 +154,11 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_brickColorBleed" + "name": "m_brickColorBleed" } }, { - "id": "ao", + "name": "ao", "displayName": "Ambient Occlusion", "description": "The strength of baked ambient occlusion in the grout lines.", "type": "Float", @@ -167,7 +167,7 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_aoFactor" + "name": "m_aoFactor" } } ] diff --git a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR.materialtype b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR.materialtype index fb2b8523e1..81ebd63c28 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR.materialtype +++ b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR.materialtype @@ -4,24 +4,24 @@ "version": 3, "groups": [ { - "id": "settings", + "name": "settings", "displayName": "Settings" } ], "properties": { "settings": [ { - "id": "color", + "name": "color", "displayName": "Color", "type": "Color", "defaultValue": [ 1.0, 1.0, 1.0 ], "connection": { "type": "ShaderInput", - "id": "m_baseColor" + "name": "m_baseColor" } }, { - "id": "metallic", + "name": "metallic", "displayName": "Metallic", "type": "Float", "defaultValue": 0.0, @@ -29,11 +29,11 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_metallic" + "name": "m_metallic" } }, { - "id": "roughness", + "name": "roughness", "displayName": "Roughness", "type": "Float", "defaultValue": 1.0, @@ -41,7 +41,7 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_roughness" + "name": "m_roughness" } } ] diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h index 565c9f00a3..bf18d36c2c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h @@ -32,10 +32,10 @@ namespace AtomToolsFramework // AtomToolsDocumentRequestBus::Handler implementation AZStd::string_view GetAbsolutePath() const override; AZStd::string_view GetRelativePath() const override; - const AZStd::any& GetPropertyValue(const AZ::Name& propertyFullName) const override; - const AtomToolsFramework::DynamicProperty& GetProperty(const AZ::Name& propertyFullName) const override; + const AZStd::any& GetPropertyValue(const AZ::Name& propertyId) const override; + const AtomToolsFramework::DynamicProperty& GetProperty(const AZ::Name& propertyId) const override; bool IsPropertyGroupVisible(const AZ::Name& propertyGroupFullName) const override; - void SetPropertyValue(const AZ::Name& propertyFullName, const AZStd::any& value) override; + void SetPropertyValue(const AZ::Name& propertyId, const AZStd::any& value) override; bool Open(AZStd::string_view loadPath) override; bool Reopen() override; bool Save() override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h index a8ef7852ea..6a21a8a2df 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace AtomToolsFramework { diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/DynamicProperty/DynamicProperty.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/DynamicProperty/DynamicProperty.h index 9ee53680ae..68e5e38cbd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/DynamicProperty/DynamicProperty.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/DynamicProperty/DynamicProperty.h @@ -42,8 +42,8 @@ namespace AtomToolsFramework AZ_CLASS_ALLOCATOR(DynamicPropertyConfig, AZ::SystemAllocator, 0); DynamicPropertyType m_dataType = DynamicPropertyType::Invalid; - AZ::Name m_id; - AZStd::string m_nameId; + AZ::Name m_id; //!< The full property ID, which will normally be "groupName.propertyName" + AZStd::string m_name; AZStd::string m_displayName; AZStd::string m_groupName; AZStd::string m_description; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h index 900dc3535c..6e5ac6e463 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h @@ -41,27 +41,27 @@ namespace AtomToolsFramework //! Add a group consisting of a collapsable header and widget virtual void AddGroup( - const AZStd::string& groupNameId, + const AZStd::string& groupName, const AZStd::string& groupDisplayName, const AZStd::string& groupDescription, QWidget* groupWidget) = 0; //! Sets the visibility of a specific property group. This impacts both the header and the widget. - virtual void SetGroupVisible(const AZStd::string& groupNameId, bool visible) = 0; + virtual void SetGroupVisible(const AZStd::string& groupName, bool visible) = 0; //! Returns whether a specific property is visible. //! Note this follows the same rules as QWidget::isVisible(), meaning a group could be not visible due to the widget's parents being not visible. - virtual bool IsGroupVisible(const AZStd::string& groupNameId) const = 0; + virtual bool IsGroupVisible(const AZStd::string& groupName) const = 0; //! Returns whether a specific property is explicitly hidden. //! Note this follows the same rules as QWidget::isHidden(), meaning a group that is hidden will not become visible automatically when the parent becomes visible. - virtual bool IsGroupHidden(const AZStd::string& groupNameId) const = 0; + virtual bool IsGroupHidden(const AZStd::string& groupName) const = 0; //! Calls Refresh for a specific InspectorGroupWidget, allowing for non-destructive UI changes - virtual void RefreshGroup(const AZStd::string& groupNameId) = 0; + virtual void RefreshGroup(const AZStd::string& groupName) = 0; //! Calls Rebuild for a specific InspectorGroupWidget, allowing for destructive UI changes - virtual void RebuildGroup(const AZStd::string& groupNameId) = 0; + virtual void RebuildGroup(const AZStd::string& groupName) = 0; //! Calls Refresh for all InspectorGroupWidget, allowing for non-destructive UI changes virtual void RefreshAll() = 0; @@ -70,13 +70,13 @@ namespace AtomToolsFramework virtual void RebuildAll() = 0; //! Expands a specific group - virtual void ExpandGroup(const AZStd::string& groupNameId) = 0; + virtual void ExpandGroup(const AZStd::string& groupName) = 0; //! Collapses a specific group - virtual void CollapseGroup(const AZStd::string& groupNameId) = 0; + virtual void CollapseGroup(const AZStd::string& groupName) = 0; //! Checks the expansion state of a specific group - virtual bool IsGroupExpanded(const AZStd::string& groupNameId) const = 0; + virtual bool IsGroupExpanded(const AZStd::string& groupName) const = 0; //! Expands all groups and headers virtual void ExpandAll() = 0; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h index adcd94ca10..7e655d978e 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h @@ -52,33 +52,33 @@ namespace AtomToolsFramework void AddGroupsEnd() override; void AddGroup( - const AZStd::string& groupNameId, + const AZStd::string& groupName, const AZStd::string& groupDisplayName, const AZStd::string& groupDescription, QWidget* groupWidget) override; - void SetGroupVisible(const AZStd::string& groupNameId, bool visible) override; - bool IsGroupVisible(const AZStd::string& groupNameId) const override; - bool IsGroupHidden(const AZStd::string& groupNameId) const override; + void SetGroupVisible(const AZStd::string& groupName, bool visible) override; + bool IsGroupVisible(const AZStd::string& groupName) const override; + bool IsGroupHidden(const AZStd::string& groupName) const override; - void RefreshGroup(const AZStd::string& groupNameId) override; - void RebuildGroup(const AZStd::string& groupNameId) override; + void RefreshGroup(const AZStd::string& groupName) override; + void RebuildGroup(const AZStd::string& groupName) override; void RefreshAll() override; void RebuildAll() override; - void ExpandGroup(const AZStd::string& groupNameId) override; - void CollapseGroup(const AZStd::string& groupNameId) override; - bool IsGroupExpanded(const AZStd::string& groupNameId) const override; + void ExpandGroup(const AZStd::string& groupName) override; + void CollapseGroup(const AZStd::string& groupName) override; + bool IsGroupExpanded(const AZStd::string& groupName) const override; void ExpandAll() override; void CollapseAll() override; protected: - virtual bool ShouldGroupAutoExpanded(const AZStd::string& groupNameId) const; - virtual void OnGroupExpanded(const AZStd::string& groupNameId); - virtual void OnGroupCollapsed(const AZStd::string& groupNameId); - virtual void OnHeaderClicked(const AZStd::string& groupNameId, QMouseEvent* event); + virtual bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const; + virtual void OnGroupExpanded(const AZStd::string& groupName); + virtual void OnGroupCollapsed(const AZStd::string& groupName); + virtual void OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event); private: QScopedPointer m_ui; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h index 4956fbc1dc..1200cb3d79 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -103,7 +102,6 @@ namespace AtomToolsFramework class ModularViewportCameraControllerInstance final : public AzFramework::MultiViewportControllerInstanceInterface , public ModularViewportCameraControllerRequestBus::Handler - , private AzFramework::ViewportDebugDisplayEventBus::Handler { public: explicit ModularViewportCameraControllerInstance(AzFramework::ViewportId viewportId, ModularViewportCameraController* controller); @@ -114,16 +112,12 @@ namespace AtomToolsFramework void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override; // ModularViewportCameraControllerRequestBus overrides ... - void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) override; - AZStd::optional LookAtAfterInterpolation() const override; + void InterpolateToTransform(const AZ::Transform& worldFromLocal) override; AZ::Transform GetReferenceFrame() const override; void SetReferenceFrame(const AZ::Transform& worldFromLocal) override; void ClearReferenceFrame() override; private: - // AzFramework::ViewportDebugDisplayEventBus overrides ... - void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; - //! Update the reference frame after a change has been made to the camera //! view without updating the internal camera via user input. void RefreshReferenceFrame(); @@ -154,9 +148,8 @@ namespace AtomToolsFramework CameraAnimation m_cameraAnimation; //!< Camera animation state (used during CameraMode::Animation). CameraMode m_cameraMode = CameraMode::Control; //!< The current mode the camera is operating in. - AZStd::optional m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished. - //!< Will be cleared when the view changes (camera looks away). - AZ::Transform m_referenceFrameOverride = AZ::Transform::CreateIdentity(); //!< + //! An additional reference frame the camera can operate in (identity has no effect). + AZ::Transform m_referenceFrameOverride = AZ::Transform::CreateIdentity(); //! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally). bool m_updatingTransformInternally = false; //! Listen for camera view changes outside of the camera controller. diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h index 388d24164a..ab397692e4 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h @@ -29,11 +29,7 @@ namespace AtomToolsFramework //! Begin a smooth transition of the camera to the requested transform. //! @param worldFromLocal The transform of where the camera should end up. - //! @param lookAtDistance The distance between the camera transform and the imagined look at point. - virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) = 0; - - //! Look at point after an interpolation has finished and no translation has occurred. - virtual AZStd::optional LookAtAfterInterpolation() const = 0; + virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0; //! Return the current reference frame. //! @note If a reference frame has not been set or a frame has been cleared, this is just the identity. diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index 318a49027a..623d759c9f 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -22,8 +21,6 @@ #include #include #include -#include -#include namespace AtomToolsFramework { @@ -38,8 +35,6 @@ namespace AtomToolsFramework , public AzFramework::WindowRequestBus::Handler , protected AzFramework::InputChannelEventListener , protected AZ::TickBus::Handler - , protected AZ::Render::Bootstrap::NotificationBus::Handler - , protected AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler { public: //! Creates a RenderViewportWidget. @@ -126,7 +121,6 @@ namespace AtomToolsFramework // AZ::TickBus::Handler ... void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; - int GetTickOrder() override; // QWidget ... void resizeEvent(QResizeEvent *event) override; @@ -134,21 +128,9 @@ namespace AtomToolsFramework void enterEvent(QEvent* event) override; void leaveEvent(QEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; - void focusInEvent(QFocusEvent* event) override; - - // AZ::Render::Bootstrap::NotificationBus::Handler ... - void OnFrameRateLimitChanged(float fpsLimit) override; - - // AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler ... - void OnInactiveViewportFrameRateChanged(float fpsLimit) override; private: - AzFramework::NativeWindowHandle GetNativeWindowHandle() const; - void UpdateFrameRate(); - - void SetScreen(QScreen* screen); void SendWindowResizeEvent(); - void NotifyUpdateRefreshRate(); // The underlying ViewportContext, our entry-point to the Atom RPI. AZ::RPI::ViewportContextPtr m_viewportContext; @@ -169,11 +151,5 @@ namespace AtomToolsFramework AZ::ScriptTimePoint m_time; // Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList. AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr; - // Stores our current screen, used for tracking the current refresh rate. - QScreen* m_screen = nullptr; - // Stores the last RenderViewportWidget that has received user focus. - // This is used for optional framerate throtting for "inactive" viewports via the - // ed_inactive_viewport_fps_limit CVAR. - AZ::EnvironmentVariable m_lastFocusedViewport; }; } //namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidgetNotificationBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidgetNotificationBus.h deleted file mode 100644 index 0563bd6bf2..0000000000 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidgetNotificationBus.h +++ /dev/null @@ -1,35 +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 - * - */ - -#pragma once - -#include - -namespace AtomToolsFramework -{ - //! Provides an interface for providing notifications specific to RenderViewportWidget. - //! @note Most behaviors in RenderViewportWidget are handled by its underyling - //! ViewportContext, this bus is specifically for functionality exclusive to the - //! Qt layer provided by RenderViewportWidget. - class RenderViewportWidgetNotifications : public AZ::EBusTraits - { - public: - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; - - //! Triggered when the idle frame rate limit for inactive viewports changed. - //! Controlled by the ed_inactive_viewport_fps_limit CVAR. - //! Active viewports are controlled by the r_fps_limit CVAR. - virtual void OnInactiveViewportFrameRateChanged([[maybe_unused]]float fpsLimit){} - - protected: - ~RenderViewportWidgetNotifications() = default; - }; - - using RenderViewportWidgetNotificationBus = AZ::EBus; -} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp index 336737f419..1e2c685c3c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp @@ -38,16 +38,16 @@ namespace AtomToolsFramework return m_relativePath; } - const AZStd::any& AtomToolsDocument::GetPropertyValue([[maybe_unused]] const AZ::Name& propertyFullName) const + const AZStd::any& AtomToolsDocument::GetPropertyValue([[maybe_unused]] const AZ::Name& propertyId) const { - AZ_UNUSED(propertyFullName); + AZ_UNUSED(propertyId); AZ_Error("AtomToolsDocument", false, "%s not implemented.", __FUNCTION__); return m_invalidValue; } - const AtomToolsFramework::DynamicProperty& AtomToolsDocument::GetProperty([[maybe_unused]] const AZ::Name& propertyFullName) const + const AtomToolsFramework::DynamicProperty& AtomToolsDocument::GetProperty([[maybe_unused]] const AZ::Name& propertyId) const { - AZ_UNUSED(propertyFullName); + AZ_UNUSED(propertyId); AZ_Error("AtomToolsDocument", false, "%s not implemented.", __FUNCTION__); return m_invalidProperty; } @@ -59,9 +59,9 @@ namespace AtomToolsFramework return false; } - void AtomToolsDocument::SetPropertyValue([[maybe_unused]] const AZ::Name& propertyFullName, [[maybe_unused]] const AZStd::any& value) + void AtomToolsDocument::SetPropertyValue([[maybe_unused]] const AZ::Name& propertyId, [[maybe_unused]] const AZStd::any& value) { - AZ_UNUSED(propertyFullName); + AZ_UNUSED(propertyId); AZ_UNUSED(value); AZ_Error("AtomToolsDocument", false, "%s not implemented.", __FUNCTION__); } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/DynamicProperty/DynamicProperty.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/DynamicProperty/DynamicProperty.cpp index d08728aad1..2054858726 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/DynamicProperty/DynamicProperty.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/DynamicProperty/DynamicProperty.cpp @@ -192,7 +192,7 @@ namespace AtomToolsFramework AZStd::string DynamicProperty::GetDisplayName() const { - return !m_config.m_displayName.empty() ? m_config.m_displayName : m_config.m_nameId; + return !m_config.m_displayName.empty() ? m_config.m_displayName : m_config.m_name; } AZStd::string DynamicProperty::GetGroupName() const diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp index e1f8573bb3..86ac4a3df1 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace AtomToolsFramework { diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp index 5eda93ca59..fbe188364a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp @@ -31,20 +31,26 @@ namespace AtomToolsFramework void InspectorWidget::AddHeading(QWidget* headingWidget) { - headingWidget->setParent(m_ui->m_headingSection); - m_ui->m_headingSectionLayout->addWidget(headingWidget); + headingWidget->setParent(this); + m_ui->m_headingContentsLayout->addWidget(headingWidget); } void InspectorWidget::ClearHeading() { - qDeleteAll(m_ui->m_headingSection->findChildren(QString(), Qt::FindDirectChildrenOnly)); - qDeleteAll(m_ui->m_headingSectionLayout->children()); + while (QLayoutItem* child = m_ui->m_headingContentsLayout->takeAt(0)) + { + delete child->widget(); + delete child; + } } void InspectorWidget::Reset() { - qDeleteAll(m_ui->m_groupContents->findChildren(QString(), Qt::FindDirectChildrenOnly)); - qDeleteAll(m_ui->m_groupContentsLayout->children()); + while (QLayoutItem* child = m_ui->m_groupContentsLayout->takeAt(0)) + { + delete child->widget(); + delete child; + } m_groups.clear(); } @@ -66,7 +72,7 @@ namespace AtomToolsFramework } void InspectorWidget::AddGroup( - const AZStd::string& groupNameId, + const AZStd::string& groupName, const AZStd::string& groupDisplayName, const AZStd::string& groupDescription, QWidget* groupWidget) @@ -76,31 +82,31 @@ namespace AtomToolsFramework groupHeader->setToolTip(groupDescription.c_str()); m_ui->m_groupContentsLayout->addWidget(groupHeader); - groupWidget->setObjectName(groupNameId.c_str()); + groupWidget->setObjectName(groupName.c_str()); groupWidget->setParent(m_ui->m_groupContents); m_ui->m_groupContentsLayout->addWidget(groupWidget); - m_groups[groupNameId] = {groupHeader, groupWidget}; + m_groups[groupName] = {groupHeader, groupWidget}; - connect(groupHeader, &InspectorGroupHeaderWidget::clicked, this, [this, groupNameId](QMouseEvent* event) { - OnHeaderClicked(groupNameId, event); + connect(groupHeader, &InspectorGroupHeaderWidget::clicked, this, [this, groupName](QMouseEvent* event) { + OnHeaderClicked(groupName, event); }); - connect(groupHeader, &InspectorGroupHeaderWidget::expanded, this, [this, groupNameId]() { OnGroupExpanded(groupNameId); }); - connect(groupHeader, &InspectorGroupHeaderWidget::collapsed, this, [this, groupNameId]() { OnGroupCollapsed(groupNameId); }); + connect(groupHeader, &InspectorGroupHeaderWidget::expanded, this, [this, groupName]() { OnGroupExpanded(groupName); }); + connect(groupHeader, &InspectorGroupHeaderWidget::collapsed, this, [this, groupName]() { OnGroupCollapsed(groupName); }); - if (ShouldGroupAutoExpanded(groupNameId)) + if (ShouldGroupAutoExpanded(groupName)) { - ExpandGroup(groupNameId); + ExpandGroup(groupName); } else { - CollapseGroup(groupNameId); + CollapseGroup(groupName); } } - void InspectorWidget::SetGroupVisible(const AZStd::string& groupNameId, bool visible) + void InspectorWidget::SetGroupVisible(const AZStd::string& groupName, bool visible) { - auto groupItr = m_groups.find(groupNameId); + auto groupItr = m_groups.find(groupName); if (groupItr != m_groups.end()) { groupItr->second.m_header->setVisible(visible); @@ -108,29 +114,29 @@ namespace AtomToolsFramework } } - bool InspectorWidget::IsGroupVisible(const AZStd::string& groupNameId) const + bool InspectorWidget::IsGroupVisible(const AZStd::string& groupName) const { - auto groupItr = m_groups.find(groupNameId); + auto groupItr = m_groups.find(groupName); return groupItr != m_groups.end() ? groupItr->second.m_header->isVisible() : false; } - bool InspectorWidget::IsGroupHidden(const AZStd::string& groupNameId) const + bool InspectorWidget::IsGroupHidden(const AZStd::string& groupName) const { - auto groupItr = m_groups.find(groupNameId); + auto groupItr = m_groups.find(groupName); return groupItr != m_groups.end() ? groupItr->second.m_header->isHidden() : false; } - void InspectorWidget::RefreshGroup(const AZStd::string& groupNameId) + void InspectorWidget::RefreshGroup(const AZStd::string& groupName) { - for (auto groupWidget : m_ui->m_groupContents->findChildren(groupNameId.c_str())) + for (auto groupWidget : m_ui->m_groupContents->findChildren(groupName.c_str())) { groupWidget->Refresh(); } } - void InspectorWidget::RebuildGroup(const AZStd::string& groupNameId) + void InspectorWidget::RebuildGroup(const AZStd::string& groupName) { - for (auto groupWidget : m_ui->m_groupContents->findChildren(groupNameId.c_str())) + for (auto groupWidget : m_ui->m_groupContents->findChildren(groupName.c_str())) { groupWidget->Rebuild(); } @@ -152,9 +158,9 @@ namespace AtomToolsFramework } } - void InspectorWidget::ExpandGroup(const AZStd::string& groupNameId) + void InspectorWidget::ExpandGroup(const AZStd::string& groupName) { - auto groupItr = m_groups.find(groupNameId); + auto groupItr = m_groups.find(groupName); if (groupItr != m_groups.end()) { groupItr->second.m_header->SetExpanded(true); @@ -162,9 +168,9 @@ namespace AtomToolsFramework } } - void InspectorWidget::CollapseGroup(const AZStd::string& groupNameId) + void InspectorWidget::CollapseGroup(const AZStd::string& groupName) { - auto groupItr = m_groups.find(groupNameId); + auto groupItr = m_groups.find(groupName); if (groupItr != m_groups.end()) { groupItr->second.m_header->SetExpanded(false); @@ -172,9 +178,9 @@ namespace AtomToolsFramework } } - bool InspectorWidget::IsGroupExpanded(const AZStd::string& groupNameId) const + bool InspectorWidget::IsGroupExpanded(const AZStd::string& groupName) const { - auto groupItr = m_groups.find(groupNameId); + auto groupItr = m_groups.find(groupName); return groupItr != m_groups.end() ? groupItr->second.m_header->IsExpanded() : false; } @@ -196,33 +202,33 @@ namespace AtomToolsFramework } } - bool InspectorWidget::ShouldGroupAutoExpanded(const AZStd::string& groupNameId) const + bool InspectorWidget::ShouldGroupAutoExpanded(const AZStd::string& groupName) const { - AZ_UNUSED(groupNameId); + AZ_UNUSED(groupName); return true; } - void InspectorWidget::OnGroupExpanded(const AZStd::string& groupNameId) + void InspectorWidget::OnGroupExpanded(const AZStd::string& groupName) { - AZ_UNUSED(groupNameId); + AZ_UNUSED(groupName); } - void InspectorWidget::OnGroupCollapsed(const AZStd::string& groupNameId) + void InspectorWidget::OnGroupCollapsed(const AZStd::string& groupName) { - AZ_UNUSED(groupNameId); + AZ_UNUSED(groupName); } - void InspectorWidget::OnHeaderClicked(const AZStd::string& groupNameId, QMouseEvent* event) + void InspectorWidget::OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event) { if (event->button() == Qt::MouseButton::LeftButton) { - if (!IsGroupExpanded(groupNameId)) + if (!IsGroupExpanded(groupName)) { - ExpandGroup(groupNameId); + ExpandGroup(groupName); } else { - CollapseGroup(groupNameId); + CollapseGroup(groupName); } return; } @@ -230,8 +236,8 @@ namespace AtomToolsFramework if (event->button() == Qt::MouseButton::RightButton) { QMenu menu; - menu.addAction("Expand", [this, groupNameId]() { ExpandGroup(groupNameId); })->setEnabled(!IsGroupExpanded(groupNameId)); - menu.addAction("Collapse", [this, groupNameId]() { CollapseGroup(groupNameId); })->setEnabled(IsGroupExpanded(groupNameId)); + menu.addAction("Expand", [this, groupName]() { ExpandGroup(groupName); })->setEnabled(!IsGroupExpanded(groupName)); + menu.addAction("Collapse", [this, groupName]() { CollapseGroup(groupName); })->setEnabled(IsGroupExpanded(groupName)); menu.addAction("Expand All", [this]() { ExpandAll(); }); menu.addAction("Collapse All", [this]() { CollapseAll(); }); menu.exec(event->globalPos()); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.ui b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.ui index 13f9c9e41c..c197ee172e 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.ui +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.ui @@ -30,104 +30,85 @@ 0 - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::ScrollBarAsNeeded - - 0 + + true - - 0 - - - 0 - - - 0 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::ScrollBarAsNeeded + + + + 0 + 0 + 679 + 767 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 - - true + + 0 - - - - 0 - 0 - 683 - 763 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - -
+ + + + + diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/MaterialPropertyUtil.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/MaterialPropertyUtil.cpp index 641cf63d04..0b14f9313f 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/MaterialPropertyUtil.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/MaterialPropertyUtil.cpp @@ -74,7 +74,7 @@ namespace AtomToolsFramework void ConvertToPropertyConfig(AtomToolsFramework::DynamicPropertyConfig& propertyConfig, const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition) { propertyConfig.m_dataType = ConvertToEditableType(propertyDefinition.m_dataType); - propertyConfig.m_nameId = propertyDefinition.m_nameId; + propertyConfig.m_name = propertyDefinition.m_name; propertyConfig.m_displayName = propertyDefinition.m_displayName; propertyConfig.m_description = propertyDefinition.m_description; propertyConfig.m_defaultValue = ConvertToEditableType(propertyDefinition.m_value); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index bcc9a08f77..a92bfdbcdb 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -21,15 +21,6 @@ namespace AtomToolsFramework { - AZ_CVAR( - AZ::Color, - ed_cameraSystemOrbitPointColor, - AZ::Color::CreateFromRgba(255, 255, 255, 255), - nullptr, - AZ::ConsoleFunctorFlags::Null, - ""); - AZ_CVAR(float, ed_cameraSystemOrbitPointSize, 0.1f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); - AZ::Transform TransformFromMatrix4x4(const AZ::Matrix4x4& matrix) { const auto rotation = AZ::Matrix3x3::CreateFromMatrix4x4(matrix); @@ -200,14 +191,12 @@ namespace AtomToolsFramework m_cameraViewMatrixChangeHandler = AZ::RPI::ViewportContext::MatrixChangedEvent::Handler(handleCameraChange); m_modularCameraViewportContext->ConnectViewMatrixChangedHandler(m_cameraViewMatrixChangeHandler); - AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId()); ModularViewportCameraControllerRequestBus::Handler::BusConnect(viewportId); } ModularViewportCameraControllerInstance::~ModularViewportCameraControllerInstance() { ModularViewportCameraControllerRequestBus::Handler::BusDisconnect(); - AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect(); } bool ModularViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) @@ -238,19 +227,6 @@ namespace AtomToolsFramework { m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count()); m_camera = AzFramework::SmoothCamera(m_camera, m_targetCamera, m_cameraProps, event.m_deltaTime.count()); - - // if there has been an interpolation, only clear the look at point if it is no longer - // centered in the view (the camera has looked away from it) - if (m_lookAtAfterInterpolation.has_value()) - { - if (const float lookDirection = - (*m_lookAtAfterInterpolation - m_camera.Translation()).GetNormalized().Dot(m_camera.Transform().GetBasisY()); - !AZ::IsCloseMag(lookDirection, 1.0f, 0.001f)) - { - m_lookAtAfterInterpolation = {}; - } - } - m_modularCameraViewportContext->SetCameraTransform(m_referenceFrameOverride * m_camera.Transform()); } else if (m_cameraMode == CameraMode::Animation) @@ -272,7 +248,8 @@ namespace AtomToolsFramework const AZ::Vector3 eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(current)); m_camera.m_pitch = eulerAngles.GetX(); m_camera.m_yaw = eulerAngles.GetZ(); - m_camera.m_lookAt = current.GetTranslation(); + m_camera.m_pivot = current.GetTranslation(); + m_camera.m_offset = AZ::Vector3::CreateZero(); m_targetCamera = m_camera; m_modularCameraViewportContext->SetCameraTransform(current); @@ -287,27 +264,10 @@ namespace AtomToolsFramework m_updatingTransformInternally = false; } - void ModularViewportCameraControllerInstance::DisplayViewport( - [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) - { - if (const float alpha = AZStd::min(-m_camera.m_lookDist / 5.0f, 1.0f); alpha > AZ::Constants::FloatEpsilon) - { - const AZ::Color orbitPointColor = ed_cameraSystemOrbitPointColor; - debugDisplay.SetColor(orbitPointColor.GetR(), orbitPointColor.GetG(), orbitPointColor.GetB(), alpha); - debugDisplay.DrawWireSphere(m_camera.m_lookAt, ed_cameraSystemOrbitPointSize); - } - } - - void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance) + void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal) { m_cameraMode = CameraMode::Animation; m_cameraAnimation = CameraAnimation{ m_referenceFrameOverride * m_camera.Transform(), worldFromLocal, 0.0f }; - m_lookAtAfterInterpolation = worldFromLocal.GetTranslation() + worldFromLocal.GetBasisY() * lookAtDistance; - } - - AZStd::optional ModularViewportCameraControllerInstance::LookAtAfterInterpolation() const - { - return m_lookAtAfterInterpolation; } AZ::Transform ModularViewportCameraControllerInstance::GetReferenceFrame() const @@ -325,8 +285,8 @@ namespace AtomToolsFramework m_referenceFrameOverride = worldFromLocal; m_targetCamera.m_pitch = 0.0f; m_targetCamera.m_yaw = 0.0f; - m_targetCamera.m_lookAt = AZ::Vector3::CreateZero(); - m_targetCamera.m_lookDist = 0.0f; + m_targetCamera.m_offset = AZ::Vector3::CreateZero(); + m_targetCamera.m_pivot = AZ::Vector3::CreateZero(); m_camera = m_targetCamera; } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 2a15041e20..7192afebf7 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -6,42 +6,23 @@ * */ -#include -#include -#include +#include #include #include -#include -#include -#include +#include #include #include #include #include +#include +#include +#include #include -#include #include -#include -#include -#include +#include #include - -static void OnInactiveViewportFrameRateChanged(const float& fpsLimit) -{ - AtomToolsFramework::RenderViewportWidgetNotificationBus::Broadcast( - &AtomToolsFramework::RenderViewportWidgetNotificationBus::Events::OnInactiveViewportFrameRateChanged, fpsLimit); -} - -AZ_CVAR( - float, - ed_inactive_viewport_fps_limit, - 0, - OnInactiveViewportFrameRateChanged, - AZ::ConsoleFunctorFlags::Null, - "The maximum framerate to render viewports that don't have focus at"); - -static constexpr const char* LastFocusedViewportVariableName = "AtomToolsFramework::RenderViewportWidget::LastFocusedViewport"; +#include namespace AtomToolsFramework { @@ -49,12 +30,6 @@ namespace AtomToolsFramework : QWidget(parent) , AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDefault()) { - m_lastFocusedViewport = AZ::Environment::FindVariable(LastFocusedViewportVariableName); - if (!m_lastFocusedViewport) - { - m_lastFocusedViewport = AZ::Environment::CreateVariable(LastFocusedViewportVariableName, nullptr); - } - if (shouldInitializeViewportContext) { InitializeViewportContext(); @@ -63,24 +38,13 @@ namespace AtomToolsFramework setUpdatesEnabled(false); setFocusPolicy(Qt::FocusPolicy::WheelFocus); setMouseTracking(true); - - // Wait a frame for our window handle to be constructed, then wire up our screen change signals. - QTimer::singleShot( - 0, - [this]() - { - QObject::connect(windowHandle(), &QWindow::screenChanged, this, &RenderViewportWidget::SetScreen); - }); - SetScreen(screen()); } bool RenderViewportWidget::InitializeViewportContext(AzFramework::ViewportId id) { if (m_viewportContext != nullptr) { - AZ_Assert( - id == AzFramework::InvalidViewportId || m_viewportContext->GetId() == id, - "Attempted to reinitialize RenderViewportWidget with a different ID"); + AZ_Assert(id == AzFramework::InvalidViewportId || m_viewportContext->GetId() == id, "Attempted to reinitialize RenderViewportWidget with a different ID"); return true; } @@ -95,7 +59,7 @@ namespace AtomToolsFramework // Before we do anything else, we must create a ViewportContext which will give us a ViewportId if we didn't manually specify one. AZ::RPI::ViewportContextRequestsInterface::CreationParameters params; params.device = AZ::RHI::RHISystemInterface::Get()->GetDevice(); - params.windowHandle = GetNativeWindowHandle(); + params.windowHandle = reinterpret_cast(winId()); params.id = id; AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle); m_viewportContext = viewportContextManager->CreateViewportContext(AZ::Name(), params); @@ -116,46 +80,29 @@ namespace AtomToolsFramework AzFramework::InputChannelEventListener::Connect(); AZ::TickBus::Handler::BusConnect(); AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle); - AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect(); - AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler::BusConnect(); m_inputChannelMapper = new AzToolsFramework::QtEventToAzInputMapper(this, id); // Forward input events to our controller list. - QObject::connect( - m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this, + QObject::connect(m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this, [this](const AzFramework::InputChannel* inputChannel, QEvent* event) + { + AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); + if (m_controllerList->HandleInputChannelEvent(AzFramework::ViewportControllerInputEvent{GetId(), windowId, *inputChannel})) { - const AzFramework::NativeWindowHandle windowId = GetNativeWindowHandle(); - if (m_controllerList->HandleInputChannelEvent( - AzFramework::ViewportControllerInputEvent{ GetId(), windowId, *inputChannel })) + // If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate. + if (event) { - // If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate. - if (event) - { - event->setAccepted(true); - } + event->setAccepted(true); } - }); - - // Update our target frame rate. If we're the only viewport, become active. - if (m_lastFocusedViewport.Get() == nullptr) - { - m_lastFocusedViewport.Set(this); - } - UpdateFrameRate(); - + } + }); return true; } RenderViewportWidget::~RenderViewportWidget() { - if (m_lastFocusedViewport.Get() == this) - { - m_lastFocusedViewport.Set(nullptr); - } - AzFramework::WindowRequestBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); AzFramework::InputChannelEventListener::Disconnect(); @@ -234,22 +181,17 @@ namespace AtomToolsFramework bool shouldConsumeEvent = true; - const bool eventHandled = m_controllerList->HandleInputChannelEvent({ GetId(), GetNativeWindowHandle(), inputChannel }); + AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); + const bool eventHandled = m_controllerList->HandleInputChannelEvent({GetId(), windowId, inputChannel}); - // If our controllers handled the event and it's one we can safely consume (i.e. it's not an Ended event that other viewports might - // need), consume it. + // If our controllers handled the event and it's one we can safely consume (i.e. it's not an Ended event that other viewports might need), consume it. return eventHandled && shouldConsumeEvent; } - void RenderViewportWidget::OnTick([[maybe_unused]] float deltaTime, AZ::ScriptTimePoint time) + void RenderViewportWidget::OnTick([[maybe_unused]]float deltaTime, AZ::ScriptTimePoint time) { m_time = time; - m_controllerList->UpdateViewport({ GetId(), AzFramework::FloatSeconds(deltaTime), m_time }); - } - - int RenderViewportWidget::GetTickOrder() - { - return AZ::ComponentTickBus::TICK_PRE_RENDER; + m_controllerList->UpdateViewport({GetId(), AzFramework::FloatSeconds(deltaTime), m_time}); } void RenderViewportWidget::resizeEvent([[maybe_unused]] QResizeEvent* event) @@ -277,75 +219,6 @@ namespace AtomToolsFramework m_mousePosition = event->localPos(); } - void RenderViewportWidget::focusInEvent([[maybe_unused]] QFocusEvent* event) - { - RenderViewportWidget* lastFocusedViewport = m_lastFocusedViewport.Get(); - if (lastFocusedViewport == this) - { - return; - } - - RenderViewportWidget* previousFocusWidget = lastFocusedViewport; - m_lastFocusedViewport.Set(this); - - // Ensure this viewport and whatever viewport last had focus (if any) respect - // the active / inactive viewport frame rate settings. - UpdateFrameRate(); - if (previousFocusWidget != nullptr) - { - previousFocusWidget->UpdateFrameRate(); - } - } - - void RenderViewportWidget::OnFrameRateLimitChanged([[maybe_unused]] float fpsLimit) - { - UpdateFrameRate(); - } - - void RenderViewportWidget::OnInactiveViewportFrameRateChanged([[maybe_unused]] float fpsLimit) - { - UpdateFrameRate(); - } - - AzFramework::NativeWindowHandle RenderViewportWidget::GetNativeWindowHandle() const - { - return reinterpret_cast(winId()); - } - - void RenderViewportWidget::UpdateFrameRate() - { - if (ed_inactive_viewport_fps_limit > 0.f && m_lastFocusedViewport.Get() != this) - { - m_viewportContext->SetFpsLimit(ed_inactive_viewport_fps_limit); - } - else - { - float fpsLimit = 0.f; - AZ::Render::Bootstrap::RequestBus::BroadcastResult(fpsLimit, &AZ::Render::Bootstrap::RequestBus::Events::GetFrameRateLimit); - m_viewportContext->SetFpsLimit(fpsLimit); - } - } - - void RenderViewportWidget::SetScreen(QScreen* screen) - { - if (m_screen != screen) - { - if (m_screen) - { - QObject::disconnect(m_screen, &QScreen::refreshRateChanged, this, &RenderViewportWidget::NotifyUpdateRefreshRate); - } - - if (screen) - { - QObject::connect(m_screen, &QScreen::refreshRateChanged, this, &RenderViewportWidget::NotifyUpdateRefreshRate); - } - - NotifyUpdateRefreshRate(); - - m_screen = screen; - } - } - void RenderViewportWidget::SendWindowResizeEvent() { // Scale the size by the DPI of the platform to @@ -353,14 +226,8 @@ namespace AtomToolsFramework const QSize uiWindowSize = size(); const QSize windowSize = uiWindowSize * devicePixelRatioF(); - AzFramework::WindowNotificationBus::Event( - GetNativeWindowHandle(), &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); - } - - void RenderViewportWidget::NotifyUpdateRefreshRate() - { - AzFramework::WindowNotificationBus::Event( - GetNativeWindowHandle(), &AzFramework::WindowNotificationBus::Events::OnRefreshRateChanged, GetDisplayRefreshRate()); + const AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); + AzFramework::WindowNotificationBus::Event(windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); } AZ::Name RenderViewportWidget::GetCurrentContextName() const @@ -418,7 +285,9 @@ namespace AtomToolsFramework // Build camera state from Atom camera transforms AzFramework::CameraState cameraState = AzFramework::CreateCameraFromWorldFromViewMatrix( - currentView->GetViewToWorldMatrix(), AZ::Vector2{ aznumeric_cast(width()), aznumeric_cast(height()) }); + currentView->GetViewToWorldMatrix(), + AZ::Vector2{aznumeric_cast(width()), aznumeric_cast(height())} + ); AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(cameraState, currentView->GetViewToClipMatrix()); // Convert from Z-up @@ -430,7 +299,8 @@ namespace AtomToolsFramework AzFramework::ScreenPoint RenderViewportWidget::ViewportWorldToScreen(const AZ::Vector3& worldPosition) { - if (AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView(); currentView == nullptr) + if (AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView(); + currentView == nullptr) { return AzFramework::ScreenPoint(0, 0); } @@ -443,10 +313,12 @@ namespace AtomToolsFramework const auto& cameraProjection = m_viewportContext->GetCameraProjectionMatrix(); const auto& cameraView = m_viewportContext->GetCameraViewMatrix(); - const AZ::Vector4 normalizedScreenPosition{ screenPosition.m_x * 2.f / width() - 1.0f, - (height() - screenPosition.m_y) * 2.f / height() - 1.0f, - 1.f - depth, // [GFX TODO] [ATOM-1501] Currently we always assume reverse depth - 1.f }; + const AZ::Vector4 normalizedScreenPosition { + screenPosition.m_x * 2.f / width() - 1.0f, + (height() - screenPosition.m_y) * 2.f / height() - 1.0f, + 1.f - depth, // [GFX TODO] [ATOM-1501] Currently we always assume reverse depth + 1.f + }; AZ::Matrix4x4 worldFromScreen = cameraProjection * cameraView; worldFromScreen.InvertFull(); @@ -475,7 +347,7 @@ namespace AtomToolsFramework AZ::Vector3 rayDirection = pos1.value() - pos0.value(); rayDirection.Normalize(); - return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{ rayOrigin, rayDirection }; + return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{rayOrigin, rayDirection}; } float RenderViewportWidget::DeviceScalingFactor() @@ -505,12 +377,12 @@ namespace AtomToolsFramework AzFramework::WindowSize RenderViewportWidget::GetClientAreaSize() const { - return AzFramework::WindowSize{ aznumeric_cast(width()), aznumeric_cast(height()) }; + return AzFramework::WindowSize{aznumeric_cast(width()), aznumeric_cast(height())}; } void RenderViewportWidget::ResizeClientArea(AzFramework::WindowSize clientAreaSize) { - const QSize targetSize = QSize{ aznumeric_cast(clientAreaSize.m_width), aznumeric_cast(clientAreaSize.m_height) }; + const QSize targetSize = QSize{aznumeric_cast(clientAreaSize.m_width), aznumeric_cast(clientAreaSize.m_height)}; resize(targetSize); } @@ -520,7 +392,7 @@ namespace AtomToolsFramework return false; } - void RenderViewportWidget::SetFullScreenState([[maybe_unused]] bool fullScreenState) + void RenderViewportWidget::SetFullScreenState([[maybe_unused]]bool fullScreenState) { // The RenderViewportWidget does not currently support full screen. } @@ -543,20 +415,11 @@ namespace AtomToolsFramework uint32_t RenderViewportWidget::GetDisplayRefreshRate() const { - return static_cast(screen()->refreshRate()); + return 60; } uint32_t RenderViewportWidget::GetSyncInterval() const { - uint32_t interval = 1; - - // Get vsync_interval from AzFramework::NativeWindow, which owns it. - // NativeWindow also handles broadcasting OnVsyncIntervalChanged to all - // WindowNotificationBus listeners. - if (auto console = AZ::Interface::Get()) - { - console->GetCvarValue("vsync_interval", interval); - } - return interval; + return 1; } -} // namespace AtomToolsFramework +} //namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake index a24b45179f..3d4bb82eec 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake @@ -28,7 +28,6 @@ set(FILES Include/AtomToolsFramework/Util/MaterialPropertyUtil.h Include/AtomToolsFramework/Util/Util.h Include/AtomToolsFramework/Viewport/RenderViewportWidget.h - Include/AtomToolsFramework/Viewport/RenderViewportWidgetNotificationBus.h Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h Include/AtomToolsFramework/Window/AtomToolsMainWindow.h diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Window/MaterialEditorWindowSettings.h b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Window/MaterialEditorWindowSettings.h index dd42f79106..c56da58ff1 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Window/MaterialEditorWindowSettings.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Window/MaterialEditorWindowSettings.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #endif diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index d032f35e38..17e292ac16 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -59,7 +59,7 @@ namespace MaterialEditor return &m_materialTypeSourceData; } - const AZStd::any& MaterialDocument::GetPropertyValue(const AZ::Name& propertyFullName) const + const AZStd::any& MaterialDocument::GetPropertyValue(const AZ::Name& propertyId) const { using namespace AZ; using namespace RPI; @@ -70,10 +70,10 @@ namespace MaterialEditor return m_invalidValue; } - const auto it = m_properties.find(propertyFullName); + const auto it = m_properties.find(propertyId); if (it == m_properties.end()) { - AZ_Error("MaterialDocument", false, "Material document property could not be found: '%s'.", propertyFullName.GetCStr()); + AZ_Error("MaterialDocument", false, "Material document property could not be found: '%s'.", propertyId.GetCStr()); return m_invalidValue; } @@ -81,7 +81,7 @@ namespace MaterialEditor return property.GetValue(); } - const AtomToolsFramework::DynamicProperty& MaterialDocument::GetProperty(const AZ::Name& propertyFullName) const + const AtomToolsFramework::DynamicProperty& MaterialDocument::GetProperty(const AZ::Name& propertyId) const { if (!IsOpen()) { @@ -89,10 +89,10 @@ namespace MaterialEditor return m_invalidProperty; } - const auto it = m_properties.find(propertyFullName); + const auto it = m_properties.find(propertyId); if (it == m_properties.end()) { - AZ_Error("MaterialDocument", false, "Material document property could not be found: '%s'.", propertyFullName.GetCStr()); + AZ_Error("MaterialDocument", false, "Material document property could not be found: '%s'.", propertyId.GetCStr()); return m_invalidProperty; } @@ -118,7 +118,7 @@ namespace MaterialEditor return it->second; } - void MaterialDocument::SetPropertyValue(const AZ::Name& propertyFullName, const AZStd::any& value) + void MaterialDocument::SetPropertyValue(const AZ::Name& propertyId, const AZStd::any& value) { using namespace AZ; using namespace RPI; @@ -129,10 +129,10 @@ namespace MaterialEditor return; } - const auto it = m_properties.find(propertyFullName); + const auto it = m_properties.find(propertyId); if (it == m_properties.end()) { - AZ_Error("MaterialDocument", false, "Material document property could not be found: '%s'.", propertyFullName.GetCStr()); + AZ_Error("MaterialDocument", false, "Material document property could not be found: '%s'.", propertyId.GetCStr()); return; } @@ -143,8 +143,7 @@ namespace MaterialEditor AtomToolsFramework::DynamicProperty& property = it->second; property.SetValue(AtomToolsFramework::ConvertToEditableType(propertyValue)); - const AZ::RPI::MaterialPropertyId propertyId = AZ::RPI::MaterialPropertyId::Parse(propertyFullName.GetStringView()); - const auto propertyIndex = m_materialInstance->FindPropertyIndex(propertyFullName); + const auto propertyIndex = m_materialInstance->FindPropertyIndex(propertyId); if (!propertyIndex.IsNull()) { if (m_materialInstance->SetPropertyValue(propertyIndex, propertyValue)) @@ -593,8 +592,9 @@ namespace MaterialEditor bool result = true; // populate sourceData with properties that meet the filter - m_materialTypeSourceData.EnumerateProperties([this, &sourceData, &propertyFilter, &result](const AZStd::string& groupNameId, const AZStd::string& propertyNameId, const auto& propertyDefinition) { - const MaterialPropertyId propertyId(groupNameId, propertyNameId); + m_materialTypeSourceData.EnumerateProperties([this, &sourceData, &propertyFilter, &result](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition) { + + const MaterialPropertyId propertyId(groupName, propertyName); const auto it = m_properties.find(propertyId.GetFullName()); if (it != m_properties.end() && propertyFilter(it->second)) @@ -609,7 +609,7 @@ namespace MaterialEditor return false; } - sourceData.m_properties[groupNameId][propertyNameId].m_value = propertyValue; + sourceData.m_properties[groupName][propertyName].m_value = propertyValue; } } return true; @@ -770,11 +770,11 @@ namespace MaterialEditor // Populate the property map from a combination of source data and assets // Assets must still be used for now because they contain the final accumulated value after all other materials // in the hierarchy are applied - m_materialTypeSourceData.EnumerateProperties([this, &parentPropertyValues](const AZStd::string& groupNameId, const AZStd::string& propertyNameId, const auto& propertyDefinition) { + m_materialTypeSourceData.EnumerateProperties([this, &parentPropertyValues](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition) { AtomToolsFramework::DynamicPropertyConfig propertyConfig; // Assign id before conversion so it can be used in dynamic description - propertyConfig.m_id = MaterialPropertyId(groupNameId, propertyNameId).GetCStr(); + propertyConfig.m_id = MaterialPropertyId(groupName, propertyName).GetCStr(); const auto& propertyIndex = m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id); const bool propertyIndexInBounds = propertyIndex.IsValid() && propertyIndex.GetIndex() < m_materialAsset->GetPropertyValues().size(); @@ -786,8 +786,8 @@ namespace MaterialEditor propertyConfig.m_showThumbnail = true; propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType(m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]); propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType(parentPropertyValues[propertyIndex.GetIndex()]); - auto groupDefinition = m_materialTypeSourceData.FindGroup(groupNameId); - propertyConfig.m_groupName = groupDefinition ? groupDefinition->m_displayName : groupNameId; + auto groupDefinition = m_materialTypeSourceData.FindGroup(groupName); + propertyConfig.m_groupName = groupDefinition ? groupDefinition->m_displayName : groupName; m_properties[propertyConfig.m_id] = AtomToolsFramework::DynamicProperty(propertyConfig); } return true; @@ -796,7 +796,7 @@ namespace MaterialEditor // Populate the property group visibility map for (MaterialTypeSourceData::GroupDefinition& group : m_materialTypeSourceData.GetGroupDefinitionsInDisplayOrder()) { - m_propertyGroupVisibility[AZ::Name{group.m_nameId}] = true; + m_propertyGroupVisibility[AZ::Name{group.m_name}] = true; } // Adding properties for material type and parent as part of making dynamic @@ -808,7 +808,7 @@ namespace MaterialEditor AtomToolsFramework::DynamicPropertyConfig propertyConfig; propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::Asset; propertyConfig.m_id = "overview.materialType"; - propertyConfig.m_nameId = "materialType"; + propertyConfig.m_name = "materialType"; propertyConfig.m_displayName = "Material Type"; propertyConfig.m_groupName = "Overview"; propertyConfig.m_description = "The material type defines the layout, properties, default values, shader connections, and other " @@ -823,7 +823,7 @@ namespace MaterialEditor propertyConfig = {}; propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::Asset; propertyConfig.m_id = "overview.parentMaterial"; - propertyConfig.m_nameId = "parentMaterial"; + propertyConfig.m_name = "parentMaterial"; propertyConfig.m_displayName = "Parent Material"; propertyConfig.m_groupName = "Overview"; propertyConfig.m_description = @@ -846,7 +846,7 @@ namespace MaterialEditor propertyConfig = {}; propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::String; propertyConfig.m_id = MaterialPropertyId(UvGroupName, shaderInput).GetCStr(); - propertyConfig.m_nameId = shaderInput; + propertyConfig.m_name = shaderInput; propertyConfig.m_displayName = shaderInput; propertyConfig.m_groupName = "UV Sets"; propertyConfig.m_description = shaderInput; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h index d732680b7b..03997a2a91 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h @@ -43,10 +43,10 @@ namespace MaterialEditor //////////////////////////////////////////////////////////////////////// // AtomToolsFramework::AtomToolsDocument //////////////////////////////////////////////////////////////////////// - const AZStd::any& GetPropertyValue(const AZ::Name& propertyFullName) const override; - const AtomToolsFramework::DynamicProperty& GetProperty(const AZ::Name& propertyFullName) const override; + const AZStd::any& GetPropertyValue(const AZ::Name& propertyId) const override; + const AtomToolsFramework::DynamicProperty& GetProperty(const AZ::Name& propertyId) const override; bool IsPropertyGroupVisible(const AZ::Name& propertyGroupFullName) const override; - void SetPropertyValue(const AZ::Name& propertyFullName, const AZStd::any& value) override; + void SetPropertyValue(const AZ::Name& propertyId, const AZStd::any& value) override; bool Open(AZStd::string_view loadPath) override; bool Reopen() override; bool Save() override; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp index 7571738597..932e2e7436 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index 7edff11174..6d6fd377e3 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -182,7 +182,9 @@ namespace MaterialEditor AZ_Error("MaterialViewportRenderer", m_shadowCatcherMaterial != nullptr, "Could not create shadow catcher material."); AZ::Render::MaterialAssignmentMap shadowCatcherMaterials; - shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = m_shadowCatcherMaterial; + auto& shadowCatcherMaterialAssignment = shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId]; + shadowCatcherMaterialAssignment.m_materialInstance = m_shadowCatcherMaterial; + shadowCatcherMaterialAssignment.m_materialInstancePreCreated = true; AZ::Render::MaterialComponentRequestBus::Event(m_shadowCatcherEntity->GetId(), &AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, shadowCatcherMaterials); @@ -291,7 +293,9 @@ namespace MaterialEditor MaterialDocumentRequestBus::EventResult(materialInstance, documentId, &MaterialDocumentRequestBus::Events::GetInstance); AZ::Render::MaterialAssignmentMap materials; - materials[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = materialInstance; + auto& materialAssignment = materials[AZ::Render::DefaultMaterialAssignmentId]; + materialAssignment.m_materialInstance = materialInstance; + materialAssignment.m_materialInstancePreCreated = true; AZ::Render::MaterialComponentRequestBus::Event(m_modelEntity->GetId(), &AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, materials); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index 28d7d3d3f5..df0b179dc1 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -44,20 +44,20 @@ namespace MaterialEditor AtomToolsFramework::InspectorWidget::Reset(); } - bool MaterialInspector::ShouldGroupAutoExpanded(const AZStd::string& groupNameId) const + bool MaterialInspector::ShouldGroupAutoExpanded(const AZStd::string& groupName) const { - auto stateItr = m_windowSettings->m_inspectorCollapsedGroups.find(GetGroupSaveStateKey(groupNameId)); + auto stateItr = m_windowSettings->m_inspectorCollapsedGroups.find(GetGroupSaveStateKey(groupName)); return stateItr == m_windowSettings->m_inspectorCollapsedGroups.end(); } - void MaterialInspector::OnGroupExpanded(const AZStd::string& groupNameId) + void MaterialInspector::OnGroupExpanded(const AZStd::string& groupName) { - m_windowSettings->m_inspectorCollapsedGroups.erase(GetGroupSaveStateKey(groupNameId)); + m_windowSettings->m_inspectorCollapsedGroups.erase(GetGroupSaveStateKey(groupName)); } - void MaterialInspector::OnGroupCollapsed(const AZStd::string& groupNameId) + void MaterialInspector::OnGroupCollapsed(const AZStd::string& groupName) { - m_windowSettings->m_inspectorCollapsedGroups.insert(GetGroupSaveStateKey(groupNameId)); + m_windowSettings->m_inspectorCollapsedGroups.insert(GetGroupSaveStateKey(groupName)); } void MaterialInspector::OnDocumentOpened(const AZ::Uuid& documentId) @@ -86,9 +86,9 @@ namespace MaterialEditor AddGroupsEnd(); } - AZ::Crc32 MaterialInspector::GetGroupSaveStateKey(const AZStd::string& groupNameId) const + AZ::Crc32 MaterialInspector::GetGroupSaveStateKey(const AZStd::string& groupName) const { - return AZ::Crc32(AZStd::string::format("MaterialInspector::PropertyGroup::%s::%s", m_documentPath.c_str(), groupNameId.c_str())); + return AZ::Crc32(AZStd::string::format("MaterialInspector::PropertyGroup::%s::%s", m_documentPath.c_str(), groupName.c_str())); } bool MaterialInspector::CompareInstanceNodeProperties( @@ -105,10 +105,10 @@ namespace MaterialEditor MaterialDocumentRequestBus::EventResult( materialTypeSourceData, m_documentId, &MaterialDocumentRequestBus::Events::GetMaterialTypeSourceData); - const AZStd::string groupNameId = "overview"; + const AZStd::string groupName = "overview"; const AZStd::string groupDisplayName = "Overview"; const AZStd::string groupDescription = materialTypeSourceData->m_description; - auto& group = m_groups[groupNameId]; + auto& group = m_groups[groupName]; AtomToolsFramework::DynamicProperty property; AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult( @@ -122,9 +122,9 @@ namespace MaterialEditor // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupNameId), + &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupName), [this](const auto source, const auto target) { return CompareInstanceNodeProperties(source, target); }); - AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget); + AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget); } void MaterialInspector::AddUvNamesGroup() @@ -132,10 +132,10 @@ namespace MaterialEditor AZ::Data::Asset materialAsset; MaterialDocumentRequestBus::EventResult(materialAsset, m_documentId, &MaterialDocumentRequestBus::Events::GetAsset); - const AZStd::string groupNameId = UvGroupName; + const AZStd::string groupName = UvGroupName; const AZStd::string groupDisplayName = "UV Sets"; const AZStd::string groupDescription = "UV set names in this material, which can be renamed to match those in the model."; - auto& group = m_groups[groupNameId]; + auto& group = m_groups[groupName]; const auto& uvNameMap = materialAsset->GetMaterialTypeAsset()->GetUvNameMap(); group.m_properties.reserve(uvNameMap.size()); @@ -145,7 +145,7 @@ namespace MaterialEditor AtomToolsFramework::DynamicProperty property; AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult( property, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetProperty, - AZ::RPI::MaterialPropertyId(groupNameId, uvNamePair.m_shaderInput.ToString()).GetFullName()); + AZ::RPI::MaterialPropertyId(groupName, uvNamePair.m_shaderInput.ToString()).GetFullName()); group.m_properties.push_back(property); property.SetValue(property.GetConfig().m_parentValue); @@ -153,9 +153,9 @@ namespace MaterialEditor // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupNameId), + &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupName), [this](const auto source, const auto target) { return CompareInstanceNodeProperties(source, target); }); - AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget); + AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget); } void MaterialInspector::AddPropertiesGroup() @@ -166,14 +166,14 @@ namespace MaterialEditor for (const auto& groupDefinition : materialTypeSourceData->GetGroupDefinitionsInDisplayOrder()) { - const AZStd::string& groupNameId = groupDefinition.m_nameId; - const AZStd::string& groupDisplayName = !groupDefinition.m_displayName.empty() ? groupDefinition.m_displayName : groupNameId; + const AZStd::string& groupName = groupDefinition.m_name; + const AZStd::string& groupDisplayName = !groupDefinition.m_displayName.empty() ? groupDefinition.m_displayName : groupName; const AZStd::string& groupDescription = !groupDefinition.m_description.empty() ? groupDefinition.m_description : groupDisplayName; - auto& group = m_groups[groupNameId]; + auto& group = m_groups[groupName]; const auto& propertyLayout = materialTypeSourceData->m_propertyLayout; - const auto& propertyListItr = propertyLayout.m_properties.find(groupNameId); + const auto& propertyListItr = propertyLayout.m_properties.find(groupName); if (propertyListItr != propertyLayout.m_properties.end()) { group.m_properties.reserve(propertyListItr->second.size()); @@ -182,21 +182,21 @@ namespace MaterialEditor AtomToolsFramework::DynamicProperty property; AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult( property, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetProperty, - AZ::RPI::MaterialPropertyId(groupNameId, propertyDefinition.m_nameId).GetFullName()); + AZ::RPI::MaterialPropertyId(groupName, propertyDefinition.m_name).GetFullName()); group.m_properties.push_back(property); } } // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupNameId), + &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupName), [this](const auto source, const auto target) { return CompareInstanceNodeProperties(source, target); }); - AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget); + AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget); bool isGroupVisible = false; AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult( - isGroupVisible, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsPropertyGroupVisible, AZ::Name{groupNameId}); - SetGroupVisible(groupNameId, isGroupVisible); + isGroupVisible, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsPropertyGroupVisible, AZ::Name{groupName}); + SetGroupVisible(groupName, isGroupVisible); } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h index 845a8cb0f7..dedcd79f5e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h @@ -37,12 +37,12 @@ namespace MaterialEditor void Reset() override; protected: - bool ShouldGroupAutoExpanded(const AZStd::string& groupNameId) const override; - void OnGroupExpanded(const AZStd::string& groupNameId) override; - void OnGroupCollapsed(const AZStd::string& groupNameId) override; + bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const override; + void OnGroupExpanded(const AZStd::string& groupName) override; + void OnGroupCollapsed(const AZStd::string& groupName) override; private: - AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupNameId) const; + AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupName) const; bool CompareInstanceNodeProperties( const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target) const; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/SettingsDialog/SettingsWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/SettingsDialog/SettingsWidget.cpp index c7d4b195a3..2a061a2ed6 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/SettingsDialog/SettingsWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/SettingsDialog/SettingsWidget.cpp @@ -35,26 +35,26 @@ namespace MaterialEditor void SettingsWidget::AddDocumentSettingsGroup() { - const AZStd::string groupNameId = "documentSettings"; + const AZStd::string groupName = "documentSettings"; const AZStd::string groupDisplayName = "Document Settings"; const AZStd::string groupDescription = "Document Settings"; const AZ::Crc32 saveStateKey(AZStd::string::format("SettingsWidget::DocumentSettingsGroup")); AddGroup( - groupNameId, groupDisplayName, groupDescription, + groupName, groupDisplayName, groupDescription, new AtomToolsFramework::InspectorPropertyGroupWidget( m_documentSettings.get(), nullptr, m_documentSettings->TYPEINFO_Uuid(), this, this, saveStateKey)); } void SettingsWidget::AddDocumentSystemSettingsGroup() { - const AZStd::string groupNameId = "documentSystemSettings"; + const AZStd::string groupName = "documentSystemSettings"; const AZStd::string groupDisplayName = "Document System Settings"; const AZStd::string groupDescription = "Document System Settings"; const AZ::Crc32 saveStateKey(AZStd::string::format("SettingsWidget::DocumentSystemSettingsGroup")); AddGroup( - groupNameId, groupDisplayName, groupDescription, + groupName, groupDisplayName, groupDescription, new AtomToolsFramework::InspectorPropertyGroupWidget( m_documentSystemSettings.get(), nullptr, m_documentSystemSettings->TYPEINFO_Uuid(), this, this, saveStateKey)); } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp index d4269c2438..a00d3eec05 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -53,19 +54,19 @@ namespace MaterialEditor void ViewportSettingsInspector::AddGeneralGroup() { - const AZStd::string groupNameId = "generalSettings"; + const AZStd::string groupName = "generalSettings"; const AZStd::string groupDisplayName = "General Settings"; const AZStd::string groupDescription = "General Settings"; AddGroup( - groupNameId, groupDisplayName, groupDescription, + groupName, groupDisplayName, groupDescription, new AtomToolsFramework::InspectorPropertyGroupWidget( - m_viewportSettings.get(), nullptr, m_viewportSettings->TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupNameId))); + m_viewportSettings.get(), nullptr, m_viewportSettings->TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupName))); } void ViewportSettingsInspector::AddModelGroup() { - const AZStd::string groupNameId = "modelSettings"; + const AZStd::string groupName = "modelSettings"; const AZStd::string groupDisplayName = "Model Settings"; const AZStd::string groupDescription = "Model Settings"; @@ -93,12 +94,12 @@ namespace MaterialEditor if (m_modelPreset) { auto inspectorWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - m_modelPreset.get(), nullptr, m_modelPreset.get()->TYPEINFO_Uuid(), this, groupWidget, GetGroupSaveStateKey(groupNameId)); + m_modelPreset.get(), nullptr, m_modelPreset.get()->TYPEINFO_Uuid(), this, groupWidget, GetGroupSaveStateKey(groupName)); groupWidget->layout()->addWidget(inspectorWidget); } - AddGroup(groupNameId, groupDisplayName, groupDescription, groupWidget); + AddGroup(groupName, groupDisplayName, groupDescription, groupWidget); } void ViewportSettingsInspector::AddModelPreset() @@ -152,7 +153,7 @@ namespace MaterialEditor void ViewportSettingsInspector::AddLightingGroup() { - const AZStd::string groupNameId = "lightingSettings"; + const AZStd::string groupName = "lightingSettings"; const AZStd::string groupDisplayName = "Lighting Settings"; const AZStd::string groupDescription = "Lighting Settings"; @@ -181,12 +182,12 @@ namespace MaterialEditor { auto inspectorWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( m_lightingPreset.get(), nullptr, m_lightingPreset.get()->TYPEINFO_Uuid(), this, groupWidget, - GetGroupSaveStateKey(groupNameId)); + GetGroupSaveStateKey(groupName)); groupWidget->layout()->addWidget(inspectorWidget); } - AddGroup(groupNameId, groupDisplayName, groupDescription, groupWidget); + AddGroup(groupName, groupDisplayName, groupDescription, groupWidget); } void ViewportSettingsInspector::AddLightingPreset() @@ -354,25 +355,25 @@ namespace MaterialEditor return savePath; } - AZ::Crc32 ViewportSettingsInspector::GetGroupSaveStateKey(const AZStd::string& groupNameId) const + AZ::Crc32 ViewportSettingsInspector::GetGroupSaveStateKey(const AZStd::string& groupName) const { - return AZ::Crc32(AZStd::string::format("ViewportSettingsInspector::PropertyGroup::%s", groupNameId.c_str())); + return AZ::Crc32(AZStd::string::format("ViewportSettingsInspector::PropertyGroup::%s", groupName.c_str())); } - bool ViewportSettingsInspector::ShouldGroupAutoExpanded(const AZStd::string& groupNameId) const + bool ViewportSettingsInspector::ShouldGroupAutoExpanded(const AZStd::string& groupName) const { - auto stateItr = m_windowSettings->m_inspectorCollapsedGroups.find(GetGroupSaveStateKey(groupNameId)); + auto stateItr = m_windowSettings->m_inspectorCollapsedGroups.find(GetGroupSaveStateKey(groupName)); return stateItr == m_windowSettings->m_inspectorCollapsedGroups.end(); } - void ViewportSettingsInspector::OnGroupExpanded(const AZStd::string& groupNameId) + void ViewportSettingsInspector::OnGroupExpanded(const AZStd::string& groupName) { - m_windowSettings->m_inspectorCollapsedGroups.erase(GetGroupSaveStateKey(groupNameId)); + m_windowSettings->m_inspectorCollapsedGroups.erase(GetGroupSaveStateKey(groupName)); } - void ViewportSettingsInspector::OnGroupCollapsed(const AZStd::string& groupNameId) + void ViewportSettingsInspector::OnGroupCollapsed(const AZStd::string& groupName) { - m_windowSettings->m_inspectorCollapsedGroups.insert(GetGroupSaveStateKey(groupNameId)); + m_windowSettings->m_inspectorCollapsedGroups.insert(GetGroupSaveStateKey(groupName)); } } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.h index d1cd7a7cf3..6299ddb1f2 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.h @@ -75,10 +75,10 @@ namespace MaterialEditor AZStd::string GetDefaultUniqueSaveFilePath(const AZStd::string& baseName) const; - AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupNameId) const; - bool ShouldGroupAutoExpanded(const AZStd::string& groupNameId) const override; - void OnGroupExpanded(const AZStd::string& groupNameId) override; - void OnGroupCollapsed(const AZStd::string& groupNameId) override; + AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupName) const; + bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const override; + void OnGroupExpanded(const AZStd::string& groupName) override; + void OnGroupCollapsed(const AZStd::string& groupName) override; AZ::Render::ModelPresetPtr m_modelPreset; AZ::Render::LightingPresetPtr m_lightingPreset; diff --git a/Gems/Atom/Utils/Code/CMakeLists.txt b/Gems/Atom/Utils/Code/CMakeLists.txt index 63a2e029f7..89bc8dd0a5 100644 --- a/Gems/Atom/Utils/Code/CMakeLists.txt +++ b/Gems/Atom/Utils/Code/CMakeLists.txt @@ -24,6 +24,7 @@ ly_add_target( Gem::Atom_RHI.Public PUBLIC Gem::Atom_RHI.Reflect + 3rdParty::libpng ) ################################################################################ @@ -43,6 +44,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) BUILD_DEPENDENCIES PRIVATE AZ::AzTest + AZ::AzFramework Gem::Atom_Utils.Static ) ly_add_googletest( diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h index 2d05a68771..d1027daa36 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h @@ -7,6 +7,7 @@ */ #pragma once +#include #include #include #include diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/PngFile.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/PngFile.h new file mode 100644 index 0000000000..1de27e9b96 --- /dev/null +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/PngFile.h @@ -0,0 +1,98 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include + +namespace AZ +{ + namespace Utils + { + //! This is a light wrapper class for libpng, to load and save .png files. + //! Functionality is limited, feel free to add more features as needed. + class PngFile + { + public: + using ErrorHandler = AZStd::function; + + struct LoadSettings + { + ErrorHandler m_errorHandler{}; //!< optional callback function describing any errors that are encountered + bool m_stripAlpha = false; //!< the alpha channel will be skipped, loading an RGBA image as RGB + LoadSettings() {}; // clang errors out if this is not provided. + }; + + struct SaveSettings + { + ErrorHandler m_errorHandler{}; //!< optional callback function describing any errors that are encountered + bool m_stripAlpha = false; //!< the alpha channel will be skipped, saving an RGBA buffer as RGB + int m_compressionLevel = 6; //!< this is the zlib compression level. See png_set_compression_level in png.h + SaveSettings() {}; // clang errors out if this is not provided. + }; + + // To keep things simple for now we limit all images to RGB and RGBA, 8 bits per channel. + enum class Format + { + Unknown, + RGB, + RGBA + }; + + //! @return the loaded PngFile or an invalid PngFile if there was an error. + static PngFile Load(const char* path, LoadSettings loadSettings = {}); + + //! Create a PngFile from an RHI data buffer. + //! @param size the dimensions of the image (m_depth is not used, assumed to be 1) + //! @param format indicates the pixel format represented by @data. Only a limited set of formats are supported, see implementation. + //! @param data the buffer of image data. The size of the buffer must match the @size and @format parameters. + //! @param errorHandler optional callback function describing any errors that are encountered + //! @return the created PngFile or an invalid PngFile if there was an error. + static PngFile Create(const RHI::Size& size, RHI::Format format, AZStd::array_view data, ErrorHandler errorHandler = {}); + static PngFile Create(const RHI::Size& size, RHI::Format format, AZStd::vector&& data, ErrorHandler errorHandler = {}); + + PngFile() = default; + AZ_DEFAULT_MOVE(PngFile) + + //! @return true if the save operation was successful + bool Save(const char* path, SaveSettings saveSettings = {}); + + bool IsValid() const; + operator bool() const { return IsValid(); } + + uint32_t GetWidth() const { return m_width; } + uint32_t GetHeight() const { return m_height; } + + Format GetBufferFormat() const { return m_bufferFormat; } + const AZStd::vector& GetBuffer() const { return m_buffer; } + + //! Returns a r-value reference that can be moved. This will invalidate the PngFile. + AZStd::vector&& TakeBuffer(); + + private: + AZ_DEFAULT_COPY(PngFile) + + static const int HeaderSize = 8; + + static void DefaultErrorHandler(const char* message); + + uint32_t m_width = 0; + uint32_t m_height = 0; + int32_t m_bitDepth = 0; + + Format m_bufferFormat = Format::Unknown; + AZStd::vector m_buffer; + }; + } +} // namespace AZ diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/Utils.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/Utils.h index 73a61b2345..25b96319c5 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/Utils.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/Utils.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include diff --git a/Gems/Atom/Utils/Code/Source/PngFile.cpp b/Gems/Atom/Utils/Code/Source/PngFile.cpp new file mode 100644 index 0000000000..09a5d950e5 --- /dev/null +++ b/Gems/Atom/Utils/Code/Source/PngFile.cpp @@ -0,0 +1,324 @@ +/* + * 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 +#include + +namespace AZ +{ + namespace Utils + { + namespace + { + void PngImage_user_error_fn(png_structp png_ptr, png_const_charp error_msg) + { + PngFile::ErrorHandler* errorHandler = reinterpret_cast(png_get_error_ptr(png_ptr)); + (*errorHandler)(error_msg); + } + + void PngImage_user_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg) + { + AZ_Warning("PngFile", false, "%s", warning_msg); + } + } + + PngFile PngFile::Create(const RHI::Size& size, RHI::Format format, AZStd::array_view data, ErrorHandler errorHandler) + { + return Create(size, format, AZStd::vector{data.begin(), data.end()}, errorHandler); + } + + PngFile PngFile::Create(const RHI::Size& size, RHI::Format format, AZStd::vector&& data, ErrorHandler errorHandler) + { + if (!errorHandler) + { + errorHandler = [](const char* message) { DefaultErrorHandler(message); }; + } + + PngFile image; + + if (RHI::Format::R8G8B8A8_UNORM == format) + { + if (size.m_width * size.m_height * 4 == data.size()) + { + image.m_width = size.m_width; + image.m_height = size.m_height; + image.m_bitDepth = 8; + image.m_bufferFormat = PngFile::Format::RGBA; + image.m_buffer = AZStd::move(data); + } + else + { + errorHandler("Invalid arguments. Buffer size does not match the image dimensions."); + } + } + else + { + errorHandler(AZStd::string::format("Cannot create PngFile with unsupported format %s", AZ::RHI::ToString(format)).c_str()); + } + + return image; + } + + PngFile PngFile::Load(const char* path, LoadSettings loadSettings) + { + if (!loadSettings.m_errorHandler) + { + loadSettings.m_errorHandler = [path](const char* message) { DefaultErrorHandler(AZStd::string::format("Could not load file '%s'. %s", path, message).c_str()); }; + } + + // For documentation of this code, see http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf chapter 3 + + FILE* fp = NULL; + azfopen(&fp, path, "rb"); // return type differs across platforms so can't do inside if + if (!fp) + { + loadSettings.m_errorHandler("Cannot open file."); + return {}; + } + + png_byte header[HeaderSize] = {}; + + if (fread(header, 1, HeaderSize, fp) != HeaderSize) + { + fclose(fp); + loadSettings.m_errorHandler("Invalid png header."); + return {}; + } + + bool isPng = !png_sig_cmp(header, 0, HeaderSize); + if (!isPng) + { + fclose(fp); + loadSettings.m_errorHandler("Invalid png header."); + return {}; + } + + png_voidp user_error_ptr = &loadSettings.m_errorHandler; + png_error_ptr user_error_fn = PngImage_user_error_fn; + png_error_ptr user_warning_fn = PngImage_user_warning_fn; + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, user_error_fn, user_warning_fn); + if (!png_ptr) + { + fclose(fp); + loadSettings.m_errorHandler("png_create_read_struct failed."); + return {}; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + fclose(fp); + loadSettings.m_errorHandler("png_create_info_struct failed."); + return {}; + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fp); + loadSettings.m_errorHandler("png_create_info_struct failed."); + return {}; + } + +AZ_PUSH_DISABLE_WARNING(4611, "-Wunknown-warning-option") // Disables "interaction between '_setjmp' and C++ object destruction is non-portable". See https://docs.microsoft.com/en-us/cpp/preprocessor/warning?view=msvc-160 + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + // We don't report an error message here because the user_error_fn should have done that already. + return {}; + } +AZ_POP_DISABLE_WARNING + + png_init_io(png_ptr, fp); + + png_set_sig_bytes(png_ptr, HeaderSize); + + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, NULL, 0); + + // To keep things simple for now we limit all images to RGB and RGBA, 8 bits per channel + int png_transforms = PNG_TRANSFORM_PACKING | // Expand 1, 2 and 4-bit samples to bytes + PNG_TRANSFORM_STRIP_16 | // Reduce 16 bit samples to 8 bits + PNG_TRANSFORM_GRAY_TO_RGB; + + if (loadSettings.m_stripAlpha) + { + png_transforms |= PNG_TRANSFORM_STRIP_ALPHA; + } + + png_read_png(png_ptr, info_ptr, png_transforms, NULL); + + // Note that libpng will allocate row_pointers for us. If we want to manage the memory ourselves, we need to call png_set_rows. + // In that case we would have to use the low level interface: png_read_info, png_read_image, and png_read_end. + png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr); + + PngFile pngFile; + + int colorType = 0; + + png_get_IHDR(png_ptr, info_ptr, &pngFile.m_width, &pngFile.m_height, &pngFile.m_bitDepth, &colorType, NULL, NULL, NULL); + + uint32_t bytesPerPixel = 0; + + switch (colorType) + { + case PNG_COLOR_TYPE_RGB: + pngFile.m_bufferFormat = PngFile::Format::RGB; + bytesPerPixel = 3; + break; + case PNG_COLOR_TYPE_RGBA: + pngFile.m_bufferFormat = PngFile::Format::RGBA; + bytesPerPixel = 4; + break; + case PNG_COLOR_TYPE_PALETTE: + // Handles cases where the image uses 1, 2, or 4 bit samples. + // Note bytesPerPixel is 3 because we use PNG_TRANSFORM_PACKING + pngFile.m_bufferFormat = PngFile::Format::RGB; + bytesPerPixel = 3; + break; + default: + AZ_Assert(false, "The png transforms should have ensured a pixel format of RGB or RGBA, 8 bits per channel"); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fp); + loadSettings.m_errorHandler("Unsupported pixel format."); + return {}; + } + + // In the future we could use the low-level interface to avoid copying the image (and provide progress callbacks) + pngFile.m_buffer.set_capacity(pngFile.m_width * pngFile.m_height * bytesPerPixel); + for (uint32_t rowIndex = 0; rowIndex < pngFile.m_height; ++rowIndex) + { + png_bytep row = row_pointers[rowIndex]; + pngFile.m_buffer.insert(pngFile.m_buffer.end(), row, row + (pngFile.m_width * bytesPerPixel)); + } + + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return pngFile; + } + + bool PngFile::Save(const char* path, SaveSettings saveSettings) + { + if (!saveSettings.m_errorHandler) + { + saveSettings.m_errorHandler = [path](const char* message) { DefaultErrorHandler(AZStd::string::format("Could not save file '%s'. %s", path, message).c_str()); }; + } + + if (!IsValid()) + { + saveSettings.m_errorHandler("This PngFile is invalid."); + return false; + } + + // For documentation of this code, see http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf chapter 4 + + FILE* fp = NULL; + azfopen(&fp, path, "wb"); // return type differs across platforms so can't do inside if + if (!fp) + { + saveSettings.m_errorHandler("Cannot open file."); + return false; + } + + png_voidp user_error_ptr = &saveSettings.m_errorHandler; + png_error_ptr user_error_fn = PngImage_user_error_fn; + png_error_ptr user_warning_fn = PngImage_user_warning_fn; + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, user_error_fn, user_warning_fn); + if (!png_ptr) + { + fclose(fp); + saveSettings.m_errorHandler("png_create_write_struct failed."); + return false; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + fclose(fp); + saveSettings.m_errorHandler("png_destroy_write_struct failed."); + return false; + } + +AZ_PUSH_DISABLE_WARNING(4611, "-Wunknown-warning-option") // Disables "interaction between '_setjmp' and C++ object destruction is non-portable". See https://docs.microsoft.com/en-us/cpp/preprocessor/warning?view=msvc-160 + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + // We don't report an error message here because the user_error_fn should have done that already. + return false; + } +AZ_POP_DISABLE_WARNING + + png_init_io(png_ptr, fp); + + int colorType = 0; + if (saveSettings.m_stripAlpha || m_bufferFormat == PngFile::Format::RGB) + { + colorType = PNG_COLOR_TYPE_RGB; + } + else + { + colorType = PNG_COLOR_TYPE_RGBA; + } + + int transforms = PNG_TRANSFORM_IDENTITY; + if (saveSettings.m_stripAlpha && m_bufferFormat == PngFile::Format::RGBA) + { + transforms |= PNG_TRANSFORM_STRIP_FILLER_AFTER; + } + + png_set_IHDR(png_ptr, info_ptr, m_width, m_height, m_bitDepth, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + png_set_compression_level(png_ptr, saveSettings.m_compressionLevel); + + const uint32_t bytesPerPixel = (m_bufferFormat == PngFile::Format::RGB) ? 3 : 4; + + AZStd::vector rows; + rows.reserve(m_height); + for (uint32_t i = 0; i < m_height; ++i) + { + rows.push_back(m_buffer.begin() + m_width * bytesPerPixel * i); + } + + png_set_rows(png_ptr, info_ptr, rows.begin()); + + png_write_png(png_ptr, info_ptr, transforms, NULL); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + + return true; + } + + void PngFile::DefaultErrorHandler(const char* message) + { + AZ_Error("PngFile", false, "%s", message); + } + + bool PngFile::IsValid() const + { + return + !m_buffer.empty() && + m_width > 0 && + m_height > 0 && + m_bitDepth > 0; + } + + AZStd::vector&& PngFile::TakeBuffer() + { + return AZStd::move(m_buffer); + } + + } // namespace Utils +}// namespace AZ diff --git a/Gems/Atom/Utils/Code/Tests/PngFileTests.cpp b/Gems/Atom/Utils/Code/Tests/PngFileTests.cpp new file mode 100644 index 0000000000..1c5b844204 --- /dev/null +++ b/Gems/Atom/Utils/Code/Tests/PngFileTests.cpp @@ -0,0 +1,313 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + using namespace AZ::Utils; + + class PngFileTests + : public AllocatorsFixture + { + protected: + AZ::IO::Path m_testImageFolder; + AZ::IO::Path m_tempPngFilePath; + AZStd::vector m_primaryColors3x1; + AZStd::unique_ptr m_localFileIO; + + void SetUp() override + { + AllocatorsFixture::SetUp(); + + m_testImageFolder = AZ::IO::Path(AZ::Test::GetEngineRootPath()) / AZ::IO::Path("Gems/Atom/Utils/Code/Tests/PngTestImages", '/'); + + m_tempPngFilePath = m_testImageFolder / "temp.png"; + + m_localFileIO.reset(aznew AZ::IO::LocalFileIO()); + AZ::IO::FileIOBase::SetInstance(m_localFileIO.get()); + + AZ::IO::FileIOBase::GetInstance()->Remove(m_tempPngFilePath.c_str()); + + m_primaryColors3x1 = { + 255u, 0u, 0u, 255u, + 0u, 255u, 0u, 255u, + 0u, 0u, 255u, 255u + }; + } + + void TearDown() override + { + m_testImageFolder = AZ::IO::Path{}; + m_tempPngFilePath = AZ::IO::Path{}; + m_primaryColors3x1 = AZStd::vector{}; + + AZ::IO::FileIOBase::SetInstance(nullptr); + m_localFileIO.reset(); + + AllocatorsFixture::TearDown(); + } + + struct Color3 : public AZStd::array + { + using Base = AZStd::array; + Color3(uint8_t r, uint8_t g, uint8_t b) : Base({r, g, b}) {} + Color3(const uint8_t* raw) : Base({raw[0], raw[1], raw[2]}) {} + }; + + struct Color4 : public AZStd::array + { + using Base = AZStd::array; + Color4(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : Base({r, g, b, a}) {} + Color4(const uint8_t* raw) : Base({raw[0], raw[1], raw[2], raw[3]}) {} + }; + }; + + TEST_F(PngFileTests, LoadRgb) + { + PngFile image = PngFile::Load((m_testImageFolder / "ColorChart_rgb.png").c_str()); + EXPECT_TRUE(image.IsValid()); + EXPECT_EQ(image.GetBufferFormat(), PngFile::Format::RGB); + EXPECT_EQ(image.GetWidth(), 3); + EXPECT_EQ(image.GetHeight(), 2); + EXPECT_EQ(image.GetBuffer().size(), 18); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 0), Color3(255u, 0u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 3), Color3(0u, 255u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 6), Color3(0u, 0u, 255u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 9), Color3(255u, 255u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 12), Color3(0u, 255u, 255u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 15), Color3(255u, 0u, 255u)); + } + + TEST_F(PngFileTests, LoadRgba) + { + PngFile image = PngFile::Load((m_testImageFolder / "ColorChart_rgba.png").c_str()); + EXPECT_TRUE(image.IsValid()); + EXPECT_EQ(image.GetBufferFormat(), PngFile::Format::RGBA); + EXPECT_EQ(image.GetWidth(), 3); + EXPECT_EQ(image.GetHeight(), 2); + EXPECT_EQ(image.GetBuffer().size(), 24); + EXPECT_EQ(Color4(image.GetBuffer().begin() + 0), Color4(255u, 0u, 0u, 200u)); + EXPECT_EQ(Color4(image.GetBuffer().begin() + 4), Color4(0u, 255u, 0u, 150u)); + EXPECT_EQ(Color4(image.GetBuffer().begin() + 8), Color4(0u, 0u, 255u, 100u)); + EXPECT_EQ(Color4(image.GetBuffer().begin() + 12), Color4(255u, 255u, 0u, 125u)); + EXPECT_EQ(Color4(image.GetBuffer().begin() + 16), Color4(0u, 255u, 255u, 175u)); + EXPECT_EQ(Color4(image.GetBuffer().begin() + 20), Color4(255u, 0u, 255u, 75u)); + } + + TEST_F(PngFileTests, LoadRgbaStripAlpha) + { + PngFile::LoadSettings loadSettings; + loadSettings.m_stripAlpha = true; + + PngFile image = PngFile::Load((m_testImageFolder / "ColorChart_rgba.png").c_str(), loadSettings); + // Note these checks are identical to the LoadRgb test. + EXPECT_TRUE(image.IsValid()); + EXPECT_EQ(image.GetBufferFormat(), PngFile::Format::RGB); + EXPECT_EQ(image.GetWidth(), 3); + EXPECT_EQ(image.GetHeight(), 2); + EXPECT_EQ(image.GetBuffer().size(), 18); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 0), Color3(255u, 0u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 3), Color3(0u, 255u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 6), Color3(0u, 0u, 255u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 9), Color3(255u, 255u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 12), Color3(0u, 255u, 255u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 15), Color3(255u, 0u, 255u)); + } + + TEST_F(PngFileTests, LoadColorPaletteTwoBits) + { + PngFile image = PngFile::Load((m_testImageFolder / "ColorPalette_2bit.png").c_str()); + EXPECT_TRUE(image.IsValid()); + EXPECT_EQ(image.GetBufferFormat(), PngFile::Format::RGB); + EXPECT_EQ(image.GetWidth(), 1); + EXPECT_EQ(image.GetHeight(), 3); + EXPECT_EQ(image.GetBuffer().size(), 9); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 0), Color3(255u, 0u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 3), Color3(0u, 255u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 6), Color3(0u, 0u, 255u)); + } + + TEST_F(PngFileTests, LoadGrayscaleOneBit) + { + PngFile image = PngFile::Load((m_testImageFolder / "GrayPalette_1bit.png").c_str()); + EXPECT_TRUE(image.IsValid()); + EXPECT_EQ(image.GetBufferFormat(), PngFile::Format::RGB); + EXPECT_EQ(image.GetWidth(), 1); + EXPECT_EQ(image.GetHeight(), 2); + EXPECT_EQ(image.GetBuffer().size(), 6); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 0), Color3(0u, 0u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 3), Color3(255u, 255u, 255u)); + } + + TEST_F(PngFileTests, LoadRgba64Bits) + { + PngFile image = PngFile::Load((m_testImageFolder / "Gradient_rgb_16bpc.png").c_str()); + EXPECT_TRUE(image.IsValid()); + EXPECT_EQ(image.GetBufferFormat(), PngFile::Format::RGB); + EXPECT_EQ(image.GetWidth(), 5); + EXPECT_EQ(image.GetHeight(), 1); + EXPECT_EQ(image.GetBuffer().size(), 15); + // The values in this file are 30.0f, 30.1f, 30.2f, 30.3f, 30.4f. But we use PNG_TRANSFORM_STRIP_16 to reduce them to 8 bits per channel for simplicity. + EXPECT_EQ(Color3(image.GetBuffer().begin() + 0), Color3(76u, 0u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 3), Color3(77u, 0u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 6), Color3(77u, 0u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 9), Color3(77u, 0u, 0u)); + EXPECT_EQ(Color3(image.GetBuffer().begin() + 12), Color3(77u, 0u, 0u)); + } + + TEST_F(PngFileTests, CreateCopy) + { + AZStd::vector data = m_primaryColors3x1; + + PngFile savedImage = PngFile::Create(AZ::RHI::Size{3, 1, 0}, AZ::RHI::Format::R8G8B8A8_UNORM, data); + EXPECT_TRUE(savedImage.IsValid()); + EXPECT_EQ(savedImage.GetWidth(), 3); + EXPECT_EQ(savedImage.GetHeight(), 1); + EXPECT_EQ(savedImage.GetBuffer(), data); + } + + TEST_F(PngFileTests, CreateMove) + { + AZStd::vector data = m_primaryColors3x1; + + PngFile savedImage = PngFile::Create(AZ::RHI::Size{3, 1, 0}, AZ::RHI::Format::R8G8B8A8_UNORM, AZStd::move(data)); + EXPECT_TRUE(savedImage.IsValid()); + EXPECT_EQ(savedImage.GetWidth(), 3); + EXPECT_EQ(savedImage.GetHeight(), 1); + EXPECT_EQ(savedImage.GetBuffer(), m_primaryColors3x1); + EXPECT_TRUE(data.empty()); // The data should have been moved + } + + TEST_F(PngFileTests, SaveRgba) + { + PngFile savedImage = PngFile::Create(AZ::RHI::Size{3, 1, 0}, AZ::RHI::Format::R8G8B8A8_UNORM, m_primaryColors3x1); + bool result = savedImage.Save(m_tempPngFilePath.c_str()); + EXPECT_TRUE(result); + + PngFile loadedImage = PngFile::Load(m_tempPngFilePath.c_str()); + EXPECT_TRUE(loadedImage.IsValid()); + EXPECT_EQ(loadedImage.GetBufferFormat(), savedImage.GetBufferFormat()); + EXPECT_EQ(loadedImage.GetWidth(), savedImage.GetWidth()); + EXPECT_EQ(loadedImage.GetHeight(), savedImage.GetHeight()); + EXPECT_EQ(loadedImage.GetBuffer(), savedImage.GetBuffer()); + } + + TEST_F(PngFileTests, SaveRgbaStripAlpha) + { + PngFile savedImage = PngFile::Create(AZ::RHI::Size{3, 1, 0}, AZ::RHI::Format::R8G8B8A8_UNORM, m_primaryColors3x1); + + PngFile::SaveSettings saveSettings; + saveSettings.m_stripAlpha = true; + + bool result = savedImage.Save(m_tempPngFilePath.c_str(), saveSettings); + EXPECT_TRUE(result); + + // The alpha was stripped when saving. Now we load the data without stripping anything and should find + // that there is no alpha channel. + + PngFile loadedImage = PngFile::Load(m_tempPngFilePath.c_str()); + + // The dimensions are the same... + EXPECT_TRUE(loadedImage.IsValid()); + EXPECT_EQ(loadedImage.GetWidth(), savedImage.GetWidth()); + EXPECT_EQ(loadedImage.GetHeight(), savedImage.GetHeight()); + + // ... but the format is different + EXPECT_NE(loadedImage.GetBufferFormat(), savedImage.GetBufferFormat()); + EXPECT_EQ(loadedImage.GetBufferFormat(), PngFile::Format::RGB); + + // ... and the loaded data is smaller + EXPECT_NE(loadedImage.GetBuffer(), savedImage.GetBuffer()); + EXPECT_EQ(Color3(loadedImage.GetBuffer().begin() + 0), Color3(255u, 0u, 0u)); + EXPECT_EQ(Color3(loadedImage.GetBuffer().begin() + 3), Color3(0u, 255u, 0u)); + EXPECT_EQ(Color3(loadedImage.GetBuffer().begin() + 6), Color3(0u, 0u, 255u)); + } + + TEST_F(PngFileTests, Error_CreateUnsupportedFormat) + { + AZStd::vector data = m_primaryColors3x1; + + AZStd::string gotErrorMessage; + + PngFile savedImage = PngFile::Create(AZ::RHI::Size{3, 1, 0}, AZ::RHI::Format::R32_UINT, data, + [&gotErrorMessage](const char* errorMessage) { gotErrorMessage = errorMessage; }); + + EXPECT_FALSE(savedImage.IsValid()); + EXPECT_TRUE(gotErrorMessage.find("unsupported format R32_UINT") != AZStd::string::npos); + } + + TEST_F(PngFileTests, Error_CreateIncorrectBufferSize) + { + AZStd::vector data = m_primaryColors3x1; + + AZStd::string gotErrorMessage; + + PngFile savedImage = PngFile::Create(AZ::RHI::Size{3, 2, 0}, AZ::RHI::Format::R8G8B8A8_UNORM, data, + [&gotErrorMessage](const char* errorMessage) { gotErrorMessage = errorMessage; }); + + EXPECT_FALSE(savedImage.IsValid()); + EXPECT_TRUE(gotErrorMessage.find("does not match") != AZStd::string::npos); + } + + TEST_F(PngFileTests, Error_LoadFileNotFound) + { + AZStd::string gotErrorMessage; + + PngFile::LoadSettings loadSettings; + loadSettings.m_errorHandler = [&gotErrorMessage](const char* errorMessage) { gotErrorMessage = errorMessage; }; + + PngFile image = PngFile::Load((m_testImageFolder / "DoesNotExist.png").c_str(), loadSettings); + EXPECT_FALSE(image.IsValid()); + EXPECT_TRUE(gotErrorMessage.find("not open file") != AZStd::string::npos); + } + + TEST_F(PngFileTests, Error_LoadEmptyFile) + { + AZStd::string gotErrorMessage; + + PngFile::LoadSettings loadSettings; + loadSettings.m_errorHandler = [&gotErrorMessage](const char* errorMessage) { gotErrorMessage = errorMessage; }; + + PngFile image = PngFile::Load((m_testImageFolder / "EmptyFile.png").c_str(), loadSettings); + EXPECT_FALSE(image.IsValid()); + EXPECT_TRUE(gotErrorMessage.find("Invalid png header") != AZStd::string::npos); + } + + TEST_F(PngFileTests, Error_LoadNotPngFile) + { + AZStd::string gotErrorMessage; + + PngFile::LoadSettings loadSettings; + loadSettings.m_errorHandler = [&gotErrorMessage](const char* errorMessage) { gotErrorMessage = errorMessage; }; + + PngFile image = PngFile::Load((m_testImageFolder / "ColorChart_rgba.jpg").c_str(), loadSettings); + EXPECT_FALSE(image.IsValid()); + EXPECT_TRUE(gotErrorMessage.find("Invalid png header") != AZStd::string::npos); + } + + TEST_F(PngFileTests, Error_SaveInvalidPngFile) + { + AZStd::string gotErrorMessage; + + PngFile::SaveSettings saveSettings; + saveSettings.m_errorHandler = [&gotErrorMessage](const char* errorMessage) { gotErrorMessage = errorMessage; }; + + PngFile savedImage; + bool result = savedImage.Save(m_tempPngFilePath.c_str(), saveSettings); + EXPECT_FALSE(result); + EXPECT_TRUE(gotErrorMessage.find("PngFile is invalid") != AZStd::string::npos); + EXPECT_FALSE(AZ::IO::FileIOBase::GetInstance()->Exists(m_tempPngFilePath.c_str())); + } +} diff --git a/Gems/Atom/Utils/Code/Tests/PngTestImages/.gitignore b/Gems/Atom/Utils/Code/Tests/PngTestImages/.gitignore new file mode 100644 index 0000000000..ea08b96982 --- /dev/null +++ b/Gems/Atom/Utils/Code/Tests/PngTestImages/.gitignore @@ -0,0 +1 @@ +temp.png \ No newline at end of file diff --git a/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgb.png b/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgb.png new file mode 100644 index 0000000000..4dc14cc8eb --- /dev/null +++ b/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af0d0d079354495ff96aa266ecb4092d679e6c5a952e8b2d4ee743e538d54809 +size 126 diff --git a/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgba.jpg b/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgba.jpg new file mode 100644 index 0000000000..ea6f2283bd --- /dev/null +++ b/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgba.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01cad8dd75c9a26169e858960817424362c16cbf2a8df8bc55857f6172ce2526 +size 834 diff --git a/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgba.png b/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgba.png new file mode 100644 index 0000000000..943208e2cf --- /dev/null +++ b/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorChart_rgba.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ddb5f70eaf72fe09add6cc7cdf7cbba2d327ab312bb9bde73d7f456f80b8d6e +size 141 diff --git a/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorPalette_2bit.png b/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorPalette_2bit.png new file mode 100644 index 0000000000..38bdbc9755 --- /dev/null +++ b/Gems/Atom/Utils/Code/Tests/PngTestImages/ColorPalette_2bit.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b50e93bbbd320d69752df2c25b4118c0198226a0d230781a6ed93f89b7eea053 +size 142 diff --git a/Gems/Atom/Utils/Code/Tests/PngTestImages/EmptyFile.png b/Gems/Atom/Utils/Code/Tests/PngTestImages/EmptyFile.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/Atom/Utils/Code/Tests/PngTestImages/Gradient_rgb_16bpc.png b/Gems/Atom/Utils/Code/Tests/PngTestImages/Gradient_rgb_16bpc.png new file mode 100644 index 0000000000..c28c2baf36 --- /dev/null +++ b/Gems/Atom/Utils/Code/Tests/PngTestImages/Gradient_rgb_16bpc.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb56e8bf15a4727bcebba579cb97a36e1f26e4a4870b2b4a38bdbd4b4bd076fd +size 127 diff --git a/Gems/Atom/Utils/Code/Tests/PngTestImages/GrayPalette_1bit.png b/Gems/Atom/Utils/Code/Tests/PngTestImages/GrayPalette_1bit.png new file mode 100644 index 0000000000..752e100691 --- /dev/null +++ b/Gems/Atom/Utils/Code/Tests/PngTestImages/GrayPalette_1bit.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:710fd5c80af49a42249a471ded6c9119be79d6748218073bb8297ffb4b4e62d1 +size 137 diff --git a/Gems/Atom/Utils/Code/atom_utils_files.cmake b/Gems/Atom/Utils/Code/atom_utils_files.cmake index b1f8170317..dd4654b738 100644 --- a/Gems/Atom/Utils/Code/atom_utils_files.cmake +++ b/Gems/Atom/Utils/Code/atom_utils_files.cmake @@ -23,6 +23,7 @@ set(FILES Include/Atom/Utils/ImGuiShaderMetrics.inl Include/Atom/Utils/ImGuiTransientAttachmentProfiler.h Include/Atom/Utils/ImGuiTransientAttachmentProfiler.inl + Include/Atom/Utils/PngFile.h Include/Atom/Utils/PpmFile.h Include/Atom/Utils/StableDynamicArray.h Include/Atom/Utils/StableDynamicArray.inl @@ -31,6 +32,7 @@ set(FILES Include/Atom/Utils/AssetCollectionAsyncLoader.h Source/DdsFile.cpp Source/ImageComparison.cpp + Source/PngFile.cpp Source/PpmFile.cpp Source/Utils.cpp Source/AssetCollectionAsyncLoader.cpp diff --git a/Gems/Atom/Utils/Code/atom_utils_tests_files.cmake b/Gems/Atom/Utils/Code/atom_utils_tests_files.cmake index 9069eeb682..5b43758a60 100644 --- a/Gems/Atom/Utils/Code/atom_utils_tests_files.cmake +++ b/Gems/Atom/Utils/Code/atom_utils_tests_files.cmake @@ -8,5 +8,6 @@ set(FILES Tests/ImageComparisonTests.cpp + Tests/PngFileTests.cpp Tests/StableDynamicArrayTests.cpp ) diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp index c5268b427f..86b3011836 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp @@ -1726,7 +1726,7 @@ void AZ::FFont::DrawScreenAlignedText3d( { return; } - AZ::Vector3 positionNDC = AzFramework::WorldToScreenNDC( + AZ::Vector3 positionNDC = AzFramework::WorldToScreenNdc( params.m_position, currentView->GetWorldToViewMatrix(), currentView->GetViewToClipMatrix() diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp index bf356fa4b5..7d659ebb7a 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp @@ -183,15 +183,6 @@ namespace AZ::Render DrawFramerate(); } - void AtomViewportDisplayInfoSystemComponent::OnFrameEnd() - { - auto currentTime = AZStd::chrono::system_clock::now(); - if (!m_fpsHistory.empty()) - { - m_fpsHistory.back().m_endFrameTime = currentTime; - } - } - AtomBridge::ViewportInfoDisplayState AtomViewportDisplayInfoSystemComponent::GetDisplayState() const { return aznumeric_cast(r_displayInfo.operator int()); @@ -257,11 +248,11 @@ namespace AZ::Render void AtomViewportDisplayInfoSystemComponent::UpdateFramerate() { auto currentTime = AZStd::chrono::system_clock::now(); - while (!m_fpsHistory.empty() && (currentTime - m_fpsHistory.front().m_beginFrameTime) > m_fpsInterval) + while (!m_fpsHistory.empty() && (currentTime - m_fpsHistory.front()) > m_fpsInterval) { m_fpsHistory.pop_front(); } - m_fpsHistory.push_back(FrameTimingInfo(currentTime)); + m_fpsHistory.push_back(currentTime); } void AtomViewportDisplayInfoSystemComponent::DrawFramerate() @@ -270,31 +261,25 @@ namespace AZ::Render double minFPS = DBL_MAX; double maxFPS = 0; AZStd::chrono::duration deltaTime; - AZStd::chrono::milliseconds totalFrameMS(0); for (const auto& time : m_fpsHistory) { if (lastTime.has_value()) { - deltaTime = time.m_beginFrameTime - lastTime.value(); + deltaTime = time - lastTime.value(); double fps = AZStd::chrono::seconds(1) / deltaTime; minFPS = AZStd::min(minFPS, fps); maxFPS = AZStd::max(maxFPS, fps); } - lastTime = time.m_beginFrameTime; - - if (time.m_endFrameTime.has_value()) - { - totalFrameMS += time.m_endFrameTime.value() - time.m_beginFrameTime; - } + lastTime = time; } double averageFPS = 0; double averageFrameMs = 0; if (m_fpsHistory.size() > 1) { - deltaTime = m_fpsHistory.back().m_beginFrameTime - m_fpsHistory.front().m_beginFrameTime; - averageFPS = AZStd::chrono::seconds(m_fpsHistory.size() - 1) / deltaTime; - averageFrameMs = aznumeric_cast(totalFrameMS.count()) / (m_fpsHistory.size() - 1); + deltaTime = m_fpsHistory.back() - m_fpsHistory.front(); + averageFPS = AZStd::chrono::seconds(m_fpsHistory.size()) / deltaTime; + averageFrameMs = 1000.0f/averageFPS; } const double frameIntervalSeconds = m_fpsInterval.count(); @@ -303,7 +288,7 @@ namespace AZ::Render AZStd::string::format( "FPS %.1f [%.0f..%.0f], %.1fms/frame, avg over %.1fs", averageFPS, - minFPS == DBL_MAX ? 0.0 : minFPS, + minFPS, maxFPS, averageFrameMs, frameIntervalSeconds), diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.h b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.h index 689cfdb43b..1d53e188d0 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.h @@ -45,7 +45,6 @@ namespace AZ // AZ::RPI::ViewportContextNotificationBus::Handler overrides... void OnRenderTick() override; - void OnFrameEnd() override; // AZ::AtomBridge::AtomViewportInfoDisplayRequestBus::Handler overrides... AtomBridge::ViewportInfoDisplayState GetDisplayState() const override; @@ -62,8 +61,6 @@ namespace AZ void DrawPassInfo(); void DrawFramerate(); - void UpdateScene(AZ::RPI::ScenePtr scene); - static constexpr float BaseFontSize = 0.7f; AZStd::string m_rendererDescription; @@ -71,17 +68,7 @@ namespace AZ AzFramework::FontDrawInterface* m_fontDrawInterface = nullptr; float m_lineSpacing; AZStd::chrono::duration m_fpsInterval = AZStd::chrono::seconds(1); - struct FrameTimingInfo - { - AZStd::chrono::system_clock::time_point m_beginFrameTime; - AZStd::optional m_endFrameTime; - - explicit FrameTimingInfo(AZStd::chrono::system_clock::time_point beginFrameTime) - : m_beginFrameTime(beginFrameTime) - { - } - }; - AZStd::deque m_fpsHistory; + AZStd::deque m_fpsHistory; AZStd::optional m_lastMemoryUpdate; bool m_updateRootPassQuery = true; }; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h index ace16ba6ca..ed7f1564ac 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h @@ -151,7 +151,9 @@ namespace AZ //! Returns the list of all ModelMaterialSlot's for the model, across all LODs. virtual RPI::ModelMaterialSlotMap GetModelMaterialSlots() const = 0; + //! Returns the available, overridable material slots and the default assigned materials virtual MaterialAssignmentMap GetMaterialAssignments() const = 0; + virtual AZStd::unordered_set GetModelUvNames() const = 0; }; using MaterialReceiverRequestBus = EBus; @@ -161,6 +163,7 @@ namespace AZ : public ComponentBus { public: + //! Notification that overridable material slots are available or have changed virtual void OnMaterialAssignmentsChanged() = 0; }; using MaterialReceiverNotificationBus = EBus; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.cpp index ac37e293b9..ee66518779 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.cpp @@ -6,6 +6,7 @@ * */ #include "AttachmentComponent.h" +#include #include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp index 878eb51efb..b10a3e6695 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp index 49b31388ad..5d16164d92 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp index 8aec22e22c..8b864613c4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp index 013a8ee1b4..7f676e4c10 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/ImageBasedLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/ImageBasedLightComponentConfig.cpp index c98d5be5ef..8ddd727076 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/ImageBasedLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/ImageBasedLightComponentConfig.cpp @@ -7,6 +7,7 @@ */ #include +#include #include namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index a3152faf87..8aec91616b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -88,6 +88,12 @@ namespace AZ AZ::Data::AssetId materialAssetId = {}; MaterialComponentRequestBus::EventResult( materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId); + if (!materialAssetId.IsValid()) + { + MaterialComponentRequestBus::EventResult( + materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, + m_materialAssignmentId); + } if (!materialAssetId.IsValid()) { @@ -169,7 +175,7 @@ namespace AZ void MaterialPropertyInspector::AddDetailsGroup() { - const AZStd::string& groupNameId = "Details"; + const AZStd::string& groupName = "Details"; const AZStd::string& groupDisplayName = "Details"; const AZStd::string& groupDescription = ""; @@ -235,15 +241,15 @@ namespace AZ propertyGroupContainer->layout()->addWidget(materialInfoWidget); - AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupContainer); + AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupContainer); } void MaterialPropertyInspector::AddUvNamesGroup() { - const AZStd::string groupNameId = AZ::RPI::UvGroupName; + const AZStd::string groupName = AZ::RPI::UvGroupName; const AZStd::string groupDisplayName = "UV Sets"; const AZStd::string groupDescription = "UV set names in this material, which can be renamed to match those in the model."; - auto& group = m_groups[groupNameId]; + auto& group = m_groups[groupName]; const RPI::MaterialUvNameMap& uvNameMap = m_editData.m_materialAsset->GetMaterialTypeAsset()->GetUvNameMap(); group.m_properties.reserve(uvNameMap.size()); @@ -257,8 +263,8 @@ namespace AZ propertyConfig = {}; propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::String; - propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupNameId, shaderInputStr).GetCStr(); - propertyConfig.m_nameId = shaderInputStr; + propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupName, shaderInputStr).GetCStr(); + propertyConfig.m_name = shaderInputStr; propertyConfig.m_displayName = shaderInputStr; propertyConfig.m_groupName = groupDisplayName; propertyConfig.m_description = shaderInputStr; @@ -271,8 +277,8 @@ namespace AZ // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - &group, nullptr, group.TYPEINFO_Uuid(), this, this, GetSaveStateKeyForGroup(groupNameId)); - AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget); + &group, nullptr, group.TYPEINFO_Uuid(), this, this, GetSaveStateKeyForGroup(groupName)); + AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget); } void MaterialPropertyInspector::Populate() @@ -285,13 +291,13 @@ namespace AZ // Copy all of the properties from the material asset to the source data that will be exported for (const auto& groupDefinition : m_editData.m_materialTypeSourceData.GetGroupDefinitionsInDisplayOrder()) { - const AZStd::string& groupNameId = groupDefinition.m_nameId; - const AZStd::string& groupDisplayName = !groupDefinition.m_displayName.empty() ? groupDefinition.m_displayName : groupNameId; + const AZStd::string& groupName = groupDefinition.m_name; + const AZStd::string& groupDisplayName = !groupDefinition.m_displayName.empty() ? groupDefinition.m_displayName : groupName; const AZStd::string& groupDescription = !groupDefinition.m_description.empty() ? groupDefinition.m_description : groupDisplayName; - auto& group = m_groups[groupNameId]; + auto& group = m_groups[groupName]; const auto& propertyLayout = m_editData.m_materialTypeSourceData.m_propertyLayout; - const auto& propertyListItr = propertyLayout.m_properties.find(groupNameId); + const auto& propertyListItr = propertyLayout.m_properties.find(groupName); if (propertyListItr != propertyLayout.m_properties.end()) { group.m_properties.reserve(propertyListItr->second.size()); @@ -300,7 +306,7 @@ namespace AZ AtomToolsFramework::DynamicPropertyConfig propertyConfig; // Assign id before conversion so it can be used in dynamic description - propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupNameId, propertyDefinition.m_nameId).GetFullName(); + propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupName, propertyDefinition.m_name).GetFullName(); AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, propertyDefinition); @@ -316,8 +322,8 @@ namespace AZ // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - &group, nullptr, group.TYPEINFO_Uuid(), this, this, GetSaveStateKeyForGroup(groupNameId)); - AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget); + &group, nullptr, group.TYPEINFO_Uuid(), this, this, GetSaveStateKeyForGroup(groupName)); + AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget); } AddGroupsEnd(); @@ -496,11 +502,11 @@ namespace AZ } } - AZ::Crc32 MaterialPropertyInspector::GetSaveStateKeyForGroup(const AZStd::string& groupNameId) const + AZ::Crc32 MaterialPropertyInspector::GetSaveStateKeyForGroup(const AZStd::string& groupName) const { return AZ::Crc32(AZStd::string::format( "MaterialPropertyInspector::PropertyGroup::%s::%s", m_editData.m_materialAssetId.ToString().c_str(), - groupNameId.c_str())); + groupName.c_str())); } bool MaterialPropertyInspector::AreNodePropertyValuesEqual( @@ -728,11 +734,16 @@ namespace AZ void MaterialPropertyInspector::UpdateUI() { - AZ::Data::AssetId assetId; + AZ::Data::AssetId materialAssetId = {}; MaterialComponentRequestBus::EventResult( - assetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId); + materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId); + if (!materialAssetId.IsValid()) + { + MaterialComponentRequestBus::EventResult( + materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_materialAssignmentId); + } - if (IsLoaded() && m_editData.m_materialAssetId == assetId) + if (IsLoaded() && m_editData.m_materialAssetId == materialAssetId) { LoadOverridesFromEntity(); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h index 11eb2cec51..d3142ddc95 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h @@ -100,7 +100,7 @@ namespace AZ void RunEditorMaterialFunctors(); void UpdateMaterialInstanceProperty(const AtomToolsFramework::DynamicProperty& property); - AZ::Crc32 GetSaveStateKeyForGroup(const AZStd::string& groupNameId) const; + AZ::Crc32 GetSaveStateKeyForGroup(const AZStd::string& groupName) const; static bool AreNodePropertyValuesEqual( const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index 363221d3fc..f5d38f48c4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -117,10 +118,16 @@ namespace AZ } }; + AZ::Data::AssetId EditorMaterialComponentSlot::GetActiveAssetId() const + { + return m_materialAsset.GetId().IsValid() ? m_materialAsset.GetId() : GetDefaultAssetId(); + } + AZ::Data::AssetId EditorMaterialComponentSlot::GetDefaultAssetId() const { AZ::Data::AssetId assetId; - MaterialComponentRequestBus::EventResult(assetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_id); + MaterialComponentRequestBus::EventResult( + assetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_id); return assetId; } @@ -134,7 +141,7 @@ namespace AZ bool EditorMaterialComponentSlot::HasSourceData() const { // The slot only has valid source data if the source path is valid and the file has the correct extension - const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_materialAsset.GetId()); + const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(GetActiveAssetId()); return !sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension); } @@ -219,7 +226,7 @@ namespace AZ void EditorMaterialComponentSlot::OpenMaterialEditor() const { - const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_materialAsset.GetId()); + const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(GetActiveAssetId()); if (!sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension)) { EditorMaterialSystemComponentRequestBus::Broadcast( @@ -235,7 +242,7 @@ namespace AZ void EditorMaterialComponentSlot::OpenUvNameMapInspector() { - if (m_materialAsset.GetId().IsValid()) + if (GetActiveAssetId().IsValid()) { AZStd::unordered_set modelUvNames; MaterialReceiverRequestBus::EventResult(modelUvNames, m_entityId, &MaterialReceiverRequestBus::Events::GetModelUvNames); @@ -251,7 +258,7 @@ namespace AZ }; if (EditorMaterialComponentInspector::OpenInspectorDialog( - m_materialAsset.GetId(), matModUvOverrides, modelUvNames, applyMatModUvOverrideChangedCallback)) + GetActiveAssetId(), matModUvOverrides, modelUvNames, applyMatModUvOverrideChangedCallback)) { OnDataChanged(); } @@ -273,10 +280,10 @@ namespace AZ action->setEnabled(HasSourceData()); action = menu.addAction("Edit Material Instance...", [this]() { OpenMaterialInspector(); }); - action->setEnabled(m_materialAsset.GetId().IsValid()); + action->setEnabled(GetActiveAssetId().IsValid()); action = menu.addAction("Edit Material Instance UV Map...", [this]() { OpenUvNameMapInspector(); }); - action->setEnabled(m_materialAsset.GetId().IsValid()); + action->setEnabled(GetActiveAssetId().IsValid()); menu.addSeparator(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h index 01e357f1bb..377fe9a18b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h @@ -30,6 +30,7 @@ namespace AZ static void Reflect(ReflectContext* context); static bool ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); + AZ::Data::AssetId GetActiveAssetId() const; AZ::Data::AssetId GetDefaultAssetId() const; AZStd::string GetLabel() const; bool HasSourceData() const; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp index e7059ccdf7..070c42fd7c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp @@ -137,8 +137,8 @@ namespace AZ // Copy all of the properties from the material asset to the source data that will be exported result = true; - editData.m_materialTypeSourceData.EnumerateProperties([&](const AZStd::string& groupNameId, const AZStd::string& propertyNameId, const auto& propertyDefinition) { - const AZ::RPI::MaterialPropertyId propertyId(groupNameId, propertyNameId); + editData.m_materialTypeSourceData.EnumerateProperties([&](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition) { + const AZ::RPI::MaterialPropertyId propertyId(groupName, propertyName); const AZ::RPI::MaterialPropertyIndex propertyIndex = editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId.GetFullName()); @@ -170,7 +170,7 @@ namespace AZ return true; } - exportData.m_properties[groupNameId][propertyDefinition.m_nameId].m_value = propertyValue; + exportData.m_properties[groupName][propertyDefinition.m_name].m_value = propertyValue; return true; }); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialModelUvNameMapInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialModelUvNameMapInspector.cpp index 91b519a06b..64bc54bae8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialModelUvNameMapInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialModelUvNameMapInspector.cpp @@ -81,7 +81,7 @@ namespace AZ { AddGroupsBegin(); - const AZStd::string groupNameId = "ModelUvMap"; + const AZStd::string groupName = "ModelUvMap"; const AZStd::string groupDisplayName = "Material to Model UV Map"; const AZStd::string groupDescription = "Custom map that maps a UV name from the material to one from the model."; @@ -96,8 +96,8 @@ namespace AZ const AZStd::string materialUvName = m_materialUvNames[i].m_uvName.GetStringView(); propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::Enum; - propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupNameId, shaderInput).GetFullName(); - propertyConfig.m_nameId = shaderInput; + propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupName, shaderInput).GetFullName(); + propertyConfig.m_name = shaderInput; propertyConfig.m_displayName = materialUvName; propertyConfig.m_description = shaderInput; propertyConfig.m_defaultValue = 0u; @@ -108,7 +108,7 @@ namespace AZ m_group.m_properties.back().SetValue(AZStd::any(m_modelUvNameIndices[i])); } - AddGroup(groupNameId, groupDisplayName, groupDescription, + AddGroup(groupName, groupDisplayName, groupDescription, new AtomToolsFramework::InspectorPropertyGroupWidget(&m_group, nullptr, m_group.TYPEINFO_Uuid(), this)); AddGroupsEnd(); @@ -238,7 +238,7 @@ namespace AZ ResetModelUvNameIndices(); - const AZStd::string groupNameId = "ModelUvMap"; + const AZStd::string groupName = "ModelUvMap"; size_t uvSize = m_materialUvNames.size(); for (size_t i = 0u; i < uvSize; ++i) @@ -248,8 +248,8 @@ namespace AZ const AZStd::string materialUvName = m_materialUvNames[i].m_uvName.GetStringView(); propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::Enum; - propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupNameId, shaderInput).GetFullName(); - propertyConfig.m_nameId = shaderInput; + propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupName, shaderInput).GetFullName(); + propertyConfig.m_name = shaderInput; propertyConfig.m_displayName = materialUvName; propertyConfig.m_description = shaderInput; propertyConfig.m_defaultValue = 0u; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp index 1d9f1e81dd..4c26042a26 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -110,12 +111,14 @@ namespace AZ m_queuedMaterialUpdateNotification = false; MaterialComponentRequestBus::Handler::BusConnect(m_entityId); + MaterialReceiverNotificationBus::Handler::BusConnect(m_entityId); LoadMaterials(); } void MaterialComponentController::Deactivate() { MaterialComponentRequestBus::Handler::BusDisconnect(); + MaterialReceiverNotificationBus::Handler::BusDisconnect(); TickBus::Handler::BusDisconnect(); ReleaseMaterials(); @@ -142,59 +145,29 @@ namespace AZ { InitializeMaterialInstance(asset); } - + void MaterialComponentController::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { - AZStd::unordered_set propertyOverrides; - AZStd::swap(m_queuedPropertyOverrides, propertyOverrides); + AZStd::unordered_set materialsWithDirtyProperties; + AZStd::swap(m_materialsWithDirtyProperties, materialsWithDirtyProperties); // Iterate through all MaterialAssignmentId's that have property overrides and attempt to apply them // if material instance is already compiling, delay application of property overrides until next frame - for (const auto& materialAssignmentId : propertyOverrides) + for (const auto& materialAssignmentId : materialsWithDirtyProperties) { const auto materialIt = m_configuration.m_materials.find(materialAssignmentId); - if (materialIt == m_configuration.m_materials.end()) - { - //Skip materials that do not exist in the map - continue; - } - - auto materialInstance = materialIt->second.m_materialInstance; - if (!materialInstance) + if (materialIt != m_configuration.m_materials.end()) { - //Skip materials with an invalid instances - continue; - } - - if (!materialInstance->CanCompile()) - { - //If a material cannot currently be compiled then it must be queued again - m_queuedPropertyOverrides.emplace(materialAssignmentId); - continue; - } - - const auto& propertyOverrides2 = materialIt->second.m_propertyOverrides; - for (auto& propertyPair : propertyOverrides2) - { - if (propertyPair.second.empty()) + if (!materialIt->second.ApplyProperties()) { - continue; + // If a material cannot currently be compiled then it must be queued again + m_materialsWithDirtyProperties.emplace(materialAssignmentId); } - - const auto& materialPropertyIndex = materialInstance->FindPropertyIndex(propertyPair.first); - if (materialPropertyIndex.IsNull()) - { - continue; - } - - materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second)); } - - materialInstance->Compile(); } - // Only disconnect from tick bus and send notification after all pending properties have been applied - if (m_queuedPropertyOverrides.empty()) + // Only disconnect from tick bus and send notification after all pending properties have been applied + if (m_materialsWithDirtyProperties.empty()) { if (m_queuedMaterialUpdateNotification) { @@ -212,15 +185,35 @@ namespace AZ Data::AssetBus::MultiHandler::BusDisconnect(); bool anyQueued = false; - for (auto& materialPair : m_configuration.m_materials) + auto queueAsset = [&anyQueued, this](AZ::Data::Asset& materialAsset) -> bool { - auto& materialAsset = materialPair.second.m_materialAsset; - - if (materialAsset.GetId().IsValid() && !Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId())) + if (materialAsset.GetId().IsValid() && !this->Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId())) { anyQueued = true; materialAsset.QueueLoad(); - Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId()); + this->Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId()); + return true; + } + return false; + }; + + for (auto& materialPair : m_configuration.m_materials) + { + if (materialPair.second.m_materialInstancePreCreated) + { + continue; + } + + materialPair.second.m_defaultMaterialAsset = {}; + if (!queueAsset(materialPair.second.m_materialAsset)) + { + // Only assign and load the default material if there was no material override and there are propoerties to apply + if (!materialPair.second.m_propertyOverrides.empty() || !materialPair.second.m_matModUvOverrides.empty()) + { + materialPair.second.m_defaultMaterialAsset = AZ::Data::Asset( + GetDefaultMaterialAssetId(materialPair.first), AZ::AzTypeInfo::Uuid()); + queueAsset(materialPair.second.m_defaultMaterialAsset); + } } } @@ -229,14 +222,12 @@ namespace AZ ReleaseMaterials(); } } - + void MaterialComponentController::InitializeMaterialInstance(const Data::Asset& asset) { bool allReady = true; - - for (auto& materialPair : m_configuration.m_materials) + auto updateAsset = [&](AZ::Data::Asset& materialAsset) { - auto& materialAsset = materialPair.second.m_materialAsset; if (materialAsset.GetId() == asset.GetId()) { materialAsset = asset; @@ -246,6 +237,12 @@ namespace AZ { allReady = false; } + }; + + for (auto& materialPair : m_configuration.m_materials) + { + updateAsset(materialPair.second.m_materialAsset); + updateAsset(materialPair.second.m_defaultMaterialAsset); } if (allReady) @@ -268,11 +265,7 @@ namespace AZ for (auto& materialPair : m_configuration.m_materials) { - if (materialPair.second.m_materialAsset.GetId().IsValid()) - { - materialPair.second.m_materialAsset.Release(); - materialPair.second.m_materialInstance = nullptr; - } + materialPair.second.Release(); } MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials); @@ -334,7 +327,7 @@ namespace AZ // this function is called twice once material asset is changed, a temp variable is // needed to prevent material asset going out of scope during second call // before LoadMaterials() is called [LYN-2249] - auto temp = m_configuration.m_materials; + auto temp = m_configuration.m_materials; m_configuration.m_materials = materials; LoadMaterials(); } @@ -440,12 +433,7 @@ namespace AZ AZ::Data::AssetId MaterialComponentController::GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const { auto materialIt = m_configuration.m_materials.find(materialAssignmentId); - if (materialIt == m_configuration.m_materials.end()) - { - return {}; - } - - return materialIt->second.m_materialAsset.GetId(); + return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_materialAsset.GetId() : AZ::Data::AssetId(); } void MaterialComponentController::ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) @@ -459,18 +447,21 @@ namespace AZ void MaterialComponentController::SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value) { auto& materialAssignment = m_configuration.m_materials[materialAssignmentId]; + const bool wasEmpty = materialAssignment.m_propertyOverrides.empty(); + materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value; - // When applying property overrides for the first time, new instance needs to be created in case the current instance is already used somewhere else to keep overrides local - if (materialAssignment.m_propertyOverrides.empty()) + if (materialAssignment.RequiresLoading()) { - materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value; - materialAssignment.RebuildInstance(); - MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialAssignment); - QueueMaterialUpdateNotification(); + LoadMaterials(); + return; } - else + + if (wasEmpty != materialAssignment.m_propertyOverrides.empty()) { - materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value; + materialAssignment.RebuildInstance(); + MaterialComponentNotificationBus::Event( + m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialAssignment); + QueueMaterialUpdateNotification(); } QueuePropertyChanges(materialAssignmentId); @@ -703,6 +694,12 @@ namespace AZ const bool wasEmpty = materialAssignment.m_propertyOverrides.empty(); materialAssignment.m_propertyOverrides = propertyOverrides; + if (materialAssignment.RequiresLoading()) + { + LoadMaterials(); + return; + } + if (wasEmpty != materialAssignment.m_propertyOverrides.empty()) { materialAssignment.RebuildInstance(); @@ -712,14 +709,11 @@ namespace AZ QueuePropertyChanges(materialAssignmentId); } - MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) const + MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides( + const MaterialAssignmentId& materialAssignmentId) const { const auto materialIt = m_configuration.m_materials.find(materialAssignmentId); - if (materialIt == m_configuration.m_materials.end()) - { - return {}; - } - return materialIt->second.m_propertyOverrides; + return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_propertyOverrides : MaterialPropertyOverrideMap(); } void MaterialComponentController::SetModelUvOverrides( @@ -729,6 +723,12 @@ namespace AZ const bool wasEmpty = materialAssignment.m_matModUvOverrides.empty(); materialAssignment.m_matModUvOverrides = modelUvOverrides; + if (materialAssignment.RequiresLoading()) + { + LoadMaterials(); + return; + } + if (wasEmpty != materialAssignment.m_matModUvOverrides.empty()) { materialAssignment.RebuildInstance(); @@ -742,16 +742,24 @@ namespace AZ const MaterialAssignmentId& materialAssignmentId) const { const auto materialIt = m_configuration.m_materials.find(materialAssignmentId); - if (materialIt == m_configuration.m_materials.end()) + return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_matModUvOverrides : AZ::RPI::MaterialModelUvOverrideMap(); + } + + void MaterialComponentController::OnMaterialAssignmentsChanged() + { + for (const auto& materialPair : m_configuration.m_materials) { - return {}; + if (materialPair.second.RequiresLoading()) + { + LoadMaterials(); + return; + } } - return materialIt->second.m_matModUvOverrides; } void MaterialComponentController::QueuePropertyChanges(const MaterialAssignmentId& materialAssignmentId) { - m_queuedPropertyOverrides.emplace(materialAssignmentId); + m_materialsWithDirtyProperties.emplace(materialAssignmentId); if (!TickBus::Handler::BusIsConnected()) { TickBus::Handler::BusConnect(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h index 2bf15ca3b8..1594456a31 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h @@ -22,6 +22,7 @@ namespace AZ //! to provide material overrides on a per-entity basis. class MaterialComponentController final : MaterialComponentRequestBus::Handler + , MaterialReceiverNotificationBus::Handler , Data::AssetBus::MultiHandler , TickBus::Handler { @@ -100,6 +101,9 @@ namespace AZ const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) override; AZ::RPI::MaterialModelUvOverrideMap GetModelUvOverrides(const MaterialAssignmentId& materialAssignmentId) const override; + //! MaterialReceiverNotificationBus::Handler overrides... + void OnMaterialAssignmentsChanged() override; + private: AZ_DISABLE_COPY(MaterialComponentController); @@ -121,7 +125,7 @@ namespace AZ EntityId m_entityId; MaterialComponentConfig m_configuration; - AZStd::unordered_set m_queuedPropertyOverrides; + AZStd::unordered_set m_materialsWithDirtyProperties; bool m_queuedMaterialUpdateNotification = false; }; } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index 9e0c285001..6cdb44a9e1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -193,41 +194,13 @@ namespace AZ incompatible.push_back(AZ_CRC("MeshService", 0x71d8a455)); } - // [GFX TODO] [ATOM-13339] Remove the ModelAsset id fix up function in MeshComponentController - // Model id was changed due to fix for [ATOM-13312]. We can remove this code when all the levels are updated. - void FixUpModelAsset(Data::Asset& modelAsset) - { - Data::AssetId assetId; - Data::AssetCatalogRequestBus::BroadcastResult( - assetId, - &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, - modelAsset.GetHint().c_str(), - AZ::RPI::ModelAsset::TYPEINFO_Uuid(), - false); - if (assetId != modelAsset.GetId()) - { - if (assetId.IsValid()) - { - modelAsset = Data::Asset{ assetId, AZ::RPI::ModelAsset::TYPEINFO_Uuid(), modelAsset.GetHint().c_str() }; - modelAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::QueueLoad); - } - else - { - AZ_Error("MeshComponentController", false, "Failed to find asset id for [%s] ", modelAsset.GetHint().c_str()); - } - } - } - MeshComponentController::MeshComponentController(const MeshComponentConfig& config) : m_configuration(config) { - FixUpModelAsset(m_configuration.m_modelAsset); } void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair) { - FixUpModelAsset(m_configuration.m_modelAsset); - const AZ::EntityId entityId = entityComponentIdPair.GetEntityId(); m_entityComponentIdPair = entityComponentIdPair; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentConfig.cpp index 9c137f5b72..f9a427a3a0 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentConfig.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index d06bc981fe..e10a8b14c9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -6,9 +6,10 @@ * */ -#include #include #include +#include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponent.cpp index d38130bef6..3318f84905 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponent.cpp @@ -8,6 +8,8 @@ #include +#include + namespace AZ { namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/LookModificationComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/LookModificationComponentConfig.cpp index f77f67635f..4b1665895f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/LookModificationComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/LookModificationComponentConfig.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/LookModificationComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/LookModificationComponentController.cpp index aedb25bb36..4f321ed8b1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/LookModificationComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/LookModificationComponentController.cpp @@ -7,6 +7,7 @@ */ #include +#include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/RadiusWeightModifierComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/RadiusWeightModifierComponent.cpp index 53b508c619..d668533807 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/RadiusWeightModifierComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/RadiusWeightModifierComponent.cpp @@ -8,6 +8,8 @@ #include +#include + namespace AZ { namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/ShapeWeightModifierComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/ShapeWeightModifierComponent.cpp index fd3063fe98..bc3f6f8719 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/ShapeWeightModifierComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/ShapeWeightModifierComponent.cpp @@ -8,6 +8,8 @@ #include +#include + namespace AZ { namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/ShapeWeightModifierComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/ShapeWeightModifierComponentController.cpp index 42898cd188..d4761efa96 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/ShapeWeightModifierComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/ShapeWeightModifierComponentController.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index aa68333108..d7fcd124d0 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp index 764741a5cf..4022dfda9b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentConfig.cpp index b86cccf341..288892cddf 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentConfig.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include diff --git a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp index c2fb81e86a..8c6c0a8e05 100644 --- a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -208,7 +209,7 @@ namespace Blast itr.second.Disconnect(); } m_collisionHandlers.clear(); - + BlastFamilyDamageRequestBus::MultiHandler::BusDisconnect(); BlastFamilyComponentRequestBus::Handler::BusDisconnect(); @@ -546,7 +547,7 @@ namespace Blast } m_solver->notifyActorCreated(*actor.GetTkActor().getActorLL()); - + if (auto* physicsSystem = AZ::Interface::Get()) { AZStd::pair foundBody = physicsSystem->FindAttachedBodyHandleFromEntityId(actor.GetEntity()->GetId()); diff --git a/Gems/Blast/Code/Source/Components/BlastMeshDataComponent.cpp b/Gems/Blast/Code/Source/Components/BlastMeshDataComponent.cpp index 17848a53d3..a9f079dd65 100644 --- a/Gems/Blast/Code/Source/Components/BlastMeshDataComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastMeshDataComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include #include namespace Blast diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp index 851cb4460b..0c697f12b6 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp index 3bc6da4297..90f530895a 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp index 9d0e4fe2ff..83085b5f9c 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphFollowerParameterAction.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphFollowerParameterAction.cpp index 1355e2b0e5..3d8add320a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphFollowerParameterAction.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphFollowerParameterAction.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphReferenceNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphReferenceNode.cpp index 605810c5cc..16f192615d 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphReferenceNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphReferenceNode.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphSymbolicFollowerParameterAction.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphSymbolicFollowerParameterAction.cpp index fda679b8d0..e1f15c1ab2 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphSymbolicFollowerParameterAction.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphSymbolicFollowerParameterAction.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MotionSet.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/MotionSet.cpp index d8880f6fcc..56f7ee567e 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MotionSet.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MotionSet.cpp @@ -726,10 +726,12 @@ namespace EMotionFX { MCore::LockGuardRecursive lock(m_mutex); - return AZStd::accumulate(begin(m_childSets), end(m_childSets), size_t{0}, [](size_t total, const MotionSet* motionSet) + size_t result = 0; + for (const MotionSet* motionSet : m_childSets) { - return total + motionSet->GetIsOwnedByRuntime(); - }); + result += !motionSet->GetIsOwnedByRuntime(); + } + return result; } diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index da4c72b70a..ab55f909c2 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/AnimGraphComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/AnimGraphComponent.cpp index 704b746e3f..c47c18ba7d 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/AnimGraphComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/AnimGraphComponent.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleMotionComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleMotionComponent.cpp index dd7fdeea50..6876051c63 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleMotionComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleMotionComponent.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index ed4edda891..94e52a172f 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorAnimGraphComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorAnimGraphComponent.cpp index 8721acbf3d..9833fcc87a 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorAnimGraphComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorAnimGraphComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/Gems/EMotionFX/Code/Tests/EMotionFXBuilderTests.cpp b/Gems/EMotionFX/Code/Tests/EMotionFXBuilderTests.cpp index acdd78fc0f..0155b38da2 100644 --- a/Gems/EMotionFX/Code/Tests/EMotionFXBuilderTests.cpp +++ b/Gems/EMotionFX/Code/Tests/EMotionFXBuilderTests.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp index 9940a01b7a..688eff7671 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp index 8d472945cc..f68bef1c93 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include diff --git a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp index 9e01b690e0..6fe8d32484 100644 --- a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp @@ -8,6 +8,7 @@ #include "ImageGradientComponent.h" #include +#include #include #include #include diff --git a/Gems/GraphCanvas/Code/Source/Translation/TranslationAsset.cpp b/Gems/GraphCanvas/Code/Source/Translation/TranslationAsset.cpp index fbd7bdfd22..a461866c46 100644 --- a/Gems/GraphCanvas/Code/Source/Translation/TranslationAsset.cpp +++ b/Gems/GraphCanvas/Code/Source/Translation/TranslationAsset.cpp @@ -8,6 +8,8 @@ #include +#include + #include #include diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/StateController.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/StateController.h index 053ed98978..54dccb8ae0 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/StateController.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/StateController.h @@ -8,6 +8,7 @@ #pragma once #include +#include // A configurable queue that allows for multiple sources to try to control a single value in a configurable way // such that each object can control the object independently of the other systems, while still maintaining a reasonable state. diff --git a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYEntityOutliner.cpp b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYEntityOutliner.cpp index 2df6c9984b..1bf9d26fdf 100644 --- a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYEntityOutliner.cpp +++ b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYEntityOutliner.cpp @@ -11,6 +11,7 @@ #ifdef IMGUI_ENABLED #include +#include #include #include #include diff --git a/Gems/LmbrCentral/Code/Source/Builders/LuaBuilder/LuaBuilderWorker.h b/Gems/LmbrCentral/Code/Source/Builders/LuaBuilder/LuaBuilderWorker.h index 1d3a61626b..6f2aeecb8f 100644 --- a/Gems/LmbrCentral/Code/Source/Builders/LuaBuilder/LuaBuilderWorker.h +++ b/Gems/LmbrCentral/Code/Source/Builders/LuaBuilder/LuaBuilderWorker.h @@ -7,10 +7,14 @@ */ #pragma once -#include -#include #include #include +#include +#include +namespace AZ +{ + class ScriptContext; +} namespace LuaBuilder { @@ -39,6 +43,10 @@ namespace LuaBuilder JobStepOutcome RunCompileJob(const AssetBuilderSDK::ProcessJobRequest& request); JobStepOutcome RunCopyJob(const AssetBuilderSDK::ProcessJobRequest& request); - JobStepOutcome WriteAssetInfo(const AssetBuilderSDK::ProcessJobRequest& request, AZStd::string_view destFileName, AZStd::string_view debugName, AZ::ScriptContext& scriptContext); + JobStepOutcome WriteAssetInfo( + const AssetBuilderSDK::ProcessJobRequest& request, + AZStd::string_view destFileName, + AZStd::string_view debugName, + AZ::ScriptContext& scriptContext); }; -} +} // namespace LuaBuilder diff --git a/Gems/LmbrCentral/Code/Source/Scripting/EditorSpawnerComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/EditorSpawnerComponent.cpp index 315480c869..f43b69f703 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/EditorSpawnerComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/EditorSpawnerComponent.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include diff --git a/Gems/LmbrCentral/Code/Source/Scripting/SpawnerComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/SpawnerComponent.cpp index 5305a68d84..f79dc3f598 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/SpawnerComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/SpawnerComponent.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Gems/LmbrCentral/Code/Tests/Builders/SliceBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/SliceBuilderTests.cpp index 647a5dedef..73ef7143c8 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/SliceBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/SliceBuilderTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/LyShine/Code/Source/UiSpawnerComponent.cpp b/Gems/LyShine/Code/Source/UiSpawnerComponent.cpp index e9e4e4539f..6eb04b3684 100644 --- a/Gems/LyShine/Code/Source/UiSpawnerComponent.cpp +++ b/Gems/LyShine/Code/Source/UiSpawnerComponent.cpp @@ -7,6 +7,7 @@ */ #include "UiSpawnerComponent.h" +#include #include #include #include diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Common.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Common.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja similarity index 99% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja index eb4b81ea96..cf62f6f901 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja @@ -909,6 +909,7 @@ enum class NetworkProperties controller->Set{{ UpperFirst(Property.attrib['Name']) }}({{ LowerFirst(Property.attrib['Name']) }}); {% endif %} }) +{% if Property.attrib['GenerateEventBindings']|booleanTrue %} {% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' -%} ->Method("GetOn{{ UpperFirst(Property.attrib['Name']) }}ChangedEvent", [](AZ::EntityId id) -> AZ::Event* {% else %} @@ -936,6 +937,7 @@ enum class NetworkProperties {% else %} ->Attribute(AZ::Script::Attributes::AzEventDescription, AZ::BehaviorAzEventDescription{ "On {{ UpperFirst(Property.attrib['Name']) }} Changed Event", {"New {{ Property.attrib['Type'] }}"} }) {% endif %} +{% endif %} {% endif %} {% endcall %} @@ -1518,7 +1520,7 @@ namespace {{ Component.attrib['Namespace'] }} {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Client')|indent(4) }} - behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}") + behaviorContext->Class<{{ ComponentBaseName }}>("{{ ComponentBaseName }}") ->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}") ->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}") diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h index 478c925299..9af22b1fdf 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h @@ -19,6 +19,22 @@ namespace Physics namespace Multiplayer { + //! NetworkCharacterRequests + //! ComponentBus handled by NetworkCharacterComponentController. + //! Bus was created for exposing controller methods to script; C++ users should access the controller directly. + class NetworkCharacterRequests : public AZ::ComponentBus + { + public: + //! TryMoveWithVelocity + //! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync. + //! Velocity will be applied over delta-time to determine the movement amount. + //! Returns this entity's world-space position after the move. + virtual AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime) = 0; + }; + + typedef AZ::EBus NetworkCharacterRequestBus; + + //! NetworkCharacterComponent //! Provides multiplayer support for game-play player characters. class NetworkCharacterComponent @@ -39,6 +55,12 @@ namespace Multiplayer incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService")); } + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + NetworkCharacterComponentBase::GetRequiredServices(required); + required.push_back(AZ_CRC_CE("PhysXCharacterControllerService")); + } + // AZ::Component void OnInit() override {} void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; @@ -65,18 +87,22 @@ namespace Multiplayer //! Class provides the ability to move characters in physical space while keeping the network in-sync. class NetworkCharacterComponentController : public NetworkCharacterComponentControllerBase + , private NetworkCharacterRequestBus::Handler { public: + AZ_RTTI(NetworkCharacterComponentController, "{C91851A2-8B95-4484-9F97-BFF9D1F528A0}") + static void Reflect(AZ::ReflectContext* context); NetworkCharacterComponentController(NetworkCharacterComponent& parent); // NetworkCharacterComponentControllerBase void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + // NetworkCharacterRequestBus::Handler //! TryMoveWithVelocity //! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync. //! Velocity will be applied over delta-time to determine the movement amount. //! Returns this entity's world-space position after the move. - AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime); + AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime) override; }; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h index 19379fc959..cec73b1b71 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h @@ -1,5 +1,6 @@ /* - * 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. + * 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 * diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index 634cac74b2..7b7cb52fe1 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp index b14fc8761f..9e15ae6ce0 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -19,7 +20,7 @@ namespace Multiplayer { - + bool CollisionLayerBasedControllerFilter(const physx::PxController& controllerA, const physx::PxController& controllerB) { PHYSX_SCENE_READ_LOCK(controllerA.getActor()->getScene()); @@ -82,8 +83,8 @@ namespace Multiplayer return physx::PxQueryHitType::eNONE; } - - void NetworkCharacterComponent::NetworkCharacterComponent::Reflect(AZ::ReflectContext* context) + + void NetworkCharacterComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) @@ -92,6 +93,7 @@ namespace Multiplayer ->Version(1); } NetworkCharacterComponentBase::Reflect(context); + NetworkCharacterComponentController::Reflect(context); } NetworkCharacterComponent::NetworkCharacterComponent() @@ -115,7 +117,7 @@ namespace Multiplayer callbackManager->SetObjectPreFilter(CollisionLayerBasedObjectPreFilter); } } - + if (!HasController()) { GetNetworkTransformComponent()->TranslationAddEvent(m_translationEventHandler); @@ -133,7 +135,7 @@ namespace Multiplayer } void NetworkCharacterComponent::OnSyncRewind() - { + { if (m_physicsCharacter == nullptr) { return; @@ -161,6 +163,18 @@ namespace Multiplayer return state.touchedActor != nullptr || (state.collisionFlags & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) != 0; } + void NetworkCharacterComponentController::Reflect(AZ::ReflectContext* context) + { + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("NetworkCharacterRequestBus") + ->Event("TryMoveWithVelocity", &NetworkCharacterRequestBus::Events::TryMoveWithVelocity, {{ { "Velocity" }, { "DeltaTime" } }}); + + behaviorContext->Class("NetworkCharacterComponentController") + ->RequestBus("NetworkCharacterRequestBus"); + } + } + NetworkCharacterComponentController::NetworkCharacterComponentController(NetworkCharacterComponent& parent) : NetworkCharacterComponentControllerBase(parent) { @@ -169,12 +183,12 @@ namespace Multiplayer void NetworkCharacterComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { - ; + NetworkCharacterRequestBus::Handler::BusConnect(GetEntity()->GetId()); } void NetworkCharacterComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { - ; + NetworkCharacterRequestBus::Handler::BusDisconnect(GetEntity()->GetId()); } AZ::Vector3 NetworkCharacterComponentController::TryMoveWithVelocity(const AZ::Vector3& velocity, [[maybe_unused]] float deltaTime) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp index bcf855d834..725ebc024c 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp @@ -1,5 +1,6 @@ /* - * 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. + * 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 * diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index e66eb8d3a3..023c5ea3af 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp index 5d677c6101..198deebbe3 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index 3516255c7d..7782444a6e 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -51,11 +51,11 @@ set(FILES Include/Multiplayer/NetworkTime/RewindableObject.inl Include/Multiplayer/Physics/PhysicsUtils.h Include/Multiplayer/ReplicationWindows/IReplicationWindow.h - Source/AutoGen/AutoComponentTypes_Header.jinja - Source/AutoGen/AutoComponentTypes_Source.jinja - Source/AutoGen/AutoComponent_Common.jinja - Source/AutoGen/AutoComponent_Header.jinja - Source/AutoGen/AutoComponent_Source.jinja + Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja + Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja + Include/Multiplayer/AutoGen/AutoComponent_Common.jinja + Include/Multiplayer/AutoGen/AutoComponent_Header.jinja + Include/Multiplayer/AutoGen/AutoComponent_Source.jinja Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml Source/AutoGen/Multiplayer.AutoPackets.xml Source/AutoGen/MultiplayerEditor.AutoPackets.xml diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponent.cpp b/Gems/NvCloth/Code/Source/Components/ClothComponent.cpp index 0bbe52faf1..b252d3b5e6 100644 --- a/Gems/NvCloth/Code/Source/Components/ClothComponent.cpp +++ b/Gems/NvCloth/Code/Source/Components/ClothComponent.cpp @@ -8,6 +8,7 @@ #include #include +#include #include diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ClothComponentMesh.cpp b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ClothComponentMesh.cpp index ecf2bd0a54..9a211290e3 100644 --- a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ClothComponentMesh.cpp +++ b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ClothComponentMesh.cpp @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include namespace NvCloth { diff --git a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp index 113d660b32..420c6f3a5c 100644 --- a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp +++ b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include diff --git a/Gems/NvCloth/Code/Source/Editor/MeshNodeHandler.cpp b/Gems/NvCloth/Code/Source/Editor/MeshNodeHandler.cpp index d42277ebb4..9f874f7765 100644 --- a/Gems/NvCloth/Code/Source/Editor/MeshNodeHandler.cpp +++ b/Gems/NvCloth/Code/Source/Editor/MeshNodeHandler.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/Gems/NvCloth/Code/Source/Editor/MeshNodeHandler.h b/Gems/NvCloth/Code/Source/Editor/MeshNodeHandler.h index 76161aa838..be4ac2ae36 100644 --- a/Gems/NvCloth/Code/Source/Editor/MeshNodeHandler.h +++ b/Gems/NvCloth/Code/Source/Editor/MeshNodeHandler.h @@ -10,6 +10,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include #endif diff --git a/Gems/NvCloth/Code/Source/Utils/MeshAssetHelper.cpp b/Gems/NvCloth/Code/Source/Utils/MeshAssetHelper.cpp index c95c8896ab..b72a5594e8 100644 --- a/Gems/NvCloth/Code/Source/Utils/MeshAssetHelper.cpp +++ b/Gems/NvCloth/Code/Source/Utils/MeshAssetHelper.cpp @@ -7,6 +7,7 @@ */ #include +#include #include diff --git a/Gems/PhysX/Code/Editor/ColliderAssetScaleMode.h b/Gems/PhysX/Code/Editor/ColliderAssetScaleMode.h index 4659bf3677..24e33ea584 100644 --- a/Gems/PhysX/Code/Editor/ColliderAssetScaleMode.h +++ b/Gems/PhysX/Code/Editor/ColliderAssetScaleMode.h @@ -8,21 +8,20 @@ #pragma once -#include "ColliderSubComponentMode.h" +#include #include namespace PhysX { /// Sub component mode for modifying the asset scale on a collider in the viewport. - class ColliderAssetScaleMode - : public PhysX::ColliderSubComponentMode + class ColliderAssetScaleMode : public PhysXSubComponentModeBase { public: AZ_CLASS_ALLOCATOR_DECL ColliderAssetScaleMode(); - // ColliderSubComponentMode ... + // PhysXSubComponentModeBase ... void Setup(const AZ::EntityComponentIdPair& idPair) override; void Refresh(const AZ::EntityComponentIdPair& idPair) override; void Teardown(const AZ::EntityComponentIdPair& idPair) override; diff --git a/Gems/PhysX/Code/Editor/ColliderBoxMode.h b/Gems/PhysX/Code/Editor/ColliderBoxMode.h index a8e0c03a3a..75ce928d80 100644 --- a/Gems/PhysX/Code/Editor/ColliderBoxMode.h +++ b/Gems/PhysX/Code/Editor/ColliderBoxMode.h @@ -8,19 +8,18 @@ #pragma once -#include "ColliderSubComponentMode.h" +#include #include namespace PhysX { /// Sub component mode for modifying the box dimensions on a collider. - class ColliderBoxMode - : public PhysX::ColliderSubComponentMode + class ColliderBoxMode : public PhysXSubComponentModeBase { public: AZ_CLASS_ALLOCATOR_DECL - // ColliderSubComponentMode ... + // PhysXSubComponentModeBase ... void Setup(const AZ::EntityComponentIdPair& idPair) override; void Refresh(const AZ::EntityComponentIdPair& idPair) override; void Teardown(const AZ::EntityComponentIdPair& idPair) override; diff --git a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h index b152801988..ceedd84a35 100644 --- a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h +++ b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h @@ -8,7 +8,7 @@ #pragma once -#include "ColliderSubComponentMode.h" +#include #include #include @@ -16,13 +16,13 @@ namespace PhysX { /// Sub component mode for modifying the height and radius on a capsule collider. class ColliderCapsuleMode - : public PhysX::ColliderSubComponentMode + : public PhysXSubComponentModeBase , private AzFramework::EntityDebugDisplayEventBus::Handler { public: AZ_CLASS_ALLOCATOR_DECL - // ColliderSubComponentMode ... + // PhysXSubComponentModeBase ... void Setup(const AZ::EntityComponentIdPair& idPair) override; void Refresh(const AZ::EntityComponentIdPair& idPair) override; void Teardown(const AZ::EntityComponentIdPair& idPair) override; diff --git a/Gems/PhysX/Code/Editor/ColliderComponentMode.cpp b/Gems/PhysX/Code/Editor/ColliderComponentMode.cpp index 994811b8d1..5de625f679 100644 --- a/Gems/PhysX/Code/Editor/ColliderComponentMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderComponentMode.cpp @@ -7,7 +7,6 @@ */ #include "ColliderComponentMode.h" -#include "ColliderSubComponentMode.h" #include "ColliderOffsetMode.h" #include "ColliderRotationMode.h" #include "ColliderBoxMode.h" @@ -15,6 +14,7 @@ #include "ColliderCapsuleMode.h" #include "ColliderAssetScaleMode.h" +#include #include #include diff --git a/Gems/PhysX/Code/Editor/ColliderComponentMode.h b/Gems/PhysX/Code/Editor/ColliderComponentMode.h index 420e621054..b4ca060065 100644 --- a/Gems/PhysX/Code/Editor/ColliderComponentMode.h +++ b/Gems/PhysX/Code/Editor/ColliderComponentMode.h @@ -15,7 +15,7 @@ namespace PhysX { - class ColliderSubComponentMode; + class PhysXSubComponentModeBase; //! ComponentMode for the Collider Component - Manages a list of Sub-Component Modes and //! is responsible for switching between and activating them. @@ -53,7 +53,7 @@ namespace PhysX void CreateSubModes(); void ResetCurrentMode(); - AZStd::unordered_map> m_subModes; + AZStd::unordered_map> m_subModes; SubMode m_subMode = SubMode::Dimensions; //! Create the Viewport UI cluster for sub mode selection. diff --git a/Gems/PhysX/Code/Editor/ColliderOffsetMode.h b/Gems/PhysX/Code/Editor/ColliderOffsetMode.h index bcf140a5e3..6a2e6a8a12 100644 --- a/Gems/PhysX/Code/Editor/ColliderOffsetMode.h +++ b/Gems/PhysX/Code/Editor/ColliderOffsetMode.h @@ -8,21 +8,20 @@ #pragma once -#include "ColliderSubComponentMode.h" +#include #include namespace PhysX { /// Sub component mode for modifying offset on a collider in the viewport. - class ColliderOffsetMode - : public PhysX::ColliderSubComponentMode + class ColliderOffsetMode : public PhysXSubComponentModeBase { public: AZ_CLASS_ALLOCATOR_DECL ColliderOffsetMode(); - // ColliderSubComponentMode ... + // PhysXSubComponentModeBase ... void Setup(const AZ::EntityComponentIdPair& idPair) override; void Refresh(const AZ::EntityComponentIdPair& idPair) override; void Teardown(const AZ::EntityComponentIdPair& idPair) override; diff --git a/Gems/PhysX/Code/Editor/ColliderRotationMode.h b/Gems/PhysX/Code/Editor/ColliderRotationMode.h index e1f578aed7..dc37e23b0e 100644 --- a/Gems/PhysX/Code/Editor/ColliderRotationMode.h +++ b/Gems/PhysX/Code/Editor/ColliderRotationMode.h @@ -8,7 +8,7 @@ #pragma once -#include "ColliderSubComponentMode.h" +#include #include #include @@ -16,7 +16,7 @@ namespace PhysX { /// Sub component mode for modifying the rotation on a collider in the viewport. class ColliderRotationMode - : public PhysX::ColliderSubComponentMode + : public PhysXSubComponentModeBase , private AzFramework::EntityDebugDisplayEventBus::Handler { public: @@ -24,7 +24,7 @@ namespace PhysX ColliderRotationMode(); - // ColliderSubComponentMode ... + // PhysXSubComponentModeBase ... void Setup(const AZ::EntityComponentIdPair& idPair) override; void Refresh(const AZ::EntityComponentIdPair& idPair) override; void Teardown(const AZ::EntityComponentIdPair& idPair) override; diff --git a/Gems/PhysX/Code/Editor/ColliderSphereMode.h b/Gems/PhysX/Code/Editor/ColliderSphereMode.h index 08ee813cd0..7d255d598b 100644 --- a/Gems/PhysX/Code/Editor/ColliderSphereMode.h +++ b/Gems/PhysX/Code/Editor/ColliderSphereMode.h @@ -8,7 +8,7 @@ #pragma once -#include "ColliderSubComponentMode.h" +#include #include #include @@ -16,13 +16,13 @@ namespace PhysX { /// Sub component mode for modifying the box dimensions on a collider. class ColliderSphereMode - : public PhysX::ColliderSubComponentMode + : public PhysXSubComponentModeBase , private AzFramework::EntityDebugDisplayEventBus::Handler { public: AZ_CLASS_ALLOCATOR_DECL - // ColliderSubComponentMode ... + // PhysXSubComponentModeBase ... void Setup(const AZ::EntityComponentIdPair& idPair) override; void Refresh(const AZ::EntityComponentIdPair& idPair) override; void Teardown(const AZ::EntityComponentIdPair& idPair) override; diff --git a/Gems/PhysX/Code/Editor/ColliderSubComponentMode.h b/Gems/PhysX/Code/Editor/ColliderSubComponentMode.h deleted file mode 100644 index 4711a20c86..0000000000 --- a/Gems/PhysX/Code/Editor/ColliderSubComponentMode.h +++ /dev/null @@ -1,42 +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 - * - */ - -#pragma once - -namespace AZ -{ - class EntityComponentIdPair; -} - -namespace PhysX -{ - /// Interface to implement when adding a new editing mode - /// for collider component mode. - class ColliderSubComponentMode - { - public: - virtual ~ColliderSubComponentMode() {}; - - /// Called when the mode is entered to initialise the mode. - /// @param idPair The entity/component id pair. - virtual void Setup(const AZ::EntityComponentIdPair& idPair) = 0; - - /// Called when the mode needs to refresh it's values. - /// @param idPair The entity/component id pair. - virtual void Refresh(const AZ::EntityComponentIdPair& idPair) = 0; - - /// Called when the mode exits to perform cleanup. - /// @param idPair The entity/component id pair. - virtual void Teardown(const AZ::EntityComponentIdPair& idPair) = 0; - - /// Called when reset hotkey is pressed. - /// Should reset values in the sub component mode to sensible defaults. - /// @param idPair The entity/component id pair. - virtual void ResetValues(const AZ::EntityComponentIdPair& idPair) = 0; - }; -} diff --git a/Gems/PhysX/Code/Editor/EditorClassConverters.cpp b/Gems/PhysX/Code/Editor/EditorClassConverters.cpp index e55ac6bbda..c9f5626dab 100644 --- a/Gems/PhysX/Code/Editor/EditorClassConverters.cpp +++ b/Gems/PhysX/Code/Editor/EditorClassConverters.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/Gems/PhysX/Code/Editor/EditorJointCommon.h b/Gems/PhysX/Code/Editor/EditorJointCommon.h new file mode 100644 index 0000000000..031b293942 --- /dev/null +++ b/Gems/PhysX/Code/Editor/EditorJointCommon.h @@ -0,0 +1,17 @@ +/* + * 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 + +namespace PhysX +{ + //! Pair of floating point values for angular limits. + using AngleLimitsFloatPair = AZStd::pair; +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorJointComponentMode.cpp b/Gems/PhysX/Code/Editor/EditorJointComponentMode.cpp deleted file mode 100644 index 39b028bef3..0000000000 --- a/Gems/PhysX/Code/Editor/EditorJointComponentMode.cpp +++ /dev/null @@ -1,577 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace PhysX -{ - namespace - { - //! Uri's for shortcut actions. - const AZ::Crc32 GoToNextModeActionUri = AZ_CRC("com.o3de.action.physx.joint.nextmode", 0xe9cf4ed6); - const AZ::Crc32 GoToPrevModeActionUri = AZ_CRC("com.o3de.action.physx.joint.prevmode", 0xe70f8daa); - } - - const AZStd::string EditorJointComponentMode::s_parameterAngularPair = "Twist Limits"; - const AZStd::string EditorJointComponentMode::s_parameterDamping = "Damping"; - const AZStd::string EditorJointComponentMode::s_parameterMaxForce = "Maximum Force"; - const AZStd::string EditorJointComponentMode::s_parameterMaxTorque = "Maximum Torque"; - const AZStd::string EditorJointComponentMode::s_parameterPosition = "Position"; - const AZStd::string EditorJointComponentMode::s_parameterRotation = "Rotation"; - const AZStd::string EditorJointComponentMode::s_parameterSnapPosition = "Snap Position"; - const AZStd::string EditorJointComponentMode::s_parameterSnapRotation = "Snap Rotation"; - const AZStd::string EditorJointComponentMode::s_parameterStiffness = "Stiffness"; - const AZStd::string EditorJointComponentMode::s_parameterSwingLimit = "Swing Limits"; - const AZStd::string EditorJointComponentMode::s_parameterTolerance = "Tolerance"; - const AZStd::string EditorJointComponentMode::s_parameterTransform = "Transform"; - const AZStd::string EditorJointComponentMode::s_parameterComponentMode = "Component Mode"; - const AZStd::string EditorJointComponentMode::s_parameterLeadEntity = "Lead Entity"; - const AZStd::string EditorJointComponentMode::s_parameterSelectOnSnap = "Select on Snap"; - - EditorSubComponentModeConfig::EditorSubComponentModeConfig(const AZStd::string& name - , EditorSubComponentModeType type) - : m_name(name), m_type(type) - { - } - - EditorSubComponentModeConfig::EditorSubComponentModeConfig(const AZStd::string& name - , EditorSubComponentModeType type - , float exponent - , float max - , float min) - : m_name(name), m_type(type), m_exponent(exponent), m_max(max), m_min(min) - { - } - - EditorSubComponentModeConfig::EditorSubComponentModeConfig(const AZStd::string& name - , EditorSubComponentModeType type - , const AZ::Vector3& axis - , float max - , float min) - : m_name(name), m_type(type), m_axis(axis), m_max(max), m_min(min) - { - } - - EditorSubComponentModeConfig::EditorSubComponentModeConfig(const AZStd::string& name - , EditorSubComponentModeType type - , float max - , float min) - : m_name(name), m_type(type), m_max(max), m_min(min) - { - } - - EditorJointComponentMode::EditorJointComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType) - : EditorBaseComponentMode(entityComponentIdPair, componentType) - , m_entityComponentIdPair(entityComponentIdPair) - , m_componentType(componentType) - { - EditorJointRequestBus::Event( - m_entityComponentIdPair - , &EditorJointRequests::SetBoolValue - , EditorJointComponentMode::s_parameterComponentMode - , true); - } - - EditorJointComponentMode::~EditorJointComponentMode() - { - EditorJointRequestBus::Event( - m_entityComponentIdPair - , &EditorJointRequests::SetBoolValue - , EditorJointComponentMode::s_parameterComponentMode - , false); - } - - bool EditorJointComponentMode::HandleMouseInteraction( - const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) - { - if (mouseInteraction.m_mouseEvent == AzToolsFramework::ViewportInteraction::MouseEvent::Wheel && - mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl()) - { - NextMode(); - return true; - } - - // Propagate mouse interaction to sub-component mode. - if (m_currentSubComponentMode) - { - m_currentSubComponentMode->HandleMouseInteraction(mouseInteraction); - } - - return false; - } - - AZStd::vector EditorJointComponentMode::PopulateActionsImpl() - { - AzToolsFramework::ActionOverride goToNextMode; - goToNextMode.SetUri(GoToNextModeActionUri); - goToNextMode.SetKeySequence(QKeySequence(Qt::Key_Tab)); - goToNextMode.SetTitle("Next Mode"); - goToNextMode.SetTip("Go to next mode"); - goToNextMode.SetEntityComponentIdPair(GetEntityComponentIdPair()); - goToNextMode.SetCallback([this]() - { - NextMode(); - }); - - AzToolsFramework::ActionOverride goToPrevMode; - goToPrevMode.SetUri(GoToPrevModeActionUri); - goToPrevMode.SetKeySequence(QKeySequence(Qt::SHIFT + Qt::Key_Tab)); - goToPrevMode.SetTitle("Previous Mode"); - goToPrevMode.SetTip("Go to previous mode"); - goToPrevMode.SetEntityComponentIdPair(GetEntityComponentIdPair()); - goToPrevMode.SetCallback([this]() - { - PreviousMode(); - }); - - return {goToNextMode, goToPrevMode}; - } - - void EditorJointComponentMode::NextMode() - { - const bool isForwardChange = true; - ChangeMode(isForwardChange); - } - - void EditorJointComponentMode::PreviousMode() - { - const bool isForwardChange = false; - ChangeMode(isForwardChange); - } - - void EditorJointComponentMode::ChangeMode(bool forwardChange) - { - if (m_configMap.empty()) - { - return; - } - - AZStd::string previousModeName; - if (m_currentSubComponentMode) - { - previousModeName = m_currentSubComponentMode->m_name; - m_currentSubComponentMode.reset(); - } - - ConfigMapIter configIter = m_configMap.begin(); - if (!previousModeName.empty()) - { - configIter = m_configMap.find(previousModeName); - } - - AZ::u32 iterCount = 0; - do - { - if (forwardChange) - { - ++configIter; - if (configIter == m_configMap.end()) - { - configIter = m_configMap.begin(); - } - } - else - { - if (configIter == m_configMap.begin()) - { - configIter = m_configMap.end(); - } - --configIter; - } - ++iterCount; - } while (!IsSubComponentModeUsed(configIter->second.m_name) && iterCount != m_configMap.size()); - - if (iterCount == m_configMap.size()) // All sub component modes are not in use. - { - return; - } - - SetCurrentSubComponentMode(configIter->second.m_name); - } - - void EditorJointComponentMode::SetCurrentSubComponentMode(const AZStd::string& subComponentModeName) - { - ConfigMapIter configIter = m_configMap.find(subComponentModeName); - if (configIter == m_configMap.end()) - { - AZ_Warning("EditorJointComponentMode" - , false - , "Attempt to set sub component mode which does not exist: %s" - , subComponentModeName.c_str()); - return; - } - EditorSubComponentModeConfig config = configIter->second; - - EditorJointRequestBus::EventResult( - config.m_selectLeadOnSnap, m_entityComponentIdPair - , &EditorJointRequests::GetBoolValue - , s_parameterSelectOnSnap); - - m_currentSubComponentMode.reset(); - - switch (config.m_type) - { - case EditorSubComponentModeType::Linear: - SetSubComponentModeLinear(config); - break; - case EditorSubComponentModeType::AnglePair: - SetSubComponentModeAnglePair(config); - break; - case EditorSubComponentModeType::AngleCone: - SetSubComponentModeAngleCone(config); - break; - case EditorSubComponentModeType::Vec3: - SetSubComponentModeVec3(config); - break; - case EditorSubComponentModeType::Rotation: - SetSubComponentModeRotation(config); - break; - case EditorSubComponentModeType::SnapPosition: - SetSubComponentModeSnapPosition(config); - break; - case EditorSubComponentModeType::SnapRotation: - SetSubComponentModeSnapRotation(config); - break; - default: - AZ_Error("EditorJointComponentMode::SetCurrentSubComponentMode" - , false - , "Unsupported sub-component mode type."); - } - } - - bool EditorJointComponentMode::IsSubComponentModeUsed(const AZStd::string& subComponentModeName) - { - bool isUsed = false; - EditorJointRequestBus::EventResult( - isUsed, m_entityComponentIdPair - , &EditorJointRequests::IsParameterUsed - , subComponentModeName); - return isUsed; - } - - void EditorJointComponentMode::SetSubComponentModeAngleCone(const EditorSubComponentModeConfig& config) - { - m_currentSubComponentMode = AZStd::make_shared(m_entityComponentIdPair - , m_componentType - , config.m_name - , config.m_max - , config.m_min); - } - - void EditorJointComponentMode::SetSubComponentModeAnglePair(const EditorSubComponentModeConfig& config) - { - m_currentSubComponentMode = AZStd::make_shared(m_entityComponentIdPair - , m_componentType - , config.m_name - , config.m_axis - , config.m_max - , config.m_min - , config.m_min - , -config.m_max); - } - - void EditorJointComponentMode::SetSubComponentModeLinear(const EditorSubComponentModeConfig& config) - { - m_currentSubComponentMode = AZStd::make_shared(m_entityComponentIdPair - , m_componentType - , config.m_name - , config.m_exponent - , config.m_max - , config.m_min); - } - - void EditorJointComponentMode::SetSubComponentModeVec3(const EditorSubComponentModeConfig& config) - { - m_currentSubComponentMode = AZStd::make_shared(m_entityComponentIdPair - , m_componentType - , config.m_name); - } - - void EditorJointComponentMode::SetSubComponentModeRotation(const EditorSubComponentModeConfig& config) - { - m_currentSubComponentMode = AZStd::make_shared(m_entityComponentIdPair - , m_componentType - , config.m_name); - } - - void EditorJointComponentMode::SetSubComponentModeSnapPosition(const EditorSubComponentModeConfig& config) - { - m_currentSubComponentMode = AZStd::make_shared(m_entityComponentIdPair - , m_componentType - , config.m_name - , config.m_selectLeadOnSnap); - } - - void EditorJointComponentMode::SetSubComponentModeSnapRotation(const EditorSubComponentModeConfig& config) - { - m_currentSubComponentMode = AZStd::make_shared(m_entityComponentIdPair - , m_componentType - , config.m_name); - } - - EditorBallJointComponentMode::EditorBallJointComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType) - : EditorJointComponentMode(entityComponentIdPair, componentType) - { - m_configMap = Configure(); - NextMode(); - } - - EditorBallJointComponentMode::~EditorBallJointComponentMode() - { - if (m_currentSubComponentMode) - { - m_currentSubComponentMode.reset(); - } - } - - void EditorJointComponentMode::Refresh() - { - if (m_currentSubComponentMode) - { - m_currentSubComponentMode->Refresh(); - } - } - - AZStd::map EditorBallJointComponentMode::Configure() - { - AZStd::map configMap; - - configMap.emplace( - EditorJointComponentMode::s_parameterPosition - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterPosition - , EditorSubComponentModeType::Vec3) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterRotation - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterRotation - , EditorSubComponentModeType::Rotation) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterSnapPosition - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterSnapPosition - , EditorSubComponentModeType::SnapPosition) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterSnapRotation - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterSnapRotation - , EditorSubComponentModeType::SnapRotation) - ); - - const float exponentBreakage = 1.0f; - - configMap.emplace( - EditorJointComponentMode::s_parameterMaxForce - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxForce - , EditorSubComponentModeType::Linear - , exponentBreakage - , PhysX::EditorJointConfig::s_breakageMax - , PhysX::EditorJointConfig::s_breakageMin) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterMaxTorque - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxTorque - , EditorSubComponentModeType::Linear - , exponentBreakage - , PhysX::EditorJointConfig::s_breakageMax - , PhysX::EditorJointConfig::s_breakageMin) - ); - - const float exponentSpring = 2.0f; - - configMap.emplace( - EditorJointComponentMode::s_parameterDamping - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterDamping - , EditorSubComponentModeType::Linear - , exponentSpring - , PhysX::EditorJointLimitConeConfig::s_springMax - , PhysX::EditorJointLimitConeConfig::s_springMin) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterStiffness - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterStiffness - , EditorSubComponentModeType::Linear - , exponentSpring - , PhysX::EditorJointLimitConeConfig::s_springMax - , PhysX::EditorJointLimitConeConfig::s_springMin) - ); - - // Cone tip to base is always X-axis. - //The angle cone defines the limitations for rotation about the Y and Z axes. - configMap.emplace( - EditorJointComponentMode::s_parameterSwingLimit - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterSwingLimit - , EditorSubComponentModeType::AngleCone - , PhysX::EditorJointLimitConeConfig::s_angleMax - , PhysX::EditorJointLimitConeConfig::s_angleMin) - ); - - return configMap; - } - - EditorFixedJointComponentMode::EditorFixedJointComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType) - : EditorJointComponentMode(entityComponentIdPair, componentType) - { - m_configMap = Configure(); - NextMode(); - } - - EditorFixedJointComponentMode::~EditorFixedJointComponentMode() - { - if (m_currentSubComponentMode) - { - m_currentSubComponentMode.reset(); - } - } - - AZStd::map EditorFixedJointComponentMode::Configure() - { - AZStd::map configMap; - - configMap.emplace( - EditorJointComponentMode::s_parameterPosition - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterPosition - , EditorSubComponentModeType::Vec3) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterRotation - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterRotation - , EditorSubComponentModeType::Rotation) - ); - - const float exponentBreakage = 1.0f; - - configMap.emplace( - EditorJointComponentMode::s_parameterMaxForce - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxForce - , EditorSubComponentModeType::Linear - , exponentBreakage - , PhysX::EditorJointConfig::s_breakageMax - , PhysX::EditorJointConfig::s_breakageMin) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterMaxTorque - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxTorque - , EditorSubComponentModeType::Linear - , exponentBreakage - , PhysX::EditorJointConfig::s_breakageMax - , PhysX::EditorJointConfig::s_breakageMin) - ); - - return configMap; - } - - EditorHingeJointComponentMode::EditorHingeJointComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType) - : EditorJointComponentMode(entityComponentIdPair, componentType) - { - m_configMap = Configure(); - NextMode(); - } - - EditorHingeJointComponentMode::~EditorHingeJointComponentMode() - { - if (m_currentSubComponentMode) - { - m_currentSubComponentMode.reset(); - } - } - - AZStd::map EditorHingeJointComponentMode::Configure() - { - AZStd::map configMap; - - configMap.emplace( - EditorJointComponentMode::s_parameterPosition - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterPosition - , EditorSubComponentModeType::Vec3) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterRotation - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterRotation - , EditorSubComponentModeType::Rotation) - ); - - const float exponentBreakage = 1.0f; - - configMap.emplace( - EditorJointComponentMode::s_parameterMaxForce - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxForce - , EditorSubComponentModeType::Linear - , exponentBreakage - , PhysX::EditorJointConfig::s_breakageMax - , PhysX::EditorJointConfig::s_breakageMin) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterMaxTorque - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxTorque - , EditorSubComponentModeType::Linear - , exponentBreakage - , PhysX::EditorJointConfig::s_breakageMax - , PhysX::EditorJointConfig::s_breakageMin) - ); - - const float exponentSpring = 2.0f; - - configMap.emplace( - EditorJointComponentMode::s_parameterDamping - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterDamping - , EditorSubComponentModeType::Linear - , exponentSpring - , PhysX::EditorJointLimitPairConfig::s_springMax - , PhysX::EditorJointLimitPairConfig::s_springMin) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterStiffness - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterStiffness - , EditorSubComponentModeType::Linear - , exponentSpring - , PhysX::EditorJointLimitPairConfig::s_springMax - , PhysX::EditorJointLimitPairConfig::s_springMin) - ); - - AZ::Vector3 axis = AZ::Vector3::CreateAxisX(); // PhysX revolute joints uses the x-axis by default - configMap.emplace( - EditorJointComponentMode::s_parameterAngularPair - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterAngularPair - , EditorSubComponentModeType::AnglePair - , axis - , PhysX::EditorJointLimitPairConfig::s_angleMax - , PhysX::EditorJointLimitPairConfig::s_angleMin) - ); - - configMap.emplace( - EditorJointComponentMode::s_parameterSnapPosition - , EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterSnapPosition - , EditorSubComponentModeType::SnapPosition) - ); - - return configMap; - } -} // namespace LmbrCentral diff --git a/Gems/PhysX/Code/Editor/EditorJointComponentMode.h b/Gems/PhysX/Code/Editor/EditorJointComponentMode.h deleted file mode 100644 index 023edcc24b..0000000000 --- a/Gems/PhysX/Code/Editor/EditorJointComponentMode.h +++ /dev/null @@ -1,173 +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 - * - */ -#pragma once - -#include -#include - -#include - -namespace PhysX -{ - enum class EditorSubComponentModeType : AZ::u8 - { - Linear, /// sub-component mode to modify a single linear value, e.g. a float value. - AnglePair, /// sub-component mode to modify a pair of float values representing angles. - AngleCone, /// sub-component mode to modify a constraint's swing limits and local transformation. - Rotation, /// sub-component mode to modify local transformation. - SnapPosition, /// sub-component mode to modify local position using a point-and-snap feature in the viewport. - SnapRotation, /// sub-component mode to modify local rotation using a point-and-snap feature in the viewport. - Vec3 /// sub-component mode to modify a Vector3 value. - }; - - /// Contains configuration of a sub-component mode. Shared by different types of sub-component mode. - /// Alternative implementation of this struct using AZStd::Variant is pending the development of the rest of the joint types. - struct EditorSubComponentModeConfig - { - EditorSubComponentModeConfig() = default; - - EditorSubComponentModeConfig(const AZStd::string& name - , EditorSubComponentModeType type); - - EditorSubComponentModeConfig(const AZStd::string& name - , EditorSubComponentModeType type - , float exponent - , float max - , float min); - - EditorSubComponentModeConfig(const AZStd::string& name - , EditorSubComponentModeType type - , const AZ::Vector3& axis - , float max - , float min); - - EditorSubComponentModeConfig(const AZStd::string& name - , EditorSubComponentModeType type - , float max - , float min); - - AZStd::string m_name; - EditorSubComponentModeType m_type; - AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); - float m_exponent = 1.0f; - float m_max = FLT_MAX; - float m_min = -FLT_MAX; - bool m_selectLeadOnSnap = true; ///< A user may use the snap-to-position component mode to snap the position of a joint to an entity. This flag indicates if the snapped-to entity would be selected as a joint's lead when that happens. - }; - - /// Generic component mode that supports multiple sub-component modes. - class EditorJointComponentMode - : public AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode - { - public: - static const AZStd::string s_parameterAngularPair; - static const AZStd::string s_parameterDamping; - static const AZStd::string s_parameterMaxForce; - static const AZStd::string s_parameterMaxTorque; - static const AZStd::string s_parameterPosition; - static const AZStd::string s_parameterRotation; - static const AZStd::string s_parameterSnapPosition; - static const AZStd::string s_parameterSnapRotation; - static const AZStd::string s_parameterStiffness; - static const AZStd::string s_parameterSwingLimit; - static const AZStd::string s_parameterTolerance; - static const AZStd::string s_parameterTransform; - static const AZStd::string s_parameterComponentMode; - static const AZStd::string s_parameterLeadEntity; - static const AZStd::string s_parameterSelectOnSnap; - - EditorJointComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType); - ~EditorJointComponentMode(); - - // EditorBaseComponentMode - void Refresh() override; - - /// Returns map of sub-component mode configurations required by this component mode. - virtual AZStd::map Configure() = 0; - - protected: - /// AzToolsFramework::ViewportInteraction::MouseViewportRequests - bool HandleMouseInteraction( - const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; - - /// EditorBaseComponentMode - AZStd::vector PopulateActionsImpl() override; - - /// Changes to the next sub-component mode found in m_configMap. - void NextMode(); - - /// Changes to the previous sub-component mode found in m_configMap. - void PreviousMode(); - - /// Changes to the next or previous sub-component mode found in m_configMap. - void ChangeMode(bool forwardChange); - - /// Replaces m_currentSubComponentMode with a new one instantiated using the configuration identified by the input subComponentModeName. - void SetCurrentSubComponentMode(const AZStd::string& subComponentModeName); - - AZStd::shared_ptr m_currentSubComponentMode = nullptr; ///< The active sub-component mode in this component mode. - AZ::Uuid m_componentType = AZ::Uuid::CreateNull(); - AZStd::map m_configMap; ///< Contains sub-component mode configurations supported by this component mode. - AZ::EntityComponentIdPair m_entityComponentIdPair; - - private: - bool IsSubComponentModeUsed(const AZStd::string& subComponentModeName); - - void SetSubComponentModeAngleCone(const EditorSubComponentModeConfig& config); - void SetSubComponentModeAnglePair(const EditorSubComponentModeConfig& config); - void SetSubComponentModeLinear(const EditorSubComponentModeConfig& config); - void SetSubComponentModeVec3(const EditorSubComponentModeConfig& config); - void SetSubComponentModeRotation(const EditorSubComponentModeConfig& config); - void SetSubComponentModeSnapPosition(const EditorSubComponentModeConfig& config); - void SetSubComponentModeSnapRotation(const EditorSubComponentModeConfig& config); - }; - - /// Ball joint specific component mode. Configure() is overriden to set up the required sub-component modes. - class EditorBallJointComponentMode - : public EditorJointComponentMode - { - public: - EditorBallJointComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType); - ~EditorBallJointComponentMode(); - - // EditorJointComponentMode - AZStd::map Configure() override; - }; - - /// Fixed joint specific component mode. Configure() is overriden to set up the required sub-component modes. - class EditorFixedJointComponentMode - : public EditorJointComponentMode - { - public: - EditorFixedJointComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType); - ~EditorFixedJointComponentMode(); - - // EditorJointComponentMode - AZStd::map Configure() override; - }; - - /// Hinge joint specific component mode. Configure() is overriden to set up the required sub-component modes. - class EditorHingeJointComponentMode - : public EditorJointComponentMode - { - public: - EditorHingeJointComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType); - ~EditorHingeJointComponentMode(); - - // EditorJointComponentMode - AZStd::map Configure() override; - }; - - using ConfigMap = AZStd::map; - using ConfigMapIter = ConfigMap::iterator; - using ConfigMapReverseIter = ConfigMap::reverse_iterator; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorJointTypeDrawer.cpp b/Gems/PhysX/Code/Editor/EditorJointTypeDrawer.cpp deleted file mode 100644 index 53b27f6cde..0000000000 --- a/Gems/PhysX/Code/Editor/EditorJointTypeDrawer.cpp +++ /dev/null @@ -1,68 +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 -#include - -namespace PhysX -{ - EditorJointTypeDrawer::EditorJointTypeDrawer(EditorJointType jointType, - AzFramework::EntityContextId entityContextId, - const AZStd::string& subComponentModeName): - m_subComponentModeName(subComponentModeName) - { - AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(entityContextId); - EditorJointTypeDrawerBus::Handler::BusConnect(EditorJointTypeDrawerId(jointType, - EditorSubComponentModeNameCrc(subComponentModeName))); - } - - EditorJointTypeDrawer::~EditorJointTypeDrawer() - { - EditorJointTypeDrawerBus::Handler::BusDisconnect(); - AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect(); - } - - void EditorJointTypeDrawer::DisplayViewport2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) - { - const AZ::u32 stateBefore = debugDisplay.GetState(); - - const AzFramework::CameraState cameraState = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId); - - const float xOffsetCurrentMode = 125.0f; - const float yOffsetCurrentMode = 55.0f; - - const float xOffsetHotKeys = 125.0f; - const float yOffsetHotKeys = 30.0f; - - const float viewportWidthHalf = cameraState.m_viewportSize.GetX() / 2.0f; - const float viewportHeight = cameraState.m_viewportSize.GetY(); - - debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, 1.0f)); - - AZStd::string screenTextCurrentMode = "Edit mode: " + m_subComponentModeName; - float xPos = viewportWidthHalf - xOffsetCurrentMode; - float yPos = viewportHeight - yOffsetCurrentMode; - float textSize = 2.0f; - debugDisplay.Draw2dTextLabel(xPos, yPos, textSize, screenTextCurrentMode.c_str()); - - AZStd::string screenTextHotKeys = " or to change modes"; - xPos = viewportWidthHalf - xOffsetHotKeys; - yPos = viewportHeight - yOffsetHotKeys; - textSize = 1.2f; - debugDisplay.Draw2dTextLabel(xPos, yPos, textSize, screenTextHotKeys.c_str()); - - debugDisplay.SetState(stateBefore); - } - - AZStd::shared_ptr EditorJointTypeDrawer::GetEditorJointTypeDrawer() - { - return shared_from_this(); - } - -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorJointTypeDrawer.h b/Gems/PhysX/Code/Editor/EditorJointTypeDrawer.h deleted file mode 100644 index 4172424f00..0000000000 --- a/Gems/PhysX/Code/Editor/EditorJointTypeDrawer.h +++ /dev/null @@ -1,41 +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 - * - */ -#pragma once - -#include -#include -#include - -namespace PhysX -{ - /// This class enables drawing in the viewport once for the component modes of multiple components in one entity. - /// Until the component mode framework allows a way to do this, a work-around like this class is necessary. - /// An instance of this class is created for each pair of component type and sub-component mode. - class EditorJointTypeDrawer - : public EditorJointTypeDrawerBus::Handler - , public AZStd::enable_shared_from_this - , private AzFramework::ViewportDebugDisplayEventBus::Handler - { - public: - EditorJointTypeDrawer(EditorJointType id, - AzFramework::EntityContextId entityContextId, - const AZStd::string& subComponentModeName); - ~EditorJointTypeDrawer(); - - private: - // AzFramework::ViewportDebugDisplayEventBus - void DisplayViewport2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; - - // PhysX::EditorJointTypeDrawerBus - AZStd::shared_ptr GetEditorJointTypeDrawer() override; - - AZStd::string m_subComponentModeName;///< Name of the sub component mode. E.g. Position, Rotation, Snap Position, etc. - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorJointTypeDrawerBus.h b/Gems/PhysX/Code/Editor/EditorJointTypeDrawerBus.h deleted file mode 100644 index e47c447fe2..0000000000 --- a/Gems/PhysX/Code/Editor/EditorJointTypeDrawerBus.h +++ /dev/null @@ -1,31 +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 - * - */ -#pragma once - -#include - -namespace PhysX -{ - class EditorJointTypeDrawer; - - using EditorJointType = AZ::Uuid; - using EditorSubComponentModeNameCrc = AZ::Crc32; - using EditorJointTypeDrawerId = AZStd::pair; - - /// The sub-component mode of a component type uses this bus (by invoking GetEditorJointTypeDrawer) to retrieve a drawer. - /// If nothing is returned, it creates an instance of the drawer that will be shared by other instances of the same component type. - class EditorJointTypeDrawerRequests : public AZ::EBusTraits - { - public: - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; - using BusIdType = EditorJointTypeDrawerId; - - virtual AZStd::shared_ptr GetEditorJointTypeDrawer() = 0; - }; - using EditorJointTypeDrawerBus = AZ::EBus; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeAngleCone.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeAngleCone.cpp deleted file mode 100644 index 43b9fd77f3..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeAngleCone.cpp +++ /dev/null @@ -1,453 +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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace -{ - const float ArrowLength = 2.0f; - const float ConeHeight = 3.0f; - const float XRotationManipulatorRadius = 2.0f; - const float XRotationManipulatorWidth = 0.05f; -} - -namespace PhysX -{ - struct SharedRotationState - { - AZ::Vector3 m_axis; - AZ::Quaternion m_savedOrientation = AZ::Quaternion::CreateIdentity(); - AngleLimitsFloatPair m_valuePair; - }; - - EditorSubComponentModeAngleCone::EditorSubComponentModeAngleCone( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name - , float max - , float min) - : EditorSubComponentModeBase(entityComponentIdPair, componentType, name) - , m_max(max) - , m_min(min) - { - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - const AZ::Quaternion localRotation = localTransform.GetRotation(); - - // Initialize manipulators used to resize the base of the cone. - m_yLinearManipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform); - m_yLinearManipulator->AddEntityComponentIdPair(m_entityComponentId); - m_yLinearManipulator->SetAxis(AZ::Vector3::CreateAxisZ()); - - m_zLinearManipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform); - m_zLinearManipulator->AddEntityComponentIdPair(m_entityComponentId); - m_zLinearManipulator->SetAxis(AZ::Vector3::CreateAxisY()); - - m_yzPlanarManipulator = AzToolsFramework::PlanarManipulator::MakeShared(worldTransform); - m_yzPlanarManipulator->AddEntityComponentIdPair(m_entityComponentId); - m_yzPlanarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ()); - - ConfigureLinearView(ArrowLength - , AZ::Color(1.0f, 0.0f, 0.0f, 1.0f) - , AZ::Color(0.0f, 1.0f, 0.0f, 1.0f) - , AZ::Color(0.0f, 0.0f, 1.0f, 1.0f)); - - ConfigurePlanarView(AZ::Color(0.0f, 1.0f, 0.0f, 1.0f) - , AZ::Color(0.0f, 0.0f, 1.0f, 1.0f)); - - // Position and orientate manipulators - AZ::Transform displacementTransform = localTransform; - AZ::Vector3 displacementTranslate = localRotation.TransformVector(AZ::Vector3(ConeHeight, 0.0f, 0.0f)); - displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); - - m_yLinearManipulator->SetLocalTransform(displacementTransform); - m_zLinearManipulator->SetLocalTransform(displacementTransform); - m_yzPlanarManipulator->SetLocalTransform(displacementTransform); - - // Initialize rotation manipulator for rotating cone - m_xRotationManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform); - m_xRotationManipulator->AddEntityComponentIdPair(m_entityComponentId); - m_xRotationManipulator->SetAxis(AZ::Vector3::CreateAxisX()); - m_xRotationManipulator->SetLocalTransform(localTransform); - - const AZ::Color xRotationManipulatorColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f); - m_xRotationManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle( - *m_xRotationManipulator, xRotationManipulatorColor, - XRotationManipulatorRadius, XRotationManipulatorWidth, AzToolsFramework::DrawHalfDottedCircle)); - - AZStd::shared_ptr sharedRotationState = - AZStd::make_shared(); - - struct SharedState - { - AngleLimitsFloatPair m_startValues; - }; - auto sharedState = AZStd::make_shared(); - - m_yLinearManipulator->InstallLeftMouseDownCallback( - [this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable - { - AngleLimitsFloatPair currentValue; - EditorJointRequestBus::EventResult( - currentValue, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , m_name); - sharedState->m_startValues = currentValue; - }); - - m_yLinearManipulator->InstallMouseMoveCallback( - [this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) - { - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - const AZ::Quaternion localRotation = localTransform.GetRotation(); - const float axisDisplacement = action.LocalPositionOffset().Dot(localRotation.TransformVector(action.m_fixed.m_axis)); - const float originalBaseY = tan(AZ::DegToRad(sharedState->m_startValues.first)) * ConeHeight; - const float newBaseY = originalBaseY + axisDisplacement; - const float newAngle = AZ::GetClamp(AZ::RadToDeg(atan(newBaseY / ConeHeight)), m_min, m_max); - - EditorJointRequestBus::Event( - m_entityComponentId - , &EditorJointRequests::SetLinearValuePair - , m_name - , AngleLimitsFloatPair(newAngle, sharedState->m_startValues.second)); - - m_yLinearManipulator->SetBoundsDirty(); - }); - - m_zLinearManipulator->InstallLeftMouseDownCallback( - [this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable - { - AngleLimitsFloatPair currentValue; - EditorJointRequestBus::EventResult( - currentValue, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , m_name); - sharedState->m_startValues = currentValue; - }); - - m_zLinearManipulator->InstallMouseMoveCallback( - [this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) - { - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - const AZ::Quaternion localRotation = localTransform.GetRotation(); - const float axisDisplacement = action.LocalPositionOffset().Dot(localRotation.TransformVector(action.m_fixed.m_axis)); - const float originalBaseZ = tan(AZ::DegToRad(sharedState->m_startValues.second)) * ConeHeight; - const float newBaseZ = originalBaseZ + axisDisplacement; - const float newAngle = AZ::GetClamp(AZ::RadToDeg(atan(newBaseZ / ConeHeight)), m_min, m_max); - - EditorJointRequestBus::Event( - m_entityComponentId - , &EditorJointRequests::SetLinearValuePair - , m_name - , AngleLimitsFloatPair(sharedState->m_startValues.first, newAngle)); - - m_zLinearManipulator->SetBoundsDirty(); - }); - - m_yzPlanarManipulator->InstallLeftMouseDownCallback( - [this, sharedState](const AzToolsFramework::PlanarManipulator::Action& /*action*/) mutable - { - AngleLimitsFloatPair currentValue; - EditorJointRequestBus::EventResult( - currentValue, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , m_name); - sharedState->m_startValues = currentValue; - }); - - m_yzPlanarManipulator->InstallMouseMoveCallback( - [this, sharedState](const AzToolsFramework::PlanarManipulator::Action& action) - { - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - const AZ::Quaternion localRotation = localTransform.GetRotation(); - - const float axisDisplacementY = action.LocalPositionOffset().Dot(localRotation.TransformVector(AZ::Vector3::CreateAxisY())); - const float axisDisplacementZ = action.LocalPositionOffset().Dot(localRotation.TransformVector(AZ::Vector3::CreateAxisZ())); - const float axisDisplacement = axisDisplacementZ > axisDisplacementY? axisDisplacementZ : axisDisplacementY; - - const float originalBaseY = tan(AZ::DegToRad(sharedState->m_startValues.first)) * ConeHeight; - const float newBaseY = originalBaseY + axisDisplacement; - const float newAngleY = AZ::GetClamp(AZ::RadToDeg(atan(newBaseY / ConeHeight)), m_min, m_max); - - const float originalBaseZ = tan(AZ::DegToRad(sharedState->m_startValues.second)) * ConeHeight; - const float newBaseZ = originalBaseZ + axisDisplacement; - const float newAngleZ = AZ::GetClamp(AZ::RadToDeg(atan(newBaseZ / ConeHeight)), m_min, m_max); - - EditorJointRequestBus::Event( - m_entityComponentId - , &EditorJointRequests::SetLinearValuePair - , m_name - , AngleLimitsFloatPair(newAngleY, newAngleZ)); - - m_yzPlanarManipulator->SetBoundsDirty(); - }); - - struct SharedStateXRotate - { - AZ::Transform m_startTM; - }; - auto sharedStateXRotate = AZStd::make_shared(); - - auto mouseDownCallback = [this, sharedRotationState](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void - { - AZ::Quaternion normalizedStart = action.m_start.m_rotation.GetNormalized(); - sharedRotationState->m_axis = AZ::Vector3(normalizedStart.GetX(), normalizedStart.GetY(), normalizedStart.GetZ()); - sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity(); - - AngleLimitsFloatPair currentValue; - EditorJointRequestBus::EventResult( - currentValue, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , m_name); - - sharedRotationState->m_valuePair = currentValue; - }; - - auto mouseDownRotateXCallback = [this, sharedStateXRotate]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action) mutable -> void - { - PhysX::EditorJointRequestBus::EventResult(sharedStateXRotate->m_startTM - , m_entityComponentId - , &PhysX::EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - }; - - m_xRotationManipulator->InstallLeftMouseDownCallback(mouseDownRotateXCallback); - - m_xRotationManipulator->InstallMouseMoveCallback( - [this, sharedStateXRotate] - (const AzToolsFramework::AngularManipulator::Action& action) mutable -> void - { - const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; - - AZ::Transform newTransform = AZ::Transform::CreateIdentity(); - newTransform = sharedStateXRotate->m_startTM * AZ::Transform::CreateFromQuaternion(action.m_current.m_delta); - - PhysX::EditorJointRequestBus::Event(m_entityComponentId - , &PhysX::EditorJointRequests::SetVector3Value - , PhysX::EditorJointComponentMode::s_parameterPosition - , newTransform.GetTranslation()); - PhysX::EditorJointRequestBus::Event(m_entityComponentId - , &PhysX::EditorJointRequests::SetVector3Value - , PhysX::EditorJointComponentMode::s_parameterRotation - , newTransform.GetRotation().GetEulerDegrees()); - - m_yLinearManipulator->SetLocalOrientation(manipulatorOrientation); - m_zLinearManipulator->SetLocalOrientation(manipulatorOrientation); - m_yLinearManipulator->SetAxis(action.m_current.m_delta.TransformVector(AZ::Vector3::CreateAxisY())); - m_zLinearManipulator->SetAxis(action.m_current.m_delta.TransformVector(AZ::Vector3::CreateAxisZ())); - m_xRotationManipulator->SetLocalOrientation(manipulatorOrientation); - - m_yLinearManipulator->SetBoundsDirty(); - m_zLinearManipulator->SetBoundsDirty(); - m_xRotationManipulator->SetBoundsDirty(); - }); - - m_xRotationManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_yLinearManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_zLinearManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_yzPlanarManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - - AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId()); - - Refresh(); - } - - EditorSubComponentModeAngleCone::~EditorSubComponentModeAngleCone() - { - AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); - - m_xRotationManipulator->Unregister(); - m_yLinearManipulator->Unregister(); - m_zLinearManipulator->Unregister(); - m_yzPlanarManipulator->Unregister(); - } - - void EditorSubComponentModeAngleCone::Refresh() - { - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - float coneHeight = ConeHeight; - AngleLimitsFloatPair yzSwingAngleLimits; - EditorJointRequestBus::EventResult( - yzSwingAngleLimits, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , m_name); - - // Draw inverted cone (negative cone height) if angles are larger than 90 deg. - if (yzSwingAngleLimits.first > 90.0f || yzSwingAngleLimits.second > 90.0f) - { - coneHeight = -ConeHeight; - } - - // reposition manipulators - const AZ::Quaternion localRotation = localTransform.GetRotation(); - const AZ::Vector3 linearManipulatorOffset = localTransform.GetTranslation() + localRotation.TransformVector(AZ::Vector3(coneHeight, 0.0f, 0.0f)); - - m_xRotationManipulator->SetLocalTransform(localTransform); - m_xRotationManipulator->SetBoundsDirty(); - - localTransform.SetTranslation(linearManipulatorOffset); - - m_yLinearManipulator->SetLocalTransform(localTransform); - m_zLinearManipulator->SetLocalTransform(localTransform); - m_yzPlanarManipulator->SetLocalTransform(localTransform); - m_yLinearManipulator->SetBoundsDirty(); - m_zLinearManipulator->SetBoundsDirty(); - m_yzPlanarManipulator->SetBoundsDirty(); - } - - void EditorSubComponentModeAngleCone::ConfigureLinearView( - float axisLength, [[maybe_unused]] const AZ::Color& axis1Color, const AZ::Color& axis2Color, - const AZ::Color& axis3Color) - { - const float coneLength = 0.28f; - const float coneRadius = 0.07f; - - const auto configureLinearView = [coneLength, axisLength, coneRadius]( - AzToolsFramework::LinearManipulator* linearManipulator, const AZ::Color& color) - { - AzToolsFramework::ManipulatorViews views; - views.emplace_back(CreateManipulatorViewLine( - *linearManipulator, color, axisLength, AzToolsFramework::ManipulatorLineBoundWidth())); - views.emplace_back(CreateManipulatorViewCone( - *linearManipulator, color, linearManipulator->GetAxis() * (axisLength - coneLength), - coneLength, coneRadius)); - linearManipulator->SetViews(AZStd::move(views)); - }; - - configureLinearView(m_yLinearManipulator.get(), axis2Color); - configureLinearView(m_zLinearManipulator.get(), axis3Color); - } - - void EditorSubComponentModeAngleCone::ConfigurePlanarView(const AZ::Color& planeColor, - const AZ::Color& plane2Color) - { - const float planeSize = 0.6f; - AzToolsFramework::ManipulatorViews views; - views.emplace_back(CreateManipulatorViewQuad( - *m_yzPlanarManipulator - , planeColor - , plane2Color - , planeSize)); - m_yzPlanarManipulator->SetViews(AZStd::move(views)); - } - - void EditorSubComponentModeAngleCone::DisplayEntityViewport( - [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) - { - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - AZ::u32 stateBefore = debugDisplay.GetState(); - debugDisplay.CullOff(); - - debugDisplay.PushMatrix(worldTransform); - debugDisplay.PushMatrix(localTransform); - - const float xAxisArrowLength = 2.0f; - debugDisplay.SetColor(AZ::Color(1.0f, 0.0f, 0.0f, 1.0f)); - debugDisplay.DrawArrow(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(xAxisArrowLength, 0.0f, 0.0f)); - - AngleLimitsFloatPair yzSwingAngleLimits; - EditorJointRequestBus::EventResult( - yzSwingAngleLimits, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , m_name); - - const AZ::u32 numEllipseSamples = 16; - AZ::Vector3 ellipseSamples[numEllipseSamples]; - float coneHeight = ConeHeight; - - // Draw inverted cone if angles are larger than 90 deg. - if (yzSwingAngleLimits.first > 90.0f || yzSwingAngleLimits.second > 90.0f) - { - coneHeight = -ConeHeight; - } - - // Compute points along perimeter of cone base - const float coney = tanf(AZ::DegToRad(yzSwingAngleLimits.first)) * coneHeight; - const float conez = tanf(AZ::DegToRad(yzSwingAngleLimits.second)) * coneHeight; - const float step = AZ::Constants::TwoPi / numEllipseSamples; - for (size_t i = 0; i < numEllipseSamples; ++i) - { - const float angleStep = step * i; - ellipseSamples[i].SetX(coneHeight); - ellipseSamples[i].SetY(conez * sin(angleStep)); - ellipseSamples[i].SetZ(coney * cos(angleStep)); - } - - // draw cone - for (size_t i = 0; i < numEllipseSamples; ++i) - { - size_t nextIndex = i + 1; - if (i == numEllipseSamples - 1) - { - nextIndex = 0; - } - - // draw cone sides - debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, 0.2f)); - debugDisplay.DrawTri(AZ::Vector3(0.0f, 0.0f, 0.0f), ellipseSamples[i], ellipseSamples[nextIndex]); - - // draw parameter of cone base - debugDisplay.SetColor(AZ::Color(0.4f, 0.4f, 0.4f, 0.4f)); - debugDisplay.DrawLine(ellipseSamples[i], ellipseSamples[nextIndex]); - } - - // draw axis lines at base of cone, and from tip to base. - debugDisplay.SetColor(AZ::Color(0.5f, 0.5f, 0.5f, 0.6f)); - debugDisplay.DrawLine(ellipseSamples[0], ellipseSamples[numEllipseSamples/2]); - debugDisplay.DrawLine(ellipseSamples[numEllipseSamples*3/4], ellipseSamples[numEllipseSamples/4]); - debugDisplay.DrawLine(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(coneHeight, 0.0f, 0.0f)); - - debugDisplay.PopMatrix();//pop local transform - debugDisplay.PopMatrix();//pop world transform - debugDisplay.SetState(stateBefore); - - // reposition and reorientate manipulators - Refresh(); - } -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeAngleCone.h b/Gems/PhysX/Code/Editor/EditorSubComponentModeAngleCone.h deleted file mode 100644 index bf113b4be1..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeAngleCone.h +++ /dev/null @@ -1,63 +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 - * - */ -#pragma once - -#include -#include -#include -#include - -namespace AzToolsFramework -{ - class AngularManipulator; - class LinearManipulator; - class PlanarManipulator; -} - -namespace PhysX -{ - class EditorSubComponentModeAngleCone - : public PhysX::EditorSubComponentModeBase - , private AzFramework::EntityDebugDisplayEventBus::Handler - { - public: - EditorSubComponentModeAngleCone( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name - , float max - , float min); - ~EditorSubComponentModeAngleCone(); - - // PhysX::EditorSubComponentModeBase - void Refresh() override; - - private: - // AzFramework::EntityDebugDisplayEventBus - void DisplayEntityViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; - - void ConfigureLinearView( - float axisLength, - const AZ::Color& axis1Color, const AZ::Color& axis2Color, - const AZ::Color& axis3Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)); - - void ConfigurePlanarView(const AZ::Color& planeColor = AZ::Color(0.0f, 1.0f, 0.0f, 0.5f) - , const AZ::Color& plane2Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)); - - AZStd::shared_ptr m_xRotationManipulator; - AZStd::shared_ptr m_yLinearManipulator; - AZStd::shared_ptr m_zLinearManipulator; - AZStd::shared_ptr m_yzPlanarManipulator; - - float m_max = FLT_MAX; - float m_min = 0.0f; - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeAnglePair.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeAnglePair.cpp deleted file mode 100644 index 6fa74a3f05..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeAnglePair.cpp +++ /dev/null @@ -1,318 +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 -#include -#include - -#include -#include -#include - -namespace -{ - const float Alpha = 0.6f; - const AZ::Color ColorDefault = AZ::Color(1.0f, 1.0f, 1.0f, Alpha); - const AZ::Color ColorFirst = AZ::Color(1.0f, 0.0f, 0.0f, Alpha); - const AZ::Color ColorSecond = AZ::Color(0.0f, 1.0f, 0.0f, Alpha); - const AZ::Color ColorSweepArc = AZ::Color(1.0f, 1.0f, 1.0f, Alpha); - - const float SweepLineDisplaceFactor = 0.5f; - const float SweepLineThickness = 1.0f; - const float SweepLineGranularity = 1.0f; -} - -namespace PhysX -{ - EditorSubComponentModeAnglePair::EditorSubComponentModeAnglePair( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name - , const AZ::Vector3& axis - , float firstMax - , float firstMin - , float secondMax - , float secondMin) - : EditorSubComponentModeBase(entityComponentIdPair, componentType, name) - , m_axis(axis) - , m_firstMax(firstMax) - , m_firstMin(firstMin) - , m_secondMax(secondMax) - , m_secondMin(secondMin) - { - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - const AZ::Quaternion localRotation = localTransform.GetRotation(); - - AZ::Vector3 displacement = m_axis; - AZ::Transform displacementTransform = localTransform; - AZ::Vector3 displacementTranslate = localRotation.TransformVector(displacement); - displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); - - m_firstManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform); - m_firstManipulator->AddEntityComponentIdPair(m_entityComponentId); - m_firstManipulator->SetAxis(m_axis); - m_firstManipulator->SetLocalTransform(displacementTransform); - - displacement = -m_axis; - displacementTranslate = localRotation.TransformVector(displacement); - displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); - m_secondManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform); - m_secondManipulator->AddEntityComponentIdPair(m_entityComponentId); - m_secondManipulator->SetAxis(m_axis); - m_secondManipulator->SetLocalTransform(displacementTransform); - - const float manipulatorRadius = 2.0f; - const float manipulatorWidth = 0.05f; - m_firstManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle( - *m_firstManipulator, ColorFirst, - manipulatorRadius, manipulatorWidth, AzToolsFramework::DrawHalfDottedCircle)); - - m_secondManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle( - *m_secondManipulator, ColorSecond, - manipulatorRadius, manipulatorWidth, AzToolsFramework::DrawHalfDottedCircle)); - - Refresh(); - - AZStd::shared_ptr sharedRotationState = - AZStd::make_shared(); - - auto mouseDownCallback = [this, sharedRotationState](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void - { - const AZ::Quaternion normalizedStart = action.m_start.m_rotation.GetNormalized(); - sharedRotationState->m_axis = AZ::Vector3(normalizedStart.GetX(), normalizedStart.GetY(), normalizedStart.GetZ()); - sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity(); - - AngleLimitsFloatPair currentValue; - EditorJointRequestBus::EventResult( - currentValue, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , m_name); - - sharedRotationState->m_valuePair = currentValue; - }; - - m_firstManipulator->InstallLeftMouseDownCallback(mouseDownCallback); - - m_secondManipulator->InstallLeftMouseDownCallback(mouseDownCallback); - - m_firstManipulator->InstallMouseMoveCallback( - [this, sharedRotationState] - (const AzToolsFramework::AngularManipulator::Action& action) mutable -> void - { - float angleDelta; - AZ::Quaternion manipulatorOrientation; - const float newValue = MouseMove(sharedRotationState, action, true, angleDelta, manipulatorOrientation); - if (newValue > m_firstMax || newValue < m_firstMin) - { - return; - } - m_firstManipulator->SetLocalOrientation(manipulatorOrientation); - const float newFirstValue = AZ::GetClamp(sharedRotationState->m_valuePair.first + angleDelta, m_firstMin, m_firstMax); - - EditorJointRequestBus::Event( - m_entityComponentId - , &EditorJointRequests::SetLinearValuePair - , m_name - , AngleLimitsFloatPair(newFirstValue, sharedRotationState->m_valuePair.second)); - - m_firstManipulator->SetBoundsDirty(); - }); - - m_secondManipulator->InstallMouseMoveCallback( - [this, sharedRotationState] - (const AzToolsFramework::AngularManipulator::Action& action) mutable -> void - { - float angleDelta; - AZ::Quaternion manipulatorOrientation; - const float newValue = MouseMove(sharedRotationState, action, false, angleDelta, manipulatorOrientation); - if (newValue > m_secondMax || newValue < m_secondMin) - { - return; //Not handling values exceeding limits - } - - m_secondManipulator->SetLocalOrientation(manipulatorOrientation); - float newSecondValue = AZ::GetClamp(sharedRotationState->m_valuePair.second + angleDelta, m_secondMin, m_secondMax); - - EditorJointRequestBus::Event( - m_entityComponentId - , &EditorJointRequests::SetLinearValuePair - , m_name - , AngleLimitsFloatPair(sharedRotationState->m_valuePair.first, newSecondValue)); - - m_secondManipulator->SetBoundsDirty(); - }); - - - m_firstManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_secondManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - - AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId()); - } - - EditorSubComponentModeAnglePair::~EditorSubComponentModeAnglePair() - { - AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); - - m_firstManipulator->Unregister(); - m_secondManipulator->Unregister(); - } - - void EditorSubComponentModeAnglePair::Refresh() - { - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - const AZ::Quaternion localRotation = localTransform.GetRotation(); - - AZ::Transform displacementTransform = localTransform; - AZ::Vector3 displacement = m_axis; - AZ::Vector3 displacementTranslate = localRotation.TransformVector(displacement); - displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); - m_firstManipulator->SetLocalTransform(displacementTransform); - - displacement = -m_axis; - displacementTranslate = localRotation.TransformVector(displacement); - displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); - m_secondManipulator->SetLocalTransform(displacementTransform); - - m_firstManipulator->SetBoundsDirty(); - m_secondManipulator->SetBoundsDirty(); - } - - void EditorSubComponentModeAnglePair::DisplayEntityViewport( - [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) - { - AngleLimitsFloatPair currentValue; - EditorJointRequestBus::EventResult( - currentValue, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , m_name); - - const float size = 2.0f; - AZ::Vector3 axisPoint = m_axis * size * 0.5f; - - AZStd::array points = { - -axisPoint - , axisPoint - , axisPoint - , -axisPoint - }; - - if (abs(m_axis.GetX() - 1.0f) < FLT_EPSILON) - { - points[2].SetZ(size); - points[3].SetZ(size); - } - else if (abs(m_axis.GetY() - 1.0f) < FLT_EPSILON) - { - points[2].SetX(size); - points[3].SetX(size); - } - else if (abs(m_axis.GetZ() - 1.0f) < FLT_EPSILON) - { - points[2].SetX(size); - points[3].SetX(size); - } - - AZ::u32 stateBefore = debugDisplay.GetState(); - debugDisplay.CullOff(); - debugDisplay.SetAlpha(Alpha); - - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - debugDisplay.PushMatrix(worldTransform); - debugDisplay.PushMatrix(localTransform); - - debugDisplay.SetColor(ColorSweepArc); - - const AZ::Vector3 zeroVector = AZ::Vector3::CreateZero(); - const AZ::Vector3 posPosition = m_axis * SweepLineDisplaceFactor; - const AZ::Vector3 negPosition = -posPosition; - debugDisplay.DrawArc(posPosition, SweepLineThickness, -currentValue.first, currentValue.first, SweepLineGranularity, -m_axis); - debugDisplay.DrawArc(zeroVector, SweepLineThickness, -currentValue.first, currentValue.first, SweepLineGranularity, -m_axis); - debugDisplay.DrawArc(negPosition, SweepLineThickness, -currentValue.first, currentValue.first, SweepLineGranularity, -m_axis); - debugDisplay.DrawArc(posPosition, SweepLineThickness, 0.0f, abs(currentValue.second), SweepLineGranularity, -m_axis); - debugDisplay.DrawArc(zeroVector, SweepLineThickness, 0.0f, abs(currentValue.second), SweepLineGranularity, -m_axis); - debugDisplay.DrawArc(negPosition, SweepLineThickness, 0.0f, abs(currentValue.second), SweepLineGranularity, -m_axis); - - AZ::Quaternion firstRotate = AZ::Quaternion::CreateFromAxisAngle(m_axis, AZ::DegToRad(currentValue.first)); - AZ::Transform firstTM = AZ::Transform::CreateFromQuaternion(firstRotate); - debugDisplay.PushMatrix(firstTM); - debugDisplay.SetColor(ColorFirst); - debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]); - debugDisplay.PopMatrix(); - - AZ::Quaternion secondRotate = AZ::Quaternion::CreateFromAxisAngle(m_axis, AZ::DegToRad(currentValue.second)); - AZ::Transform secondTM = AZ::Transform::CreateFromQuaternion(secondRotate); - debugDisplay.PushMatrix(secondTM); - debugDisplay.SetColor(ColorSecond); - debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]); - debugDisplay.PopMatrix(); - - debugDisplay.SetColor(ColorDefault); - debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]); - - debugDisplay.PopMatrix(); // pop local transform - debugDisplay.PopMatrix(); // pop global transform - debugDisplay.SetState(stateBefore); - - // reposition and reorientate manipulators - Refresh(); - } - - float EditorSubComponentModeAnglePair::MouseMove(AZStd::shared_ptr& sharedRotationState - , const AzToolsFramework::AngularManipulator::Action& action - , bool isFirstValue - , float& angleDelta - , AZ::Quaternion& manipulatorOrientation) - { - sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull(); - angleDelta = 0.0f; - AZ::Vector3 axis = m_axis; - sharedRotationState->m_savedOrientation.ConvertToAxisAngle(axis, angleDelta); - // Polarity of axis is switched by ConvertToAxisAngle call depending on direction of rotation - if (abs(m_axis.GetX() - 1.0f) < FLT_EPSILON) - { - angleDelta = AZ::RadToDeg(angleDelta) * axis.GetX(); - } - else if (abs(m_axis.GetY() - 1.0f) < FLT_EPSILON) - { - angleDelta = AZ::RadToDeg(angleDelta) * axis.GetY(); - } - else if (abs(m_axis.GetZ() - 1.0f) < FLT_EPSILON) - { - angleDelta = AZ::RadToDeg(angleDelta) * axis.GetZ(); - } - - manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; - - if (isFirstValue) - { - return sharedRotationState->m_valuePair.first + angleDelta; - } - else - { - return sharedRotationState->m_valuePair.second + angleDelta; - } - } -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeAnglePair.h b/Gems/PhysX/Code/Editor/EditorSubComponentModeAnglePair.h deleted file mode 100644 index cb96570ebe..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeAnglePair.h +++ /dev/null @@ -1,66 +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 - * - */ -#pragma once - -#include -#include - -#include -#include - -namespace PhysX -{ - class EditorSubComponentModeAnglePair - : public PhysX::EditorSubComponentModeBase - , private AzFramework::EntityDebugDisplayEventBus::Handler - { - public: - EditorSubComponentModeAnglePair( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name - , const AZ::Vector3& axis - , float firstMax - , float firstMin - , float secondMax - , float secondMin); - ~EditorSubComponentModeAnglePair(); - - // PhysX::EditorSubComponentModeBase - void Refresh() override; - - private: - struct SharedRotationState - { - AZ::Vector3 m_axis; - AZ::Quaternion m_savedOrientation = AZ::Quaternion::CreateIdentity(); - AngleLimitsFloatPair m_valuePair; - }; - - // AzFramework::EntityDebugDisplayEventBus - void DisplayEntityViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; - - float MouseMove(AZStd::shared_ptr& sharedRotationState - , const AzToolsFramework::AngularManipulator::Action& action - , bool isFirstValue - , float& angleDelta - , AZ::Quaternion& manipulatorOrientation); - - AZStd::shared_ptr m_firstManipulator; - AZStd::shared_ptr m_secondManipulator; - - AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); - float m_firstMax = FLT_MAX; - float m_firstMin = -FLT_MAX; - float m_secondMax = FLT_MAX; - float m_secondMin = -FLT_MAX; - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeBase.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeBase.cpp deleted file mode 100644 index 1e02d52859..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeBase.cpp +++ /dev/null @@ -1,37 +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 -#include -#include -#include - -namespace PhysX -{ - EditorSubComponentModeBase::EditorSubComponentModeBase( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name) - : m_entityComponentId(entityComponentIdPair), - m_name(name) - { - // The first time this is called, no object will respond to this bus call as no object is connected at the address, - // and m_jointTypeDrawer will remain as a nullptr. - EditorJointTypeDrawerBus::EventResult(m_jointTypeDrawer, - EditorJointTypeDrawerId(componentType, EditorSubComponentModeNameCrc(name)), - &EditorJointTypeDrawerBus::Events::GetEditorJointTypeDrawer); - - if (!m_jointTypeDrawer) - { - // Once this is called, the bus call to GetEditorJointTypeDrawer above will no longer get a null response, until m_jointTypeDrawer is destroyed. - m_jointTypeDrawer = AZStd::make_shared(componentType, - AzToolsFramework::GetEntityContextId(), - m_name); - } - } -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeBase.h b/Gems/PhysX/Code/Editor/EditorSubComponentModeBase.h deleted file mode 100644 index 09e06927f3..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeBase.h +++ /dev/null @@ -1,55 +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 - * - */ -#pragma once - -#include - -namespace AzFramework -{ - class DebugDisplayRequests; - struct ViewportInfo; -} - -namespace AzToolsFramework -{ - namespace ViewportInteraction - { - struct MouseInteractionEvent; - } -} - -namespace PhysX -{ - class EditorJointTypeDrawer; - - /// Base class for (joints) sub-component modes. - class EditorSubComponentModeBase - { - public: - EditorSubComponentModeBase( - const AZ::EntityComponentIdPair& entityComponentIdPair, - const AZ::Uuid& componentType, - const AZStd::string& name); - virtual ~EditorSubComponentModeBase() = default; - - /// Additional mouse handling by sub-component mode. Does not absorb mouse event. - virtual void HandleMouseInteraction( - [[maybe_unused]] const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) {}; - - virtual void Refresh() = 0; - - AZStd::string m_name;///< Name of sub-component mode. - - protected: - AZ::EntityComponentIdPair m_entityComponentId;///< Entity Id and component pair. - - private: - AZStd::shared_ptr m_jointTypeDrawer;///< Drawer that draws component type specific objects in the viewport. - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeLinear.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeLinear.cpp deleted file mode 100644 index 378e1fb982..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeLinear.cpp +++ /dev/null @@ -1,133 +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 -#include - -#include -#include - -#include -#include -#include -#include - -namespace PhysX -{ - EditorSubComponentModeLinear::EditorSubComponentModeLinear( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name - , float exponent - , float max - , float min) - : EditorSubComponentModeBase(entityComponentIdPair, componentType, name) - , m_exponent(exponent) - , m_inverseExponent(1.0f/exponent) - , m_max(max) - , m_min(min) - { - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - m_manipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform); - m_manipulator->AddEntityComponentIdPair(m_entityComponentId); - m_manipulator->SetAxis(AZ::Vector3::CreateAxisX()); - m_manipulator->SetLocalTransform(localTransform); - - Refresh(); - - const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f); - const float manipulatorSize = 0.05f; - - AzToolsFramework::ManipulatorViews views; - views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor - , manipulatorSize)); - m_manipulator->SetViews(AZStd::move(views)); - - struct SharedState - { - float m_startingValue = 0.0f; - }; - auto sharedState = AZStd::make_shared(); - - m_manipulator->InstallLeftMouseDownCallback( - [this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable - { - float currentValue = 0.0f; - - EditorJointRequestBus::EventResult( - currentValue, m_entityComponentId - , &EditorJointRequests::GetLinearValue - , m_name); - sharedState->m_startingValue = currentValue; - }); - - m_manipulator->InstallMouseMoveCallback( - [this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) - { - const float axisDisplacement = action.LocalPositionOffset().Dot(action.m_fixed.m_axis); - - float newValue = AZ::GetClamp(sharedState->m_startingValue + DisplacementToDeltaValue(axisDisplacement), m_min, m_max); - EditorJointRequestBus::Event( - m_entityComponentId - , &EditorJointRequests::SetLinearValue - , m_name - , newValue); - - const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(0.01f, 0.0f, 0.0f)); - m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition)); - m_manipulator->SetBoundsDirty(); - }); - - m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - } - - EditorSubComponentModeLinear::~EditorSubComponentModeLinear() - { - m_manipulator->Unregister(); - } - - void EditorSubComponentModeLinear::Refresh() - { - float currentValue = 0.0f; - - EditorJointRequestBus::EventResult( - currentValue, m_entityComponentId - , &EditorJointRequests::GetLinearValue - , m_name); - - m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * ValueToDisplacement(currentValue))); - } - - float EditorSubComponentModeLinear::DisplacementToDeltaValue(float displacement) const - { - if (displacement > 0.0f) - { - return powf(displacement, m_exponent); - } - else if (displacement < 0.0f) - { - return -powf(fabsf(displacement), m_exponent); - } - else - { - return 0.0f; - } - } - - float EditorSubComponentModeLinear::ValueToDisplacement(float value) const - { - return powf(value, m_inverseExponent); - } -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeLinear.h b/Gems/PhysX/Code/Editor/EditorSubComponentModeLinear.h deleted file mode 100644 index 4d44d4aad4..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeLinear.h +++ /dev/null @@ -1,46 +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 - * - */ -#pragma once - -#include - -namespace AzToolsFramework -{ - class LinearManipulator; -} - -namespace PhysX -{ - class EditorSubComponentModeLinear - : public PhysX::EditorSubComponentModeBase - { - public: - EditorSubComponentModeLinear( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name - , float exponent - , float max - , float min); - ~EditorSubComponentModeLinear(); - - // PhysX::EditorSubComponentModeBase - void Refresh() override; - - private: - float DisplacementToDeltaValue(float displacement) const; - float ValueToDisplacement(float value) const; - - float m_exponent = 1.0f; - float m_inverseExponent = 1.0f; - AZStd::shared_ptr m_manipulator; - float m_max = FLT_MAX; - float m_min = -FLT_MAX; - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeRotation.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeRotation.cpp deleted file mode 100644 index 7586ecc955..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeRotation.cpp +++ /dev/null @@ -1,151 +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 -#include -#include -#include - -#include -#include -#include -#include - -namespace PhysX -{ - EditorSubComponentModeRotation::EditorSubComponentModeRotation( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name) - : EditorSubComponentModeBase(entityComponentIdPair, componentType, name) - { - CreateManipulators(); - RegisterManipulators(); - AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId()); - } - - EditorSubComponentModeRotation::~EditorSubComponentModeRotation() - { - AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); - UnregisterManipulators(); - } - - void EditorSubComponentModeRotation::Refresh() - { - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - for (auto rotationManipulator : m_rotationManipulators) - { - rotationManipulator->SetLocalTransform(localTransform); - rotationManipulator->SetBoundsDirty(); - } - } - - void EditorSubComponentModeRotation::DisplayEntityViewport( - [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, - [[maybe_unused]] AzFramework::DebugDisplayRequests& debugDisplay) - { - Refresh(); //Update position and orientation of manipulators. - } - - void EditorSubComponentModeRotation::CreateManipulators() - { - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - const AZ::Quaternion worldRotation = worldTransform.GetRotation(); - - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - const AZStd::array axes = { AZ::Vector3::CreateAxisX() - , AZ::Vector3::CreateAxisY() - , AZ::Vector3::CreateAxisZ()}; - - const AZStd::array colors = { AZ::Color(1.0f, 0.0f, 0.0f, 1.0f) - , AZ::Color(0.0f, 1.0f, 0.0f, 1.0f) - , AZ::Color(0.0f, 0.0f, 1.0f, 1.0f)}; - - for (AZ::u32 i = 0; i < 3; ++i) - { - m_rotationManipulators[i] = AzToolsFramework::AngularManipulator::MakeShared(worldTransform); - m_rotationManipulators[i]->AddEntityComponentIdPair(m_entityComponentId); - m_rotationManipulators[i]->SetAxis(axes[i]); - m_rotationManipulators[i]->SetLocalTransform(localTransform); - const float manipulatorRadius = 2.0f; - m_rotationManipulators[i]->SetView(AzToolsFramework::CreateManipulatorViewCircle( - *m_rotationManipulators[i], colors[i], manipulatorRadius, - AzToolsFramework::ManipulatorCicleBoundWidth(), AzToolsFramework::DrawHalfDottedCircle)); - } - - Refresh(); - InstallManipulatorMouseCallbacks(); - } - - void EditorSubComponentModeRotation::InstallManipulatorMouseCallbacks() - { - struct SharedState - { - AZ::Transform m_startTM; - }; - auto sharedState = AZStd::make_shared(); - - auto mouseDownRotateXCallback = [this, sharedState]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action) mutable -> void - { - PhysX::EditorJointRequestBus::EventResult(sharedState->m_startTM - , m_entityComponentId - , &PhysX::EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - }; - - for (AZ::u32 index = 0; index < 3; ++index) - { - m_rotationManipulators[index]->InstallLeftMouseDownCallback(mouseDownRotateXCallback); - - m_rotationManipulators[index]->InstallMouseMoveCallback( - [this, index, sharedState] - (const AzToolsFramework::AngularManipulator::Action& action) mutable -> void - { - const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; - - AZ::Transform newTransform = AZ::Transform::CreateIdentity(); - newTransform = sharedState->m_startTM * AZ::Transform::CreateFromQuaternion(action.m_current.m_delta); - - PhysX::EditorJointRequestBus::Event(m_entityComponentId - , &PhysX::EditorJointRequests::SetVector3Value - , PhysX::EditorJointComponentMode::s_parameterRotation - , newTransform.GetRotation().GetEulerDegrees()); - - m_rotationManipulators[index]->SetLocalOrientation(manipulatorOrientation); - m_rotationManipulators[index]->SetBoundsDirty(); - }); - } - } - - void EditorSubComponentModeRotation::RegisterManipulators() - { - for (auto rotationManipulator : m_rotationManipulators) - { - rotationManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - } - } - - void EditorSubComponentModeRotation::UnregisterManipulators() - { - for (auto rotationManipulator : m_rotationManipulators) - { - rotationManipulator->Unregister(); - } - } -} diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeRotation.h b/Gems/PhysX/Code/Editor/EditorSubComponentModeRotation.h deleted file mode 100644 index 349738909a..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeRotation.h +++ /dev/null @@ -1,48 +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 - * - */ -#pragma once - -#include -#include - -namespace AzToolsFramework -{ - class AngularManipulator; -} - -namespace PhysX -{ - class EditorSubComponentModeRotation - : public PhysX::EditorSubComponentModeBase - , private AzFramework::EntityDebugDisplayEventBus::Handler - { - public: - EditorSubComponentModeRotation( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name); - ~EditorSubComponentModeRotation(); - - // PhysX::EditorSubComponentModeBase - void Refresh() override; - - private: - // AzFramework::EntityDebugDisplayEventBus - void DisplayEntityViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; - - void CreateManipulators(); - void InstallManipulatorMouseCallbacks(); - void RegisterManipulators(); - void UnregisterManipulators(); - - AZStd::array, 3> m_rotationManipulators; - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapPosition.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapPosition.cpp deleted file mode 100644 index b086eb64ff..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapPosition.cpp +++ /dev/null @@ -1,97 +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 -#include -#include -#include - -#include -#include -#include -#include - -namespace PhysX -{ - EditorSubComponentModeSnapPosition::EditorSubComponentModeSnapPosition( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name - , bool selectLeadOnSnap) - : EditorSubComponentModeSnap(entityComponentIdPair, componentType, name) - , m_selectLeadOnSnap(selectLeadOnSnap) - { - InitMouseDownCallBack(); - m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId()); - } - - EditorSubComponentModeSnapPosition::~EditorSubComponentModeSnapPosition() - { - AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); - m_manipulator->Unregister(); - } - - void EditorSubComponentModeSnapPosition::DisplaySpecificSnapType( - [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay, - const AZ::Vector3& jointPosition, - const AZ::Vector3& snapDirection, - const float snapLength) - { - const float arrowLength = 1.0f; - const float iconGap = 1.0f; - const AZ::Vector3 iconPosition = jointPosition + - (snapDirection * (snapLength + arrowLength + iconGap)); - - debugDisplay.SetColor(AZ::Colors::Red); - debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(arrowLength, 0.0f, 0.0f)); - debugDisplay.SetColor(AZ::Colors::Green); - debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(0.2f, arrowLength, 0.2f)); - debugDisplay.SetColor(AZ::Colors::Blue); - debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(0.0f, 0.0f, arrowLength)); - } - - void EditorSubComponentModeSnapPosition::InitMouseDownCallBack() - { - m_manipulator->InstallLeftMouseDownCallback( - [this](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable - { - if (!m_pickedEntity.IsValid()) - { - return; - } - - const AZ::Vector3 newLocalPosition = PhysX::Utils::ComputeJointLocalTransform( - PhysX::Utils::GetEntityWorldTransformWithScale(m_pickedEntity), - PhysX::Utils::GetEntityWorldTransformWithScale(m_entityComponentId.GetEntityId())).GetTranslation(); - - PhysX::EditorJointRequestBus::Event(m_entityComponentId - , &PhysX::EditorJointRequests::SetVector3Value - , PhysX::EditorJointComponentMode::s_parameterPosition - , newLocalPosition); - - const bool selectedEntityIsNotJointEntity = m_pickedEntity != m_entityComponentId.GetEntityId(); - - AZ_Error("EditorSubComponentModeSnapPosition", - selectedEntityIsNotJointEntity, - "Joint's lead entity cannot be the same as the entity in which the joint resides. Select lead entity on snap failed."); - - if (m_selectLeadOnSnap && - selectedEntityIsNotJointEntity) - { - PhysX::EditorJointRequestBus::Event(m_entityComponentId - , &PhysX::EditorJointRequests::SetEntityIdValue - , PhysX::EditorJointComponentMode::s_parameterLeadEntity - , m_pickedEntity); - } - - m_manipulator->SetBoundsDirty(); - }); - } -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapPosition.h b/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapPosition.h deleted file mode 100644 index d2fe803431..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapPosition.h +++ /dev/null @@ -1,42 +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 - * - */ -#pragma once - -#include - -namespace PhysX -{ - /// This sub-component mode gets an entity position from its base class on mouse down - /// and sets a position (AZ::Vector3) value in the component that uses it. - class EditorSubComponentModeSnapPosition - : public EditorSubComponentModeSnap - { - public: - EditorSubComponentModeSnapPosition( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name - , bool selectLeadOnSnap); - ~EditorSubComponentModeSnapPosition() override; - - protected: - // PhysX::EditorSubComponentModeSnap - void DisplaySpecificSnapType( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay, - const AZ::Vector3& jointPosition, - const AZ::Vector3& snapDirection, - float snapLength) override; - - void InitMouseDownCallBack() override; - - private: - bool m_selectLeadOnSnap = true; - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.cpp deleted file mode 100644 index c68e058e29..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.cpp +++ /dev/null @@ -1,122 +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 -#include -#include - -#include -#include -#include -#include - -namespace PhysX -{ - EditorSubComponentModeSnapRotation::EditorSubComponentModeSnapRotation( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name) - : EditorSubComponentModeSnap(entityComponentIdPair, componentType, name) - { - InitMouseDownCallBack(); - m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId()); - } - - EditorSubComponentModeSnapRotation::~EditorSubComponentModeSnapRotation() - { - AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); - m_manipulator->Unregister(); - } - - void EditorSubComponentModeSnapRotation::DisplaySpecificSnapType( - [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay, - const AZ::Vector3& jointPosition, - const AZ::Vector3& snapDirection, - const float snapLength) - { - const float circleRadius = 0.5f; - const float iconGap = 1.0f; - - const AZ::Vector3 iconPosition = jointPosition + - (snapDirection * (snapLength + circleRadius * 2.0f + iconGap)); - - debugDisplay.SetColor(AZ::Colors::Red); - debugDisplay.DrawCircle(iconPosition, circleRadius, 0); - debugDisplay.SetColor(AZ::Colors::Green); - debugDisplay.DrawCircle(iconPosition, circleRadius, 1); - debugDisplay.SetColor(AZ::Colors::Blue); - debugDisplay.DrawCircle(iconPosition, circleRadius, 2); - } - - void EditorSubComponentModeSnapRotation::InitMouseDownCallBack() - { - m_manipulator->InstallLeftMouseDownCallback( - [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action) mutable - { - if (!m_pickedEntity.IsValid()) - { - return; - } - - AZ::EntityId leadEntityId; - PhysX::EditorJointRequestBus::EventResult(leadEntityId, - m_entityComponentId, - &PhysX::EditorJointRequests::GetEntityIdValue, - PhysX::EditorJointComponentMode::s_parameterLeadEntity); - - if (leadEntityId.IsValid() && m_pickedEntity == leadEntityId) - { - AZ_Warning("EditorsubComponentModeSnapRotation", - false, - "The entity %s is the lead of the joint. Please snap rotation (or orientation) of joint to another entity that is not the lead entity.", - GetPickedEntityName().c_str()); - return; - } - - AZ::Transform worldTransform = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldTransform, m_entityComponentId.GetEntityId(), &AZ::TransformInterface::GetWorldTM); - worldTransform.ExtractUniformScale(); - - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - AZ::Transform pickedEntityTransform = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - pickedEntityTransform, m_pickedEntity, &AZ::TransformInterface::GetWorldTM); - - const AZ::Transform worldTransformInv = worldTransform.GetInverse(); - const AZ::Vector3 pickedLocalPosition = worldTransformInv.TransformVector(pickedEntityTransform.GetTranslation()) - localTransform.GetTranslation(); - - if (abs(pickedLocalPosition.GetLength()) < FLT_EPSILON) - { - AZ_Warning("EditorsubComponentModeSnapRotation", - false, - "The entity %s is too close to the joint position. Please snap rotation to an entity that is not at the position of the joint.", - GetPickedEntityName().c_str()); - return; - } - - const AZ::Vector3 targetDirection = pickedLocalPosition.GetNormalized(); - const AZ::Vector3 sourceDirection = AZ::Vector3::CreateAxisX(); - const AZ::Quaternion newLocalRotation = AZ::Quaternion::CreateShortestArc(sourceDirection, targetDirection); - - PhysX::EditorJointRequestBus::Event(m_entityComponentId - , &PhysX::EditorJointRequests::SetVector3Value - , PhysX::EditorJointComponentMode::s_parameterRotation //using rotation parameter name to set the local rotation value - , newLocalRotation.GetEulerDegrees()); - - m_manipulator->SetBoundsDirty(); - }); - } -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.h b/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.h deleted file mode 100644 index 06d41df206..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.h +++ /dev/null @@ -1,38 +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 - * - */ -#pragma once - -#include - -namespace PhysX -{ - /// TThis sub-component mode gets an entity position from its base class on mouse down - /// and sets a rotation (AZ::Quaternion) value in the component that uses it. - class EditorSubComponentModeSnapRotation - : public EditorSubComponentModeSnap - { - public: - EditorSubComponentModeSnapRotation( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name); - ~EditorSubComponentModeSnapRotation() override; - - protected: - // PhysX::EditorSubComponentModeSnap - void DisplaySpecificSnapType( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay, - const AZ::Vector3& jointPosition, - const AZ::Vector3& snapDirection, - float snapLength) override; - - void InitMouseDownCallBack() override; - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeVec3.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeVec3.cpp deleted file mode 100644 index 7e8affb086..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeVec3.cpp +++ /dev/null @@ -1,91 +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 -#include -#include - -#include -#include -#include - -namespace PhysX -{ - EditorSubComponentModeVec3::EditorSubComponentModeVec3( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name) - : EditorSubComponentModeBase(entityComponentIdPair, componentType, name) - , m_translationManipulators(AzToolsFramework::TranslationManipulators::Dimensions::Three, - AZ::Transform::Identity(), AZ::Vector3::CreateOne()) - { - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - AZ::Vector3 localTranslation; - EditorJointRequestBus::EventResult( - localTranslation, m_entityComponentId - , &EditorJointRequests::GetVector3Value - , m_name); - - m_translationManipulators.SetSpace(worldTransform); - m_translationManipulators.SetLocalPosition(localTranslation); - m_translationManipulators.AddEntityComponentIdPair(m_entityComponentId); - - m_translationManipulators.Register(AzToolsFramework::g_mainManipulatorManagerId); - - AzToolsFramework::ConfigureTranslationManipulatorAppearance3d(&m_translationManipulators); - - m_translationManipulators.InstallLinearManipulatorMouseMoveCallback([this]( - const AzToolsFramework::LinearManipulator::Action& action) - { - OnManipulatorMoved(action.LocalPosition()); - }); - - m_translationManipulators.InstallPlanarManipulatorMouseMoveCallback([this]( - const AzToolsFramework::PlanarManipulator::Action& action) - { - OnManipulatorMoved(action.LocalPosition()); - }); - - m_translationManipulators.InstallSurfaceManipulatorMouseMoveCallback([this]( - const AzToolsFramework::SurfaceManipulator::Action& action) - { - OnManipulatorMoved(action.LocalPosition()); - }); - } - - EditorSubComponentModeVec3::~EditorSubComponentModeVec3() - { - m_translationManipulators.Unregister(); - } - - void EditorSubComponentModeVec3::Refresh() - { - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - AZ::Vector3 localTranslation; - EditorJointRequestBus::EventResult( - localTranslation, m_entityComponentId - , &EditorJointRequests::GetVector3Value - , m_name); - - m_translationManipulators.SetSpace(worldTransform); - m_translationManipulators.SetLocalPosition(localTranslation); - m_translationManipulators.SetBoundsDirty(); - } - - void EditorSubComponentModeVec3::OnManipulatorMoved(const AZ::Vector3& position) - { - m_translationManipulators.SetLocalPosition(position); - EditorJointRequestBus::Event( - m_entityComponentId - , &EditorJointRequests::SetVector3Value - , m_name - , position); - } -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeVec3.h b/Gems/PhysX/Code/Editor/EditorSubComponentModeVec3.h deleted file mode 100644 index 849125eb1c..0000000000 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeVec3.h +++ /dev/null @@ -1,34 +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 - * - */ -#pragma once - -#include -#include - -namespace PhysX -{ - class EditorSubComponentModeVec3 - : public PhysX::EditorSubComponentModeBase - { - public: - EditorSubComponentModeVec3( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name); - ~EditorSubComponentModeVec3(); - - // PhysX::EditorSubComponentModeBase - void Refresh() override; - - private: - void OnManipulatorMoved(const AZ::Vector3& position); - - AzToolsFramework::TranslationManipulators m_translationManipulators; - }; -} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentMode.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentMode.cpp new file mode 100644 index 0000000000..ce95e27db3 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentMode.cpp @@ -0,0 +1,601 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace PhysX +{ + AZ_CLASS_ALLOCATOR_IMPL(JointsComponentMode, AZ::SystemAllocator, 0); + + namespace SubModeData + { + const AZ::Crc32 SwitchToTranslationSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtotranslationsubmode"); + static const char* TranslationTitle = "Switch to Position Mode"; + static const char* TranslationToolTip = "Position Mode - Change the position of the joint."; + + const AZ::Crc32 SwitchToRotationSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtorotationsubmode"); + static const char* RotationTitle = "Switch to Rotation Mode"; + static const char* RotationToolTip = "Rotation Mode- Change the rotation of the joint."; + + const AZ::Crc32 SwitchToMaxForceSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtomaxforce"); + static const char* MaxForceTitle = "Switch to Max Force Mode"; + static const char* MaxForceToolTip = "Max Force Mode - Change the maximum force allowed before the joint breaks."; + + const AZ::Crc32 SwitchToMaxTorqueSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtomaxtorque"); + static const char* MaxTorqueTitle = "Switch to Max Torque Mode"; + static const char* MaxTorqueToolTip = "Max Torque Mode - Change the maximum torque allowed before the joint breaks."; + + const AZ::Crc32 SwitchToDampingSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtodamping"); + static const char* DampingTitle = "Switch to Damping Mode"; + static const char* DampingToolTip = "Damping Mode - Change the damping strength of the joint when beyond the limit."; + + const AZ::Crc32 SwitchToStiffnessSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtostiffness"); + static const char* StiffnessTitle = "Switch to Stiffness Mode"; + static const char* StiffnessToolTip = "Stiffness Mode - Change the stiffness strength of the joint when beyond the limit."; + + const AZ::Crc32 SwitchToTwistLimitsSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtotwistlimits"); + static const char* TwistLimitsTitle = "Switch to Twist Limits Mode"; + static const char* TwistLimitsToolTip = "Twist Limits Mode - Change the limits of the joint."; + + const AZ::Crc32 SwitchToSwingLimitsSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtoswinglimits"); + static const char* SwingLimitsTitle = "Switch to Swing Limits Mode"; + static const char* SwingLimitsToolTip = "Swing Limits Mode - Change the limits of the joint."; + + const AZ::Crc32 SwitchToSnapPositionSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtosnapposition"); + static const char* SnapPositionTitle = "Switch to Snap Position Mode"; + static const char* SnapPositionToolTip = "Snap Position Mode - Snap the position of the joint to another Entity."; + + const AZ::Crc32 SwitchToSnapRotationSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtosnaprotation"); + static const char* SnapRotationTitle = "Switch to Snap Rotation Mode"; + static const char* SnapRotationToolTip = "Snap Rotation Mode - Snap the rotation of the joint toward another Entity."; + + const AZ::Crc32 ResetSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.resetsubmode"); + static const char* ResetTitle = "Reset Current Mode"; + static const char* ResetToolTip = "Reset changes made during this mode edit."; + + } // namespace ActionData + + namespace Internal + { + static AzToolsFramework::ViewportUi::ButtonId RegisterClusterButton( + AzToolsFramework::ViewportUi::ClusterId clusterId, const char* iconName, const char* tooltip) + { + AzToolsFramework::ViewportUi::ButtonId buttonId; + AzToolsFramework::ViewportUi::ViewportUiRequestBus::EventResult( + buttonId, AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::CreateClusterButton, clusterId, + AZStd::string::format(":/stylesheet/img/UI20/toolbar/%s.svg", iconName)); + + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, clusterId, buttonId, tooltip); + + return buttonId; + } + + void RefreshUI() + { + // The reason this is in a free function is because ColliderComponentMode + // privately inherits from ToolsApplicationNotificationBus. Trying to invoke + // the bus inside the class scope causes the compiler to complain it's not accessible + // to due private inheritance. + // Using the global namespace operator :: should have fixed that, except there + // is a bug in the Microsoft compiler meaning it doesn't work. So this is a work around. + AzToolsFramework::ToolsApplicationNotificationBus::Broadcast( + &AzToolsFramework::ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values); + } + } // namespace Internal + + JointsComponentMode::JointsComponentMode(const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType) + : AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode(entityComponentIdPair, componentType) + { + m_modeSelectionClusterIds.assign(static_cast(ClusterGroups::GroupCount), AzToolsFramework::ViewportUi::InvalidClusterId); + SetupSubModes(entityComponentIdPair); + + EditorJointRequestBus::Event( + entityComponentIdPair, &EditorJointRequests::SetBoolValue, JointsComponentModeCommon::ParamaterNames::ComponentMode, true); + } + + JointsComponentMode::~JointsComponentMode() + { + EditorJointRequestBus::Event( + GetEntityComponentIdPair(), &EditorJointRequests::SetBoolValue, JointsComponentModeCommon::ParamaterNames::ComponentMode, + false); + + TeardownSubModes(); + m_subModes[m_subMode]->Teardown(GetEntityComponentIdPair()); + } + + void JointsComponentMode::Refresh() + { + m_subModes[m_subMode]->Refresh(GetEntityComponentIdPair()); + } + + AZStd::vector JointsComponentMode::PopulateActionsImpl() + { + const AZ::EntityComponentIdPair entityComponentIdPair = GetEntityComponentIdPair(); + + AZStd::vector subModesState; + EditorJointRequestBus::EventResult(subModesState, entityComponentIdPair, &EditorJointRequests::GetSubComponentModesState); + + auto makeActionOverride = [](const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Crc32& actionUri, + const QString& title, const QString& tip,const AZStd::function&& callback) + { + AzToolsFramework::ActionOverride actionOverride; + actionOverride.SetTitle(title); + actionOverride.SetTip(tip); + actionOverride.SetUri(actionUri); + actionOverride.SetEntityComponentIdPair(entityComponentIdPair); + actionOverride.SetCallback(callback); + return actionOverride; + }; + + AZStd::vector actions; + + // translation action + { + AzToolsFramework::ActionOverride translateAction = makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToTranslationSubMode, + SubModeData::TranslationTitle, SubModeData::TranslationToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::Translation, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Translation]); + }); + translateAction.SetKeySequence(QKeySequence(Qt::Key_1)); + actions.emplace_back(translateAction); + } + + // rotation action + { + AzToolsFramework::ActionOverride rotationAction = makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToRotationSubMode, + SubModeData::RotationTitle, SubModeData::RotationToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::Rotation, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Rotation]); + }); + rotationAction.SetKeySequence(QKeySequence(Qt::Key_2)); + actions.emplace_back(rotationAction); + } + + //setup action for other enabled options + for (auto [modeType, parameterString] : subModesState) + { + switch (modeType) + { + case JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce: + { + actions.emplace_back(makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToMaxForceSubMode, + SubModeData::MaxForceTitle, SubModeData::MaxForceToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce]); + })); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque: + { + actions.emplace_back(makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToMaxTorqueSubMode, + SubModeData::MaxTorqueTitle, SubModeData::MaxTorqueToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque]); + })); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::Damping: + { + actions.emplace_back(makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToDampingSubMode, + SubModeData::DampingTitle, SubModeData::DampingToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::Damping, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Damping]); + })); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness: + { + actions.emplace_back(makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToStiffnessSubMode, + SubModeData::StiffnessTitle, SubModeData::StiffnessToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness]); + })); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits: + { + actions.emplace_back(makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToTwistLimitsSubMode, + SubModeData::TwistLimitsTitle, SubModeData::TwistLimitsToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits]); + })); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits: + { + actions.emplace_back(makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToSwingLimitsSubMode, + SubModeData::SwingLimitsTitle, SubModeData::SwingLimitsToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits]); + })); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition: + { + actions.emplace_back(makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToSnapPositionSubMode, + SubModeData::SnapPositionTitle, SubModeData::SnapPositionToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition]); + })); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation: + { + actions.emplace_back(makeActionOverride( + entityComponentIdPair, SubModeData::SwitchToSnapRotationSubMode, + SubModeData::SnapRotationTitle, SubModeData::SnapRotationToolTip, + [this]() + { + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation]); + })); + } + break; + } + } + + // reset values + { + AzToolsFramework::ActionOverride resetValuesAction = makeActionOverride( + entityComponentIdPair, SubModeData::ResetSubMode, + SubModeData::ResetTitle, SubModeData::ResetToolTip, + [this]() + { + ResetCurrentMode(); + }); + resetValuesAction.SetKeySequence(QKeySequence(Qt::Key_R)); + actions.emplace_back(resetValuesAction); + } + + return actions; + } + + AZStd::vector JointsComponentMode::PopulateViewportUiImpl() + { + return AZStd::vector(m_modeSelectionClusterIds.begin(), m_modeSelectionClusterIds.end()); + } + + void JointsComponentMode::SetCurrentMode(JointsComponentModeCommon::SubComponentModes::ModeType newMode, ButtonData& buttonData) + { + + if (auto subMode = m_subModes.find(newMode); + subMode != m_subModes.end()) + { + const AZ::EntityComponentIdPair entityComponentIdPair = GetEntityComponentIdPair(); + m_subModes[m_subMode]->Teardown(entityComponentIdPair); + m_subMode = newMode; + subMode->second->Setup(entityComponentIdPair); + + // if this button is on a different cluster. clear the active state. + if (m_activeButton.m_clusterId != buttonData.m_clusterId) + { + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::ClearClusterActiveButton, m_activeButton.m_clusterId); + } + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, buttonData.m_clusterId, + buttonData.m_buttonId); + m_activeButton = buttonData; + } + else + { + AZ_Assert(false, "PhysXJoints Uninitialized joint component mode selected."); + } + } + + bool JointsComponentMode::HandleMouseInteraction(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) + { + // Propagate mouse interaction to sub-component mode. + if (m_subModes[m_subMode]) + { + m_subModes[m_subMode]->HandleMouseInteraction(mouseInteraction); + } + + return false; + } + + void JointsComponentMode::SetupSubModes(const AZ::EntityComponentIdPair& entityComponentIdPair) + { + //create the 3 cluster groups + for (auto& clusterId : m_modeSelectionClusterIds) + { + AzToolsFramework::ViewportUi::ViewportUiRequestBus::EventResult( + clusterId, AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::CreateCluster, + AzToolsFramework::ViewportUi::Alignment::TopLeft); + } + + //retrieve the enabled sub components from the entity + AZStd::vector subModesState; + EditorJointRequestBus::EventResult(subModesState, entityComponentIdPair, &EditorJointRequests::GetSubComponentModesState); + + const AzToolsFramework::ViewportUi::ClusterId group1ClusterId = GetClusterId(ClusterGroups::Group1); + const AzToolsFramework::ViewportUi::ClusterId group2ClusterId = GetClusterId(ClusterGroups::Group2); + //hide cluster 2, if something is added to it. it will make is visible + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, + group2ClusterId, false); + + const AzToolsFramework::ViewportUi::ClusterId group3ClusterId = GetClusterId(ClusterGroups::Group3); + // hide cluster 3, if something is added to it. it will make is visible + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, + group3ClusterId, false); + + //translation and rotation are enabled for all joints in group 1 + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Translation] = + AZStd::make_unique(); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Translation] = + ButtonData{ group1ClusterId, Internal::RegisterClusterButton(group1ClusterId, "Move", SubModeData::TranslationToolTip) }; + + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Rotation] = AZStd::make_unique(); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Rotation] = + ButtonData{ group1ClusterId, Internal::RegisterClusterButton(group1ClusterId, "Rotate", SubModeData::RotationTitle) }; + + //some constants for some modes + const float ExponentBreakage = 1.0f; + const float ExponentSpring = 2.0f; + + //setup the remaining modes if they're in the enabled list. + for (auto [modeType, parameterString] : subModesState) + { + switch (modeType) + { + case JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce: + { + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce] = + AZStd::make_unique( + parameterString, ExponentBreakage, EditorJointConfig::s_breakageMax, EditorJointConfig::s_breakageMin); + + const AzToolsFramework::ViewportUi::ButtonId buttonId = + Internal::RegisterClusterButton(group3ClusterId, "joints/MaxForce", SubModeData::MaxForceToolTip); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce] = + ButtonData{ group3ClusterId, buttonId }; + + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group3ClusterId, true); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque: + { + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque] = + AZStd::make_unique( + parameterString, ExponentBreakage, EditorJointConfig::s_breakageMax, EditorJointConfig::s_breakageMin); + + const AzToolsFramework::ViewportUi::ButtonId buttonId = + Internal::RegisterClusterButton(group3ClusterId, "joints/MaxTorque", SubModeData::MaxTorqueToolTip); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque] = + ButtonData{ group3ClusterId, buttonId }; + + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group3ClusterId, true); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::Damping: + { + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Damping] = + AZStd::make_unique( + parameterString, ExponentSpring, EditorJointLimitBase::s_springMax, EditorJointLimitBase::s_springMin); + + const AzToolsFramework::ViewportUi::ButtonId buttonId = + Internal::RegisterClusterButton(group2ClusterId, "joints/Damping", SubModeData::DampingToolTip); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Damping] = ButtonData{ group2ClusterId, buttonId }; + + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group2ClusterId, true); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness: + { + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness] = + AZStd::make_unique( + parameterString, ExponentSpring, EditorJointLimitBase::s_springMax, EditorJointLimitBase::s_springMin); + + const AzToolsFramework::ViewportUi::ButtonId buttonId = + Internal::RegisterClusterButton(group2ClusterId, "joints/Stiffness", SubModeData::StiffnessToolTip); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness] = + ButtonData{ group2ClusterId, buttonId }; + + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group2ClusterId, true); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits: + { + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits] = + AZStd::make_unique( + parameterString, + AZ::Vector3::CreateAxisX(), // PhysX revolute joints uses the x-axis by default + EditorJointLimitPairConfig::s_angleMax, EditorJointLimitPairConfig::s_angleMin); + + const AzToolsFramework::ViewportUi::ButtonId buttonId = + Internal::RegisterClusterButton(group2ClusterId, "joints/TwistLimits", SubModeData::TwistLimitsToolTip); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits] = + ButtonData{ group2ClusterId, buttonId }; + + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group2ClusterId, true); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits: + { + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits] = + AZStd::make_unique( + parameterString, EditorJointLimitPairConfig::s_angleMax, EditorJointLimitPairConfig::s_angleMin); + + const AzToolsFramework::ViewportUi::ButtonId buttonId = + Internal::RegisterClusterButton(group2ClusterId, "joints/SwingLimits", SubModeData::SwingLimitsToolTip); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits] = + ButtonData{ group2ClusterId, buttonId }; + + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group2ClusterId, true); + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition: + { + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition] = + AZStd::make_unique(); + + const AzToolsFramework::ViewportUi::ButtonId buttonId = + Internal::RegisterClusterButton(group1ClusterId, "joints/SnapPosition", SubModeData::SnapPositionToolTip); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition] = + ButtonData{ group1ClusterId, buttonId }; + } + break; + case JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation: + { + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation] = + AZStd::make_unique(); + + const AzToolsFramework::ViewportUi::ButtonId buttonId = + Internal::RegisterClusterButton(group1ClusterId, "joints/SnapRotation", SubModeData::SnapRotationToolTip); + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation] = + ButtonData{ group1ClusterId, buttonId }; + } + break; + } + } + + //register click handler for the buttons + m_modeSelectionHandlers.push_back(AZ::Event::Handler( + [this](AzToolsFramework::ViewportUi::ButtonId buttonId) + { + for (auto itr : m_buttonData) + { + if (itr.second.m_clusterId == GetClusterId(ClusterGroups::Group1) && itr.second.m_buttonId == buttonId) + { + SetCurrentMode(itr.first, itr.second); + break; + } + } + })); + m_modeSelectionHandlers.push_back(AZ::Event::Handler( + [this](AzToolsFramework::ViewportUi::ButtonId buttonId) + { + for (auto itr : m_buttonData) + { + if (itr.second.m_clusterId == GetClusterId(ClusterGroups::Group2) && itr.second.m_buttonId == buttonId) + { + SetCurrentMode(itr.first, itr.second); + break; + } + } + })); + m_modeSelectionHandlers.push_back(AZ::Event::Handler( + [this](AzToolsFramework::ViewportUi::ButtonId buttonId) + { + for (auto itr : m_buttonData) + { + if (itr.second.m_clusterId == GetClusterId(ClusterGroups::Group3) && itr.second.m_buttonId == buttonId) + { + SetCurrentMode(itr.first, itr.second); + break; + } + } + })); + + for (int i = 0; i < static_cast(ClusterGroups::GroupCount); i++) + { + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, + &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, m_modeSelectionClusterIds[i], + m_modeSelectionHandlers[i]); + } + + // set the translate as enabled by default. + SetCurrentMode( + JointsComponentModeCommon::SubComponentModes::ModeType::Translation, + m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Translation]); + + m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Translation]->Setup(GetEntityComponentIdPair()); + m_subMode = JointsComponentModeCommon::SubComponentModes::ModeType::Translation; + } + + void JointsComponentMode::ResetCurrentMode() + { + const AZ::EntityComponentIdPair entityComponentIdPair = GetEntityComponentIdPair(); + m_subModes[m_subMode]->ResetValues(entityComponentIdPair); + m_subModes[m_subMode]->Refresh(entityComponentIdPair); + + Internal::RefreshUI(); + } + + void JointsComponentMode::TeardownSubModes() + { + for (auto clusterid : m_modeSelectionClusterIds) + { + AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event( + AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::RemoveCluster, + clusterid); + } + } + + AzToolsFramework::ViewportUi::ClusterId JointsComponentMode::GetClusterId(ClusterGroups group) + { + return m_modeSelectionClusterIds[static_cast(group)]; + } +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentMode.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentMode.h new file mode 100644 index 0000000000..812bc71ec1 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentMode.h @@ -0,0 +1,79 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace AZ +{ + class EntityComponentIdPair; +} + +namespace PhysX +{ + //! Class responsible for managing component mode for joints. + class JointsComponentMode final + : public AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode + { + public: + AZ_CLASS_ALLOCATOR_DECL; + + JointsComponentMode(const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType); + ~JointsComponentMode(); + + // EditorBaseComponentMode ... + void Refresh() override; + AZStd::vector PopulateActionsImpl() override; + AZStd::vector PopulateViewportUiImpl() override; + private: + //! Used to identify the group of component modes. + enum class ClusterGroups + { + Group1 = 0, //!< Position Joint, Rotate Joint, Snap Position, Snap Rotation. + Group2, //!< Damping, Stiffness, Twist Limits, Swing Limits. + Group3, //!< Max Force, Max Torque. + + GroupCount + }; + + //! Used to track the cluster that a specific button is apart of. + struct ButtonData + { + AzToolsFramework::ViewportUi::ClusterId m_clusterId; + AzToolsFramework::ViewportUi::ButtonId m_buttonId; + }; + + void SetCurrentMode(JointsComponentModeCommon::SubComponentModes::ModeType newMode, ButtonData& buttonData); + bool HandleMouseInteraction(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; + void SetupSubModes(const AZ::EntityComponentIdPair& entityComponentIdPair); + void ResetCurrentMode(); + void TeardownSubModes(); + + AzToolsFramework::ViewportUi::ClusterId GetClusterId(ClusterGroups group); + + AZStd::fixed_vector(ClusterGroups::GroupCount)> m_modeSelectionClusterIds; //!< List of the cluster ui's. The sub modes are split across 3 groups and each group is it's own cluster ui. + AZStd::unordered_map m_buttonData; //!< Mapping of joint component modes to the button data. + + AZStd::fixed_vector::Handler, static_cast(ClusterGroups::GroupCount)> + m_modeSelectionHandlers; //!< Input handlers for each cluster UI. + ButtonData m_activeButton; //!< The current highlighted button data. + + JointsComponentModeCommon::SubComponentModes::ModeType m_subMode = JointsComponentModeCommon::SubComponentModes::ModeType::Translation; //!< The current component mode that is active. + AZStd::unordered_map> m_subModes; //!< The logic handlers for each component mode. + }; +} diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.cpp new file mode 100644 index 0000000000..a400745c51 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.cpp @@ -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 + * + */ + +#include + +namespace PhysX::JointsComponentModeCommon +{ + const AZStd::string_view ParamaterNames::TwistLimits = "Twist Limits"; + const AZStd::string_view ParamaterNames::Damping = "Damping"; + const AZStd::string_view ParamaterNames::MaxForce = "Maximum Force"; + const AZStd::string_view ParamaterNames::MaxTorque = "Maximum Torque"; + const AZStd::string_view ParamaterNames::Position = "Position"; + const AZStd::string_view ParamaterNames::Rotation = "Rotation"; + const AZStd::string_view ParamaterNames::SnapPosition = "Snap Position"; + const AZStd::string_view ParamaterNames::SnapRotation = "Snap Rotation"; + const AZStd::string_view ParamaterNames::Stiffness = "Stiffness"; + const AZStd::string_view ParamaterNames::SwingLimit = "Swing Limits"; + const AZStd::string_view ParamaterNames::Transform = "Transform"; + const AZStd::string_view ParamaterNames::ComponentMode = "Component Mode"; + const AZStd::string_view ParamaterNames::LeadEntity = "Lead Entity"; + +} // namespace PhysX::JointsComponentModeCommon diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h new file mode 100644 index 0000000000..3deb3b7e3f --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h @@ -0,0 +1,70 @@ +/* + * 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 +#include +#include +#include + +namespace PhysX::JointsComponentModeCommon +{ + namespace SubComponentModes + { + //! Used to identify a specific sub component mode used with joints + enum class ModeType : AZ::u32 + { + Translation = 0, + Rotation, + MaxForce, + MaxTorque, + Damping, + Stiffness, + TwistLimits, + SwingLimits, + SnapPosition, + SnapRotation, + + ModeCount + }; + + //! Shared data structure used with Angle Cone and Angle Pair sub component modes. + //! Holds information about the axis and limits of the angle. + struct AngleModesSharedRotationState + { + AZ::Vector3 m_axis; //!< The Axis of rotation to apply the limits. + AZ::Quaternion m_savedOrientation = AZ::Quaternion::CreateIdentity(); //!< The angle delta of the last mouse action. + PhysX::AngleLimitsFloatPair m_valuePair; //!< the current limits of the angle. + }; + } // namespace SubComponentModes + + //! Name Identifiers for the joint components sub modes. + struct ParamaterNames + { + static const AZStd::string_view TwistLimits; + static const AZStd::string_view Damping; + static const AZStd::string_view MaxForce; + static const AZStd::string_view MaxTorque; + static const AZStd::string_view Position; + static const AZStd::string_view Rotation; + static const AZStd::string_view SnapPosition; + static const AZStd::string_view SnapRotation; + static const AZStd::string_view Stiffness; + static const AZStd::string_view SwingLimit; + static const AZStd::string_view Transform; + static const AZStd::string_view ComponentMode; + static const AZStd::string_view LeadEntity; + }; + + //! A pairing of Sub component Names, and Id. + struct SubModeParamaterState + { + SubComponentModes::ModeType m_modeType = SubComponentModes::ModeType::ModeCount; //!< The Id of the sub component mode. + AZStd::string m_parameterName; //!< The name of the sub component mode. + }; +} // namespace PhysX::JointsComponentModeCommon diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.cpp new file mode 100644 index 0000000000..11abe0fa2a --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.cpp @@ -0,0 +1,432 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace PhysX +{ + AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeAngleCone, AZ::SystemAllocator, 0); + + namespace Internal + { + const float ArrowLength = 2.0f; + const float ConeHeight = 3.0f; + const float XRotationManipulatorRadius = 2.0f; + const float XRotationManipulatorWidth = 0.05f; + } // namespace Internal + + JointsSubComponentModeAngleCone::JointsSubComponentModeAngleCone( + const AZStd::string& propertyName, float max, float min) + : m_propertyName(propertyName) + , m_max(max) + , m_min(min) + { + + } + + void JointsSubComponentModeAngleCone::Setup(const AZ::EntityComponentIdPair& idPair) + { + m_entityComponentIdPair = idPair; + EditorJointRequestBus::EventResult( + m_resetPostion, m_entityComponentIdPair, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Position); + EditorJointRequestBus::EventResult( + m_resetRotation, m_entityComponentIdPair, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation); + EditorJointRequestBus::EventResult( + m_resetLimits, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + + AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentIdPair.GetEntityId()); + + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + const AZ::Quaternion localRotation = localTransform.GetRotation(); + + // Initialize manipulators used to resize the base of the cone. + m_yLinearManipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform); + m_yLinearManipulator->AddEntityComponentIdPair(m_entityComponentIdPair); + m_yLinearManipulator->SetAxis(AZ::Vector3::CreateAxisZ()); + + m_zLinearManipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform); + m_zLinearManipulator->AddEntityComponentIdPair(m_entityComponentIdPair); + m_zLinearManipulator->SetAxis(AZ::Vector3::CreateAxisY()); + + m_yzPlanarManipulator = AzToolsFramework::PlanarManipulator::MakeShared(worldTransform); + m_yzPlanarManipulator->AddEntityComponentIdPair(m_entityComponentIdPair); + m_yzPlanarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ()); + + ConfigureLinearView( + Internal::ArrowLength, AZ::Color(1.0f, 0.0f, 0.0f, 1.0f), AZ::Color(0.0f, 1.0f, 0.0f, 1.0f), AZ::Color(0.0f, 0.0f, 1.0f, 1.0f)); + + ConfigurePlanarView(AZ::Color(0.0f, 1.0f, 0.0f, 1.0f), AZ::Color(0.0f, 0.0f, 1.0f, 1.0f)); + + // Position and orientate manipulators + AZ::Transform displacementTransform = localTransform; + AZ::Vector3 displacementTranslate = localRotation.TransformVector(AZ::Vector3(Internal::ConeHeight, 0.0f, 0.0f)); + displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); + + m_yLinearManipulator->SetLocalTransform(displacementTransform); + m_zLinearManipulator->SetLocalTransform(displacementTransform); + m_yzPlanarManipulator->SetLocalTransform(displacementTransform); + + // Initialize rotation manipulator for rotating cone + m_xRotationManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform); + m_xRotationManipulator->AddEntityComponentIdPair(m_entityComponentIdPair); + m_xRotationManipulator->SetAxis(AZ::Vector3::CreateAxisX()); + m_xRotationManipulator->SetLocalTransform(localTransform); + + const AZ::Color xRotationManipulatorColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f); + m_xRotationManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle( + *m_xRotationManipulator, xRotationManipulatorColor, Internal::XRotationManipulatorRadius, Internal::XRotationManipulatorWidth, + AzToolsFramework::DrawHalfDottedCircle)); + + AZStd::shared_ptr sharedRotationState = + AZStd::make_shared(); + + struct SharedState + { + AngleLimitsFloatPair m_startValues; + }; + auto sharedState = AZStd::make_shared(); + + m_yLinearManipulator->InstallLeftMouseDownCallback( + [this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable + { + AngleLimitsFloatPair currentValue; + EditorJointRequestBus::EventResult( + currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + sharedState->m_startValues = currentValue; + }); + + m_yLinearManipulator->InstallMouseMoveCallback( + [this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) + { + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue, + JointsComponentModeCommon::ParamaterNames::Transform); + const AZ::Quaternion localRotation = localTransform.GetRotation(); + const float axisDisplacement = action.LocalPositionOffset().Dot(localRotation.TransformVector(action.m_fixed.m_axis)); + const float originalBaseY = tan(AZ::DegToRad(sharedState->m_startValues.first)) * Internal::ConeHeight; + const float newBaseY = originalBaseY + axisDisplacement; + const float newAngle = AZ::GetClamp(AZ::RadToDeg(atan(newBaseY / Internal::ConeHeight)), m_min, m_max); + + EditorJointRequestBus::Event( + m_entityComponentIdPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, + AngleLimitsFloatPair(newAngle, sharedState->m_startValues.second)); + + m_yLinearManipulator->SetBoundsDirty(); + }); + + m_zLinearManipulator->InstallLeftMouseDownCallback( + [this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable + { + AngleLimitsFloatPair currentValue; + EditorJointRequestBus::EventResult( + currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + sharedState->m_startValues = currentValue; + }); + + m_zLinearManipulator->InstallMouseMoveCallback( + [this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) + { + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue, + JointsComponentModeCommon::ParamaterNames::Transform); + const AZ::Quaternion localRotation = localTransform.GetRotation(); + const float axisDisplacement = action.LocalPositionOffset().Dot(localRotation.TransformVector(action.m_fixed.m_axis)); + const float originalBaseZ = tan(AZ::DegToRad(sharedState->m_startValues.second)) * Internal::ConeHeight; + const float newBaseZ = originalBaseZ + axisDisplacement; + const float newAngle = AZ::GetClamp(AZ::RadToDeg(atan(newBaseZ / Internal::ConeHeight)), m_min, m_max); + + EditorJointRequestBus::Event( + m_entityComponentIdPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, + AngleLimitsFloatPair(sharedState->m_startValues.first, newAngle)); + + m_zLinearManipulator->SetBoundsDirty(); + }); + + m_yzPlanarManipulator->InstallLeftMouseDownCallback( + [this, sharedState]([[maybe_unused]]const AzToolsFramework::PlanarManipulator::Action& action) mutable + { + AngleLimitsFloatPair currentValue; + EditorJointRequestBus::EventResult( + currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + sharedState->m_startValues = currentValue; + }); + + m_yzPlanarManipulator->InstallMouseMoveCallback( + [this, sharedState](const AzToolsFramework::PlanarManipulator::Action& action) + { + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue, + JointsComponentModeCommon::ParamaterNames::Transform); + + const AZ::Quaternion localRotation = localTransform.GetRotation(); + + const float axisDisplacementY = action.LocalPositionOffset().Dot(localRotation.TransformVector(AZ::Vector3::CreateAxisY())); + const float axisDisplacementZ = action.LocalPositionOffset().Dot(localRotation.TransformVector(AZ::Vector3::CreateAxisZ())); + const float axisDisplacement = axisDisplacementZ > axisDisplacementY ? axisDisplacementZ : axisDisplacementY; + + const float originalBaseY = tan(AZ::DegToRad(sharedState->m_startValues.first)) * Internal::ConeHeight; + const float newBaseY = originalBaseY + axisDisplacement; + const float newAngleY = AZ::GetClamp(AZ::RadToDeg(atan(newBaseY / Internal::ConeHeight)), m_min, m_max); + + const float originalBaseZ = tan(AZ::DegToRad(sharedState->m_startValues.second)) * Internal::ConeHeight; + const float newBaseZ = originalBaseZ + axisDisplacement; + const float newAngleZ = AZ::GetClamp(AZ::RadToDeg(atan(newBaseZ / Internal::ConeHeight)), m_min, m_max); + + EditorJointRequestBus::Event( + m_entityComponentIdPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, + AngleLimitsFloatPair(newAngleY, newAngleZ)); + + m_yzPlanarManipulator->SetBoundsDirty(); + }); + + struct SharedStateXRotate + { + AZ::Transform m_startTM; + }; + auto sharedStateXRotate = AZStd::make_shared(); + + auto mouseDownCallback = [this, sharedRotationState](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void + { + AZ::Quaternion normalizedStart = action.m_start.m_rotation.GetNormalized(); + sharedRotationState->m_axis = AZ::Vector3(normalizedStart.GetX(), normalizedStart.GetY(), normalizedStart.GetZ()); + sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity(); + + AngleLimitsFloatPair currentValue; + EditorJointRequestBus::EventResult( + currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + + sharedRotationState->m_valuePair = currentValue; + }; + + auto mouseDownRotateXCallback = + [this, sharedStateXRotate]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action) mutable -> void + { + PhysX::EditorJointRequestBus::EventResult( + sharedStateXRotate->m_startTM, m_entityComponentIdPair, &PhysX::EditorJointRequests::GetTransformValue, + JointsComponentModeCommon::ParamaterNames::Transform); + }; + + m_xRotationManipulator->InstallLeftMouseDownCallback(mouseDownRotateXCallback); + + m_xRotationManipulator->InstallMouseMoveCallback( + [this, sharedStateXRotate](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void + { + const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; + + AZ::Transform newTransform = AZ::Transform::CreateIdentity(); + newTransform = sharedStateXRotate->m_startTM * AZ::Transform::CreateFromQuaternion(action.m_current.m_delta); + + PhysX::EditorJointRequestBus::Event( + m_entityComponentIdPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position, + newTransform.GetTranslation()); + PhysX::EditorJointRequestBus::Event( + m_entityComponentIdPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation, + newTransform.GetRotation().GetEulerDegrees()); + + m_yLinearManipulator->SetLocalOrientation(manipulatorOrientation); + m_zLinearManipulator->SetLocalOrientation(manipulatorOrientation); + m_yLinearManipulator->SetAxis(action.m_current.m_delta.TransformVector(AZ::Vector3::CreateAxisY())); + m_zLinearManipulator->SetAxis(action.m_current.m_delta.TransformVector(AZ::Vector3::CreateAxisZ())); + m_xRotationManipulator->SetLocalOrientation(manipulatorOrientation); + + m_yLinearManipulator->SetBoundsDirty(); + m_zLinearManipulator->SetBoundsDirty(); + m_xRotationManipulator->SetBoundsDirty(); + }); + + m_xRotationManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); + m_yLinearManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); + m_zLinearManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); + m_yzPlanarManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); + + AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentIdPair.GetEntityId()); + + Refresh(m_entityComponentIdPair); + } + + void JointsSubComponentModeAngleCone::Refresh(const AZ::EntityComponentIdPair& idPair) + { + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + + float coneHeight = Internal::ConeHeight; + AngleLimitsFloatPair yzSwingAngleLimits; + EditorJointRequestBus::EventResult(yzSwingAngleLimits, idPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + + // Draw inverted cone (negative cone height) if angles are larger than 90 deg. + if (yzSwingAngleLimits.first > 90.0f || yzSwingAngleLimits.second > 90.0f) + { + coneHeight = -Internal::ConeHeight; + } + + // reposition manipulators + const AZ::Quaternion localRotation = localTransform.GetRotation(); + const AZ::Vector3 linearManipulatorOffset = + localTransform.GetTranslation() + localRotation.TransformVector(AZ::Vector3(coneHeight, 0.0f, 0.0f)); + + m_xRotationManipulator->SetLocalTransform(localTransform); + + localTransform.SetTranslation(linearManipulatorOffset); + + m_yLinearManipulator->SetLocalTransform(localTransform); + m_zLinearManipulator->SetLocalTransform(localTransform); + m_yzPlanarManipulator->SetLocalTransform(localTransform); + } + + void JointsSubComponentModeAngleCone::Teardown(const AZ::EntityComponentIdPair& idPair) + { + AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + + m_xRotationManipulator->RemoveEntityComponentIdPair(idPair); + m_xRotationManipulator->Unregister(); + m_yLinearManipulator->RemoveEntityComponentIdPair(idPair); + m_yLinearManipulator->Unregister(); + m_zLinearManipulator->RemoveEntityComponentIdPair(idPair); + m_zLinearManipulator->Unregister(); + m_yzPlanarManipulator->RemoveEntityComponentIdPair(idPair); + m_yzPlanarManipulator->Unregister(); + } + + void JointsSubComponentModeAngleCone::ResetValues(const AZ::EntityComponentIdPair& idPair) + { + EditorJointRequestBus::Event( + idPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position, m_resetPostion); + EditorJointRequestBus::Event( + idPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation, m_resetRotation); + EditorJointRequestBus::Event(idPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, m_resetLimits); + } + + void JointsSubComponentModeAngleCone::ConfigureLinearView( + float axisLength, [[maybe_unused]] const AZ::Color& axis1Color, const AZ::Color& axis2Color, const AZ::Color& axis3Color) + { + const float coneLength = 0.28f; + const float coneRadius = 0.07f; + + const auto configureLinearView = + [coneLength, axisLength, coneRadius](AzToolsFramework::LinearManipulator* linearManipulator, const AZ::Color& color) + { + AzToolsFramework::ManipulatorViews views; + views.emplace_back(CreateManipulatorViewLine( + *linearManipulator, color, axisLength, + AzToolsFramework::ManipulatorLineBoundWidth(AzFramework::InvalidViewportId))); + views.emplace_back(CreateManipulatorViewCone( + *linearManipulator, color, linearManipulator->GetAxis() * (axisLength - coneLength), coneLength, coneRadius)); + linearManipulator->SetViews(AZStd::move(views)); + }; + + configureLinearView(m_yLinearManipulator.get(), axis2Color); + configureLinearView(m_zLinearManipulator.get(), axis3Color); + } + + void JointsSubComponentModeAngleCone::ConfigurePlanarView(const AZ::Color& planeColor, const AZ::Color& plane2Color) + { + const float planeSize = 0.6f; + AzToolsFramework::ManipulatorViews views; + views.emplace_back(CreateManipulatorViewQuad(*m_yzPlanarManipulator, planeColor, plane2Color, planeSize)); + m_yzPlanarManipulator->SetViews(AZStd::move(views)); + } + + void JointsSubComponentModeAngleCone::DisplayEntityViewport( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentIdPair.GetEntityId()); + + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue, + JointsComponentModeCommon::ParamaterNames::Transform); + + AZ::u32 stateBefore = debugDisplay.GetState(); + debugDisplay.CullOff(); + + debugDisplay.PushMatrix(worldTransform); + debugDisplay.PushMatrix(localTransform); + + const float xAxisArrowLength = 2.0f; + debugDisplay.SetColor(AZ::Color(1.0f, 0.0f, 0.0f, 1.0f)); + debugDisplay.DrawArrow(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(xAxisArrowLength, 0.0f, 0.0f)); + + AngleLimitsFloatPair yzSwingAngleLimits; + EditorJointRequestBus::EventResult( + yzSwingAngleLimits, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + + const AZ::u32 numEllipseSamples = 16; + AZ::Vector3 ellipseSamples[numEllipseSamples]; + float coneHeight = Internal::ConeHeight; + + // Draw inverted cone if angles are larger than 90 deg. + if (yzSwingAngleLimits.first > 90.0f || yzSwingAngleLimits.second > 90.0f) + { + coneHeight = -Internal::ConeHeight; + } + + // Compute points along perimeter of cone base + const float coney = tanf(AZ::DegToRad(yzSwingAngleLimits.first)) * coneHeight; + const float conez = tanf(AZ::DegToRad(yzSwingAngleLimits.second)) * coneHeight; + const float step = AZ::Constants::TwoPi / numEllipseSamples; + for (size_t i = 0; i < numEllipseSamples; ++i) + { + const float angleStep = step * i; + ellipseSamples[i].SetX(coneHeight); + ellipseSamples[i].SetY(conez * sin(angleStep)); + ellipseSamples[i].SetZ(coney * cos(angleStep)); + } + + // draw cone + for (size_t i = 0; i < numEllipseSamples; ++i) + { + size_t nextIndex = i + 1; + if (i == numEllipseSamples - 1) + { + nextIndex = 0; + } + + // draw cone sides + debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, 0.2f)); + debugDisplay.DrawTri(AZ::Vector3(0.0f, 0.0f, 0.0f), ellipseSamples[i], ellipseSamples[nextIndex]); + + // draw parameter of cone base + debugDisplay.SetColor(AZ::Color(0.4f, 0.4f, 0.4f, 0.4f)); + debugDisplay.DrawLine(ellipseSamples[i], ellipseSamples[nextIndex]); + } + + // draw axis lines at base of cone, and from tip to base. + debugDisplay.SetColor(AZ::Color(0.5f, 0.5f, 0.5f, 0.6f)); + debugDisplay.DrawLine(ellipseSamples[0], ellipseSamples[numEllipseSamples / 2]); + debugDisplay.DrawLine(ellipseSamples[numEllipseSamples * 3 / 4], ellipseSamples[numEllipseSamples / 4]); + debugDisplay.DrawLine(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(coneHeight, 0.0f, 0.0f)); + + debugDisplay.PopMatrix(); // pop local transform + debugDisplay.PopMatrix(); // pop world transform + debugDisplay.SetState(stateBefore); + + // reposition and reorientate manipulators + Refresh(m_entityComponentIdPair); + } + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h new file mode 100644 index 0000000000..3f6d001112 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h @@ -0,0 +1,69 @@ +/* + * 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 +#include +#include +#include +#include +#include + +namespace AzToolsFramework +{ + class AngularManipulator; + class LinearManipulator; + class PlanarManipulator; +} // namespace AzToolsFramework + +namespace PhysX +{ + class JointsSubComponentModeAngleCone final + : public PhysXSubComponentModeBase + , private AzFramework::EntityDebugDisplayEventBus::Handler + { + public: + AZ_CLASS_ALLOCATOR_DECL; + + JointsSubComponentModeAngleCone(const AZStd::string& propertyName, float max, float min); + + // PhysXSubComponentModeBase ... + void Setup(const AZ::EntityComponentIdPair& idPair) override; + void Refresh(const AZ::EntityComponentIdPair& idPair) override; + void Teardown(const AZ::EntityComponentIdPair& idPair) override; + void ResetValues(const AZ::EntityComponentIdPair& idPair) override; + + private: + void ConfigureLinearView( + float axisLength, + const AZ::Color& axis1Color, + const AZ::Color& axis2Color, + const AZ::Color& axis3Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)); + + void ConfigurePlanarView( + const AZ::Color& planeColor = AZ::Color(0.0f, 1.0f, 0.0f, 0.5f), + const AZ::Color& plane2Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)); + + // AzFramework::EntityDebugDisplayEventBus + void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + float m_max = AZStd::numeric_limits::max(); + float m_min = -AZStd::numeric_limits::max(); + + AZ::EntityComponentIdPair m_entityComponentIdPair; + AZ::Vector3 m_resetPostion; + AZ::Vector3 m_resetRotation; + AngleLimitsFloatPair m_resetLimits; + AZStd::string m_propertyName; + AZStd::shared_ptr m_xRotationManipulator; + AZStd::shared_ptr m_yLinearManipulator; + AZStd::shared_ptr m_zLinearManipulator; + AZStd::shared_ptr m_yzPlanarManipulator; + }; +} diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.cpp new file mode 100644 index 0000000000..84b0cd4b5b --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.cpp @@ -0,0 +1,305 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace PhysX +{ + AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeAnglePair, AZ::SystemAllocator, 0); + + namespace Internal + { + const float Alpha = 0.6f; + const AZ::Color ColorDefault = AZ::Color(1.0f, 1.0f, 1.0f, Alpha); + const AZ::Color ColorFirst = AZ::Color(1.0f, 0.0f, 0.0f, Alpha); + const AZ::Color ColorSecond = AZ::Color(0.0f, 1.0f, 0.0f, Alpha); + const AZ::Color ColorSweepArc = AZ::Color(1.0f, 1.0f, 1.0f, Alpha); + + const float SweepLineDisplaceFactor = 0.5f; + const float SweepLineThickness = 1.0f; + const float SweepLineGranularity = 1.0f; + } // namespace Internal + + JointsSubComponentModeAnglePair::JointsSubComponentModeAnglePair( + const AZStd::string& propertyName, const AZ::Vector3& axis, float max, float min) + : m_propertyName(propertyName) + , m_axis(axis) + , m_firstMax(max) + , m_firstMin(min) + , m_secondMax(min) + , m_secondMin(-max) + { + + } + + void JointsSubComponentModeAnglePair::Setup(const AZ::EntityComponentIdPair& idPair) + { + m_entityComponentIdPair = idPair; + EditorJointRequestBus::EventResult(m_resetValue, idPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + + const AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(idPair.GetEntityId()); + + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + const AZ::Quaternion localRotation = localTransform.GetRotation(); + + AZ::Vector3 displacement = m_axis; + AZ::Transform displacementTransform = localTransform; + AZ::Vector3 displacementTranslate = localRotation.TransformVector(displacement); + displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); + + m_firstManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform); + m_firstManipulator->AddEntityComponentIdPair(idPair); + m_firstManipulator->SetAxis(m_axis); + m_firstManipulator->SetLocalTransform(displacementTransform); + + displacement = -m_axis; + displacementTranslate = localRotation.TransformVector(displacement); + displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); + m_secondManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform); + m_secondManipulator->AddEntityComponentIdPair(idPair); + m_secondManipulator->SetAxis(m_axis); + m_secondManipulator->SetLocalTransform(displacementTransform); + + const float manipulatorRadius = 2.0f; + const float manipulatorWidth = 0.05f; + m_firstManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle( + *m_firstManipulator, Internal::ColorFirst, manipulatorRadius, manipulatorWidth, AzToolsFramework::DrawHalfDottedCircle)); + + m_secondManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle( + *m_secondManipulator, Internal::ColorSecond, manipulatorRadius, manipulatorWidth, AzToolsFramework::DrawHalfDottedCircle)); + + Refresh(idPair); + + m_sharedRotationState = AZStd::make_shared(); + + auto mouseDownCallback = [this, + idPair](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void + { + const AZ::Quaternion normalizedStart = action.m_start.m_rotation.GetNormalized(); + m_sharedRotationState->m_axis = AZ::Vector3(normalizedStart.GetX(), normalizedStart.GetY(), normalizedStart.GetZ()); + m_sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity(); + + AngleLimitsFloatPair currentValue; + EditorJointRequestBus::EventResult(currentValue, idPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + + m_sharedRotationState->m_valuePair = currentValue; + }; + + m_firstManipulator->InstallLeftMouseDownCallback(mouseDownCallback); + + m_secondManipulator->InstallLeftMouseDownCallback(mouseDownCallback); + + m_firstManipulator->InstallMouseMoveCallback( + [this, idPair](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void + { + float angleDelta; + AZ::Quaternion manipulatorOrientation; + const float newValue = MouseMove(m_sharedRotationState, action, true, angleDelta, manipulatorOrientation); + if (newValue > m_firstMax || newValue < m_firstMin) + { + return; + } + m_firstManipulator->SetLocalOrientation(manipulatorOrientation); + const float newFirstValue = AZ::GetClamp(m_sharedRotationState->m_valuePair.first + angleDelta, m_firstMin, m_firstMax); + + EditorJointRequestBus::Event( + idPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, + AngleLimitsFloatPair(newFirstValue, m_sharedRotationState->m_valuePair.second)); + }); + + m_secondManipulator->InstallMouseMoveCallback( + [this, idPair](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void + { + float angleDelta; + AZ::Quaternion manipulatorOrientation; + const float newValue = MouseMove(m_sharedRotationState, action, false, angleDelta, manipulatorOrientation); + if (newValue > m_secondMax || newValue < m_secondMin) + { + return; // Not handling values exceeding limits + } + + m_secondManipulator->SetLocalOrientation(manipulatorOrientation); + float newSecondValue = AZ::GetClamp(m_sharedRotationState->m_valuePair.second + angleDelta, m_secondMin, m_secondMax); + + EditorJointRequestBus::Event( + idPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, + AngleLimitsFloatPair(m_sharedRotationState->m_valuePair.first, newSecondValue)); + }); + + m_firstManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); + m_secondManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); + + AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(idPair.GetEntityId()); + } + + void JointsSubComponentModeAnglePair::Refresh(const AZ::EntityComponentIdPair& idPair) + { + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + const AZ::Quaternion localRotation = localTransform.GetRotation(); + + AZ::Transform displacementTransform = localTransform; + AZ::Vector3 displacement = m_axis; + AZ::Vector3 displacementTranslate = localRotation.TransformVector(displacement); + displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); + m_firstManipulator->SetLocalTransform(displacementTransform); + + displacement = -m_axis; + displacementTranslate = localRotation.TransformVector(displacement); + displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate); + m_secondManipulator->SetLocalTransform(displacementTransform); + } + + void JointsSubComponentModeAnglePair::Teardown(const AZ::EntityComponentIdPair& idPair) + { + AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + m_firstManipulator->RemoveEntityComponentIdPair(idPair); + m_firstManipulator->Unregister(); + m_secondManipulator->RemoveEntityComponentIdPair(idPair); + m_secondManipulator->Unregister(); + } + + void JointsSubComponentModeAnglePair::ResetValues(const AZ::EntityComponentIdPair& idPair) + { + EditorJointRequestBus::Event(idPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, m_resetValue); + + m_firstManipulator->SetLocalOrientation(AZ::Quaternion::CreateFromAxisAngle(m_axis, m_resetValue.first)); + m_secondManipulator->SetLocalOrientation(AZ::Quaternion::CreateFromAxisAngle(m_axis, m_resetValue.first)); + } + + float JointsSubComponentModeAnglePair::MouseMove( + AZStd::shared_ptr& sharedRotationState, + const AzToolsFramework::AngularManipulator::Action& action, + bool isFirstValue, + float& angleDelta, + AZ::Quaternion& manipulatorOrientation) + { + sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull(); + angleDelta = 0.0f; + AZ::Vector3 axis = m_axis; + sharedRotationState->m_savedOrientation.ConvertToAxisAngle(axis, angleDelta); + // Polarity of axis is switched by ConvertToAxisAngle call depending on direction of rotation + if (abs(m_axis.GetX() - 1.0f) < FLT_EPSILON) + { + angleDelta = AZ::RadToDeg(angleDelta) * axis.GetX(); + } + else if (abs(m_axis.GetY() - 1.0f) < FLT_EPSILON) + { + angleDelta = AZ::RadToDeg(angleDelta) * axis.GetY(); + } + else if (abs(m_axis.GetZ() - 1.0f) < FLT_EPSILON) + { + angleDelta = AZ::RadToDeg(angleDelta) * axis.GetZ(); + } + + manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; + + if (isFirstValue) + { + return sharedRotationState->m_valuePair.first + angleDelta; + } + return sharedRotationState->m_valuePair.second + angleDelta; + } + + void JointsSubComponentModeAnglePair::DisplayEntityViewport( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + AngleLimitsFloatPair currentValue; + EditorJointRequestBus::EventResult(currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName); + + const float size = 2.0f; + AZ::Vector3 axisPoint = m_axis * size * 0.5f; + + AZStd::array points = { -axisPoint, axisPoint, axisPoint, -axisPoint }; + + if (abs(m_axis.GetX() - 1.0f) < FLT_EPSILON) + { + points[2].SetZ(size); + points[3].SetZ(size); + } + else if (abs(m_axis.GetY() - 1.0f) < FLT_EPSILON) + { + points[2].SetX(size); + points[3].SetX(size); + } + else if (abs(m_axis.GetZ() - 1.0f) < FLT_EPSILON) + { + points[2].SetX(size); + points[3].SetX(size); + } + + AZ::u32 stateBefore = debugDisplay.GetState(); + debugDisplay.CullOff(); + debugDisplay.SetAlpha(Internal::Alpha); + + AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentIdPair.GetEntityId()); + + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + + debugDisplay.PushMatrix(worldTransform); + debugDisplay.PushMatrix(localTransform); + + debugDisplay.SetColor(Internal::ColorSweepArc); + + const AZ::Vector3 zeroVector = AZ::Vector3::CreateZero(); + const AZ::Vector3 posPosition = m_axis * Internal::SweepLineDisplaceFactor; + const AZ::Vector3 negPosition = -posPosition; + debugDisplay.DrawArc( + posPosition, Internal::SweepLineThickness, -currentValue.first, currentValue.first, Internal::SweepLineGranularity, -m_axis); + debugDisplay.DrawArc( + zeroVector, Internal::SweepLineThickness, -currentValue.first, currentValue.first, Internal::SweepLineGranularity, -m_axis); + debugDisplay.DrawArc( + negPosition, Internal::SweepLineThickness, -currentValue.first, currentValue.first, Internal::SweepLineGranularity, -m_axis); + debugDisplay.DrawArc( + posPosition, Internal::SweepLineThickness, 0.0f, abs(currentValue.second), Internal::SweepLineGranularity, -m_axis); + debugDisplay.DrawArc( + zeroVector, Internal::SweepLineThickness, 0.0f, abs(currentValue.second), Internal::SweepLineGranularity, -m_axis); + debugDisplay.DrawArc( + negPosition, Internal::SweepLineThickness, 0.0f, abs(currentValue.second), Internal::SweepLineGranularity, -m_axis); + + AZ::Quaternion firstRotate = AZ::Quaternion::CreateFromAxisAngle(m_axis, AZ::DegToRad(currentValue.first)); + AZ::Transform firstTM = AZ::Transform::CreateFromQuaternion(firstRotate); + debugDisplay.PushMatrix(firstTM); + debugDisplay.SetColor(Internal::ColorFirst); + debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]); + debugDisplay.PopMatrix(); + + AZ::Quaternion secondRotate = AZ::Quaternion::CreateFromAxisAngle(m_axis, AZ::DegToRad(currentValue.second)); + AZ::Transform secondTM = AZ::Transform::CreateFromQuaternion(secondRotate); + debugDisplay.PushMatrix(secondTM); + debugDisplay.SetColor(Internal::ColorSecond); + debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]); + debugDisplay.PopMatrix(); + + debugDisplay.SetColor(Internal::ColorDefault); + debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]); + + debugDisplay.PopMatrix(); // pop local transform + debugDisplay.PopMatrix(); // pop global transform + debugDisplay.SetState(stateBefore); + + // reposition and reorientate manipulators + Refresh(m_entityComponentIdPair); + } + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.h new file mode 100644 index 0000000000..b50a16bf77 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.h @@ -0,0 +1,70 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + class Vector3; +} // namespace AZ + +namespace PhysX +{ + namespace AngleComponentModes + { + struct SharedRotationState; + } // namespace AngleComponentModes + + class JointsSubComponentModeAnglePair final + : public PhysXSubComponentModeBase + , private AzFramework::EntityDebugDisplayEventBus::Handler + { + public: + AZ_CLASS_ALLOCATOR_DECL; + + JointsSubComponentModeAnglePair(const AZStd::string& propertyName, const AZ::Vector3& axis, float max, float min); + + // PhysXSubComponentModeBase ... + void Setup(const AZ::EntityComponentIdPair& idPair) override; + void Refresh(const AZ::EntityComponentIdPair& idPair) override; + void Teardown(const AZ::EntityComponentIdPair& idPair) override; + void ResetValues(const AZ::EntityComponentIdPair& idPair) override; + + private: + float MouseMove( + AZStd::shared_ptr& sharedRotationState, + const AzToolsFramework::AngularManipulator::Action& action, + bool isFirstValue, + float& angleDelta, + AZ::Quaternion& manipulatorOrientation); + + // AzFramework::EntityDebugDisplayEventBus + void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); + float m_firstMax = AZStd::numeric_limits::max(); + float m_firstMin = -AZStd::numeric_limits::max(); + float m_secondMax = AZStd::numeric_limits::max(); + float m_secondMin = -AZStd::numeric_limits::max(); + AZStd::shared_ptr m_sharedRotationState; + AngleLimitsFloatPair m_resetValue; + AZ::EntityComponentIdPair m_entityComponentIdPair; + + AZStd::string m_propertyName; + AZStd::shared_ptr m_firstManipulator; + AZStd::shared_ptr m_secondManipulator; + }; +} diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.cpp new file mode 100644 index 0000000000..ec073b49ef --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.cpp @@ -0,0 +1,133 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace PhysX +{ + AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeLinearFloat, AZ::SystemAllocator, 0); + + JointsSubComponentModeLinearFloat::JointsSubComponentModeLinearFloat( + const AZStd::string& propertyName, float exponent, float max, float min) + : m_propertyName(propertyName) + , m_exponent(exponent) + , m_inverseExponent(1.0f / exponent) + , m_max(max) + , m_min(min) + { + + } + + void JointsSubComponentModeLinearFloat::Setup(const AZ::EntityComponentIdPair& idPair) + { + EditorJointRequestBus::EventResult(m_resetValue, idPair, &EditorJointRequests::GetLinearValue, m_propertyName); + + const AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(idPair.GetEntityId()); + + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + + m_manipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform); + m_manipulator->AddEntityComponentIdPair(idPair); + m_manipulator->SetAxis(AZ::Vector3::CreateAxisX()); + m_manipulator->SetLocalTransform(localTransform); + + Refresh(idPair); + + const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f); + const float manipulatorSize = 0.05f; + + AzToolsFramework::ManipulatorViews views; + views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor, manipulatorSize)); + m_manipulator->SetViews(AZStd::move(views)); + + struct SharedState + { + float m_startingValue = 0.0f; + }; + auto sharedState = AZStd::make_shared(); + + m_manipulator->InstallLeftMouseDownCallback( + [this, sharedState, idPair]([[maybe_unused]]const AzToolsFramework::LinearManipulator::Action& action) mutable + { + float currentValue = 0.0f; + + EditorJointRequestBus::EventResult(currentValue, idPair, &EditorJointRequests::GetLinearValue, m_propertyName); + sharedState->m_startingValue = currentValue; + }); + + m_manipulator->InstallMouseMoveCallback( + [this, sharedState, idPair](const AzToolsFramework::LinearManipulator::Action& action) + { + const float axisDisplacement = action.LocalPositionOffset().Dot(action.m_fixed.m_axis); + + float newValue = AZ::GetClamp(sharedState->m_startingValue + DisplacementToDeltaValue(axisDisplacement), m_min, m_max); + EditorJointRequestBus::Event(idPair, &EditorJointRequests::SetLinearValue, m_propertyName, newValue); + + const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(0.01f, 0.0f, 0.0f)); + m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition)); + }); + + m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); + } + + void JointsSubComponentModeLinearFloat::Refresh(const AZ::EntityComponentIdPair& idPair) + { + float currentValue = 0.0f; + EditorJointRequestBus::EventResult(currentValue, idPair, &EditorJointRequests::GetLinearValue, m_propertyName); + + m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * ValueToDisplacement(currentValue))); + } + + void JointsSubComponentModeLinearFloat::Teardown(const AZ::EntityComponentIdPair& idPair) + { + m_manipulator->RemoveEntityComponentIdPair(idPair); + m_manipulator->Unregister(); + m_manipulator.reset(); + } + + void JointsSubComponentModeLinearFloat::ResetValues(const AZ::EntityComponentIdPair& idPair) + { + EditorJointRequestBus::Event(idPair, &EditorJointRequests::SetLinearValue, m_propertyName, m_resetValue); + m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * ValueToDisplacement(m_resetValue))); + } + + float JointsSubComponentModeLinearFloat::DisplacementToDeltaValue(float displacement) const + { + if (displacement > 0.0f) + { + return AZStd::pow(displacement, m_exponent); + } + else if (displacement < 0.0f) + { + return -AZStd::pow(fabsf(displacement), m_exponent); + } + else + { + return 0.0f; + } + } + + float JointsSubComponentModeLinearFloat::ValueToDisplacement(float value) const + { + return AZStd::pow(value, m_inverseExponent); + } + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.h new file mode 100644 index 0000000000..d65e59d081 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.h @@ -0,0 +1,56 @@ +/* + * 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 +#include +#include +#include +#include + +namespace AZ +{ + class Vector3; +} // namespace AZ + +namespace AzToolsFramework +{ + class LinearManipulator; +} // namespace AzToolsFramework + +namespace PhysX +{ + class JointsSubComponentModeLinearFloat final + : public PhysXSubComponentModeBase + { + public: + AZ_CLASS_ALLOCATOR_DECL; + + JointsSubComponentModeLinearFloat( + const AZStd::string& propertyName, float exponent, float max, float min); + + // PhysXSubComponentModeBase ... + void Setup(const AZ::EntityComponentIdPair& idPair) override; + void Refresh(const AZ::EntityComponentIdPair& idPair) override; + void Teardown(const AZ::EntityComponentIdPair& idPair) override; + void ResetValues(const AZ::EntityComponentIdPair& idPair) override; + + private: + float DisplacementToDeltaValue(float displacement) const; + float ValueToDisplacement(float value) const; + + float m_exponent = 1.0f; + float m_inverseExponent = 1.0f; + float m_max = AZStd::numeric_limits::max(); + float m_min = -AZStd::numeric_limits::max(); + float m_resetValue = 0.0f; + AZStd::string m_propertyName; + AZStd::shared_ptr m_manipulator; + }; +} diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.cpp new file mode 100644 index 0000000000..2bca1700e5 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.cpp @@ -0,0 +1,134 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace PhysX +{ + AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeRotation, AZ::SystemAllocator, 0); + + void JointsSubComponentModeRotation::Setup(const AZ::EntityComponentIdPair& idPair) + { + AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(idPair.GetEntityId()); + + const AZ::Quaternion worldRotation = worldTransform.GetRotation(); + + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + + EditorJointRequestBus::EventResult( + m_resetValue, idPair, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation); + + const AZStd::array axes = { AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ() }; + + const AZStd::array colors = { AZ::Color(1.0f, 0.0f, 0.0f, 1.0f), AZ::Color(0.0f, 1.0f, 0.0f, 1.0f), + AZ::Color(0.0f, 0.0f, 1.0f, 1.0f) }; + + for (AZ::u32 i = 0; i < 3; ++i) + { + m_manipulators[i] = AzToolsFramework::AngularManipulator::MakeShared(worldTransform); + m_manipulators[i]->AddEntityComponentIdPair(idPair); + m_manipulators[i]->SetAxis(axes[i]); + m_manipulators[i]->SetLocalTransform(localTransform); + const float manipulatorRadius = 2.0f; + m_manipulators[i]->SetView(AzToolsFramework::CreateManipulatorViewCircle( + *m_manipulators[i], colors[i], manipulatorRadius, + AzToolsFramework::ManipulatorCicleBoundWidth(), AzToolsFramework::DrawHalfDottedCircle)); + + m_manipulators[i]->Register(AzToolsFramework::g_mainManipulatorManagerId); + } + InstallManipulatorMouseCallbacks(idPair); + } + + void JointsSubComponentModeRotation::Refresh(const AZ::EntityComponentIdPair& idPair) + { + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + + for (auto rotationManipulator : m_manipulators) + { + rotationManipulator->SetLocalTransform(localTransform); + } + } + + void JointsSubComponentModeRotation::Teardown(const AZ::EntityComponentIdPair& idPair) + { + for (auto rotationManipulator : m_manipulators) + { + rotationManipulator->RemoveEntityComponentIdPair(idPair); + rotationManipulator->Unregister(); + } + } + + void JointsSubComponentModeRotation::ResetValues(const AZ::EntityComponentIdPair& idPair) + { + EditorJointRequestBus::Event( + idPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation, m_resetValue); + + const AZ::Quaternion reset = AZ::Quaternion::CreateFromEulerAnglesDegrees(m_resetValue); + for (auto manipulator : m_manipulators) + { + manipulator->SetLocalOrientation(reset); + } + } + + void JointsSubComponentModeRotation::InstallManipulatorMouseCallbacks(const AZ::EntityComponentIdPair& idPair) + { + struct SharedState + { + AZ::Transform m_startTM; + }; + auto sharedState = AZStd::make_shared(); + + auto mouseDownRotateXCallback = + [sharedState, idPair]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action) mutable -> void + { + EditorJointRequestBus::EventResult( + sharedState->m_startTM, idPair, &PhysX::EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + }; + + for (AZ::u32 index = 0; index < 3; ++index) + { + m_manipulators[index]->InstallLeftMouseDownCallback(mouseDownRotateXCallback); + + m_manipulators[index]->InstallMouseMoveCallback( + [this, index, sharedState, idPair](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void + { + const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; + + AZ::Transform newTransform = AZ::Transform::CreateIdentity(); + newTransform = sharedState->m_startTM * AZ::Transform::CreateFromQuaternion(action.m_current.m_delta); + + EditorJointRequestBus::Event( + idPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation, + newTransform.GetRotation().GetEulerDegrees()); + + m_manipulators[index]->SetLocalOrientation(manipulatorOrientation); + }); + } + } +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.h new file mode 100644 index 0000000000..a9a1888dd0 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.h @@ -0,0 +1,44 @@ +/* + * 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 +#include +#include +#include +#include + +namespace AzToolsFramework +{ + class AngularManipulator; +} + +namespace PhysX +{ + class JointsSubComponentModeRotation final + : public PhysXSubComponentModeBase + { + public: + AZ_CLASS_ALLOCATOR_DECL; + + JointsSubComponentModeRotation() = default; + + // PhysXSubComponentModeBase ... + void Setup(const AZ::EntityComponentIdPair& idPair) override; + void Refresh(const AZ::EntityComponentIdPair& idPair) override; + void Teardown(const AZ::EntityComponentIdPair& idPair) override; + void ResetValues(const AZ::EntityComponentIdPair& idPair) override; + + private: + void InstallManipulatorMouseCallbacks(const AZ::EntityComponentIdPair& idPair); + + AZ::Vector3 m_resetValue = AZ::Vector3::CreateZero(); + AZStd::array, 3> m_manipulators; + }; +} diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnap.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.cpp similarity index 71% rename from Gems/PhysX/Code/Editor/EditorSubComponentModeSnap.cpp rename to Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.cpp index 5220b2e274..0f3467eb54 100644 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnap.cpp +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.cpp @@ -1,4 +1,3 @@ - /* * 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. @@ -6,49 +5,67 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include +#include + +#include #include +#include #include -#include -#include +#include #include #include namespace PhysX { - EditorSubComponentModeSnap::EditorSubComponentModeSnap( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name) - : EditorSubComponentModeBase(entityComponentIdPair, componentType, name) + AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeSnap, AZ::SystemAllocator, 0); + + void JointsSubComponentModeSnap::Setup(const AZ::EntityComponentIdPair& idPair) { + m_entityComponentId = idPair; + AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); AZ::Transform localTransform = AZ::Transform::CreateIdentity(); EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); + localTransform, m_entityComponentId, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); m_manipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform); m_manipulator->AddEntityComponentIdPair(m_entityComponentId); m_manipulator->SetAxis(AZ::Vector3::CreateAxisX()); m_manipulator->SetLocalTransform(localTransform); - Refresh(); + Refresh(idPair); const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f); const float manipulatorSize = 0.05f; AzToolsFramework::ManipulatorViews views; - views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor - , manipulatorSize)); + views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor, manipulatorSize)); m_manipulator->SetViews(AZStd::move(views)); + + m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); + AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId()); } - void EditorSubComponentModeSnap::HandleMouseInteraction( - const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) + void JointsSubComponentModeSnap::Refresh(const AZ::EntityComponentIdPair& idPair) + { + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + + m_manipulator->SetLocalTransform(localTransform); + } + + void JointsSubComponentModeSnap::Teardown(const AZ::EntityComponentIdPair& idPair) + { + AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + + m_manipulator->RemoveEntityComponentIdPair(idPair); + m_manipulator->Unregister(); + m_manipulator.reset(); + } + + void JointsSubComponentModeSnap::HandleMouseInteraction(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) { if (mouseInteraction.m_mouseEvent == AzToolsFramework::ViewportInteraction::MouseEvent::Move) { @@ -63,25 +80,36 @@ namespace PhysX const AZ::Quaternion worldRotateInv = worldRotate.GetInverseFull(); m_manipulator->SetLocalPosition(worldRotateInv.TransformVector(m_pickedPosition - worldTransform.GetTranslation())); - m_manipulator->SetBoundsDirty(); } } } - void EditorSubComponentModeSnap::Refresh() + AZStd::string JointsSubComponentModeSnap::GetPickedEntityName() + { + AZStd::string pickedEntityName; + if (m_pickedEntity.IsValid()) + { + AZ::ComponentApplicationBus::BroadcastResult( + pickedEntityName, &AZ::ComponentApplicationRequests::GetEntityName, m_pickedEntity); + } + return pickedEntityName; + } + + AZ::Vector3 JointsSubComponentModeSnap::GetPosition() const { + AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); + + AZ::Quaternion worldRotate = worldTransform.GetRotation(); + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); + localTransform, m_entityComponentId, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); - m_manipulator->SetLocalTransform(localTransform); + return worldTransform.GetTranslation() + worldRotate.TransformVector(localTransform.GetTranslation()); } - void EditorSubComponentModeSnap::DisplayEntityViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + void JointsSubComponentModeSnap::DisplayEntityViewport( + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { AZ::u32 stateBefore = debugDisplay.GetState(); @@ -89,11 +117,9 @@ namespace PhysX AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - AZ::Transform localTransform = AZ::Transform::CreateIdentity();; + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); + localTransform, m_entityComponentId, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); debugDisplay.PushMatrix(worldTransform); debugDisplay.PushMatrix(localTransform); @@ -104,9 +130,7 @@ namespace PhysX AngleLimitsFloatPair yzSwingAngleLimits; EditorJointRequestBus::EventResult( - yzSwingAngleLimits, m_entityComponentId - , &EditorJointRequests::GetLinearValuePair - , PhysX::EditorJointComponentMode::s_parameterSwingLimit); + yzSwingAngleLimits, m_entityComponentId, &EditorJointRequests::GetLinearValuePair, JointsComponentModeCommon::ParamaterNames::SwingLimit); const AZ::u32 numEllipseSamples = 16; AZStd::array ellipseSamples; @@ -154,8 +178,8 @@ namespace PhysX debugDisplay.DrawLine(ellipseSamples[numEllipseSamples * 3 / 4], ellipseSamples[numEllipseSamples / 4]); debugDisplay.DrawLine(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(coneHeight, 0.0f, 0.0f)); - debugDisplay.PopMatrix();//pop local transform - debugDisplay.PopMatrix();//pop world transform + debugDisplay.PopMatrix(); // pop local transform + debugDisplay.PopMatrix(); // pop world transform // draw line from joint to mouse-over entity if (m_pickedEntity.IsValid()) @@ -173,40 +197,9 @@ namespace PhysX debugDisplay.DrawWireBox(m_pickedEntityAabb.GetMin(), m_pickedEntityAabb.GetMax()); // draw something, e.g. an icon, to indicate type of snapping - DisplaySpecificSnapType(viewportInfo, - debugDisplay, - position, - directionNorm, - directionLength); + DisplaySpecificSnapType(viewportInfo, debugDisplay, position, directionNorm, directionLength); } debugDisplay.SetState(stateBefore); } - - AZStd::string EditorSubComponentModeSnap::GetPickedEntityName() - { - AZStd::string pickedEntityName; - if (m_pickedEntity.IsValid()) - { - AZ::ComponentApplicationBus::BroadcastResult(pickedEntityName, - &AZ::ComponentApplicationRequests::GetEntityName, - m_pickedEntity); - } - return pickedEntityName; - } - - AZ::Vector3 EditorSubComponentModeSnap::GetPosition() const - { - AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId()); - - AZ::Quaternion worldRotate = worldTransform.GetRotation(); - - AZ::Transform localTransform = AZ::Transform::CreateIdentity(); - EditorJointRequestBus::EventResult( - localTransform, m_entityComponentId - , &EditorJointRequests::GetTransformValue - , PhysX::EditorJointComponentMode::s_parameterTransform); - - return worldTransform.GetTranslation() + worldRotate.TransformVector(localTransform.GetTranslation()); - } } // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnap.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h similarity index 51% rename from Gems/PhysX/Code/Editor/EditorSubComponentModeSnap.h rename to Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h index 585d46c2b6..843fffd4c8 100644 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnap.h +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h @@ -1,4 +1,3 @@ - /* * 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. @@ -6,12 +5,16 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once +#include +#include +#include #include -#include #include +#include namespace AzToolsFramework { @@ -20,48 +23,43 @@ namespace AzToolsFramework namespace PhysX { - /// This sub-component mode uses EditorViewportEntityPicker to get the position of an entity that the mouse is hovering over. - /// Classes inheriting from this class can use the mouse-over entity position to perform custom actions. - class EditorSubComponentModeSnap - : public PhysX::EditorSubComponentModeBase + class JointsSubComponentModeSnap + : public PhysXSubComponentModeBase , protected AzFramework::EntityDebugDisplayEventBus::Handler { public: - EditorSubComponentModeSnap( - const AZ::EntityComponentIdPair& entityComponentIdPair - , const AZ::Uuid& componentType - , const AZStd::string& name); - virtual ~EditorSubComponentModeSnap() = default; + AZ_CLASS_ALLOCATOR_DECL; - // PhysX::EditorSubComponentModeBase - void HandleMouseInteraction( - const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; - void Refresh() override; + JointsSubComponentModeSnap() = default; + + // PhysXSubComponentModeBase ... + virtual void Setup(const AZ::EntityComponentIdPair& idPair) override; + virtual void Refresh(const AZ::EntityComponentIdPair& idPair) override; + virtual void Teardown(const AZ::EntityComponentIdPair& idPair) override; + void HandleMouseInteraction(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; protected: + AZStd::string GetPickedEntityName(); + AZ::Vector3 GetPosition() const; + // AzFramework::EntityDebugDisplayEventBus - void DisplayEntityViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; + void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; - /// Override to draw specific snap type display + //! Override to draw specific snap type display virtual void DisplaySpecificSnapType( [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, [[maybe_unused]] AzFramework::DebugDisplayRequests& debugDisplay, [[maybe_unused]] const AZ::Vector3& jointPosition, [[maybe_unused]] const AZ::Vector3& snapDirection, - [[maybe_unused]] float snapLength) {} - - virtual void InitMouseDownCallBack() = 0; - - AZStd::string GetPickedEntityName(); - AZ::Vector3 GetPosition() const; - - AZStd::shared_ptr m_manipulator; + [[maybe_unused]] float snapLength) + { + } EditorViewportEntityPicker m_picker; AZ::EntityId m_pickedEntity; AZ::Aabb m_pickedEntityAabb = AZ::Aabb::CreateNull(); AZ::Vector3 m_pickedPosition; + AZ::EntityComponentIdPair m_entityComponentId; + AZStd::shared_ptr m_manipulator; }; -} // namespace PhysX +} diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.cpp new file mode 100644 index 0000000000..809ccd97cb --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.cpp @@ -0,0 +1,91 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +namespace PhysX +{ + AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeSnapPosition, AZ::SystemAllocator, 0); + + void JointsSubComponentModeSnapPosition::Setup(const AZ::EntityComponentIdPair& idPair) + { + JointsSubComponentModeSnap::Setup(idPair); + + PhysX::EditorJointRequestBus::EventResult( + m_resetPosition, m_entityComponentId, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Position); + + PhysX::EditorJointRequestBus::EventResult( + m_resetLeadEntity, m_entityComponentId, &PhysX::EditorJointRequests::GetEntityIdValue, + JointsComponentModeCommon::ParamaterNames::LeadEntity); + + m_manipulator->InstallLeftMouseDownCallback( + [this](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable + { + if (!m_pickedEntity.IsValid()) + { + return; + } + + const AZ::Vector3 newLocalPosition = PhysX::Utils::ComputeJointLocalTransform( + PhysX::Utils::GetEntityWorldTransformWithScale(m_pickedEntity), + PhysX::Utils::GetEntityWorldTransformWithScale(m_entityComponentId.GetEntityId())) + .GetTranslation(); + + PhysX::EditorJointRequestBus::Event( + m_entityComponentId, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position, + newLocalPosition); + + const bool selectedEntityIsNotJointEntity = m_pickedEntity != m_entityComponentId.GetEntityId(); + + AZ_Error( + "EditorSubComponentModeSnapPosition", selectedEntityIsNotJointEntity, + "Joint's lead entity cannot be the same as the entity in which the joint resides. Select lead entity on snap failed."); + + if (selectedEntityIsNotJointEntity) + { + PhysX::EditorJointRequestBus::Event( + m_entityComponentId, &PhysX::EditorJointRequests::SetEntityIdValue, JointsComponentModeCommon::ParamaterNames::LeadEntity, + m_pickedEntity); + } + }); + } + + void JointsSubComponentModeSnapPosition::ResetValues([[maybe_unused]]const AZ::EntityComponentIdPair& idPair) + { + PhysX::EditorJointRequestBus::Event( + m_entityComponentId, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position, m_resetPosition); + PhysX::EditorJointRequestBus::Event( + m_entityComponentId, &PhysX::EditorJointRequests::SetEntityIdValue, JointsComponentModeCommon::ParamaterNames::LeadEntity, m_resetLeadEntity); + } + + void JointsSubComponentModeSnapPosition::DisplaySpecificSnapType( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, + AzFramework::DebugDisplayRequests& debugDisplay, + const AZ::Vector3& jointPosition, + const AZ::Vector3& snapDirection, + float snapLength) + { + const float arrowLength = 1.0f; + const float iconGap = 1.0f; + const AZ::Vector3 iconPosition = jointPosition + (snapDirection * (snapLength + arrowLength + iconGap)); + + debugDisplay.SetColor(AZ::Colors::Red); + debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(arrowLength, 0.0f, 0.0f)); + debugDisplay.SetColor(AZ::Colors::Green); + debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(0.2f, arrowLength, 0.2f)); + debugDisplay.SetColor(AZ::Colors::Blue); + debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(0.0f, 0.0f, arrowLength)); + } + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.h new file mode 100644 index 0000000000..c762db54d2 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.h @@ -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 +#include +#include + +namespace PhysX +{ + class JointsSubComponentModeSnapPosition final + : public JointsSubComponentModeSnap + { + public: + AZ_CLASS_ALLOCATOR_DECL; + + JointsSubComponentModeSnapPosition() = default; + + // JointsSubComponentModeSnap ... + void Setup(const AZ::EntityComponentIdPair& idPair) override; + void ResetValues(const AZ::EntityComponentIdPair& idPair) override; + + protected: + // JointsSubComponentModeSnap ... + void DisplaySpecificSnapType( + const AzFramework::ViewportInfo& viewportInfo, + AzFramework::DebugDisplayRequests& debugDisplay, + const AZ::Vector3& jointPosition, + const AZ::Vector3& snapDirection, + float snapLength) override; + + private: + AZ::Vector3 m_resetPosition; + AZ::EntityId m_resetLeadEntity; + }; +} diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.cpp new file mode 100644 index 0000000000..7d48bf7772 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.cpp @@ -0,0 +1,117 @@ +/* + * 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 + +#include +#include +#include +#include + +#include +#include +#include + +namespace PhysX +{ + AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeSnapRotation, AZ::SystemAllocator, 0); + + void JointsSubComponentModeSnapRotation::Setup(const AZ::EntityComponentIdPair& idPair) + { + JointsSubComponentModeSnap::Setup(idPair); + + PhysX::EditorJointRequestBus::EventResult( + m_resetRotation, m_entityComponentId, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation); + + m_manipulator->InstallLeftMouseDownCallback( + [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action) mutable + { + if (!m_pickedEntity.IsValid()) + { + return; + } + + AZ::EntityId leadEntityId; + PhysX::EditorJointRequestBus::EventResult( + leadEntityId, m_entityComponentId, &PhysX::EditorJointRequests::GetEntityIdValue, + JointsComponentModeCommon::ParamaterNames::LeadEntity); + + if (leadEntityId.IsValid() && m_pickedEntity == leadEntityId) + { + AZ_Warning( + "EditorsubComponentModeSnapRotation", false, + "The entity %s is the lead of the joint. Please snap rotation (or orientation) of joint to another entity that is " + "not the lead entity.", + GetPickedEntityName().c_str()); + return; + } + + AZ::Transform worldTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(worldTransform, m_entityComponentId.GetEntityId(), &AZ::TransformInterface::GetWorldTM); + worldTransform.ExtractUniformScale(); + + AZ::Transform localTransform = AZ::Transform::CreateIdentity(); + EditorJointRequestBus::EventResult( + localTransform, m_entityComponentId, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform); + + AZ::Transform pickedEntityTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(pickedEntityTransform, m_pickedEntity, &AZ::TransformInterface::GetWorldTM); + + const AZ::Transform worldTransformInv = worldTransform.GetInverse(); + const AZ::Vector3 pickedLocalPosition = + worldTransformInv.TransformVector(pickedEntityTransform.GetTranslation()) - localTransform.GetTranslation(); + + if (AZStd::abs(pickedLocalPosition.GetLength()) < FLT_EPSILON) + { + AZ_Warning( + "EditorsubComponentModeSnapRotation", false, + "The entity %s is too close to the joint position. Please snap rotation to an entity that is not at the position " + "of the joint.", + GetPickedEntityName().c_str()); + return; + } + + const AZ::Vector3 targetDirection = pickedLocalPosition.GetNormalized(); + const AZ::Vector3 sourceDirection = AZ::Vector3::CreateAxisX(); + const AZ::Quaternion newLocalRotation = AZ::Quaternion::CreateShortestArc(sourceDirection, targetDirection); + + PhysX::EditorJointRequestBus::Event( + m_entityComponentId, &PhysX::EditorJointRequests::SetVector3Value, + JointsComponentModeCommon::ParamaterNames::Rotation // using rotation parameter name to set the local rotation value + , + newLocalRotation.GetEulerDegrees()); + }); + } + + void JointsSubComponentModeSnapRotation::ResetValues([[maybe_unused]] const AZ::EntityComponentIdPair& idPair) + { + PhysX::EditorJointRequestBus::Event( + m_entityComponentId, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation, m_resetRotation); + } + + void JointsSubComponentModeSnapRotation::DisplaySpecificSnapType( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, + AzFramework::DebugDisplayRequests& debugDisplay, + const AZ::Vector3& jointPosition, + const AZ::Vector3& snapDirection, + float snapLength) + { + const float circleRadius = 0.5f; + const float iconGap = 1.0f; + + const AZ::Vector3 iconPosition = jointPosition + (snapDirection * (snapLength + circleRadius * 2.0f + iconGap)); + + debugDisplay.SetColor(AZ::Colors::Red); + debugDisplay.DrawCircle(iconPosition, circleRadius, 0); + debugDisplay.SetColor(AZ::Colors::Green); + debugDisplay.DrawCircle(iconPosition, circleRadius, 1); + debugDisplay.SetColor(AZ::Colors::Blue); + debugDisplay.DrawCircle(iconPosition, circleRadius, 2); + } + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.h new file mode 100644 index 0000000000..d8aa613684 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.h @@ -0,0 +1,41 @@ +/* + * 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 +#include +#include + +namespace PhysX +{ + class JointsSubComponentModeSnapRotation final + : public JointsSubComponentModeSnap + { + public: + AZ_CLASS_ALLOCATOR_DECL; + + JointsSubComponentModeSnapRotation() = default; + + // JointsSubComponentModeSnap ... + void Setup(const AZ::EntityComponentIdPair& idPair) override; + void ResetValues(const AZ::EntityComponentIdPair& idPair) override; + + protected: + // JointsSubComponentModeSnap ... + void DisplaySpecificSnapType( + const AzFramework::ViewportInfo& viewportInfo, + AzFramework::DebugDisplayRequests& debugDisplay, + const AZ::Vector3& jointPosition, + const AZ::Vector3& snapDirection, + float snapLength) override; + + private: + AZ::Vector3 m_resetRotation; + }; +} diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.cpp b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.cpp new file mode 100644 index 0000000000..becb79a1d9 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.cpp @@ -0,0 +1,90 @@ +/* + * 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 + +#include +#include +#include +#include + +#include +#include +#include + +namespace PhysX +{ + AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeTranslation, AZ::SystemAllocator, 0); + + JointsSubComponentModeTranslation::JointsSubComponentModeTranslation() + : m_manipulator( + AzToolsFramework::TranslationManipulators::Dimensions::Three, AZ::Transform::Identity(), AZ::Vector3::CreateOne()) + { + } + + void JointsSubComponentModeTranslation::Setup(const AZ::EntityComponentIdPair& idPair) + { + AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(idPair.GetEntityId()); + + EditorJointRequestBus::EventResult( + m_resetValue, idPair, &EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Position); + + m_manipulator.SetSpace(worldTransform); + m_manipulator.SetLocalPosition(m_resetValue); + + m_manipulator.AddEntityComponentIdPair(idPair); + m_manipulator.Register(AzToolsFramework::g_mainManipulatorManagerId); + AzToolsFramework::ConfigureTranslationManipulatorAppearance3d(&m_manipulator); + m_manipulator.InstallLinearManipulatorMouseMoveCallback( + [this, idPair](const AzToolsFramework::LinearManipulator::Action& action) + { + OnManipulatorMoved(action.LocalPosition(), idPair); + }); + + m_manipulator.InstallPlanarManipulatorMouseMoveCallback( + [this, idPair](const AzToolsFramework::PlanarManipulator::Action& action) + { + OnManipulatorMoved(action.LocalPosition(), idPair); + }); + + m_manipulator.InstallSurfaceManipulatorMouseMoveCallback( + [this, idPair](const AzToolsFramework::SurfaceManipulator::Action& action) + { + OnManipulatorMoved(action.LocalPosition(), idPair); + }); + } + + void JointsSubComponentModeTranslation::Refresh(const AZ::EntityComponentIdPair& idPair) + { + AZ::Vector3 localTranslation; + EditorJointRequestBus::EventResult( + localTranslation, idPair, &EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Position); + m_manipulator.SetLocalPosition(localTranslation); + } + + void JointsSubComponentModeTranslation::Teardown(const AZ::EntityComponentIdPair& idPair) + { + m_manipulator.RemoveEntityComponentIdPair(idPair); + m_manipulator.Unregister(); + } + + void JointsSubComponentModeTranslation::ResetValues(const AZ::EntityComponentIdPair& idPair) + { + PhysX::EditorJointRequestBus::Event( + idPair, &EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position, m_resetValue); + m_manipulator.SetLocalPosition(m_resetValue); + } + + void JointsSubComponentModeTranslation::OnManipulatorMoved(const AZ::Vector3& position, const AZ::EntityComponentIdPair& idPair) + { + m_manipulator.SetLocalPosition(position); + PhysX::EditorJointRequestBus::Event( + idPair, &EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position, position); + } + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.h new file mode 100644 index 0000000000..2e25556889 --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.h @@ -0,0 +1,43 @@ +/* + * 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 +#include +#include +#include + +namespace AZ +{ + class Vector3; +} // namespace AZ + +namespace PhysX +{ + class JointsSubComponentModeTranslation final + : public PhysXSubComponentModeBase + { + public: + AZ_CLASS_ALLOCATOR_DECL; + + JointsSubComponentModeTranslation(); + + // PhysXSubComponentModeBase ... + void Setup(const AZ::EntityComponentIdPair& idPair) override; + void Refresh(const AZ::EntityComponentIdPair& idPair) override; + void Teardown(const AZ::EntityComponentIdPair& idPair) override; + void ResetValues(const AZ::EntityComponentIdPair& idPair) override; + + private: + void OnManipulatorMoved(const AZ::Vector3& position, const AZ::EntityComponentIdPair& idPair); + + AZ::Vector3 m_resetValue = AZ::Vector3::CreateZero(); + AzToolsFramework::TranslationManipulators m_manipulator; + }; +} diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/PhysXSubComponentModeBase.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/PhysXSubComponentModeBase.h new file mode 100644 index 0000000000..0478d6e1ee --- /dev/null +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/PhysXSubComponentModeBase.h @@ -0,0 +1,48 @@ +/* + * 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 + +namespace AZ +{ + class EntityComponentIdPair; +} // namespace AZ + +namespace AzToolsFramework::ViewportInteraction +{ + struct MouseInteractionEvent; +} // namespace AzToolsFramework::ViewportInteraction + +namespace PhysX +{ + class PhysXSubComponentModeBase + { + public: + virtual ~PhysXSubComponentModeBase() = default; + + //! Called when the mode is entered to initialize the mode. + //! @param idPair The entity/component id pair. + virtual void Setup(const AZ::EntityComponentIdPair& idPair) = 0; + + //! Called when the mode needs to refresh it's values. + //! @param idPair The entity/component id pair. + virtual void Refresh(const AZ::EntityComponentIdPair& idPair) = 0; + + //! Called when the mode exits to perform cleanup. + //! @param idPair The entity/component id pair. + virtual void Teardown(const AZ::EntityComponentIdPair& idPair) = 0; + + //! Called when reset hot key is pressed. + //! Should reset values in the sub component mode to sensible defaults. + //! @param idPair The entity/component id pair. + virtual void ResetValues(const AZ::EntityComponentIdPair& idPair) = 0; + + //! Additional mouse handling by sub-component mode. Does not absorb mouse event. + virtual void HandleMouseInteraction( + [[maybe_unused]] const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) {}; + }; +} // namespace PhysX diff --git a/Gems/PhysX/Code/Include/PhysX/EditorJointBus.h b/Gems/PhysX/Code/Include/PhysX/EditorJointBus.h index e62a8b7e15..20145f1f76 100644 --- a/Gems/PhysX/Code/Include/PhysX/EditorJointBus.h +++ b/Gems/PhysX/Code/Include/PhysX/EditorJointBus.h @@ -9,59 +9,58 @@ #include #include +#include +#include namespace PhysX { - /// Pair of floating point values for angular limits. - using AngleLimitsFloatPair = AZStd::pair; - - /// Messages serviced by Editor Joint Components. + //! Messages serviced by Editor Joint Components. class EditorJointRequests : public AZ::EntityComponentBus { public: static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - /// Get bool parameter value identified by name. - /// @return Value of bool parameter. + //! Get bool parameter value identified by name. + //! @return Value of bool parameter. virtual bool GetBoolValue(const AZStd::string& parameterName) = 0; - /// Get entityID parameter value identified by name. - /// @return Value of entityID parameter. + //! Get entityID parameter value identified by name. + //! @return Value of entityID parameter. virtual AZ::EntityId GetEntityIdValue(const AZStd::string& parameterName) = 0; - /// Get linear parameter value identified by name. - /// @return Value of linear value parameter. + //! Get linear parameter value identified by name. + //! @return Value of linear value parameter. virtual float GetLinearValue(const AZStd::string& parameterName) = 0; - /// Get linear parameter value pair identified by name. - /// @return Linear parameter value pair. + //! Get linear parameter value pair identified by name. + //! @return Linear parameter value pair. virtual AngleLimitsFloatPair GetLinearValuePair(const AZStd::string& parameterName) = 0; - /// Get vector3 value identified by name. - /// @return Vector3 parameter value. + //! Get vector3 value identified by name. + //! @return Vector3 parameter value. virtual AZ::Vector3 GetVector3Value(const AZStd::string& parameterName) = 0; - /// Get transform value identified by name. - /// @return Transform parameter value. + //! Get transform value identified by name. + //! @return Transform parameter value. virtual AZ::Transform GetTransformValue(const AZStd::string& parameterName) = 0; - /// Checks if parameter is used. - virtual bool IsParameterUsed(const AZStd::string& parameterName) = 0; + //! Get the Sub Component modes to enable. + virtual AZStd::vector GetSubComponentModesState() = 0; - /// Set bool parameter value identified by name. + //! Set bool parameter value identified by name. virtual void SetBoolValue(const AZStd::string& parameterName, bool value) = 0; - /// Set entity ID parameter value identified by name. + //! Set entity ID parameter value identified by name. virtual void SetEntityIdValue(const AZStd::string& parameterName, AZ::EntityId value) = 0; - /// Set linear parameter value identified by name. + //! Set linear parameter value identified by name. virtual void SetLinearValue(const AZStd::string& parameterName, float value) = 0; - /// Set linear parameter value pair identified by name. + //! Set linear parameter value pair identified by name. virtual void SetLinearValuePair(const AZStd::string& parameterName, const AngleLimitsFloatPair& valuePair) = 0; - /// Set vector3 parameter value identified by name. + //! Set vector3 parameter value identified by name. virtual void SetVector3Value(const AZStd::string& parameterName, const AZ::Vector3& value) = 0; }; using EditorJointRequestBus = AZ::EBus; diff --git a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp index da000baeed..92952bcdaa 100644 --- a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp @@ -13,7 +13,8 @@ #include #include -#include +#include +#include #include #include @@ -73,9 +74,8 @@ namespace PhysX AzToolsFramework::EditorComponentSelectionNotificationsBus::Handler::BusConnect(entityId); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler* selection = this; - m_componentModeDelegate.ConnectWithSingleComponentMode < - EditorBallJointComponent, EditorBallJointComponentMode>( - AZ::EntityComponentIdPair(entityId, GetId()), selection); + m_componentModeDelegate.ConnectWithSingleComponentMode( + AZ::EntityComponentIdPair(entityId, GetId()), selection); PhysX::EditorJointRequestBus::Handler::BusConnect(AZ::EntityComponentIdPair(entityId, GetId())); } @@ -100,23 +100,19 @@ namespace PhysX float EditorBallJointComponent::GetLinearValue(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::MaxForce) { return m_config.m_forceMax; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::MaxTorque) { return m_config.m_torqueMax; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterTolerance) - { - return m_swingLimit.m_standardLimitConfig.m_tolerance; - } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterDamping) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::Damping) { return m_swingLimit.m_standardLimitConfig.m_damping; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterStiffness) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::Stiffness) { return m_swingLimit.m_standardLimitConfig.m_stiffness; } @@ -126,7 +122,7 @@ namespace PhysX AngleLimitsFloatPair EditorBallJointComponent::GetLinearValuePair(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterSwingLimit) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::SwingLimit) { return AngleLimitsFloatPair(m_swingLimit.m_limitY, m_swingLimit.m_limitZ); } @@ -134,49 +130,58 @@ namespace PhysX return AngleLimitsFloatPair(); } - bool EditorBallJointComponent::IsParameterUsed(const AZStd::string& parameterName) + AZStd::vector EditorBallJointComponent::GetSubComponentModesState() { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce - || parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque - ) - { - return m_config.m_breakable; - } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterTolerance) + AZStd::vector subModes; + + subModes.emplace_back(JointsComponentModeCommon::SubModeParamaterState{ + JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition, + JointsComponentModeCommon::ParamaterNames::SnapPosition }); + subModes.emplace_back(JointsComponentModeCommon::SubModeParamaterState{ + JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation, + JointsComponentModeCommon::ParamaterNames::SnapRotation }); + + if (AZStd::vector baseSubModes = + EditorJointComponent::GetSubComponentModesState(); + !baseSubModes.empty()) { - return !m_swingLimit.m_standardLimitConfig.m_isSoftLimit; + subModes.insert(subModes.end(), baseSubModes.begin(), baseSubModes.end()); } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterDamping) - { - return m_swingLimit.m_standardLimitConfig.m_isSoftLimit; - } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterStiffness) + + if (m_swingLimit.m_standardLimitConfig.m_isLimited) { - return m_swingLimit.m_standardLimitConfig.m_isSoftLimit; - } + subModes.emplace_back( + JointsComponentModeCommon::SubModeParamaterState{ JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits, + JointsComponentModeCommon::ParamaterNames::SwingLimit }); - return true; // Sub-component mode always enabled unless disabled explicitly. + if (m_swingLimit.m_standardLimitConfig.m_isSoftLimit) + { + subModes.emplace_back(JointsComponentModeCommon::SubModeParamaterState{ + JointsComponentModeCommon::SubComponentModes::ModeType::Damping, JointsComponentModeCommon::ParamaterNames::Damping }); + subModes.emplace_back( + JointsComponentModeCommon::SubModeParamaterState{ JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness, + JointsComponentModeCommon::ParamaterNames::Stiffness }); + } + } + + return subModes; } void EditorBallJointComponent::SetLinearValue(const AZStd::string& parameterName, float value) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::MaxForce) { m_config.m_forceMax = value; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::MaxTorque) { m_config.m_torqueMax = value; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterTolerance) - { - m_swingLimit.m_standardLimitConfig.m_tolerance = value; - } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterDamping) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::Damping) { m_swingLimit.m_standardLimitConfig.m_damping = value; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterStiffness) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::Stiffness) { m_swingLimit.m_standardLimitConfig.m_stiffness = value; } @@ -184,7 +189,7 @@ namespace PhysX void EditorBallJointComponent::SetLinearValuePair(const AZStd::string& parameterName, const AngleLimitsFloatPair& valuePair) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterSwingLimit) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::SwingLimit) { m_swingLimit.m_limitY = valuePair.first; m_swingLimit.m_limitZ = valuePair.second; @@ -193,7 +198,7 @@ namespace PhysX void EditorBallJointComponent::SetBoolValue(const AZStd::string& parameterName, bool value) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterComponentMode) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::ComponentMode) { m_swingLimit.m_standardLimitConfig.m_inComponentMode = value; m_config.m_inComponentMode = value; @@ -202,10 +207,6 @@ namespace PhysX &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay , AzToolsFramework::Refresh_EntireTree); } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterSelectOnSnap) - { - m_config.m_selectLeadOnSnap = value; - } } void EditorBallJointComponent::DisplayEntityViewport( @@ -228,7 +229,7 @@ namespace PhysX EditorJointRequestBus::EventResult(localTransform, AZ::EntityComponentIdPair(entityId, GetId()), &EditorJointRequests::GetTransformValue, - PhysX::EditorJointComponentMode::s_parameterTransform); + PhysX::JointsComponentModeCommon::ParamaterNames::Transform); AZ::u32 stateBefore = debugDisplay.GetState(); debugDisplay.CullOff(); diff --git a/Gems/PhysX/Code/Source/EditorBallJointComponent.h b/Gems/PhysX/Code/Source/EditorBallJointComponent.h index 1702532a88..ad925dc79d 100644 --- a/Gems/PhysX/Code/Source/EditorBallJointComponent.h +++ b/Gems/PhysX/Code/Source/EditorBallJointComponent.h @@ -41,7 +41,7 @@ namespace PhysX // PhysX::EditorJointRequests float GetLinearValue(const AZStd::string& parameterName) override; AngleLimitsFloatPair GetLinearValuePair(const AZStd::string& parameterName) override; - bool IsParameterUsed(const AZStd::string& parameterName) override; + AZStd::vector GetSubComponentModesState() override; void SetBoolValue(const AZStd::string& parameterName, bool value) override; void SetLinearValue(const AZStd::string& parameterName, float value) override; void SetLinearValuePair(const AZStd::string& parameterName, const AngleLimitsFloatPair& valuePair) override; diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 566903d84a..7c07ca207c 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp index bf5af6b78d..94f57690ba 100644 --- a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include namespace PhysX @@ -70,9 +70,8 @@ namespace PhysX AzToolsFramework::EditorComponentSelectionNotificationsBus::Handler::BusConnect(entityId); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler* selection = this; - m_componentModeDelegate.ConnectWithSingleComponentMode < - EditorFixedJointComponent, EditorFixedJointComponentMode>( - AZ::EntityComponentIdPair(entityId, GetId()), selection); + m_componentModeDelegate.ConnectWithSingleComponentMode( + AZ::EntityComponentIdPair(entityId, GetId()), selection); PhysX::EditorJointRequestBus::Handler::BusConnect(AZ::EntityComponentIdPair(entityId, GetId())); } @@ -91,4 +90,9 @@ namespace PhysX m_config.m_followerEntity = GetEntityId(); // joint is always in the same entity as the follower body. gameEntity->CreateComponent(m_config.ToGameTimeConfig(), m_config.ToGenericProperties()); } + + AZStd::vector EditorFixedJointComponent::GetSubComponentModesState() + { + return EditorJointComponent::GetSubComponentModesState(); + } } diff --git a/Gems/PhysX/Code/Source/EditorFixedJointComponent.h b/Gems/PhysX/Code/Source/EditorFixedJointComponent.h index 10056020d8..f32243776d 100644 --- a/Gems/PhysX/Code/Source/EditorFixedJointComponent.h +++ b/Gems/PhysX/Code/Source/EditorFixedJointComponent.h @@ -37,6 +37,9 @@ namespace PhysX // EditorComponentBase void BuildGameEntity(AZ::Entity* gameEntity) override; + // EditorJointComponent + AZStd::vector GetSubComponentModesState() override; + private: using ComponentModeDelegate = AzToolsFramework::ComponentModeFramework::ComponentModeDelegate; ComponentModeDelegate m_componentModeDelegate; ///< Responsible for detecting ComponentMode activation diff --git a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp index 6676b9865e..5537502327 100644 --- a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp @@ -12,8 +12,9 @@ #include #include +#include +#include #include -#include #include #include @@ -74,7 +75,7 @@ namespace PhysX AzToolsFramework::EditorComponentSelectionRequestsBus::Handler* selection = this; m_componentModeDelegate.ConnectWithSingleComponentMode < - EditorHingeJointComponent, EditorHingeJointComponentMode>( + EditorHingeJointComponent, JointsComponentMode>( AZ::EntityComponentIdPair(entityId, GetId()), selection); PhysX::EditorJointRequestBus::Handler::BusConnect(AZ::EntityComponentIdPair(entityId, GetId())); @@ -100,23 +101,19 @@ namespace PhysX float EditorHingeJointComponent::GetLinearValue(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::MaxForce) { return m_config.m_forceMax; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::MaxTorque) { return m_config.m_torqueMax; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterTolerance) - { - return m_angularLimit.m_standardLimitConfig.m_tolerance; - } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterDamping) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::Damping) { return m_angularLimit.m_standardLimitConfig.m_damping; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterStiffness) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::Stiffness) { return m_angularLimit.m_standardLimitConfig.m_stiffness; } @@ -126,7 +123,7 @@ namespace PhysX AngleLimitsFloatPair EditorHingeJointComponent::GetLinearValuePair(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterAngularPair) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::TwistLimits) { return AngleLimitsFloatPair(m_angularLimit.m_limitPositive, m_angularLimit.m_limitNegative); } @@ -134,33 +131,41 @@ namespace PhysX return AngleLimitsFloatPair(); } - bool EditorHingeJointComponent::IsParameterUsed(const AZStd::string& parameterName) + AZStd::vector EditorHingeJointComponent::GetSubComponentModesState() { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce - || parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque - ) - { - return m_config.m_breakable; - } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterTolerance) + AZStd::vector subModes; + subModes.emplace_back(JointsComponentModeCommon::SubModeParamaterState{ + JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition, + JointsComponentModeCommon::ParamaterNames::SnapPosition }); + + if (AZStd::vector baseSubModes = + EditorJointComponent::GetSubComponentModesState(); + !baseSubModes.empty()) { - return !m_angularLimit.m_standardLimitConfig.m_isSoftLimit; + subModes.insert(subModes.end(), baseSubModes.begin(), baseSubModes.end()); } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterDamping) - { - return m_angularLimit.m_standardLimitConfig.m_isSoftLimit; - } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterStiffness) + + if (m_angularLimit.m_standardLimitConfig.m_isLimited) { - return m_angularLimit.m_standardLimitConfig.m_isSoftLimit; - } + subModes.emplace_back( + JointsComponentModeCommon::SubModeParamaterState{ JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits, + JointsComponentModeCommon::ParamaterNames::TwistLimits }); - return true; // Sub-component mode always enabled unless disabled explicitly. + if (m_angularLimit.m_standardLimitConfig.m_isSoftLimit) + { + subModes.emplace_back(JointsComponentModeCommon::SubModeParamaterState{ + JointsComponentModeCommon::SubComponentModes::ModeType::Damping, JointsComponentModeCommon::ParamaterNames::Damping }); + subModes.emplace_back( + JointsComponentModeCommon::SubModeParamaterState{ JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness, + JointsComponentModeCommon::ParamaterNames::Stiffness }); + } + } + return subModes; } void EditorHingeJointComponent::SetBoolValue(const AZStd::string& parameterName, bool value) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterComponentMode) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::ComponentMode) { m_angularLimit.m_standardLimitConfig.m_inComponentMode = value; m_config.m_inComponentMode = value; @@ -169,31 +174,23 @@ namespace PhysX &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay , AzToolsFramework::Refresh_EntireTree); } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterSelectOnSnap) - { - m_config.m_selectLeadOnSnap = value; - } } void EditorHingeJointComponent::SetLinearValue(const AZStd::string& parameterName, float value) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::MaxForce) { m_config.m_forceMax = value; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::MaxTorque) { m_config.m_torqueMax = value; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterTolerance) - { - m_angularLimit.m_standardLimitConfig.m_tolerance = value; - } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterDamping) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::Damping) { m_angularLimit.m_standardLimitConfig.m_damping = value; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterStiffness) + else if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::Stiffness) { m_angularLimit.m_standardLimitConfig.m_stiffness = value; } @@ -201,7 +198,7 @@ namespace PhysX void EditorHingeJointComponent::SetLinearValuePair(const AZStd::string& parameterName, const AngleLimitsFloatPair& valuePair) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterAngularPair) + if (parameterName == PhysX::JointsComponentModeCommon::ParamaterNames::TwistLimits) { m_angularLimit.m_limitPositive = valuePair.first; m_angularLimit.m_limitNegative = valuePair.second; @@ -267,7 +264,7 @@ namespace PhysX EditorJointRequestBus::EventResult(localTransform, AZ::EntityComponentIdPair(entityId, GetId()), &EditorJointRequests::GetTransformValue, - PhysX::EditorJointComponentMode::s_parameterTransform); + PhysX::JointsComponentModeCommon::ParamaterNames::Transform); debugDisplay.PushMatrix(worldTransform); debugDisplay.PushMatrix(localTransform); diff --git a/Gems/PhysX/Code/Source/EditorHingeJointComponent.h b/Gems/PhysX/Code/Source/EditorHingeJointComponent.h index 7c5bd8ad2f..6a2010464e 100644 --- a/Gems/PhysX/Code/Source/EditorHingeJointComponent.h +++ b/Gems/PhysX/Code/Source/EditorHingeJointComponent.h @@ -41,7 +41,7 @@ namespace PhysX // PhysX::EditorJointRequests float GetLinearValue(const AZStd::string& parameterName) override; AngleLimitsFloatPair GetLinearValuePair(const AZStd::string& parameterName) override; - bool IsParameterUsed(const AZStd::string& parameterName) override; + AZStd::vector GetSubComponentModesState() override; void SetBoolValue(const AZStd::string& parameterName, bool value) override; void SetLinearValue(const AZStd::string& parameterName, float value) override; void SetLinearValuePair(const AZStd::string& parameterName, const AngleLimitsFloatPair& valuePair) override; diff --git a/Gems/PhysX/Code/Source/EditorJointComponent.cpp b/Gems/PhysX/Code/Source/EditorJointComponent.cpp index 7b2d612cf1..88e192026d 100644 --- a/Gems/PhysX/Code/Source/EditorJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorJointComponent.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include @@ -134,21 +134,18 @@ namespace PhysX bool EditorJointComponent::GetBoolValue(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterComponentMode) + if (parameterName == JointsComponentModeCommon::ParamaterNames::ComponentMode) { return m_config.m_inComponentMode; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterSelectOnSnap) - { - return m_config.m_selectLeadOnSnap; - } + AZ_Error("EditorJointComponent::GetBoolValue", false, "bool parameter not recognized: %s", parameterName.c_str()); return false; } AZ::EntityId EditorJointComponent::GetEntityIdValue(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterLeadEntity) + if (parameterName == JointsComponentModeCommon::ParamaterNames::LeadEntity) { return m_config.m_leadEntity; } @@ -160,11 +157,11 @@ namespace PhysX float EditorJointComponent::GetLinearValue(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce) + if (parameterName == JointsComponentModeCommon::ParamaterNames::MaxForce) { return m_config.m_forceMax; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque) + else if (parameterName == JointsComponentModeCommon::ParamaterNames::MaxTorque) { return m_config.m_torqueMax; } @@ -181,7 +178,7 @@ namespace PhysX AZ::Transform EditorJointComponent::GetTransformValue(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterTransform) + if (parameterName == JointsComponentModeCommon::ParamaterNames::Transform) { return AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateFromEulerAnglesDegrees(m_config.m_localRotation), m_config.m_localPosition); @@ -192,11 +189,11 @@ namespace PhysX AZ::Vector3 EditorJointComponent::GetVector3Value(const AZStd::string& parameterName) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterPosition) + if (parameterName == JointsComponentModeCommon::ParamaterNames::Position) { return m_config.m_localPosition; } - if (parameterName == PhysX::EditorJointComponentMode::s_parameterRotation) + if (parameterName == JointsComponentModeCommon::ParamaterNames::Rotation) { return m_config.m_localRotation; } @@ -204,24 +201,27 @@ namespace PhysX return AZ::Vector3::CreateZero(); } - bool EditorJointComponent::IsParameterUsed(const AZStd::string& parameterName) + AZStd::vector EditorJointComponent::GetSubComponentModesState() { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce - || parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque - ) + AZStd::vector subModes; + if (m_config.m_breakable) { - return m_config.m_breakable; + subModes.emplace_back(JointsComponentModeCommon::SubModeParamaterState{ + JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce, JointsComponentModeCommon::ParamaterNames::MaxForce }); + subModes.emplace_back(JointsComponentModeCommon::SubModeParamaterState{ + JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque, + JointsComponentModeCommon::ParamaterNames::MaxTorque }); } - return true; // Sub-component mode always enabled unless disabled explicitly. + return subModes; } void EditorJointComponent::SetLinearValue(const AZStd::string& parameterName, float value) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxForce) + if (parameterName == JointsComponentModeCommon::ParamaterNames::MaxForce) { m_config.m_forceMax = value; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterMaxTorque) + else if (parameterName == JointsComponentModeCommon::ParamaterNames::MaxTorque) { m_config.m_torqueMax = value; } @@ -235,11 +235,11 @@ namespace PhysX void EditorJointComponent::SetVector3Value(const AZStd::string& parameterName, const AZ::Vector3& value) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterPosition) + if (parameterName == JointsComponentModeCommon::ParamaterNames::Position) { m_config.m_localPosition = value; } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterRotation) + else if (parameterName == JointsComponentModeCommon::ParamaterNames::Rotation) { m_config.m_localRotation = value; } @@ -247,7 +247,7 @@ namespace PhysX void EditorJointComponent::SetBoolValue(const AZStd::string& parameterName, bool value) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterComponentMode) + if (parameterName == JointsComponentModeCommon::ParamaterNames::ComponentMode) { m_config.m_inComponentMode = value; @@ -255,15 +255,11 @@ namespace PhysX &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay , AzToolsFramework::Refresh_EntireTree); } - else if (parameterName == PhysX::EditorJointComponentMode::s_parameterSelectOnSnap) - { - m_config.m_selectLeadOnSnap = value; - } } void EditorJointComponent::SetEntityIdValue(const AZStd::string& parameterName, AZ::EntityId value) { - if (parameterName == PhysX::EditorJointComponentMode::s_parameterLeadEntity) + if (parameterName == JointsComponentModeCommon::ParamaterNames::LeadEntity) { m_config.SetLeadEntityId(value); } diff --git a/Gems/PhysX/Code/Source/EditorJointComponent.h b/Gems/PhysX/Code/Source/EditorJointComponent.h index 8e92e9820a..69142e6aed 100644 --- a/Gems/PhysX/Code/Source/EditorJointComponent.h +++ b/Gems/PhysX/Code/Source/EditorJointComponent.h @@ -56,7 +56,7 @@ namespace PhysX AngleLimitsFloatPair GetLinearValuePair(const AZStd::string& parameterName) override; AZ::Transform GetTransformValue(const AZStd::string& parameterName) override; AZ::Vector3 GetVector3Value(const AZStd::string& parameterName) override; - bool IsParameterUsed(const AZStd::string& parameterName) override; + AZStd::vector GetSubComponentModesState() override; void SetBoolValue(const AZStd::string& parameterName, bool value) override; void SetEntityIdValue(const AZStd::string& parameterName, AZ::EntityId value) override; void SetLinearValue(const AZStd::string& parameterName, float value) override; diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 57bfe17a30..2bbc04faae 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -676,7 +676,6 @@ namespace PhysX LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusDisconnect(); - AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); AzToolsFramework::Components::EditorComponentBase::Deactivate(); if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 792d3c4327..431b34b1ae 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -58,7 +57,6 @@ namespace PhysX //! component to create geometry in the PhysX simulation. class EditorShapeColliderComponent : public AzToolsFramework::Components::EditorComponentBase - , protected AzFramework::EntityDebugDisplayEventBus::Handler , protected AzToolsFramework::EntitySelectionEvents::Bus::Handler , private AZ::TransformNotificationBus::Handler , protected DebugDraw::DisplayCallback diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp index bb32f707be..4a4f12558d 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/PhysX/Code/physx_editor_files.cmake b/Gems/PhysX/Code/physx_editor_files.cmake index 82f26feff3..5c0c038976 100644 --- a/Gems/PhysX/Code/physx_editor_files.cmake +++ b/Gems/PhysX/Code/physx_editor_files.cmake @@ -82,7 +82,6 @@ set(FILES Editor/ComboBoxEditButtonPair.cpp Editor/ColliderComponentMode.h Editor/ColliderComponentMode.cpp - Editor/ColliderSubComponentMode.h Editor/ColliderOffsetMode.h Editor/ColliderOffsetMode.cpp Editor/ColliderBoxMode.h @@ -99,36 +98,35 @@ set(FILES Editor/DebugDraw.h Editor/PolygonPrismMeshUtils.cpp Editor/PolygonPrismMeshUtils.h - Editor/EditorJointComponentMode.cpp - Editor/EditorJointComponentMode.h + Editor/EditorJointCommon.h Editor/EditorJointConfiguration.cpp Editor/EditorJointConfiguration.h - Editor/EditorJointTypeDrawer.cpp - Editor/EditorJointTypeDrawer.h - Editor/EditorJointTypeDrawerBus.h - Editor/EditorSubComponentModeAngleCone.cpp - Editor/EditorSubComponentModeAngleCone.h - Editor/EditorSubComponentModeAnglePair.cpp - Editor/EditorSubComponentModeAnglePair.h - Editor/EditorSubComponentModeBase.cpp - Editor/EditorSubComponentModeBase.h - Editor/EditorSubComponentModeLinear.cpp - Editor/EditorSubComponentModeLinear.h - Editor/EditorSubComponentModeRotation.cpp - Editor/EditorSubComponentModeRotation.h - Editor/EditorSubComponentModeSnap.cpp - Editor/EditorSubComponentModeSnap.h - Editor/EditorSubComponentModeSnapPosition.cpp - Editor/EditorSubComponentModeSnapPosition.h - Editor/EditorSubComponentModeSnapRotation.cpp - Editor/EditorSubComponentModeSnapRotation.h - Editor/EditorSubComponentModeVec3.cpp - Editor/EditorSubComponentModeVec3.h Editor/EditorViewportEntityPicker.cpp Editor/EditorViewportEntityPicker.h Editor/Source/Components/EditorSystemComponent.h Editor/Source/Components/EditorSystemComponent.cpp + Editor/Source/ComponentModes/Joints/JointsComponentMode.h + Editor/Source/ComponentModes/Joints/JointsComponentMode.cpp + Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h + Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.cpp + Editor/Source/ComponentModes/PhysXSubComponentModeBase.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.cpp + Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.cpp + Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.cpp + Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.cpp + Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.cpp + Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.cpp + Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.cpp + Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.h + Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.cpp Editor/Source/Configuration/PhysXEditorSettingsRegistryManager.h Editor/Source/Configuration/PhysXEditorSettingsRegistryManager.cpp ) diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp index 7d3096a6da..ab6770c3fe 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp @@ -7,6 +7,7 @@ */ #include "PrefabBuilderTests.h" +#include #include #include #include diff --git a/Gems/PythonAssetBuilder/Code/Source/PythonBuilderNotificationHandler.h b/Gems/PythonAssetBuilder/Code/Source/PythonBuilderNotificationHandler.h index 669ef21914..099bc9d587 100644 --- a/Gems/PythonAssetBuilder/Code/Source/PythonBuilderNotificationHandler.h +++ b/Gems/PythonAssetBuilder/Code/Source/PythonBuilderNotificationHandler.h @@ -9,6 +9,7 @@ #include #include +#include namespace PythonAssetBuilder { diff --git a/Gems/PythonAssetBuilder/Code/Source/PythonBuilderWorker.cpp b/Gems/PythonAssetBuilder/Code/Source/PythonBuilderWorker.cpp index 0e05c5e7bf..f06b4a22d7 100644 --- a/Gems/PythonAssetBuilder/Code/Source/PythonBuilderWorker.cpp +++ b/Gems/PythonAssetBuilder/Code/Source/PythonBuilderWorker.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -56,7 +57,7 @@ namespace PythonAssetBuilder { this->ProcessJob(request, response); }; - + // connect to the shutdown signal handler AssetBuilderCommandBus::Handler::BusConnect(m_assetBuilderDesc->m_busId); diff --git a/Gems/SceneProcessing/Code/Source/Config/Components/SceneProcessingConfigSystemComponent.cpp b/Gems/SceneProcessing/Code/Source/Config/Components/SceneProcessingConfigSystemComponent.cpp index ea0c3520bf..281def11b5 100644 --- a/Gems/SceneProcessing/Code/Source/Config/Components/SceneProcessingConfigSystemComponent.cpp +++ b/Gems/SceneProcessing/Code/Source/Config/Components/SceneProcessingConfigSystemComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include diff --git a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp index 9c3cdca3e8..3c168855d8 100644 --- a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp +++ b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace ScriptCanvasBuilderCpp { diff --git a/Gems/ScriptCanvas/Code/Editor/Settings.h b/Gems/ScriptCanvas/Code/Editor/Settings.h index 7eff40ec43..eefb7454c3 100644 --- a/Gems/ScriptCanvas/Code/Editor/Settings.h +++ b/Gems/ScriptCanvas/Code/Editor/Settings.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/AssetGraphSceneDataBus.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/AssetGraphSceneDataBus.h index 2b53cf9fd5..cbb8ef12d6 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/AssetGraphSceneDataBus.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/AssetGraphSceneDataBus.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/RuntimeAsset.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/RuntimeAsset.cpp index e708efc71f..0dcd8ebbbd 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/RuntimeAsset.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/RuntimeAsset.cpp @@ -9,6 +9,7 @@ #include "RuntimeAsset.h" #include +#include namespace ScriptCanvasRuntimeAssetCpp { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja index 64e92c1ac7..1ddffe3de9 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja @@ -14,6 +14,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT {% endmacro %} #include #include +#include #include #include diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja index 236866ffeb..f80c932735 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja @@ -15,6 +15,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include #include #include #include diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.cpp index 8d324e95c0..4d7c8a2cda 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.cpp @@ -503,7 +503,7 @@ namespace ScriptCanvas void InitializeInterpretedStatics(const RuntimeData& runtimeData) { -#if defined(PROFILE) || defined(AZ_DEBUG_BUILD) +#if defined(AZ_PROFILE_BUILD) || defined(AZ_DEBUG_BUILD) Execution::InitializeFromLuaStackFunctions(const_cast(runtimeData.m_debugMap)); #endif if (runtimeData.RequiresStaticInitialization()) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp index 6cda0b75dd..542fc2f0c1 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp @@ -14,6 +14,7 @@ #include #include +#include namespace FunctionDefinitionNodeCpp { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp index 7e3f5f7d24..ca57824b78 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp @@ -652,8 +652,13 @@ namespace ScriptCanvas return {}; } - bool Method::GetBehaviorContextClassMethod(const AZStd::string&, const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const + bool Method::GetBehaviorContextClassMethod(const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const { + if (m_lookupName.empty() && m_className.empty()) + { + return false; + } + AZStd::string prettyClassName; AZStd::string methodName = m_lookupName; @@ -749,15 +754,14 @@ namespace ScriptCanvas AZStd::tuple Method::LookupMethod() const { using TupleType = AZStd::tuple; - AZStd::string methodName = m_lookupName; - + AZStd::string prettyClassName; const AZ::BehaviorClass* bcClass{}; const AZ::BehaviorMethod* method{}; EventType eventType; - if (GetBehaviorContextClassMethod(m_lookupName, bcClass, method, eventType)) + if (GetBehaviorContextClassMethod(bcClass, method, eventType)) { return TupleType{ method, m_methodType, eventType, bcClass }; } @@ -776,7 +780,7 @@ namespace ScriptCanvas const AZ::BehaviorMethod* method{}; EventType eventType; - if (GetBehaviorContextClassMethod(m_lookupName, bcClass, method, eventType)) + if (GetBehaviorContextClassMethod(bcClass, method, eventType)) { m_eventType = eventType; ConfigureMethod(*method, bcClass); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h index 1f1daddc71..8a8dc09856 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h @@ -168,7 +168,7 @@ namespace ScriptCanvas AZ_INLINE void SetWarnOnMissingFunction(bool enabled) { m_warnOnMissingFunction = enabled; } - bool GetBehaviorContextClassMethod(const AZStd::string& name, const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const; + bool GetBehaviorContextClassMethod(const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const; private: friend struct ScriptCanvas::BehaviorContextMethodHelper; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersionConverters.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersionConverters.cpp index 2998b3c2ee..23e4a8cc7a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersionConverters.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersionConverters.cpp @@ -7,6 +7,7 @@ */ #include "VersionConverters.h" +#include #include #include diff --git a/Gems/ScriptCanvas/Code/Tests/ScriptCanvasBuilderTests.cpp b/Gems/ScriptCanvas/Code/Tests/ScriptCanvasBuilderTests.cpp index 5b38b640ca..cba14feac6 100644 --- a/Gems/ScriptCanvas/Code/Tests/ScriptCanvasBuilderTests.cpp +++ b/Gems/ScriptCanvas/Code/Tests/ScriptCanvasBuilderTests.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationTest.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationTest.h index 4a3ba89832..1e4fb27831 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationTest.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationTest.h @@ -11,6 +11,7 @@ #include #include +#include #include #include diff --git a/Gems/ScriptCanvasTesting/Code/Source/Nodes/BehaviorContextObjectTestNode.h b/Gems/ScriptCanvasTesting/Code/Source/Nodes/BehaviorContextObjectTestNode.h index 2093912dd9..cc7bda1c95 100644 --- a/Gems/ScriptCanvasTesting/Code/Source/Nodes/BehaviorContextObjectTestNode.h +++ b/Gems/ScriptCanvasTesting/Code/Source/Nodes/BehaviorContextObjectTestNode.h @@ -10,6 +10,9 @@ #include +#include +#include + namespace ScriptCanvasTestingNodes { //! This object is used to test the use of BehaviorContext classes diff --git a/Gems/ScriptEvents/Code/Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBroadcast.h b/Gems/ScriptEvents/Code/Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBroadcast.h index de2563c6ad..d29bf5910c 100644 --- a/Gems/ScriptEvents/Code/Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBroadcast.h +++ b/Gems/ScriptEvents/Code/Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBroadcast.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -38,8 +39,8 @@ namespace ScriptEvents void ReserveArguments(size_t numArguments); - size_t GetNumArguments() const override { return m_behaviorParameters.size(); } - const AZ::BehaviorParameter* GetArgument(size_t index) const override + size_t GetNumArguments() const override { return m_behaviorParameters.size(); } + const AZ::BehaviorParameter* GetArgument(size_t index) const override { if (index >= m_behaviorParameters.size()) { diff --git a/Gems/ScriptEvents/Code/Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventMethod.h b/Gems/ScriptEvents/Code/Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventMethod.h index 0ff8194e56..a0893417fc 100644 --- a/Gems/ScriptEvents/Code/Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventMethod.h +++ b/Gems/ScriptEvents/Code/Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventMethod.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -38,8 +39,8 @@ namespace ScriptEvents void ReserveArguments(size_t numArguments); - size_t GetNumArguments() const override { return m_behaviorParameters.size(); } - const AZ::BehaviorParameter* GetArgument(size_t index) const override + size_t GetNumArguments() const override { return m_behaviorParameters.size(); } + const AZ::BehaviorParameter* GetArgument(size_t index) const override { if (index >= m_behaviorParameters.size()) { @@ -47,7 +48,7 @@ namespace ScriptEvents return nullptr; } - return &m_behaviorParameters[index]; + return &m_behaviorParameters[index]; } const AZStd::string* GetArgumentName(size_t index) const override { return &m_argumentNames[index]; } diff --git a/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventMethod.h b/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventMethod.h index a04b5969bf..77f8d1cbd4 100644 --- a/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventMethod.h +++ b/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventMethod.h @@ -9,10 +9,12 @@ #pragma once #include -#include -#include #include +namespace AZ +{ + class ReflectContext; +} namespace ScriptEvents { //! Holds the versioned definition for each of a script events. @@ -21,7 +23,6 @@ namespace ScriptEvents class Method { public: - AZ_TYPE_INFO(Method, "{E034EA83-C798-413D-ACE8-4923C51CF4F7}"); Method() @@ -67,29 +68,7 @@ namespace ScriptEvents FromScript(dc); } - void FromScript(AZ::ScriptDataContext& dc) - { - if (dc.GetNumArguments() > 0) - { - AZStd::string name; - if (dc.IsString(0) && dc.ReadArg(0, name)) - { - m_name.Set(name.c_str()); - } - - if (dc.GetNumArguments() > 1) - { - AZ::Uuid returnType; - if (dc.ReadArg(1, returnType)) - { - m_returnType.Set(returnType); - } - } - } - - //AZ_TracePrintf("Script Events", "Added Script Method: %s (return type: %s)\n", GetName().c_str(), m_returnType.IsEmpty() ? "none" : GetReturnType().ToString().c_str()); - - } + void FromScript(AZ::ScriptDataContext& dc); ~Method() { @@ -111,150 +90,71 @@ namespace ScriptEvents return m_parameters.back(); } - static void Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Field("m_name", &Method::m_name) - ->Field("m_tooltip", &Method::m_tooltip) - ->Field("m_returnType", &Method::m_returnType) - ->Field("m_parameters", &Method::m_parameters) - ; - - if (AZ::EditContext* editContext = serializeContext->GetEditContext()) - { - editContext->Class("Script Event", "A script event's definition") - ->DataElement(AZ::Edit::UIHandlers::Default, &Method::m_name, "Name", "The specified name for this event, represents a callable function (i.e. MyScriptEvent())") - ->DataElement(AZ::Edit::UIHandlers::Default, &Method::m_tooltip, "Tooltip", "A description of this event") - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &Method::m_returnType, "Return value type", "the typeid of the return value, ex. AZ::type_info::Uuid foo()") - ->Attribute(AZ::Edit::Attributes::GenericValueList, &Types::GetValidReturnTypes) - ->DataElement(AZ::Edit::UIHandlers::Default, &Method::m_parameters, "Parameters", "A list of parameters for the EBus event, ex. void foo(Parameter1, Parameter2)") - ; - } - } + static void Reflect(AZ::ReflectContext* context); - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class("Method") - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Method("AddParameter", &Method::AddParameter) - ->Property("Name", BehaviorValueProperty(&Method::m_name)) - ->Property("ReturnType", BehaviorValueProperty(&Method::m_returnType)) - ->Property("Parameters", BehaviorValueProperty(&Method::m_parameters)) - ; - } + AZStd::string GetName() const + { + return m_name.Get() ? *m_name.Get() : ""; } - - AZStd::string GetName() const { return m_name.Get() ? *m_name.Get() : ""; } - AZStd::string GetTooltip() const { return m_tooltip.Get() ? *m_tooltip.Get() : ""; } - const AZ::Uuid GetReturnType() const { return m_returnType.Get() ? *m_returnType.Get() : AZ::Uuid::CreateNull(); } - const AZStd::vector& GetParameters() const { return m_parameters; } - - ScriptEventData::VersionedProperty& GetNameProperty() { return m_name; } - ScriptEventData::VersionedProperty& GetTooltipProperty() { return m_tooltip; } - ScriptEventData::VersionedProperty& GetReturnTypeProperty() { return m_returnType; } - - const ScriptEventData::VersionedProperty& GetNameProperty() const { return m_name; } - const ScriptEventData::VersionedProperty& GetTooltipProperty() const { return m_tooltip; } - const ScriptEventData::VersionedProperty& GetReturnTypeProperty() const { return m_returnType; } - - AZ::Crc32 GetEventId() const { return AZ::Crc32(GetNameProperty().GetId().ToString().c_str()); } - - //! Validates that the asset data being stored is valid and supported. - AZ::Outcome Validate() const + AZStd::string GetTooltip() const { - const AZStd::string name = GetName(); - const AZ::Uuid returnType = GetReturnType(); - - // Validate address type - if (!Types::IsValidReturnType(returnType)) - { - return AZ::Failure(AZStd::string::format("The specified type %s is not valid as return type for Script Event: %s", returnType.ToString().c_str(), name.c_str())); - } - - // Definition name cannot be empty - if (name.empty()) - { - return AZ::Failure(AZStd::string("Definition name cannot be empty")); - } - - // Name cannot start with a number - if (isdigit(name.at(0))) - { - return AZ::Failure(AZStd::string::format("%s, names cannot start with a number", name.c_str())); - } - - // Conform to valid function names - AZStd::smatch match; + return m_tooltip.Get() ? *m_tooltip.Get() : ""; + } - // Ascii-only - AZStd::regex asciionly_regex("[^\x0A\x0D\x20-\x7E]"); - AZStd::regex_match(name, match, asciionly_regex); - if (!match.empty()) - { - return AZ::Failure(AZStd::string::format("%s, invalid name, names may only contain ASCII characters", name.c_str())); - } + const AZ::Uuid GetReturnType() const + { + return m_returnType.Get() ? *m_returnType.Get() : AZ::Uuid::CreateNull(); + } - AZStd::regex validate_regex("[_[:alpha:]][_[:alnum:]]*"); - AZStd::regex_match(name, match, validate_regex); - if (match.empty()) - { - return AZ::Failure(AZStd::string::format("%s, invalid name specified", name.c_str())); - } + const AZStd::vector& GetParameters() const + { + return m_parameters; + } - AZStd::string parameterName; - int parameterIndex = 0; - for (const Parameter& parameter : m_parameters) - { - auto outcome = parameter.Validate(); - if (!outcome.IsSuccess()) - { - return outcome; - } + ScriptEventData::VersionedProperty& GetNameProperty() + { + return m_name; + } - if (parameter.GetName().compare(parameterName) == 0) - { - return AZ::Failure(AZStd::string::format("Cannot have duplicate parameter names (%d: %s) make sure each parameter name is unique", parameterIndex, parameterName.c_str())); - } + ScriptEventData::VersionedProperty& GetTooltipProperty() + { + return m_tooltip; + } - parameterName = parameter.GetName(); - ++parameterIndex; - } + ScriptEventData::VersionedProperty& GetReturnTypeProperty() + { + return m_returnType; + } - return AZ::Success(true); + const ScriptEventData::VersionedProperty& GetNameProperty() const + { + return m_name; } - void PreSave() + const ScriptEventData::VersionedProperty& GetTooltipProperty() const { - m_name.PreSave(); - m_tooltip.PreSave(); - m_returnType.PreSave(); + return m_tooltip; + } - for (Parameter parameter : m_parameters) - { - parameter.PreSave(); - } + const ScriptEventData::VersionedProperty& GetReturnTypeProperty() const + { + return m_returnType; } - void Flatten() + AZ::Crc32 GetEventId() const { - m_name.Flatten(); - m_tooltip.Flatten(); - m_returnType.Flatten(); - for (Parameter& parameter : m_parameters) - { - parameter.Flatten(); - } + return AZ::Crc32(GetNameProperty().GetId().ToString().c_str()); } + //! Validates that the asset data being stored is valid and supported. + AZ::Outcome Validate() const; + void PreSave(); + void Flatten(); private: - ScriptEventData::VersionedProperty m_name; ScriptEventData::VersionedProperty m_tooltip; ScriptEventData::VersionedProperty m_returnType; AZStd::vector m_parameters; + }; - }; - -} +} // namespace ScriptEvents diff --git a/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventParameter.h b/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventParameter.h index 3871a04be5..ad5b872d79 100644 --- a/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventParameter.h +++ b/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventParameter.h @@ -9,10 +9,7 @@ #pragma once #include -#include #include -#include -#include namespace ScriptEvents { @@ -45,109 +42,11 @@ namespace ScriptEvents FromScript(dc); } - void FromScript(AZ::ScriptDataContext& dc) - { - if (dc.GetNumArguments() > 0) - { - AZStd::string name; - if (dc.ReadArg(0, name)) - { - m_name.Set(name.c_str()); - } - - if (dc.GetNumArguments() > 1) - { - AZ::Uuid parameterType; - if (dc.ReadArg(1, parameterType)) - { - m_type.Set(parameterType); - } - } - } - - //AZ_TracePrintf("Script Events", "Added Parameter: %s (type: %s)\n", GetName().c_str(), GetType().ToString() .c_str()); - - } + void FromScript(AZ::ScriptDataContext& dc); - static void Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Field("m_name", &Parameter::m_name) - ->Field("m_tooltip", &Parameter::m_tooltip) - ->Field("m_type", &Parameter::m_type) - ; - - if (AZ::EditContext* editContext = serializeContext->GetEditContext()) - { - editContext->Class("A Script Event's method parameter", "A parameter to a Script Event's event definition") - ->DataElement(AZ::Edit::UIHandlers::Default, &Parameter::m_name, "Name", "Name of the parameter, ex. void foo(int thisIsTheParameterName)") - ->DataElement(AZ::Edit::UIHandlers::Default, &Parameter::m_tooltip, "Tooltip", "A description of this parameter") - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &Parameter::m_type, "Type", "The typeid of the parameter, ex. void foo(AZ::type_info::Uuid())") - ->Attribute(AZ::Edit::Attributes::GenericValueList, &Types::GetValidParameterTypes) - ; - } - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class("Parameter") - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Property("Name", BehaviorValueProperty(&Parameter::m_name)) - ->Property("Type", BehaviorValueProperty(&Parameter::m_type)) - ; - } - } - - AZ::Outcome Validate() const - { - const AZStd::string& name = GetName(); - const AZ::Uuid* parameterType = m_type.Get(); - - AZ_Assert(parameterType && !parameterType->IsNull(), "The Parameter type should not be null"); - - // Validate address type - if (!Types::IsValidParameterType(*parameterType)) - { - return AZ::Failure(AZStd::string::format("The specified type %s is not valid as parameter type for Script Event: %s", (*parameterType).ToString().c_str(), name.c_str())); - } - - // Definition name cannot be empty - if (name.empty()) - { - return AZ::Failure(AZStd::string("Definition name cannot be empty")); - } - - // Name cannot start with a number - if (isdigit(name.at(0))) - { - return AZ::Failure(AZStd::string::format("%s, names cannot start with a number", name.c_str())); - } - - // Conform to valid function names - AZStd::smatch match; - - // Ascii-only - AZStd::regex asciionly_regex("[^\x0A\x0D\x20-\x7E]"); - AZStd::regex_match(name, match, asciionly_regex); - if (match.size() > 0) - { - return AZ::Failure(AZStd::string::format("%s, invalid name, names may only contain ASCII characters", name.c_str())); - } - - // Function name syntax - AZStd::regex validate_regex("[_[:alpha:]][_[:alnum:]]*"); - AZStd::regex_match(name, match, validate_regex); - if (match.size() == 0) - { - return AZ::Failure(AZStd::string::format("%s, invalid name specified", name.c_str())); - } - - return AZ::Success(true); - - } + static void Reflect(AZ::ReflectContext* context); + AZ::Outcome Validate() const; AZStd::string GetName() const { return m_name.Get() ? *m_name.Get() : ""; } AZStd::string GetTooltip() const { return m_tooltip.Get() ? *m_tooltip.Get() : ""; } AZ::Uuid GetType() const { return m_type.Get() ? *m_type.Get() : AZ::Uuid::CreateNull(); } diff --git a/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventTypes.h b/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventTypes.h index a523d932ea..96233e5867 100644 --- a/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventTypes.h +++ b/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventTypes.h @@ -10,6 +10,14 @@ #include +#include + +namespace AZ +{ + class BehaviorClass; + class BehaviorMethod; +} + namespace ScriptEvents { namespace Types diff --git a/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventsAssetRef.h b/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventsAssetRef.h index 619dd42483..526a36f6b9 100644 --- a/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventsAssetRef.h +++ b/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventsAssetRef.h @@ -9,12 +9,10 @@ #include #include -#include #include #include #include #include -#include #include @@ -38,40 +36,7 @@ namespace ScriptEvents using AssetChangedCB = AZStd::function&, void* userData)>; - static void Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(0) - ->Field("Asset", &ScriptEventsAssetRef::m_asset) - ; - - if (AZ::EditContext* editContext = serializeContext->GetEditContext()) - { - editContext->Class("Script Event Asset", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(AZ::Edit::UIHandlers::Default, &ScriptEventsAssetRef::m_asset, "Script Event Asset", "") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &ScriptEventsAssetRef::OnAssetChanged) - //TODO #lsempe: hook up to open Asset Editor when ready - //->Attribute("EditButton", "") - //->Attribute("EditDescription", "Open in Script Canvas Editor") - //->Attribute("EditCallback", &ScriptEventsAssetRef::LaunchScriptCanvasEditor) - ; - } - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) - ->Attribute(AZ::Script::Attributes::ConstructibleFromNil, false) - ->Method("Get", &ScriptEventsAssetRef::GetDefinition) - ; - } - } + static void Reflect(AZ::ReflectContext* context); ScriptEventsAssetRef() = default; @@ -100,98 +65,22 @@ namespace ScriptEvents return nullptr; } - void SetAsset(const AZ::Data::Asset& asset) - { - m_asset = asset; - - if (m_asset.IsReady()) - { - if (ScriptEventsAsset* scriptEventAsset = m_asset.GetAs()) - { - scriptEventAsset->m_definition.RegisterInternal(); - } - } - else - { - if (AZ::Data::AssetBus::Handler::BusIsConnectedId(m_asset.GetId())) - { - AZ::Data::AssetBus::Handler::BusDisconnect(m_asset.GetId()); - } - - AZ::Data::AssetBus::Handler::BusConnect(m_asset.GetId()); - } - } + void SetAsset(const AZ::Data::Asset& asset); AZ::Data::Asset GetAsset() const { return m_asset; } - void Load(bool loadBlocking /*= false*/) - { - if (!m_asset.IsReady()) - { - AZ::Data::AssetInfo assetInfo; - AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, m_asset.GetId()); - if (assetInfo.m_assetId.IsValid()) - { - auto& assetManager = AZ::Data::AssetManager::Instance(); - m_asset = assetManager.GetAsset(m_asset.GetId(), azrtti_typeid(), m_asset.GetAutoLoadBehavior()); - - if(loadBlocking) - { - m_asset.BlockUntilLoadComplete(); - } - } - } - } - - AZ::u32 OnAssetChanged() - { - SetAsset(m_asset); - Load(false); - - if (m_assetNotifyCallback) - { - m_assetNotifyCallback(m_asset, m_userData); - } - - return AZ::Edit::PropertyRefreshLevels::None; - } + void Load(bool loadBlocking /*= false*/); + AZ::u32 OnAssetChanged(); //===================================================================== // AZ::Data::AssetBus - void OnAssetReady(AZ::Data::Asset asset) override - { - if (ScriptEventsAsset* scriptEventAsset = m_asset.GetAs()) - { - scriptEventAsset->m_definition.RegisterInternal(); - } - } - - void OnAssetReloaded(AZ::Data::Asset asset) override - { - SetAsset(asset); - - if (m_assetNotifyCallback) - { - m_assetNotifyCallback(m_asset, m_userData); - } - } - - void OnAssetUnloaded([[maybe_unused]] const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetType assetType) override - { - if (ScriptEventsAsset* ebusAsset = m_asset.GetAs()) - { - bool isRegistered = false; - //ScriptEventsLegacy::RegistrationRequestBus::BroadcastResult(isRegistered, &ScriptEventsLegacy::RegistrationRequestBus::Events::IsBusRegistered, ebusAsset->m_scriptEventsDefinition.m_name); - if (isRegistered) - { - //ScriptEventsLegacy::RegistrationRequestBus::Broadcast(&ScriptEventsLegacy::RegistrationRequestBus::Events::Unregister, ebusAsset->m_scriptEventsDefinition.m_name); - } - } - } + void OnAssetReady(AZ::Data::Asset asset) override; + void OnAssetReloaded(AZ::Data::Asset asset) override; + void OnAssetUnloaded(const AZ::Data::AssetId assetId, const AZ::Data::AssetType assetType) override; void OnAssetSaved(AZ::Data::Asset asset, [[maybe_unused]] bool isSuccessful) override { diff --git a/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsEditorGem.cpp b/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsEditorGem.cpp index 8ac100c01f..8ab014ec15 100644 --- a/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsEditorGem.cpp +++ b/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsEditorGem.cpp @@ -13,6 +13,7 @@ #include #include +#include #if defined(SCRIPTEVENTS_EDITOR) namespace ScriptEvents diff --git a/Gems/ScriptEvents/Code/Source/ScriptEventMethod.cpp b/Gems/ScriptEvents/Code/Source/ScriptEventMethod.cpp new file mode 100644 index 0000000000..5b7bb590af --- /dev/null +++ b/Gems/ScriptEvents/Code/Source/ScriptEventMethod.cpp @@ -0,0 +1,167 @@ +/* + * 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 "ScriptEvents/ScriptEventMethod.h" + +#include +#include +#include + +namespace ScriptEvents +{ + void Method::FromScript(AZ::ScriptDataContext& dc) + { + if (dc.GetNumArguments() > 0) + { + AZStd::string name; + if (dc.IsString(0) && dc.ReadArg(0, name)) + { + m_name.Set(name.c_str()); + } + + if (dc.GetNumArguments() > 1) + { + AZ::Uuid returnType; + if (dc.ReadArg(1, returnType)) + { + m_returnType.Set(returnType); + } + } + } + + // AZ_TracePrintf("Script Events", "Added Script Method: %s (return type: %s)\n", GetName().c_str(), m_returnType.IsEmpty() ? "none" + // : GetReturnType().ToString().c_str()); + } + void Method::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Field("m_name", &Method::m_name) + ->Field("m_tooltip", &Method::m_tooltip) + ->Field("m_returnType", &Method::m_returnType) + ->Field("m_parameters", &Method::m_parameters); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("Script Event", "A script event's definition") + ->DataElement( + AZ::Edit::UIHandlers::Default, &Method::m_name, "Name", + "The specified name for this event, represents a callable function (i.e. MyScriptEvent())") + ->DataElement(AZ::Edit::UIHandlers::Default, &Method::m_tooltip, "Tooltip", "A description of this event") + ->DataElement( + AZ::Edit::UIHandlers::ComboBox, &Method::m_returnType, "Return value type", + "the typeid of the return value, ex. AZ::type_info::Uuid foo()") + ->Attribute(AZ::Edit::Attributes::GenericValueList, &Types::GetValidReturnTypes) + ->DataElement( + AZ::Edit::UIHandlers::Default, &Method::m_parameters, "Parameters", + "A list of parameters for the EBus event, ex. void foo(Parameter1, Parameter2)"); + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("Method") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Method("AddParameter", &Method::AddParameter) + ->Property("Name", BehaviorValueProperty(&Method::m_name)) + ->Property("ReturnType", BehaviorValueProperty(&Method::m_returnType)) + ->Property("Parameters", BehaviorValueProperty(&Method::m_parameters)); + } + } + + AZ::Outcome Method::Validate() const + { + const AZStd::string name = GetName(); + const AZ::Uuid returnType = GetReturnType(); + + // Validate address type + if (!Types::IsValidReturnType(returnType)) + { + return AZ::Failure(AZStd::string::format( + "The specified type %s is not valid as return type for Script Event: %s", returnType.ToString().c_str(), + name.c_str())); + } + + // Definition name cannot be empty + if (name.empty()) + { + return AZ::Failure(AZStd::string("Definition name cannot be empty")); + } + + // Name cannot start with a number + if (isdigit(name.at(0))) + { + return AZ::Failure(AZStd::string::format("%s, names cannot start with a number", name.c_str())); + } + + // Conform to valid function names + AZStd::smatch match; + + // Ascii-only + AZStd::regex asciionly_regex("[^\x0A\x0D\x20-\x7E]"); + AZStd::regex_match(name, match, asciionly_regex); + if (!match.empty()) + { + return AZ::Failure(AZStd::string::format("%s, invalid name, names may only contain ASCII characters", name.c_str())); + } + + AZStd::regex validate_regex("[_[:alpha:]][_[:alnum:]]*"); + AZStd::regex_match(name, match, validate_regex); + if (match.empty()) + { + return AZ::Failure(AZStd::string::format("%s, invalid name specified", name.c_str())); + } + + AZStd::string parameterName; + int parameterIndex = 0; + for (const Parameter& parameter : m_parameters) + { + auto outcome = parameter.Validate(); + if (!outcome.IsSuccess()) + { + return outcome; + } + + if (parameter.GetName().compare(parameterName) == 0) + { + return AZ::Failure(AZStd::string::format( + "Cannot have duplicate parameter names (%d: %s) make sure each parameter name is unique", parameterIndex, + parameterName.c_str())); + } + + parameterName = parameter.GetName(); + ++parameterIndex; + } + + return AZ::Success(true); + } + + void Method::PreSave() + { + m_name.PreSave(); + m_tooltip.PreSave(); + m_returnType.PreSave(); + + for (Parameter parameter : m_parameters) + { + parameter.PreSave(); + } + } + + void Method::Flatten() + { + m_name.Flatten(); + m_tooltip.Flatten(); + m_returnType.Flatten(); + for (Parameter& parameter : m_parameters) + { + parameter.Flatten(); + } + } +} // namespace ScriptEvents diff --git a/Gems/ScriptEvents/Code/Source/ScriptEventParameter.cpp b/Gems/ScriptEvents/Code/Source/ScriptEventParameter.cpp new file mode 100644 index 0000000000..1f888de10f --- /dev/null +++ b/Gems/ScriptEvents/Code/Source/ScriptEventParameter.cpp @@ -0,0 +1,122 @@ +/* + * 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 "ScriptEvents/ScriptEventParameter.h" + +#include +#include +#include +#include + +namespace ScriptEvents +{ + void Parameter::FromScript(AZ::ScriptDataContext& dc) + { + if (dc.GetNumArguments() > 0) + { + AZStd::string name; + if (dc.ReadArg(0, name)) + { + m_name.Set(name.c_str()); + } + + if (dc.GetNumArguments() > 1) + { + AZ::Uuid parameterType; + if (dc.ReadArg(1, parameterType)) + { + m_type.Set(parameterType); + } + } + } + + // AZ_TracePrintf("Script Events", "Added Parameter: %s (type: %s)\n", GetName().c_str(), GetType().ToString() + // .c_str()); + } + + void Parameter::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Field("m_name", &Parameter::m_name) + ->Field("m_tooltip", &Parameter::m_tooltip) + ->Field("m_type", &Parameter::m_type); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("A Script Event's method parameter", "A parameter to a Script Event's event definition") + ->DataElement( + AZ::Edit::UIHandlers::Default, &Parameter::m_name, "Name", + "Name of the parameter, ex. void foo(int thisIsTheParameterName)") + ->DataElement(AZ::Edit::UIHandlers::Default, &Parameter::m_tooltip, "Tooltip", "A description of this parameter") + ->DataElement( + AZ::Edit::UIHandlers::ComboBox, &Parameter::m_type, "Type", + "The typeid of the parameter, ex. void foo(AZ::type_info::Uuid())") + ->Attribute(AZ::Edit::Attributes::GenericValueList, &Types::GetValidParameterTypes); + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("Parameter") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Property("Name", BehaviorValueProperty(&Parameter::m_name)) + ->Property("Type", BehaviorValueProperty(&Parameter::m_type)); + } + } + + AZ::Outcome Parameter::Validate() const + { + const AZStd::string& name = GetName(); + const AZ::Uuid* parameterType = m_type.Get(); + + AZ_Assert(parameterType && !parameterType->IsNull(), "The Parameter type should not be null"); + + // Validate address type + if (!Types::IsValidParameterType(*parameterType)) + { + return AZ::Failure(AZStd::string::format( + "The specified type %s is not valid as parameter type for Script Event: %s", + (*parameterType).ToString().c_str(), name.c_str())); + } + + // Definition name cannot be empty + if (name.empty()) + { + return AZ::Failure(AZStd::string("Definition name cannot be empty")); + } + + // Name cannot start with a number + if (isdigit(name.at(0))) + { + return AZ::Failure(AZStd::string::format("%s, names cannot start with a number", name.c_str())); + } + + // Conform to valid function names + AZStd::smatch match; + + // Ascii-only + AZStd::regex asciionly_regex("[^\x0A\x0D\x20-\x7E]"); + AZStd::regex_match(name, match, asciionly_regex); + if (match.size() > 0) + { + return AZ::Failure(AZStd::string::format("%s, invalid name, names may only contain ASCII characters", name.c_str())); + } + + // Function name syntax + AZStd::regex validate_regex("[_[:alpha:]][_[:alnum:]]*"); + AZStd::regex_match(name, match, validate_regex); + if (match.size() == 0) + { + return AZ::Failure(AZStd::string::format("%s, invalid name specified", name.c_str())); + } + + return AZ::Success(true); + } +} // namespace ScriptEvents diff --git a/Gems/ScriptEvents/Code/Source/ScriptEventsAssetRef.cpp b/Gems/ScriptEvents/Code/Source/ScriptEventsAssetRef.cpp new file mode 100644 index 0000000000..d0aa4e1b10 --- /dev/null +++ b/Gems/ScriptEvents/Code/Source/ScriptEventsAssetRef.cpp @@ -0,0 +1,135 @@ +/* + * 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 "ScriptEvents/ScriptEventsAssetRef.h" + +#include +#include +#include + +namespace ScriptEvents +{ + void ScriptEventsAssetRef::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class()->Version(0)->Field("Asset", &ScriptEventsAssetRef::m_asset); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("Script Event Asset", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &ScriptEventsAssetRef::m_asset, "Script Event Asset", "") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &ScriptEventsAssetRef::OnAssetChanged) + // TODO #lsempe: hook up to open Asset Editor when ready + //->Attribute("EditButton", "") + //->Attribute("EditDescription", "Open in Script Canvas Editor") + //->Attribute("EditCallback", &ScriptEventsAssetRef::LaunchScriptCanvasEditor) + ; + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Attribute(AZ::Script::Attributes::ConstructibleFromNil, false) + ->Method("Get", &ScriptEventsAssetRef::GetDefinition); + } + } + + void ScriptEventsAssetRef::SetAsset(const AZ::Data::Asset& asset) + { + m_asset = asset; + + if (m_asset.IsReady()) + { + if (ScriptEventsAsset* scriptEventAsset = m_asset.GetAs()) + { + scriptEventAsset->m_definition.RegisterInternal(); + } + } + else + { + if (AZ::Data::AssetBus::Handler::BusIsConnectedId(m_asset.GetId())) + { + AZ::Data::AssetBus::Handler::BusDisconnect(m_asset.GetId()); + } + + AZ::Data::AssetBus::Handler::BusConnect(m_asset.GetId()); + } + } + + void ScriptEventsAssetRef::Load(bool loadBlocking /*= false*/) + { + if (!m_asset.IsReady()) + { + AZ::Data::AssetInfo assetInfo; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, m_asset.GetId()); + if (assetInfo.m_assetId.IsValid()) + { + auto& assetManager = AZ::Data::AssetManager::Instance(); + m_asset = assetManager.GetAsset(m_asset.GetId(), azrtti_typeid(), m_asset.GetAutoLoadBehavior()); + + if(loadBlocking) + { + m_asset.BlockUntilLoadComplete(); + } + } + } + } + + AZ::u32 ScriptEventsAssetRef::OnAssetChanged() + { + SetAsset(m_asset); + Load(false); + + if (m_assetNotifyCallback) + { + m_assetNotifyCallback(m_asset, m_userData); + } + + return AZ::Edit::PropertyRefreshLevels::None; + } + + void ScriptEventsAssetRef::OnAssetReady(AZ::Data::Asset asset) + { + if (ScriptEventsAsset* scriptEventAsset = m_asset.GetAs()) + { + scriptEventAsset->m_definition.RegisterInternal(); + } + } + + void ScriptEventsAssetRef::OnAssetReloaded(AZ::Data::Asset asset) + { + SetAsset(asset); + + if (m_assetNotifyCallback) + { + m_assetNotifyCallback(m_asset, m_userData); + } + } + + void ScriptEventsAssetRef::OnAssetUnloaded( + [[maybe_unused]] const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetType assetType) + { + if (ScriptEventsAsset* ebusAsset = m_asset.GetAs()) + { + bool isRegistered = false; + // ScriptEventsLegacy::RegistrationRequestBus::BroadcastResult(isRegistered, + // &ScriptEventsLegacy::RegistrationRequestBus::Events::IsBusRegistered, ebusAsset->m_scriptEventsDefinition.m_name); + if (isRegistered) + { + // ScriptEventsLegacy::RegistrationRequestBus::Broadcast(&ScriptEventsLegacy::RegistrationRequestBus::Events::Unregister, + // ebusAsset->m_scriptEventsDefinition.m_name); + } + } + } +} // namespace ScriptEvents diff --git a/Gems/ScriptEvents/Code/scriptevents_common_files.cmake b/Gems/ScriptEvents/Code/scriptevents_common_files.cmake index c83518db32..5938050f5d 100644 --- a/Gems/ScriptEvents/Code/scriptevents_common_files.cmake +++ b/Gems/ScriptEvents/Code/scriptevents_common_files.cmake @@ -9,6 +9,9 @@ set(FILES Source/ScriptEventsSystemComponent.h Source/ScriptEventsSystemComponent.cpp + Source/ScriptEventParameter.cpp + Source/ScriptEventMethod.cpp + Source/ScriptEventsAssetRef.cpp Include/ScriptEvents/ScriptEventsGem.h Include/ScriptEvents/ScriptEventsAsset.h Include/ScriptEvents/ScriptEventsAsset.cpp diff --git a/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp b/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp index 42fa05473c..215efd4656 100644 --- a/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp +++ b/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp @@ -7,11 +7,12 @@ */ #include "InputConfigurationComponent.h" +#include #include +#include #include -#include #include -#include +#include #include namespace StartingPointInput diff --git a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material new file mode 100644 index 0000000000..095fbba21a --- /dev/null +++ b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "PbrTerrain.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 1, + "properties": { + "settings": { + "baseColor": [ 0.78, 0.59, 0.48 ] + } + } +} diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype new file mode 100644 index 0000000000..bf6cbbb850 --- /dev/null +++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype @@ -0,0 +1,140 @@ +{ + "description": "A material for rendering terrain with a physically-based rendering (PBR) material shading model.", + "propertyLayout": { + "version": 1, + "groups": [ + { + "id": "settings", + "displayName": "Settings" + } + ], + "properties": { + "general": [ + { + "id": "applySpecularAA", + "displayName": "Apply Specular AA", + "description": "Whether to apply specular anti-aliasing in the shader.", + "type": "Bool", + "defaultValue": false, + "connection": { + "type": "ShaderOption", + "id": "o_applySpecularAA" + } + }, + { + "id": "enableShadows", + "displayName": "Enable Shadows", + "description": "Whether to use the shadow maps.", + "type": "Bool", + "defaultValue": true, + "connection": { + "type": "ShaderOption", + "id": "o_enableShadows" + } + }, + { + "id": "enableDirectionalLights", + "displayName": "Enable Directional Lights", + "description": "Whether to use directional lights.", + "type": "Bool", + "defaultValue": true, + "connection": { + "type": "ShaderOption", + "id": "o_enableDirectionalLights" + } + }, + { + "id": "enablePunctualLights", + "displayName": "Enable Punctual Lights", + "description": "Whether to use punctual lights.", + "type": "Bool", + "defaultValue": true, + "connection": { + "type": "ShaderOption", + "id": "o_enablePunctualLights" + } + }, + { + "id": "enableAreaLights", + "displayName": "Enable Area Lights", + "description": "Whether to use area lights.", + "type": "Bool", + "defaultValue": true, + "connection": { + "type": "ShaderOption", + "id": "o_enableAreaLights" + } + }, + { + "id": "enableIBL", + "displayName": "Enable IBL", + "description": "Whether to use Image Based Lighting (IBL).", + "type": "Bool", + "defaultValue": true, + "connection": { + "type": "ShaderOption", + "id": "o_enableIBL" + } + }, + { + "id": "forwardPassIBLSpecular", + "displayName": "Forward Pass IBL Specular", + "description": "Whether to apply IBL specular in the forward pass.", + "type": "Bool", + "defaultValue": false, + "connection": { + "type": "ShaderOption", + "id": "o_materialUseForwardPassIBLSpecular" + } + } + ], + "settings": [ + { + "id": "heightmapImage", + "displayName": "Heightmap Image", + "description": "Heightmap of the terrain, controlled by the runtime.", + "type": "Image", + "connection": { + "type": "ShaderInput", + "id": "m_heightmapImage" + } + }, + { + "id": "baseColor", + "displayName": "Base Color", + "type": "Color", + "defaultValue": [ 0.18, 0.18, 0.18 ], + "connection": { + "type": "ShaderInput", + "id": "m_baseColor" + } + }, + { + "id": "roughness", + "displayName": "Roughness", + "type": "Float", + "defaultValue": 1.0, + "min": 0.0, + "max": 1.0, + "connection": { + "type": "ShaderInput", + "id": "m_roughness" + } + } + ] + } + }, + "shaders": [ + { + "file": "../../Shaders/Terrain/TerrainPBR_ForwardPass.shader" + }, + { + "file": "../../Shaders/Terrain/Terrain_Shadowmap.shader" + }, + { + "file": "../../Shaders/Terrain/Terrain_DepthPass.shader" + } + ], + "functors": [ + ] +} diff --git a/Gems/Terrain/Assets/Shaders/Terrain/Terrain.azsl b/Gems/Terrain/Assets/Shaders/Terrain/Terrain.azsl deleted file mode 100644 index 50d11ca610..0000000000 --- a/Gems/Terrain/Assets/Shaders/Terrain/Terrain.azsl +++ /dev/null @@ -1,95 +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 -#include -#include -#include "TerrainCommon.azsli" - -struct VertexOutput -{ - linear centroid float4 m_position : SV_Position; - float3 m_normal : NORMAL; - float3 m_tangent : TANGENT; - float3 m_bitangent : BITANGENT; - float m_height : UV; -}; - -struct ForwardPassOutput -{ - float4 m_diffuseColor : SV_Target0; //!< RGB = Diffuse Lighting, A = Blend Alpha (for blended surfaces) OR A = special encoding of surfaceScatteringFactor, m_subsurfaceScatteringQuality, o_enableSubsurfaceScattering - float4 m_specularColor : SV_Target1; //!< RGB = Specular Lighting, A = Unused - float4 m_albedo : SV_Target2; //!< RGB = Surface albedo pre-multiplied by other factors that will be multiplied later by diffuse GI, A = specularOcclusion - float4 m_specularF0 : SV_Target3; //!< RGB = Specular F0, A = roughness - float4 m_normal : SV_Target4; //!< RGB10 = EncodeNormalSignedOctahedron(worldNormal), A2 = multiScatterCompensationEnabled -}; - -struct PixelOutput -{ - float4 m_color : SV_Target0; -}; - -VertexOutput MainVS(in VertexInput input) -{ - VertexOutput output; - ObjectSrg::TerrainData terrainData = ObjectSrg::m_terrainData; - - float2 uv = input.m_uv; - float2 origUv = lerp(terrainData.m_uvMin, terrainData.m_uvMax, uv); - output.m_position = GetTerrainProjectedPosition(terrainData, input.m_position, origUv); - - // Calculate normal - float up = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, -1.0f)); - float right = GetHeight(origUv + terrainData.m_uvStep * float2( 1.0f, 0.0f)); - float down = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, 1.0f)); - float left = GetHeight(origUv + terrainData.m_uvStep * float2(-1.0f, 0.0f)); - - output.m_bitangent = normalize(float3(0.0, terrainData.m_sampleSpacing * 2.0f, down - up)); - output.m_tangent = normalize(float3(terrainData.m_sampleSpacing * 2.0f, 0.0, right - left)); - output.m_normal = cross(output.m_tangent, output.m_bitangent); - - output.m_height = GetHeight(origUv); - return output; -} - -ForwardPassOutput MainPS(in VertexOutput input) -{ - ForwardPassOutput output; - - float3 lightDirection = normalize(float3(-1.0, 1.0, -1.0)); - float3 lightIntensity = float3(1.0, 1.0, 1.0); - if (SceneSrg::m_directionalLightCount > 0) - { - lightDirection = SceneSrg::m_directionalLights[0].m_direction; - lightIntensity = SceneSrg::m_directionalLights[0].m_rgbIntensityLux; - } - - // Fake light intensity ranges from 1.0 for normals directly facing the light to zero for those - // directly facing away. - const float minLight = 0.01; - const float midLight = 0.1; - float lightDot = dot(normalize(input.m_normal), -lightDirection); - lightIntensity *= lightDot > 0.0 ? - lightDot * (1.0 - midLight) + midLight : // surface facing light - (lightDot + 1.0) * (midLight - minLight) + minLight; // surface facing away - - output.m_diffuseColor.rgb = 0.5 * lightIntensity; - output.m_diffuseColor.a = 0.0f; - - output.m_specularColor.rgb = 0.0; - - output.m_albedo.rgb = 0.25 + input.m_height * 0.5; - output.m_albedo.a = 0.0; - - output.m_specularF0.rgb = 0.04; - output.m_specularF0.a = 1.0; - - output.m_normal.rgb = input.m_normal; - output.m_normal.a = 0.0; - - return output; -} diff --git a/Gems/Terrain/Assets/Shaders/Terrain/Terrain.shader b/Gems/Terrain/Assets/Shaders/Terrain/Terrain.shader deleted file mode 100644 index e844a54a21..0000000000 --- a/Gems/Terrain/Assets/Shaders/Terrain/Terrain.shader +++ /dev/null @@ -1,33 +0,0 @@ -{ - "Source" : "./Terrain.azsl", - - "DepthStencilState" : - { - "Depth" : - { - "Enable" : true, - "CompareFunc" : "GreaterEqual" - } - }, - - "DrawList" : "forward", - - "ProgramSettings": - { - "EntryPoints": - [ - { - "name": "MainVS", - "type": "Vertex" - }, - { - "name": "MainPS", - "type": "Fragment" - } - ] - }, - - "BlendState" : { - "Enable" : false - } -} diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli index 18b85bbd43..9df7b35ce8 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli @@ -9,18 +9,6 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject { - Texture2D m_heightmapImage; - - Sampler PointSampler - { - MinFilter = Point; - MagFilter = Point; - MipFilter = Point; - AddressU = Clamp; - AddressV = Clamp; - AddressW = Clamp; - }; - row_major float3x4 m_modelToWorld; struct TerrainData @@ -33,14 +21,131 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject }; TerrainData m_terrainData; + + // The below shouldn't be in this SRG but needs to be for now because the lighting functions depend on them. + + //! Reflection Probe (smallest probe volume that overlaps the object position) + struct ReflectionProbeData + { + row_major float3x4 m_modelToWorld; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; + float m_padding; + bool m_useReflectionProbe; + bool m_useParallaxCorrection; + }; + + ReflectionProbeData m_reflectionProbeData; + TextureCube m_reflectionProbeCubeMap; + + float4x4 GetReflectionProbeWorldMatrix() + { + float4x4 modelToWorld = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0]; + modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1]; + modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2]; + return modelToWorld; + } + + float4x4 GetReflectionProbeWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0]; + modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1]; + modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2]; + return modelToWorldInverse; + } +} + +ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial +{ + Texture2D m_heightmapImage; + + Sampler HeightmapSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Point; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; + + float3 m_baseColor; + float m_roughness; } +option bool o_useTerrainSmoothing = false; + struct VertexInput { float2 m_position : POSITION; float2 m_uv : UV; }; +// Sample a texture with a 5 tap B-Spline. Consider ripping this out and putting in a more general location. +// This function samples a 4x4 neighborhood around the uv. Normally this would take 16 samples, but by taking +// advantage of bilinear filtering this can be done with 9 taps on the edges between pixels. The cost is further +// reduced by dropping the diagonals. +float SampleBSpline5Tap(Texture2D texture, SamplerState textureSampler, float2 uv, float2 textureSize, float2 rcpTextureSize) +{ + // Think of sample locations in the 4x4 neighborhood as having a top left coordinate of 0,0 and + // a bottom right coordinate of 3,3. + + // Find the position in texture space then round it to get the center of the 1,1 pixel (tc1) + float2 texelPos = uv * textureSize; + float2 tc1= floor(texelPos - 0.5) + 0.5; + + // Offset from center position to texel + float2 f = texelPos - tc1; + + // Compute B-Spline weights based on the offset + float2 OneMinusF = (1.0 - f); + float2 OneMinusF2 = OneMinusF * OneMinusF; + float2 OneMinusF3 = OneMinusF2 * OneMinusF; + float2 w0 = OneMinusF3; + float2 w1 = 4.0 + 3.0 * f * f * f - 6.0 * f * f; + float2 w2 = 4.0 + 3.0 * OneMinusF3 - 6.0 * OneMinusF2; + float2 w3 = f * f * f; + + float2 w12 = w1 + w2; + + // Compute uv coordinates for sampling the texture + float2 tc0 = (tc1 - 1.0f) * rcpTextureSize; + float2 tc3 = (tc1 + 2.0f) * rcpTextureSize; + float2 tc12 = (tc1 + w2 / w12) * rcpTextureSize; + + // Compute sample weights + float sw0 = w12.x * w12.y; // middle + float sw1 = w12.x * w0.y; // top + float sw2 = w0.x * w12.y; // left + float sw3 = w12.x * w3.y; // bottom + float sw4 = w3.x * w12.y; // right + + // total weight of samples to normalize result. + float totalWeight = sw0 + sw1 + sw2 + sw3 + sw4; + + float result = 0.0f; + result += texture.SampleLevel(textureSampler, float2(tc12.x, tc12.y), 0.0).r * sw0; + result += texture.SampleLevel(textureSampler, float2(tc12.x, tc0.y), 0.0).r * sw1; + result += texture.SampleLevel(textureSampler, float2( tc0.x, tc12.y), 0.0).r * sw2; + result += texture.SampleLevel(textureSampler, float2(tc12.x, tc3.y), 0.0).r * sw3; + result += texture.SampleLevel(textureSampler, float2( tc3.x, tc12.y), 0.0).r * sw4; + + return result / totalWeight; +} + float4x4 GetObject_WorldMatrix() { float4x4 modelToWorld = float4x4( @@ -58,10 +163,23 @@ float4x4 GetObject_WorldMatrix() float GetHeight(float2 origUv) { float2 uv = clamp(origUv + (ObjectSrg::m_terrainData.m_uvStep * 0.5f), 0.0f, 1.0f); - return ObjectSrg::m_terrainData.m_heightScale * (ObjectSrg::m_heightmapImage.SampleLevel(ObjectSrg::PointSampler, uv, 0).r - 0.5f); + float height = 0.0f; + + if (o_useTerrainSmoothing) + { + float2 textureSize; + TerrainMaterialSrg::m_heightmapImage.GetDimensions(textureSize.x, textureSize.y); + height = SampleBSpline5Tap(TerrainMaterialSrg::m_heightmapImage, TerrainMaterialSrg::HeightmapSampler, uv, textureSize, rcp(textureSize)); + } + else + { + height = TerrainMaterialSrg::m_heightmapImage.SampleLevel(TerrainMaterialSrg::HeightmapSampler, uv, 0).r; + } + + return ObjectSrg::m_terrainData.m_heightScale * (height - 0.5f); } -float4 GetTerrainProjectedPosition(ObjectSrg::TerrainData terrainData, float2 vertexPosition, float2 uv) +float3 GetTerrainWorldPosition(ObjectSrg::TerrainData terrainData, float2 vertexPosition, float2 uv) { // Remove all vertices outside our bounds by turning them into NaN positions. if (any(uv > 1.0) || any (uv < 0.0)) @@ -71,6 +189,10 @@ float4 GetTerrainProjectedPosition(ObjectSrg::TerrainData terrainData, float2 ve // Loop up the height and calculate our final position. float height = GetHeight(uv); - float4 worldPosition = mul(GetObject_WorldMatrix(), float4(vertexPosition, height, 1.0f)); - return mul(ViewSrg::m_viewProjectionMatrix, worldPosition); + return mul(GetObject_WorldMatrix(), float4(vertexPosition, height, 1.0f)).xyz; +} + +float4 GetTerrainProjectedPosition(ObjectSrg::TerrainData terrainData, float2 vertexPosition, float2 uv) +{ + return mul(ViewSrg::m_viewProjectionMatrix, float4(GetTerrainWorldPosition(terrainData, vertexPosition, uv), 1.0)); } diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl new file mode 100644 index 0000000000..d77b944498 --- /dev/null +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl @@ -0,0 +1,134 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct VSOutput +{ + float4 m_position : SV_Position; + float3 m_normal: NORMAL; + float3 m_tangent : TANGENT; + float3 m_bitangent : BITANGENT; + float3 m_worldPosition : UV0; + float3 m_shadowCoords[ViewSrg::MaxCascadeCount] : UV2; + float2 m_uv : UV1; +}; + +VSOutput TerrainPBR_MainPassVS(VertexInput IN) +{ + VSOutput OUT; + + ObjectSrg::TerrainData terrainData = ObjectSrg::m_terrainData; + + float2 uv = IN.m_uv; + float2 origUv = lerp(terrainData.m_uvMin, terrainData.m_uvMax, uv); + float3 worldPosition = GetTerrainWorldPosition(terrainData, IN.m_position, origUv); + OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); + OUT.m_worldPosition = worldPosition; + + // Calculate normal + float up = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, -1.0f)); + float right = GetHeight(origUv + terrainData.m_uvStep * float2( 1.0f, 0.0f)); + float down = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, 1.0f)); + float left = GetHeight(origUv + terrainData.m_uvStep * float2(-1.0f, 0.0f)); + + OUT.m_bitangent = normalize(float3(0.0, terrainData.m_sampleSpacing * 2.0f, down - up)); + OUT.m_tangent = normalize(float3(terrainData.m_sampleSpacing * 2.0f, 0.0, right - left)); + OUT.m_normal = cross(OUT.m_tangent, OUT.m_bitangent); + OUT.m_uv = uv; + + // directional light shadow + const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight; + if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount) + { + DirectionalLightShadow::GetShadowCoords( + shadowIndex, + worldPosition, + OUT.m_shadowCoords); + } + + return OUT; +} + +ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) +{ + // ------- Surface ------- + + Surface surface; + + // Position, Normal, Roughness + surface.position = IN.m_worldPosition.xyz; + surface.normal = normalize(IN.m_normal); + surface.roughnessLinear = TerrainMaterialSrg::m_roughness; + surface.CalculateRoughnessA(); + + // Albedo, SpecularF0 + const float specularF0Factor = 0.5f; + float3 color = TerrainMaterialSrg::m_baseColor; + surface.SetAlbedoAndSpecularF0(color, specularF0Factor, 0.0); + + // Clear Coat, Transmission + surface.clearCoat.InitializeToZero(); + surface.transmission.InitializeToZero(); + + // ------- LightingData ------- + + LightingData lightingData; + + // Light iterator + lightingData.tileIterator.Init(IN.m_position, PassSrg::m_lightListRemapped, PassSrg::m_tileLightData); + lightingData.Init(surface.position, surface.normal, surface.roughnessLinear); + + // Shadow, Occlusion + lightingData.shadowCoords = IN.m_shadowCoords; + + // Diffuse and Specular response + lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear); + lightingData.diffuseResponse = 1.0f - lightingData.specularResponse; + + const float alpha = 1.0f; + + // ------- Lighting Calculation ------- + + // Apply Decals + ApplyDecals(lightingData.tileIterator, surface); + + // Apply Direct Lighting + ApplyDirectLighting(surface, lightingData); + + // Apply Image Based Lighting (IBL) + ApplyIBL(surface, lightingData); + + // Finalize Lighting + lightingData.FinalizeLighting(surface.transmission.tint); + + PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha); + + // ------- Output ------- + + ForwardPassOutput OUT; + + OUT.m_diffuseColor = lightingOutput.m_diffuseColor; + OUT.m_diffuseColor.w = -1; // Subsurface scattering is disabled + OUT.m_specularColor = lightingOutput.m_specularColor; + OUT.m_specularF0 = lightingOutput.m_specularF0; + OUT.m_albedo = lightingOutput.m_albedo; + OUT.m_normal = lightingOutput.m_normal; + + return OUT; +} diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.shader b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.shader new file mode 100644 index 0000000000..0e6f0beb1d --- /dev/null +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.shader @@ -0,0 +1,42 @@ +{ + "Source" : "./TerrainPBR_ForwardPass.azsl", + + "DepthStencilState" : + { + "Depth" : + { + "Enable" : true, + "CompareFunc" : "GreaterEqual" + }, + "Stencil" : + { + "Enable" : true, + "ReadMask" : "0x00", + "WriteMask" : "0xFF", + "FrontFace" : + { + "Func" : "Always", + "DepthFailOp" : "Keep", + "FailOp" : "Keep", + "PassOp" : "Replace" + } + } + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "TerrainPBR_MainPassVS", + "type": "Vertex" + }, + { + "name": "TerrainPBR_MainPassPS", + "type": "Fragment" + } + ] + }, + + "DrawList" : "forward" +} diff --git a/Gems/Terrain/Assets/Shaders/Terrain/Terrain_DepthPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/Terrain_DepthPass.azsl index 20c56323ac..fd855a1bcd 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/Terrain_DepthPass.azsl +++ b/Gems/Terrain/Assets/Shaders/Terrain/Terrain_DepthPass.azsl @@ -8,6 +8,7 @@ #include #include #include "TerrainCommon.azsli" +#include struct VSDepthOutput { diff --git a/Gems/Terrain/Assets/Shaders/Terrain/Terrain_Shadowmap.shader b/Gems/Terrain/Assets/Shaders/Terrain/Terrain_Shadowmap.shader new file mode 100644 index 0000000000..290c83bddf --- /dev/null +++ b/Gems/Terrain/Assets/Shaders/Terrain/Terrain_Shadowmap.shader @@ -0,0 +1,26 @@ +{ + "Source" : "Terrain_DepthPass", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" } + }, + + "DrawList" : "shadow", + + "RasterState" : + { + "depthBias" : "10", + "depthBiasSlopeScale" : "4" + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + } + ] + } +} diff --git a/Gems/Terrain/Code/CMakeLists.txt b/Gems/Terrain/Code/CMakeLists.txt index fc07294dab..17ac3d8474 100644 --- a/Gems/Terrain/Code/CMakeLists.txt +++ b/Gems/Terrain/Code/CMakeLists.txt @@ -21,6 +21,7 @@ ly_add_target( AZ::AzFramework Gem::Atom_RPI.Public Gem::Atom_Utils.Static + Gem::Atom_Feature_Common Gem::GradientSignal Gem::SurfaceData Gem::LmbrCentral diff --git a/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp index afaf0470b4..5357ccce32 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp @@ -13,8 +13,6 @@ #include #include -#include -#include #include namespace Terrain @@ -34,8 +32,6 @@ namespace Terrain ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } - - Terrain::TerrainFeatureProcessor::Reflect(context); } } @@ -49,9 +45,8 @@ namespace Terrain incompatible.push_back(AZ_CRC_CE("TerrainService")); } - void TerrainSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + void TerrainSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) { - required.push_back(AZ_CRC_CE("RPISystem")); } void TerrainSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) @@ -68,14 +63,11 @@ namespace Terrain // every time an entity is added or removed to a level. If this ever changes, the Terrain System ownership could move into // the level component. m_terrainSystem = new TerrainSystem(); - AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); } void TerrainSystemComponent::Deactivate() { delete m_terrainSystem; m_terrainSystem = nullptr; - - AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); } } diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp index 7233d08281..e140129563 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp @@ -95,6 +95,8 @@ namespace Terrain AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId()); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + RefreshCachedWireframeGrid(AZ::Aabb::CreateNull()); } void TerrainWorldDebuggerComponent::Deactivate() diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp new file mode 100644 index 0000000000..3bf34fa019 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp @@ -0,0 +1,213 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace Terrain +{ + void TerrainWorldRendererConfig::Reflect(AZ::ReflectContext* context) + { + Terrain::TerrainFeatureProcessor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class()->Version(1); + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("Terrain World Renderer Component", "Enables terrain rendering") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true); + } + } + } + + void TerrainWorldRendererComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainRendererService")); + } + + void TerrainWorldRendererComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainRendererService")); + } + + void TerrainWorldRendererComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainService")); + } + + void TerrainWorldRendererComponent::Reflect(AZ::ReflectContext* context) + { + TerrainWorldRendererConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class()->Version(0)->Field( + "Configuration", &TerrainWorldRendererComponent::m_configuration); + } + } + + TerrainWorldRendererComponent::TerrainWorldRendererComponent(const TerrainWorldRendererConfig& configuration) + : m_configuration(configuration) + { + } + + TerrainWorldRendererComponent::~TerrainWorldRendererComponent() + { + if (m_terrainRendererActive) + { + Deactivate(); + } + } + + AZ::RPI::Scene* TerrainWorldRendererComponent::GetScene() const + { + // Find the entity context for the entity ID. + AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult( + entityContextId, GetEntityId(), &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); + + return AZ::RPI::Scene::GetSceneForEntityContextId(entityContextId); + } + + void TerrainWorldRendererComponent::Activate() + { + // On component activation, register the terrain feature processor with Atom and the scene related to this entity context. + + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + + if (AZ::RPI::Scene* scene = GetScene(); scene) + { + m_terrainFeatureProcessor = scene->EnableFeatureProcessor(); + } + + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + m_terrainRendererActive = true; + } + + void TerrainWorldRendererComponent::Deactivate() + { + // On component deactivation, unregister the feature processor and remove it from the default scene. + + m_terrainRendererActive = false; + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + + if (AZ::RPI::Scene* scene = GetScene(); scene) + { + if (scene->GetFeatureProcessor()) + { + scene->DisableFeatureProcessor(); + } + } + m_terrainFeatureProcessor = nullptr; + + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + } + + bool TerrainWorldRendererComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainWorldRendererComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + void TerrainWorldRendererComponent::OnTerrainDataDestroyBegin() + { + // If the terrain is being destroyed, remove all existing terrain data from the feature processor. + + if (m_terrainFeatureProcessor) + { + m_terrainFeatureProcessor->RemoveTerrainData(); + } + } + + void TerrainWorldRendererComponent::OnTerrainDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion, [[maybe_unused]] TerrainDataChangedMask dataChangedMask) + { + // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). + // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions + // that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously. + // (One case where this was previously able to occur was in rapid updating of the Preview widget on the + // GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly) + auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); + typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); + + AZ::Vector2 queryResolution = AZ::Vector2(1.0f); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + + AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); + + + AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); + + uint32_t width = aznumeric_cast( + (float)worldBounds.GetXExtent() / queryResolution.GetX()); + uint32_t height = aznumeric_cast( + (float)worldBounds.GetYExtent() / queryResolution.GetY()); + AZStd::vector pixels; + pixels.resize_no_construct(width * height); + const uint32_t pixelDataSize = width * height * sizeof(float); + memset(pixels.data(), 0, pixelDataSize); + + for (uint32_t y = 0; y < height; y++) + { + for (uint32_t x = 0; x < width; x++) + { + bool terrainExists = true; + float terrainHeight = 0.0f; + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, + (x * queryResolution.GetX()) + worldBounds.GetMin().GetX(), + (y * queryResolution.GetY()) + worldBounds.GetMin().GetY(), + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, + &terrainExists); + + pixels[(y * width) + x] = + (terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(); + } + } + + if (m_terrainFeatureProcessor) + { + m_terrainFeatureProcessor->UpdateTerrainData(transform, worldBounds, queryResolution.GetX(), width, height, pixels); + } + } + +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h new file mode 100644 index 0000000000..a1e9498be8 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h @@ -0,0 +1,75 @@ +/* + * 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 +#include +#include + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace AZ::RPI +{ + class Scene; +} + +namespace Terrain +{ + class TerrainFeatureProcessor; + + class TerrainWorldRendererConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainWorldRendererConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainWorldRendererConfig, "{08C5863C-092D-4A69-8226-4978E4F6E343}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + }; + + + class TerrainWorldRendererComponent + : public AZ::Component + , public AzFramework::Terrain::TerrainDataNotificationBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainWorldRendererComponent, "{3B0DB71E-5944-437C-8C88-70F8B405BFC7}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainWorldRendererComponent(const TerrainWorldRendererConfig& configuration); + TerrainWorldRendererComponent() = default; + ~TerrainWorldRendererComponent() override; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + protected: + void OnTerrainDataDestroyBegin() override; + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + + AZ::RPI::Scene* GetScene() const; + + private: + TerrainWorldRendererConfig m_configuration; + bool m_terrainRendererActive{ false }; + TerrainFeatureProcessor* m_terrainFeatureProcessor{ nullptr }; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp new file mode 100644 index 0000000000..2d42585fa5 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp @@ -0,0 +1,56 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + void EditorTerrainWorldRendererComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::Reflect(context); + + AZ::SerializeContext* serializeContext = azrtti_cast(context); + + if (serializeContext) + { + serializeContext->Class() + ->Version(0) + ; + + if (auto editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Terrain World Renderer", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Terrain") + ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/TerrainWorldRenderer.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/TerrainWorldRenderer.svg") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ; + } + } + } + + + void EditorTerrainWorldRendererComponent::Init() + { + BaseClassType::Init(); + } + + void EditorTerrainWorldRendererComponent::Activate() + { + BaseClassType::Activate(); + } + + AZ::u32 EditorTerrainWorldRendererComponent::ConfigurationChanged() + { + return BaseClassType::ConfigurationChanged(); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h new file mode 100644 index 0000000000..92ce648988 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h @@ -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 +#include +#include + +namespace Terrain +{ + class EditorTerrainWorldRendererComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainWorldRendererComponent, "{7BEFF763-89A6-4EDA-B199-B049A8E757AF}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + AZ::u32 ConfigurationChanged() override; + + protected: + using BaseClassType::m_configuration; + using BaseClassType::m_component; + using BaseClassType::m_visible; + + private: + }; +} diff --git a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp index 0ba416cbb7..5305087694 100644 --- a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp +++ b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace Terrain { @@ -25,6 +26,7 @@ namespace Terrain Terrain::EditorTerrainSystemComponent::CreateDescriptor(), Terrain::EditorTerrainWorldComponent::CreateDescriptor(), Terrain::EditorTerrainWorldDebuggerComponent::CreateDescriptor(), + Terrain::EditorTerrainWorldRendererComponent::CreateDescriptor(), }); } diff --git a/Gems/Terrain/Code/Source/TerrainModule.cpp b/Gems/Terrain/Code/Source/TerrainModule.cpp index 29b2542472..24b87b4ab3 100644 --- a/Gems/Terrain/Code/Source/TerrainModule.cpp +++ b/Gems/Terrain/Code/Source/TerrainModule.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ namespace Terrain TerrainSystemComponent::CreateDescriptor(), TerrainWorldComponent::CreateDescriptor(), TerrainWorldDebuggerComponent::CreateDescriptor(), + TerrainWorldRendererComponent::CreateDescriptor(), TerrainHeightGradientListComponent::CreateDescriptor(), TerrainLayerSpawnerComponent::CreateDescriptor(), TerrainSurfaceDataSystemComponent::CreateDescriptor(), diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 511b206b84..3e5f9a0608 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -20,28 +20,39 @@ #include #include #include +#include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include #include #include #include #include - +#include namespace Terrain { namespace { - const uint32_t DEFAULT_UploadBufferSize = 512 * 1024; // 512k [[maybe_unused]] const char* TerrainFPName = "TerrainFeatureProcessor"; } + namespace MaterialInputs + { + static const char* const HeightmapImage("settings.heightmapImage"); + } + namespace ShaderInputs { - static const char* const HeightmapImage("m_heightmapImage"); static const char* const ModelToWorld("m_modelToWorld"); static const char* const TerrainData("m_terrainData"); } @@ -60,142 +71,44 @@ namespace Terrain void TerrainFeatureProcessor::Activate() { m_areaData = {}; - - InitializeAtomStuff(); - EnableSceneNotification(); + Initialize(); } - void TerrainFeatureProcessor::ConfigurePipelineState(ShaderState& shaderState, bool assertOnFail) + void TerrainFeatureProcessor::Initialize() { - if (shaderState.m_shader == nullptr) - { - AZ_Assert(shaderState.m_shader || !assertOnFail, "Terrain shader failed to load correctly."); - return; - } - - bool success = GetParentScene()->ConfigurePipelineState(shaderState.m_shader->GetDrawListTag(), shaderState.m_pipelineStateDescriptor); - AZ_Assert(success || !assertOnFail, "Couldn't configure the pipeline state."); - if (success) { - shaderState.m_pipelineState = shaderState.m_shader->AcquirePipelineState(shaderState.m_pipelineStateDescriptor); - AZ_Assert(shaderState.m_pipelineState, "Failed to acquire default pipeline state."); - } - } - - void TerrainFeatureProcessor::InitializeAtomStuff() - { - m_rhiSystem = AZ::RHI::RHISystemInterface::Get(); - - { - auto LoadShader = [this](const char* filePath, ShaderState& shaderState) - { - shaderState.m_shader = AZ::RPI::LoadShader(filePath); - if (!shaderState.m_shader) - { - AZ_Error(TerrainFPName, false, "Failed to find or create a shader instance from shader asset '%s'", filePath); - return; - } - - // Create the data layout - shaderState.m_pipelineStateDescriptor = AZ::RHI::PipelineStateDescriptorForDraw{}; + // Load the terrain material asynchronously + const AZStd::string materialFilePath = "Materials/Terrain/DefaultPbrTerrain.azmaterial"; + m_materialAssetLoader = AZStd::make_unique(); + *m_materialAssetLoader = AZ::RPI::AssetUtils::AsyncAssetLoader::Create(materialFilePath, 0u, + [&](AZ::Data::Asset assetData, bool success) -> void { - AZ::RHI::InputStreamLayoutBuilder layoutBuilder; - - layoutBuilder.AddBuffer() - ->Channel("POSITION", AZ::RHI::Format::R32G32_FLOAT) - ->Channel("UV", AZ::RHI::Format::R32G32_FLOAT) - ; - shaderState.m_pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End(); + const AZ::Data::Asset& materialAsset = static_cast>(assetData); + if (success) + { + m_materialInstance = AZ::RPI::Material::FindOrCreate(assetData); + if (!materialAsset->GetObjectSrgLayout()) + { + AZ_Error("TerrainFeatureProcessor", false, "No per-object ShaderResourceGroup found on terrain material."); + } + } } - - auto shaderVariant = shaderState.m_shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId); - shaderVariant.ConfigurePipelineState(shaderState.m_pipelineStateDescriptor); - - // If this fails to run now, it's ok, we'll initialize it in OnRenderPipelineAdded later. - ConfigurePipelineState(shaderState, false); - }; - - LoadShader("Shaders/Terrain/Terrain.azshader", m_shaderStates[ShaderType::Forward]); - LoadShader("Shaders/Terrain/Terrain_DepthPass.azshader", m_shaderStates[ShaderType::Depth]); - - // Forward and depth shader use same srg layout. - AZ::RHI::Ptr perObjectSrgLayout = - m_shaderStates[ShaderType::Forward].m_shader->FindShaderResourceGroupLayout(AZ::Name{"ObjectSrg"}); - - if (!perObjectSrgLayout) - { - AZ_Error(TerrainFPName, false, "Failed to get shader resource group layout"); - return; - } - else if (!perObjectSrgLayout->IsFinalized()) - { - AZ_Error(TerrainFPName, false, "Shader resource group layout is not loaded"); - return; - } - - m_heightmapImageIndex = perObjectSrgLayout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::HeightmapImage)); - AZ_Error(TerrainFPName, m_heightmapImageIndex.IsValid(), "Failed to find shader input image %s.", ShaderInputs::HeightmapImage); - - m_modelToWorldIndex = perObjectSrgLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); - AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); - - m_terrainDataIndex = perObjectSrgLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData)); - AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData); - } - - AZ::RHI::BufferPoolDescriptor dmaPoolDescriptor; - dmaPoolDescriptor.m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Host; - dmaPoolDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly; - - m_hostPool = AZ::RHI::Factory::Get().CreateBufferPool(); - m_hostPool->SetName(AZ::Name("TerrainVertexPool")); - AZ::RHI::ResultCode resultCode = m_hostPool->Init(*m_rhiSystem->GetDevice(), dmaPoolDescriptor); - - if (resultCode != AZ::RHI::ResultCode::Success) - { - AZ_Error(TerrainFPName, false, "Failed to create host buffer pool from RPI"); - return; + ); } - InitializeTerrainPatch(); - - if (!InitializeRenderBuffers()) + if (!InitializePatchModel()) { AZ_Error(TerrainFPName, false, "Failed to create Terrain render buffers!"); return; } } - void TerrainFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] AZ::RPI::RenderPipelinePtr pipeline) - { - for (ShaderState& shaderState: m_shaderStates) - { - ConfigurePipelineState(shaderState, true); - } - } - - void TerrainFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] AZ::RPI::RenderPipeline* pipeline) - { - } - - void TerrainFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] AZ::RPI::RenderPipeline* renderPipeline) - { - } - - void TerrainFeatureProcessor::Deactivate() { DisableSceneNotification(); - DestroyRenderBuffers(); + m_patchModel = {}; m_areaData = {}; - - if (m_hostPool) - { - m_hostPool.reset(); - } - - m_rhiSystem = nullptr; } void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) @@ -254,240 +167,269 @@ namespace Terrain { AZ_PROFILE_FUNCTION(AzRender); - if ((m_shaderStates[ShaderType::Forward].m_shader == nullptr) || - (m_shaderStates[ShaderType::Depth].m_shader == nullptr) || - m_shaderStates[ShaderType::Forward].m_shader->GetDrawListTag().IsNull() || - m_shaderStates[ShaderType::Depth].m_shader->GetDrawListTag().IsNull()) - { - return; - } - if (!m_areaData.m_terrainBounds.IsValid()) { return; } - if (m_areaData.m_propertiesDirty) + if (m_areaData.m_propertiesDirty && m_materialInstance) { m_areaData.m_propertiesDirty = false; m_sectorData.clear(); - AZ::RHI::DrawPacketBuilder drawPacketBuilder; + AZ::RPI::MaterialPropertyIndex heightmapPropertyIndex = + m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::HeightmapImage)); + AZ_Error(TerrainFPName, heightmapPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::HeightmapImage); + AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; + m_materialInstance->SetPropertyValue(heightmapPropertyIndex, heightmapImage); + m_materialInstance->Compile(); - uint32_t numIndices = static_cast(m_gridIndices.size()); + const auto layout = m_materialInstance->GetAsset()->GetObjectSrgLayout(); - AZ::RHI::DrawIndexed drawIndexed; - drawIndexed.m_indexCount = numIndices; - drawIndexed.m_indexOffset = 0; - drawIndexed.m_vertexOffset = 0; + m_modelToWorldIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); + AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); + + m_terrainDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData)); + AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData); float xFirstPatchStart = - m_areaData.m_terrainBounds.GetMin().GetX() - fmod(m_areaData.m_terrainBounds.GetMin().GetX(), m_gridMeters); - float xLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetX() - fmod(m_areaData.m_terrainBounds.GetMax().GetX(), m_gridMeters); + m_areaData.m_terrainBounds.GetMin().GetX() - fmod(m_areaData.m_terrainBounds.GetMin().GetX(), GridMeters); + float xLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetX() - fmod(m_areaData.m_terrainBounds.GetMax().GetX(), GridMeters); float yFirstPatchStart = - m_areaData.m_terrainBounds.GetMin().GetY() - fmod(m_areaData.m_terrainBounds.GetMin().GetY(), m_gridMeters); - float yLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetY() - fmod(m_areaData.m_terrainBounds.GetMax().GetY(), m_gridMeters); + m_areaData.m_terrainBounds.GetMin().GetY() - fmod(m_areaData.m_terrainBounds.GetMin().GetY(), GridMeters); + float yLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetY() - fmod(m_areaData.m_terrainBounds.GetMax().GetY(), GridMeters); - for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += m_gridMeters) + for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) { - for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += m_gridMeters) + for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) { - drawPacketBuilder.Begin(nullptr); - drawPacketBuilder.SetDrawArguments(drawIndexed); - drawPacketBuilder.SetIndexBufferView(m_indexBufferView); - auto& forwardShader = m_shaderStates[ShaderType::Forward].m_shader; - - auto resourceGroup = AZ::RPI::ShaderResourceGroup::Create(forwardShader->GetAsset(), forwardShader->GetSupervariantIndex(), AZ::Name("ObjectSrg")); - if (!resourceGroup) + const auto& materialAsset = m_materialInstance->GetAsset(); + auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); + auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); + if (!objectSrg) { - AZ_Error(TerrainFPName, false, "Failed to create shader resource group"); - return; + AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping."); + continue; } - AZStd::array uvMin = { 0.0f, 0.0f }; - AZStd::array uvMax = { 1.0f, 1.0f }; + { // Update SRG + + AZStd::array uvMin = { 0.0f, 0.0f }; + AZStd::array uvMax = { 1.0f, 1.0f }; - uvMin[0] = (float)((xPatch - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent()); - uvMin[1] = (float)((yPatch - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent()); + uvMin[0] = (float)((xPatch - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent()); + uvMin[1] = (float)((yPatch - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent()); - uvMax[0] = - (float)(((xPatch + m_gridMeters) - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent()); - uvMax[1] = - (float)(((yPatch + m_gridMeters) - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent()); + uvMax[0] = + (float)(((xPatch + GridMeters) - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent()); + uvMax[1] = + (float)(((yPatch + GridMeters) - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent()); - AZStd::array uvStep = - { - 1.0f / m_areaData.m_heightmapImageWidth, 1.0f / m_areaData.m_heightmapImageHeight, - }; + AZStd::array uvStep = + { + 1.0f / m_areaData.m_heightmapImageWidth, 1.0f / m_areaData.m_heightmapImageHeight, + }; - AZ::Transform transform = m_areaData.m_transform; - transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); + AZ::Transform transform = m_areaData.m_transform; + transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); - AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); + AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); - resourceGroup->SetImage(m_heightmapImageIndex, m_areaData.m_heightmapImage); - resourceGroup->SetConstant(m_modelToWorldIndex, matrix3x4); + objectSrg->SetConstant(m_modelToWorldIndex, matrix3x4); - ShaderTerrainData terrainDataForSrg; - terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing; - terrainDataForSrg.m_heightScale = m_areaData.m_heightScale; - terrainDataForSrg.m_uvMin = uvMin; - terrainDataForSrg.m_uvMax = uvMax; - terrainDataForSrg.m_uvStep = uvStep; - resourceGroup->SetConstant(m_terrainDataIndex, terrainDataForSrg); + ShaderTerrainData terrainDataForSrg; + terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing; + terrainDataForSrg.m_heightScale = m_areaData.m_heightScale; + terrainDataForSrg.m_uvMin = uvMin; + terrainDataForSrg.m_uvMax = uvMax; + terrainDataForSrg.m_uvStep = uvStep; + objectSrg->SetConstant(m_terrainDataIndex, terrainDataForSrg); - resourceGroup->Compile(); - drawPacketBuilder.AddShaderResourceGroup(resourceGroup->GetRHIShaderResourceGroup()); + objectSrg->Compile(); + } - auto addDrawItem = [&](ShaderState& shaderState) - { - AZ::RHI::DrawPacketBuilder::DrawRequest drawRequest; - drawRequest.m_listTag = shaderState.m_shader->GetDrawListTag(); - drawRequest.m_pipelineState = shaderState.m_pipelineState.get(); - drawRequest.m_streamBufferViews = AZStd::array_view(&m_vertexBufferView, 1); - drawPacketBuilder.AddDrawItem(drawRequest); - }; - - for (ShaderState& shaderState : m_shaderStates) + m_sectorData.push_back(); + SectorData& sectorData = m_sectorData.back(); + + for (auto& lod : m_patchModel->GetLods()) { - addDrawItem(shaderState); + AZ::RPI::ModelLod& modelLod = *lod.get(); + sectorData.m_drawPackets.emplace_back(modelLod, 0, m_materialInstance, objectSrg); + AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back(); + + // set the shader option to select forward pass IBL specular if necessary + if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false })) + { + AZ_Warning("MeshDrawPacket", false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); + } + uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; + drawPacket.SetStencilRef(stencilRef); + drawPacket.Update(*GetParentScene(), true); } - //addDrawItem(m_shaderStates[ShaderType::Forward]); - - m_sectorData.emplace_back( - drawPacketBuilder.End(), + + sectorData.m_aabb = AZ::Aabb::CreateFromMinMax( AZ::Vector3(xPatch, yPatch, m_areaData.m_terrainBounds.GetMin().GetZ()), - AZ::Vector3(xPatch + m_gridMeters, yPatch + m_gridMeters, m_areaData.m_terrainBounds.GetMax().GetZ()) - ), - resourceGroup - ); + AZ::Vector3(xPatch + GridMeters, yPatch + GridMeters, m_areaData.m_terrainBounds.GetMax().GetZ()) + ); + sectorData.m_srg = objectSrg; } } } - - for (auto& view : process.m_views) + + for (auto& sectorData : m_sectorData) { - AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix()); - for (auto& sectorData : m_sectorData) + uint8_t lodChoice = AZ::RPI::ModelLodAsset::LodCountMax; + + // Go through all cameras and choose an LOD based on the closest camera. + for (auto& view : process.m_views) + { + if ((view->GetUsageFlags() & AZ::RPI::View::UsageFlags::UsageCamera) > 0) + { + AZ::Vector3 cameraPosition = view->GetCameraTransform().GetTranslation(); + AZ::Vector2 cameraPositionXY = AZ::Vector2(cameraPosition.GetX(), cameraPosition.GetY()); + AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY()); + + float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY); + float lodForCamera = ceilf(AZ::GetMax(0.0f, log2f(sectorDistance / (GridMeters * 4.0f)))); + lodChoice = AZ::GetMin(lodChoice, aznumeric_cast(lodForCamera)); + } + } + + // Add the correct LOD draw packet for visible sectors. + for (auto& view : process.m_views) { + AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix()); if (viewFrustum.IntersectAabb(sectorData.m_aabb) != AZ::IntersectResult::Exterior) { - view->AddDrawPacket(sectorData.m_drawPacket.get()); + uint8_t lodToRender = AZ::GetMin(lodChoice, aznumeric_cast(sectorData.m_drawPackets.size() - 1)); + view->AddDrawPacket(sectorData.m_drawPackets.at(lodToRender).GetRHIDrawPacket()); } } } } - void TerrainFeatureProcessor::InitializeTerrainPatch() + void TerrainFeatureProcessor::InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata) { - m_gridVertices.clear(); - m_gridIndices.clear(); + patchdata.m_positions.clear(); + patchdata.m_uvs.clear(); + patchdata.m_indices.clear(); + + uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed. + size_t size = gridVertices * gridVertices; - for (float y = 0.0f; y < m_gridMeters; y += m_gridSpacing) + patchdata.m_positions.reserve(size); + patchdata.m_uvs.reserve(size); + + for (uint16_t y = 0; y < gridVertices; ++y) { - for (float x = 0.0f; x < m_gridMeters; x += m_gridSpacing) + for (uint16_t x = 0; x < gridVertices; ++x) { - float x0 = x; - float x1 = x + m_gridSpacing; - float y0 = y; - float y1 = y + m_gridSpacing; - - uint16_t startIndex = (uint16_t)(m_gridVertices.size()); - - m_gridVertices.emplace_back(x0, y0, x0 / m_gridMeters, y0 / m_gridMeters); - m_gridVertices.emplace_back(x1, y0, x1 / m_gridMeters, y0 / m_gridMeters); - m_gridVertices.emplace_back(x0, y1, x0 / m_gridMeters, y1 / m_gridMeters); - m_gridVertices.emplace_back(x1, y1, x1 / m_gridMeters, y1 / m_gridMeters); - - m_gridIndices.emplace_back(startIndex); - m_gridIndices.emplace_back(aznumeric_cast(startIndex + 1)); - m_gridIndices.emplace_back(aznumeric_cast(startIndex + 2)); - m_gridIndices.emplace_back(aznumeric_cast(startIndex + 1)); - m_gridIndices.emplace_back(aznumeric_cast(startIndex + 3)); - m_gridIndices.emplace_back(aznumeric_cast(startIndex + 2)); + patchdata.m_positions.push_back({ aznumeric_cast(x) * gridSpacing, aznumeric_cast(y) * gridSpacing }); + patchdata.m_uvs.push_back({ aznumeric_cast(x) / gridSize, aznumeric_cast(y) / gridSize }); } } - } - bool TerrainFeatureProcessor::InitializeRenderBuffers() + patchdata.m_indices.reserve(gridSize * gridSize * 6); // total number of quads, 2 triangles with 6 indices per quad. + + for (uint16_t y = 0; y < gridSize; ++y) + { + for (uint16_t x = 0; x < gridSize; ++x) + { + uint16_t topLeft = y * gridVertices + x; + uint16_t topRight = topLeft + 1; + uint16_t bottomLeft = (y + 1) * gridVertices + x; + uint16_t bottomRight = bottomLeft + 1; + + patchdata.m_indices.emplace_back(topLeft); + patchdata.m_indices.emplace_back(topRight); + patchdata.m_indices.emplace_back(bottomLeft); + patchdata.m_indices.emplace_back(bottomLeft); + patchdata.m_indices.emplace_back(topRight); + patchdata.m_indices.emplace_back(bottomRight); + } + } + } + + AZ::Outcome> TerrainFeatureProcessor::CreateBufferAsset( + const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName) { - AZ::RHI::ResultCode result = AZ::RHI::ResultCode::Fail; - - // Create geometry buffers - m_indexBuffer = AZ::RHI::Factory::Get().CreateBuffer(); - m_vertexBuffer = AZ::RHI::Factory::Get().CreateBuffer(); + AZ::RPI::BufferAssetCreator creator; + creator.Begin(AZ::Uuid::CreateRandom()); - m_indexBuffer->SetName(AZ::Name("TerrainIndexBuffer")); - m_vertexBuffer->SetName(AZ::Name("TerrainVertexBuffer")); + AZ::RHI::BufferDescriptor bufferDescriptor; + bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly | AZ::RHI::BufferBindFlags::ShaderRead; + bufferDescriptor.m_byteCount = static_cast(bufferViewDescriptor.m_elementSize) * static_cast(bufferViewDescriptor.m_elementCount); - AZStd::vector> buffers = { m_indexBuffer , m_vertexBuffer }; + creator.SetBuffer(data, bufferDescriptor.m_byteCount, bufferDescriptor); + creator.SetBufferViewDescriptor(bufferViewDescriptor); + creator.SetUseCommonPool(AZ::RPI::CommonBufferPoolType::StaticInputAssembly); - // Fill our buffers with the vertex/index data - for (size_t bufferIndex = 0; bufferIndex < buffers.size(); ++bufferIndex) + AZ::Data::Asset bufferAsset; + if (creator.End(bufferAsset)) { - AZ::RHI::Ptr buffer = buffers[bufferIndex]; + bufferAsset.SetHint(bufferName); + return AZ::Success(bufferAsset); + } + + return AZ::Failure(); + } - // Initialize the buffer + bool TerrainFeatureProcessor::InitializePatchModel() + { + AZ::RPI::ModelAssetCreator modelAssetCreator; + modelAssetCreator.Begin(AZ::Uuid::CreateRandom()); - AZ::RHI::BufferInitRequest bufferRequest; - bufferRequest.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, DEFAULT_UploadBufferSize }; - bufferRequest.m_buffer = buffer.get(); + uint16_t gridSize = GridSize; + float gridSpacing = GridSpacing; - result = m_hostPool->InitBuffer(bufferRequest); + for (uint32_t i = 0; i < AZ::RPI::ModelLodAsset::LodCountMax && gridSize > 0; ++i) + { + PatchData patchData; + InitializeTerrainPatch(gridSize, gridSpacing, patchData); - if (result != AZ::RHI::ResultCode::Success) + auto positionBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_positions.size()), AZ::RHI::Format::R32G32_FLOAT); + auto positionsOutcome = CreateBufferAsset(patchData.m_positions.data(), positionBufferViewDesc, "TerrainPatchPositions"); + + auto uvBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_uvs.size()), AZ::RHI::Format::R32G32_FLOAT); + auto uvsOutcome = CreateBufferAsset(patchData.m_uvs.data(), uvBufferViewDesc, "TerrainPatchUvs"); + + auto indexBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_indices.size()), AZ::RHI::Format::R16_UINT); + auto indicesOutcome = CreateBufferAsset(patchData.m_indices.data(), indexBufferViewDesc, "TerrainPatchIndices"); + + if (!positionsOutcome.IsSuccess() || !uvsOutcome.IsSuccess() || !indicesOutcome.IsSuccess()) { AZ_Error(TerrainFPName, false, "Failed to create GPU buffers for Terrain"); return false; } + + AZ::RPI::ModelLodAssetCreator modelLodAssetCreator; + modelLodAssetCreator.Begin(AZ::Uuid::CreateRandom()); - // Grab a pointer to the buffer's data - - m_hostPool->OrphanBuffer(*buffer); - - AZ::RHI::BufferMapResponse mapResponse; - m_hostPool->MapBuffer(AZ::RHI::BufferMapRequest(*buffer, 0, DEFAULT_UploadBufferSize), mapResponse); - - auto* mappedData = reinterpret_cast(mapResponse.m_data); + modelLodAssetCreator.BeginMesh(); + modelLodAssetCreator.AddMeshStreamBuffer(AZ::RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), {positionsOutcome.GetValue(), positionBufferViewDesc}); + modelLodAssetCreator.AddMeshStreamBuffer(AZ::RHI::ShaderSemantic{ "UV" }, AZ::Name(), {uvsOutcome.GetValue(), uvBufferViewDesc}); + modelLodAssetCreator.SetMeshIndexBuffer({indicesOutcome.GetValue(), indexBufferViewDesc}); - //0th index should always be the index buffer - if (bufferIndex == 0) - { - // Fill the index buffer with our terrain patch indices - const uint64_t idxSize = m_gridIndices.size() * sizeof(uint16_t); - memcpy(mappedData, m_gridIndices.data(), idxSize); + AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0, 0.0, 0.0), AZ::Vector3(GridMeters, GridMeters, 0.0)); + modelLodAssetCreator.SetMeshAabb(AZStd::move(aabb)); + modelLodAssetCreator.SetMeshName(AZ::Name("Terrain Patch")); + modelLodAssetCreator.EndMesh(); - m_indexBufferView = AZ::RHI::IndexBufferView( - *buffer, 0, static_cast(idxSize), AZ::RHI::IndexFormat::Uint16); - } - else - { - // Fill the vertex buffer with our terrain patch vertices - const uint64_t elementSize = m_gridVertices.size() * sizeof(Vertex); - memcpy(mappedData, m_gridVertices.data(), elementSize); - - m_vertexBufferView = AZ::RHI::StreamBufferView( - *buffer, 0, static_cast(elementSize), static_cast(sizeof(Vertex))); - } + AZ::Data::Asset modelLodAsset; + modelLodAssetCreator.End(modelLodAsset); + + modelAssetCreator.AddLodAsset(AZStd::move(modelLodAsset)); - m_hostPool->UnmapBuffer(*buffer); + gridSize = gridSize / 2; + gridSpacing *= 2.0f; } - return true; - } - - void TerrainFeatureProcessor::DestroyRenderBuffers() - { - m_indexBuffer.reset(); - m_vertexBuffer.reset(); + AZ::Data::Asset modelAsset; + bool success = modelAssetCreator.End(modelAsset); - m_indexBufferView = {}; - m_vertexBufferView = {}; + m_patchModel = AZ::RPI::Model::FindOrCreate(modelAsset); - for (ShaderState& shaderState : m_shaderStates) - { - shaderState.Reset(); - } + return success; } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index 382f473b25..160d3113c2 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -14,9 +14,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -26,6 +28,15 @@ #include #include +namespace AZ::RPI +{ + namespace AssetUtils + { + class AsyncAssetLoader; + } + class Model; +} + namespace Terrain { class TerrainFeatureProcessor final @@ -57,28 +68,6 @@ namespace Terrain private: - // System-level references to the shader, pipeline, and shader-related information - enum ShaderType - { - Depth, - Forward, - Count, - }; - - struct ShaderState - { - AZ::Data::Instance m_shader; - AZ::RHI::ConstPtr m_pipelineState; - AZ::RHI::PipelineStateDescriptorForDraw m_pipelineStateDescriptor; - - void Reset() - { - m_shader.reset(); - m_pipelineState.reset(); - m_pipelineStateDescriptor = {}; - } - }; - struct ShaderTerrainData // Must align with struct in Object Srg { AZStd::array m_uvMin; @@ -87,62 +76,47 @@ namespace Terrain float m_sampleSpacing; float m_heightScale; }; + + struct VertexPosition + { + float m_posx; + float m_posy; + }; - // RPI::SceneNotificationBus overrides ... - void OnRenderPipelineAdded(AZ::RPI::RenderPipelinePtr pipeline) override; - void OnRenderPipelineRemoved(AZ::RPI::RenderPipeline* pipeline) override; - void OnRenderPipelinePassesChanged(AZ::RPI::RenderPipeline* renderPipeline) override; - - void InitializeAtomStuff(); - void ConfigurePipelineState(ShaderState& shaderState, bool assertOnFail); - - void InitializeTerrainPatch(); + struct VertexUv + { + float m_u; + float m_v; + }; - bool InitializeRenderBuffers(); - void DestroyRenderBuffers(); + struct PatchData + { + AZStd::vector m_positions; + AZStd::vector m_uvs; + AZStd::vector m_indices; + }; + + void Initialize(); + void InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata); + bool InitializePatchModel(); void ProcessSurfaces(const FeatureProcessor::RenderPacket& process); + + AZ::Outcome> CreateBufferAsset( + const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName); // System-level parameters - const float m_gridSpacing{ 1.0f }; - const float m_gridMeters{ 32.0f }; - - // System-level cached reference to the Atom RHI - AZ::RHI::RHISystemInterface* m_rhiSystem = nullptr; + static constexpr float GridSpacing{ 1.0f }; + static constexpr uint32_t GridSize{ 64 }; // number of terrain quads (vertices are m_gridSize + 1) + static constexpr float GridMeters{ GridSpacing * GridSize }; - AZStd::array m_shaderStates; + AZStd::unique_ptr m_materialAssetLoader; + AZ::Data::Instance m_materialInstance; - AZ::RHI::ShaderInputImageIndex m_heightmapImageIndex; AZ::RHI::ShaderInputConstantIndex m_modelToWorldIndex; AZ::RHI::ShaderInputConstantIndex m_terrainDataIndex; - // Pos_float_2 + UV_float_2 - struct Vertex - { - float m_posx; - float m_posy; - float m_u; - float m_v; - - Vertex(float posx, float posy, float u, float v) - : m_posx(posx) - , m_posy(posy) - , m_u(u) - , m_v(v) - { - } - }; - - // System-level definition of a grid patch. (ex: 32m x 32m) - AZStd::vector m_gridVertices; - AZStd::vector m_gridIndices; - - // System-level data related to the grid patch - AZ::RHI::Ptr m_hostPool = nullptr; - AZ::RHI::Ptr m_indexBuffer; - AZ::RHI::Ptr m_vertexBuffer; - AZ::RHI::IndexBufferView m_indexBufferView; - AZ::RHI::StreamBufferView m_vertexBufferView; + AZ::Data::Instance m_patchModel; // Per-area data struct TerrainAreaData @@ -161,15 +135,9 @@ namespace Terrain struct SectorData { - AZ::Data::Instance m_srg; + AZ::Data::Instance m_srg; // Hold on to ref so it's not dropped AZ::Aabb m_aabb; - AZStd::unique_ptr m_drawPacket; - - SectorData(const AZ::RHI::DrawPacket* drawPacket, AZ::Aabb aabb, AZ::Data::Instance srg) - : m_srg(srg) - , m_aabb(aabb) - , m_drawPacket(drawPacket) - {} + AZStd::fixed_vector m_drawPackets; }; AZStd::vector m_sectorData; diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 5ec6af6f37..46ac677dd1 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -12,10 +12,6 @@ #include #include -#include -#include -#include - using namespace Terrain; bool TerrainLayerPriorityComparator::operator()(const AZ::EntityId& layer1id, const AZ::EntityId& layer2id) const @@ -114,18 +110,6 @@ void TerrainSystem::Deactivate() m_terrainSettingsDirty = true; m_requestedSettings.m_systemActive = false; - if (auto rpi = AZ::RPI::RPISystemInterface::Get(); rpi) - { - if (auto defaultScene = rpi->GetDefaultScene(); defaultScene) - { - const AZ::RPI::Scene* scene = defaultScene.get(); - if (auto terrainFeatureProcessor = scene->GetFeatureProcessor(); terrainFeatureProcessor) - { - terrainFeatureProcessor->RemoveTerrainData(); - } - } - } - AzFramework::Terrain::TerrainDataNotificationBus::Broadcast( &AzFramework::Terrain::TerrainDataNotificationBus::Events::OnTerrainDataDestroyEnd); } @@ -507,60 +491,6 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) m_currentSettings = m_requestedSettings; } - if (m_currentSettings.m_systemActive && m_terrainHeightDirty) - { - AZStd::shared_lock lock(m_areaMutex); - - // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). - // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions - // that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously. - // (One case where this was previously able to occur was in rapid updating of the Preview widget on the - // GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly) - auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); - typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); - - AZ::Transform transform = AZ::Transform::CreateTranslation(m_currentSettings.m_worldBounds.GetCenter()); - - uint32_t width = aznumeric_cast( - (float)m_currentSettings.m_worldBounds.GetXExtent() / m_currentSettings.m_heightQueryResolution.GetX()); - uint32_t height = aznumeric_cast( - (float)m_currentSettings.m_worldBounds.GetYExtent() / m_currentSettings.m_heightQueryResolution.GetY()); - AZStd::vector pixels; - pixels.resize_no_construct(width * height); - const uint32_t pixelDataSize = width * height * sizeof(float); - memset(pixels.data(), 0, pixelDataSize); - - for (uint32_t y = 0; y < height; y++) - { - for (uint32_t x = 0; x < width; x++) - { - bool terrainExists; - float terrainHeight = GetTerrainAreaHeight( - (x * m_currentSettings.m_heightQueryResolution.GetX()) + m_currentSettings.m_worldBounds.GetMin().GetX(), - (y * m_currentSettings.m_heightQueryResolution.GetY()) + m_currentSettings.m_worldBounds.GetMin().GetY(), - terrainExists); - - pixels[(y * width) + x] = - (terrainHeight - m_currentSettings.m_worldBounds.GetMin().GetZ()) / - m_currentSettings.m_worldBounds.GetExtents().GetZ(); - } - } - - if (auto rpi = AZ::RPI::RPISystemInterface::Get(); rpi) - { - if (auto defaultScene = rpi->GetDefaultScene(); defaultScene) - { - const AZ::RPI::Scene* scene = defaultScene.get(); - if (auto terrainFeatureProcessor = scene->GetFeatureProcessor(); terrainFeatureProcessor) - { - terrainFeatureProcessor->UpdateTerrainData( - transform, m_currentSettings.m_worldBounds, m_currentSettings.m_heightQueryResolution.GetX(), width, height, - pixels); - } - } - } - } - if (terrainSettingsChanged || m_terrainHeightDirty) { // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). diff --git a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp index 91a9744f0b..e4fb0f348e 100644 --- a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp +++ b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp @@ -19,15 +19,31 @@ #include using ::testing::AtLeast; +using ::testing::FloatNear; +using ::testing::FloatEq; +using ::testing::IsFalse; +using ::testing::Ne; using ::testing::NiceMock; using ::testing::Return; class TerrainSystemTest : public ::testing::Test { protected: + // Defines a structure for defining both an XY position and the expected height for that position. + struct HeightTestPoint + { + AZ::Vector2 m_testLocation; + float m_expectedHeight; + }; + AZ::ComponentApplication m_app; AZStd::unique_ptr m_terrainSystem; + AZStd::unique_ptr> m_boxShapeRequests; + AZStd::unique_ptr> m_shapeRequests; + AZStd::unique_ptr> m_terrainAreaHeightRequests; + + void SetUp() override { AZ::ComponentApplication::Descriptor appDesc; @@ -41,6 +57,9 @@ protected: void TearDown() override { m_terrainSystem.reset(); + m_boxShapeRequests.reset(); + m_shapeRequests.reset(); + m_terrainAreaHeightRequests.reset(); m_app.Destroy(); } @@ -71,6 +90,53 @@ protected: m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); return entity->CreateComponent(); } + + // Create a terrain system with reasonable defaults for testing, but with the ability to override the defaults + // on a test-by-test basis. + void CreateAndActivateTerrainSystem( + AZ::Vector2 queryResolution = AZ::Vector2(1.0f), + AZ::Aabb worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-128.0f), AZ::Vector3(128.0f))) + { + // Create the terrain system and give it one tick to fully initialize itself. + m_terrainSystem = AZStd::make_unique(); + m_terrainSystem->SetTerrainAabb(worldBounds); + m_terrainSystem->SetTerrainHeightQueryResolution(queryResolution); + m_terrainSystem->Activate(); + AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + } + + + AZStd::unique_ptr CreateAndActivateMockTerrainLayerSpawner( + const AZ::Aabb& spawnerBox, + const AZStd::function& mockHeights) + { + // Create the base entity with a mock box shape, Terrain Layer Spawner, and height provider. + auto entity = CreateEntity(); + CreateComponent(entity.get()); + CreateComponent(entity.get()); + + m_boxShapeRequests = AZStd::make_unique>(entity->GetId()); + m_shapeRequests = AZStd::make_unique>(entity->GetId()); + + // Set up the box shape to return whatever spawnerBox was passed in. + ON_CALL(*m_shapeRequests, GetEncompassingAabb).WillByDefault(Return(spawnerBox)); + + // Set up a mock height provider to use the passed-in mock height function to generate a height. + m_terrainAreaHeightRequests = AZStd::make_unique>(entity->GetId()); + ON_CALL(*m_terrainAreaHeightRequests, GetHeight) + .WillByDefault( + [mockHeights](const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) + { + // By default, set the outPosition to the input position and terrain to always exist. + outPosition = inPosition; + terrainExists = true; + // Let the test function modify these values based on the needs of the specific test. + mockHeights(outPosition, terrainExists); + }); + + ActivateEntity(entity.get()); + return entity; + } }; TEST_F(TerrainSystemTest, TrivialCreateDestroy) @@ -122,10 +188,8 @@ TEST_F(TerrainSystemTest, TerrainDoesNotExistWhenNoTerrainLayerSpawnersAreRegist // will return false for terrainExists, returns a height equal to the min world bounds of the terrain system, and returns // a normal facing up the Z axis. - // Create the terrain system and give it one tick to fully initialize itself. - m_terrainSystem = AZStd::make_unique(); - m_terrainSystem->Activate(); - AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + // Create and activate the terrain system with our testing defaults for world bounds and query resolution. + CreateAndActivateTerrainSystem(); AZ::Aabb worldBounds = m_terrainSystem->GetTerrainAabb(); @@ -139,7 +203,7 @@ TEST_F(TerrainSystemTest, TerrainDoesNotExistWhenNoTerrainLayerSpawnersAreRegist bool terrainExists = true; float height = m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); EXPECT_FALSE(terrainExists); - EXPECT_EQ(height, worldBounds.GetMin().GetZ()); + EXPECT_FLOAT_EQ(height, worldBounds.GetMin().GetZ()); terrainExists = true; AZ::Vector3 normal = m_terrainSystem->GetNormal( @@ -162,37 +226,21 @@ TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) // The terrain system should only query Heights from the TerrainAreaHeightRequest bus within the // TerrainLayerSpawner region, and so those values should only get returned from GetHeight for queries inside that region. - // Create the base entity with a mock Box Shape and a Terrain Layer Spawner. - auto entity = CreateEntity(); - CreateComponent(entity.get()); - CreateComponent(entity.get()); - - // Set up the box shape to return a box from (0,0,5) to (10, 10, 15) - AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); - NiceMock boxShapeRequests(entity->GetId()); - NiceMock shapeRequests(entity->GetId()); - ON_CALL(shapeRequests, GetEncompassingAabb).WillByDefault(Return(spawnerBox)); - - // Set up a mock height provider that always returns 5.0 and a normal of Y-up. - const float spawnerHeight = 5.0f; - NiceMock terrainAreaHeightRequests(entity->GetId()); - ON_CALL(terrainAreaHeightRequests, GetHeight) - .WillByDefault( - [spawnerHeight](const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) - { - outPosition = inPosition; - outPosition.SetZ(spawnerHeight); - terrainExists = true; - }); - - ActivateEntity(entity.get()); + // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and always returns a height of 5. + constexpr float spawnerHeight = 5.0f; + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(spawnerHeight); + terrainExists = true; + }); // Verify that terrain exists within the layer spawner bounds, and doesn't exist outside of it. - // Create the terrain system and give it one tick to fully initialize itself. - m_terrainSystem = AZStd::make_unique(); - m_terrainSystem->Activate(); - AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + // Create and activate the terrain system with our testing defaults for world bounds and query resolution. + CreateAndActivateTerrainSystem(); // Create a box that's twice as big as the layer spawner box. Loop through it and verify that points within the layer box contain // terrain and the expected height & normal values, and points outside the layer box don't contain terrain. @@ -215,7 +263,7 @@ TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) { EXPECT_TRUE(heightQueryTerrainExists); EXPECT_FALSE(isHole); - EXPECT_EQ(height, spawnerHeight); + EXPECT_FLOAT_EQ(height, spawnerHeight); } else { @@ -226,3 +274,202 @@ TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) } } +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithExactSamplersIgnoreQueryGrid) +{ + // Verify that when using the "EXACT" height sampler, the returned heights come directly from the height provider at the exact + // requested location, instead of the position being quantized to the height query grid. + + // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and generates a height based on a sine wave + // using a frequency of 1m and an amplitude of 10m. i.e. Heights will range between -10 to 10 meters, but will have a value of 0 + // every 0.5 meters. The sine wave value is based on the absolute X position only, for simplicity. + constexpr float amplitudeMeters = 10.0f; + constexpr float frequencyMeters = 1.0f; + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(amplitudeMeters * sin(AZ::Constants::TwoPi * (position.GetX() / frequencyMeters))); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution that exactly matches + // the frequency of our sine wave. If our height queries rely on the query resolution, we should always get a value of 0. + const AZ::Vector2 queryResolution(frequencyMeters); + CreateAndActivateTerrainSystem(queryResolution); + + // Test an arbitrary set of points that should all produce non-zero heights with the EXACT sampler. They're not aligned with the + // query resolution, or with the 0 points on the sine wave. + const AZ::Vector2 nonZeroPoints[] = { AZ::Vector2(0.3f), AZ::Vector2(2.8f), AZ::Vector2(5.9f), AZ::Vector2(7.7f) }; + for (auto& nonZeroPoint : nonZeroPoints) + { + AZ::Vector3 position(nonZeroPoint.GetX(), nonZeroPoint.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); + + // We've chosen a bunch of places on the sine wave that should return a non-zero positive or negative value. + constexpr float epsilon = 0.0001f; + EXPECT_GT(fabsf(height), epsilon); + } + + // Test an arbitrary set of points that should all produce zero heights with the EXACT sampler, since they align with 0 points on the + // sine wave, regardless of whether or not they align to the query resolution. + const AZ::Vector2 zeroPoints[] = { AZ::Vector2(0.5f), AZ::Vector2(1.0f), AZ::Vector2(5.0f), AZ::Vector2(7.5f) }; + for (auto& zeroPoint : zeroPoints) + { + AZ::Vector3 position(zeroPoint.GetX(), zeroPoint.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); + + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, 0.0f, epsilon); + } +} + +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithClampSamplersUseQueryGrid) +{ + // Verify that when using the "CLAMP" height sampler, the requested location is quantized to the height query grid before fetching + // the height. + + // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal + // to the X + Y position, so if either one doesn't get clamped we'll get an unexpected result. + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(position.GetX() + position.GetY()); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 0.25 meter intervals. + const AZ::Vector2 queryResolution(0.25f); + CreateAndActivateTerrainSystem(queryResolution); + + // Test some points and verify that the results always go "downward", whether they're in positive or negative space. + // (Z contains the the expected result for convenience). + const HeightTestPoint testPoints[] = + { + { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0.00 + 0.00 + { AZ::Vector2(0.3f, 0.3f), 0.5f }, // Should return a height of 0.25 + 0.25 + { AZ::Vector2(2.8f, 2.8f), 5.5f }, // Should return a height of 2.75 + 2.75 + { AZ::Vector2(5.5f, 5.5f), 11.0f }, // Should return a height of 5.50 + 5.50 + { AZ::Vector2(7.7f, 7.7f), 15.0f }, // Should return a height of 7.50 + 7.50 + + { AZ::Vector2(-0.3f, -0.3f), -1.0f }, // Should return a height of -0.50 + -0.50 + { AZ::Vector2(-2.8f, -2.8f), -6.0f }, // Should return a height of -3.00 + -3.00 + { AZ::Vector2(-5.5f, -5.5f), -11.0f }, // Should return a height of -5.50 + -5.50 + { AZ::Vector2(-7.7f, -7.7f), -15.5f } // Should return a height of -7.75 + -7.75 + }; + for (auto& testPoint : testPoints) + { + const float expectedHeight = testPoint.m_expectedHeight; + + AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP, &heightQueryTerrainExists); + + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, expectedHeight, epsilon); + } +} + +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithBilinearSamplersUseQueryGridToInterpolate) +{ + // Verify that when using the "BILINEAR" height sampler, the heights are interpolated from points sampled from the query grid. + + // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal + // to the X + Y position, so we'll have heights that look like this on our grid: + // 0 *---* 1 + // | | + // 1 *---* 2 + // However, everywhere inside the grid box, we'll generate heights much larger than X + Y. It will have no effect on exact grid + // points, but it will noticeably affect the expected height values if any points get sampled in-between grid points. + + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); + const float amplitudeMeters = 10.0f; + const float frequencyMeters = 1.0f; + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists) + { + // Our generated height will be X + Y. + float expectedHeight = position.GetX() + position.GetY(); + + // If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height. + // This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries. + float unexpectedVariance = amplitudeMeters * + (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters)); + position.SetZ(expectedHeight + unexpectedVariance); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals. + const AZ::Vector2 queryResolution(frequencyMeters); + CreateAndActivateTerrainSystem(queryResolution); + + // Test some points and verify that the results are the expected bilinear filtered result, + // whether they're in positive or negative space. + // (Z contains the the expected result for convenience). + const HeightTestPoint testPoints[] = { + + // Queries directly on grid points. These should return values of X + Y. + { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0 + 0 + { AZ::Vector2(1.0f, 0.0f), 1.0f }, // Should return a height of 1 + 0 + { AZ::Vector2(0.0f, 1.0f), 1.0f }, // Should return a height of 0 + 1 + { AZ::Vector2(1.0f, 1.0f), 2.0f }, // Should return a height of 1 + 1 + { AZ::Vector2(3.0f, 5.0f), 8.0f }, // Should return a height of 3 + 5 + + { AZ::Vector2(-1.0f, 0.0f), -1.0f }, // Should return a height of -1 + 0 + { AZ::Vector2(0.0f, -1.0f), -1.0f }, // Should return a height of 0 + -1 + { AZ::Vector2(-1.0f, -1.0f), -2.0f }, // Should return a height of -1 + -1 + { AZ::Vector2(-3.0f, -5.0f), -8.0f }, // Should return a height of -3 + -5 + + // Queries that are on a grid edge (one axis on the grid, the other somewhere in-between). + // These should just be a linear interpolation of the points, so it should still be X + Y. + + { AZ::Vector2(0.25f, 0.0f), 0.25f }, // Should return a height of -0.25 + 0 + { AZ::Vector2(3.75f, 0.0f), 3.75f }, // Should return a height of -3.75 + 0 + { AZ::Vector2(0.0f, 0.25f), 0.25f }, // Should return a height of 0 + -0.25 + { AZ::Vector2(0.0f, 3.75f), 3.75f }, // Should return a height of 0 + -3.75 + + { AZ::Vector2(2.0f, 3.75f), 5.75f }, // Should return a height of -2 + -3.75 + { AZ::Vector2(2.25f, 4.0f), 6.25f }, // Should return a height of -2.25 + -4 + + { AZ::Vector2(-0.25f, 0.0f), -0.25f }, // Should return a height of -0.25 + 0 + { AZ::Vector2(-3.75f, 0.0f), -3.75f }, // Should return a height of -3.75 + 0 + { AZ::Vector2(0.0f, -0.25f), -0.25f }, // Should return a height of 0 + -0.25 + { AZ::Vector2(0.0f, -3.75f), -3.75f }, // Should return a height of 0 + -3.75 + + { AZ::Vector2(-2.0f, -3.75f), -5.75f }, // Should return a height of -2 + -3.75 + { AZ::Vector2(-2.25f, -4.0f), -6.25f }, // Should return a height of -2.25 + -4 + + // Queries inside a grid square (both axes are in-between grid points) + // This is a full bilinear interpolation, but because we're using X + Y for our heights, the interpolated values + // should *still* be X + Y assuming the points were sampled correctly from the grid points. + + { AZ::Vector2(3.25f, 5.25f), 8.5f }, // Should return a height of 3.25 + 5.25 + { AZ::Vector2(7.71f, 9.74f), 17.45f }, // Should return a height of 7.71 + 9.74 + + { AZ::Vector2(-3.25f, -5.25f), -8.5f }, // Should return a height of -3.25 + -5.25 + { AZ::Vector2(-7.71f, -9.74f), -17.45f }, // Should return a height of -7.71 + -9.74 + }; + + // Loop through every test point and validate it. + for (auto& testPoint : testPoints) + { + const float expectedHeight = testPoint.m_expectedHeight; + + AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, &heightQueryTerrainExists); + + // Verify that our height query returned the bilinear filtered result we expect. + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, expectedHeight, epsilon); + } +} diff --git a/Gems/Terrain/Code/terrain_editor_shared_files.cmake b/Gems/Terrain/Code/terrain_editor_shared_files.cmake index 9868f7d310..d4719f46d5 100644 --- a/Gems/Terrain/Code/terrain_editor_shared_files.cmake +++ b/Gems/Terrain/Code/terrain_editor_shared_files.cmake @@ -15,6 +15,8 @@ set(FILES Source/EditorComponents/EditorTerrainWorldComponent.h Source/EditorComponents/EditorTerrainWorldDebuggerComponent.cpp Source/EditorComponents/EditorTerrainWorldDebuggerComponent.h + Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp + Source/EditorComponents/EditorTerrainWorldRendererComponent.h Source/EditorComponents/EditorTerrainSystemComponent.cpp Source/EditorComponents/EditorTerrainSystemComponent.h Source/EditorTerrainModule.cpp diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index 6a76dd7141..2c013fa332 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -19,6 +19,8 @@ set(FILES Source/Components/TerrainWorldComponent.h Source/Components/TerrainWorldDebuggerComponent.cpp Source/Components/TerrainWorldDebuggerComponent.h + Source/Components/TerrainWorldRendererComponent.cpp + Source/Components/TerrainWorldRendererComponent.h Source/TerrainRenderer/TerrainFeatureProcessor.cpp Source/TerrainRenderer/TerrainFeatureProcessor.h Source/TerrainSystem/TerrainSystem.cpp diff --git a/Gems/Vegetation/Code/Source/Components/DescriptorListComponent.cpp b/Gems/Vegetation/Code/Source/Components/DescriptorListComponent.cpp index 96bef0abda..de2078e2e5 100644 --- a/Gems/Vegetation/Code/Source/Components/DescriptorListComponent.cpp +++ b/Gems/Vegetation/Code/Source/Components/DescriptorListComponent.cpp @@ -8,6 +8,7 @@ #include "DescriptorListComponent.h" #include +#include #include #include #include diff --git a/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.cpp b/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.cpp index dc205079fd..9c7fa34dac 100644 --- a/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.cpp +++ b/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace Vegetation { diff --git a/Gems/Vegetation/Code/Source/DynamicSliceInstanceSpawner.cpp b/Gems/Vegetation/Code/Source/DynamicSliceInstanceSpawner.cpp index 0186bba89d..8e6bbf0ed8 100644 --- a/Gems/Vegetation/Code/Source/DynamicSliceInstanceSpawner.cpp +++ b/Gems/Vegetation/Code/Source/DynamicSliceInstanceSpawner.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/Vegetation/Code/Source/PrefabInstanceSpawner.cpp b/Gems/Vegetation/Code/Source/PrefabInstanceSpawner.cpp index efeb60d598..b386f17cab 100644 --- a/Gems/Vegetation/Code/Source/PrefabInstanceSpawner.cpp +++ b/Gems/Vegetation/Code/Source/PrefabInstanceSpawner.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/Gems/Vegetation/Code/Tests/VegetationMocks.h b/Gems/Vegetation/Code/Tests/VegetationMocks.h index ee620c81a2..ec3760057c 100644 --- a/Gems/Vegetation/Code/Tests/VegetationMocks.h +++ b/Gems/Vegetation/Code/Tests/VegetationMocks.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp index 00f5c0e4f4..a02f0e3c6b 100644 --- a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp +++ b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp @@ -11,6 +11,7 @@ #include "EditorWhiteBoxMeshAsset.h" #include "Util/WhiteBoxEditorUtil.h" +#include #include #include #include diff --git a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp index f18b69393c..849e2272a2 100644 --- a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp +++ b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp @@ -16,6 +16,7 @@ #include "Util/WhiteBoxEditorUtil.h" #include "WhiteBoxComponent.h" +#include #include #include #include diff --git a/Tools/LyTestTools/ly_test_tools/__init__.py b/Tools/LyTestTools/ly_test_tools/__init__.py index 3fb84e9fe5..58340342ab 100755 --- a/Tools/LyTestTools/ly_test_tools/__init__.py +++ b/Tools/LyTestTools/ly_test_tools/__init__.py @@ -11,16 +11,16 @@ import sys logger = logging.getLogger(__name__) -# Supported platforms. +# Supported platforms ALL_PLATFORM_OPTIONS = ['android', 'ios', 'linux', 'mac', 'windows'] -ALL_LAUNCHER_OPTIONS = ['android', 'base', 'mac', 'windows', 'windows_editor', 'windows_dedicated', 'windows_generic'] +ALL_LAUNCHER_OPTIONS = ['android', 'base', 'linux', 'mac', 'windows', 'windows_editor', 'windows_dedicated', 'windows_generic'] ANDROID = False IOS = False # Not implemented - see SPEC-2505 LINUX = sys.platform.startswith('linux') # Not implemented - see SPEC-2501 MAC = sys.platform.startswith('darwin') WINDOWS = sys.platform.startswith('win') -# Detect platforms. +# Detect available platforms HOST_OS_PLATFORM = 'unknown' HOST_OS_EDITOR = 'unknown' HOST_OS_DEDICATED_SERVER = 'unknown' @@ -51,9 +51,12 @@ elif MAC: from ly_test_tools.launchers import MacLauncher LAUNCHERS['mac'] = MacLauncher elif LINUX: - logger.warning(f'Linux operating system is currently not supported, LyTestTools only supports Windows and Mac.') HOST_OS_PLATFORM = 'linux' - HOST_OS_EDITOR = NotImplementedError('LyTestTools does not yet support Linux editor') - HOST_OS_DEDICATED_SERVER = NotImplementedError('LyTestTools does not yet support Linux dedicated server') + HOST_OS_EDITOR = 'linux_editor' + HOST_OS_DEDICATED_SERVER = 'linux_dedicated' + from ly_test_tools.launchers.platforms.linux.launcher import (LinuxLauncher, LinuxEditor, DedicatedLinuxLauncher) + LAUNCHERS['linux'] = LinuxLauncher + LAUNCHERS['linux_editor'] = LinuxEditor + LAUNCHERS['linux_dedicated'] = DedicatedLinuxLauncher else: - logger.warning(f'WARNING: LyTestTools only supports Windows and Mac, got HOST_OS_PLATFORM: "{HOST_OS_PLATFORM}".') + logger.warning(f'WARNING: LyTestTools only supports Windows, Mac, and Linux. Unexpectedly detected HOST_OS_PLATFORM: "{HOST_OS_PLATFORM}".') diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py new file mode 100644 index 0000000000..cdbd379183 --- /dev/null +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py @@ -0,0 +1,78 @@ +""" +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 + +Linux directory and workspace mappings +""" + +import os +import logging + +from ly_test_tools._internal.managers.workspace import AbstractWorkspaceManager +from ly_test_tools._internal.managers.abstract_resource_locator import AbstractResourceLocator + +logger = logging.getLogger(__name__) + +CACHE_DIR = 'linux' +CONFIG_FILE = 'system_linux_pc.cfg' + + +class _LinuxResourceManager(AbstractResourceLocator): + """ + Override for locating resources in a Linux operating system running LyTestTools. + """ + def __init__(self, build_directory: str, project: str): + pass + + def platform_config_file(self): + """ + :return: path to the platform config file + """ + return os.path.join(self.engine_root(), CONFIG_FILE) + + def platform_cache(self): + """ + :return: path to cache for the Linux operating system + """ + return os.path.join(self.project_cache(), CACHE_DIR) + + def project_log(self): + """ + :return: path to 'log' dir in the platform cache dir + """ + return os.path.join(self.project(), 'user', 'log') + + def project_screenshots(self): + """ + :return: path to 'screenshot' dir in the platform cache dir + """ + return os.path.join(self.project(), 'user', 'ScreenShots') + + def editor_log(self): + """ + :return: path to editor.log + """ + return os.path.join(self.project_log(), "editor.log") + + +class LinuxWorkspaceManager(AbstractWorkspaceManager): + """ + A Linux host WorkspaceManager. Contains Mac overridden functions for the AbstractWorkspaceManager class. + Also creates a Mac host ResourceLocator for directory and build mappings. + """ + def __init__( + self, + build_directory=None, + project=None, + tmp_path=None, + output_path=None, + ): + # Type: (str,str,str,str) -> None + super(LinuxWorkspaceManager, self).__init__( + _LinuxResourceManager(build_directory, project), + project=project, + tmp_path=tmp_path, + output_path=output_path, + ) diff --git a/Tools/LyTestTools/ly_test_tools/builtin/helpers.py b/Tools/LyTestTools/ly_test_tools/builtin/helpers.py index f40c8c2234..59117a42f9 100755 --- a/Tools/LyTestTools/ly_test_tools/builtin/helpers.py +++ b/Tools/LyTestTools/ly_test_tools/builtin/helpers.py @@ -9,7 +9,7 @@ Helper file for assisting in building workspaces and setting up LTT with the cur import ly_test_tools._internal.pytest_plugin as pytest_plugin import ly_test_tools._internal.managers.workspace as internal_workspace -from ly_test_tools import MAC, WINDOWS +from ly_test_tools import LINUX, MAC, WINDOWS import os, stat @@ -47,6 +47,11 @@ def create_builtin_workspace( elif MAC: from ly_test_tools._internal.managers.platforms.mac import MacWorkspaceManager build_class = MacWorkspaceManager + elif LINUX: + from ly_test_tools._internal.managers.platforms.linux import LinuxWorkspaceManager + build_class = LinuxWorkspaceManager + else: + raise NotImplementedError("No workspace manager found for current Operating System") instance = build_class( build_directory=build_directory, diff --git a/Tools/LyTestTools/ly_test_tools/environment/process_utils.py b/Tools/LyTestTools/ly_test_tools/environment/process_utils.py index be2f279512..1c0be299a8 100755 --- a/Tools/LyTestTools/ly_test_tools/environment/process_utils.py +++ b/Tools/LyTestTools/ly_test_tools/environment/process_utils.py @@ -12,8 +12,8 @@ import psutil import subprocess import ctypes +import ly_test_tools import ly_test_tools.environment.waiter as waiter -from ly_test_tools import WINDOWS, MAC logger = logging.getLogger(__name__) _PROCESS_OUTPUT_ENCODING = 'utf-8' @@ -182,7 +182,7 @@ def process_is_unresponsive(name): :param name: the name of the process to check :return: True if the specified process is unresponsive and False otherwise """ - if WINDOWS: + if ly_test_tools.WINDOWS: output = check_output(['tasklist', '/FI', f'IMAGENAME eq {name}', '/FI', 'STATUS eq NOT RESPONDING']) @@ -194,7 +194,7 @@ def process_is_unresponsive(name): return True logger.debug(f"Process '{name}' was not unresponsive.") return False - elif MAC: + else: cmd = ["ps", "-axc", "-o", "command,state"] output = check_output(cmd) for line in output.splitlines()[1:]: @@ -209,8 +209,6 @@ def process_is_unresponsive(name): return True logger.debug(f"Process '{name}' was not unresponsive.") return False - else: - raise NotImplementedError('Only Windows and Mac hosts are supported.') def check_output(command, **kwargs): @@ -406,7 +404,7 @@ def close_windows_process(pid, timeout=20, raise_on_missing=False): :param pid: the pid of the process to kill :param raise_on_missing: if set to True, raise RuntimeError if the process does not already exist """ - if not WINDOWS: + if not ly_test_tools.WINDOWS: raise NotImplementedError("close_windows_process() is only implemented on Windows.") if pid is None: diff --git a/Tools/LyTestTools/ly_test_tools/environment/reg_cleaner.py b/Tools/LyTestTools/ly_test_tools/environment/reg_cleaner.py index a3dee1319f..1ffd5a1b4c 100755 --- a/Tools/LyTestTools/ly_test_tools/environment/reg_cleaner.py +++ b/Tools/LyTestTools/ly_test_tools/environment/reg_cleaner.py @@ -8,8 +8,10 @@ Reg cleaner: tools for working with the lumberyard windows registry keys """ import logging import os -import winreg +import ly_test_tools +if ly_test_tools.WINDOWS: + import winreg # OS-specific module availability, must be mocked if this file is accessed elsewhere e.g. unit tests import ly_test_tools.environment.process_utils as process_utils CONST_LY_REG = r'SOFTWARE\O3DE\O3DE' diff --git a/Tools/LyTestTools/ly_test_tools/environment/watchdog.py b/Tools/LyTestTools/ly_test_tools/environment/watchdog.py index 58ac87d9e7..6186a3d354 100755 --- a/Tools/LyTestTools/ly_test_tools/environment/watchdog.py +++ b/Tools/LyTestTools/ly_test_tools/environment/watchdog.py @@ -14,8 +14,8 @@ import re import psutil import time +import ly_test_tools import ly_test_tools.environment.process_utils as process_utils -from ly_test_tools import WINDOWS logger = logging.getLogger(__name__) @@ -28,7 +28,7 @@ class Watchdog(object): DEFAULT_JOIN_TIMEOUT = 10 # seconds def __init__(self, bool_fn, interval=1, raise_on_condition=True, name='ly_test_watchdog', error_message=''): - # type: (func, int, bool, str, str) -> Watchdog + # type: (function, int, bool, str, str) -> Watchdog """ A Watchdog object that takes in a boolean function. It spawns a thread that loops over the boolean function until it returns True. If the boolean function returns True, then a flag will be set and an exception @@ -44,7 +44,7 @@ class Watchdog(object): """ self.caught_failure = False self.name = name - + self._bool_fn = bool_fn self._interval = interval self._raise_on_condition = raise_on_condition @@ -66,7 +66,7 @@ class Watchdog(object): self.caught_failure = False def stop(self, join_timeout=DEFAULT_JOIN_TIMEOUT): - # type: () -> None + # type: (int) -> None """ Stops the watchdog's thread if it's executing by enabling its shutdown Event. If the target function's condition was found, then it will either raise an exception or log an error message. @@ -106,7 +106,7 @@ class Watchdog(object): """ The main function of the watchdog thread. It will repeatedly call its target function until the target function returns True, in which it will set the self.caught_failure attribute to True. - + :return: None """ while True: @@ -128,7 +128,7 @@ class ProcessUnresponsiveWatchdog(Watchdog): Watches a process ID and reports if it is unresponsive for a given timeout. If multiple processes need to be watched, then multiple watchdogs should be instantiated. Note: This is for windows OS only. - + :param process_id: The process id to watch :param interval: The interval (in seconds) for how frequently the bool_fn is called on the thread. :param raise_on_condition: If True, raises an exception when bool_fn returns True. If False, logs an error @@ -138,8 +138,8 @@ class ProcessUnresponsiveWatchdog(Watchdog): :param unresponsive_timeout_seconds: How long the process needs to be unresponsive for in order for the watchdog to report (in seconds). """ - if not WINDOWS: - raise (NotImplementedError, "Process watchdog is only implemented on Windows.") + if not ly_test_tools.WINDOWS: + pass # TODO add non-windows support self._unresponsive_timeout = unresponsive_timeout_seconds self._calculated_timeout_point = None self._pid = process_id diff --git a/Tools/LyTestTools/ly_test_tools/launchers/__init__.py b/Tools/LyTestTools/ly_test_tools/launchers/__init__.py index bb299d89f9..94b712bade 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/__init__.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/__init__.py @@ -6,7 +6,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT """ from ly_test_tools.launchers.platforms.base import Launcher +from ly_test_tools.launchers.platforms.linux.launcher import LinuxLauncher, LinuxEditor, DedicatedLinuxLauncher from ly_test_tools.launchers.platforms.mac.launcher import MacLauncher -from ly_test_tools.launchers.platforms.win.launcher import ( - WinLauncher, DedicatedWinLauncher, WinEditor, WinGenericLauncher) +from ly_test_tools.launchers.platforms.win.launcher import WinLauncher, DedicatedWinLauncher, WinEditor, WinGenericLauncher from ly_test_tools.launchers.platforms.android.launcher import AndroidLauncher diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/__init__.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/__init__.py new file mode 100644 index 0000000000..f5193b300e --- /dev/null +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/__init__.py @@ -0,0 +1,6 @@ +""" +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 +""" diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py new file mode 100644 index 0000000000..210519d197 --- /dev/null +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py @@ -0,0 +1,224 @@ +""" +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 + +Linux compatible launcher +""" + +import logging +import os +import subprocess + +import ly_test_tools.environment.waiter +import ly_test_tools.launchers.exceptions + +from ly_test_tools.launchers.platforms.base import Launcher +from ly_test_tools.launchers.exceptions import TeardownError, ProcessNotStartedError +from tempfile import TemporaryFile + +log = logging.getLogger(__name__) + + +class LinuxLauncher(Launcher): + def __init__(self, build, args): + super(LinuxLauncher, self).__init__(build, args) + self._proc = None + self._ret_code = None + self._tmpout = None + log.debug("Initialized Linux Launcher") + + def binary_path(self): + """ + Return full path to the launcher for this build's configuration and project + + :return: full path to .GameLauncher + """ + assert self.workspace.project is not None + return os.path.join(self.workspace.paths.build_directory(), f"{self.workspace.project}.GameLauncher") + + def setup(self, backupFiles=True, launch_ap=True, configure_settings=True): + """ + Perform setup of this launcher, must be called before launching. + Subclasses should call its parent's setup() before calling its own code, unless it changes configuration files + + :param backupFiles: Bool to backup setup files + :param lauch_ap: Bool to lauch the asset processor + :return: None + """ + # Backup + if backupFiles: + self.backup_settings() + + # Base setup defaults to None + if launch_ap is None: + launch_ap = True + + # Modify and re-configure + if configure_settings: + self.configure_settings() + super(LinuxLauncher, self).setup(backupFiles, launch_ap) + + def launch(self): + """ + Launch the executable and track the subprocess + + :return: None + """ + command = [self.binary_path()] + self.args + self._tmpout = TemporaryFile() + self._proc = subprocess.Popen(command, stdout=self._tmpout, stderr=self._tmpout, universal_newlines=True) + log.debug(f"Started Linux Launcher with command: {command}") + + def get_output(self, encoding="utf-8"): + if self._tmpout is None: + raise ProcessNotStartedError("Process must be started before retrieving output") + + self._tmpout.seek(0) + return self._tmpout.read().decode(encoding) + + def teardown(self): + """ + Perform teardown of this launcher, undoing actions taken by calling setup() + Subclasses should call its parent's teardown() after performing its own teardown. + + :return: None + """ + self.restore_settings() + super(LinuxLauncher, self).teardown() + + def kill(self): + """ + This is a hard kill, and then wait to make sure until it actually ended. + + :return: None + """ + if self._proc is not None: + self._proc.kill() + ly_test_tools.environment.waiter.wait_for( + lambda: not self.is_alive(), + exc=ly_test_tools.launchers.exceptions.TeardownError( + f"Unable to terminate active Linux Launcher with process ID {self._proc.pid}") + ) + self._proc = None + self._ret_code = None + log.debug("Linux Launcher terminated successfully") + + def is_alive(self): + """ + Check the process to verify activity. Side effect of setting self.proc to None if it has ended. + + :return: None + """ + if self._proc is None: + return False + else: + if self._proc.poll() is not None: + self._ret_code = self._proc.poll() + self._proc = None + return False + return True + + def get_pid(self): + # type: () -> int or None + """ + Returns the pid of the launcher process if it exists, else it returns None + + :return: process id or None + """ + if self._proc: + return self._proc.pid + return None + + def get_returncode(self): + # type: () -> int or None + """ + Returns the returncode of the launcher process if it exists, else return None. + The returncode attribute is set when the process is terminated. + + :return: The returncode of the launcher's process + """ + if self._proc: + return self._proc.poll() + else: + return self._ret_code + + def check_returncode(self): + # type: () -> None + """ + Checks the return code of the launcher if it exists. Raises a CrashError if the returncode is non-zero. Returns + None otherwise. This function must be called after exiting the launcher properly and NOT using its provided + teardown(). Provided teardown() will always return a non-zero returncode and should not be checked. + + :return: None + """ + return_code = self.get_returncode() + if return_code != 0: + log.error(f"Launcher exited with non-zero return code: {return_code}") + raise ly_test_tools.launchers.exceptions.CrashError() + return None + + def configure_settings(self): + """ + Configures system level settings and syncs the launcher to the targeted console IP. + + :return: None + """ + # Update settings via the settings registry to avoid modifying the bootstrap.cfg + host_ip = '127.0.0.1' + self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/project_path={self.workspace.paths.project()}"') + self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/remote_ip={host_ip}"') + self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"') + self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"') + + self.workspace.settings.modify_platform_setting("r_AssetProcessorShaderCompiler", 1) + self.workspace.settings.modify_platform_setting("r_ShaderCompilerServer", host_ip) + self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", host_ip) + + +class DedicatedLinuxLauncher(LinuxLauncher): + + def setup(self, backupFiles=True, launch_ap=False): + """ + Perform setup of this launcher, must be called before launching. + Subclasses should call its parent's setup() before calling its own code, unless it changes configuration files + + :param backupFiles: Bool to backup setup files + :param lauch_ap: Bool to lauch the asset processor + :return: None + """ + # Base setup defaults to None + if launch_ap is None: + launch_ap = False + + super(DedicatedLinuxLauncher, self).setup(backupFiles, launch_ap) + + def binary_path(self): + """ + Return full path to the dedicated server launcher for the build directory. + + :return: full path to Launcher_Server + """ + assert self.workspace.project is not None, ( + 'Project cannot be NoneType - please specify a project name string.') + return os.path.join(f"{self.workspace.paths.build_directory()}", + f"{self.workspace.project}.ServerLauncher") + + +class LinuxEditor(LinuxLauncher): + + def __init__(self, build, args): + super(LinuxEditor, self).__init__(build, args) + self.args.append('--regset="/Amazon/Settings/EnableSourceControl=false"') + self.args.append('--regset="/Amazon/AWS/Preferences/AWSAttributionConsentShown=true"') + self.args.append('--regset="/Amazon/AWS/Preferences/AWSAttributionEnabled=false"') + + def binary_path(self): + """ + Return full path to the Editor for this build's configuration and project + + :return: full path to Editor + """ + assert self.workspace.project is not None + return os.path.join(self.workspace.paths.build_directory(), "Editor") diff --git a/Tools/LyTestTools/tests/CMakeLists.txt b/Tools/LyTestTools/tests/CMakeLists.txt index a6d7a0734c..31766f3080 100644 --- a/Tools/LyTestTools/tests/CMakeLists.txt +++ b/Tools/LyTestTools/tests/CMakeLists.txt @@ -39,6 +39,14 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT TEST_SUITE smoke COMPONENT TestTools ) + + ly_add_pytest( + NAME LyTestTools_IntegTests_ProcessUtilsLinux_smoke_no_gpu + PATH ${CMAKE_CURRENT_LIST_DIR}/integ/test_process_utils_linux.py + TEST_SERIAL + TEST_SUITE smoke + COMPONENT TestTools + ) # Regression tests. ly_add_pytest( diff --git a/Tools/LyTestTools/tests/integ/test_process_utils_linux.py b/Tools/LyTestTools/tests/integ/test_process_utils_linux.py new file mode 100644 index 0000000000..23ad8f6653 --- /dev/null +++ b/Tools/LyTestTools/tests/integ/test_process_utils_linux.py @@ -0,0 +1,39 @@ +""" +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 +""" +import subprocess +import pytest + +import ly_test_tools.environment.process_utils as process_utils +import ly_test_tools.environment.waiter as waiter +from ly_test_tools import LINUX + +if LINUX: + pytestmark = pytest.mark.SUITE_smoke +else: + pytestmark = pytest.mark.skipif(not LINUX, reason="Only runs on Linux") + + +class TestProcessUtils(object): + + def test_KillLinuxProcess_ProcessStarted_KilledSuccessfully(self): + # Construct a simple timeout command + linux_executable = 'timeout' + command = [linux_executable, '5s', 'echo'] + + # Verification function for the waiter to call + def process_killed(): + return not process_utils.process_exists(linux_executable, ignore_extensions=True) + + # Create a new process with no output in a new session + with subprocess.Popen(command, stdout=subprocess.DEVNULL, start_new_session=True): + # Ensure that the process was started + assert process_utils.process_exists(linux_executable, ignore_extensions=True), \ + f"Process '{linux_executable}' was expected to exist, but could not be found." + # Kill the process using the process_utils module + process_utils.kill_processes_named(linux_executable, ignore_extensions=True) + # Verify that the process was killed + waiter.wait_for(process_killed, timeout=2) # Raises exception if the process is alive. diff --git a/Tools/LyTestTools/tests/unit/test_builtin_helpers.py b/Tools/LyTestTools/tests/unit/test_builtin_helpers.py index 9eb8a1703d..1c583632a8 100755 --- a/Tools/LyTestTools/tests/unit/test_builtin_helpers.py +++ b/Tools/LyTestTools/tests/unit/test_builtin_helpers.py @@ -8,7 +8,6 @@ Unit tests for ly_test_tools.builtin.helpers functions. """ import unittest.mock as mock import os - import pytest import ly_test_tools.builtin.helpers @@ -112,10 +111,11 @@ class TestBuiltinHelpers(object): ly_test_tools._internal.managers.abstract_resource_locator._find_engine_root( initial_path='mock_dev_dir') + @mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager.teardown') @mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager.setup') @mock.patch('ly_test_tools._internal.managers.artifact_manager.NullArtifactManager', mock.MagicMock()) @mock.patch('os.path.exists', mock.MagicMock(return_value=True)) - def test_SetupBuiltinWorkspace_ValidWorkspaceSetup_ReturnsWorkspaceObject(self, mock_setup): + def test_SetupTeardownBuiltinWorkspace_ValidWorkspaceSetup_ReturnsWorkspaceObject(self, mock_setup, mock_teardown): mock_test_name = 'mock_test_name' mock_test_amount = 10 mock_workspace = ly_test_tools.builtin.helpers.create_builtin_workspace( @@ -125,25 +125,16 @@ class TestBuiltinHelpers(object): output_path='mock_output_path', ) - under_test = ly_test_tools.builtin.helpers.setup_builtin_workspace( + setup_test = ly_test_tools.builtin.helpers.setup_builtin_workspace( mock_workspace, mock_test_name, mock_test_amount) - assert under_test == mock_workspace + assert setup_test == mock_workspace assert mock_setup.call_count == 1 mock_workspace.artifact_manager.set_test_name.assert_called_with( test_name=mock_test_name, amount=mock_test_amount) - @mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager.teardown') - @mock.patch('os.path.exists', mock.MagicMock(return_value=True)) - def test_TeardownBuiltinWorkspace_ValidWorkspaceSetup_ReturnsWorkspaceObject(self, mock_teardown): - mock_workspace = ly_test_tools.builtin.helpers.create_builtin_workspace( - build_directory='build_directory', - project='mock_project', - tmp_path='mock_tmp_path', - output_path='mock_output_path', - ) - - under_test = ly_test_tools.builtin.helpers.teardown_builtin_workspace(mock_workspace) + # Teardown not tested separately due to patched MockedAbstractResourceLocator creating a StopIteration error on Linux + teardown_test = ly_test_tools.builtin.helpers.teardown_builtin_workspace(mock_workspace) - assert under_test == mock_workspace + assert teardown_test == mock_workspace assert mock_teardown.call_count == 1 diff --git a/Tools/LyTestTools/tests/unit/test_file_system.py b/Tools/LyTestTools/tests/unit/test_file_system.py index 80c7e51f53..53ca5a700e 100755 --- a/Tools/LyTestTools/tests/unit/test_file_system.py +++ b/Tools/LyTestTools/tests/unit/test_file_system.py @@ -208,20 +208,6 @@ class TestUnZip(unittest.TestCase): self.assertEqual(path, expected_path) - @mock.patch('os.path.exists') - @mock.patch('ly_test_tools.environment.file_system.check_free_space') - @mock.patch('os.path.join') - def test_Unzip_ReleaseBuild_JoinCalledWithNoPathNoExtension(self, mock_join, mock_check_free, mock_exists): - - path = '' - self.src_path = r'C:\packages\lumberyard-1.2.0.3-54321-pc-1234.zip' - mock_exists.return_value = self.exists - - with mock.patch(self.decomp_obj_name, self.mock_decomp): - path = self.call_decomp(self.dest_path, self.src_path) - - mock_join.assert_called_once_with(self.dest_path, 'lumberyard-1.2.0.3-54321-pc-1234') - @mock.patch('ly_test_tools.environment.file_system.logger') @mock.patch('os.path.exists') @mock.patch('ly_test_tools.environment.file_system.check_free_space') @@ -375,20 +361,6 @@ class TestUnTgz(unittest.TestCase): self.assertEqual(path, expected_path) - @mock.patch('ly_test_tools.environment.file_system.check_free_space') - @mock.patch('os.path.join') - @mock.patch('os.stat') - def test_Untgz_ReleaseBuild_JoinCalledWithNoPathNoExtension(self, mock_stat, mock_join, mock_check_free): - - mock_stat.return_value = self.src_stat - - path = '' - self.src_path = r'C:\packages\lumberyard-1.2.0.3-54321-pc-1234.zip' - with mock.patch(self.decomp_obj_name, self.mock_decomp): - path = self.call_decomp(self.dest_path, self.src_path) - - mock_join.assert_called_once_with(self.dest_path, 'lumberyard-1.2.0.3-54321-pc-1234') - @mock.patch('ly_test_tools.environment.file_system.logger') @mock.patch('os.path.exists') @mock.patch('ly_test_tools.environment.file_system.check_free_space') diff --git a/Tools/LyTestTools/tests/unit/test_launcher_base.py b/Tools/LyTestTools/tests/unit/test_launcher_base.py index 8d7daaab72..1ab9129a7d 100755 --- a/Tools/LyTestTools/tests/unit/test_launcher_base.py +++ b/Tools/LyTestTools/tests/unit/test_launcher_base.py @@ -204,21 +204,20 @@ class TestLauncherBuilder(object): """ def test_CreateLauncher_DummyWorkspace_DefaultLauncher(self): dummy_workspace = mock.MagicMock() - launcher_platform = 'windows' under_test = ly_test_tools.launchers.launcher_helper.create_launcher( - dummy_workspace, launcher_platform) + dummy_workspace, ly_test_tools.HOST_OS_EDITOR) assert isinstance(under_test, ly_test_tools.launchers.Launcher) def test_CreateDedicateLauncher_DummyWorkspace_DefaultLauncher(self): dummy_workspace = mock.MagicMock() - launcher_platform = 'windows_dedicated' under_test = ly_test_tools.launchers.launcher_helper.create_dedicated_launcher( - dummy_workspace, launcher_platform) + dummy_workspace, ly_test_tools.HOST_OS_DEDICATED_SERVER) assert isinstance(under_test, ly_test_tools.launchers.Launcher) + @mock.patch('os.path.exists', mock.MagicMock(return_value=True)) def test_CreateEditor_DummyWorkspace_DefaultLauncher(self): dummy_workspace = mock.MagicMock() - launcher_platform = 'windows_editor' + dummy_workspace.paths.build_directory.return_value = 'dummy' under_test = ly_test_tools.launchers.launcher_helper.create_editor( - dummy_workspace, launcher_platform) + dummy_workspace, ly_test_tools.HOST_OS_GENERIC_EXECUTABLE) assert isinstance(under_test, ly_test_tools.launchers.Launcher) diff --git a/Tools/LyTestTools/tests/unit/test_launcher_linux.py b/Tools/LyTestTools/tests/unit/test_launcher_linux.py new file mode 100644 index 0000000000..2a6c6f7014 --- /dev/null +++ b/Tools/LyTestTools/tests/unit/test_launcher_linux.py @@ -0,0 +1,63 @@ +""" +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 + +Unit Tests for linux launcher-wrappers: all are sanity code-path tests, since no interprocess actions should be taken +""" +import os +import pytest +import unittest.mock as mock + +import ly_test_tools.launchers + +pytestmark = pytest.mark.SUITE_smoke + + +class TestLinuxLauncher(object): + + def test_Construct_TestDoubles_LinuxLauncherCreated(self): + under_test = ly_test_tools.launchers.LinuxLauncher(mock.MagicMock(), ["some_args"]) + assert isinstance(under_test, ly_test_tools.launchers.Launcher) + assert isinstance(under_test, ly_test_tools.launchers.LinuxLauncher) + + def test_BinaryPath_DummyPath_AddPathToApp(self): + dummy_path = "dummy_workspace_path" + dummy_project = "dummy_project" + mock_workspace = mock.MagicMock() + mock_workspace.paths.build_directory.return_value = dummy_path + mock_workspace.project = dummy_project + launcher = ly_test_tools.launchers.LinuxLauncher(mock_workspace, ["some_args"]) + + under_test = launcher.binary_path() + expected = os.path.join(dummy_path, f"{dummy_project}.GameLauncher") + + assert under_test == expected + + @mock.patch('ly_test_tools.launchers.LinuxLauncher.binary_path', mock.MagicMock) + @mock.patch('subprocess.Popen') + def test_Launch_DummyArgs_ArgsPassedToPopen(self, mock_subprocess): + dummy_args = ["some_args"] + launcher = ly_test_tools.launchers.LinuxLauncher(mock.MagicMock(), dummy_args) + + launcher.launch() + + mock_subprocess.assert_called_once() + name, args, kwargs = mock_subprocess.mock_calls[0] + unpacked_args = args[0] # args is a list inside a tuple + assert len(dummy_args) > 0, "accidentally removed dummy_args" + for expected_arg in dummy_args: + assert expected_arg in unpacked_args + + @mock.patch('ly_test_tools.launchers.LinuxLauncher.is_alive') + def test_Kill_MockAliveFalse_SilentSuccess(self, mock_alive): + mock_alive.return_value = False + mock_proc = mock.MagicMock() + launcher = ly_test_tools.launchers.LinuxLauncher(mock.MagicMock(), ["dummy"]) + launcher._proc = mock_proc + + launcher.kill() + + mock_proc.kill.assert_called_once() + mock_alive.assert_called_once() diff --git a/Tools/LyTestTools/tests/unit/test_process_utils.py b/Tools/LyTestTools/tests/unit/test_process_utils.py index a3cdae84ad..de40fb8e34 100755 --- a/Tools/LyTestTools/tests/unit/test_process_utils.py +++ b/Tools/LyTestTools/tests/unit/test_process_utils.py @@ -191,7 +191,7 @@ class TestSubprocessCheckCallWrapperSafe(unittest.TestCase): reason="tests.unit.test_process_utils is restricted to the Windows platform.") class TestCloseWindowsProcess(unittest.TestCase): - @mock.patch('ly_test_tools.environment.process_utils.WINDOWS', False) + @mock.patch('ly_test_tools.WINDOWS', False) def test_CloseWindowsProccess_NotOnWindows_Error(self): with pytest.raises(NotImplementedError): process_utils.close_windows_process(1) diff --git a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake index 47c3c66463..5de2ad0253 100644 --- a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake +++ b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake @@ -24,6 +24,7 @@ ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-android TARGETS Phy ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-android TARGETS mikkelsen PACKAGE_HASH 075e8e4940884971063b5a9963014e2e517246fa269c07c7dc55b8cf2cd99705) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-android TARGETS googletest PACKAGE_HASH 95671be75287a61c9533452835c3647e9c1b30f81b34b43bcb0ec1997cc23894) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-android TARGETS GoogleBenchmark PACKAGE_HASH 20b46e572211a69d7d94ddad1c89ec37bb958711d6ad4025368ac89ea83078fb) +ly_associate_package(PACKAGE_NAME libpng-1.6.37-rev1-android TARGETS libpng PACKAGE_HASH 51d3ec1559c5595196c11e11674cf5745989d3073bf33dabc6697e3eee77a1cc) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-android TARGETS libsamplerate PACKAGE_HASH bf13662afe65d02bcfa16258a4caa9b875534978227d6f9f36c9cfa92b3fb12b) ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-android TARGETS OpenSSL PACKAGE_HASH 4036d4019d722f0e1b7a1621bf60b5a17ca6a65c9c78fd8701cee1131eec8480) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-android TARGETS zlib PACKAGE_HASH 85b730b97176772538cfcacd6b6aaf4655fc2d368d134d6dd55e02f28f183826) diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 816ef6db30..e634cb19c5 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -30,12 +30,13 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev6-linux ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-linux TARGETS PhysX PACKAGE_HASH a110249cbef4f266b0002c4ee9a71f59f373040cefbe6b82f1e1510c811edde6) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-linux TARGETS etc2comp PACKAGE_HASH 9283aa5db5bb7fb90a0ddb7a9f3895317c8ebe8044943124bbb3673a41407430) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-linux TARGETS mcpp PACKAGE_HASH 0aa713f3f2c156cb2f17d9b800aed8acf9df5ab167c48b679853ecb040da9a67) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-linux TARGETS mcpp PACKAGE_HASH df7a998d0bc3fedf44b5bdebaf69ddad6033355b71a590e8642445ec77bc6c41) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux TARGETS mikkelsen PACKAGE_HASH 5973b1e71a64633588eecdb5b5c06ca0081f7be97230f6ef64365cbda315b9c8) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux TARGETS googletest PACKAGE_HASH 7b7ad330f369450c316a4c4592d17fbb4c14c731c95bd8f37757203e8c2bbc1b) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-linux TARGETS GoogleBenchmark PACKAGE_HASH 4038878f337fc7e0274f0230f71851b385b2e0327c495fc3dd3d1c18a807928d) ly_associate_package(PACKAGE_NAME unwind-1.2.1-linux TARGETS unwind PACKAGE_HASH 3453265fb056e25432f611a61546a25f60388e315515ad39007b5925dd054a77) ly_associate_package(PACKAGE_NAME qt-5.15.2-rev5-linux TARGETS Qt PACKAGE_HASH 76b395897b941a173002845c7219a5f8a799e44b269ffefe8091acc048130f28) +ly_associate_package(PACKAGE_NAME libpng-1.6.37-rev1-linux TARGETS libpng PACKAGE_HASH 896451999f1de76375599aec4b34ae0573d8d34620d9ab29cc30b8739c265ba6) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux TARGETS libsamplerate PACKAGE_HASH 41643c31bc6b7d037f895f89d8d8d6369e906b92eff42b0fe05ee6a100f06261) ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-linux TARGETS OpenSSL PACKAGE_HASH b779426d1e9c5ddf71160d5ae2e639c3b956e0fb5e9fcaf9ce97c4526024e3bc) ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev3-linux TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 88c4a359325d749bc34090b9ac466424847f3b71ba0de15045cf355c17c07099) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 58cafdd4b8..fd46191015 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -32,12 +32,13 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev5-mac ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-mac TARGETS PhysX PACKAGE_HASH 5e092a11d5c0a50c4dd99bb681a04b566a4f6f29aa08443d9bffc8dc12c27c8e) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-mac TARGETS etc2comp PACKAGE_HASH 1966ab101c89db7ecf30984917e0a48c0d02ee0e4d65b798743842b9469c0818) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-mac TARGETS mcpp PACKAGE_HASH 48a9c5197bf72843fb9ac44825501ee16bbe3e72e086a32b8c9c05bf47db12ab) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-mac TARGETS mcpp PACKAGE_HASH be9558905c9c49179ef3d7d84f0a5472415acdf7fe2d76eb060d9431723ddf2e) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-mac TARGETS mikkelsen PACKAGE_HASH 83af99ca8bee123684ad254263add556f0cf49486c0b3e32e6d303535714e505) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-mac TARGETS googletest PACKAGE_HASH cbf020d5ef976c5db8b6e894c6c63151ade85ed98e7c502729dd20172acae5a8) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-mac TARGETS GoogleBenchmark PACKAGE_HASH ad25de0146769c91e179953d845de2bec8ed4a691f973f47e3eb37639381f665) ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-mac TARGETS OpenSSL PACKAGE_HASH 28adc1c0616ac0482b2a9d7b4a3a3635a1020e87b163f8aba687c501cf35f96c) ly_associate_package(PACKAGE_NAME qt-5.15.2-rev5-mac TARGETS Qt PACKAGE_HASH 9d25918351898b308ded3e9e571fff6f26311b2071aeafd00dd5b249fdf53f7e) +ly_associate_package(PACKAGE_NAME libpng-1.6.37-mac TARGETS libpng PACKAGE_HASH 1ad76cd038ccc1f288f83c5fe2859a0f35c5154e1fe7658e1230cc428d318a8b) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-mac TARGETS zlib PACKAGE_HASH 21714e8a6de4f2523ee92a7f52d51fbee29c5f37ced334e00dc3c029115b472e) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-mac TARGETS squish-ccr PACKAGE_HASH 155bfbfa17c19a9cd2ef025de14c5db598f4290045d5b0d83ab58cb345089a77) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index d02fefc0e9..333c952051 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -33,7 +33,7 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-windows ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-windows TARGETS PhysX PACKAGE_HASH 0c5ffbd9fa588e5cf7643721a7cfe74d0fe448bf82252d39b3a96d06dfca2298) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-windows TARGETS etc2comp PACKAGE_HASH fc9ae937b2ec0d42d5e7d0e9e8c80e5e4d257673fb33bc9b7d6db76002117123) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-windows TARGETS mcpp PACKAGE_HASH 511672598fa319bfb8db87f965b59abff1620bb7c1dcf7669e039a8acd8d3ff8) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-windows TARGETS mcpp PACKAGE_HASH 794789aba639bfe2f4e8fcb4424d679933dd6290e523084aa0a4e287ac44acb2) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-windows TARGETS mikkelsen PACKAGE_HASH 872c4d245a1c86139aa929f2b465b63ea4ea55b04ced50309135dd4597457a4e) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-windows TARGETS googletest PACKAGE_HASH 7e8f03ae8a01563124e3daa06386f25a2b311c10bb95bff05cae6c41eff83837) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-windows TARGETS GoogleBenchmark PACKAGE_HASH 0c94ca69ae8e7e4aab8e90032b5c82c5964410429f3dd9dbb1f9bf4fe032b1d4) @@ -41,6 +41,7 @@ ly_associate_package(PACKAGE_NAME d3dx12-headers-rev1-windows ly_associate_package(PACKAGE_NAME pyside2-qt-5.15.1-rev2-windows TARGETS pyside2 PACKAGE_HASH c90f3efcc7c10e79b22a33467855ad861f9dbd2e909df27a5cba9db9fa3edd0f) ly_associate_package(PACKAGE_NAME openimageio-2.1.16.0-rev2-windows TARGETS OpenImageIO PACKAGE_HASH 85a2a6cf35cbc4c967c56ca8074babf0955c5b490c90c6e6fd23c78db99fc282) ly_associate_package(PACKAGE_NAME qt-5.15.2-rev4-windows TARGETS Qt PACKAGE_HASH a4634caaf48192cad5c5f408504746e53d338856148285057274f6a0ccdc071d) +ly_associate_package(PACKAGE_NAME libpng-1.6.37-rev1-windows TARGETS libpng PACKAGE_HASH aa20c894fbd7cdaea585a54e37620b3454a7e414a58128acd68ccf6fe76c47d6) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-windows TARGETS libsamplerate PACKAGE_HASH dcf3c11a96f212a52e2c9241abde5c364ee90b0f32fe6eeb6dcdca01d491829f) ly_associate_package(PACKAGE_NAME OpenMesh-8.1-rev1-windows TARGETS OpenMesh PACKAGE_HASH 1c1df639358526c368e790dfce40c45cbdfcfb1c9a041b9d7054a8949d88ee77) ly_associate_package(PACKAGE_NAME civetweb-1.8-rev1-windows TARGETS civetweb PACKAGE_HASH 36d0e58a59bcdb4dd70493fb1b177aa0354c945b06c30416348fd326cf323dd4) diff --git a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake index 25fbaf830f..25df8101b4 100644 --- a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake +++ b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake @@ -25,6 +25,7 @@ ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-ios TARGETS PhysX ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-ios TARGETS mikkelsen PACKAGE_HASH 976aaa3ccd8582346132a10af253822ccc5d5bcc9ea5ba44d27848f65ee88a8a) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-ios TARGETS googletest PACKAGE_HASH 2f121ad9784c0ab73dfaa58e1fee05440a82a07cc556bec162eeb407688111a7) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-ios TARGETS GoogleBenchmark PACKAGE_HASH c2ffaed2b658892b1bcf81dee4b44cd1cb09fc78d55584ef5cb8ab87f2d8d1ae) +ly_associate_package(PACKAGE_NAME libpng-1.6.37-ios TARGETS libpng PACKAGE_HASH 18a8217721083c4dc46514105be43ca764fa9c994a74aa0b57766ea6f8187e7b) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-ios TARGETS libsamplerate PACKAGE_HASH 7656b961697f490d4f9c35d2e61559f6fc38c32102e542a33c212cd618fc2119) ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-ios TARGETS OpenSSL PACKAGE_HASH cd0dfce3086a7172777c63dadbaf0ac3695b676119ecb6d0614b5fb1da03462f) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-ios TARGETS zlib PACKAGE_HASH a59fc0f83a02c616b679799310e9d86fde84514c6d2acefa12c6def0ae4a880c) diff --git a/cmake/Platform/Common/Configurations_common.cmake b/cmake/Platform/Common/Configurations_common.cmake index 15f7344958..d16dfa3eea 100644 --- a/cmake/Platform/Common/Configurations_common.cmake +++ b/cmake/Platform/Common/Configurations_common.cmake @@ -36,7 +36,7 @@ ly_append_configurations_options( AZ_BUILD_CONFIGURATION_TYPE="${LY_BUILD_CONFIGURATION_TYPE_DEBUG}" DEFINES_PROFILE _PROFILE - PROFILE + AZ_PROFILE_BUILD=1 NDEBUG AZ_ENABLE_TRACING AZ_ENABLE_DEBUG_TOOLS diff --git a/cmake/Platform/Mac/InstallUtils_mac.cmake.in b/cmake/Platform/Mac/InstallUtils_mac.cmake.in index de6d9ddf65..d73c4db459 100644 --- a/cmake/Platform/Mac/InstallUtils_mac.cmake.in +++ b/cmake/Platform/Mac/InstallUtils_mac.cmake.in @@ -155,7 +155,9 @@ function(ly_copy source_file target_directory) endfunction() function(ly_download_and_codesign_sdk_python) - execute_process(COMMAND ${CMAKE_COMMAND} -DPAL_PLATFORM_NAME=Mac -DLY_3RDPARTY_PATH=${CMAKE_INSTALL_PREFIX}/python -P ${CMAKE_INSTALL_PREFIX}/python/get_python.cmake) + execute_process(COMMAND ${CMAKE_COMMAND} -DPAL_PLATFORM_NAME=Mac -DLY_3RDPARTY_PATH=${CMAKE_INSTALL_PREFIX}/python -P ${CMAKE_INSTALL_PREFIX}/python/get_python.cmake + WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX} + ) fixup_python_framework(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework) codesign_python_framework_binaries(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework) codesign_file(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework @LY_ROOT_FOLDER@/python/Platform/Mac/PythonEntitlements.plist) diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index d3f4b33b03..61cb101909 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -151,18 +151,18 @@ if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") if(NOT DEFINED LY_ASSET_DEPLOY_ASSET_TYPE) set(LY_ASSET_DEPLOY_ASSET_TYPE @LY_ASSET_DEPLOY_ASSET_TYPE@) endif() - message(STATUS "Generating ${install_output_folder}/Engine.pak from @full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + message(STATUS "Generating ${install_output_folder}/engine.pak from @full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") file(MAKE_DIRECTORY "${install_output_folder}") cmake_path(SET cache_product_path "@full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") file(GLOB product_assets "${cache_product_path}/*") if(product_assets) execute_process( - COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_output_folder}/Engine.pak" --format=zip -- ${product_assets} + COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_output_folder}/engine.pak" --format=zip -- ${product_assets} WORKING_DIRECTORY "${cache_product_path}" RESULT_VARIABLE archive_creation_result ) if(archive_creation_result EQUAL 0) - message(STATUS "${install_output_folder}/Engine.pak generated") + message(STATUS "${install_output_folder}/engine.pak generated") endif() endif() endif() diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index a508329d02..d8f1021590 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -368,7 +368,6 @@ def main(args): if not third_party_path.is_dir(): raise common.LmbrCmdError(f"Invalid --third-party-path '{parsed_args.third_party_path}'.", common.ERROR_CODE_INVALID_PARAMETER) - third_party_path = third_party_path.parent build_dir = parsed_args.build_dir diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index e70fa32878..ee4c27b0a9 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -83,7 +83,8 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest", + "TEST_RESULTS": "True" } }, "test_profile_nounity": { @@ -95,7 +96,8 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest", + "TEST_RESULTS": "True" } }, "asset_profile": { @@ -143,7 +145,8 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", - "CTEST_OPTIONS": "-L (SUITE_periodic)" + "CTEST_OPTIONS": "-L (SUITE_periodic)", + "TEST_RESULTS": "True" } }, "sandbox_test_profile": { @@ -178,7 +181,8 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", - "CTEST_OPTIONS": "-L (SUITE_benchmark)" + "CTEST_OPTIONS": "-L (SUITE_benchmark)", + "TEST_RESULTS": "True" } }, "release": { diff --git a/scripts/build/Platform/Mac/build_config.json b/scripts/build/Platform/Mac/build_config.json index 2cf445a836..7eb3cb5699 100644 --- a/scripts/build/Platform/Mac/build_config.json +++ b/scripts/build/Platform/Mac/build_config.json @@ -102,7 +102,8 @@ "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", - "CTEST_OPTIONS": "-L \"(SUITE_periodic)\"" + "CTEST_OPTIONS": "-L \"(SUITE_periodic)\"", + "TEST_RESULTS": "True" } }, "benchmark_test_profile": { @@ -118,7 +119,8 @@ "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", - "CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"" + "CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"", + "TEST_RESULTS": "True" } }, "release": {