From 62231aeb4cc415a6c43de8bcfab09e087aeffaab Mon Sep 17 00:00:00 2001 From: Mike Chang Date: Mon, 25 Oct 2021 13:48:17 -0700 Subject: [PATCH 01/10] Add xcb input dev packages (#4962) Signed-off-by: Mike Chang --- .../Platform/Linux/package-list.ubuntu-bionic.txt | 7 ++++++- .../Platform/Linux/package-list.ubuntu-focal.txt | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt index 625909e214..a09d823950 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt @@ -12,7 +12,12 @@ libxcb-xinerama0 # For Qt plugins at runtime libxcb-xinput0 # For Qt plugins at runtime libfontconfig1-dev # For Qt plugins at runtime libcurl4-openssl-dev # For HttpRequestor -libsdl2-dev # for WWise/Audio +libsdl2-dev # For WWise/Audio +libxcb-xkb-dev # For xcb keyboard input +libxkbcommon-x11-dev # For xcb keyboard input +libxkbcommon-dev # For xcb keyboard input +libxcb-xfixes0-dev # For mouse input +libxcb-xinput-dev # For mouse input zlib1g-dev mesa-common-dev diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt index 2cbbe58b38..e0e03cda90 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt @@ -16,5 +16,7 @@ libsdl2-dev # for WWise/Audio libxcb-xkb-dev # For xcb keyboard input libxkbcommon-x11-dev # For xcb keyboard input libxkbcommon-dev # For xcb keyboard input +libxcb-xfixes0-dev # For mouse input +libxcb-xinput-dev # For mouse input zlib1g-dev mesa-common-dev From 29dbb0b08935bfdf0c1c364d3458482b46e369d8 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:57:53 -0700 Subject: [PATCH 02/10] Set Project Manager minimum height 700px (#4970) Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- Code/Tools/ProjectManager/Resources/ProjectManager.qss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 6694168f2b..db0ec7e91a 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -9,7 +9,7 @@ QMainWindow { #ScreensCtrl { min-width:1200px; - min-height:800px; + min-height:700px; } QPushButton:focus { From d7045f4c3148229495adc557647541afb249c040 Mon Sep 17 00:00:00 2001 From: Jonny Galloway Date: Mon, 25 Oct 2021 18:18:27 -0500 Subject: [PATCH 03/10] Scaffold PythonGem template (#4888) * Renamed ctest_pytest.ini to pytest.ini so it is used by default, added TestSuite_ as collection file (#4822) * Fixed warnings of unused marks, renamed ctest_pytest.ini to pytest.ini to better consistency on runs * Fixed some test suites to run propertly * Fix missing arguments * Fixed missing cmakelists and renamed missing file * Temp disable editor_testing_tests as timeout in jenkins Signed-off-by: Jonny Gallowy * scaffold PythonGem template Signed-off-by: Jonny Gallowy * CustomTool was not added to CMakeLists Signed-off-by: Jonny Gallowy * Update Templates/PythonGem/Template/Code/CMakeLists.txt makes sense thank you for the help Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Signed-off-by: Jonny Gallowy * Update Templates/PythonGem/Template/Code/CMakeLists.txt Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Signed-off-by: Jonny Gallowy * Delete ${NameLower}_tests_files.cmake Signed-off-by: Jonny Gallowy * suggested changes made Signed-off-by: Jonny Gallowy * cleanup not needed files Signed-off-by: Jonny Gallowy * fix up template.json Signed-off-by: Jonny Gallowy * made a correction Signed-off-by: Jonny Gallowy * made a correction Signed-off-by: Jonny Gallowy * fixes Signed-off-by: Jonny Gallowy * fixes Signed-off-by: Jonny Gallowy * more fixes, fixed a file name Signed-off-by: Jonny Gallowy * Added helper method to az_qt_helpers for retrieving the main window instance. Signed-off-by: Chris Galvan * additional fixes Signed-off-by: Jonny Gallowy * Fix for AP trowing errors at boot (can not get mainwindow) Signed-off-by: Jonny Gallowy * Added missing EBus connect/disconnect calls in the EditorSystemComponent Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * additional fix up Signed-off-by: Jonny Gallowy * adding dialog internals Signed-off-by: Jonny Gallowy Co-authored-by: AMZN-AlexOteiza <82234181+AMZN-AlexOteiza@users.noreply.github.com> Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Co-authored-by: Chris Galvan --- Templates/CMakeLists.txt | 2 + Templates/PythonGem/Template/CMakeLists.txt | 14 ++ .../Code/${NameLower}_editor_files.cmake | 14 ++ .../${NameLower}_editor_shared_files.cmake | 11 + .../${NameLower}_editor_tests_files.cmake | 11 + .../PythonGem/Template/Code/CMakeLists.txt | 76 ++++++ .../Code/Include/${Name}/${Name}Bus.h | 40 ++++ .../Linux/${NameLower}_linux_files.cmake | 15 ++ .../${NameLower}_shared_linux_files.cmake | 15 ++ .../Code/Platform/Linux/PAL_linux.cmake | 11 + .../Platform/Mac/${NameLower}_mac_files.cmake | 15 ++ .../Mac/${NameLower}_shared_mac_files.cmake | 15 ++ .../Template/Code/Platform/Mac/PAL_mac.cmake | 11 + .../${NameLower}_shared_windows_files.cmake | 15 ++ .../Windows/${NameLower}_windows_files.cmake | 15 ++ .../Code/Platform/Windows/PAL_windows.cmake | 11 + .../Code/Source/${Name}EditorModule.cpp | 47 ++++ .../Source/${Name}EditorSystemComponent.cpp | 70 ++++++ .../Source/${Name}EditorSystemComponent.h | 42 ++++ .../Code/Source/${Name}ModuleInterface.h | 36 +++ .../Template/Code/Tests/${Name}EditorTest.cpp | 13 ++ .../Editor/Scripts/${NameLower}_dialog.py | 46 ++++ .../Template/Editor/Scripts/__init__.py | 9 + .../Template/Editor/Scripts/bootstrap.py | 117 ++++++++++ Templates/PythonGem/Template/gem.json | 16 ++ Templates/PythonGem/Template/preview.png | 3 + Templates/PythonGem/template.json | 216 ++++++++++++++++++ 27 files changed, 906 insertions(+) create mode 100644 Templates/PythonGem/Template/CMakeLists.txt create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake create mode 100644 Templates/PythonGem/Template/Code/CMakeLists.txt create mode 100644 Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h create mode 100644 Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp create mode 100644 Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py create mode 100644 Templates/PythonGem/Template/Editor/Scripts/__init__.py create mode 100644 Templates/PythonGem/Template/Editor/Scripts/bootstrap.py create mode 100644 Templates/PythonGem/Template/gem.json create mode 100644 Templates/PythonGem/Template/preview.png create mode 100644 Templates/PythonGem/template.json diff --git a/Templates/CMakeLists.txt b/Templates/CMakeLists.txt index 1a3a45b5ec..84a708989a 100644 --- a/Templates/CMakeLists.txt +++ b/Templates/CMakeLists.txt @@ -9,6 +9,8 @@ ly_install_directory( DIRECTORIES AssetGem + CustomTool + PythonGem DefaultGem DefaultProject MinimalProject diff --git a/Templates/PythonGem/Template/CMakeLists.txt b/Templates/PythonGem/Template/CMakeLists.txt new file mode 100644 index 0000000000..d61bbd9e7d --- /dev/null +++ b/Templates/PythonGem/Template/CMakeLists.txt @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR}) +set(o3de_gem_json ${o3de_gem_path}/gem.json) +o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name") +o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path) + +add_subdirectory(Code) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake new file mode 100644 index 0000000000..8362d37f52 --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Include/${Name}/${Name}Bus.h + Source/${Name}ModuleInterface.h + Source/${Name}EditorSystemComponent.cpp + Source/${Name}EditorSystemComponent.h +) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake new file mode 100644 index 0000000000..2d4ceae97d --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorModule.cpp +) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake new file mode 100644 index 0000000000..ff45c2fc1c --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Tests/${Name}EditorTest.cpp +) diff --git a/Templates/PythonGem/Template/Code/CMakeLists.txt b/Templates/PythonGem/Template/Code/CMakeLists.txt new file mode 100644 index 0000000000..b7a5ac89a9 --- /dev/null +++ b/Templates/PythonGem/Template/Code/CMakeLists.txt @@ -0,0 +1,76 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR} +# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} +# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform +# in which case it will see if that platform is present here or in the restricted folder. +# i.e. It could here in our gem : Gems/${Name}/Code/Platform/ or +# //Gems/${Name}/Code +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem +# is supported by this platform. +include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + + +# If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which +# will also depend on ${Name}.Static +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME ${Name}.Editor.Static STATIC + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_editor_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + AZ::AzToolsFramework + ) + + ly_add_target( + NAME ${Name}.Editor GEM_MODULE + NAMESPACE Gem + AUTOMOC + FILES_CMAKE + ${NameLower}_editor_shared_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + Gem::${Name}.Editor.Static + ) + + # By default, we will specify that the above target ${Name} would be used by + # Tool and Builder type targets when this gem is enabled. If you don't want it + # active in Tools or Builders by default, delete one of both of the following lines: + ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor) + ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor) + + +endif() + +################################################################################ +# Tests +################################################################################ +# See if globally, tests are supported +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + # We globally support tests, see if we support tests on this platform for ${Name}.Static + + # If we are a host platform we want to add tools test like editor tests here + if(PAL_TRAIT_BUILD_HOST_TOOLS) + endif() +endif() diff --git a/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h new file mode 100644 index 0000000000..d09bb2b009 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h @@ -0,0 +1,40 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Requests + { + public: + AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}"); + virtual ~${SanitizedCppName}Requests() = default; + // Put your public methods here + }; + + class ${SanitizedCppName}BusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; + +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp b/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp new file mode 100644 index 0000000000..644c513747 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp @@ -0,0 +1,47 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}EditorModule + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0); + + ${SanitizedCppName}EditorModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}EditorSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + * Non-SystemComponents should not be added here + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList { + azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule) diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp new file mode 100644 index 0000000000..1493c98e68 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp @@ -0,0 +1,70 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ + // {END_LICENSE} + +#include +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class<${SanitizedCppName}EditorSystemComponent, AZ::Component>(); + } + } + + ${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } + + void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + void ${SanitizedCppName}EditorSystemComponent::Activate() + { + ${SanitizedCppName}RequestBus::Handler::BusConnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + } + + void ${SanitizedCppName}EditorSystemComponent::Deactivate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h new file mode 100644 index 0000000000..1db8725a9e --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h @@ -0,0 +1,42 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ + // {END_LICENSE} + +#pragma once +#include +#include <${Name}/${Name}Bus.h> + +#include + +namespace ${SanitizedCppName} +{ + /// System component for ${SanitizedCppName} editor + class ${SanitizedCppName}EditorSystemComponent + : public ${SanitizedCppName}RequestBus::Handler + , private AzToolsFramework::EditorEvents::Bus::Handler + , public AZ::Component + { + public: + AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}"); + static void Reflect(AZ::ReflectContext* context); + + ${SanitizedCppName}EditorSystemComponent(); + ~${SanitizedCppName}EditorSystemComponent(); + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // AZ::Component + void Activate(); + void Deactivate(); + }; +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h b/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h new file mode 100644 index 0000000000..4ddfc9c007 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h @@ -0,0 +1,36 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}ModuleInterface + : public AZ::Module + { + public: + AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0); + + ${SanitizedCppName}ModuleInterface() + { + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + }; + } + }; +}// namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py b/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py new file mode 100644 index 0000000000..39515711ae --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py @@ -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 +""" +# ------------------------------------------------------------------------- +"""${SanitizedCppName}\\editor\\scripts\\${SanitizedCppName}_dialog.py +Generated from O3DE PythonGem Template""" + +import azlmbr +from shiboken2 import wrapInstance, getCppPointer +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QEvent, Qt +from PySide2.QtWidgets import QVBoxLayout, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton + +# Once PySide2 has been bootstrapped, register our ${SanitizedCppName}Dialog with the Editor + +class ${SanitizedCppName}Dialog(QDialog): + def __init__(self, parent=None): + super(${SanitizedCppName}Dialog, self).__init__(parent) + + self.setObjectName("${SanitizedCppName}Dialog") + + self.setWindowTitle("HelloWorld, ${SanitizedCppName} Dialog") + + self.mainLayout = QVBoxLayout(self) + + self.introLabel = QLabel("Put your cool stuff here!") + + self.mainLayout.addWidget(self.introLabel, 0, Qt.AlignCenter) + + self.helpText = str("For help getting started," + "visit the UI Development documentation
" + "or come ask a question in the sig-ui-ux channel on Discord") + + self.helpLabel = QLabel() + self.helpLabel.setTextFormat(Qt.RichText) + self.helpLabel.setText(self.helpText) + self.helpLabel.setOpenExternalLinks(True) + + self.mainLayout.addWidget(self.helpLabel, 0, Qt.AlignCenter) + + self.setLayout(self.mainLayout) + + return \ No newline at end of file diff --git a/Templates/PythonGem/Template/Editor/Scripts/__init__.py b/Templates/PythonGem/Template/Editor/Scripts/__init__.py new file mode 100644 index 0000000000..b5da0c7ff0 --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/__init__.py @@ -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 +""" +# ------------------------------------------------------------------------- + +__ALL__ = ['bootstrap','${NameLower}_dialog'] \ No newline at end of file diff --git a/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py b/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py new file mode 100644 index 0000000000..060116d36c --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py @@ -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 +""" +# ------------------------------------------------------------------------- +"""${SanitizedCppName}\\editor\\scripts\\boostrap.py +Generated from O3DE PythonGem Template""" + +import azlmbr +import az_qt_helpers +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QEvent, Qt +from PySide2.QtWidgets import QMainWindow, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +class SampleUI(QtWidgets.QDialog): + """Lightweight UI Test Class created a button""" + def __init__(self, parent, title='Not Set'): + super(SampleUI, self).__init__(parent) + self.setWindowTitle(title) + self.initUI() + + def initUI(self): + mainLayout = QtWidgets.QHBoxLayout() + testBtn = QtWidgets.QPushButton("I am just a Button man!") + mainLayout.addWidget(testBtn) + self.setLayout(mainLayout) +# ------------------------------------------------------------------------- + +if __name__ == "__main__": + print("${SanitizedCppName}.boostrap, Generated from O3DE PythonGem Template") + + # --------------------------------------------------------------------- + # validate pyside before continuing + try: + azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'IsActive') + params = azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'GetQtBootstrapParameters') + params is not None and params.mainWindowId is not 0 + from PySide2 import QtWidgets + except Exception as e: + _LOGGER.error(f'Pyside not available, exception: {e}') + raise e + + # keep going, import the other PySide2 bits we will use + from PySide2 import QtGui + from PySide2.QtCore import Slot + from shiboken2 import wrapInstance, getCppPointer + + # Get our Editor main window + _widget_main_window = None + try: + _widget_main_window = az_qt_helpers.get_editor_main_window() + except: + pass # may be booting in the AP? + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # creat a custom menu + _tag_str = '${SanitizedCppName}' + + # create our own menuBar + ${SanitizedCppName}_menu = _widget_main_window.menuBar().addMenu(f"&{_tag_str}") + + # nest a menu for util/tool launching + ${SanitizedCppName}_launch_menu = ${SanitizedCppName}_menu.addMenu("examples") + else: + print('No O3DE MainWindow') + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # (1) add the first SampleUI + action_launch_sample_ui = ${SanitizedCppName}_launch_menu.addAction("O3DE:SampleUI") + + @Slot() + def clicked_sample_ui(): + while 1: # simple PySide2 test, set to 0 to disable + ui = SampleUI(parent=_widget_main_window, title='O3DE:SampleUI') + ui.show() + break + return + # Add click event to menu bar + action_launch_sample_ui.triggered.connect(clicked_sample_ui) + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # (1) and custom external module Qwidget + action_launch_${SanitizedCppName}_dialog = ${SanitizedCppName}_launch_menu.addAction("O3DE:${SanitizedCppName}_dialog") + + @Slot() + def clicked_${SanitizedCppName}_dialog(): + while 1: # simple PySide2 test, set to 0 to disable + try: + import az_qt_helpers + from ${NameLower}_dialog import ${SanitizedCppName}Dialog + az_qt_helpers.register_view_pane('${SanitizedCppName} Popup', ${SanitizedCppName}Dialog) + except Exception as e: + print(f'Error: {e}') + print('Skipping register our ${SanitizedCppName}Dialog with the Editor.') + ${SanitizedCppName}_dialog = ${SanitizedCppName}Dialog(parent=_widget_main_window) + ${SanitizedCppName}_dialog.show() + break + return + # Add click event to menu bar + action_launch_${SanitizedCppName}_dialog.triggered.connect(clicked_${SanitizedCppName}_dialog) + # --------------------------------------------------------------------- + + # end \ No newline at end of file diff --git a/Templates/PythonGem/Template/gem.json b/Templates/PythonGem/Template/gem.json new file mode 100644 index 0000000000..353ad6bf8d --- /dev/null +++ b/Templates/PythonGem/Template/gem.json @@ -0,0 +1,16 @@ +{ + "gem_name": "${Name}", + "display_name": "${Name}", + "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", + "type": "Code", + "summary": "A short description of ${Name}.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "${Name}" + ], + "icon_path": "preview.png", + "requirements": "" +} diff --git a/Templates/PythonGem/Template/preview.png b/Templates/PythonGem/Template/preview.png new file mode 100644 index 0000000000..0f393ac886 --- /dev/null +++ b/Templates/PythonGem/Template/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ac9dd09bde78f389e3725ac49d61eff109857e004840bc0bc3881739df9618d +size 2217 diff --git a/Templates/PythonGem/template.json b/Templates/PythonGem/template.json new file mode 100644 index 0000000000..75be757abb --- /dev/null +++ b/Templates/PythonGem/template.json @@ -0,0 +1,216 @@ +{ + "template_name": "PythonGem", + "restricted_name": "o3de", + "restricted_platform_relative_path": "Templates", + "origin": "The primary repo for PythonGem goes here: i.e. http://www.mydomain.com", + "license": "What license PythonGem uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "PythonGem", + "summary": "A short description of PythonGem.", + "canonical_tags": [], + "user_tags": [ + "PythonGem" + ], + "icon_path": "preview.png", + "copyFiles": [ + { + "file": "CMakeLists.txt", + "origin": "CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_files.cmake", + "origin": "Code/${NameLower}_editor_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_shared_files.cmake", + "origin": "Code/${NameLower}_editor_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_tests_files.cmake", + "origin": "Code/${NameLower}_editor_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/CMakeLists.txt", + "origin": "Code/CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Include/${Name}/${Name}Bus.h", + "origin": "Code/Include/${Name}/${Name}Bus.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/PAL_linux.cmake", + "origin": "Code/Platform/Linux/PAL_linux.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/PAL_mac.cmake", + "origin": "Code/Platform/Mac/PAL_mac.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/PAL_windows.cmake", + "origin": "Code/Platform/Windows/PAL_windows.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorModule.cpp", + "origin": "Code/Source/${Name}EditorModule.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.cpp", + "origin": "Code/Source/${Name}EditorSystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.h", + "origin": "Code/Source/${Name}EditorSystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}ModuleInterface.h", + "origin": "Code/Source/${Name}ModuleInterface.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}EditorTest.cpp", + "origin": "Code/Tests/${Name}EditorTest.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/__init__.py", + "origin": "Editor/Scripts/__init__.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/bootstrap.py", + "origin": "Editor/Scripts/bootstrap.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/${NameLower}_dialog.py", + "origin": "Editor/Scripts/${NameLower}_dialog.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "gem.json", + "origin": "gem.json", + "isTemplated": true, + "isOptional": false + }, + { + "file": "preview.png", + "origin": "preview.png", + "isTemplated": false, + "isOptional": false + } + ], + "createDirectories": [ + { + "dir": "Assets", + "origin": "Assets" + }, + { + "dir": "Code", + "origin": "Code" + }, + { + "dir": "Editor", + "origin": "Editor" + }, + { + "dir": "Editor/Scripts", + "origin": "Editor/Scripts" + }, + { + "dir": "Code/Include", + "origin": "Code/Include" + }, + { + "dir": "Code/Include/${Name}", + "origin": "Code/Include/${Name}" + }, + { + "dir": "Code/Platform", + "origin": "Code/Platform" + }, + { + "dir": "Code/Platform/Linux", + "origin": "Code/Platform/Linux" + }, + { + "dir": "Code/Platform/Mac", + "origin": "Code/Platform/Mac" + }, + { + "dir": "Code/Platform/Windows", + "origin": "Code/Platform/Windows" + }, + { + "dir": "Code/Source", + "origin": "Code/Source" + }, + { + "dir": "Code/Tests", + "origin": "Code/Tests" + } + ] +} From c00b99f2d592cbe5141101b8782cfe0f27da33d6 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:33:32 -0700 Subject: [PATCH 04/10] Disable custom titlebar on Mac, Linux, fix resize (#4973) Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- Code/Tools/ProjectManager/CMakeLists.txt | 1 + .../Platform/Linux/PAL_linux_files.cmake | 2 ++ .../Platform/Linux/ProjectManager_Traits_Linux.h | 11 +++++++++++ .../Platform/Linux/ProjectManager_Traits_Platform.h | 11 +++++++++++ .../ProjectManager/Platform/Mac/PAL_mac_files.cmake | 2 ++ .../Platform/Mac/ProjectManager_Traits_Mac.h | 11 +++++++++++ .../Platform/Mac/ProjectManager_Traits_Platform.h | 11 +++++++++++ .../Platform/Windows/PAL_windows_files.cmake | 2 ++ .../Platform/Windows/ProjectManager_Traits_Platform.h | 11 +++++++++++ .../Platform/Windows/ProjectManager_Traits_Windows.h | 11 +++++++++++ Code/Tools/ProjectManager/Source/Application.cpp | 7 ++++++- 11 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h create mode 100644 Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h create mode 100644 Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index d34abcbc6c..a47ccb62c9 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -34,6 +34,7 @@ ly_add_target( INCLUDE_DIRECTORIES PRIVATE Source + Platform/${PAL_PLATFORM_NAME} BUILD_DEPENDENCIES PRIVATE 3rdParty::Qt::Core diff --git a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake index 11222602d5..c3acd44f9b 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_linux.cpp ProjectUtils_linux.cpp ProjectManagerDefs_linux.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Linux.h ) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h new file mode 100644 index 0000000000..7c0543361f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h @@ -0,0 +1,11 @@ +/* + * 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 + +#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..97aee25507 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h @@ -0,0 +1,11 @@ +/* + * 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 diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake index 54b35f0d3c..6d4d453f21 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_mac.cpp ProjectUtils_mac.cpp ProjectManagerDefs_mac.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Mac.h ) diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h new file mode 100644 index 0000000000..7c0543361f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h @@ -0,0 +1,11 @@ +/* + * 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 + +#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..dc77e77fd0 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h @@ -0,0 +1,11 @@ +/* + * 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 diff --git a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake index d95b0d2502..22b4614ddf 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_windows.cpp ProjectUtils_windows.cpp ProjectManagerDefs_windows.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Windows.h ) diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..f5eac50dbc --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h @@ -0,0 +1,11 @@ +/* + * 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 diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h new file mode 100644 index 0000000000..e6422b5a77 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h @@ -0,0 +1,11 @@ +/* + * 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 + +#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR true diff --git a/Code/Tools/ProjectManager/Source/Application.cpp b/Code/Tools/ProjectManager/Source/Application.cpp index a7e4805ce9..29e0df3c3a 100644 --- a/Code/Tools/ProjectManager/Source/Application.cpp +++ b/Code/Tools/ProjectManager/Source/Application.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -194,8 +195,12 @@ namespace O3DE::ProjectManager // set stylesheet after creating the main window or their styles won't get updated AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss")); - // the decoration wrapper is intended to remember window positioning and sizing + // the decoration wrapper is intended to remember window positioning and sizing +#if AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR auto wrapper = new AzQtComponents::WindowDecorationWrapper(); +#else + auto wrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionDisabled); +#endif wrapper->setGuest(m_mainWindow.data()); // show the main window here to apply the stylesheet before restoring geometry or we From 7ab0871327e3c51de99331a6ebd8893afb180524 Mon Sep 17 00:00:00 2001 From: Shirang Jia Date: Mon, 25 Oct 2021 19:16:29 -0700 Subject: [PATCH 05/10] Upload AP logs when there is an AP eeror (#4896) * Updating LFS config to new endpoint (#1624) Signed-off-by: AMZN-alexpete * Updates licenses to APACHE-2.0 OR MIT (#1685) Not to be committed before 7/6/2021 Signed-off-by: lawsonamzn <70027408+lawsonamzn@users.noreply.github.com> * Updating CONTRIBUTING.md Signed-off-by: Terry Michaels * Fixed typo Signed-off-by: Terry Michaels * Updated text to be more descriptive Signed-off-by: Terry Michaels * Upload AP logs when there is an AP eeror Signed-off-by: shiranj * Add AP log upload step for single step Asset jobs Signed-off-by: shiranj * Address more comments Signed-off-by: shiranj * Only call CreateUploadAPLogsStage() once in try/catch block Signed-off-by: shiranj * Revert acciental update to README.md Signed-off-by: shiranj Co-authored-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Co-authored-by: Chris Galvan Co-authored-by: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com> Co-authored-by: Terry Michaels --- scripts/build/Jenkins/Jenkinsfile | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 1388366661..f5e5edcf66 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -102,6 +102,10 @@ def IsJobEnabled(branchName, buildTypeMap, pipelineName, platformName) { } } +def IsAPLogUpload(branchName, jobName) { + return !IsPullRequest(branchName) && jobName.toLowerCase().contains('asset') && env.AP_LOGS_S3_BUCKET +} + def GetRunningPipelineName(JENKINS_JOB_NAME) { // If the job name has an underscore def job_parts = JENKINS_JOB_NAME.tokenize('/')[0].tokenize('_') @@ -431,6 +435,27 @@ def ExportTestScreenshots(Map options, String branchName, String platformName, S } } +def UploadAPLogs(Map options, String branchName, String jobName, String workspace, Map params) { + dir("${workspace}/${ENGINE_REPOSITORY_NAME}") { + projects = params.CMAKE_LY_PROJECTS.split(",") + projects.each{ project -> + def apLogsPath = "${project}/user/log" + def s3UploadScriptPath = "scripts/build/tools/upload_to_s3.py" + if(env.IS_UNIX) { + pythonPath = "${options.PYTHON_DIR}/python.sh" + } + else { + pythonPath = "${options.PYTHON_DIR}/python.cmd" + } + def command = "${pythonPath} -u ${s3UploadScriptPath} --base_dir ${apLogsPath} " + + "--file_regex \".*\" --bucket ${env.AP_LOGS_S3_BUCKET} " + + "--search_subdirectories True --key_prefix ${env.JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${jobName}" + + "--extra-args {\"ACL\": \"bucket-owner-full-control\"}" + palSh(command, "Uploading AP logs for job ${jobName} for branch ${branchName}", false) + } + } + } + def PostBuildCommonSteps(String workspace, boolean mount = true) { echo 'Starting post-build common steps...' @@ -494,6 +519,14 @@ def CreateExportTestScreenshotsStage(Map pipelineConfig, String branchName, Stri } } +def CreateUploadAPLogsStage(Map pipelineConfig, String branchName, String jobName, String workspace, Map params) { + return { + stage("${jobName}_upload_ap_logs") { + UploadAPLogs(pipelineConfig, branchName, jobName, workspace, params) + } + } +} + def CreateTeardownStage(Map environmentVars) { return { stage('Teardown') { @@ -543,6 +576,9 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar error "Node disconnected during build: ${e}" // Error raised to retry stage on a new node } } + if (IsAPLogUpload(branchName, build_job_name)) { + CreateUploadAPLogsStage(pipelineConfig, branchName, build_job_name, envVars['WORKSPACE'], platform.value.build_types[build_job_name].PARAMETERS).call() + } // All other errors will be raised outside the retry block currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' currentException = e.toString() From 77d02ea657e4c381640f26931cdd3b83d38ac8fd Mon Sep 17 00:00:00 2001 From: Roman <69218254+amzn-rhhong@users.noreply.github.com> Date: Tue, 26 Oct 2021 01:59:02 -0700 Subject: [PATCH 06/10] Add some debug rendering options. (#4950) * [WIP] Adding rendering options Signed-off-by: rhhong * code review feedback. Also add the renderflag as qsettings. Signed-off-by: rhhong * fix broken test Signed-off-by: rhhong * fix linux build Signed-off-by: rhhong --- .../EMotionFXAtom/Assets/Icons/Resources.qrc | 1 + .../Assets/Icons/Visualization.svg | 9 + .../Code/Source/AtomActorDebugDraw.cpp | 415 ++++++++++++++++++ .../Code/Source/AtomActorDebugDraw.h | 56 +++ .../Code/Source/AtomActorInstance.cpp | 118 +---- .../Code/Source/AtomActorInstance.h | 11 +- .../Tools/EMStudio/AnimViewportRenderer.cpp | 14 + .../Tools/EMStudio/AnimViewportRenderer.h | 6 +- .../Tools/EMStudio/AnimViewportRequestBus.h | 5 +- .../Tools/EMStudio/AnimViewportToolBar.cpp | 126 ++++-- .../Code/Tools/EMStudio/AnimViewportToolBar.h | 11 + .../Tools/EMStudio/AnimViewportWidget.cpp | 46 +- .../Code/Tools/EMStudio/AnimViewportWidget.h | 8 + .../Code/Tools/EMStudio/AtomRenderPlugin.cpp | 10 +- .../Code/emotionfx_atom_files.cmake | 2 + .../Include/Integration/ActorComponentBus.h | 3 - .../Integration/Components/ActorComponent.cpp | 31 +- .../Integration/Components/ActorComponent.h | 5 +- .../Components/EditorActorComponent.cpp | 15 +- .../Editor/Components/EditorActorComponent.h | 3 + .../Rendering/RenderActorInstance.h | 12 +- .../Source/Integration/Rendering/RenderFlag.h | 44 ++ .../Code/Tests/RenderBackendManagerTests.cpp | 3 +- 23 files changed, 759 insertions(+), 195 deletions(-) create mode 100644 Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Visualization.svg create mode 100644 Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp create mode 100644 Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h create mode 100644 Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Resources.qrc b/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Resources.qrc index 28ae322d5b..7924ef1c4e 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Resources.qrc +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Resources.qrc @@ -1,5 +1,6 @@ Camera_category.svg + Visualization.svg diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Visualization.svg b/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Visualization.svg new file mode 100644 index 0000000000..3d1b40d1b6 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Visualization.svg @@ -0,0 +1,9 @@ + + + + Icons / System / View + Created with Sketch. + + + + diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp new file mode 100644 index 0000000000..eaaf04fcf2 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp @@ -0,0 +1,415 @@ +/* + * 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 + +namespace AZ::Render +{ + AtomActorDebugDraw::AtomActorDebugDraw(AZ::EntityId entityId) + { + m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); + } + + void AtomActorDebugDraw::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance) + { + if (!m_auxGeomFeatureProcessor || !instance) + { + return; + } + + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + if (!auxGeom) + { + return; + } + + // Render aabb + if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_AABB]) + { + RenderAABB(instance); + } + + // Render skeleton + if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_LINESKELETON]) + { + RenderSkeleton(instance); + } + + // Render internal EMFX debug lines. + if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_EMFX_DEBUG]) + { + RenderEMFXDebugDraw(instance); + } + + // Render vertex normal, face normal, tagent and wireframe. + const bool renderVertexNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS]; + const bool renderFaceNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_FACENORMALS]; + const bool renderTangents = renderFlags[EMotionFX::ActorRenderFlag::RENDER_TANGENTS]; + const bool renderWireframe = renderFlags[EMotionFX::ActorRenderFlag::RENDER_WIREFRAME]; + + if (renderVertexNormals || renderFaceNormals || renderTangents || renderWireframe) + { + // Iterate through all enabled nodes + const EMotionFX::Pose* pose = instance->GetTransformData()->GetCurrentPose(); + const size_t geomLODLevel = instance->GetLODLevel(); + const size_t numEnabled = instance->GetNumEnabledNodes(); + for (size_t i = 0; i < numEnabled; ++i) + { + EMotionFX::Node* node = instance->GetActor()->GetSkeleton()->GetNode(instance->GetEnabledNode(i)); + EMotionFX::Mesh* mesh = instance->GetActor()->GetMesh(geomLODLevel, node->GetNodeIndex()); + const AZ::Transform globalTM = pose->GetWorldSpaceTransform(node->GetNodeIndex()).ToAZTransform(); + + m_currentMesh = nullptr; + + if (!mesh) + { + continue; + } + + RenderNormals(mesh, globalTM, renderVertexNormals, renderFaceNormals); + if (renderTangents) + { + RenderTangents(mesh, globalTM); + } + } + } + } + + void AtomActorDebugDraw::PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM) + { + // Check if we have already prepared for the given mesh + if (m_currentMesh == mesh) + { + return; + } + + // Set our new current mesh + m_currentMesh = mesh; + + // Get the number of vertices and the data + const uint32 numVertices = m_currentMesh->GetNumVertices(); + AZ::Vector3* positions = (AZ::Vector3*)m_currentMesh->FindVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS); + + // Check if the vertices fits in our buffer + if (m_worldSpacePositions.size() < numVertices) + { + m_worldSpacePositions.resize(numVertices); + } + + // Pre-calculate the world space positions + for (uint32 i = 0; i < numVertices; ++i) + { + m_worldSpacePositions[i] = worldTM.TransformPoint(positions[i]); + } + } + + void AtomActorDebugDraw::RenderAABB(EMotionFX::ActorInstance* instance) + { + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + const AZ::Aabb& aabb = instance->GetAabb(); + auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line); + } + + void AtomActorDebugDraw::RenderSkeleton(EMotionFX::ActorInstance* instance) + { + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + + const EMotionFX::TransformData* transformData = instance->GetTransformData(); + const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton(); + const EMotionFX::Pose* pose = transformData->GetCurrentPose(); + + const size_t lodLevel = instance->GetLODLevel(); + const size_t numJoints = skeleton->GetNumNodes(); + + m_auxVertices.clear(); + m_auxVertices.reserve(numJoints * 2); + + for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex) + { + const EMotionFX::Node* joint = skeleton->GetNode(jointIndex); + if (!joint->GetSkeletalLODStatus(lodLevel)) + { + continue; + } + + const size_t parentIndex = joint->GetParentIndex(); + if (parentIndex == InvalidIndex) + { + continue; + } + + const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position; + m_auxVertices.emplace_back(parentPos); + + const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position; + m_auxVertices.emplace_back(bonePos); + } + + const AZ::Color skeletonColor(0.604f, 0.804f, 0.196f, 1.0f); + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = &skeletonColor; + lineArgs.m_colorCount = 1; + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } + + void AtomActorDebugDraw::RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance) + { + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + + EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw(); + debugDraw.Lock(); + EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(instance); + actorInstanceData->Lock(); + const AZStd::vector& lines = actorInstanceData->GetLines(); + if (lines.empty()) + { + actorInstanceData->Unlock(); + debugDraw.Unlock(); + return; + } + + m_auxVertices.clear(); + m_auxVertices.reserve(lines.size() * 2); + m_auxColors.clear(); + m_auxColors.reserve(m_auxVertices.size()); + + for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines()) + { + m_auxVertices.emplace_back(line.m_start); + m_auxColors.emplace_back(line.m_startColor); + m_auxVertices.emplace_back(line.m_end); + m_auxColors.emplace_back(line.m_endColor); + } + + AZ_Assert(m_auxVertices.size() == m_auxColors.size(), "Number of vertices and number of colors need to match."); + actorInstanceData->Unlock(); + debugDraw.Unlock(); + + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = m_auxColors.data(); + lineArgs.m_colorCount = static_cast(m_auxColors.size()); + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } + + void AtomActorDebugDraw::RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals) + { + if (!mesh) + { + return; + } + + if (!vertexNormals && !faceNormals) + { + return; + } + + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + if (!auxGeom) + { + return; + } + + // TODO: Move line color to a render setting. + const float faceNormalsScale = 0.01f; + const AZ::Color colorFaceNormals = AZ::Colors::Lime; + const float vertexNormalsScale = 0.01f; + const AZ::Color colorVertexNormals = AZ::Colors::Orange; + + PrepareForMesh(mesh, worldTM); + + AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS); + + // Render face normals + if (faceNormals) + { + const size_t numSubMeshes = mesh->GetNumSubMeshes(); + for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex) + { + EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex); + const uint32 numTriangles = subMesh->GetNumPolygons(); + const uint32 startVertex = subMesh->GetStartVertex(); + const uint32* indices = subMesh->GetIndices(); + + m_auxVertices.clear(); + m_auxVertices.reserve(numTriangles * 2); + m_auxColors.clear(); + m_auxColors.reserve(m_auxVertices.size()); + + for (uint32 triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex) + { + const uint32 triangleStartIndex = triangleIndex * 3; + const uint32 indexA = indices[triangleStartIndex + 0] + startVertex; + const uint32 indexB = indices[triangleStartIndex + 1] + startVertex; + const uint32 indexC = indices[triangleStartIndex + 2] + startVertex; + + const AZ::Vector3& posA = m_worldSpacePositions[indexA]; + const AZ::Vector3& posB = m_worldSpacePositions[indexB]; + const AZ::Vector3& posC = m_worldSpacePositions[indexC]; + + const AZ::Vector3 normalDir = (posB - posA).Cross(posC - posA).GetNormalized(); + + // Calculate the center pos + const AZ::Vector3 normalPos = (posA + posB + posC) * (1.0f / 3.0f); + + m_auxVertices.emplace_back(normalPos); + m_auxColors.emplace_back(colorFaceNormals); + m_auxVertices.emplace_back(normalPos + (normalDir * faceNormalsScale)); + m_auxColors.emplace_back(colorFaceNormals); + } + } + + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = m_auxColors.data(); + lineArgs.m_colorCount = static_cast(m_auxColors.size()); + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } + + // render vertex normals + if (vertexNormals) + { + const size_t numSubMeshes = mesh->GetNumSubMeshes(); + for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex) + { + EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex); + const uint32 numVertices = subMesh->GetNumVertices(); + const uint32 startVertex = subMesh->GetStartVertex(); + + m_auxVertices.clear(); + m_auxVertices.reserve(numVertices * 2); + m_auxColors.clear(); + m_auxColors.reserve(m_auxVertices.size()); + + for (uint32 j = 0; j < numVertices; ++j) + { + const uint32 vertexIndex = j + startVertex; + const AZ::Vector3& position = m_worldSpacePositions[vertexIndex]; + const AZ::Vector3 normal = worldTM.TransformVector(normals[vertexIndex]).GetNormalizedSafe() * vertexNormalsScale; + + m_auxVertices.emplace_back(position); + m_auxColors.emplace_back(colorFaceNormals); + m_auxVertices.emplace_back(position + normal); + m_auxColors.emplace_back(colorFaceNormals); + } + } + + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = m_auxColors.data(); + lineArgs.m_colorCount = static_cast(m_auxColors.size()); + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } + } + + void AtomActorDebugDraw::RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM) + { + if (!mesh) + { + return; + } + + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + if (!auxGeom) + { + return; + } + + // TODO: Move line color to a render setting. + const AZ::Color colorTangents = AZ::Colors::Red; + const AZ::Color mirroredBitangentColor = AZ::Colors::Yellow; + const AZ::Color colorBitangents = AZ::Colors::White; + const float scale = 0.01f; + + // Get the tangents and check if this mesh actually has tangents + AZ::Vector4* tangents = static_cast(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_TANGENTS)); + if (!tangents) + { + return; + } + + AZ::Vector3* bitangents = static_cast(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_BITANGENTS)); + + PrepareForMesh(mesh, worldTM); + + AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS); + const uint32 numVertices = mesh->GetNumVertices(); + + m_auxVertices.clear(); + m_auxVertices.reserve(numVertices * 2); + m_auxColors.clear(); + m_auxColors.reserve(m_auxVertices.size()); + + // Render the tangents and bitangents + AZ::Vector3 orgTangent, tangent, bitangent; + for (uint32 i = 0; i < numVertices; ++i) + { + orgTangent.Set(tangents[i].GetX(), tangents[i].GetY(), tangents[i].GetZ()); + tangent = (worldTM.TransformVector(orgTangent)).GetNormalized(); + + if (bitangents) + { + bitangent = bitangents[i]; + } + else + { + bitangent = tangents[i].GetW() * normals[i].Cross(orgTangent); + } + bitangent = (worldTM.TransformVector(bitangent)).GetNormalizedSafe(); + + m_auxVertices.emplace_back(m_worldSpacePositions[i]); + m_auxColors.emplace_back(colorTangents); + m_auxVertices.emplace_back(m_worldSpacePositions[i] + (tangent * scale)); + m_auxColors.emplace_back(colorTangents); + + if (tangents[i].GetW() < 0.0f) + { + m_auxVertices.emplace_back(m_worldSpacePositions[i]); + m_auxColors.emplace_back(mirroredBitangentColor); + m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * scale)); + m_auxColors.emplace_back(mirroredBitangentColor); + } + else + { + m_auxVertices.emplace_back(m_worldSpacePositions[i]); + m_auxColors.emplace_back(colorBitangents); + m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * scale)); + m_auxColors.emplace_back(colorBitangents); + } + } + + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = m_auxColors.data(); + lineArgs.m_colorCount = static_cast(m_auxColors.size()); + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } +} // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h new file mode 100644 index 0000000000..9f8b137f13 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.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 + +namespace EMotionFX +{ + class Mesh; + class ActorInstance; +} + +namespace AZ::RPI +{ + class AuxGeomDraw; + class AuxGeomFeatureProcessorInterface; +} + +namespace AZ::Render +{ + // Ultility class for atom debug render on actor + class AtomActorDebugDraw + { + public: + AtomActorDebugDraw(AZ::EntityId entityId); + + void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance); + + private: + + void PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM); + void RenderAABB(EMotionFX::ActorInstance* instance); + void RenderSkeleton(EMotionFX::ActorInstance* instance); + void RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance); + void RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals); + void RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM); + + EMotionFX::Mesh* m_currentMesh = nullptr; /**< A pointer to the mesh whose world space positions are in the pre-calculated positions buffer. + NULL in case we haven't pre-calculated any positions yet. */ + AZStd::vector m_worldSpacePositions; /**< The buffer used to store world space positions for rendering normals + tangents and the wireframe. */ + + RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr; + AZStd::vector m_auxVertices; + AZStd::vector m_auxColors; + }; +} diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index a3161002a0..4d1b42a0eb 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -59,7 +60,7 @@ namespace AZ AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId); } - m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(m_entityId); + m_atomActorDebugDraw = AZStd::make_unique(entityId); } AtomActorInstance::~AtomActorInstance() @@ -78,6 +79,11 @@ namespace AZ UpdateBounds(); } + void AtomActorInstance::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags) + { + m_atomActorDebugDraw->DebugDraw(renderFlags, m_actorInstance); + } + void AtomActorInstance::UpdateBounds() { // Update RenderActorInstance world bounding box @@ -99,116 +105,6 @@ namespace AZ AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(m_entityId); } - void AtomActorInstance::DebugDraw(const DebugOptions& debugOptions) - { - if (m_auxGeomFeatureProcessor) - { - if (RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue()) - { - if (debugOptions.m_drawAABB) - { - const AZ::Aabb& aabb = m_actorInstance->GetAabb(); - auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line); - } - - if (debugOptions.m_drawSkeleton) - { - RenderSkeleton(auxGeom.get()); - } - - if (debugOptions.m_emfxDebugDraw) - { - RenderEMFXDebugDraw(auxGeom.get()); - } - } - } - } - - void AtomActorInstance::RenderSkeleton(RPI::AuxGeomDraw* auxGeom) - { - AZ_Assert(m_actorInstance, "Valid actor instance required."); - const EMotionFX::TransformData* transformData = m_actorInstance->GetTransformData(); - const EMotionFX::Skeleton* skeleton = m_actorInstance->GetActor()->GetSkeleton(); - const EMotionFX::Pose* pose = transformData->GetCurrentPose(); - - const size_t lodLevel = m_actorInstance->GetLODLevel(); - const size_t numJoints = skeleton->GetNumNodes(); - - m_auxVertices.clear(); - m_auxVertices.reserve(numJoints * 2); - - for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex) - { - const EMotionFX::Node* joint = skeleton->GetNode(jointIndex); - if (!joint->GetSkeletalLODStatus(lodLevel)) - { - continue; - } - - const size_t parentIndex = joint->GetParentIndex(); - if (parentIndex == InvalidIndex) - { - continue; - } - - const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position; - m_auxVertices.emplace_back(parentPos); - - const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position; - m_auxVertices.emplace_back(bonePos); - } - - const AZ::Color skeletonColor(0.604f, 0.804f, 0.196f, 1.0f); - RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; - lineArgs.m_verts = m_auxVertices.data(); - lineArgs.m_vertCount = static_cast(m_auxVertices.size()); - lineArgs.m_colors = &skeletonColor; - lineArgs.m_colorCount = 1; - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; - auxGeom->DrawLines(lineArgs); - } - - void AtomActorInstance::RenderEMFXDebugDraw(RPI::AuxGeomDraw* auxGeom) - { - EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw(); - debugDraw.Lock(); - EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(m_actorInstance); - actorInstanceData->Lock(); - const AZStd::vector& lines = actorInstanceData->GetLines(); - if (lines.empty()) - { - actorInstanceData->Unlock(); - debugDraw.Unlock(); - return; - } - - m_auxVertices.clear(); - m_auxVertices.reserve(lines.size() * 2); - m_auxColors.clear(); - m_auxColors.reserve(m_auxVertices.size()); - - for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines()) - { - m_auxVertices.emplace_back(line.m_start); - m_auxColors.emplace_back(line.m_startColor); - m_auxVertices.emplace_back(line.m_end); - m_auxColors.emplace_back(line.m_endColor); - } - - AZ_Assert(m_auxVertices.size() == m_auxColors.size(), - "Number of vertices and number of colors need to match."); - actorInstanceData->Unlock(); - debugDraw.Unlock(); - - RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; - lineArgs.m_verts = m_auxVertices.data(); - lineArgs.m_vertCount = static_cast(m_auxVertices.size()); - lineArgs.m_colors = m_auxColors.data(); - lineArgs.m_colorCount = static_cast(m_auxColors.size()); - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; - auxGeom->DrawLines(lineArgs); - } - AZ::Aabb AtomActorInstance::GetWorldBounds() { return m_worldAABB; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h index 5ddab8bc61..7f646466a5 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h @@ -53,6 +53,7 @@ namespace AZ class SkinnedMeshInputBuffers; class MeshFeatureProcessorInterface; class AtomActor; + class AtomActorDebugDraw; //! Render node for managing and rendering actor instances. Each Actor Component //! creates an ActorRenderNode. The render node is responsible for drawing meshes and @@ -85,8 +86,8 @@ namespace AZ // RenderActorInstance overrides ... void OnTick(float timeDelta) override; + void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags); void UpdateBounds() override; - void DebugDraw(const DebugOptions& debugOptions) override; void SetMaterials(const EMotionFX::Integration::ActorAsset::MaterialList& materialPerLOD) override { AZ_UNUSED(materialPerLOD); }; void SetSkinningMethod(EMotionFX::Integration::SkinningMethod emfxSkinningMethod) override; SkinningMethod GetAtomSkinningMethod() const; @@ -184,12 +185,8 @@ namespace AZ void InitWrinkleMasks(); void UpdateWrinkleMasks(); - // Helper and debug geometry rendering - void RenderSkeleton(RPI::AuxGeomDraw* auxGeom); - void RenderEMFXDebugDraw(RPI::AuxGeomDraw* auxGeom); - RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr; - AZStd::vector m_auxVertices; - AZStd::vector m_auxColors; + // Debug geometry rendering + AZStd::unique_ptr m_atomActorDebugDraw; AZStd::intrusive_ptr m_skinnedMeshInputBuffers = nullptr; AZStd::intrusive_ptr m_skinnedMeshInstance; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp index faa033956a..f6186be235 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -206,6 +206,20 @@ namespace EMStudio return result; } + void AnimViewportRenderer::UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags) + { + for (AZ::Entity* entity : m_actorEntities) + { + EMotionFX::Integration::ActorComponent* actorComponent = entity->FindComponent(); + if (!actorComponent) + { + AZ_Assert(false, "Found entity without actor component in the actor entity list."); + continue; + } + actorComponent->SetRenderFlag(renderFlags); + } + } + void AnimViewportRenderer::ResetEnvironment() { // Reset environment diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h index 1de4cbdb1c..a4f67ddfd1 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h @@ -52,6 +52,8 @@ namespace EMStudio //! Return the center position of the existing objects. AZ::Vector3 GetCharacterCenter() const; + void UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags); + private: // This function resets the light, camera and other environment settings. @@ -79,10 +81,6 @@ namespace EMStudio AZ::Entity* m_postProcessEntity = nullptr; AZ::Entity* m_iblEntity = nullptr; - AZ::Entity* m_cameraEntity = nullptr; - AZ::Component* m_cameraComponent = nullptr; - AZ::Entity* m_modelEntity = nullptr; - AZ::Data::AssetId m_modelAssetId; AZ::Entity* m_gridEntity = nullptr; AZStd::vector m_actorEntities; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h index 03784c2158..da4a054c53 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h @@ -8,7 +8,7 @@ #pragma once #include - +#include namespace EMStudio { @@ -35,6 +35,9 @@ namespace EMStudio //! Set the camera view mode. virtual void SetCameraViewMode(CameraViewMode mode) = 0; + + //! Toggle render option flag + virtual void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag) = 0; }; using AnimViewportRequestBus = AZ::EBus; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp index a47a773e10..50cd088f5d 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp @@ -13,7 +13,6 @@ #include #include - namespace EMStudio { AnimViewportToolBar::AnimViewportToolBar(QWidget* parent) @@ -21,40 +20,107 @@ namespace EMStudio { AzQtComponents::ToolBar::addMainToolBarStyle(this); - // Add the camera button - QToolButton* cameraButton = new QToolButton(this); - QMenu* cameraMenu = new QMenu(cameraButton); - - // Add the camera option - const AZStd::vector> cameraOptionNames = { - { CameraViewMode::FRONT, "Front" }, { CameraViewMode::BACK, "Back" }, { CameraViewMode::TOP, "Top" }, - { CameraViewMode::BOTTOM, "Bottom" }, { CameraViewMode::LEFT, "Left" }, { CameraViewMode::RIGHT, "Right" }, - }; - - for (const auto& pair : cameraOptionNames) + // Add the render view options button + QToolButton* renderOptionsButton = new QToolButton(this); { - CameraViewMode mode = pair.first; - cameraMenu->addAction( - pair.second.c_str(), - [mode]() - { - // Send the reset camera event. - AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode); - }); + QMenu* contextMenu = new QMenu(renderOptionsButton); + + renderOptionsButton->setText("Render Options"); + renderOptionsButton->setMenu(contextMenu); + renderOptionsButton->setPopupMode(QToolButton::InstantPopup); + renderOptionsButton->setVisible(true); + renderOptionsButton->setIcon(QIcon(":/EMotionFXAtom/Visualization.svg")); + addWidget(renderOptionsButton); + + CreateViewOptionEntry(contextMenu, "Solid", EMotionFX::ActorRenderFlag::RENDER_SOLID); + CreateViewOptionEntry(contextMenu, "Wireframe", EMotionFX::ActorRenderFlag::RENDER_WIREFRAME); + CreateViewOptionEntry(contextMenu, "Lighting", EMotionFX::ActorRenderFlag::RENDER_LIGHTING); + CreateViewOptionEntry(contextMenu, "Backface Culling", EMotionFX::ActorRenderFlag::RENDER_BACKFACECULLING); + contextMenu->addSeparator(); + CreateViewOptionEntry(contextMenu, "Vertex Normals", EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS); + CreateViewOptionEntry(contextMenu, "Face Normals", EMotionFX::ActorRenderFlag::RENDER_FACENORMALS); + CreateViewOptionEntry(contextMenu, "Tangents", EMotionFX::ActorRenderFlag::RENDER_TANGENTS); + CreateViewOptionEntry(contextMenu, "Actor Bounding Boxes", EMotionFX::ActorRenderFlag::RENDER_AABB); + contextMenu->addSeparator(); + CreateViewOptionEntry(contextMenu, "Line Skeleton", EMotionFX::ActorRenderFlag::RENDER_LINESKELETON); + CreateViewOptionEntry(contextMenu, "Solid Skeleton", EMotionFX::ActorRenderFlag::RENDER_SKELETON); + CreateViewOptionEntry(contextMenu, "Joint Names", EMotionFX::ActorRenderFlag::RENDER_NODENAMES); + CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION); + CreateViewOptionEntry(contextMenu, "Actor Bind Pose", EMotionFX::ActorRenderFlag::RENDER_ACTORBINDPOSE); + contextMenu->addSeparator(); } - cameraMenu->addSeparator(); - cameraMenu->addAction("Reset Camera", - []() + // Add the camera button + QToolButton* cameraButton = new QToolButton(this); + { + QMenu* cameraMenu = new QMenu(cameraButton); + + // Add the camera option + const AZStd::vector> cameraOptionNames = { + { CameraViewMode::FRONT, "Front" }, { CameraViewMode::BACK, "Back" }, { CameraViewMode::TOP, "Top" }, + { CameraViewMode::BOTTOM, "Bottom" }, { CameraViewMode::LEFT, "Left" }, { CameraViewMode::RIGHT, "Right" }, + }; + + for (const auto& pair : cameraOptionNames) + { + CameraViewMode mode = pair.first; + cameraMenu->addAction( + pair.second.c_str(), + [mode]() + { + // Send the reset camera event. + AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode); + }); + } + + cameraMenu->addSeparator(); + cameraMenu->addAction( + "Reset Camera", + []() + { + // Send the reset camera event. + AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::ResetCamera); + }); + cameraButton->setMenu(cameraMenu); + cameraButton->setText("Camera Option"); + cameraButton->setPopupMode(QToolButton::InstantPopup); + cameraButton->setVisible(true); + cameraButton->setIcon(QIcon(":/EMotionFXAtom/Camera_category.svg")); + addWidget(cameraButton); + } + } + + void AnimViewportToolBar::CreateViewOptionEntry( + QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible, char* iconFileName) + { + QAction* action = menu->addAction( + menuEntryName, + [actionIndex]() { // Send the reset camera event. - AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::ResetCamera); + AnimViewportRequestBus::Broadcast( + &AnimViewportRequestBus::Events::ToggleRenderFlag, (EMotionFX::ActorRenderFlag)actionIndex); }); - cameraButton->setMenu(cameraMenu); - cameraButton->setText("Camera Option"); - cameraButton->setPopupMode(QToolButton::InstantPopup); - cameraButton->setVisible(true); - cameraButton->setIcon(QIcon(":/EMotionFXAtom/Camera_category.svg")); - addWidget(cameraButton); + action->setCheckable(true); + action->setVisible(visible); + + if (iconFileName) + { + action->setIcon(QIcon(iconFileName)); + } + + m_actions[actionIndex] = action; + } + + void AnimViewportToolBar::SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags) + { + for (size_t i = 0; i < renderFlags.size(); ++i) + { + QAction* action = m_actions[i]; + if (action) + { + action->setChecked(renderFlags[i]); + } + } } } // namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h index 23ef5fdcd8..57633e5284 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h @@ -11,8 +11,11 @@ #if !defined(Q_MOC_RUN) #include #include +#include #endif +#include + namespace EMStudio { class AnimViewportToolBar : public QToolBar @@ -20,5 +23,13 @@ namespace EMStudio public: AnimViewportToolBar(QWidget* parent = nullptr); ~AnimViewportToolBar() = default; + + void SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags); + + private: + void CreateViewOptionEntry( + QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible = true, char* iconFileName = nullptr); + + QAction* m_actions[EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS] = { nullptr }; }; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp index 2e05864adc..7af5c1607a 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ namespace EMStudio m_renderer = AZStd::make_unique(GetViewportContext()); + LoadRenderFlags(); SetupCameras(); SetupCameraController(); Reinit(); @@ -41,6 +43,7 @@ namespace EMStudio AnimViewportWidget::~AnimViewportWidget() { + SaveRenderFlags(); AnimViewportRequestBus::Handler::BusDisconnect(); } @@ -50,7 +53,14 @@ namespace EMStudio { ResetCamera(); } + m_renderer->Reinit(); + m_renderer->UpdateActorRenderFlag(m_renderFlags); + } + + EMotionFX::ActorRenderFlagBitset AnimViewportWidget::GetRenderFlags() const + { + return m_renderFlags; } void AnimViewportWidget::SetupCameras() @@ -123,7 +133,7 @@ namespace EMStudio SetCameraViewMode(CameraViewMode::DEFAULT); } - void AnimViewportWidget::SetCameraViewMode([[maybe_unused]]CameraViewMode mode) + void AnimViewportWidget::SetCameraViewMode(CameraViewMode mode) { // Set the camera view mode. const AZ::Vector3 targetPosition = m_renderer->GetCharacterCenter(); @@ -155,4 +165,38 @@ namespace EMStudio } GetViewportContext()->SetCameraTransform(AZ::Transform::CreateLookAt(cameraPosition, targetPosition)); } + + void AnimViewportWidget::ToggleRenderFlag(EMotionFX::ActorRenderFlag flag) + { + m_renderFlags[flag] = !m_renderFlags[flag]; + m_renderer->UpdateActorRenderFlag(m_renderFlags); + } + + void AnimViewportWidget::LoadRenderFlags() + { + AZStd::string renderFlagsFilename(EMStudioManager::GetInstance()->GetAppDataFolder()); + renderFlagsFilename += "AnimViewportRenderFlags.cfg"; + QSettings settings(renderFlagsFilename.c_str(), QSettings::IniFormat, this); + + for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i) + { + QString name = QString(i); + const bool isEnabled = settings.value(name).toBool(); + m_renderFlags[i] = isEnabled; + } + m_renderer->UpdateActorRenderFlag(m_renderFlags); + } + + void AnimViewportWidget::SaveRenderFlags() + { + AZStd::string renderFlagsFilename(EMStudioManager::GetInstance()->GetAppDataFolder()); + renderFlagsFilename += "AnimViewportRenderFlags.cfg"; + QSettings settings(renderFlagsFilename.c_str(), QSettings::IniFormat, this); + + for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i) + { + QString name = QString(i); + settings.setValue(name, (bool)m_renderFlags[i]); + } + } } // namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h index 6d708b0996..8aa316a8ba 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h @@ -7,9 +7,11 @@ */ #pragma once +#include #include #include #include +#include namespace EMStudio { @@ -25,14 +27,19 @@ namespace EMStudio AnimViewportRenderer* GetAnimViewportRenderer() { return m_renderer.get(); } void Reinit(bool resetCamera = true); + EMotionFX::ActorRenderFlagBitset GetRenderFlags() const; private: void SetupCameras(); void SetupCameraController(); + void LoadRenderFlags(); + void SaveRenderFlags(); + // AnimViewportRequestBus::Handler overrides void ResetCamera(); void SetCameraViewMode(CameraViewMode mode); + void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag); static constexpr float CameraDistance = 2.0f; @@ -40,5 +47,6 @@ namespace EMStudio AZStd::shared_ptr m_rotateCamera; AZStd::shared_ptr m_translateCamera; AZStd::shared_ptr m_orbitDollyScrollCamera; + EMotionFX::ActorRenderFlagBitset m_renderFlags; }; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp index bc74592485..ce2e76a6c7 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp @@ -90,12 +90,14 @@ namespace EMStudio verticalLayout->setSpacing(1); verticalLayout->setMargin(0); - // Add the tool bar - AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget); - verticalLayout->addWidget(toolBar); - // Add the viewport widget m_animViewportWidget = new AnimViewportWidget(m_innerWidget); + + // Add the tool bar + AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget); + toolBar->SetRenderFlags(m_animViewportWidget->GetRenderFlags()); + + verticalLayout->addWidget(toolBar); verticalLayout->addWidget(m_animViewportWidget); // Register command callbacks. diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_files.cmake b/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_files.cmake index 4ada953caf..413984b96d 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_files.cmake +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_files.cmake @@ -17,4 +17,6 @@ set(FILES Source/AtomActor.cpp Source/AtomActorInstance.h Source/AtomActorInstance.cpp + Source/AtomActorDebugDraw.h + Source/AtomActorDebugDraw.cpp ) diff --git a/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h b/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h index 4161b3c77d..1fd3e55f12 100644 --- a/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h +++ b/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h @@ -85,9 +85,6 @@ namespace EMotionFX /// Detach from parent entity, if attached. virtual void DetachFromEntity() {} - /// Enables debug-drawing of the actor's root. - virtual void DebugDrawRoot(bool /*enable*/) {} - /// Enables rendering of the actor. virtual bool GetRenderCharacter() const = 0; virtual void SetRenderCharacter(bool enable) = 0; diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index 206cae3f59..8520896dd2 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -209,7 +209,6 @@ namespace EMotionFX ->Event("GetJointTransform", &ActorComponentRequestBus::Events::GetJointTransform) ->Event("AttachToEntity", &ActorComponentRequestBus::Events::AttachToEntity) ->Event("DetachFromEntity", &ActorComponentRequestBus::Events::DetachFromEntity) - ->Event("DebugDrawRoot", &ActorComponentRequestBus::Events::DebugDrawRoot) ->Event("GetRenderCharacter", &ActorComponentRequestBus::Events::GetRenderCharacter) ->Event("SetRenderCharacter", &ActorComponentRequestBus::Events::SetRenderCharacter) ->Event("GetRenderActorVisible", &ActorComponentRequestBus::Events::GetRenderActorVisible) @@ -238,8 +237,7 @@ namespace EMotionFX ////////////////////////////////////////////////////////////////////////// ActorComponent::ActorComponent(const Configuration* configuration) - : m_debugDrawRoot(false) - , m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle, + : m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle, float fixedDeltatime) { if (m_actorInstance) @@ -252,6 +250,8 @@ namespace EMotionFX { m_configuration = *configuration; } + + m_debugRenderFlags[RENDER_SOLID] = true; } ////////////////////////////////////////////////////////////////////////// @@ -341,12 +341,6 @@ namespace EMotionFX } } - ////////////////////////////////////////////////////////////////////////// - void ActorComponent::DebugDrawRoot(bool enable) - { - m_debugDrawRoot = enable; - } - ////////////////////////////////////////////////////////////////////////// bool ActorComponent::GetRenderCharacter() const { @@ -400,6 +394,11 @@ namespace EMotionFX return m_sceneFinishSimHandler.IsConnected(); } + void ActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags) + { + m_debugRenderFlags = renderFlags; + } + void ActorComponent::CheckActorCreation() { // Create actor instance. @@ -573,13 +572,13 @@ namespace EMotionFX m_actorInstance->SetIsVisible(isInCameraFrustum && m_configuration.m_renderCharacter); } - RenderActorInstance::DebugOptions debugOptions; - debugOptions.m_drawAABB = m_configuration.m_renderBounds; - debugOptions.m_drawSkeleton = m_configuration.m_renderSkeleton; - debugOptions.m_drawRootTransform = m_debugDrawRoot; - debugOptions.m_rootWorldTransform = GetEntity()->GetTransform()->GetWorldTM(); - debugOptions.m_emfxDebugDraw = true; - m_renderActorInstance->DebugDraw(debugOptions); + m_renderActorInstance->SetIsVisible(m_debugRenderFlags[RENDER_SOLID]); + + // The configuration stores some debug option. When that is enabled, we override it on top of the render flags. + m_debugRenderFlags[RENDER_AABB] = m_debugRenderFlags[RENDER_AABB] || m_configuration.m_renderBounds; + m_debugRenderFlags[RENDER_SKELETON] = m_debugRenderFlags[RENDER_SKELETON] || m_configuration.m_renderSkeleton; + m_debugRenderFlags[RENDER_EMFX_DEBUG] = true; + m_renderActorInstance->DebugDraw(m_debugRenderFlags); } } diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h index 15d9736f34..9eecea2f54 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h @@ -116,7 +116,6 @@ namespace EMotionFX ActorInstance* GetActorInstance() override { return m_actorInstance.get(); } void AttachToEntity(AZ::EntityId targetEntityId, AttachmentType attachmentType) override; void DetachFromEntity() override; - void DebugDrawRoot(bool enable) override; bool GetRenderCharacter() const override; void SetRenderCharacter(bool enable) override; bool GetRenderActorVisible() const override; @@ -181,6 +180,8 @@ namespace EMotionFX bool IsPhysicsSceneSimulationFinishEventConnected() const; AZ::Data::Asset GetActorAsset() const { return m_configuration.m_actorAsset; } + void SetRenderFlag(ActorRenderFlagBitset renderFlags); + private: // AZ::TransformNotificationBus::MultiHandler void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; @@ -201,7 +202,7 @@ namespace EMotionFX AZStd::vector m_attachments; AZStd::unique_ptr m_renderActorInstance; - bool m_debugDrawRoot; ///< Enables drawing of actor root and facing. + ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag AzPhysics::SceneEvents::OnSceneSimulationFinishHandler m_sceneFinishSimHandler; }; diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index 5da9716d15..d0274b3c68 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -180,6 +180,7 @@ namespace EMotionFX , m_lodLevel(0) , m_actorAsset(AZ::Data::AssetLoadBehavior::NoLoad) { + m_debugRenderFlags[RENDER_SOLID] = true; } ////////////////////////////////////////////////////////////////////////// @@ -604,11 +605,10 @@ namespace EMotionFX m_renderActorInstance->OnTick(deltaTime); m_renderActorInstance->UpdateBounds(); - RenderActorInstance::DebugOptions debugOptions; - debugOptions.m_drawAABB = m_renderBounds; - debugOptions.m_drawSkeleton = m_renderSkeleton; - debugOptions.m_emfxDebugDraw = true; - m_renderActorInstance->DebugDraw(debugOptions); + m_debugRenderFlags[RENDER_AABB] = m_renderBounds; + m_debugRenderFlags[RENDER_SKELETON] = m_renderSkeleton; + m_debugRenderFlags[RENDER_EMFX_DEBUG] = true; + m_renderActorInstance->DebugDraw(m_debugRenderFlags); } } @@ -951,5 +951,10 @@ namespace EMotionFX LmbrCentral::AttachmentComponentRequestBus::Event(attachment, &LmbrCentral::AttachmentComponentRequestBus::Events::Reattach, true); } } + + void EditorActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags) + { + m_debugRenderFlags = renderFlags; + } } //namespace Integration } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h index 1d682b47d4..f4c663a92f 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h @@ -104,6 +104,8 @@ namespace EMotionFX ActorComponent::GetRequiredServices(required); } + void SetRenderFlag(ActorRenderFlagBitset renderFlags); + static void Reflect(AZ::ReflectContext* context); private: @@ -162,6 +164,7 @@ namespace EMotionFX size_t m_lodLevel; ActorComponent::BoundingBoxConfiguration m_bboxConfig; bool m_forceUpdateJointsOOV = false; + ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag // \todo attachmentTarget node nr // Note: LOD work in progress. For now we use one material instead of a list of material, because we don't have the support for LOD with multiple scene files. diff --git a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h index 47da0d4cca..8afb2a2f9a 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h +++ b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h @@ -16,6 +16,7 @@ #include #include +#include namespace EMotionFX { @@ -33,16 +34,7 @@ namespace EMotionFX virtual ~RenderActorInstance() = default; virtual void OnTick(float timeDelta) = 0; - - struct DebugOptions - { - bool m_drawAABB = false; - bool m_drawSkeleton = false; - bool m_drawRootTransform = false; - AZ::Transform m_rootWorldTransform = AZ::Transform::CreateIdentity(); - bool m_emfxDebugDraw = false; - }; - virtual void DebugDraw(const DebugOptions& debugOptions) = 0; + virtual void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags) = 0; SkinningMethod GetSkinningMethod() const; virtual void SetSkinningMethod(SkinningMethod skinningMethod); diff --git a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h new file mode 100644 index 0000000000..e053eae6d5 --- /dev/null +++ b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.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 + +namespace EMotionFX +{ + enum ActorRenderFlag + { + RENDER_SOLID = 0, + RENDER_WIREFRAME = 1, + RENDER_LIGHTING = 2, + RENDER_SHADOWS = 3, + RENDER_FACENORMALS = 4, + RENDER_VERTEXNORMALS = 5, + RENDER_TANGENTS = 6, + RENDER_AABB = 7, + RENDER_SKELETON = 8, + RENDER_LINESKELETON = 9, + RENDER_NODEORIENTATION = 10, + RENDER_NODENAMES = 11, + RENDER_GRID = 12, + RENDER_BACKFACECULLING = 13, + RENDER_ACTORBINDPOSE = 14, + RENDER_RAGDOLL_COLLIDERS = 15, + RENDER_RAGDOLL_JOINTLIMITS = 16, + RENDER_HITDETECTION_COLLIDERS = 17, + RENDER_USE_GRADIENTBACKGROUND = 18, + RENDER_MOTIONEXTRACTION = 19, + RENDER_CLOTH_COLLIDERS = 20, + RENDER_SIMULATEDOBJECT_COLLIDERS = 21, + RENDER_SIMULATEJOINTS = 22, + RENDER_EMFX_DEBUG = 23, + NUM_RENDERFLAGS = 24 + }; + + using ActorRenderFlagBitset = AZStd::bitset; +} diff --git a/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp b/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp index 89d6fab93b..be990f444c 100644 --- a/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp +++ b/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -64,7 +65,7 @@ namespace EMotionFX } MOCK_METHOD1(OnTick, void(float)); - MOCK_METHOD1(DebugDraw, void(const DebugOptions&)); + MOCK_METHOD1(DebugDraw, void(const EMotionFX::ActorRenderFlagBitset&)); MOCK_CONST_METHOD0(IsVisible, bool()); MOCK_METHOD1(SetIsVisible, void(bool)); MOCK_METHOD1(SetMaterials, void(const ActorAsset::MaterialList&)); From 5374860444e406b798cb8ad7f9f2ce0270dc5438 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Tue, 26 Oct 2021 02:29:12 -0700 Subject: [PATCH 07/10] Bug Fix: resolve entity ordering for EntityOutliner (#4798) (#4938) * bugifx: resolve dragging behaviour for EntityOutliner (#4798) Signed-off-by: Michael Pollind * chore: cleanup and rework logic Signed-off-by: Michael Pollind --- .../UI/Outliner/EntityOutlinerListModel.cpp | 50 ++++++++++++------- .../UI/Outliner/EntityOutlinerListModel.hxx | 9 +++- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index a5f1e29942..434a1d8303 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -943,13 +943,15 @@ namespace AzToolsFramework { return false; } - + + const int count = rowCount(parent); AZ::EntityId newParentId = GetEntityFromIndex(parent); - AZ::EntityId beforeEntityId = GetEntityFromIndex(index(row, 0, parent)); + AZ::EntityId beforeEntityId = (row >= 0 && row < count) ? GetEntityFromIndex(index(row, 0, parent)) : AZ::EntityId(); EntityIdList topLevelEntityIds; topLevelEntityIds.reserve(entityIdListContainer.m_entityIds.size()); ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::FindTopLevelEntityIdsInactive, entityIdListContainer.m_entityIds, topLevelEntityIds); - if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId)) + const auto appendActionForInvalid = newParentId.IsValid() && (row >= count) ? AppendEnd : AppendBeginning; + if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId, appendActionForInvalid)) { return false; } @@ -1046,7 +1048,7 @@ namespace AzToolsFramework return true; } - bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId) + bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId, ReparentForInvalid forInvalid) { AZ_PROFILE_FUNCTION(AzToolsFramework); if (!CanReparentEntities(newParentId, selectedEntityIds)) @@ -1056,10 +1058,18 @@ namespace AzToolsFramework m_isFilterDirty = true; - ScopedUndoBatch undo("Reparent Entities"); //capture child entity order before re-parent operation, which will automatically add order info if not present EntityOrderArray entityOrderArray = GetEntityChildOrder(newParentId); + //search for the insertion entity in the order array + const auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId); + const bool hasInvalidIndex = beforeEntityItr == entityOrderArray.end(); + if (hasInvalidIndex && forInvalid == None) + { + return false; + } + + ScopedUndoBatch undo("Reparent Entities"); // The new parent is dirty due to sort change(s) undo.MarkEntityDirty(GetEntityIdForSortInfo(newParentId)); @@ -1088,9 +1098,7 @@ namespace AzToolsFramework } } - //search for the insertion entity in the order array - auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId); - + //replace order info matching selection with bad values rather than remove to preserve layout for (auto& id : entityOrderArray) { @@ -1100,17 +1108,25 @@ namespace AzToolsFramework } } - if (newParentId.IsValid()) + //if adding to a valid parent entity, insert at the found entity location or at the head/tail depending on placeAtTail flag + if (hasInvalidIndex) { - //if adding to a valid parent entity, insert at the found entity location or at the head of the container - auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.begin(); - entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end()); - } - else + switch(forInvalid) + { + case AppendEnd: + entityOrderArray.insert(entityOrderArray.end(), processedEntityIds.begin(), processedEntityIds.end()); + break; + case AppendBeginning: + entityOrderArray.insert(entityOrderArray.begin(), processedEntityIds.begin(), processedEntityIds.end()); + break; + default: + AZ_Assert(false, "Unexpected type for ReparentForInvalid"); + break; + } + } + else { - //if adding to an invalid parent entity (the root), insert at the found entity location or at the tail of the container - auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.end(); - entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end()); + entityOrderArray.insert(beforeEntityItr, processedEntityIds.begin(), processedEntityIds.end()); } //remove placeholder entity ids diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx index 8176867038..0a46ee4850 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx @@ -72,6 +72,13 @@ namespace AzToolsFramework ColumnCount //!< Total number of columns }; + enum ReparentForInvalid + { + None, //!< For an invalid location the entity does not change location + AppendEnd, //!< Append Item to end of target parent list + AppendBeginning, //!< Append Item to the beginning of target parent list + }; + // Note: the ColumnSortIndex column isn't shown, hence the -1 and the need for a separate counter. // A wrong column count number causes refresh issues and hover mismatch on model update. static const int VisibleColumnCount = ColumnCount - 1; @@ -162,7 +169,7 @@ namespace AzToolsFramework // Buffer Processing Slots - These are called using single-shot events when the buffers begin to fill. bool CanReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds) const; - bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId()); + bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId(), ReparentForInvalid forInvalid = None); //! Use the current filter setting and re-evaluate the filter. void InvalidateFilter(); From 48fca740a217ca29274d9859de6781de476ebe27 Mon Sep 17 00:00:00 2001 From: AMZN-stankowi <4838196+AMZN-stankowi@users.noreply.github.com> Date: Tue, 26 Oct 2021 08:29:43 -0700 Subject: [PATCH 08/10] Fixed crash if you save a bundle outside the default bundle folder (#4974) Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> --- .../source/models/AssetBundlerAbstractFileTableModel.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp b/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp index 45d01b9b5b..8def0a698c 100644 --- a/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp +++ b/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp @@ -61,8 +61,12 @@ namespace AssetBundler { AZStd::string absolutePath = filePath.toUtf8().data(); if (AZ::IO::FileIOBase::GetInstance()->Exists(absolutePath.c_str())) - { - AZStd::string projectName = pathToProjectNameMap.at(absolutePath); + { + AZStd::string projectName; + if (pathToProjectNameMap.contains(absolutePath)) + { + projectName = pathToProjectNameMap.at(absolutePath); + } // If a project name is already specified, then the associated file is a default file LoadFile(absolutePath, projectName, !projectName.empty()); From b3b646dad9c16d4a93cf1058034329c615c1a54b Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:31:31 -0500 Subject: [PATCH 09/10] Moved the Asset Catalog loading from LmbrCentral to the AzFramework::Application (#4568) * Moved the loading of the AssetCatalog from LmbrCentralSystemComponent to AzFramework Application Modified the AssetCatalog::InitializeCatalog function to no longer rely on the TickBus to send out the `AssetCatalogEventBus::OnCatalogLoaded` event. It now queues a function on the AssetCatalogRequestBus to send the OnCatalogLoaded event as soon as the dispatching for the AssetCatalogRequestBus has completed on the current thread. This is done by updating the AssetCatalogRequestBus to use EBus ThreadDispatchPolicy to add a callback to invoke any queued function has soon a thread has finished dispatching and has released its DispatchMutex Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the AssetCatalogRequestBus to add a custom DispatchLockGuard The AssetCatalogRequestBus uses the custom lock guard to dispatch queued events after it has unlocked it's context mutex for the current thread. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed GetContext call from the AssetCatalogRequests::PostThreadDispatchInvoker Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the definition of FileTagQueryManager::GetDefaultFileTagFilePath function to return a path Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the AZ_CONSOLEFREEFUNC macro to actually use the _NAME The _NAME parameter was not being used before, resulting in the Console stringified name of the function being used. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed CrySystem dependencies from the BundlingSystemComponent Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Moved the loading of the AssetCatalog from LmbrCentralSystemComponent to AzFramework Application Modified the AssetCatalog::InitializeCatalog function to no longer rely on the TickBus to send out the `AssetCatalogEventBus::OnCatalogLoaded` event. It now queues a function on the AssetCatalogRequestBus to send the OnCatalogLoaded event as soon as the dispatching for the AssetCatalogRequestBus has completed on the current thread. This is done by updating the AssetCatalogRequestBus to use EBus ThreadDispatchPolicy to add a callback to invoke any queued function has soon a thread has finished dispatching and has released its DispatchMutex Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the AssetCatalogRequestBus to add a custom DispatchLockGuard The AssetCatalogRequestBus uses the custom lock guard to dispatch queued events after it has unlocked it's context mutex for the current thread. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed GetContext call from the AssetCatalogRequests::PostThreadDispatchInvoker Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the definition of FileTagQueryManager::GetDefaultFileTagFilePath function to return a path Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the AZ_CONSOLEFREEFUNC macro to actually use the _NAME The _NAME parameter was not being used before, resulting in the Console stringified name of the function being used. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed CrySystem dependencies from the BundlingSystemComponent Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Addded missing template parameter to AssetCatalogRequests The fixes the compile error. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding AssetBus::MultiHandler::BusDisconnect call The BlastSystemComponent was connecting to the Bus, but not disconnecting from it, causing an assert to fire to it being a multi-thread bus Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added support for DataDrive lifecycle events to the ComponentApplication The events are using the SettingsRegistry NotifyEvent to track when certain keys are modified to signal handlers. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Corrected invalid JSON creation in ModuleManager::DeactivateEntities Resolved clang warning about used type alias Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fix for dangling reference in lambda registered to the SettingsRegistry Notifier event This was causing the EditorPythonBinding tests to crash due to the following circumstances. First Python has created an instance of a SettingsRegistryProxy Second the SettingsRegistry sends an event during the time when the SettingsRegistryProxy exists. This issue was exposed due to the ComponentApplication Lifecycle events using the SettingsRegistry to dispatch during various times of the application workflow. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- .../AzCore/AzCore/Asset/AssetManagerBus.h | 42 +++++++- .../AzCore/Component/ComponentApplication.cpp | 22 ++++- .../AzCore/Component/ComponentApplication.h | 4 +- .../ComponentApplicationLifecycle.cpp | 87 +++++++++++++++++ .../Component/ComponentApplicationLifecycle.h | 54 +++++++++++ .../AzCore/AzCore/Console/IConsole.h | 2 +- .../AzCore/AzCore/Module/ModuleManager.cpp | 38 ++++++-- .../Settings/SettingsRegistryScriptUtils.cpp | 18 ++-- .../AzCore/AzCore/azcore_files.cmake | 2 + .../AzFramework/Application/Application.cpp | 39 +++++++- .../AzFramework/Asset/AssetCatalog.cpp | 15 +-- .../AzFramework/Asset/AssetRegistry.cpp | 1 + .../AzFramework/FileTag/FileTag.cpp | 21 ++-- .../AzFramework/AzFramework/FileTag/FileTag.h | 3 +- .../AzFramework/FileTag/FileTagComponent.cpp | 4 +- .../AzFramework/Tests/AssetCatalog.cpp | 4 +- Code/Legacy/CrySystem/IDebugCallStack.cpp | 4 +- .../SerializeContextTools/SliceConverter.cpp | 2 - .../Application/AtomToolsApplication.cpp | 2 - .../Components/BlastSystemComponent.cpp | 1 + .../Bundling/BundlingSystemComponent.cpp | 97 +++++++------------ .../Source/Bundling/BundlingSystemComponent.h | 17 +--- Gems/LmbrCentral/Code/Source/LmbrCentral.cpp | 43 +------- Gems/LmbrCentral/Code/Source/LmbrCentral.h | 10 -- Registry/application_lifecycle_events.setreg | 31 ++++++ 25 files changed, 390 insertions(+), 173 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp create mode 100644 Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h create mode 100644 Registry/application_lifecycle_events.setreg diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h b/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h index f8e263d6b0..f76ea19589 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h @@ -65,8 +65,35 @@ namespace AZ ////////////////////////////////////////////////////////////////////////// // EBusTraits overrides - Application is a singleton - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - typedef AZStd::recursive_mutex MutexType; + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + using MutexType = AZStd::recursive_mutex; + + static constexpr bool EnableEventQueue = true; + using EventQueueMutexType = AZStd::mutex; + struct PostThreadDispatchInvoker + { + ~PostThreadDispatchInvoker(); + }; + + template + struct ThreadDispatchLockGuard + { + ThreadDispatchLockGuard(DispatchMutex& contextMutex) + : m_lock{ contextMutex } + {} + ThreadDispatchLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock) + : m_lock{ contextMutex, adopt_lock } + {} + ThreadDispatchLockGuard(const ThreadDispatchLockGuard&) = delete; + ThreadDispatchLockGuard& operator=(const ThreadDispatchLockGuard&) = delete; + private: + PostThreadDispatchInvoker m_threadPolicyInvoker; + using LockType = AZStd::conditional_t, AZStd::scoped_lock>; + LockType m_lock; + }; + + template + using DispatchLockGuard = ThreadDispatchLockGuard; ////////////////////////////////////////////////////////////////////////// virtual ~AssetCatalogRequests() = default; @@ -200,6 +227,17 @@ namespace AZ using AssetCatalogRequestBus = AZ::EBus; + inline AssetCatalogRequests::PostThreadDispatchInvoker::~PostThreadDispatchInvoker() + { + if (!AssetCatalogRequestBus::IsInDispatchThisThread()) + { + if (AssetCatalogRequestBus::QueuedEventCount()) + { + AssetCatalogRequestBus::ExecuteQueuedEvents(); + } + } + } + /* * Events that AssetManager listens for */ diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index ba66d56379..dabf02e260 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -44,8 +45,6 @@ #include #include -#include -#include #include #include @@ -506,6 +505,16 @@ namespace AZ SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands); SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry); + // The /O3DE/Application/LifecycleEvents array contains a valid set of lifecycle events + // Those lifecycle events are normally read from the /Registry + // which isn't merged until ComponentApplication::Create invokes MergeSettingsToRegistry + // So pre-populate the valid lifecycle even entries + ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SystemAllocatorCreated"); + ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SettingsRegistryAvailable"); + ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "ConsoleAvailable"); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorCreated", R"({})"); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryAvailable", R"({})"); + // Create the Module Manager m_moduleManager = AZStd::make_unique(); @@ -520,6 +529,7 @@ namespace AZ m_ownsConsole = true; m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead()); m_settingsRegistryConsoleFunctors = AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_settingsRegistry, *m_console); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleAvailable", R"({})"); } } @@ -551,6 +561,7 @@ namespace AZ { AZ::Interface::Unregister(m_console); delete m_console; + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleUnavailable", R"({})"); } m_moduleManager.reset(); @@ -558,6 +569,8 @@ namespace AZ if (AZ::SettingsRegistry::Get() == m_settingsRegistry.get()) { SettingsRegistry::Unregister(m_settingsRegistry.get()); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryUnavailable", R"({})"); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorPendingDestruction", R"({})"); } m_settingsRegistry.reset(); @@ -672,6 +685,8 @@ namespace AZ ReflectionEnvironment::GetReflectionManager()->Reflect(azrtti_typeid(this), [this](ReflectContext* context) {Reflect(context); }); RegisterCoreComponents(); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerAvailable", R"({})"); + TickBus::AllowFunctionQueuing(true); SystemTickBus::AllowFunctionQueuing(true); @@ -691,6 +706,7 @@ namespace AZ // Load the actual modules LoadModules(); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsLoaded", R"({})"); // Execute user.cfg after modules have been loaded but before processing any command-line overrides AZ::IO::FixedMaxPath platformCachePath; @@ -756,12 +772,14 @@ namespace AZ m_entities.rehash(0); // force free all memory DestroyReflectionManager(); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerUnavailable", R"({})"); static_cast(m_settingsRegistry.get())->ClearNotifiers(); static_cast(m_settingsRegistry.get())->ClearMergeEvents(); // Uninit and unload any dynamic modules. m_moduleManager->UnloadModules(); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsUnloaded", R"({})"); NameDictionary::Destroy(); diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h index f2b1bb8905..6df93aff4e 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h @@ -175,6 +175,8 @@ namespace AZ bool m_loadDynamicModules = true; //! Used by test fixtures to ensure reflection occurs to edit context. bool m_createEditContext = false; + //! Indicates whether the AssetCatalog.xml should be loaded by default in Application::StartCommon + bool m_loadAssetCatalog = true; }; ComponentApplication(); @@ -356,7 +358,7 @@ namespace AZ /// Calculates the root directory of the engine. void CalculateEngineRoot(); - /// Calculates the directory where the bootstrap.cfg file resides. + /// Deprecated: The term "AppRoot" has no meaning void CalculateAppRoot(); template diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp new file mode 100644 index 0000000000..533d55ed27 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.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 + +namespace AZ::ComponentApplicationLifecycle +{ + bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName) + { + using FixedValueString = SettingsRegistryInterface::FixedValueString; + using Type = SettingsRegistryInterface::Type; + FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey }; + eventRegistrationKey += '/'; + eventRegistrationKey += eventName; + return settingsRegistry.GetType(eventRegistrationKey) == Type::Object; + } + + bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue) + { + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + using Format = AZ::SettingsRegistryInterface::Format; + + if (!ValidateEvent(settingsRegistry, eventName)) + { + AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot signal event %.*s. Name does is not a field of object "%.*s".)" + R"( Please make sure the entry exists in the '/Registry/application_lifecycle_events.setreg")" + " or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey)); + return false; + } + auto eventRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey), + AZ_STRING_ARG(eventName)); + + return settingsRegistry.MergeSettings(eventValue, Format::JsonMergePatch, eventRegistrationKey); + } + + bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName) + { + using FixedValueString = SettingsRegistryInterface::FixedValueString; + using Format = AZ::SettingsRegistryInterface::Format; + + if (!ValidateEvent(settingsRegistry, eventName)) + { + FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey }; + eventRegistrationKey += '/'; + eventRegistrationKey += eventName; + return settingsRegistry.MergeSettings(R"({})", Format::JsonMergePatch, eventRegistrationKey); + } + + return true; + } + + bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler, + AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName) + { + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + using Type = AZ::SettingsRegistryInterface::Type; + using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler; + + if (!ValidateEvent(settingsRegistry, eventName)) + { + AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot register event %.*s. Name does is not a field of object "%.*s".)" + R"( Please make sure the entry exists in the '/Registry/application_lifecycle_events.setreg")" + " or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey)); + return false; + } + auto eventNameRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey), + AZ_STRING_ARG(eventName)); + auto lifecycleCallback = [callback = AZStd::move(callback), eventNameRegistrationKey](AZStd::string_view path, Type type) + { + if (path == eventNameRegistrationKey) + { + callback(path, type); + } + }; + + handler = NotifyEventHandler(AZStd::move(lifecycleCallback)); + settingsRegistry.RegisterNotifier(handler); + + return true; + } +} diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h new file mode 100644 index 0000000000..5fc6c4b2c3 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h @@ -0,0 +1,54 @@ +/* + * 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::ComponentApplicationLifecycle +{ + //! Root Key where lifecycle events should be registered under + inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Runtime/Application/LifecycleEvents"; + + + //! Validates that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey + //! @param settingsRegistry registry where @eventName will be searched + //! @param eventName name of key that validated that exists as an element in the ApplicationLifecycleEventRegistrationKey array + //! @return true if the @eventName was found in the ApplicationLifecycleEventRegistrationKey array + bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName); + + //! Wrapper around setting a value underneath the ApplicationLifecycleEventRegistrationKey + //! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array + //! It then appends the @eventName to the ApplicationLifecycleEventRegistrationKey merges the @eventValue into + //! the SettingsRegistry at that key + //! NOTE: This function should only be invoked from ComponentApplication and its derived classes + //! @param settingsRegistry registry where eventName should be set + //! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to signal + //! @param eventValue JSON Object that will be merged into the SettingsRegistry at / + //! @return true if the eventValue was successfully merged at the / + bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue); + + //! Register that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey + //! @param settingsRegistry registry where @eventName will be searched + //! @param eventName name of key that will be stored in the ApplicationLifecycleEventRegistrationKey array + //! @return true if the event passed validation or the eventName was stored in the ApplicationLifecycleEventRegistrationKey array + bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName); + + //! Wrapper around registering the NotifyEventHandler with the SettingsRegistry for the specified event + //! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array and if + //! so moves the @callback into @handler and then registers the handler with the SettingsRegistry NotifyEvent + //! @param settingsRegistry registry where handler will be registered + //! @param handler handler where callback will be moved into and then registered with the SettingsRegistry + //! if the specified @eventName passes validation + //! @param callback will be moved into the handler if the specified @eventName is valid + //! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to register + //! @return true if the handler was registered with the SettingsRegistry NotifyEvent + bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler, + AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName); +} diff --git a/Code/Framework/AzCore/AzCore/Console/IConsole.h b/Code/Framework/AzCore/AzCore/Console/IConsole.h index 73d17ac65a..aef163d22b 100644 --- a/Code/Framework/AzCore/AzCore/Console/IConsole.h +++ b/Code/Framework/AzCore/AzCore/Console/IConsole.h @@ -262,6 +262,6 @@ static constexpr AZ::ThreadSafety ConsoleThreadSafety<_TYPE, std::enable_if_t Functor##_FUNCTION(#_FUNCTION, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION) + inline AZ::ConsoleFunctor Functor##_FUNCTION(_NAME, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION) #define AZ_CONSOLEFREEFUNC(...) AZ_MACRO_SPECIALIZE(AZ_CONSOLEFREEFUNC_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__)) diff --git a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp index 1fdeafa309..cbf3ce5243 100644 --- a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp +++ b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp @@ -10,16 +10,13 @@ #include #include -#include -#include #include #include #include -#include #include +#include +#include #include -#include -#include #include #include @@ -221,11 +218,16 @@ namespace AZ } } + AZStd::string componentNamesArray = R"({ "SystemComponents":[)"; + const char* comma = ""; // For all system components, deactivate for (auto componentIt = m_systemComponents.rbegin(); componentIt != m_systemComponents.rend(); ++componentIt) { ModuleEntity::DeactivateComponent(**componentIt); + componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, (*componentIt)->RTTI_GetTypeName()); + comma = ", "; } + componentNamesArray += R"(]})"; // For all modules that we created an entity for, set them to "Init" (meaning not Activated) for (auto& moduleData : m_ownedModules) @@ -239,6 +241,13 @@ namespace AZ // Since the system components have been deactivated clear out the vector. m_systemComponents.clear(); + + // Signal that the System Components have deactivated + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsDeactivated", componentNamesArray); + } + } //========================================================================= @@ -284,7 +293,11 @@ namespace AZ { // Split the tag list AZStd::vector tagList; - AZStd::tokenize(tags, ",", tagList); + auto TokenizeTags = [&tagList](AZStd::string_view token) + { + tagList.push_back(token); + }; + AZ::StringFunc::TokenizeVisitor(tags, TokenizeTags, ','); m_systemComponentTags.resize(tagList.size()); AZStd::transform(tagList.begin(), tagList.end(), m_systemComponentTags.begin(), [](const AZStd::string_view& tag) @@ -737,11 +750,17 @@ namespace AZ } } + AZStd::string componentNamesArray = R"({ "SystemComponents":[)"; + const char* comma = ""; // Activate the entities in the appropriate order for (Component* component : componentsToActivate) { ModuleEntity::ActivateComponent(*component); + + componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, component->RTTI_GetTypeName()); + comma = ", "; } + componentNamesArray += R"(]})"; // Done activating; set state to active for (auto& moduleData : modulesToInit) @@ -755,5 +774,12 @@ namespace AZ // Save the activated components for deactivation later m_systemComponents.insert(m_systemComponents.end(), componentsToActivate.begin(), componentsToActivate.end()); + + // Signal that the System Components are activated + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsActivated", + componentNamesArray); + } } } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp index c32591874b..5cd36785dc 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp @@ -15,20 +15,20 @@ namespace AZ::SettingsRegistryScriptUtils::Internal { - static void RegisterScriptProxyForNotify(SettingsRegistryScriptProxy& settingsRegistryProxy) + static void RegisterScriptProxyForNotify(SettingsRegistryInterface* settingsRegistry, + SettingsRegistryScriptProxy::NotifyEventProxy* notifyEventProxy) { - if (settingsRegistryProxy.IsValid()) + if (settingsRegistry != nullptr) { - auto ForwardSettingsUpdateToProxyEvent = [&settingsRegistryProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type) + auto ForwardSettingsUpdateToProxyEvent = [notifyEventProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type) { - if (settingsRegistryProxy.m_notifyEventProxy) + if (notifyEventProxy) { - settingsRegistryProxy.m_notifyEventProxy->m_scriptNotifyEvent.Signal(path); + notifyEventProxy->m_scriptNotifyEvent.Signal(path); } }; // Register the forwarding function with the BehaviorContext - settingsRegistryProxy.m_notifyEventProxy->m_settingsUpdatedHandler = - settingsRegistryProxy.m_settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent); + notifyEventProxy->m_settingsUpdatedHandler = settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent); } } @@ -37,7 +37,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal : m_settingsRegistry(AZStd::move(settingsRegistry)) , m_notifyEventProxy(AZStd::make_shared()) { - RegisterScriptProxyForNotify(*this); + RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get()); } // Raw AZ::SettingsRegistryInterface pointer is not owned by the proxy, so it's deleter is a no-op @@ -45,7 +45,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal : m_settingsRegistry(settingsRegistry, [](AZ::SettingsRegistryInterface*) {}) , m_notifyEventProxy(AZStd::make_shared()) { - RegisterScriptProxyForNotify(*this); + RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get()); } // SettingsRegistryScriptProxy function that determines if the SettingsRegistry object is valid diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 41229429f2..0c5a360844 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -41,6 +41,8 @@ set(FILES Component/ComponentApplication.cpp Component/ComponentApplication.h Component/ComponentApplicationBus.h + Component/ComponentApplicationLifecycle.cpp + Component/ComponentApplicationLifecycle.h Component/ComponentBus.cpp Component/ComponentBus.h Component/ComponentExport.h diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index f8d0ea8bb1..8fb986802e 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,11 @@ namespace AzFramework m_archiveFileIO = AZStd::make_unique(m_archive.get()); AZ::IO::FileIOBase::SetInstance(m_archiveFileIO.get()); SetFileIOAliases(); + // The FileIOAvailable event needs to be registered here as this event is sent out + // before the settings registry has merged the .setreg files from the + // (That happens in MergeSettingsToRegistry + AZ::ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "FileIOAvailable"); + AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOAvailable", R"({})"); } if (auto nativeUI = AZ::Interface::Get(); nativeUI == nullptr) @@ -172,6 +178,8 @@ namespace AzFramework // Archive classes relies on the FileIOBase DirectInstance to close // files properly m_directFileIO.reset(); + + AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOUnavailable", R"({})"); } void Application::Start(const Descriptor& descriptor, const StartupParameters& startupParameters) @@ -196,7 +204,25 @@ namespace AzFramework systemEntity->Activate(); AZ_Assert(systemEntity->GetState() == AZ::Entity::State::Active, "System Entity failed to activate."); - m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active); + + if (m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active); m_isStarted) + { + if (m_startupParameters.m_loadAssetCatalog) + { + // Start Monitoring Asset changes over the network and load the AssetCatalog + auto StartMonitoringAssetsAndLoadCatalog = [this](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + if (AZ::IO::FixedMaxPath assetCatalogPath; + m_settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) + { + assetCatalogPath /= "assetcatalog.xml"; + assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str()); + } + }; + using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus; + AssetCatalogBus::Broadcast(AZStd::move(StartMonitoringAssetsAndLoadCatalog)); + } + } } void Application::PreModuleLoad() @@ -210,6 +236,17 @@ namespace AzFramework { if (m_isStarted) { + if (m_startupParameters.m_loadAssetCatalog) + { + // Stop Monitoring Assets changes + auto StopMonitoringAssets = [](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + assetCatalogRequests->StopMonitoringAssets(); + }; + using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus; + AssetCatalogBus::Broadcast(AZStd::move(StopMonitoringAssets)); + } + ApplicationLifecycleEvents::Bus::Broadcast(&ApplicationLifecycleEvents::OnApplicationAboutToStop); m_pimpl.reset(); diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp index e6b8211c28..3d76bcefc4 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp @@ -565,7 +565,7 @@ namespace AzFramework if (!bytes.empty()) { - AZStd::shared_ptr < AzFramework::AssetRegistry> prevRegistry; + AZStd::shared_ptr prevRegistry; if (!m_initialized) { // First time initialization may have updates already processed which we want to apply @@ -589,7 +589,6 @@ namespace AzFramework AZ_TracePrintf("AssetCatalog", "Loaded registry containing %u assets.\n", m_registry->m_assetIdToInfo.size()); // It's currently possible in tools for us to have received updates from AP which were applied before the catalog was ready to load - // due to CryPak and CrySystem coming online later than our components if (!m_initialized) { ApplyDeltaCatalog(prevRegistry); @@ -611,12 +610,13 @@ namespace AzFramework // the mutex. If the listener tries to perform a blocking asset load via GetAsset() / BlockUntilLoadComplete(), the spawned asset // thread will make a call to the AssetCatalogRequestBus and block on the held mutex. This would cause a deadlock, since the listener // won't free the mutex until the load is complete. - // So instead, queue the notification until the next tick, so that it doesn't occur within the AssetCatalogRequestBus mutex, and also + // So instead, queue the notification until after the AssetCatalogRequestBus mutex is unlocked for the current thread, and also // so that the entire AssetCatalog initialization is complete. - AZ::TickBus::QueueFunction([catalogRegistryString = AZStd::string(catalogRegistryFile)]() - { - AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str()); - }); + auto OnCatalogLoaded = [catalogRegistryString = AZStd::string(catalogRegistryFile)]() + { + AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str()); + }; + AZ::Data::AssetCatalogRequestBus::QueueFunction(AZStd::move(OnCatalogLoaded)); } } @@ -978,6 +978,7 @@ namespace AzFramework AZStd::lock_guard lock(m_registryMutex); m_registry->Clear(); + m_initialized = false; } diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp index f9eefa7639..26a22f4625 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp @@ -61,6 +61,7 @@ namespace AzFramework //========================================================================= void AssetRegistry::Clear() { + m_assetDependencies = {}; m_assetIdToInfo = AssetIdToInfoMap(); m_assetPathToId = AssetPathToIdMap(); } diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp index f820ee56ed..abbb9ec2f1 100644 --- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp +++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp @@ -10,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -89,19 +89,19 @@ namespace AzFramework bool FileTagManager::Save(FileTagType fileTagType, const AZStd::string& destinationFilePath = AZStd::string()) { AzFramework::FileTag::FileTagAsset* fileTagAsset = GetFileTagAsset(fileTagType); - AZStd::string filePathToSave = destinationFilePath; + AZ::IO::Path filePathToSave = destinationFilePath; if (filePathToSave.empty()) { filePathToSave = FileTagQueryManager::GetDefaultFileTagFilePath(fileTagType); } - if (!AzFramework::StringFunc::EndsWith(filePathToSave, AzFramework::FileTag::FileTagAsset::Extension())) + if (!filePathToSave.Extension().Native().ends_with(AzFramework::FileTag::FileTagAsset::Extension())) { AZ_Error("FileTag", false, "Unable to save tag file (%s). Invalid file extension, file tag can only have (%s) extension.\n", filePathToSave.c_str(), AzFramework::FileTag::FileTagAsset::Extension()); return false; } - return AZ::Utils::SaveObjectToFile(filePathToSave, AZ::DataStream::StreamType::ST_XML, fileTagAsset); + return AZ::Utils::SaveObjectToFile(filePathToSave.Native(), AZ::DataStream::StreamType::ST_XML, fileTagAsset); } AZ::Outcome FileTagManager::AddTagsInternal(AZStd::string filePath, FileTagType fileTagType, AZStd::vector fileTags, AzFramework::FileTag::FilePatternType filePatternType) @@ -239,17 +239,22 @@ namespace AzFramework QueryFileTagsEventBus::Handler::BusDisconnect(); } - AZStd::string FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType) + AZ::IO::Path FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType) { - auto destinationFilePath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / EngineAssetSourceRelPath; + AZ::IO::Path destinationFilePath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(destinationFilePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + } + destinationFilePath /= EngineAssetSourceRelPath; destinationFilePath /= fileTagType == FileTagType::Exclude ? ExcludeFileName : IncludeFileName; destinationFilePath.ReplaceExtension(AzFramework::FileTag::FileTagAsset::Extension()); - return destinationFilePath.String(); + return destinationFilePath; } bool FileTagQueryManager::Load(const AZStd::string& filePath) { - AZStd::string fileToLoad = filePath; + AZ::IO::Path fileToLoad = filePath; if (fileToLoad.empty()) { fileToLoad = GetDefaultFileTagFilePath(m_fileTagType); diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h index be3d6e6d08..d2dee3f629 100644 --- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h +++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace AzFramework @@ -88,7 +89,7 @@ namespace AzFramework ///////////////////////////////////////////////////////////////////////// - static AZStd::string GetDefaultFileTagFilePath(FileTagType fileTagType); + static AZ::IO::Path GetDefaultFileTagFilePath(FileTagType fileTagType); protected: diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp index 1e03a6df0d..1d09500415 100644 --- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp @@ -16,6 +16,7 @@ #include #include +#include #include namespace AzFramework @@ -66,7 +67,8 @@ namespace AzFramework m_excludeFileQueryManager.reset(aznew FileTagQueryManager(FileTagType::Exclude)); if (!m_excludeFileQueryManager.get()->Load()) { - AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n", FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str()); + AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n", + FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str()); } AzFramework::AssetCatalogEventBus::Handler::BusConnect(); diff --git a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp index 8cdc54f34f..9e5c72f74c 100644 --- a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp @@ -308,7 +308,9 @@ namespace UnitTest registry->Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); - m_app->Start(desc); + AZ::ComponentApplication::StartupParameters startupParameters; + startupParameters.m_loadAssetCatalog = false; + m_app->Start(desc, startupParameters); // 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 diff --git a/Code/Legacy/CrySystem/IDebugCallStack.cpp b/Code/Legacy/CrySystem/IDebugCallStack.cpp index deb9c11581..a02658d34a 100644 --- a/Code/Legacy/CrySystem/IDebugCallStack.cpp +++ b/Code/Legacy/CrySystem/IDebugCallStack.cpp @@ -242,7 +242,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...) va_end(ArgList); AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; - AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle); + AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle); if (fileHandle != AZ::IO::InvalidHandle) { AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer)); @@ -254,7 +254,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...) ////////////////////////////////////////////////////////////////////////// void IDebugCallStack::StartMemLog() { - AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle); + AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle); assert(m_memAllocFileHandle != AZ::IO::InvalidHandle); } diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index 6cfe072f80..d0528a9cad 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -81,8 +81,6 @@ namespace AZ // Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus // so that the OnCatalogLoaded event gets processed now, instead of during application shutdown. - AZ::Data::AssetCatalogRequestBus::Broadcast( - &AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml"); application.Tick(); AZStd::string logggingScratchBuffer; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index e8ec77e7bd..ba3bfe4718 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -175,8 +175,6 @@ namespace AtomToolsFramework AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast( &AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized); - AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml"); - if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized()) { AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp index b7ee805b3c..00629eb65d 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp @@ -143,6 +143,7 @@ namespace Blast void BlastSystemComponent::Deactivate() { AZ_PROFILE_FUNCTION(Physics); + AZ::Data::AssetBus::MultiHandler::BusDisconnect(); CrySystemEventBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); BlastSystemRequestBus::Handler::BusDisconnect(); diff --git a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp index c9fc656488..168a3640a8 100644 --- a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp @@ -9,43 +9,40 @@ #include "BundlingSystemComponent.h" #include +#include +#include #include #include +#include #include -#include - -#include -#include - #include + namespace LmbrCentral { const char bundleRoot[] = "@products@"; + // Calls the LoadBundles method + static void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs); + // Calls the UnloadBundles method + static void ConsoleCommandUnloadBundles(const AZ::ConsoleCommandContainer& commandArgs); + + AZ_CONSOLEFREEFUNC("loadbundles", ConsoleCommandLoadBundles, AZ::ConsoleFunctorFlags::Null, "Load Asset Bundles"); + AZ_CONSOLEFREEFUNC("unloadbundles", ConsoleCommandUnloadBundles, AZ::ConsoleFunctorFlags::Null, "Unload Asset Bundles"); + void BundlingSystemComponent::Activate() { BundlingSystemRequestBus::Handler::BusConnect(); - CrySystemEventBus::Handler::BusConnect(); AZ::IO::ArchiveNotificationBus::Handler::BusConnect(); } void BundlingSystemComponent::Deactivate() { AZ::IO::ArchiveNotificationBus::Handler::BusDisconnect(); - CrySystemEventBus::Handler::BusDisconnect(); BundlingSystemRequestBus::Handler::BusDisconnect(); } - void BundlingSystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) - { - AZ_UNUSED(systemInitParams); - - system.GetIConsole()->AddCommand("loadbundles", ConsoleCommandLoadBundles); - system.GetIConsole()->AddCommand("unloadbundles", ConsoleCommandUnloadBundles); - } - void BundlingSystemComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -58,7 +55,7 @@ namespace LmbrCentral AZStd::vector BundlingSystemComponent::GetBundleList(const char* bundlePath, const char* bundleExtension) const { - AZStd::string fileFilter{ AZStd::string::format("*%s",bundleExtension) }; + AZStd::string fileFilter{ AZStd::string::format("*%s", bundleExtension) }; AZStd::vector bundleList; AZ::IO::FileIOBase::GetInstance()->FindFiles(bundlePath, fileFilter.c_str(), [&bundleList](const char* foundPath) -> bool @@ -73,29 +70,28 @@ namespace LmbrCentral return bundleList; } - void BundlingSystemComponent::ConsoleCommandLoadBundles(IConsoleCmdArgs* pCmdArgs) + void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs) { const char defaultBundleFolder[] = "bundles"; const char defaultBundleExtension[] = ".pak"; - const char* bundleFolder = pCmdArgs->GetArgCount() > 1 ? pCmdArgs->GetArg(1) : defaultBundleFolder; - const char* bundleExtension = pCmdArgs->GetArgCount() > 2 ? pCmdArgs->GetArg(2) : defaultBundleExtension; + AZ::CVarFixedString bundleFolder = commandArgs.size() > 0 ? AZ::CVarFixedString(commandArgs[0]) : defaultBundleFolder; + AZ::CVarFixedString bundleExtension = commandArgs.size() > 1 ? AZ::CVarFixedString(commandArgs[1]) : defaultBundleExtension; - BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder, bundleExtension); + BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder.c_str(), bundleExtension.c_str()); } - void BundlingSystemComponent::ConsoleCommandUnloadBundles(IConsoleCmdArgs* pCmdArgs) + void ConsoleCommandUnloadBundles([[maybe_unused]] const AZ::ConsoleCommandContainer& commandArgs) { - AZ_UNUSED(pCmdArgs); BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::UnloadBundles); } void BundlingSystemComponent::UnloadBundles() { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) + auto archive = AZ::Interface::Get(); + if (!archive) { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to unload bundles!"); + AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!"); return; } if (!m_bundleModeBundles.size()) @@ -106,7 +102,7 @@ namespace LmbrCentral AZStd::lock_guard openBundleLock(m_bundleModeMutex); for (const auto& thisBundle : m_bundleModeBundles) { - if (crySystem->GetIPak()->ClosePack(thisBundle.c_str())) + if (archive->ClosePack(thisBundle.c_str())) { AZ_TracePrintf("BundlingSystem", "Unloaded %s\n",thisBundle.c_str()); } @@ -128,15 +124,8 @@ namespace LmbrCentral return; } - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to load bundles!"); - return; - } - - auto cryPak = crySystem->GetIPak(); - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!"); return; @@ -152,8 +141,8 @@ namespace LmbrCentral } } AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath); - if (cryPak->OpenPack(bundleRoot, thisBundle.c_str())) + AZ::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath); + if (archive->OpenPack(bundleRoot, thisBundle.c_str())) { AZ_TracePrintf("BundlingSystem", "Loaded bundle %s\n",bundlePath.c_str()); m_bundleModeBundles.emplace_back(AZStd::move(bundlePath)); @@ -230,28 +219,21 @@ namespace LmbrCentral void BundlingSystemComponent::OpenDependentBundles(const char* bundleName, AZStd::shared_ptr bundleManifest) { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to load dependent bundles for %s", bundleName); - return; - } - - auto cryPak{ crySystem->GetIPak() }; - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load dependent bundles for %s", bundleName); return; } AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(bundleName, folderPath); + AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath); for (const auto& thisBundle : bundleManifest->GetDependentBundleNames()) { AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); + AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); - if (!cryPak->OpenPack(bundleRoot, bundlePath.c_str())) + if (!archive->OpenPack(bundleRoot, bundlePath.c_str())) { // We're not bailing here intentionally - try to open the remaining bundles AZ_Warning("BundlingSystem", false, "Failed to open dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName); @@ -300,28 +282,21 @@ namespace LmbrCentral void BundlingSystemComponent::CloseDependentBundles(const char* bundleName, AZStd::shared_ptr bundleManifest) { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't get ISystem to close dependent bundles for %s", bundleName); - return; - } - - auto cryPak{ crySystem->GetIPak() }; - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't get IArchive to close dependent bundles for %s", bundleName); return; } AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(bundleName, folderPath); + AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath); for (const auto& thisBundle : bundleManifest->GetDependentBundleNames()) { AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); + AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); - if (!cryPak->ClosePack(bundlePath.c_str())) + if (!archive->ClosePack(bundlePath.c_str())) { // We're not bailing here intentionally - try to close the remaining bundles AZ_Warning("BundlingSystem", false, "Failed to close dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName); diff --git a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h index 15fdf1103c..e7c8775530 100644 --- a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h +++ b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h @@ -19,11 +19,8 @@ #include -#include #include -struct IConsoleCmdArgs; - namespace AzFramework { class AssetBundleManifest; @@ -42,10 +39,9 @@ namespace LmbrCentral * System component for managing bundles */ class BundlingSystemComponent - : public AZ::Component, - public BundlingSystemRequestBus::Handler, - public CrySystemEventBus::Handler, - public AZ::IO::ArchiveNotificationBus::Handler + : public AZ::Component + , public BundlingSystemRequestBus::Handler + , public AZ::IO::ArchiveNotificationBus::Handler { public: AZ_COMPONENT(BundlingSystemComponent, "{0FB7153D-EE80-4B1C-9584-134270401AAF}"); @@ -70,13 +66,6 @@ namespace LmbrCentral void BundleOpened(const char* bundleName, AZStd::shared_ptr bundleManifest, const char* nextBundle, AZStd::shared_ptr bundleCatalog) override; void BundleClosed(const char* bundleName) override; - // CrySystemEventBus - void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override; - - // Calls the LoadBundles method - static void ConsoleCommandLoadBundles(IConsoleCmdArgs* pCmdArgs); - // Calls the UnloadBundles method - static void ConsoleCommandUnloadBundles(IConsoleCmdArgs* pCmdArgs); AZStd::vector GetBundleList(const char* bundlePath, const char* bundleExtension) const; diff --git a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp index a5884ccce9..f167be8b84 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp +++ b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp @@ -84,8 +84,6 @@ namespace LmbrCentral { - static const char* s_assetCatalogFilename = "assetcatalog.xml"; - using LmbrCentralAllocatorScope = AZ::AllocatorScope; // This component boots the required allocators for LmbrCentral everywhere but AssetBuilders @@ -354,8 +352,7 @@ namespace LmbrCentral AZ_Assert(AZ::Data::AssetManager::IsReady(), "Asset manager isn't ready!"); // Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService". - auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); - if (assetCatalog) + if (auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); assetCatalog) { assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo::Uuid()); assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo::Uuid()); @@ -376,7 +373,6 @@ namespace LmbrCentral assetCatalog->AddExtension("cax"); } - CrySystemEventBus::Handler::BusConnect(); AZ::Data::AssetManagerNotificationBus::Handler::BusConnect(); @@ -448,7 +444,6 @@ namespace LmbrCentral m_unhandledAssetInfo.clear(); AZ::Data::AssetManagerNotificationBus::Handler::BusDisconnect(); - CrySystemEventBus::Handler::BusDisconnect(); // AssetHandler's destructor calls Unregister() m_assetHandlers.clear(); @@ -459,42 +454,6 @@ namespace LmbrCentral } m_allocatorShutdowns.clear(); } - - void LmbrCentralSystemComponent::OnCrySystemPreInitialize([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& systemInitParams) - { - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, StartMonitoringAssets); - } - - void LmbrCentralSystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) - { -#if !defined(AZ_MONOLITHIC_BUILD) - // When module is linked dynamically, we must set our gEnv pointer. - // When module is linked statically, we'll share the application's gEnv pointer. - gEnv = system.GetGlobalEnvironment(); -#endif - - // Enable catalog now that application's asset root is set. - if (system.GetGlobalEnvironment()->IsEditor()) - { - // In the editor, we build the catalog by scanning the disk. - if (systemInitParams.pUserCallback) - { - systemInitParams.pUserCallback->OnInitProgress("Refreshing asset catalog..."); - } - } - - // load the catalog from disk (supported over VFS). - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, LoadCatalog, AZStd::string::format("@products@/%s", s_assetCatalogFilename).c_str()); - } - - void LmbrCentralSystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system) - { - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, StopMonitoringAssets); - -#if !defined(AZ_MONOLITHIC_BUILD) - gEnv = nullptr; -#endif - } } // namespace LmbrCentral #if !defined(LMBR_CENTRAL_EDITOR) diff --git a/Gems/LmbrCentral/Code/Source/LmbrCentral.h b/Gems/LmbrCentral/Code/Source/LmbrCentral.h index 9a0c327ea7..650061dce6 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentral.h +++ b/Gems/LmbrCentral/Code/Source/LmbrCentral.h @@ -15,8 +15,6 @@ #include #include -#include - /*! * \namespace LmbrCentral * LmbrCentral ties together systems from CryEngine and systems from the AZ framework. @@ -49,7 +47,6 @@ namespace LmbrCentral */ class LmbrCentralSystemComponent : public AZ::Component - , private CrySystemEventBus::Handler , private AZ::Data::AssetManagerNotificationBus::Handler { public: @@ -71,13 +68,6 @@ namespace LmbrCentral void Deactivate() override; //////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////// - // CrySystemEvents - void OnCrySystemPreInitialize(ISystem& system, const SSystemInitParams& systemInitParams) override; - void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override; - void OnCrySystemShutdown(ISystem& system) override; - //////////////////////////////////////////////////////////////////////////// - AZStd::vector > m_assetHandlers; AZStd::vector > m_unhandledAssetInfo; AZStd::vector> m_allocatorShutdowns; diff --git a/Registry/application_lifecycle_events.setreg b/Registry/application_lifecycle_events.setreg new file mode 100644 index 0000000000..58baf267b0 --- /dev/null +++ b/Registry/application_lifecycle_events.setreg @@ -0,0 +1,31 @@ +// The Lifecycle events contains the name of the event as a string +// ComponentApplication derived classes +// will set these these keys to a JSON Object indicate an event has occured +// A callback can be registered with the SettingsRegistry +// to be notified when that key is set +// The JSON object that is set will contain any payload data +// related to the event +{ + "O3DE" : { + "Runtime": { + "Application": { + "LifecycleEvents": { + "SystemComponentsActivated": {}, + "SystemComponentsDeactivated": {}, + "ReflectionManagerAvailable": {}, + "ReflectionManagerUnavailable": {}, + "SystemAllocatorCreated": {}, + "SystemAllocatorPendingDestruction": {}, + "SettingsRegistryAvailable": {}, + "SettingsRegistryUnavailable": {}, + "ConsoleAvailable": {}, + "ConsoleUnavailable": {}, + "GemsLoaded": {}, + "GemsUnloaded": {}, + "FileIOAvailable": {}, + "FileIOUnavailable": {} + } + } + } + } +} From 81acd559d0673a5d76b85c591e1877d2e84032c1 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:52:33 -0500 Subject: [PATCH 10/10] [LYN-7529] Create unit tests for ResolveSourceFileDependencyPath (#4790) * Add unit tests for ResolveSourceFileDependencyPath Fix crash when file is not in scan folder Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Switch to using vector of AZ strings for better error output Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Switch to using UnorderedElementsAre Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add some comments and fix typo Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../AssetManager/assetProcessorManager.cpp | 21 +-- .../AssetProcessorManagerTest.cpp | 139 ++++++++++++++++++ .../assetmanager/AssetProcessorManagerTest.h | 8 + 3 files changed, 158 insertions(+), 10 deletions(-) diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp index fc806f6c9e..5e061491f0 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp @@ -3576,23 +3576,24 @@ namespace AssetProcessor // Absolute path, just check the 1 scan folder if (AZ::IO::PathView(encodedFileData.toUtf8().constData()).IsAbsolute()) { - QString scanFolderName; - if (!m_platformConfig->ConvertToRelativePath(encodedFileData, resultDatabaseSourceName, scanFolderName)) + auto scanFolderInfo = m_platformConfig->GetScanFolderForFile(encodedFileData); + + if (!m_platformConfig->ConvertToRelativePath(encodedFileData, scanFolderInfo, resultDatabaseSourceName)) { AZ_Warning( AssetProcessor::ConsoleChannel, false, "'%s' does not appear to be in any input folder. Use relative paths instead.", sourceDependency.m_sourceFileDependencyPath.c_str()); } + else + { + // Make an absolute path that is ScanFolderPath + Part of search path before the wildcard + QDir rooted(scanFolderInfo->ScanPath()); + QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard); - auto scanFolderInfo = m_platformConfig->GetScanFolderByPath(scanFolderName); - - // Make an absolute path that is ScanFolderPath + Part of search path before the wildcard - QDir rooted(scanFolderName); - QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard); - - resolvedDependencyList.append(m_platformConfig->FindWildcardMatches( - scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders())); + resolvedDependencyList.append(m_platformConfig->FindWildcardMatches( + scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders())); + } } else // Relative path, check every scan folder { diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index da6e995c97..6c19213414 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -88,6 +88,7 @@ public: friend struct DuplicateProductsTest; friend struct DuplicateProcessTest; friend struct AbsolutePathProductDependencyTest; + friend struct WildcardSourceDependencyTest; explicit AssetProcessorManager_Test(PlatformConfiguration* config, QObject* parent = nullptr); ~AssetProcessorManager_Test() override; @@ -5308,3 +5309,141 @@ TEST_F(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase) ASSERT_TRUE(BlockUntilIdle(5000)); ASSERT_EQ(jobDetails.m_jobEntry.m_pathRelativeToWatchFolder, relFileName); } + +bool WildcardSourceDependencyTest::Test( + const AZStd::string& dependencyPath, AZStd::vector& resolvedPaths) +{ + [[maybe_unused]] QString resolvedName; + QStringList stringlistPaths; + AssetBuilderSDK::SourceFileDependency dependency(dependencyPath, AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards); + bool result = m_assetProcessorManager->ResolveSourceFileDependencyPath(dependency, resolvedName, stringlistPaths); + + // Convert to a vector of AZStd::strings because GTest handles this type better when displaying errors + for (const QString& resolvedPath : stringlistPaths) + { + resolvedPaths.emplace_back(resolvedPath.toUtf8().constData()); + } + + return result; +} + +void WildcardSourceDependencyTest::SetUp() +{ + AssetProcessorManagerTest::SetUp(); + + QDir tempPath(m_tempDir.path()); + + // Add a non-recursive scan folder. Only files directly inside of this folder should be picked up, subfolders are ignored + m_config->AddScanFolder(ScanFolderInfo(tempPath.filePath("no_recurse"), "no_recurse", + "no_recurse", false, false, m_config->GetEnabledPlatforms(), 1)); + + UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/1a.foo")); + UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/1b.foo")); + UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/a.foo")); + UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/b.foo")); + UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/folder/one/c.foo")); + UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/folder/one/d.foo")); + + // Add a file that is not in a scanfolder. Should always be ignored + UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("not/a/scanfolder/e.foo")); + + // Add a file in the non-recursive scanfolder. Since its not directly in the scan folder, it should always be ignored + UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("no_recurse/one/two/three/f.foo")); +} + +TEST_F(WildcardSourceDependencyTest, Relative_Broad) +{ + // Expect all files except for the 2 invalid ones (e and f) + AZStd::vector resolvedPaths; + + ASSERT_TRUE(Test("*.foo", resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "b.foo", "folder/one/c.foo", "folder/one/d.foo", "1a.foo", "1b.foo")); +} + +TEST_F(WildcardSourceDependencyTest, Relative_WithFolder) +{ + // Make sure we can filter to files under a folder + AZStd::vector resolvedPaths; + + ASSERT_TRUE(Test("folder/*.foo", resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("folder/one/c.foo", "folder/one/d.foo")); +} + +TEST_F(WildcardSourceDependencyTest, Relative_WildcardPath) +{ + // Make sure the * wildcard works even if the full filename is given + AZStd::vector resolvedPaths; + + ASSERT_TRUE(Test("*a.foo", resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "1a.foo")); +} + +TEST_F(WildcardSourceDependencyTest, Absolute_WithFolder) +{ + // Make sure we can use absolute paths to filter to files under a folder + AZStd::vector resolvedPaths; + QDir tempPath(m_tempDir.path()); + + ASSERT_TRUE(Test(tempPath.absoluteFilePath("subfolder2/redirected/*.foo").toUtf8().constData(), resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "b.foo", "folder/one/c.foo", "folder/one/d.foo")); +} + +TEST_F(WildcardSourceDependencyTest, Absolute_NotInScanfolder) +{ + // Files outside a scanfolder should not be returned even with an absolute path + AZStd::vector resolvedPaths; + QDir tempPath(m_tempDir.path()); + + ASSERT_TRUE(Test(tempPath.absoluteFilePath("not/a/scanfolder/*.foo").toUtf8().constData(), resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre()); +} + +TEST_F(WildcardSourceDependencyTest, Relative_NotInScanfolder) +{ + // Files outside a scanfolder should not be returned + AZStd::vector resolvedPaths; + QDir tempPath(m_tempDir.path()); + + ASSERT_TRUE(Test("*/e.foo", resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre()); +} + +TEST_F(WildcardSourceDependencyTest, Relative_InNonRecursiveScanfolder) +{ + // Files deep inside non-recursive scanfolders should not be returned + AZStd::vector resolvedPaths; + QDir tempPath(m_tempDir.path()); + + ASSERT_TRUE(Test("*/f.foo", resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre()); +} + +TEST_F(WildcardSourceDependencyTest, Absolute_InNonRecursiveScanfolder) +{ + // Absolute paths to files deep inside non-recursive scanfolders should not be returned + AZStd::vector resolvedPaths; + QDir tempPath(m_tempDir.path()); + + ASSERT_TRUE(Test(tempPath.absoluteFilePath("one/two/three/*.foo").toUtf8().constData(), resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre()); +} + +TEST_F(WildcardSourceDependencyTest, Relative_NoWildcard) +{ + // No wildcard results in a failure + AZStd::vector resolvedPaths; + QDir tempPath(m_tempDir.path()); + + ASSERT_FALSE(Test("subfolder1/1a.foo", resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre()); +} + +TEST_F(WildcardSourceDependencyTest, Absolute_NoWildcard) +{ + // No wildcard results in a failure + AZStd::vector resolvedPaths; + QDir tempPath(m_tempDir.path()); + + ASSERT_FALSE(Test(tempPath.absoluteFilePath("subfolder1/1a.foo").toUtf8().constData(), resolvedPaths)); + ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre()); +} diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h index 3443a4c519..94cc997938 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h @@ -131,6 +131,14 @@ struct MultiplatformPathDependencyTest void SetUp() override; }; +struct WildcardSourceDependencyTest + : AssetProcessorManagerTest +{ + bool Test(const AZStd::string& dependencyPath, AZStd::vector& resolvedPaths); + + void SetUp() override; +}; + struct MockBuilderInfoHandler : public AssetProcessor::AssetBuilderInfoBus::Handler {