diff --git a/AutomatedTesting/CMakeLists.txt b/AutomatedTesting/CMakeLists.txt index e239ba7674..9a870b1279 100644 --- a/AutomatedTesting/CMakeLists.txt +++ b/AutomatedTesting/CMakeLists.txt @@ -10,7 +10,7 @@ # if(NOT PROJECT_NAME) - cmake_minimum_required(VERSION 3.19) + cmake_minimum_required(VERSION 3.20) project(AutomatedTesting LANGUAGES C CXX VERSION 1.0.0.0 diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py index 173d658b3c..3f94a23696 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py @@ -49,7 +49,7 @@ class TestViewMenuOptions(EditorTestHelper): view_menu_options = [ ("Center on Selection",), ("Show Quick Access Bar",), - ("Viewport", "Wireframe"), + ("Viewport", "Configure Layout"), ("Viewport", "Go to Position"), ("Viewport", "Center on Selection"), ("Viewport", "Go to Location"), diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py index 2b3fdcbdf3..26231e33d7 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py @@ -89,7 +89,7 @@ class TestMenus(object): expected_lines = [ "Center on Selection Action triggered", "Show Quick Access Bar Action triggered", - "Wireframe Action triggered", + "Configure Layout Action triggered", "Go to Position Action triggered", "Center on Selection Action triggered", "Go to Location Action triggered", diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ca9aeacb8..4a12e0c50a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,17 +9,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# Cmake version 3.19 is the minimum version needed for all of Open 3D Engine's supported platforms -cmake_minimum_required(VERSION 3.19) - -# CMP0111 introduced in 3.19 has a bug that produces the policy to warn every time there is an -# INTERFACE IMPORTED library. We use this type of libraries for handling 3rdParty. The rest of -# the documentation states that INTERFACE IMPORTED libraries do not require to set locations, but -# the policy still warns about it. Issue: https://gitlab.kitware.com/cmake/cmake/-/issues/21470 -# The issue was fixed in 3.19.1 so we just disable the policy for 3.19 -if(CMAKE_VERSION VERSION_EQUAL 3.19) - cmake_policy(SET CMP0111 OLD) -endif() +# Cmake version 3.20 is the minimum version needed for all of Open 3D Engine's supported platforms +cmake_minimum_required(VERSION 3.20) include(cmake/LySet.cmake) include(cmake/Version.cmake) @@ -114,24 +105,25 @@ endforeach() # Post-processing ################################################################################ # The following steps have to be done after all targets are registered: -# Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic -# builds until after all the targets are known -ly_delayed_generate_static_modules_inl() # 1. Add any dependencies registered via ly_enable_gems ly_enable_gems_delayed() -# 2. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls +# 2. Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic +# builds until after all the targets are known and all the gems are enabled +ly_delayed_generate_static_modules_inl() + +# 3. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls # to provide applications with the filenames of gem modules to load # This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIES as MANUALLY_ADDED_DEPENDENCIES # if the build dependency is a MODULE_LIBRARY. That would cause a false load dependency to be generated ly_delayed_generate_settings_registry() -# 3. link targets where the dependency was yet not declared, we need to have the declaration so we do different +# 4. link targets where the dependency was yet not declared, we need to have the declaration so we do different # linking logic depending on the type of target ly_delayed_target_link_libraries() -# 4. generate a registry file for unit testing for platforms that support unit testing +# 5. generate a registry file for unit testing for platforms that support unit testing if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_delayed_generate_unit_test_module_registry() endif() diff --git a/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp b/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp index aa5d638831..708835c0cc 100644 --- a/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp +++ b/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp @@ -321,7 +321,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) { if (AZ::StringFunc::Equal(handle.m_filename.data(), LevelPakName)) { - // level folder contain pak files like 'level.pak' + // level folder contain pak files like 'level.pak' // which we only want to load during level loading. continue; } @@ -352,7 +352,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) PopulateLevels(search, folder, pPak, modFolder, false); // Load levels outside of the bundles to maintain backward compatibility. PopulateLevels(search, folder, pPak, modFolder, true); - + } void CLevelSystem::PopulateLevels( @@ -974,7 +974,7 @@ void CLevelSystem::UnloadLevel() m_lastLevelName.clear(); SAFE_RELEASE(m_pCurrentLevel); - + // Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed). // Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event). EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect); diff --git a/Code/Framework/AzAutoGen/CMakeLists.txt b/Code/Framework/AzAutoGen/CMakeLists.txt index 925e7aed29..4c79f268fe 100644 --- a/Code/Framework/AzAutoGen/CMakeLists.txt +++ b/Code/Framework/AzAutoGen/CMakeLists.txt @@ -9,8 +9,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -cmake_minimum_required(VERSION 3.0) - ly_add_target( NAME AzAutoGen HEADERONLY NAMESPACE AZ diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_simd.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_simd.inl index 770a8fa104..d06372256d 100644 --- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_simd.inl +++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_simd.inl @@ -292,8 +292,13 @@ namespace AZ const typename VecType::FloatType cmp2 = VecType::AndNot(cmp0, cmp1); // -1/x + // this step is calculated for all values of x, but only used if x > Sqrt(2) + 1 + // in order to avoid a division by zero, detect if xabs is zero here and replace it with an arbitrary value + // if xabs does equal zero, the value here doesn't matter because the result will be thrown away + typename VecType::FloatType xabsSafe = + VecType::Add(xabs, VecType::And(VecType::CmpEq(xabs, VecType::ZeroFloat()), FastLoadConstant(Simd::g_vec1111))); const typename VecType::FloatType y0 = VecType::And(cmp0, FastLoadConstant(Simd::g_HalfPi)); - typename VecType::FloatType x0 = VecType::Div(FastLoadConstant(Simd::g_vec1111), xabs); + typename VecType::FloatType x0 = VecType::Div(FastLoadConstant(Simd::g_vec1111), xabsSafe); x0 = VecType::Xor(x0, VecType::CastToFloat(FastLoadConstant(Simd::g_negateMask))); const typename VecType::FloatType y1 = VecType::And(cmp2, FastLoadConstant(Simd::g_QuarterPi)); @@ -368,8 +373,12 @@ namespace AZ typename VecType::FloatType offset = VecType::And(x_lt_0, offset1); + // the result of this part of the computation is thrown away if x equals 0, + // but if x does equal 0, it will cause a division by zero + // so replace zero by an arbitrary value here in that case + typename VecType::FloatType xSafe = VecType::Add(x, VecType::And(x_eq_0, FastLoadConstant(Simd::g_vec1111))); const typename VecType::FloatType atan_mask = VecType::Not(VecType::Or(x_eq_0, y_eq_0)); - const typename VecType::FloatType atan_arg = VecType::Div(y, x); + const typename VecType::FloatType atan_arg = VecType::Div(y, xSafe); typename VecType::FloatType atan_result = VecType::Atan(atan_arg); atan_result = VecType::Add(atan_result, offset); atan_result = VecType::AndNot(pio2_mask, atan_result); diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl index 8f35af258c..63e2dca8fd 100644 --- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl +++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl @@ -471,6 +471,7 @@ namespace AZ AZ_MATH_INLINE Vec2::FloatType Vec2::Reciprocal(FloatArgType value) { + value = Sse::ReplaceFourth(Sse::ReplaceThird(value, 1.0f), 1.0f); return Sse::Reciprocal(value); } @@ -513,6 +514,7 @@ namespace AZ AZ_MATH_INLINE Vec2::FloatType Vec2::SqrtInv(FloatArgType value) { + value = Sse::ReplaceFourth(Sse::ReplaceThird(value, 1.0f), 1.0f); return Sse::SqrtInv(value); } diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl index 78a0d5db66..75ee2ab7c5 100644 --- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl +++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl @@ -507,6 +507,7 @@ namespace AZ AZ_MATH_INLINE Vec3::FloatType Vec3::Reciprocal(FloatArgType value) { + value = Sse::ReplaceFourth(value, 1.0f); return Sse::Reciprocal(value); } @@ -549,6 +550,7 @@ namespace AZ AZ_MATH_INLINE Vec3::FloatType Vec3::SqrtInv(FloatArgType value) { + value = Sse::ReplaceFourth(value, 1.0f); return Sse::SqrtInv(value); } diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp index 7025b3977e..e3647ec2cb 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp @@ -175,4 +175,11 @@ namespace AZ::Utils path /= ".o3de"; return path.Native(); } + + AZ::IO::FixedMaxPathString GetO3deLogsDirectory() + { + AZ::IO::FixedMaxPath path = GetO3deManifestDirectory(); + path /= "Logs"; + return path.Native(); + } } diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.h b/Code/Framework/AzCore/AzCore/Utils/Utils.h index d082a3ebe0..8fb6f05742 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.h +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.h @@ -97,6 +97,9 @@ namespace AZ //! Retrieves the full path where the manifest file lives, i.e. "/.o3de/o3de_manifest.json" AZ::IO::FixedMaxPathString GetEngineManifestPath(); + //! Retrieves the full directory to the O3DE logs directory, i.e. "/.o3de/Logs" + AZ::IO::FixedMaxPathString GetO3deLogsDirectory(); + //! Retrieves the App root path to use on the current platform //! If the optional is not engaged the AppRootPath should be calculated based //! on the location of the bootstrap.cfg file diff --git a/Code/Framework/AzCore/AzCore/std/math.h b/Code/Framework/AzCore/AzCore/std/math.h index f5e2ac7ea7..fe2469eb41 100644 --- a/Code/Framework/AzCore/AzCore/std/math.h +++ b/Code/Framework/AzCore/AzCore/std/math.h @@ -26,6 +26,7 @@ namespace AZStd using std::exp2; using std::floor; using std::fmod; + using std::pow; using std::round; using std::sin; using std::sqrt; diff --git a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp index 4f8da821c1..d80094a071 100644 --- a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp @@ -693,11 +693,11 @@ namespace AzFramework // set the __index so we can read values in case we change the script // after we export the component lua_pushliteral(lua, "__index"); - lua_pushcclosure(lua, &Internal::Properties__Index, 1); + lua_pushcclosure(lua, &Internal::Properties__Index, 0); lua_rawset(lua, -3); lua_pushliteral(lua, "__newindex"); - lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1); + lua_pushcclosure(lua, &Internal::Properties__NewIndex, 0); lua_rawset(lua, -3); } lua_pop(lua, 1); // pop the properties table (or the nil value) @@ -900,11 +900,11 @@ namespace AzFramework // Ensure that this instance of Properties table has the proper __index and __newIndex metamethods. lua_newtable(lua); // This new table will become the Properties instance metatable. Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} lua_pushliteral(lua, "__index"); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index - lua_pushcclosure(lua, &Internal::Properties__Index, 1); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function + lua_pushcclosure(lua, &Internal::Properties__Index, 0); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index} lua_pushliteral(lua, "__newindex"); - lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1); + lua_pushcclosure(lua, &Internal::Properties__NewIndex, 0); lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex} lua_setmetatable(lua, -2); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {Meta{__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex} } diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h index b61956bdf9..d0107bb317 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include #include #include diff --git a/Code/Framework/AzTest/CMakeLists.txt b/Code/Framework/AzTest/CMakeLists.txt index fe5ec2d0ff..d7b8813639 100644 --- a/Code/Framework/AzTest/CMakeLists.txt +++ b/Code/Framework/AzTest/CMakeLists.txt @@ -8,25 +8,27 @@ # remove or modify any license notices. This file is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # - -ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/AzTest/Platform/${PAL_PLATFORM_NAME}) -ly_add_target( - NAME AzTest STATIC - NAMESPACE AZ - FILES_CMAKE - AzTest/aztest_files.cmake - ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake - INCLUDE_DIRECTORIES - PUBLIC - . - ${pal_dir} - BUILD_DEPENDENCIES - PUBLIC - 3rdParty::googletest::GMock - 3rdParty::googletest::GTest - 3rdParty::GoogleBenchmark - AZ::AzCore - PLATFORM_INCLUDE_FILES +if(NOT LY_MONOLITHIC_GAME) + ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/AzTest/Platform/${PAL_PLATFORM_NAME}) + + ly_add_target( + NAME AzTest STATIC + NAMESPACE AZ + FILES_CMAKE + AzTest/aztest_files.cmake + ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + . + ${pal_dir} + BUILD_DEPENDENCIES + PUBLIC + 3rdParty::googletest::GMock + 3rdParty::googletest::GTest + 3rdParty::GoogleBenchmark + AZ::AzCore + PLATFORM_INCLUDE_FILES ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake -) + ) +endif() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index 9c60b6cd35..adb4ae66c4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -12,6 +12,7 @@ #include #include #include +#include AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include @@ -22,6 +23,9 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") #include AZ_POP_DISABLE_WARNING +AZ_CVAR( + bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, + "Use the new AssetBrowser TableView for searching assets."); namespace AzToolsFramework { namespace AssetBrowser @@ -31,7 +35,11 @@ namespace AzToolsFramework AssetBrowserFilterModel::AssetBrowserFilterModel(QObject* parent) : QSortFilterProxyModel(parent) { - m_showColumn.insert(AssetBrowserModel::m_column); + m_showColumn.insert(aznumeric_cast(AssetBrowserEntry::Column::DisplayName)); + if (ed_useNewAssetBrowserTableView) + { + m_showColumn.insert(aznumeric_cast(AssetBrowserEntry::Column::Path)); + } m_collator.setNumericMode(true); AssetBrowserComponentNotificationBus::Handler::BusConnect(); } @@ -128,28 +136,58 @@ namespace AzToolsFramework auto compFilter = qobject_cast >(m_filter); if (compFilter) { - auto& subFilters = compFilter->GetSubFilters(); - auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool + const auto& subFilters = compFilter->GetSubFilters(); + + const auto compositeFilterIterator = AZStd::find_if(subFilters.cbegin(), subFilters.cend(), [subFilters](FilterConstType filter) -> bool { - auto assetTypeFilter = qobject_cast >(filter); + const auto assetTypeFilter = qobject_cast >(filter); return !assetTypeFilter.isNull(); }); - if (it != subFilters.end()) + + if (compositeFilterIterator != subFilters.end()) { - m_assetTypeFilter = qobject_cast >(*it); + m_assetTypeFilter = qobject_cast >(*compositeFilterIterator); } - it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool + + const auto compStringFilterIter = AZStd::find_if(subFilters.cbegin(), subFilters.cend(), [](FilterConstType filter) -> bool { - auto stringFilter = qobject_cast >(filter); - return !stringFilter.isNull(); + //The real StringFilter is really a CompositeFilter with just one StringFilter in its subfilter list + //To know if it is actually a StringFilter we have to get that subfilter and check if it is a Stringfilter. + const auto stringCompositeFilter = qobject_cast >(filter); + bool isStringFilter = false; + if (stringCompositeFilter) + { + const auto& stringSubfilters = stringCompositeFilter->GetSubFilters(); + auto canBeCasted = [](FilterConstType filt) -> bool + { + auto strFilter = qobject_cast>(filt); + return !strFilter.isNull(); + }; + const auto stringSubfliterConstIter = AZStd::find_if(stringSubfilters.cbegin(), stringSubfilters.cend(), canBeCasted); + + //A Composite StringFilter will only have just one subfilter and nothing more. + if (stringSubfliterConstIter != stringSubfilters.end() && stringSubfilters.size() == 1) + { + isStringFilter = true; + } + } + + return isStringFilter; }); - if (it != subFilters.end()) + if (compStringFilterIter != subFilters.end()) { - m_stringFilter = qobject_cast >(*it); + const auto compStringFilter = qobject_cast>(*compStringFilterIter); + + if (!compStringFilter->GetSubFilters().isEmpty() && compStringFilter->GetSubFilters()[0]) + { + m_stringFilter = qobject_cast>(compStringFilter->GetSubFilters()[0]); + } } + } invalidateFilter(); Q_EMIT filterChanged(); + emit stringFilterPopulated(!m_stringFilter.isNull()); } void AssetBrowserFilterModel::filterUpdatedSlot() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h index f9423b2534..5d22791699 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h @@ -45,15 +45,15 @@ namespace AzToolsFramework //asset type filtering void SetFilter(FilterConstType filter); void FilterUpdatedSlotImmediate(); - + const FilterConstType& GetFilter() const { return m_filter; } ////////////////////////////////////////////////////////////////////////// // AssetBrowserComponentNotificationBus ////////////////////////////////////////////////////////////////////////// void OnAssetBrowserComponentReady() override; Q_SIGNALS: + void stringFilterPopulated(bool); void filterChanged(); - ////////////////////////////////////////////////////////////////////////// //QSortFilterProxyModel protected: @@ -68,7 +68,7 @@ namespace AzToolsFramework protected: //set for filtering columns //if the column is in the set the column is not filtered and is shown - AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; + AZStd::fixed_unordered_set(AssetBrowserEntry::Column::Count)> m_showColumn; bool m_alreadyRecomputingFilters = false; //asset source name match filter FilterConstType m_filter; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp index d7c0164435..c0f78baccb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.cpp @@ -27,8 +27,6 @@ namespace AzToolsFramework { namespace AssetBrowser { - const int AssetBrowserModel::m_column = static_cast(AssetBrowserEntry::Column::DisplayName); - AssetBrowserModel::AssetBrowserModel(QObject* parent) : QAbstractItemModel(parent) , m_rootEntry(nullptr) @@ -143,8 +141,9 @@ namespace AzToolsFramework if (parent.isValid()) { - if ((parent.column() != static_cast(AssetBrowserEntry::Column::DisplayName)) && - (parent.column() != static_cast(AssetBrowserEntry::Column::Name))) + if ((parent.column() != aznumeric_cast(AssetBrowserEntry::Column::DisplayName)) && + (parent.column() != aznumeric_cast(AssetBrowserEntry::Column::Name)) && + (parent.column() != aznumeric_cast(AssetBrowserEntry::Column::Path))) { return 0; } @@ -164,7 +163,7 @@ namespace AzToolsFramework int AssetBrowserModel::columnCount(const QModelIndex& /*parent*/) const { - return static_cast(AssetBrowserEntry::Column::Count); + return aznumeric_cast(AssetBrowserEntry::Column::Count); } QVariant AssetBrowserModel::data(const QModelIndex& index, int role) const @@ -393,7 +392,7 @@ namespace AzToolsFramework } int row = entry->row(); - index = createIndex(row, m_column, entry); + index = createIndex(row, aznumeric_cast(AssetBrowserEntry::Column::DisplayName), entry); return true; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.h index 9e60c44dae..3c4cae5588 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserModel.h @@ -91,8 +91,6 @@ namespace AzToolsFramework static void SourceIndexesToAssetIds(const QModelIndexList& indexes, AZStd::vector& assetIds); static void SourceIndexesToAssetDatabaseEntries(const QModelIndexList& indexes, AZStd::vector& entries); - const static int m_column; - private: AZStd::shared_ptr m_rootEntry; bool m_loaded; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp new file mode 100644 index 0000000000..d22486d481 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -0,0 +1,136 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#include +#include +#include + +namespace AzToolsFramework +{ + namespace AssetBrowser + { + AssetBrowserTableModel::AssetBrowserTableModel(QObject* parent /* = nullptr */) + : QSortFilterProxyModel(parent) + { + setDynamicSortFilter(false); + } + + void AssetBrowserTableModel::setSourceModel(QAbstractItemModel* sourceModel) + { + m_filterModel = qobject_cast(sourceModel); + AZ_Assert( + m_filterModel, + "Error in AssetBrowserTableModel initialization, class expects source model to be an AssetBrowserFilterModel."); + QSortFilterProxyModel::setSourceModel(sourceModel); + } + + QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const + { + Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() == this); + if (!proxyIndex.isValid()) + { + return QModelIndex(); + } + return m_indexMap[proxyIndex.row()]; + } + + QVariant AssetBrowserTableModel::headerData(int section, Qt::Orientation orientation, int role) const + { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + { + return tr(AssetBrowserEntry::m_columnNames[section]); + } + return QSortFilterProxyModel::headerData(section, orientation, role); + } + + QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const + { + auto sourceIndex = mapToSource(index); + if (!sourceIndex.isValid()) + { + return QVariant(); + } + + AssetBrowserEntry* entry = GetAssetEntry(sourceIndex); + if (entry == nullptr) + { + AZ_Assert(false, "AssetBrowserTableModel - QModelIndex does not reference an AssetEntry. Source model is not valid."); + return QVariant(); + } + + return sourceIndex.data(role); + } + + QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const + { + return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer()); + } + + int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const + { + return !parent.isValid() ? m_indexMap.size() : 0; + } + + int AssetBrowserTableModel::BuildTableModelMap( + const QAbstractItemModel* model, const QModelIndex& parent /*= QModelIndex()*/, int row /*= 0*/) + { + int rows = model ? model->rowCount(parent) : 0; + for (int i = 0; i < rows; ++i) + { + QModelIndex index = model->index(i, 0, parent); + AssetBrowserEntry* entry = GetAssetEntry(m_filterModel->mapToSource(index)); + //We only wanna see the source assets. + if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source) + { + beginInsertRows(parent, row, row); + m_indexMap[row] = index; + endInsertRows(); + + Q_EMIT dataChanged(index, index); + ++row; + } + + if (model->hasChildren(index)) + { + row = BuildTableModelMap(model, index, row); + } + } + return row; + } + + AssetBrowserEntry* AssetBrowserTableModel::GetAssetEntry(QModelIndex index) const + { + if (index.isValid()) + { + return static_cast(index.internalPointer()); + } + else + { + AZ_Error("AssetBrowser", false, "Invalid Source Index provided to GetAssetEntry."); + return nullptr; + } + } + + void AssetBrowserTableModel::UpdateTableModelMaps() + { + emit layoutAboutToBeChanged(); + if (!m_indexMap.isEmpty()) + { + beginRemoveRows(m_indexMap.first(), m_indexMap.first().row(), m_indexMap.last().row()); + m_indexMap.clear(); + endRemoveRows(); + } + BuildTableModelMap(sourceModel()); + emit layoutChanged(); + } + } // namespace AssetBrowser +} // namespace AzToolsFramework +#include "AssetBrowser/moc_AssetBrowserTableModel.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h new file mode 100644 index 0000000000..e438bf6271 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -0,0 +1,59 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#endif + +namespace AzToolsFramework +{ + namespace AssetBrowser + { + class AssetBrowserFilterModel; + class AssetBrowserEntry; + + class AssetBrowserTableModel + : public QSortFilterProxyModel + { + Q_OBJECT + + public: + AZ_CLASS_ALLOCATOR(AssetBrowserTableModel, AZ::SystemAllocator, 0); + explicit AssetBrowserTableModel(QObject* parent = nullptr); + //////////////////////////////////////////////////////////////////// + // QSortFilterProxyModel + void setSourceModel(QAbstractItemModel* sourceModel) override; + QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + public Q_SLOTS: + void UpdateTableModelMaps(); + + protected: + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; + //////////////////////////////////////////////////////////////////// + + private: + AssetBrowserEntry* GetAssetEntry(QModelIndex index) const; + int BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); + + private: + QPointer m_filterModel; + QMap m_indexMap; + }; + } // namespace AssetBrowser +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.cpp index abd1f91bf5..8ee8f44137 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.cpp @@ -46,6 +46,7 @@ namespace AzToolsFramework const char* AssetBrowserEntry::m_columnNames[] = { "Name", + "Path", "Source ID", "Fingerprint", "Guid", @@ -128,6 +129,8 @@ namespace AzToolsFramework return QString::fromUtf8(m_name.c_str()); case Column::DisplayName: return m_displayName; + case Column::Path: + return m_displayPath; default: return QVariant(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h index 6f0eceeeea..ac5470a136 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h @@ -68,6 +68,7 @@ namespace AzToolsFramework enum class Column { Name, + Path, SourceID, Fingerprint, Guid, @@ -135,6 +136,7 @@ namespace AzToolsFramework protected: AZStd::string m_name; QString m_displayName; + QString m_displayPath; AZStd::string m_relativePath; AZStd::string m_fullPath; AZStd::vector m_children; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.cpp index 88b26ad5ee..0312edbc51 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.cpp @@ -43,6 +43,7 @@ namespace AzToolsFramework void FolderAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const { child->m_relativePath = m_relativePath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name; + child->m_displayPath = QString::fromUtf8(child->m_relativePath.c_str()); child->m_fullPath = m_fullPath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name; AssetBrowserEntry::UpdateChildPaths(child); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp index bd5ba48d8d..e626f31b62 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.cpp @@ -286,6 +286,9 @@ namespace AzToolsFramework product->m_assetType = productWithUuidDatabaseEntry.second.m_assetType; product->m_assetType.ToString(product->m_assetTypeString); AZ::Data::AssetCatalogRequestBus::BroadcastResult(product->m_relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, assetId); + QString displayPath = QString::fromUtf8(product->m_relativePath.c_str()); + displayPath.remove(QString(AZ_CORRECT_DATABASE_SEPARATOR + QString::fromUtf8(product->m_name.c_str()))); + product->m_displayPath = displayPath; EntryCache::GetInstance()->m_productAssetIdMap[assetId] = product; if (needsAdd) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp new file mode 100644 index 0000000000..894bbd8700 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -0,0 +1,178 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +AZ_PUSH_DISABLE_WARNING( + 4244 4251 4800, "-Wunknown-warning-option") // conversion from 'int' to 'float', possible loss of data, needs to have dll-interface to + // be used by clients of class 'QFlags::Int': forcing value to bool + // 'true' or 'false' (performance warning) +#include +#include +#include +#include +#include +#include +#include +AZ_POP_DISABLE_WARNING +namespace AzToolsFramework +{ + namespace AssetBrowser + { + AssetBrowserTableView::AssetBrowserTableView(QWidget* parent) + : QTableView(parent) + , m_delegate(new EntryDelegate(this)) + { + setSortingEnabled(true); + setItemDelegate(m_delegate); + verticalHeader()->hide(); + setContextMenuPolicy(Qt::CustomContextMenu); + + setMouseTracking(true); + setSortingEnabled(false); + setSelectionMode(QAbstractItemView::SingleSelection); + + connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); + + AssetBrowserViewRequestBus::Handler::BusConnect(); + AssetBrowserComponentNotificationBus::Handler::BusConnect(); + } + + AssetBrowserTableView::~AssetBrowserTableView() + { + AssetBrowserViewRequestBus::Handler::BusDisconnect(); + AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); + } + + void AssetBrowserTableView::setModel(QAbstractItemModel* model) + { + m_tableModel = qobject_cast(model); + AZ_Assert(m_tableModel, "Expecting AssetBrowserTableModel"); + m_sourceFilterModel = qobject_cast(m_tableModel->sourceModel()); + QTableView::setModel(model); + connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot); + + horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); + horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch); + } + + void AssetBrowserTableView::SetName(const QString& name) + { + m_name = name; + bool isAssetBrowserComponentReady = false; + AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady); + if (isAssetBrowserComponentReady) + { + OnAssetBrowserComponentReady(); + } + } + + AZStd::vector AssetBrowserTableView::GetSelectedAssets() const + { + QModelIndexList sourceIndexes; + for (const auto& index : selectedIndexes()) + { + if (index.column() == 0) + { + sourceIndexes.push_back(m_sourceFilterModel->mapToSource(m_tableModel->mapToSource(index))); + } + } + + AZStd::vector entries; + AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(sourceIndexes, entries); + return entries; + } + + void AssetBrowserTableView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) + { + QTableView::selectionChanged(selected, deselected); + Q_EMIT selectionChangedSignal(selected, deselected); + } + + void AssetBrowserTableView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // if selected entry is being removed, clear selection so not to select (and attempt to preview) other entries potentially + // marked for deletion + if (selectionModel() && selectionModel()->selectedIndexes().size() == 1) + { + QModelIndex selectedIndex = selectionModel()->selectedIndexes().first(); + QModelIndex parentSelectedIndex = selectedIndex.parent(); + if (parentSelectedIndex == parent && selectedIndex.row() >= start && selectedIndex.row() <= end) + { + selectionModel()->clear(); + } + } + QTableView::rowsAboutToBeRemoved(parent, start, end); + } + + void AssetBrowserTableView::layoutChangedSlot( + [[maybe_unused]] const QList& parents, [[maybe_unused]] QAbstractItemModel::LayoutChangeHint hint) + { + scrollToTop(); + } + + void AssetBrowserTableView::SelectProduct([[maybe_unused]] AZ::Data::AssetId assetID) + { + } + + void AssetBrowserTableView::SelectFileAtPath([[maybe_unused]] const AZStd::string& assetPath) + { + } + + void AssetBrowserTableView::ClearFilter() + { + emit ClearStringFilter(); + emit ClearTypeFilter(); + m_sourceFilterModel->FilterUpdatedSlotImmediate(); + } + + void AssetBrowserTableView::Update() + { + update(); + } + + void AssetBrowserTableView::OnAssetBrowserComponentReady() + { + } + + void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point) + { + const auto& selectedAssets = GetSelectedAssets(); + if (selectedAssets.size() != 1) + { + return; + } + + QMenu menu(this); + AssetBrowserInteractionNotificationBus::Broadcast( + &AssetBrowserInteractionNotificationBus::Events::AddContextMenuActions, this, &menu, selectedAssets); + if (!menu.isEmpty()) + { + menu.exec(QCursor::pos()); + } + } + } // namespace AssetBrowser +} // namespace AzToolsFramework +#include "AssetBrowser/Views/moc_AssetBrowserTableView.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h new file mode 100644 index 0000000000..15bc95f13c --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -0,0 +1,84 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once +#if !defined(Q_MOC_RUN) +#include +#include +#include + +#include +#include + +#include +#include +#include +#endif + +namespace AzToolsFramework +{ + namespace AssetBrowser + { + class AssetBrowserEntry; + class AssetBrowserTableModel; + class AssetBrowserFilterModel; + class EntryDelegate; + + class AssetBrowserTableView //! Table view that displays the asset browser entries in a list. + : public QTableView + , public AssetBrowserViewRequestBus::Handler + , public AssetBrowserComponentNotificationBus::Handler + { + Q_OBJECT + public: + explicit AssetBrowserTableView(QWidget* parent = nullptr); + ~AssetBrowserTableView() override; + + void setModel(QAbstractItemModel *model) override; + void SetName(const QString& name); + + AZStd::vector GetSelectedAssets() const; + + ////////////////////////////////////////////////////////////////////////// + // AssetBrowserViewRequestBus + virtual void SelectProduct(AZ::Data::AssetId assetID) override; + virtual void SelectFileAtPath(const AZStd::string& assetPath) override; + virtual void ClearFilter() override; + virtual void Update() override; + + ////////////////////////////////////////////////////////////////////////// + // AssetBrowserComponentNotificationBus + void OnAssetBrowserComponentReady() override; + ////////////////////////////////////////////////////////////////////////// + + + Q_SIGNALS: + void selectionChangedSignal(const QItemSelection& selected, const QItemSelection& deselected); + void ClearStringFilter(); + void ClearTypeFilter(); + + protected Q_SLOTS: + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; + void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; + void layoutChangedSlot(const QList &parents = QList(), + QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); + + private: + QString m_name; + QPointer m_tableModel = nullptr; + QPointer m_sourceFilterModel = nullptr; + EntryDelegate* m_delegate = nullptr; + + private Q_SLOTS: + void OnContextMenu(const QPoint& point); + }; + } // namespace AssetBrowser +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index 6cd60b0015..026343c2a4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -53,6 +53,7 @@ namespace AzToolsFramework setSortingEnabled(true); setItemDelegate(m_delegate); header()->hide(); + setContextMenuPolicy(Qt::CustomContextMenu); setMouseTracking(true); @@ -99,8 +100,9 @@ namespace AzToolsFramework AZStd::vector AssetBrowserTreeView::GetSelectedAssets() const { + const QModelIndexList& selectedIndexes = selectionModel()->selectedRows(); QModelIndexList sourceIndexes; - for (const auto& index : selectedIndexes()) + for (const auto& index : selectedIndexes) { sourceIndexes.push_back(m_assetBrowserSortFilterProxyModel->mapToSource(index)); } @@ -172,6 +174,7 @@ namespace AzToolsFramework void AssetBrowserTreeView::OnAssetBrowserComponentReady() { + hideColumn(aznumeric_cast(AssetBrowserEntry::Column::Path)); if (!m_name.isEmpty()) { auto crc = AZ::Crc32(m_name.toUtf8().data()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index e8abaebd6f..d0ac05020d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -74,34 +74,34 @@ namespace AzToolsFramework QPoint iconTopLeft(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); auto sourceEntry = azrtti_cast(entry); - - int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey()); - QPalette actualPalette(option.palette); - - if (sourceEntry) + if (index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)) { - if (m_showSourceControl) + int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey()); + if (sourceEntry) { - DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey()); - } - // sources with no children should be greyed out. - if (sourceEntry->GetChildCount() == 0) - { - isEnabled = false; // draw in disabled style. - actualPalette.setCurrentColorGroup(QPalette::Disabled); + if (m_showSourceControl) + { + DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey()); + } + // sources with no children should be greyed out. + if (sourceEntry->GetChildCount() == 0) + { + isEnabled = false; // draw in disabled style. + actualPalette.setCurrentColorGroup(QPalette::Disabled); + } } + + remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail + remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing. } + QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) + ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) + : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path))); - remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail - remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing. - - style->drawItemText(painter, - remainingRect, - option.displayAlignment, - actualPalette, - isEnabled, - entry->GetDisplayName(), + style->drawItemText( + painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, + displayString, isSelected ? QPalette::HighlightedText : QPalette::Text); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index fa7d5f6e9b..336b56653b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -159,12 +159,23 @@ namespace AzToolsFramework void PrefabEditorEntityOwnershipService::GetNonPrefabEntities(EntityList& entities) { - m_rootInstance->GetEntities(entities, false); + m_rootInstance->GetEntities( + [&entities](const AZStd::unique_ptr& entity) + { + entities.emplace_back(entity.get()); + return true; + }); } bool PrefabEditorEntityOwnershipService::GetAllEntities(EntityList& entities) { - m_rootInstance->GetEntities(entities, true); + m_rootInstance->GetAllEntitiesInHierarchy( + [&entities](const AZStd::unique_ptr& entity) + { + entities.emplace_back(entity.get()); + return true; + }); + return true; } @@ -252,13 +263,20 @@ namespace AzToolsFramework } AZStd::string out; - if (m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out)) + + if (!m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out)) { - const size_t bytesToWrite = out.size(); - const size_t bytesWritten = stream.Write(bytesToWrite, out.data()); - return bytesWritten == bytesToWrite; + return false; } - return false; + + const size_t bytesToWrite = out.size(); + const size_t bytesWritten = stream.Write(bytesToWrite, out.data()); + if(bytesWritten != bytesToWrite) + { + return false; + } + m_prefabSystemComponent->SetTemplateDirtyFlag(templateId, false); + return true; } void PrefabEditorEntityOwnershipService::CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) @@ -544,7 +562,7 @@ namespace AzToolsFramework return; } - m_rootInstance->GetNestedEntities([this](AZStd::unique_ptr& entity) + m_rootInstance->GetAllEntitiesInHierarchy([this](AZStd::unique_ptr& entity) { AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor."); if (entity->GetState() == AZ::Entity::State::Active) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp index 427bb9e9d1..27d8f7e5a3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -95,16 +96,34 @@ namespace AzToolsFramework return axis * snapAdjustment.m_nextSnapDistance; } + AZ::Vector3 CalculateSnappedOffset( + const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, const size_t snapAxesCount, const float size) + { + return AZStd::accumulate( + snapAxes, snapAxes + snapAxesCount, AZ::Vector3::CreateZero(), + [&unsnappedPosition, size](AZ::Vector3 acc, const AZ::Vector3& snapAxis) + { + acc += CalculateSnappedOffset(unsnappedPosition, snapAxis, size); + return acc; + }); + } + + AZ::Vector3 CalculateSnappedPosition( + const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, const size_t snapAxesCount, const float size) + { + return unsnappedPosition + CalculateSnappedOffset(unsnappedPosition, snapAxes, snapAxesCount, size); + } + AZ::Vector3 CalculateSnappedTerrainPosition( - const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float gridSize) + const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float size) { const AZ::Transform localFromWorld = worldFromLocal.GetInverse(); const AZ::Vector3 localSurfacePosition = localFromWorld.TransformPoint(worldSurfacePosition); // snap in xy plane AZ::Vector3 localSnappedSurfacePosition = localSurfacePosition + - CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisX(), gridSize) + - CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), gridSize); + CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisX(), size) + + CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), size); // find terrain height at xy snapped location float terrainHeight = 0.0f; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h index f2fa104d4c..9024625fae 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h @@ -12,6 +12,7 @@ #pragma once +#include #include #include @@ -58,10 +59,18 @@ namespace AzToolsFramework //! @note A movement of more than half size (in either direction) will cause a snap by size. AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); + //! Overload of CalculateSnappedOffset taking multiple axes. + AZ::Vector3 CalculateSnappedOffset( + const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, size_t snapAxesCount, float size); + + //! Return the final snapped position according to size (unsnappedPosition + CalculateSnappedOffset). + AZ::Vector3 CalculateSnappedPosition( + const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, size_t snapAxesCount, float size); + //! For a given point on the terrain, calculate the closest xy position snapped to the grid //! (z position is aligned to terrain height, not snapped to z grid) AZ::Vector3 CalculateSnappedTerrainPosition( - const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float gridSize); + const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float size); //! Wrapper for grid snapping and grid size bus calls. GridSnapParameters GridSnapSettings(int viewportId); @@ -84,8 +93,8 @@ namespace AzToolsFramework //! @param exponent Precision to use when rounding. inline float Round(const float value, const float exponent) { - const float precision = std::pow(10.0f, exponent); - return roundf(value * precision) / precision; + const float precision = AZStd::pow(10.0f, exponent); + return AZStd::round(value * precision) / precision; } //! Round to 3 significant digits (3 digits common usage). @@ -116,7 +125,7 @@ namespace AzToolsFramework //! when dealing with values far from the origin. inline AZ::Vector3 NonUniformScaleReciprocal(const AZ::Vector3& nonUniformScale) { - AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal(); + const AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal(); return AZ::Vector3(Round3(scaleReciprocal.GetX()), Round3(scaleReciprocal.GetY()), Round3(scaleReciprocal.GetZ())); } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp index 83a76aeb01..e5179f4229 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp @@ -373,17 +373,25 @@ namespace AzToolsFramework } } - void Instance::GetConstNestedEntities(const AZStd::function& callback) + bool Instance::GetEntities_Impl(const AZStd::function&)>& callback) { - GetConstEntities(callback); - - for (const auto& [instanceAlias, instance] : m_nestedInstances) + for (auto& [entityAlias, entity] : m_entities) { - instance->GetConstNestedEntities(callback); + if (!entity) + { + continue; + } + + if (!callback(entity)) + { + return false; + } } + + return true; } - void Instance::GetConstEntities(const AZStd::function& callback) + bool Instance::GetConstEntities_Impl(const AZStd::function& callback) const { for (const auto& [entityAlias, entity] : m_entities) { @@ -394,19 +402,83 @@ namespace AzToolsFramework if (!callback(*entity)) { - break; + return false; } } + + return true; } - void Instance::GetNestedEntities(const AZStd::function&)>& callback) + bool Instance::GetAllEntitiesInHierarchy_Impl(const AZStd::function&)>& callback) { - GetEntities(callback); + if (HasContainerEntity()) + { + if (!callback(m_containerEntity)) + { + return false; + } + } + + if (!GetEntities_Impl(callback)) + { + return false; + } for (auto& [instanceAlias, instance] : m_nestedInstances) { - instance->GetNestedEntities(callback); + if (!instance->GetAllEntitiesInHierarchy_Impl(callback)) + { + return false; + } } + + return true; + } + + bool Instance::GetAllEntitiesInHierarchyConst_Impl(const AZStd::function& callback) const + { + if (HasContainerEntity()) + { + if (!callback(*m_containerEntity)) + { + return false; + } + } + + if (!GetConstEntities_Impl(callback)) + { + return false; + } + + for (const auto& [instanceAlias, instance] : m_nestedInstances) + { + if (!instance->GetAllEntitiesInHierarchyConst_Impl(callback)) + { + return false; + } + } + + return true; + } + + void Instance::GetEntities(const AZStd::function&)>& callback) + { + GetEntities_Impl(callback); + } + + void Instance::GetConstEntities(const AZStd::function& callback) const + { + GetConstEntities_Impl(callback); + } + + void Instance::GetAllEntitiesInHierarchy(const AZStd::function&)>& callback) + { + GetAllEntitiesInHierarchy_Impl(callback); + } + + void Instance::GetAllEntitiesInHierarchyConst(const AZStd::function& callback) const + { + GetAllEntitiesInHierarchyConst_Impl(callback); } void Instance::GetNestedInstances(const AZStd::function&)>& callback) @@ -417,44 +489,6 @@ namespace AzToolsFramework } } - void Instance::GetEntities(const AZStd::function&)>& callback) - { - for (auto& [entityAlias, entity] : m_entities) - { - if (!callback(entity)) - { - break; - } - } - } - - void Instance::GetEntities(EntityList& entities, bool includeNestedEntities) - { - // Non-recursive traversal of instances - AZStd::vector instancesToTraverse = { this }; - while (!instancesToTraverse.empty()) - { - Instance* currentInstance = instancesToTraverse.back(); - instancesToTraverse.pop_back(); - if (includeNestedEntities) - { - instancesToTraverse.reserve(instancesToTraverse.size() + currentInstance->m_nestedInstances.size()); - for (const auto& instanceByAlias : currentInstance->m_nestedInstances) - { - instancesToTraverse.push_back(instanceByAlias.second.get()); - } - } - - // Size increases by 1 for each instance because we have to count the container entity also. - entities.reserve(entities.size() + currentInstance->m_entities.size() + 1); - entities.push_back(m_containerEntity.get()); - for (const auto& entityByAlias : currentInstance->m_entities) - { - entities.push_back(entityByAlias.second.get()); - } - } - } - EntityAliasOptionalReference Instance::GetEntityAlias(const AZ::EntityId& id) { if (m_instanceToTemplateEntityIdMap.count(id)) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h index 5f36ac10dc..9d3ae31796 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h @@ -121,10 +121,10 @@ namespace AzToolsFramework /** * Gets the entities in the Instance DOM. Can recursively trace all nested instances. */ - void GetConstNestedEntities(const AZStd::function& callback); - void GetConstEntities(const AZStd::function& callback); - void GetNestedEntities(const AZStd::function&)>& callback); void GetEntities(const AZStd::function&)>& callback); + void GetConstEntities(const AZStd::function& callback) const; + void GetAllEntitiesInHierarchy(const AZStd::function&)>& callback); + void GetAllEntitiesInHierarchyConst(const AZStd::function& callback) const; void GetNestedInstances(const AZStd::function&)>& callback); /** @@ -184,12 +184,6 @@ namespace AzToolsFramework static InstanceAlias GenerateInstanceAlias(); - protected: - /** - * Gets the entities owned by this instance - */ - void GetEntities(EntityList& entities, bool includeNestedEntities = false); - private: static constexpr const char s_aliasPathSeparator = '/'; @@ -197,6 +191,11 @@ namespace AzToolsFramework void RemoveEntities(const AZStd::function&)>& filter); + bool GetEntities_Impl(const AZStd::function&)>& callback); + bool GetConstEntities_Impl(const AZStd::function& callback) const; + bool GetAllEntitiesInHierarchy_Impl(const AZStd::function&)>& callback); + bool GetAllEntitiesInHierarchyConst_Impl(const AZStd::function& callback) const; + bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias); AZStd::unique_ptr DetachEntity(const EntityAlias& entityAlias); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp index 6480ac37d7..cc04fe24c1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp @@ -62,25 +62,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils } } - AZStd::vector EditorInfoRemover::GetEntitiesFromInstance(AZStd::unique_ptr& instance) + void EditorInfoRemover::GetEntitiesFromInstance( + AZStd::unique_ptr& instance, EntityList& hierarchyEntities) { - AZStd::vector result; - - instance->GetNestedEntities( - [&result](const AZStd::unique_ptr& entity) + instance->GetAllEntitiesInHierarchy( + [&hierarchyEntities](const AZStd::unique_ptr& entity) { - result.emplace_back(entity.get()); + hierarchyEntities.emplace_back(entity.get()); return true; } ); - - if (instance->HasContainerEntity()) - { - auto containerEntityReference = instance->GetContainerEntity(); - result.emplace_back(&containerEntityReference->get()); - } - - return result; } void EditorInfoRemover::SetEditorOnlyEntityHandlerFromCandidates(const EntityList& entities) @@ -543,7 +534,9 @@ exportComponent, prefabProcessorContext); } // grab all nested entities from the Instance as source entities. - EntityList sourceEntities = GetEntitiesFromInstance(instance); + EntityList sourceEntities; + GetEntitiesFromInstance(instance, sourceEntities); + EntityList exportEntities; // prepare for validation of component requirements. @@ -616,7 +609,7 @@ exportComponent, prefabProcessorContext); ); // replace entities of instance with exported ones. - instance->GetNestedEntities( + instance->GetAllEntitiesInHierarchy( [&exportEntitiesMap](AZStd::unique_ptr& entity) { auto entityId = entity->GetId(); @@ -625,14 +618,6 @@ exportComponent, prefabProcessorContext); } ); - if (instance->HasContainerEntity()) - { - if (auto found = exportEntitiesMap.find(instance->GetContainerEntityId()); found != exportEntitiesMap.end()) - { - instance->SetContainerEntity(*found->second); - } - } - // save the final result in the target Prefab DOM. PrefabDom filteredPrefab; if (!PrefabDomUtils::StoreInstanceInPrefabDom(*instance, filteredPrefab)) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h index 1e00485e42..5de5c516f8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h @@ -55,8 +55,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils protected: using EntityList = AZStd::vector; - static EntityList GetEntitiesFromInstance( - AZStd::unique_ptr& instance); + static void GetEntitiesFromInstance( + AZStd::unique_ptr& instance, EntityList& hierarchyEntities); static bool ReadComponentAttribute( AZ::Component* component, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp index 7915403c0a..d6b7aebddc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp @@ -58,8 +58,17 @@ namespace AzToolsFramework if (!path.empty()) { - infoString = - QObject::tr("(%1)").arg(path.Filename().Native().data()); + QString saveFlag = ""; + auto dirtyOutcome = m_prefabPublicInterface->HasUnsavedChanges(path); + + if (dirtyOutcome.IsSuccess() && dirtyOutcome.GetValue() == true) + { + saveFlag = "*"; + } + + infoString = QObject::tr("(%1%2)") + .arg(path.Filename().Native().data()) + .arg(saveFlag); } return infoString; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 77918565e4..54ad96535f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -588,15 +589,6 @@ namespace AzToolsFramework bool PrefabIntegrationManager::QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath) { - QWidget* mainWindow = nullptr; - EditorRequests::Bus::BroadcastResult(mainWindow, &EditorRequests::Bus::Events::GetMainWindow); - - if (mainWindow == nullptr) - { - AZ_Assert(false, "Prefab - Could not detect Editor main window to generate the asset picker."); - return false; - } - AssetSelectionModel selection; // Note, stringfilter will match every source file CONTAINING ".prefab". @@ -624,7 +616,7 @@ namespace AzToolsFramework selection.SetDisplayFilter(compositeFilterPtr); selection.SetSelectionFilter(compositeFilterPtr); - AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, mainWindow); + AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, AzToolsFramework::GetActiveWindow()); if (!selection.IsValid()) { @@ -983,12 +975,7 @@ namespace AzToolsFramework includedEntities.c_str(), referencedEntities.c_str()); - QWidget* mainWindow = nullptr; - AzToolsFramework::EditorRequests::Bus::BroadcastResult( - mainWindow, - &AzToolsFramework::EditorRequests::Bus::Events::GetMainWindow); - - QMessageBox msgBox(mainWindow); + QMessageBox msgBox(AzToolsFramework::GetActiveWindow()); msgBox.setWindowTitle("External Entity References"); msgBox.setText("The prefab contains references to external entities that are not selected."); msgBox.setInformativeText("You can move the referenced entities into this prefab or retain the external references."); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index eb16bf158e..c4f1ae33da 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -127,6 +127,7 @@ namespace AzToolsFramework static const char* const s_dittoTranslationIndividualUndoRedoDesc = "Ditto translation individual"; static const char* const s_dittoScaleIndividualWorldUndoRedoDesc = "Ditto scale individual world"; static const char* const s_dittoScaleIndividualLocalUndoRedoDesc = "Ditto scale individual local"; + static const char* const s_snapToWorldGridUndoRedoDesc = "Snap to world grid"; static const char* const s_showAllEntitiesUndoRedoDesc = s_showAllTitle; static const char* const s_lockSelectionUndoRedoDesc = s_lockSelectionTitle; static const char* const s_hideSelectionUndoRedoDesc = s_hideSelectionTitle; @@ -142,6 +143,7 @@ namespace AzToolsFramework static const char* const SpaceClusterWorldTooltip = "Toggle world space lock"; static const char* const SpaceClusterParentTooltip = "Toggle parent space lock"; static const char* const SpaceClusterLocalTooltip = "Toggle local space lock"; + static const char* const SnappingClusterSnapToWorldTooltip = "Snap selected entities to the world space grid"; static const AZ::Color s_fadedXAxisColor = AZ::Color(AZ::u8(200), AZ::u8(127), AZ::u8(127), AZ::u8(255)); static const AZ::Color s_fadedYAxisColor = AZ::Color(AZ::u8(127), AZ::u8(190), AZ::u8(127), AZ::u8(255)); @@ -150,8 +152,6 @@ namespace AzToolsFramework static const AZ::Color s_pickedOrientationColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); static const AZ::Color s_selectedEntityAabbColor = AZ::Color(0.6f, 0.6f, 0.6f, 0.4f); - static const int s_defaultViewportId = 0; - static const float s_pivotSize = 0.075f; // the size of the pivot (box) to render when selected // data passed to manipulators when processing mouse interactions @@ -503,7 +503,8 @@ namespace AzToolsFramework void EditorTransformComponentSelection::SetAllViewportUiVisible(const bool visible) { SetViewportUiClusterVisible(m_transformModeClusterId, visible); - SetViewportUiClusterVisible(m_spaceCluster.m_spaceClusterId, visible); + SetViewportUiClusterVisible(m_spaceCluster.m_clusterId, visible); + SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, visible); m_viewportUiVisible = visible; } @@ -524,8 +525,8 @@ namespace AzToolsFramework }; ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, - m_spaceCluster.m_spaceClusterId, buttonIdFromFrameFn(referenceFrame)); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_spaceCluster.m_clusterId, + buttonIdFromFrameFn(referenceFrame)); } namespace ETCS @@ -1037,6 +1038,8 @@ namespace AzToolsFramework CreateTransformModeSelectionCluster(); CreateSpaceSelectionCluster(); + CreateSnappingCluster(); + RegisterActions(); SetupBoxSelect(); RefreshSelectedEntityIdsAndRegenerateManipulators(); @@ -1048,7 +1051,8 @@ namespace AzToolsFramework DestroyManipulators(m_entityIdManipulators); DestroyCluster(m_transformModeClusterId); - DestroyCluster(m_spaceCluster.m_spaceClusterId); + DestroyCluster(m_spaceCluster.m_clusterId); + DestroyCluster(m_snappingCluster.m_clusterId); UnregisterActions(); @@ -2513,28 +2517,64 @@ namespace AzToolsFramework m_transformModeSelectionHandler); } - void EditorTransformComponentSelection::CreateSpaceSelectionCluster() + void EditorTransformComponentSelection::CreateSnappingCluster() { // create the cluster for switching spaces/reference frames ViewportUi::ViewportUiRequestBus::EventResult( - m_spaceCluster.m_spaceClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, + m_snappingCluster.m_clusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, ViewportUi::Alignment::TopRight); - // create and register the buttons (strings correspond to icons even if the values appear different) - m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "World"); - m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Parent"); - m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Local"); + m_snappingCluster.m_snapToWorldButtonId = RegisterClusterButton(m_snappingCluster.m_clusterId, "Grid"); // set button tooltips ViewportUi::ViewportUiRequestBus::Event( ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, - m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip); + m_snappingCluster.m_clusterId, m_snappingCluster.m_snapToWorldButtonId, SnappingClusterSnapToWorldTooltip); + + const auto onButtonClicked = [this](const ViewportUi::ButtonId buttonId) + { + if (buttonId == m_snappingCluster.m_snapToWorldButtonId) + { + float gridSize = 1.0f; + ViewportInteraction::ViewportInteractionRequestBus::EventResult( + gridSize, ViewportUi::DefaultViewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSize); + + SnapSelectedEntitiesToWorldGrid(gridSize); + } + }; + + m_snappingCluster.m_snappingHandler = AZ::Event::Handler(onButtonClicked); + ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, - m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, + m_snappingCluster.m_clusterId, m_snappingCluster.m_snappingHandler); + + // hide initially + SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, false); + } + + void EditorTransformComponentSelection::CreateSpaceSelectionCluster() + { + // create the cluster for switching spaces/reference frames + ViewportUi::ViewportUiRequestBus::EventResult( + m_spaceCluster.m_clusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, + ViewportUi::Alignment::TopRight); + + // create and register the buttons (strings correspond to icons even if the values appear different) + m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "World"); + m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "Parent"); + m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "Local"); + + // set button tooltips ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, - m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_localButtonId, SpaceClusterLocalTooltip); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId, + m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip); + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId, + m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip); + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId, + m_spaceCluster.m_localButtonId, SpaceClusterLocalTooltip); auto onButtonClicked = [this](ViewportUi::ButtonId buttonId) { @@ -2576,14 +2616,31 @@ namespace AzToolsFramework } ViewportUi::ViewportUiRequestBus::Event( ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonLocked, - m_spaceCluster.m_spaceClusterId, buttonId, m_spaceCluster.m_spaceLock.has_value()); + m_spaceCluster.m_clusterId, buttonId, m_spaceCluster.m_spaceLock.has_value()); }; - m_spaceCluster.m_spaceSelectionHandler = AZ::Event::Handler(onButtonClicked); + m_spaceCluster.m_spaceHandler = AZ::Event::Handler(onButtonClicked); ViewportUi::ViewportUiRequestBus::Event( ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, - m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_spaceSelectionHandler); + m_spaceCluster.m_clusterId, m_spaceCluster.m_spaceHandler); + } + + void EditorTransformComponentSelection::SnapSelectedEntitiesToWorldGrid(const float gridSize) + { + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); + + const AZStd::array snapAxes = { AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ() }; + + ScopedUndoBatch undoBatch(s_snapToWorldGridUndoRedoDesc); + for (const AZ::EntityId& entityId : m_selectedEntityIds) + { + ScopedUndoBatch::MarkEntityDirty(entityId); + SetEntityWorldTranslation( + entityId, CalculateSnappedPosition(GetWorldTranslation(entityId), snapAxes.data(), snapAxes.size(), gridSize)); + } + + RefreshManipulators(RefreshType::Translation); } EditorTransformComponentSelectionRequests::Mode EditorTransformComponentSelection::GetTransformMode() @@ -3145,15 +3202,16 @@ namespace AzToolsFramework return "Transform Component"; } - void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) + void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu( + QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) { - QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick)); - QObject::connect( - action, &QAction::triggered, action, - [this]() - { - ToggleCenterPivotSelection(); - }); + QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick)); + QObject::connect( + action, &QAction::triggered, action, + [this]() + { + ToggleCenterPivotSelection(); + }); } void EditorTransformComponentSelection::BeforeEntitySelectionChanged() @@ -3175,7 +3233,7 @@ namespace AzToolsFramework } void EditorTransformComponentSelection::AfterEntitySelectionChanged( - const EntityIdList& /*newlySelectedEntities*/, const EntityIdList& /*newlyDeselectedEntities*/) + [[maybe_unused]] const EntityIdList& newlySelectedEntities, [[maybe_unused]] const EntityIdList& newlyDeselectedEntities) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -3195,6 +3253,8 @@ namespace AzToolsFramework m_didSetSelectedEntities = false; } + SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, m_viewportUiVisible && !m_selectedEntityIds.empty()); + RegenerateManipulators(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 773921ebea..db96d91911 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -115,12 +115,25 @@ namespace AzToolsFramework SpaceCluster(const SpaceCluster&) = delete; SpaceCluster& operator=(const SpaceCluster&) = delete; - ViewportUi::ClusterId m_spaceClusterId; //!< The id identifying the reference space cluster. + ViewportUi::ClusterId m_clusterId; //!< The id identifying the reference space cluster. ViewportUi::ButtonId m_localButtonId; //!< Local reference space button id. ViewportUi::ButtonId m_parentButtonId; //!< Parent reference space button id. ViewportUi::ButtonId m_worldButtonId; //!< World reference space button id. - AZ::Event::Handler m_spaceSelectionHandler; //!< Callback for when a space cluster button is pressed. AZStd::optional m_spaceLock; //!< Locked reference frame to use if set. + AZ::Event::Handler m_spaceHandler; //!< Callback for when a space cluster button is pressed. + }; + + //! Grouping of viewport ui related state for aligning transforms to a grid. + struct SnappingCluster + { + SnappingCluster() = default; + // disable copying and moving (implicit) + SnappingCluster(const SnappingCluster&) = delete; + SnappingCluster& operator=(const SnappingCluster&) = delete; + + ViewportUi::ClusterId m_clusterId; //!< The cluster id for all snapping buttons. + ViewportUi::ButtonId m_snapToWorldButtonId; //!< The button id for snapping all axes to the world. + AZ::Event::Handler m_snappingHandler; //!< Callback for when a snapping cluster button is pressed. }; //! Entity selection/interaction handling. @@ -180,6 +193,7 @@ namespace AzToolsFramework void CreateTransformModeSelectionCluster(); void CreateSpaceSelectionCluster(); + void CreateSnappingCluster(); void ClearManipulatorTranslationOverride(); void ClearManipulatorOrientationOverride(); @@ -228,14 +242,15 @@ namespace AzToolsFramework AZStd::optional GetManipulatorTransform() override; void OverrideManipulatorOrientation(const AZ::Quaternion& orientation) override; void OverrideManipulatorTranslation(const AZ::Vector3& translation) override; - void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation); - void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation); - void ResetTranslationForSelectedEntitiesLocal(); - void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation); - void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation); - void ResetOrientationForSelectedEntitiesLocal(); - void CopyScaleToSelectedEntitiesIndividualLocal(float scale); - void CopyScaleToSelectedEntitiesIndividualWorld(float scale); + void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation) override; + void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation) override; + void ResetTranslationForSelectedEntitiesLocal() override; + void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation) override; + void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation) override; + void ResetOrientationForSelectedEntitiesLocal() override; + void CopyScaleToSelectedEntitiesIndividualLocal(float scale) override; + void CopyScaleToSelectedEntitiesIndividualWorld(float scale) override; + void SnapSelectedEntitiesToWorldGrid(float gridSize) override; // EditorManipulatorCommandUndoRedoRequestBus ... void UndoRedoEntityManipulatorCommand( @@ -320,6 +335,7 @@ namespace AzToolsFramework AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click. AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame. SpaceCluster m_spaceCluster; //!< Related viewport ui state for controlling the current reference space. + SnappingCluster m_snappingCluster; //!< Related viewport ui state for aligning positions to a grid or reference frame. bool m_viewportUiVisible = true; //!< Used to hide/show the viewport ui elements. }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h index 966f9333fc..c7b75da206 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h @@ -104,6 +104,9 @@ namespace AzToolsFramework //! Copy scale to to each individual entity in world (absolute) space. virtual void CopyScaleToSelectedEntitiesIndividualWorld(float scale) = 0; + //! Snap selected entities to be aligned with the world space grid. + virtual void SnapSelectedEntitiesToWorldGrid(float gridSize) = 0; + protected: ~EditorTransformComponentSelectionRequests() = default; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 7b2d45e652..bb2aeed4d6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -281,7 +281,7 @@ namespace AzToolsFramework::ViewportUi::Internal void ViewportUiDisplay::HideViewportUiElement(ViewportUiElementId elementId) { if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId); - element.m_widget && UiDisplayEnabled()) + element.m_widget) { element.m_widget->setVisible(false); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index e5ac1f9693..9ec0deaed6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -544,6 +544,8 @@ set(FILES AssetBrowser/AssetBrowserEntry.h AssetBrowser/AssetBrowserFilterModel.cpp AssetBrowser/AssetBrowserFilterModel.h + AssetBrowser/AssetBrowserTableModel.cpp + AssetBrowser/AssetBrowserTableModel.h AssetBrowser/AssetBrowserModel.cpp AssetBrowser/AssetBrowserModel.h AssetBrowser/AssetEntryChange.h @@ -554,6 +556,8 @@ set(FILES AssetBrowser/EBusFindAssetTypeByName.h AssetBrowser/Views/AssetBrowserTreeView.cpp AssetBrowser/Views/AssetBrowserTreeView.h + AssetBrowser/Views/AssetBrowserTableView.cpp + AssetBrowser/Views/AssetBrowserTableView.h AssetBrowser/Views/EntryDelegate.cpp AssetBrowser/Views/EntryDelegate.h AssetBrowser/Views/AssetBrowserFolderWidget.cpp diff --git a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp index cbc81ba9b8..9a34efa6f5 100644 --- a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp @@ -1,42 +1,42 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include #include #include -#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include -#include #include #include +#include #include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include using namespace AzToolsFramework; @@ -46,12 +46,11 @@ namespace AZ { return os << entityId.ToString().c_str(); } -} +} // namespace AZ namespace UnitTest { - class EditorEntityVisibilityCacheFixture - : public ToolsApplicationFixture + class EditorEntityVisibilityCacheFixture : public ToolsApplicationFixture { public: void CreateLayerAndEntityHierarchy() @@ -116,8 +115,7 @@ namespace UnitTest } // Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection. - class EditorTransformComponentSelectionFixture - : public ToolsApplicationFixture + class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture { public: void SetUpEditorFixtureImpl() override @@ -138,13 +136,11 @@ namespace UnitTest EntityIdList m_entityIds; }; - void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection( - const AZ::Quaternion& orientation) + void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation) { for (auto entityId : m_entityIds) { - AZ::TransformBus::Event( - entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation); } } @@ -152,40 +148,32 @@ namespace UnitTest { AZStd::optional manipulatorTransform; EditorTransformComponentSelectionRequestBus::EventResult( - manipulatorTransform, GetEntityContextId(), - &EditorTransformComponentSelectionRequests::GetManipulatorTransform); + manipulatorTransform, GetEntityContextId(), &EditorTransformComponentSelectionRequests::GetManipulatorTransform); return manipulatorTransform; } - void EditorTransformComponentSelectionFixture::RefreshManipulators( - EditorTransformComponentSelectionRequests::RefreshType refreshType) + void EditorTransformComponentSelectionFixture::RefreshManipulators(EditorTransformComponentSelectionRequests::RefreshType refreshType) { EditorTransformComponentSelectionRequestBus::Event( GetEntityContextId(), &EditorTransformComponentSelectionRequests::RefreshManipulators, refreshType); } - void EditorTransformComponentSelectionFixture::SetTransformMode( - EditorTransformComponentSelectionRequests::Mode transformMode) + void EditorTransformComponentSelectionFixture::SetTransformMode(EditorTransformComponentSelectionRequests::Mode transformMode) { EditorTransformComponentSelectionRequestBus::Event( - GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode, - transformMode); + GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode, transformMode); } - void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation( - const AZ::Quaternion& orientation) + void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(const AZ::Quaternion& orientation) { EditorTransformComponentSelectionRequestBus::Event( - GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation, - orientation); + GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation, orientation); } - void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation( - const AZ::Vector3& translation) + void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(const AZ::Vector3& translation) { EditorTransformComponentSelectionRequestBus::Event( - GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation, - translation); + GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation, translation); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -202,8 +190,7 @@ namespace UnitTest SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation); - const AZ::Transform manipulatorTransformBefore = - GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); // check preconditions - manipulator transform matches parent/world transform (identity) EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY())); @@ -218,8 +205,7 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then - const AZ::Transform manipulatorTransformAfter = - GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); // check postconditions - manipulator transform matches parent/world transform (identity) EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY())); @@ -229,8 +215,7 @@ namespace UnitTest { // create invalid starting orientation to guarantee correct data is coming from GetLocalRotationQuaternion AZ::Quaternion entityOrientation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), 90.0f); - AZ::TransformBus::EventResult( - entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion); + AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion); // manipulator orientation matches entity orientation EXPECT_THAT(entityOrientation, IsClose(manipulatorTransformAfter.GetRotation())); @@ -252,8 +237,7 @@ namespace UnitTest SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation); - const AZ::Transform manipulatorTransformBefore = - GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); // check preconditions - manipulator transform matches manipulator orientation override (not entity transform) EXPECT_THAT(manipulatorTransformBefore.GetBasisX(), IsClose(AZ::Vector3::CreateAxisY())); @@ -268,8 +252,7 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then - const AZ::Transform manipulatorTransformAfter = - GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); // check postconditions - manipulator transform matches parent/world space (manipulator override was cleared) EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY())); @@ -278,8 +261,7 @@ namespace UnitTest for (auto entityId : m_entityIds) { AZ::Quaternion entityOrientation; - AZ::TransformBus::EventResult( - entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion); + AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion); // entity transform matches initial (entity transform was not reset, only manipulator was) EXPECT_THAT(entityOrientation, IsClose(initialEntityOrientation)); @@ -301,16 +283,13 @@ namespace UnitTest AZ::EntityId parentId = CreateDefaultEditorEntity("Parent", &parent); AZ::EntityId childId = CreateDefaultEditorEntity("Child", &child); - AZ::TransformBus::Event( - childId, &AZ::TransformInterface::SetParent, parentId); - AZ::TransformBus::Event( - parentId, &AZ::TransformInterface::SetParent, grandParentId); + AZ::TransformBus::Event(childId, &AZ::TransformInterface::SetParent, parentId); + AZ::TransformBus::Event(parentId, &AZ::TransformInterface::SetParent, grandParentId); UnitTest::SliceAssets sliceAssets; const auto sliceAssetId = UnitTest::SaveAsSlice({ grandParent }, GetApplication(), sliceAssets); - EntityList instantiatedEntities = - UnitTest::InstantiateSlice(sliceAssetId, sliceAssets); + EntityList instantiatedEntities = UnitTest::InstantiateSlice(sliceAssetId, sliceAssets); const AZ::EntityId entityIdToMove = instantiatedEntities.back()->GetId(); EditorEntityComponentChangeDetector editorEntityChangeDetector(entityIdToMove); @@ -321,8 +300,7 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // When EditorTransformComponentSelectionRequestBus::Event( - GetEntityContextId(), - &EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual, + GetEntityContextId(), &EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual, AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::DegToRad(90.0f))); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -362,10 +340,9 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then AzToolsFramework::EntityIdList selectedEntities; - ToolsApplicationRequestBus::BroadcastResult( - selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities); + ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities); - AzToolsFramework::EntityIdList expectedSelectedEntities = {entity4, entity5, entity6}; + AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 }; EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities)); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -396,10 +373,9 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then AzToolsFramework::EntityIdList selectedEntities; - ToolsApplicationRequestBus::BroadcastResult( - selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities); + ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities); - AzToolsFramework::EntityIdList expectedSelectedEntities = {m_entity1, entity2, entity3, entity4}; + AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entity1, entity2, entity3, entity4 }; EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities)); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -416,11 +392,9 @@ namespace UnitTest const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 0.0f)); // calculate the position in screen space of the initial position of the entity - const auto initialPositionScreen = - AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState); + const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState); // calculate the position in screen space of the final position of the entity - const auto finalPositionScreen = - AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState); + const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState); // select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection) AzToolsFramework::SelectEntity(m_entity1); @@ -452,10 +426,10 @@ namespace UnitTest } // simple widget to listen for a mouse wheel event and then forward it on to the ViewportSelectionRequestBus - class WheelEventWidget - : public QWidget + class WheelEventWidget : public QWidget { using MouseInteractionResult = AzToolsFramework::ViewportInteraction::MouseInteractionResult; + public: WheelEventWidget(QWidget* parent = nullptr) : QWidget(parent) @@ -490,8 +464,7 @@ namespace UnitTest { EditorTransformComponentSelectionRequests::Mode transformMode; EditorTransformComponentSelectionRequestBus::EventResult( - transformMode, GetEntityContextId(), - &EditorTransformComponentSelectionRequestBus::Events::GetTransformMode); + transformMode, GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::GetTransformMode); return transformMode; }; @@ -519,6 +492,56 @@ namespace UnitTest EXPECT_THAT(wheelEventWidget.m_mouseInteractionResult, Eq(vi::MouseInteractionResult::Viewport)); } + TEST_F(EditorTransformComponentSelectionFixture, EntityPositionsCanBeSnappedToGrid) + { + using ::testing::Pointwise; + + m_entityIds.push_back(CreateDefaultEditorEntity("Entity2")); + m_entityIds.push_back(CreateDefaultEditorEntity("Entity3")); + + const AZStd::vector initialUnsnappedPositions = { AZ::Vector3(1.2f, 3.5f, 6.7f), AZ::Vector3(13.2f, 15.6f, 11.4f), + AZ::Vector3(4.2f, 103.2f, 16.6f) }; + AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[0]); + AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[1]); + AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[2]); + + AzToolsFramework::SelectEntities(m_entityIds); + + EditorTransformComponentSelectionRequestBus::Event( + GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 2.0f); + + AZStd::vector entityPositionsAfterSnap; + AZStd::transform( + m_entityIds.cbegin(), m_entityIds.cend(), AZStd::back_inserter(entityPositionsAfterSnap), + [](const AZ::EntityId& entityId) + { + return GetWorldTranslation(entityId); + }); + + const AZStd::vector expectedSnappedPositions = { AZ::Vector3(2.0f, 4.0f, 6.0f), AZ::Vector3(14.0f, 16.0f, 12.0f), + AZ::Vector3(4.0f, 104.0f, 16.0f) }; + EXPECT_THAT(entityPositionsAfterSnap, Pointwise(ContainerIsClose(), expectedSnappedPositions)); + } + + TEST_F(EditorTransformComponentSelectionFixture, ManipulatorStaysAlignedToEntityTranslationAfterSnap) + { + const auto initialUnsnappedPosition = AZ::Vector3(1.2f, 3.5f, 6.7f); + AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPosition); + + AzToolsFramework::SelectEntities(m_entityIds); + + EditorTransformComponentSelectionRequestBus::Event( + GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 1.0f); + + const auto entityPositionAfterSnap = GetWorldTranslation(m_entity1); + const AZ::Vector3 manipulatorPositionAfterSnap = + GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()).GetTranslation(); + + const auto expectedSnappedPosition = AZ::Vector3(1.0f, 4.0f, 7.0f); + EXPECT_THAT(entityPositionAfterSnap, IsClose(expectedSnappedPosition)); + EXPECT_THAT(expectedSnappedPosition, IsClose(manipulatorPositionAfterSnap)); + } + // struct to contain input reference frame and expected orientation outcome based on // the reference frame, selection and entity hierarchy struct ReferenceFrameWithOrientation @@ -541,19 +564,20 @@ namespace UnitTest class EditorTransformComponentSelectionSingleEntityPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P(EditorTransformComponentSelectionSingleEntityPivotFixture, PivotOrientationMatchesReferenceFrameSingleEntity) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given AZ::TransformBus::Event( m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateFromQuaternionAndTranslation( - ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); + AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -574,20 +598,20 @@ namespace UnitTest All, EditorTransformComponentSelectionSingleEntityPivotFixture, testing::Values( - ReferenceFrameWithOrientation{ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace}, - ReferenceFrameWithOrientation{ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity()}, - ReferenceFrameWithOrientation{ReferenceFrame::World, AZ::Quaternion::CreateIdentity()})); + ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace }, + ReferenceFrameWithOrientation{ ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity() }, + ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() })); class EditorTransformComponentSelectionSingleEntityWithParentPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; - - TEST_P( - EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, - PivotOrientationMatchesReferenceFrameEntityWithParent) + , public ::testing::WithParamInterface + { + }; + + TEST_P(EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, PivotOrientationMatchesReferenceFrameEntityWithParent) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -596,8 +620,7 @@ namespace UnitTest AZ::TransformBus::Event( parentEntityId, &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateFromQuaternionAndTranslation( - ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); + AZ::Transform::CreateFromQuaternionAndTranslation(ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); AZ::TransformBus::Event( m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, @@ -624,20 +647,20 @@ namespace UnitTest All, EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, testing::Values( - ReferenceFrameWithOrientation{ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace}, - ReferenceFrameWithOrientation{ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace}, - ReferenceFrameWithOrientation{ReferenceFrame::World, AZ::Quaternion::CreateIdentity()})); + ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace }, + ReferenceFrameWithOrientation{ ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace }, + ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() })); class EditorTransformComponentSelectionMultipleEntitiesPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; - - TEST_P( - EditorTransformComponentSelectionMultipleEntitiesPivotFixture, - PivotOrientationMatchesReferenceFrameMultipleEntities) + , public ::testing::WithParamInterface + { + }; + + TEST_P(EditorTransformComponentSelectionMultipleEntitiesPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntities) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientationForEntityIds; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -646,23 +669,18 @@ namespace UnitTest // setup entities in arbitrary triangle arrangement AZ::TransformBus::Event( - m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); + m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); // note: EntityIdManipulatorLookup{} is unused during this test - EntityIdManipulatorLookups lookups { - {m_entityIds[0], EntityIdManipulatorLookup{}}, - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} }, + { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -691,14 +709,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParent) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientationForEntityIds; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -711,22 +731,18 @@ namespace UnitTest ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); // note: EntityIdManipulatorLookup{} is unused during this test // only select second two entities that are children of m_entityIds[0] - EntityIdManipulatorLookups lookups{ - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -755,14 +771,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesDifferentParent) { - using ETCS::PivotOrientationResult; using ETCS::CalculatePivotOrientationForEntityIds; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -776,22 +794,18 @@ namespace UnitTest ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[3]); // note: EntityIdManipulatorLookup{} is unused during this test // only select second two entities that are children of different m_entities - EntityIdManipulatorLookups lookups{ - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -819,30 +833,29 @@ namespace UnitTest class EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture, PivotOrientationMatchesReferenceFrameSingleEntityOptionalOverride) { - using ETCS::PivotOrientationResult; using ETCS::CalculateSelectionPivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given AZ::TransformBus::Event( m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateFromQuaternionAndTranslation( - ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); + AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero())); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // When const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam(); - EntityIdManipulatorLookups lookups{ - {m_entityIds[0], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} } }; // set override frame (orientation only) OptionalFrame optionalFrame; @@ -870,14 +883,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesOptionalOverride) { - using ETCS::PivotOrientationResult; using ETCS::CalculateSelectionPivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -885,23 +900,18 @@ namespace UnitTest m_entityIds.push_back(CreateDefaultEditorEntity("Entity3")); AZ::TransformBus::Event( - m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); + m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); // note: EntityIdManipulatorLookup{} is unused during this test - EntityIdManipulatorLookups lookups{ - {m_entityIds[0], EntityIdManipulatorLookup{}}, - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} }, + { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -932,14 +942,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesNoOptionalOverride) { - using ETCS::PivotOrientationResult; using ETCS::CalculateSelectionPivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -947,23 +959,18 @@ namespace UnitTest m_entityIds.push_back(CreateDefaultEditorEntity("Entity3")); AZ::TransformBus::Event( - m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); + m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); // note: EntityIdManipulatorLookup{} is unused during this test - EntityIdManipulatorLookups lookups{ - {m_entityIds[0], EntityIdManipulatorLookup{}}, - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} }, + { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -992,14 +999,16 @@ namespace UnitTest class EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture : public EditorTransformComponentSelectionFixture - , public ::testing::WithParamInterface {}; + , public ::testing::WithParamInterface + { + }; TEST_P( EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture, PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParentNoOptionalOverride) { - using ETCS::PivotOrientationResult; using ETCS::CalculateSelectionPivotOrientation; + using ETCS::PivotOrientationResult; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given @@ -1012,21 +1021,17 @@ namespace UnitTest ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f))); AZ::TransformBus::Event( - m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); + m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f))); AZ::TransformBus::Event( - m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, - AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); + m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f))); AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]); // note: EntityIdManipulatorLookup{} is unused during this test - EntityIdManipulatorLookups lookups{ - {m_entityIds[1], EntityIdManipulatorLookup{}}, - {m_entityIds[2], EntityIdManipulatorLookup{}} - }; + EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} }, + { m_entityIds[2], EntityIdManipulatorLookup{} } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1174,13 +1179,13 @@ namespace UnitTest AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId); // Layer1 - // A - // B - // C - // Layer2 - // D - // E - // F + // A + // B + // C + // Layer2 + // D + // E + // F /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1270,13 +1275,13 @@ namespace UnitTest AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId); // Layer1 - // A - // B - // C - // Layer2 - // D - // E - // F + // A + // B + // C + // Layer2 + // D + // E + // F /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1368,8 +1373,7 @@ namespace UnitTest EXPECT_TRUE(!IsEntityVisible(m_layerId)); bool flagSetVisible = false; - EditorVisibilityRequestBus::EventResult( - flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag); + EditorVisibilityRequestBus::EventResult(flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag); // even though a layer is set to not be visible, this is recorded by SetLayerChildrenVisibility // and AreLayerChildrenVisible - the visibility flag will not be modified and remains true @@ -1377,12 +1381,12 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// } - class EditorEntityInfoRequestActivateTestComponent - : public AzToolsFramework::Components::EditorComponentBase + class EditorEntityInfoRequestActivateTestComponent : public AzToolsFramework::Components::EditorComponentBase { public: AZ_EDITOR_COMPONENT( - EditorEntityInfoRequestActivateTestComponent, "{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}", + EditorEntityInfoRequestActivateTestComponent, + "{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}", AzToolsFramework::Components::EditorComponentBase); static void Reflect(AZ::ReflectContext* context); @@ -1391,13 +1395,13 @@ namespace UnitTest void Activate() override { // ensure we can successfully read IsVisible and IsLocked (bus will be connected to in entity Init) - EditorEntityInfoRequestBus::EventResult( - m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible); - EditorEntityInfoRequestBus::EventResult( - m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked); + EditorEntityInfoRequestBus::EventResult(m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible); + EditorEntityInfoRequestBus::EventResult(m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked); } - void Deactivate() override {} + void Deactivate() override + { + } bool m_visible = false; bool m_locked = true; @@ -1407,14 +1411,11 @@ namespace UnitTest { if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class() - ->Version(0) - ; + serializeContext->Class()->Version(0); } } - class EditorEntityModelEntityInfoRequestFixture - : public ToolsApplicationFixture + class EditorEntityModelEntityInfoRequestFixture : public ToolsApplicationFixture { public: void SetUpEditorFixtureImpl() override @@ -1435,8 +1436,7 @@ namespace UnitTest // This is necessary to prevent a warning in the undo system. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, - entity->GetId()); + &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1469,8 +1469,7 @@ namespace UnitTest // This is necessary to prevent a warning in the undo system. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, - entity->GetId()); + &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp index aef3cad5f3..e3460c307f 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp @@ -93,7 +93,7 @@ namespace UnitTest // Retrieve the entity pointer from the component application bus. AZ::Entity* wheelEntityUnderAxle = nullptr; - axleInstance->GetNestedEntities([&wheelEntityUnderAxle, wheelEntityIdUnderAxle](AZStd::unique_ptr& entity) + axleInstance->GetAllEntitiesInHierarchy([&wheelEntityUnderAxle, wheelEntityIdUnderAxle](AZStd::unique_ptr& entity) { if (entity->GetId() == wheelEntityIdUnderAxle) { diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp b/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp deleted file mode 100644 index e72bd9b12c..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/BvDynamicTree.cpp +++ /dev/null @@ -1,1277 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -// Modifications copyright Amazon.com, Inc. or its affiliates. - -///BvDynamicTree implementation by Nathanael Presson -#include - -namespace GridMate -{ - // - struct btDbvtNodeEnumerator : BvDynamicTree::ICollideCollector - { - BvDynamicTree::ConstNodeArrayType nodes; - void Process(const BvDynamicTree::NodeType* n) { nodes.push_back(n); } - }; - - // - static AZ_FORCE_INLINE int indexof(const BvDynamicTree::NodeType* node) - { - return (node->m_parent->m_childs[1]==node); - } - - // - static AZ_FORCE_INLINE BvDynamicTree::VolumeType merge( const BvDynamicTree::VolumeType& a, const BvDynamicTree::VolumeType& b) - { - BvDynamicTree::VolumeType res; - Merge(a,b,res); - return res; - } - - // volume+edge lengths - static AZ_FORCE_INLINE float size(const BvDynamicTree::VolumeType& a) - { - const AZ::Vector3 edges = a.GetExtents(); - return edges.GetX()*edges.GetY()*edges.GetZ() + edges.Dot(AZ::Vector3::CreateOne()); - } - - // - static void getmaxdepth(const BvDynamicTree::NodeType* node,int depth,int& maxdepth) - { - if(node->IsInternal()) - { - getmaxdepth(node->m_childs[0],depth+1,maxdepth); - getmaxdepth(node->m_childs[0],depth+1,maxdepth); - } - else - maxdepth= AZ::GetMax(maxdepth,depth); - } - - - - //========================================================================= - // insertleaf - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::insertleaf( NodeType* root, NodeType* leaf) - { - if(!m_root) - { - m_root = leaf; - leaf->m_parent = 0; - } - else - { - if(!root->IsLeaf()) - { - do { - root=root->m_childs[Select( leaf->m_volume, - root->m_childs[0]->m_volume, - root->m_childs[1]->m_volume)]; - } while(!root->IsLeaf()); - } - NodeType* prev = root->m_parent; - NodeType* node = createnode(prev,leaf->m_volume,root->m_volume,0); - if(prev) - { - prev->m_childs[indexof(root)] = node; - node->m_childs[0] = root;root->m_parent=node; - node->m_childs[1] = leaf;leaf->m_parent=node; - do { - if(!prev->m_volume.Contains(node->m_volume)) - Merge(prev->m_childs[0]->m_volume,prev->m_childs[1]->m_volume,prev->m_volume); - else - break; - node=prev; - } while(0!=(prev=node->m_parent)); - } - else - { - node->m_childs[0] = root;root->m_parent=node; - node->m_childs[1] = leaf;leaf->m_parent=node; - m_root = node; - } - } - } - - //========================================================================= - // removeleaf - // [3/4/2009] - //========================================================================= - BvDynamicTree::NodeType* - BvDynamicTree::removeleaf( NodeType* leaf) - { - if(leaf==m_root) - { - m_root=0; - return 0; - } - else - { - NodeType* parent=leaf->m_parent; - NodeType* prev=parent->m_parent; - NodeType* sibling=parent->m_childs[1-indexof(leaf)]; - if(prev) - { - prev->m_childs[indexof(parent)]=sibling; - sibling->m_parent=prev; - deletenode(parent); - while(prev) - { - const VolumeType pb=prev->m_volume; - Merge(prev->m_childs[0]->m_volume,prev->m_childs[1]->m_volume,prev->m_volume); - if(NotEqual(pb,prev->m_volume)) - { - prev=prev->m_parent; - } else break; - } - return prev?prev:m_root; - } - else - { - m_root=sibling; - sibling->m_parent=0; - deletenode(parent); - return m_root; - } - } - } - - - //========================================================================= - // fetchleaves - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::fetchleaves(NodeType* root,NodeArrayType& leaves,int depth) - { - if(root->IsInternal()&&depth) - { - fetchleaves(root->m_childs[0],leaves,depth-1); - fetchleaves(root->m_childs[1],leaves,depth-1); - deletenode(root); - } - else - { - leaves.push_back(root); - } - } - - //========================================================================= - // split - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::split(const NodeArrayType& leaves, NodeArrayType& left, NodeArrayType& right, const AZ::Vector3& org, const AZ::Vector3& axis) - { - left.resize(0); - right.resize(0); - for(size_t i = 0, ni = leaves.size(); i < ni; ++i) - { - if (axis.Dot(leaves[i]->m_volume.GetCenter() - org) < 0.0f) - { - left.push_back(leaves[i]); - } - else - { - right.push_back(leaves[i]); - } - } - } - - //========================================================================= - // bounds - // [3/4/2009] - //========================================================================= - BvDynamicTree::VolumeType - BvDynamicTree::bounds(const NodeArrayType& leaves) - { - VolumeType volume=leaves[0]->m_volume; - for(size_t i=1,ni=leaves.size();im_volume,volume); - } - return volume; - } - - //========================================================================= - // bottomup - // [3/4/2009] - //========================================================================= - void - BvDynamicTree::bottomup( NodeArrayType& leaves ) - { - while(leaves.size()>1) - { - float minsize = std::numeric_limits::max(); - int minidx[2]={-1,-1}; - for(unsigned int i=0;im_volume,leaves[j]->m_volume)); - if(szm_volume,n[1]->m_volume,0); - p->m_childs[0] = n[0]; - p->m_childs[1] = n[1]; - n[0]->m_parent = p; - n[1]->m_parent = p; - leaves[minidx[0]] = p; - //leaves.swap(minidx[1],leaves.size()-1); - leaves[minidx[1]] = leaves.back(); - leaves.pop_back(); - } - } - - //========================================================================= - // topdown - // [3/4/2009] - //========================================================================= - BvDynamicTree::NodeType* - BvDynamicTree::topdown(NodeArrayType& leaves,int bu_treshold) - { - static const AZ::Vector3 axis[]= { AZ::Vector3(1.0f,0.0f,0.0f), AZ::Vector3(0.0f,1.0f,0.0f), AZ::Vector3(0.0f,0.0f,1.0f)}; - if(leaves.size()>1) - { - if(leaves.size()>(unsigned int)bu_treshold) - { - const VolumeType vol=bounds(leaves); - const AZ::Vector3 org=vol.GetCenter(); - NodeArrayType sets[2]; - int bestaxis=-1; - int bestmidp=(int)leaves.size(); - int splitcount[3][2]={{0,0},{0,0},{0,0}}; - - for(unsigned int i=0;im_volume.GetCenter()-org; - for(int j=0;j<3;++j) - { - ++splitcount[j][x.Dot(axis[j]) > 0.0f ? 1 : 0]; - } - } - for(unsigned int i=0;i<3;++i) - { - if((splitcount[i][0]>0)&&(splitcount[i][1]>0)) - { - // todo just remove the sign bit... - const int midp = (int)fabsf((float)(splitcount[i][0]-splitcount[i][1])); - if(midp=0) - { - sets[0].reserve(splitcount[bestaxis][0]); - sets[1].reserve(splitcount[bestaxis][1]); - split(leaves,sets[0],sets[1],org,axis[bestaxis]); - } - else - { - sets[0].reserve(leaves.size()/2+1); - sets[1].reserve(leaves.size()/2); - for(size_t i=0,ni=leaves.size();im_childs[0] = topdown(sets[0],bu_treshold); - node->m_childs[1] = topdown(sets[1],bu_treshold); - node->m_childs[0]->m_parent=node; - node->m_childs[1]->m_parent=node; - return(node); - } - else - { - bottomup(leaves); - return(leaves[0]); - } - } - return(leaves[0]); - } - - //========================================================================= - // sort - // [3/4/2009] - //========================================================================= - AZ_FORCE_INLINE BvDynamicTree::NodeType* - BvDynamicTree::sort(NodeType* n,NodeType*& r) - { - BvDynamicTree::NodeType* p=n->m_parent; - AZ_Assert(n->IsInternal(), "We can call this only for internal nodes!"); - if(p>n) - { - const int i=indexof(n); - const int j=1-i; - NodeType* s=p->m_childs[j]; - NodeType* q=p->m_parent; - AZ_Assert(n==p->m_childs[i], ""); - if(q) q->m_childs[indexof(p)]=n; else r=n; - s->m_parent=n; - p->m_parent=n; - n->m_parent=q; - p->m_childs[0]=n->m_childs[0]; - p->m_childs[1]=n->m_childs[1]; - n->m_childs[0]->m_parent=p; - n->m_childs[1]->m_parent=p; - n->m_childs[i]=p; - n->m_childs[j]=s; - AZStd::swap(p->m_volume,n->m_volume); - return(p); - } - return(n); - } - - #if 0 - static DBVT_INLINE NodeType* walkup(NodeType* n,int count) - { - while(n&&(count--)) n=n->parent; - return(n); - } - #endif - - // - // Api - // - - // - BvDynamicTree::BvDynamicTree() - { - m_root = 0; - m_free = 0; - m_lkhd = -1; - m_leaves = 0; - m_opath = 0; - } - - // - BvDynamicTree::~BvDynamicTree() - { - Clear(); - } - - // - void BvDynamicTree::Clear() - { - if(m_root) recursedeletenode(m_root); - delete m_free; - m_free=0; - } - - // - void BvDynamicTree::OptimizeBottomUp() - { - if(m_root) - { - NodeArrayType leaves; - leaves.reserve(m_leaves); - fetchleaves(m_root,leaves); - bottomup(leaves); - m_root=leaves[0]; - } - } - - // - void - BvDynamicTree::OptimizeTopDown(int bu_treshold) - { - if(m_root) - { - NodeArrayType leaves; - leaves.reserve(m_leaves); - fetchleaves(m_root,leaves); - m_root=topdown(leaves,bu_treshold); - } - } - - // - void - BvDynamicTree::OptimizeIncremental(int passes) - { - if(passes<0) passes=m_leaves; - if(m_root&&(passes>0)) - { - do { - NodeType* node=m_root; - unsigned bit=0; - while(node->IsInternal()) - { - node=sort(node,m_root)->m_childs[(m_opath>>bit)&1]; - bit=(bit+1)&(sizeof(unsigned)*8-1); - } - Update(node); - ++m_opath; - } while(--passes); - } - } - - // - BvDynamicTree::NodeType* - BvDynamicTree::Insert(const VolumeType& volume,void* data) - { - NodeType* leaf=createnode(0,volume,data); - insertleaf(m_root,leaf); - ++m_leaves; - return(leaf); - } - - // - void - BvDynamicTree::Update(NodeType* leaf,int lookahead) - { - NodeType* root=removeleaf(leaf); - if(root) - { - if(lookahead>=0) - { - for(int i=0;(im_parent;++i) - { - root=root->m_parent; - } - } else root=m_root; - } - insertleaf(root,leaf); - } - - // - void - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume) - { - NodeType* root=removeleaf(leaf); - if(root) - { - if(m_lkhd>=0) - { - for(int i=0;(im_parent;++i) - { - root=root->m_parent; - } - } else root=m_root; - } - leaf->m_volume=volume; - insertleaf(root,leaf); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const AZ::Vector3& velocity,const float margin) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.Expand(AZ::Vector3(margin)); - volume.SignedExpand(velocity); - Update(leaf,volume); - return(true); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const AZ::Vector3& velocity) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.SignedExpand(velocity); - Update(leaf,volume); - return(true); - } - - // - bool - BvDynamicTree::Update(NodeType* leaf,VolumeType& volume,const float margin) - { - if(leaf->m_volume.Contains(volume)) return(false); - volume.Expand(AZ::Vector3(margin)); - Update(leaf,volume); - return(true); - } - - // - void - BvDynamicTree::Remove(NodeType* leaf) - { - removeleaf(leaf); - deletenode(leaf); - --m_leaves; - } - - template - int findLinearSearch(BvDynamicTree::ConstNodeArrayType& arr, const T& key) - { - size_t numElements = arr.size(); - int index = (int)numElements; - - for(size_t i=0;iPrepare(m_root,(unsigned int)nodes.nodes.size()); - for(unsigned int i=0;i<(unsigned int)nodes.nodes.size();++i) - { - const NodeType* n=nodes.nodes[i]; - int p=-1; - if(n->m_parent) p = findLinearSearch(nodes.nodes,n->m_parent); - if(n->IsInternal()) - { - const int c0=findLinearSearch(nodes.nodes,n->m_childs[0]); - const int c1=findLinearSearch(nodes.nodes,n->m_childs[1]); - iwriter->WriteNode(n,i,p,c0,c1); - } - else - { - iwriter->WriteLeaf(n,i,p); - } - } - } - - // - void - BvDynamicTree::Clone(BvDynamicTree& dest,IClone* iclone) const - { - dest.Clear(); - if(m_root!=0) - { - vector stack; - stack.reserve(m_leaves); - stack.push_back(sStkCLN(m_root,0)); - do { - const size_t i=stack.size()-1; - const sStkCLN e=stack[i]; - NodeType* n= dest.createnode(e.parent,e.node->m_volume,e.node->m_data); - stack.pop_back(); - if(e.parent!=0) - e.parent->m_childs[i&1]=n; - else - dest.m_root=n; - if(e.node->IsInternal()) - { - stack.push_back(sStkCLN(e.node->m_childs[0],n)); - stack.push_back(sStkCLN(e.node->m_childs[1],n)); - } - else - { - iclone->CloneLeaf(n); - } - } while(!stack.empty()); - } - } - - // - int - BvDynamicTree::GetMaxDepth(const NodeType* node) - { - int depth=0; - if(node) getmaxdepth(node,1,depth); - return depth ; - } - - // - int - BvDynamicTree::CountLeaves(const NodeType* node) - { - if(node->IsInternal()) - return(CountLeaves(node->m_childs[0])+CountLeaves(node->m_childs[1])); - else - return(1); - } - - // - void - BvDynamicTree::ExtractLeaves(const NodeType* node,vector& leaves) - { - if(node->IsInternal()) - { - ExtractLeaves(node->m_childs[0],leaves); - ExtractLeaves(node->m_childs[1],leaves); - } - else - { - leaves.push_back(node); - } - } - - // - #if DBVT_ENABLE_BENCHMARK - - #include - #include - #include - - /* - q6600,2.4ghz - - /Ox /Ob2 /Oi /Ot /I "." /I "..\.." /I "..\..\src" /D "NDEBUG" /D "_LIB" /D "_WINDOWS" /D "_CRT_SECURE_NO_DEPRECATE" /D "_CRT_NONSTDC_NO_DEPRECATE" /D "WIN32" - /GF /FD /MT /GS- /Gy /arch:SSE2 /Zc:wchar_t- /Fp"..\..\out\release8\build\libbulletcollision\libbulletcollision.pch" - /Fo"..\..\out\release8\build\libbulletcollision\\" - /Fd"..\..\out\release8\build\libbulletcollision\bulletcollision.pdb" - /W3 /nologo /c /Wp64 /Zi /errorReport:prompt - - Benchmarking dbvt... - World scale: 100.000000 - Extents base: 1.000000 - Extents range: 4.000000 - Leaves: 8192 - sizeof(VolumeType): 32 bytes - sizeof(NodeType): 44 bytes - [1] VolumeType intersections: 3499 ms (-1%) - [2] VolumeType merges: 1934 ms (0%) - [3] BvDynamicTree::collideTT: 5485 ms (-21%) - [4] BvDynamicTree::collideTT self: 2814 ms (-20%) - [5] BvDynamicTree::collideTT xform: 7379 ms (-1%) - [6] BvDynamicTree::collideTT xform,self: 7270 ms (-2%) - [7] BvDynamicTree::rayTest: 6314 ms (0%),(332143 r/s) - [8] insert/remove: 2093 ms (0%),(1001983 ir/s) - [9] updates (teleport): 1879 ms (-3%),(1116100 u/s) - [10] updates (jitter): 1244 ms (-4%),(1685813 u/s) - [11] optimize (incremental): 2514 ms (0%),(1668000 o/s) - [12] VolumeType notequal: 3659 ms (0%) - [13] culling(OCL+fullsort): 2218 ms (0%),(461 t/s) - [14] culling(OCL+qsort): 3688 ms (5%),(2221 t/s) - [15] culling(KDOP+qsort): 1139 ms (-1%),(7192 t/s) - [16] insert/remove batch(256): 5092 ms (0%),(823704 bir/s) - [17] VolumeType select: 3419 ms (0%) - */ - - struct btDbvtBenchmark - { - struct NilPolicy : BvDynamicTree::ICollide - { - NilPolicy() : m_pcount(0),m_depth(-SIMD_INFINITY),m_checksort(true) {} - void Process(const NodeType*,const NodeType*) { ++m_pcount; } - void Process(const NodeType*) { ++m_pcount; } - void Process(const NodeType*,btScalar depth) - { - ++m_pcount; - if(m_checksort) - { if(depth>=m_depth) m_depth=depth; else printf("wrong depth: %f (should be >= %f)\r\n",depth,m_depth); } - } - int m_pcount; - btScalar m_depth; - bool m_checksort; - }; - struct P14 : BvDynamicTree::ICollide - { - struct Node - { - const NodeType* leaf; - btScalar depth; - }; - void Process(const NodeType* leaf,btScalar depth) - { - Node n; - n.leaf = leaf; - n.depth = depth; - } - static int sortfnc(const Node& a,const Node& b) - { - if(a.depthb.depth) return(-1); - return(0); - } - btAlignedObjectArray m_nodes; - }; - struct P15 : BvDynamicTree::ICollide - { - struct Node - { - const NodeType* leaf; - btScalar depth; - }; - void Process(const NodeType* leaf) - { - Node n; - n.leaf = leaf; - n.depth = dot(leaf->volume.GetCenter(),m_axis); - } - static int sortfnc(const Node& a,const Node& b) - { - if(a.depthb.depth) return(-1); - return(0); - } - btAlignedObjectArray m_nodes; - btAZ::Vector3 m_axis; - }; - static btScalar RandUnit() - { - return(rand()/(btScalar)RAND_MAX); - } - static btAZ::Vector3 RandAZ::Vector3() - { - return(btAZ::Vector3(RandUnit(),RandUnit(),RandUnit())); - } - static btAZ::Vector3 RandAZ::Vector3(btScalar cs) - { - return(RandAZ::Vector3()*cs-btAZ::Vector3(cs,cs,cs)/2); - } - static VolumeType RandVolume(btScalar cs,btScalar eb,btScalar es) - { - return(VolumeType::FromCE(RandAZ::Vector3(cs),btAZ::Vector3(eb,eb,eb)+RandAZ::Vector3()*es)); - } - static btTransform RandTransform(btScalar cs) - { - btTransform t; - t.setOrigin(RandAZ::Vector3(cs)); - t.setRotation(btQuaternion(RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2).normalized()); - return(t); - } - static void RandTree(btScalar cs,btScalar eb,btScalar es,int leaves,BvDynamicTree& dbvt) - { - dbvt.clear(); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i transforms; - btDbvtBenchmark::NilPolicy policy; - transforms.resize(cfgBenchmark5_Iterations); - for(int i=0;i transforms; - btDbvtBenchmark::NilPolicy policy; - transforms.resize(cfgBenchmark6_Iterations); - for(int i=0;i rayorg; - btAlignedObjectArray raydir; - btDbvtBenchmark::NilPolicy policy; - rayorg.resize(cfgBenchmark7_Iterations); - raydir.resize(cfgBenchmark7_Iterations); - for(int i=0;i leaves; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - dbvt.extractLeaves(dbvt.m_root,leaves); - printf("[9] updates (teleport): "); - wallclock.reset(); - for(int i=0;i(leaves[rand()%cfgLeaves]), - btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale)); - } - } - const int time=(int)wallclock.getTimeMilliseconds(); - const int up=cfgBenchmark9_Passes*cfgBenchmark9_Iterations; - printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark9_Reference)*100/time,up*1000/time); - } - if(cfgBenchmark10_Enable) - {// Benchmark 10 - srand(380843); - BvDynamicTree dbvt; - btAlignedObjectArray leaves; - btAlignedObjectArray vectors; - vectors.resize(cfgBenchmark10_Iterations); - for(int i=0;i(leaves[rand()%cfgLeaves]); - VolumeType v=VolumeType::FromMM(l->volume.GetMin()+d,l->volume.GetMax()+d); - dbvt.update(l,v); - } - } - const int time=(int)wallclock.getTimeMilliseconds(); - const int up=cfgBenchmark10_Passes*cfgBenchmark10_Iterations; - printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark10_Reference)*100/time,up*1000/time); - } - if(cfgBenchmark11_Enable) - {// Benchmark 11 - srand(380843); - BvDynamicTree dbvt; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - printf("[11] optimize (incremental): "); - wallclock.reset(); - for(int i=0;i volumes; - btAlignedObjectArray results; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - for(int i=0;i vectors; - btDbvtBenchmark::NilPolicy policy; - vectors.resize(cfgBenchmark13_Iterations); - for(int i=0;i vectors; - btDbvtBenchmark::P14 policy; - vectors.resize(cfgBenchmark14_Iterations); - for(int i=0;i vectors; - btDbvtBenchmark::P15 policy; - vectors.resize(cfgBenchmark15_Iterations); - for(int i=0;i batch; - btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); - dbvt.optimizeTopDown(); - batch.reserve(cfgBenchmark16_BatchCount); - printf("[16] Insert/remove batch(%u): ",cfgBenchmark16_BatchCount); - wallclock.reset(); - for(int i=0;i volumes; - btAlignedObjectArray results; - btAlignedObjectArray indices; - volumes.resize(cfgLeaves); - results.resize(cfgLeaves); - indices.resize(cfgLeaves); - for(int i=0;i -#include -#include - -#include -#include - -namespace GridMate -{ - namespace Internal - { - /** - * - */ - class DynamicTreeAabb : public AZ::Aabb - { - public: - GM_CLASS_ALLOCATOR(DynamicTreeAabb); - - AZ_FORCE_INLINE explicit DynamicTreeAabb() {} - AZ_FORCE_INLINE DynamicTreeAabb(const AZ::Aabb& aabb) : AZ::Aabb(aabb) {} - AZ_FORCE_INLINE explicit DynamicTreeAabb(const AZ::Vector3& min,const AZ::Vector3& max) : AZ::Aabb(AZ::Aabb::CreateFromMinMax(min,max)) {} - - AZ_FORCE_INLINE static DynamicTreeAabb CreateFromFacePoints(const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c) - { - DynamicTreeAabb vol(a,a); - vol.AddPoint(b); - vol.AddPoint(c); - return vol; - } - - AZ_FORCE_INLINE void SignedExpand(const AZ::Vector3& e) - { - AZ::Vector3 zero = AZ::Vector3::CreateZero(); - AZ::Vector3 mxE = m_max + e; - AZ::Vector3 miE = m_min + e; - m_max = AZ::Vector3::CreateSelectCmpGreater(e,zero,mxE,m_max ); - m_min = AZ::Vector3::CreateSelectCmpGreater(e,zero,m_min,miE); - } - AZ_FORCE_INLINE int Classify(const AZ::Vector3& n,const float o,int s) const - { - AZ::Vector3 pi, px; - switch(s) - { - case (0+0+0): px=m_min; - pi=m_max; break; - case (1+0+0): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());break; - case (0+2+0): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());break; - case (1+2+0): px=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());break; - case (0+0+4): px=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());break; - case (1+0+4): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());break; - case (0+2+4): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ()); - pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());break; - case (1+2+4): px=m_max; - pi=m_min;break; - } - - if (n.Dot(px) + o < 0.0f) - { - return -1; - } - if (n.Dot(pi) + o > 0.0f) - { - return 1; - } - - return 0; - } - AZ_FORCE_INLINE float ProjectMinimum(const AZ::Vector3& v, unsigned signs) const - { - const AZ::Vector3* b[]={&m_max,&m_min}; - const AZ::Vector3 p( b[(signs>>0)&1]->GetX(),b[(signs>>1)&1]->GetY(),b[(signs>>2)&1]->GetZ()); - return p.Dot(v); - } - - // Move the code here - AZ_FORCE_INLINE friend bool IntersectAabbAabb(const DynamicTreeAabb& a,const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend bool IntersectAabbPoint(const DynamicTreeAabb& a, const AZ::Vector3& b); - AZ_FORCE_INLINE friend bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b); - AZ_FORCE_INLINE friend float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend int Select(const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b); - AZ_FORCE_INLINE friend void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r); - AZ_FORCE_INLINE friend bool NotEqual(const DynamicTreeAabb& a, const DynamicTreeAabb& b); - private: - AZ_FORCE_INLINE void AddSpan(const AZ::Vector3& d, float& smi, float& smx) const - { - AZ::Vector3 vecZero = AZ::Vector3::CreateZero(); - AZ::Vector3 mxD = m_max*d; - AZ::Vector3 miD = m_min*d; - AZ::Vector3 smiAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,mxD,miD); - AZ::Vector3 smxAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,miD,mxD); - AZ::Vector3 vecOne = AZ::Vector3::CreateOne(); - // sum components - smi += smiAdd.Dot(vecOne); - smx += smxAdd.Dot(vecOne); - } - }; - - // - AZ_FORCE_INLINE bool IntersectAabbAabb(const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return a.Overlaps(b); - } - - AZ_FORCE_INLINE bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b) - { - //use plane normal to quickly select the nearest corner of the aabb - AZ::Vector3 testPoint = AZ::Vector3::CreateSelectCmpGreater(b.GetNormal(), AZ::Vector3::CreateZero(), a.GetMin(), a.GetMax()); - //test if nearest point is inside the plane - return b.GetPointDist(testPoint) <= 0.0f; - } - - // - AZ_FORCE_INLINE float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - const AZ::Vector3 d=(a.m_min+a.m_max)-(b.m_min+b.m_max); - // get abs and sum - return d.GetAbs().Dot(AZ::Vector3::CreateOne()); - } - - // - AZ_FORCE_INLINE int Select( const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return Proximity(o,a) < Proximity(o,b); - } - - // - AZ_FORCE_INLINE void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r) - { - r.m_min = AZ::Vector3::CreateSelectCmpGreater(b.m_min,a.m_min,a.m_min,b.m_min); - r.m_max = AZ::Vector3::CreateSelectCmpGreater(a.m_max,b.m_max,a.m_max,b.m_max); - } - - // - AZ_FORCE_INLINE bool NotEqual( const DynamicTreeAabb& a, const DynamicTreeAabb& b) - { - return (a.m_min != b.m_min || a.m_max != b.m_max); - } - - - /* NodeType */ - struct DynamicTreeNode - { - GM_CLASS_ALLOCATOR(DynamicTreeNode); - - DynamicTreeAabb m_volume; - DynamicTreeNode* m_parent; - AZ_FORCE_INLINE bool IsLeaf() const { return(m_childs[1]==0); } - AZ_FORCE_INLINE bool IsInternal() const { return(!IsLeaf()); } - union - { - DynamicTreeNode* m_childs[2]; - void* m_data; - int m_dataAsInt; - }; - }; - } - - /** - * Implementation of dynamic aabb tree, based on the bullet dynamic tree (btDbvt). - * - * The BvDynamicTree class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree). - * This BvDynamicTree is used for soft body collision detection and for the btDbvtBroadphase. It has a fast insert, remove and update of nodes. - * Unlike the BvTreeQuantized, nodes can be dynamically moved around, which allows for change in topology of the underlying data structure. - */ - class BvDynamicTree - { - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(BvDynamicTree); - - typedef Internal::DynamicTreeAabb VolumeType; - typedef Internal::DynamicTreeNode NodeType; - - typedef vector NodeArrayType; - typedef vector ConstNodeArrayType; - - private: - - /* Stack element */ - struct sStkNN - { - const NodeType* a; - const NodeType* b; - sStkNN() {} - sStkNN(const NodeType* na,const NodeType* nb) : a(na), b(nb) {} - }; - struct sStkNP - { - const NodeType* node; - int mask; - sStkNP(const NodeType* n, unsigned m) : node(n), mask(m) {} - }; - struct sStkNPS - { - const NodeType* node; - int mask; - float value; - sStkNPS() {} - sStkNPS(const NodeType* n, unsigned m, const float v) : node(n), mask(m), value(v) {} - }; - struct sStkCLN - { - const NodeType* node; - NodeType* parent; - sStkCLN(const NodeType* n, NodeType* p) : node(n), parent(p) {} - }; - - public: - /* ICollideCollector templated collectors should implement this functions or inherit from this class */ - struct ICollideCollector - { - void Process(const NodeType*, const NodeType*) {} - void Process(const NodeType*) {} - void Process(const NodeType* n, const float) { Process(n); } - bool Descent(const NodeType*) { return true; } - bool AllLeaves(const NodeType*) { return true; } - }; - - /* IWriter */ - struct IWriter - { - virtual ~IWriter() {} - virtual void Prepare(const NodeType* root,int numnodes) = 0; - virtual void WriteNode(const NodeType*, int index, int parent, int child0, int child1) = 0; - virtual void WriteLeaf(const NodeType*, int index, int parent) = 0; - }; - /* IClone */ - struct IClone - { - virtual ~IClone() {} - virtual void CloneLeaf(NodeType*) {} - }; - - // Constants - enum - { - SIMPLE_STACKSIZE = 64, - DOUBLE_STACKSIZE = SIMPLE_STACKSIZE * 2 - }; - - // Methods - BvDynamicTree(); - ~BvDynamicTree(); - - NodeType* GetRoot() const { return m_root; } - void Clear(); - bool Empty() const { return 0 == m_root; } - int GetNumLeaves() const { return m_leaves; } - void OptimizeBottomUp(); - void OptimizeTopDown(int bu_treshold = 128); - void OptimizeIncremental(int passes); - NodeType* Insert(const VolumeType& box,void* data); - void Update(NodeType* leaf, int lookahead=-1); - void Update(NodeType* leaf, VolumeType& volume); - bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity, const float margin); - bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity); - bool Update(NodeType* leaf, VolumeType& volume, const float margin); - void Remove(NodeType* leaf); - void Write(IWriter* iwriter) const; - void Clone(BvDynamicTree& dest, IClone* iclone=0) const; - static int GetMaxDepth(const NodeType* node); - static int CountLeaves(const NodeType* node); - static void ExtractLeaves(const NodeType* node, /*btAlignedObjectArray&*/vector& leaves); - #if DBVT_ENABLE_BENCHMARK - static void Benchmark(); - #else - static void Benchmark(){} - #endif - /** - * Collector should inherit from ICollide - */ - template - static inline void enumNodes( const NodeType* root, Collector& collector) - { - collector.Process(root); - if(root->IsInternal()) - { - enumNodes(root->m_childs[0],collector); - enumNodes(root->m_childs[1],collector); - } - } - template - static void enumLeaves( const NodeType* root,Collector& collector) - { - if(root->IsInternal()) - { - enumLeaves(root->m_childs[0],collector); - enumLeaves(root->m_childs[1],collector); - } - else - { - collector.Process(root); - } - } - template - void collideTT( const NodeType* root0,const NodeType* root1,Collector& collector) const - { - if(root0&&root1) - { - size_t depth=1; - size_t treshold=DOUBLE_STACKSIZE-4; - vector stkStack; - stkStack.resize(DOUBLE_STACKSIZE); - stkStack[0]=sStkNN(root0,root1); - do { - sStkNN p=stkStack[--depth]; - if(depth>treshold) - { - stkStack.resize(stkStack.size()*2); - treshold=stkStack.size()-4; - } - if(p.a==p.b) - { - if(p.a->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]); - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]); - } - } - else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume)) - { - if(p.a->IsInternal()) - { - if(p.b->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]); - } - else - { - stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b); - stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b); - } - } - else - { - if(p.b->IsInternal()) - { - stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]); - stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]); - } - else - { - collector.Process(p.a,p.b); - } - } - } - } while(depth); - } - } - template - void collideTTpersistentStack( const NodeType* root0, const NodeType* root1,Collector& collector) - { - if(root0&&root1) - { - size_t depth=1; - size_t treshold=DOUBLE_STACKSIZE-4; - - m_stkStack.resize(DOUBLE_STACKSIZE); - m_stkStack[0]=sStkNN(root0,root1); - do - { - sStkNN p=m_stkStack[--depth]; - if(depth>treshold) - { - m_stkStack.resize(m_stkStack.size()*2); - treshold=m_stkStack.size()-4; - } - if(p.a==p.b) - { - if(p.a->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]); - } - } - else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume)) - { - if(p.a->IsInternal()) - { - if(p.b->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]); - } - else - { - m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b); - m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b); - } - } - else - { - if(p.b->IsInternal()) - { - m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]); - m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]); - } - else - { - collector.Process(p.a,p.b); - } - } - } - } while(depth); - } - } - template - void collideTV( const NodeType* root, const VolumeType& volume, Collector& collector) const - { - if(root) - { -// ATTRIBUTE_ALIGNED16(VolumeType) volume(vol); -// btAlignedObjectArray stack; - AZStd::fixed_vector stack; - //stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(root); - do { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if(IntersectAabbAabb(n->m_volume,volume)) - { - if(n->IsInternal()) - { - stack.push_back(n->m_childs[0]); - stack.push_back(n->m_childs[1]); - } - else - { - collector.Process(n); - } - } - } while(!stack.empty()); - } - } - - template - void collideTP(const NodeType* root, const AZ::Plane& plane, Collector& collector) const - { - if (root) - { - AZStd::fixed_vector stack; - stack.push_back(root); - do - { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if (IntersectAabbPlane(n->m_volume, plane)) - { - if(n->IsInternal()) - { - stack.push_back(n->m_childs[0]); - stack.push_back(n->m_childs[1]); - } - else - { - collector.Process(n); - } - } - } while (!stack.empty()); - } - } - - ///rayTest is a re-entrant ray test, and can be called in parallel as long as the btAlignedAlloc is thread-safe (uses locking etc) - ///rayTest is slower than rayTestInternal, because it builds a local stack, using memory allocations, and it recomputes signs/rayDirectionInverses each time - template - static void rayTest( const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, Collector& collector) - { - if(root) - { - AZ::Vector3 ray = rayTo-rayFrom; - AZ::Vector3 rayDir = ray.GetNormalized(); - - ///what about division by zero? --> just set rayDirection[i] to INF/1e30 - AZ::Vector3 rayDirectionInverse = AZ::Vector3::CreateSelectCmpEqual(rayDir,AZ::Vector3::CreateZero(),AZ::Vector3(1e30),rayDir.GetReciprocal()); - - unsigned int signs[3];// = { rayDirectionInverse[0] < 0.0f, rayDirectionInverse[1] < 0.0f, rayDirectionInverse[2] < 0.0f }; - signs[0] = rayDirectionInverse.GetX() < 0.0f; - signs[1] = rayDirectionInverse.GetY() < 0.0f; - signs[2] = rayDirectionInverse.GetZ() < 0.0f; - - //float lambda_max = rayDir.Dot(ray); - - AZ::Vector3 resultNormal; - - //btAlignedObjectArray stack; - vector stack; - - int depth=1; - int treshold=DOUBLE_STACKSIZE-2; - - stack.resize(DOUBLE_STACKSIZE); - stack[0]=root; - AZ::Vector3 bounds[2]; - do { - const NodeType* node=stack[--depth]; - - bounds[0] = node->m_volume.GetMin(); - bounds[1] = node->m_volume.GetMax(); - - //float tmin = 1.0f; - //float lambda_min = 0.0f; - // todo.. - unsigned int result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/0; -#ifdef COMPARE_BTRAY_AABB2 - float param = 1.0f; - bool result2 = /*btRayAabb(rayFrom,rayTo,node->volume.GetMin(),node->volume.GetMax(),param,resultNormal)*/0; - AZ_Assert(result1 == result2, ""); -#endif //TEST_BTRAY_AABB2 - if(result1) - { - if(node->IsInternal()) - { - if(depth>treshold) - { - stack.resize(stack.size()*2); - treshold=stack.size()-2; - } - stack[depth++]=node->m_childs[0]; - stack[depth++]=node->m_childs[1]; - } - else - { - collector.Process(node); - } - } - } while(depth); - - } - } - - ///rayTestInternal is faster than rayTest, because it uses a persistent stack (to reduce dynamic memory allocations to a minimum) and it uses precomputed signs/rayInverseDirections - ///rayTestInternal is used by btDbvtBroadphase to accelerate world ray casts - template - void rayTestInternal(const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, const AZ::Vector3& rayDirectionInverse, unsigned int signs[3], const float lambda_max, const AZ::Vector3& aabbMin, const AZ::Vector3& aabbMax, Collector& collector) const - { - (void)rayFrom;(void)rayTo;(void)rayDirectionInverse;(void)signs;(void)lambda_max; - if(root) - { - AZ::Vector3 resultNormal; - - int depth=1; - int treshold=DOUBLE_STACKSIZE-2; - vector stack; - stack.resize(DOUBLE_STACKSIZE); - stack[0]=root; - AZ::Vector3 bounds[2]; - do - { - const NodeType* node=stack[--depth]; - bounds[0] = node->m_volume.GetMin()+aabbMin; - bounds[1] = node->m_volume.GetMax()+aabbMax; - - //float tmin = 1.0f; - //float lambda_min = 0.0f; - unsigned int result1=false; - // todo... - result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/false; - if(result1) - { - if(node->IsInternal()) - { - if(depth>treshold) - { - stack.resize(stack.size()*2); - treshold=stack.size()-2; - } - stack[depth++]=node->m_childs[0]; - stack[depth++]=node->m_childs[1]; - } - else - { - collector.Process(node); - } - } - } while(depth); - } - } - - template - static void collideKDOP(const NodeType* root, const AZ::Vector3* normals, const float* offsets, int count, Collector& collector) - { - (void)root;(void)normals;(void)offsets;(void)count;(void)collector; -/* if(root) - { - const int inside=(1< stack; - int signs[sizeof(unsigned)*8]; - btAssert(count=0)?1:0)+ - ((normals[i].y()>=0)?2:0)+ - ((normals[i].z()>=0)?4:0); - } - stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(sStkNP(root,0)); - do { - sStkNP se=stack[stack.size()-1]; - bool out=false; - stack.pop_back(); - for(int i=0,j=1;(!out)&&(ivolume.Classify(normals[i],offsets[i],signs[i]); - switch(side) - { - case -1: out=true;break; - case +1: se.mask|=j;break; - } - } - } - if(!out) - { - if((se.mask!=inside)&&(se.node->isinternal())) - { - stack.push_back(sStkNP(se.node->childs[0],se.mask)); - stack.push_back(sStkNP(se.node->childs[1],se.mask)); - } - else - { - if(policy.AllLeaves(se.node)) enumLeaves(se.node,policy); - } - } - } while(!stack.empty()); - }*/ - } - template - static void collideOCL( const NodeType* root, const AZ::Vector3* normals, const float* offsets, const AZ::Vector3& sortaxis, int count, Collector& collector, bool fullsort=true) - { - (void)root;(void)normals;(void)offsets;(void)sortaxis;(void)count;(void)offsets;(void)collector;(void)fullsort; -/* if(root) - { - const unsigned srtsgns=(sortaxis[0]>=0?1:0)+ - (sortaxis[1]>=0?2:0)+ - (sortaxis[2]>=0?4:0); - const int inside=(1< stock; - btAlignedObjectArray ifree; - btAlignedObjectArray stack; - int signs[sizeof(unsigned)*8]; - btAssert(count=0)?1:0)+ - ((normals[i].y()>=0)?2:0)+ - ((normals[i].z()>=0)?4:0); - } - stock.reserve(SIMPLE_STACKSIZE); - stack.reserve(SIMPLE_STACKSIZE); - ifree.reserve(SIMPLE_STACKSIZE); - stack.push_back(allocate(ifree,stock,sStkNPS(root,0,root->volume.ProjectMinimum(sortaxis,srtsgns)))); - do { - const int id=stack[stack.size()-1]; - sStkNPS se=stock[id]; - stack.pop_back();ifree.push_back(id); - if(se.mask!=inside) - { - bool out=false; - for(int i=0,j=1;(!out)&&(ivolume.Classify(normals[i],offsets[i],signs[i]); - switch(side) - { - case -1: out=true;break; - case +1: se.mask|=j;break; - } - } - } - if(out) continue; - } - if(policy.Descent(se.node)) - { - if(se.node->isinternal()) - { - const NodeType* pns[]={ se.node->childs[0],se.node->childs[1]}; - sStkNPS nes[]={ sStkNPS(pns[0],se.mask,pns[0]->volume.ProjectMinimum(sortaxis,srtsgns)), - sStkNPS(pns[1],se.mask,pns[1]->volume.ProjectMinimum(sortaxis,srtsgns))}; - const int q=nes[0].value0)) - { - // Insert 0 - j=nearest(&stack[0],&stock[0],nes[q].value,0,stack.size()); - stack.push_back(0); -#if DBVT_USE_MEMMOVE - memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1)); -#else - for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1]; -#endif - stack[j]=allocate(ifree,stock,nes[q]); - // Insert 1 - j=nearest(&stack[0],&stock[0],nes[1-q].value,j,stack.size()); - stack.push_back(0); -#if DBVT_USE_MEMMOVE - memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1)); -#else - for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1]; -#endif - stack[j]=allocate(ifree,stock,nes[1-q]); - } - else - { - stack.push_back(allocate(ifree,stock,nes[q])); - stack.push_back(allocate(ifree,stock,nes[1-q])); - } - } - else - { - policy.Process(se.node,se.value); - } - } - } while(stack.size()); - }*/ - } - - template - static void collideTU(const NodeType* root, Collector& collector) - { - (void)root;(void)collector; -/* if(root) - { - btAlignedObjectArray stack; - stack.reserve(SIMPLE_STACKSIZE); - stack.push_back(root); - do { - const NodeType* n=stack[stack.size()-1]; - stack.pop_back(); - if(policy.Descent(n)) - { - if(n->isinternal()) - { stack.push_back(n->childs[0]);stack.push_back(n->childs[1]); } - else - { policy.Process(n); } - } - } while(stack.size()>0); - }*/ - } - - private: - BvDynamicTree(const BvDynamicTree&) {} - - // Helpers - //static AZ_FORCE_INLINE int nearest(const int* i,const BvDynamicTree::sStkNPS* a,const float& v,int l,int h) - //{ - // int m=0; - // while(l>1; - // if(a[i[m]].value>=v) l=m+1; else h=m; - // } - // return h; - //} - //static AZ_FORCE_INLINE int allocate( int_fixed_stack_type& ifree, stknps_fixed_stack_type& stock, const sStkNPS& value) - //{ - // int i; - // if( !ifree.empty() ) - // { - // i=ifree[ifree.size()-1]; - // ifree.pop_back(); - // stock[i]=value; - // } - // else - // { - // i=stock.size(); - // stock.push_back(value); - // } - // return i; - //} - // - - AZ_FORCE_INLINE void deletenode( NodeType* node) - { - //btAlignedFree(pdbvt->m_free); - delete m_free; - m_free=node; - } - - void recursedeletenode( NodeType* node) - { - if(!node->IsLeaf()) - { - recursedeletenode(node->m_childs[0]); - recursedeletenode(node->m_childs[1]); - } - - if( node == m_root ) m_root=0; - deletenode(node); - } - - - AZ_FORCE_INLINE NodeType* createnode( NodeType* parent, void* data) - { - NodeType* node; - if(m_free) - { node=m_free;m_free=0; } - else - { node = aznew NodeType(); } - node->m_parent = parent; - node->m_data = data; - node->m_childs[1] = 0; - return node; - } - AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume, void* data) - { - NodeType* node = createnode(parent,data); - node->m_volume=volume; - return node; - } - // - AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume0, const VolumeType& volume1, void* data) - { - NodeType* node = createnode(parent,data); - Merge(volume0,volume1,node->m_volume); - return node; - } - void insertleaf( NodeType* root, NodeType* leaf); - NodeType* removeleaf( NodeType* leaf); - void fetchleaves(NodeType* root,NodeArrayType& leaves,int depth=-1); - void split(const NodeArrayType& leaves,NodeArrayType& left,NodeArrayType& right,const AZ::Vector3& org,const AZ::Vector3& axis); - VolumeType bounds(const NodeArrayType& leaves); - void bottomup( NodeArrayType& leaves ); - NodeType* topdown(NodeArrayType& leaves,int bu_treshold); - AZ_FORCE_INLINE NodeType* sort(NodeType* n,NodeType*& r); - - NodeType* m_root; - NodeType* m_free; - int m_lkhd; - int m_leaves; - unsigned m_opath; - - //btAlignedObjectArray m_stkStack; - // Profile and choose static or dynamic vector. - typedef AZStd::fixed_vector stknn_fixed_stack_type; - typedef AZStd::fixed_vector int_fixed_stack_type; - typedef AZStd::fixed_vector stknps_fixed_stack_type; - - stknn_fixed_stack_type m_stkStack; - }; -} - -#endif // RR_DYNAMIC_BOUNDING_VOLUME_TREE_H -#pragma once diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp b/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp deleted file mode 100644 index 13a0151bc7..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include - -#include -#include -#include - -#include -#include - -// for highly verbose internal debugging -//#define INTERNAL_DEBUG_PROXIMITY - -namespace GridMate -{ - - void ProximityInterestChunk::OnReplicaActivate(const ReplicaContext& rc) - { - m_interestHandler = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4))); - AZ_Warning("GridMate", m_interestHandler, "No proximity interest handler in the user context"); - - if (m_interestHandler) - { - m_interestHandler->OnNewRulesChunk(this, rc.m_peer); - } - } - - void ProximityInterestChunk::OnReplicaDeactivate(const ReplicaContext& rc) - { - if (rc.m_peer && m_interestHandler) - { - m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer); - } - } - - bool ProximityInterestChunk::AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx) - { - if (IsProxy()) - { - auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer); - rulePtr->Set(bbox); - m_rules.insert(AZStd::make_pair(netId, rulePtr)); - } - - return true; - } - - bool ProximityInterestChunk::RemoveRuleFn(RuleNetworkId netId, const RpcContext&) - { - if (IsProxy()) - { - m_rules.erase(netId); - } - - return true; - } - - bool ProximityInterestChunk::UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&) - { - if (IsProxy()) - { - auto it = m_rules.find(netId); - if (it != m_rules.end()) - { - it->second->Set(bbox); - } - } - - return true; - } - - bool ProximityInterestChunk::AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&) - { - ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId); - if (peerChunk) - { - auto it = peerChunk->m_rules.find(netId); - if (it == peerChunk->m_rules.end()) - { - auto rulePtr = m_interestHandler->CreateRule(peerId); - peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr)); - rulePtr->Set(bbox); - } - } - return false; - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterest - */ - ProximityInterest::ProximityInterest(ProximityInterestHandler* handler) - : m_handler(handler) - , m_bbox(AZ::Aabb::CreateNull()) - { - AZ_Assert(m_handler, "Invalid interest handler"); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestRule - */ - void ProximityInterestRule::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateRule(this); - } - - void ProximityInterestRule::Destroy() - { - m_handler->DestroyRule(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestAttribute - */ - void ProximityInterestAttribute::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateAttribute(this); - } - - void ProximityInterestAttribute::Destroy() - { - m_handler->DestroyAttribute(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestHandler - */ - ProximityInterestHandler::ProximityInterestHandler() - : m_im(nullptr) - , m_rm(nullptr) - , m_lastRuleNetId(0) - , m_rulesReplica(nullptr) - { - m_attributeWorld = AZStd::make_unique(); - AZ_Assert(m_attributeWorld, "Out of memory"); - } - - ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId) - { - ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId()); - if (m_rm && peerId == m_rm->GetLocalPeerId()) - { - m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get()); - } - - CreateAndInsertIntoSpatialStructure(rulePtr); - - return rulePtr; - } - - ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId) - { - auto newAttribute = aznew ProximityInterestAttribute(this, replicaId); - AZ_Assert(newAttribute, "Out of memory"); - - CreateAndInsertIntoSpatialStructure(newAttribute); - - return newAttribute; - } - - void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule) - { - //TODO: should be pool-allocated - delete rule; - } - - void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId()); - } - - MarkAttributesDirtyInRule(rule); - - rule->m_bbox = AZ::Aabb::CreateNull(); - m_removedRules.insert(rule); - m_localRules.erase(rule); - } - - void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get()); - } - - m_dirtyRules.insert(rule); - } - - void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib) - { - delete attrib; - } - - void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib) - { - RemoveFromSpatialStructure(attrib); - - m_attributes.erase(attrib); - m_removedAttributes.insert(attrib); - } - - void ProximityInterestHandler::RemoveFromSpatialStructure(ProximityInterestAttribute* attribute) - { - attribute->m_bbox = AZ::Aabb::CreateNull(); - m_attributeWorld->Remove(attribute->GetNode()); - attribute->SetNode(nullptr); - } - - void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib) - { - auto node = attrib->GetNode(); - AZ_Assert(node, "Attribute wasn't created correctly"); - node->m_volume = attrib->Get(); - m_attributeWorld->Update(node); - - m_dirtyAttributes.insert(attrib); - } - - void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - if (chunk != m_rulesReplica) // non-local - { - m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk)); - - for (auto& rule : m_localRules) - { - chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get()); - } - } - } - - void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - (void)chunk; - m_peerChunks.erase(peer->GetId()); - } - - RuleNetworkId ProximityInterestHandler::GetNewRuleNetId() - { - ++m_lastRuleNetId; - - if (m_rulesReplica) - { - return m_rulesReplica->GetReplicaId() | (static_cast(m_lastRuleNetId) << 32); - } - - return (static_cast(m_lastRuleNetId) << 32); - } - - ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId) - { - auto it = m_peerChunks.find(peerId); - if (it == m_peerChunks.end()) - { - return nullptr; - } - - return it->second; - } - - const InterestMatchResult& ProximityInterestHandler::GetLastResult() - { - return m_resultCache; - } - - ProximityInterestHandler::RuleSet& ProximityInterestHandler::GetAffectedRules() - { - /* - * The expectation that lots of attributes will change frequently, - * so there is no point in trying to optimize cases - * where only a few attributes have changed. - */ - if (m_dirtyAttributes.empty() && !m_dirtyRules.empty()) - { - return m_dirtyRules; - } - - /* - * Assuming all rules might have been affected. - * - * There is an optimization chance here if the number of rules is large, as in 1,000+ rules. - * To handle such scale we would need another spatial structure for rules. - */ - return m_localRules; - } - - void ProximityInterestHandler::GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes) - { - m_attributeWorld->Query(rule->Get(), nodes); - } - - void ProximityInterestHandler::ClearDirtyState() - { - m_dirtyAttributes.clear(); - m_dirtyRules.clear(); - } - - void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute) - { - m_attributes.insert(attribute); - SpatialIndex::Node* node = m_attributeWorld->Insert(attribute->Get(), attribute); - attribute->SetNode(node); - } - - void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule) - { - m_localRules.insert(rule); - } - - void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result) - { - /* - * The goal is to return all dirty attributes that were either dirty because: - * 1) they changed which rules have apply to - * 2) rules have changed and no longer apply to those attributes - * and thus resulted in different peer(s) associated with a given replica. - */ - - const RuleSet& rules = GetAffectedRules(); - - for (auto& dirtyAttribute : m_dirtyAttributes) - { - result.insert(dirtyAttribute->GetReplicaId()); - } - - /* - * The exectation is to have a lot more attributes than rules. - * The amount of rules should grow linear with amount of peers, - * so it should be OK to iterate through all rules each update. - */ - for (auto& rule : rules) - { - CheckChangesForRule(rule, result); - } - - for (auto& removedRule : m_removedRules) - { - FreeRule(removedRule); - } - m_removedRules.clear(); - - // mark removed attribute as having no peers - for (auto& removedAttribute : m_removedAttributes) - { - result.insert(removedAttribute->GetReplicaId()); - FreeAttribute(removedAttribute); - } - m_removedAttributes.clear(); - } - - void ProximityInterestHandler::CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result) - { - SpatialIndex::NodeCollector collector; - GetAttributesWithinRule(rule, collector); - - auto peerId = rule->GetPeerId(); - for (ProximityInterestAttribute* attr : collector.GetNodes()) - { - AZ_Assert(attr, "bad node?"); - - auto findIt = result.find(attr->GetReplicaId()); - if (findIt != result.end()) - { - findIt->second.insert(peerId); - } - else - { - auto resultIt = result.insert(attr->GetReplicaId()); - AZ_Assert(resultIt.second, "Successfully inserted"); - resultIt.first->second.insert(peerId); - } - } - } - - void ProximityInterestHandler::MarkAttributesDirtyInRule(ProximityInterestRule* rule) - { - SpatialIndex::NodeCollector collector; - GetAttributesWithinRule(rule, collector); - - for (ProximityInterestAttribute* attr : collector.GetNodes()) - { - AZ_Assert(attr, "bad node?"); - - UpdateAttribute(attr); - } - } - - void ProximityInterestHandler::ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after) - { - m_resultCache.clear(); - -#if defined(INTERNAL_DEBUG_PROXIMITY) - before.PrintMatchResult("before"); - after.PrintMatchResult("after"); -#endif - - /* - * 'after' contains only the stuff that might have changed - */ - for (auto& possiblyDirty : after) - { - ReplicaId repId = possiblyDirty.first; - const InterestPeerSet& peerSet = possiblyDirty.second; - - auto foundInBefore = before.find(repId); - if (foundInBefore != before.end()) - { - if (!HasSamePeers(foundInBefore->second, peerSet)) - { - // was in the last calculation but has a different peer set now - m_resultCache.insert(AZStd::make_pair(repId, peerSet)); - } - } - else - { - // since it wasn't present during last calculation - m_resultCache.insert(AZStd::make_pair(repId, peerSet)); - } - } - - // Mark attributes (replicas) for removal that have not moved but a rule (clients) no longer sees it - for (auto& possiblyDirty : before) - { - ReplicaId repId = possiblyDirty.first; - - const auto foundInAfter = after.find(repId); - /* - * If the prior state was a replica A present on peer X: "A{X}", and now A should no longer be present on any peer: "A{}" - * then by the rules of InterestHandlers interacting with InterestManager, we should return in @m_resultCache the following: - * - * A{} - indicating that replica A must be removed all peers. - * - * On the next pass, the prior state would be: "A{}" and the current state would be "A{}" as well. At that point, we have - * already sent the update to remove A from X, so @m_resultCache should no longer mention A at all. - */ - if (foundInAfter == after.end() && !possiblyDirty.second.empty() /* "not A{}" see the above comment */) - { - m_resultCache.insert(AZStd::make_pair(repId, InterestPeerSet())); - } - } - -#if defined(INTERNAL_DEBUG_PROXIMITY) - m_resultCache.PrintMatchResult("changes"); -#endif - - } - - bool ProximityInterestHandler::HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another) - { - if (one.size() != another.size()) - { - return false; - } - - for (auto& peerFromOne : one) - { - if (another.find(peerFromOne) == another.end()) - { - return false; - } - } - - // Safe to assume it's the same sets since all entries are unique in a peer sets - return true; - } - - void ProximityInterestHandler::Update() - { - InterestMatchResult newResult; - - UpdateInternal(newResult); - ProduceChanges(m_lastResult, newResult); - - m_lastResult = std::move(newResult); - ClearDirtyState(); - } - - void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager) - { - AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager); - AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n"); - AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n"); - m_im = manager; - m_rm = m_im->GetReplicaManager(); - m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this); - - auto replica = Replica::CreateReplica("ProximityInterestHandlerRules"); - m_rulesReplica = CreateAndAttachReplicaChunk(replica); - m_rm->AddPrimary(replica); - } - - void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager) - { - (void)manager; - AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im); - AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n"); - m_rulesReplica = nullptr; - m_im = nullptr; - m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)); - m_rm = nullptr; - - for (auto& chunk : m_peerChunks) - { - chunk.second->m_interestHandler = nullptr; - } - m_peerChunks.clear(); - - ClearDirtyState(); - DestroyAll(); - - m_resultCache.clear(); - } - - void ProximityInterestHandler::DestroyAll() - { - for (ProximityInterestRule* rule : m_localRules) - { - FreeRule(rule); - } - m_localRules.clear(); - - for (ProximityInterestAttribute* attr : m_attributes) - { - FreeAttribute(attr); - } - m_attributes.clear(); - - for (auto& removedRule : m_removedRules) - { - FreeRule(removedRule); - } - m_removedRules.clear(); - - for (auto& removedAttribute : m_removedAttributes) - { - FreeAttribute(removedAttribute); - } - m_removedAttributes.clear(); - } - - /////////////////////////////////////////////////////////////////////////// - ProximityInterestHandler::~ProximityInterestHandler() - { - /* - * If a handler was registered with a InterestManager, then InterestManager ought to have called OnRulesHandlerUnregistered - * but this is a safety pre-caution. - */ - DestroyAll(); - } - - SpatialIndex::SpatialIndex() - { - m_tree.reset(aznew GridMate::BvDynamicTree()); - } - - void SpatialIndex::Remove(Node* node) - { - m_tree->Remove(node); - } - - void SpatialIndex::Update(Node* node) - { - m_tree->Update(node); - } - - SpatialIndex::Node* SpatialIndex::Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute) - { - return m_tree->Insert(get, attribute); - } - - void SpatialIndex::Query(const AZ::Aabb& shape, NodeCollector& nodes) - { - m_tree->collideTV(m_tree->GetRoot(), shape, nodes); - } -} diff --git a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h b/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h deleted file mode 100644 index 6659d44f44..0000000000 --- a/Code/Framework/GridMate/GridMate/Replica/Interest/ProximityInterestHandler.h +++ /dev/null @@ -1,314 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#ifndef GM_REPLICA_PROXIMITYINTERESTHANDLER_H -#define GM_REPLICA_PROXIMITYINTERESTHANDLER_H - -#include -#include -#include -#include -#include - -#include -#include - -namespace GridMate -{ - class ProximityInterestHandler; - class ProximityInterestAttribute; - - /* - * Base interest - */ - class ProximityInterest - { - friend class ProximityInterestHandler; - - public: - const AZ::Aabb& Get() const { return m_bbox; } - - protected: - explicit ProximityInterest(ProximityInterestHandler* handler); - - ProximityInterestHandler* m_handler; - AZ::Aabb m_bbox; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity rule - */ - class ProximityInterestRule - : public InterestRule - , public ProximityInterest - { - friend class ProximityInterestHandler; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestRule); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId) - : InterestRule(peerId, netId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - class SpatialIndex - { - public: - typedef Internal::DynamicTreeNode Node; - - class NodeCollector - { - typedef AZStd::vector Type; - - public: - void Process(const Internal::DynamicTreeNode* node) - { - m_nodes.push_back(reinterpret_cast(node->m_data)); - } - - const Type& GetNodes() const - { - return m_nodes; - } - - private: - Type m_nodes; - }; - - SpatialIndex(); - ~SpatialIndex() = default; - - AZ_FORCE_INLINE void Remove(Node* node); - AZ_FORCE_INLINE void Update(Node* node); - AZ_FORCE_INLINE Node* Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void Query(const AZ::Aabb& get, NodeCollector& nodes); - - private: - AZStd::unique_ptr m_tree; - }; - - /* - * Proximity attribute - */ - class ProximityInterestAttribute - : public InterestAttribute - , public ProximityInterest - { - friend class ProximityInterestHandler; - template friend class InterestPtr; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestAttribute); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId) - : InterestAttribute(repId) - , ProximityInterest(handler) - , m_worldNode(nullptr) - {} - - void Destroy(); - - void SetNode(SpatialIndex::Node* node) { m_worldNode = node; } - SpatialIndex::Node* GetNode() const { return m_worldNode; } - SpatialIndex::Node* m_worldNode; ///< non-owning pointer - }; - /////////////////////////////////////////////////////////////////////////// - - class ProximityInterestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(ProximityInterestChunk); - - // ReplicaChunk - typedef AZStd::intrusive_ptr Ptr; - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override { return true; } - static const char* GetChunkName() { return "ProximityInterestChunk"; } - - ProximityInterestChunk() - : AddRuleRpc("AddRule") - , RemoveRuleRpc("RemoveRule") - , UpdateRuleRpc("UpdateRule") - , AddRuleForPeerRpc("AddRuleForPeerRpc") - , m_interestHandler(nullptr) - { - } - - void OnReplicaActivate(const ReplicaContext& rc) override; - void OnReplicaDeactivate(const ReplicaContext& rc) override; - - bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx); - bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&); - bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&); - bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&); - - Rpc, RpcArg>::BindInterface AddRuleRpc; - Rpc>::BindInterface RemoveRuleRpc; - Rpc, RpcArg>::BindInterface UpdateRuleRpc; - - Rpc, RpcArg, RpcArg>::BindInterface AddRuleForPeerRpc; - - unordered_map m_rules; - ProximityInterestHandler* m_interestHandler; - }; - - /* - * Rules handler - */ - class ProximityInterestHandler - : public BaseRulesHandler - { - friend class ProximityInterestRule; - friend class ProximityInterestAttribute; - friend class ProximityInterestChunk; - - public: - - typedef unordered_set AttributeSet; - typedef unordered_set RuleSet; - - GM_CLASS_ALLOCATOR(ProximityInterestHandler); - - ProximityInterestHandler(); - ~ProximityInterestHandler(); - - /* - * Creates new proximity rule and binds it to the peer. - * Note: the lifetime of the created rule is tied to the lifetime of this handler. - */ - ProximityInterestRule::Ptr CreateRule(PeerId peerId); - - /* - * Creates new proximity attribute and binds it to the replica. - * Note: the lifetime of the created attribute is tied to the lifetime of this handler. - */ - ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId); - - // Calculates rules and attributes matches - void Update() override; - - // Returns last recalculated results - const InterestMatchResult& GetLastResult() override; - - // Returns the manager it's bound to - InterestManager* GetManager() override { return m_im; } - - // Rules that this handler is aware of - const RuleSet& GetLocalRules() const { return m_localRules; } - - private: - - // BaseRulesHandler - void OnRulesHandlerRegistered(InterestManager* manager) override; - void OnRulesHandlerUnregistered(InterestManager* manager) override; - - void DestroyRule(ProximityInterestRule* rule); - void FreeRule(ProximityInterestRule* rule); - void UpdateRule(ProximityInterestRule* rule); - - void DestroyAttribute(ProximityInterestAttribute* attrib); - void FreeAttribute(ProximityInterestAttribute* attrib); - void UpdateAttribute(ProximityInterestAttribute* attrib); - - void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - - RuleNetworkId GetNewRuleNetId(); - - ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId); - - void DestroyAll(); - - InterestManager* m_im; - ReplicaManager* m_rm; - - AZ::u32 m_lastRuleNetId; - - unordered_map m_peerChunks; - - RuleSet m_localRules; - RuleSet m_removedRules; - RuleSet m_dirtyRules; - - AttributeSet m_attributes; - AttributeSet m_removedAttributes; - AttributeSet m_dirtyAttributes; - - ProximityInterestChunk* m_rulesReplica; - - // collection of all known attributes - AZStd::unique_ptr m_attributeWorld; - - InterestMatchResult m_resultCache; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // internal processing helpers - AZ_FORCE_INLINE RuleSet& GetAffectedRules(); - AZ_FORCE_INLINE void GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes); - AZ_FORCE_INLINE void ClearDirtyState(); - - AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void RemoveFromSpatialStructure(ProximityInterestAttribute* attribute); - AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule); - - void UpdateInternal(InterestMatchResult& result); - void CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result); - void MarkAttributesDirtyInRule(ProximityInterestRule* rule); - - static bool HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another); - void ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after); - - InterestMatchResult m_lastResult; - /////////////////////////////////////////////////////////////////////////////////////////////////// - }; - /////////////////////////////////////////////////////////////////////////// -} - -#endif // GM_REPLICA_PROXIMITYINTERESTHANDLER_H diff --git a/Code/Framework/GridMate/GridMate/gridmate_files.cmake b/Code/Framework/GridMate/GridMate/gridmate_files.cmake index 7cc65ff69a..615ce53027 100644 --- a/Code/Framework/GridMate/GridMate/gridmate_files.cmake +++ b/Code/Framework/GridMate/GridMate/gridmate_files.cmake @@ -100,14 +100,10 @@ set(FILES Replica/Tasks/ReplicaPriorityPolicy.h Replica/Interest/BitmaskInterestHandler.cpp Replica/Interest/BitmaskInterestHandler.h - Replica/Interest/ProximityInterestHandler.cpp - Replica/Interest/ProximityInterestHandler.h Replica/Interest/InterestDefs.h Replica/Interest/InterestManager.cpp Replica/Interest/InterestManager.h Replica/Interest/InterestQueryResult.h - Replica/Interest/BvDynamicTree.cpp - Replica/Interest/BvDynamicTree.h Replica/Interest/RulesHandler.h Serialize/Buffer.cpp Serialize/Buffer.h diff --git a/Code/Framework/GridMate/Tests/Interest.cpp b/Code/Framework/GridMate/Tests/Interest.cpp deleted file mode 100644 index c449c92083..0000000000 --- a/Code/Framework/GridMate/Tests/Interest.cpp +++ /dev/null @@ -1,1823 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ -#include "Tests.h" -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace GridMate; - -// An easy switch to use an old brute force ProximityInterestHandler for performance comparison. -#if 0 -namespace GridMate -{ - /* - * Base interest - */ - class ProximityInterest - { - friend class ProximityInterestHandler; - - public: - const AZ::Aabb& Get() const { return m_bbox; } - - protected: - explicit ProximityInterest(ProximityInterestHandler* handler); - - ProximityInterestHandler* m_handler; - AZ::Aabb m_bbox; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity rule - */ - class ProximityInterestRule - : public InterestRule - , public ProximityInterest - { - friend class ProximityInterestHandler; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestRule); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId) - : InterestRule(peerId, netId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Proximity attribute - */ - class ProximityInterestAttribute - : public InterestAttribute - , public ProximityInterest - { - friend class ProximityInterestHandler; - template friend class InterestPtr; - - public: - using Ptr = AZStd::intrusive_ptr; - - GM_CLASS_ALLOCATOR(ProximityInterestAttribute); - - void Set(const AZ::Aabb& bbox); - - private: - - // Intrusive ptr - template - friend struct AZStd::IntrusivePtrCountPolicy; - unsigned int m_refCount = 0; - AZ_FORCE_INLINE void add_ref() { ++m_refCount; } - AZ_FORCE_INLINE void release() { Destroy(); } - AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; } - /////////////////////////////////////////////////////////////////////////// - - ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId) - : InterestAttribute(repId) - , ProximityInterest(handler) - {} - - void Destroy(); - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * Rules handler - */ - class ProximityInterestHandler - : public BaseRulesHandler - { - friend class ProximityInterestRule; - friend class ProximityInterestAttribute; - friend class ProximityInterestChunk; - - public: - - typedef unordered_set AttributeSet; - typedef unordered_set RuleSet; - - GM_CLASS_ALLOCATOR(ProximityInterestHandler); - - ProximityInterestHandler(); - - // Creates new proximity rule and binds it to the peer - ProximityInterestRule::Ptr CreateRule(PeerId peerId); - - // Creates new proximity attribute and binds it to the replica - ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId); - - // Calculates rules and attributes matches - void Update() override; - - // Returns last recalculated results - const InterestMatchResult& GetLastResult() override; - - // Returns manager its bound with - InterestManager* GetManager() override { return m_im; } - - const RuleSet& GetLocalRules() { return m_localRules; } - - private: - void UpdateInternal(InterestMatchResult& result); - - // BaseRulesHandler - void OnRulesHandlerRegistered(InterestManager* manager) override; - void OnRulesHandlerUnregistered(InterestManager* manager) override; - - void DestroyRule(ProximityInterestRule* rule); - void FreeRule(ProximityInterestRule* rule); - void UpdateRule(ProximityInterestRule* rule); - - void DestroyAttribute(ProximityInterestAttribute* attrib); - void FreeAttribute(ProximityInterestAttribute* attrib); - void UpdateAttribute(ProximityInterestAttribute* attrib); - - - void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer); - - RuleNetworkId GetNewRuleNetId(); - - ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId); - - InterestManager* m_im; - ReplicaManager* m_rm; - - AZ::u32 m_lastRuleNetId; - - unordered_map m_peerChunks; - RuleSet m_localRules; - - AttributeSet m_attributes; - RuleSet m_rules; - - ProximityInterestChunk* m_rulesReplica; - - InterestMatchResult m_resultCache; - }; - /////////////////////////////////////////////////////////////////////////// - - class ProximityInterestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(ProximityInterestChunk); - - // ReplicaChunk - typedef AZStd::intrusive_ptr Ptr; - bool IsReplicaMigratable() override { return false; } - static const char* GetChunkName() { return "ProximityInterestChunk"; } - bool IsBroadcast() { return true; } - /////////////////////////////////////////////////////////////////////////// - - - ProximityInterestChunk() - : m_interestHandler(nullptr) - , AddRuleRpc("AddRule") - , RemoveRuleRpc("RemoveRule") - , UpdateRuleRpc("UpdateRule") - , AddRuleForPeerRpc("AddRuleForPeerRpc") - { - - } - - void OnReplicaActivate(const ReplicaContext& rc) override - { - m_interestHandler = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4))); - AZ_Assert(m_interestHandler, "No proximity interest handler in the user context"); - - if (m_interestHandler) - { - m_interestHandler->OnNewRulesChunk(this, rc.m_peer); - } - } - - void OnReplicaDeactivate(const ReplicaContext& rc) override - { - if (rc.m_peer && m_interestHandler) - { - m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer); - } - } - - bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx) - { - if (IsProxy()) - { - auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer); - rulePtr->Set(bbox); - m_rules.insert(AZStd::make_pair(netId, rulePtr)); - } - - return true; - } - - bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&) - { - if (IsProxy()) - { - m_rules.erase(netId); - } - - return true; - } - - bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&) - { - if (IsProxy()) - { - auto it = m_rules.find(netId); - if (it != m_rules.end()) - { - it->second->Set(bbox); - } - } - - return true; - } - - bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&) - { - ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId); - if (peerChunk) - { - auto it = peerChunk->m_rules.find(netId); - if (it == peerChunk->m_rules.end()) - { - auto rulePtr = m_interestHandler->CreateRule(peerId); - peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr)); - rulePtr->Set(bbox); - } - } - return false; - } - - Rpc, RpcArg>::BindInterface AddRuleRpc; - Rpc>::BindInterface RemoveRuleRpc; - Rpc, RpcArg>::BindInterface UpdateRuleRpc; - - Rpc, RpcArg, RpcArg>::BindInterface AddRuleForPeerRpc; - - ProximityInterestHandler* m_interestHandler; - unordered_map m_rules; - }; - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterest - */ - ProximityInterest::ProximityInterest(ProximityInterestHandler* handler) - : m_bbox(AZ::Aabb::CreateNull()) - , m_handler(handler) - { - AZ_Assert(m_handler, "Invalid interest handler"); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestRule - */ - void ProximityInterestRule::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateRule(this); - } - - void ProximityInterestRule::Destroy() - { - m_handler->DestroyRule(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestAttribute - */ - void ProximityInterestAttribute::Set(const AZ::Aabb& bbox) - { - m_bbox = bbox; - m_handler->UpdateAttribute(this); - } - - void ProximityInterestAttribute::Destroy() - { - m_handler->DestroyAttribute(this); - } - /////////////////////////////////////////////////////////////////////////// - - - /* - * ProximityInterestHandler - */ - ProximityInterestHandler::ProximityInterestHandler() - : m_im(nullptr) - , m_rm(nullptr) - , m_lastRuleNetId(0) - , m_rulesReplica(nullptr) - { - if (!ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(ReplicaChunkClassId(ProximityInterestChunk::GetChunkName()))) - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - } - - ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId) - { - ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId()); - if (peerId == m_rm->GetLocalPeerId()) - { - m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get()); - m_localRules.insert(rulePtr); - } - - return rulePtr; - } - - void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule) - { - //TODO: should be pool-allocated - delete rule; - } - - void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule) - { - if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId()); - } - - rule->m_bbox = AZ::Aabb::CreateNull(); - m_rules.insert(rule); - m_localRules.erase(rule); - } - - void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule) - { - if (rule->GetPeerId() == m_rm->GetLocalPeerId()) - { - m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get()); - } - - m_rules.insert(rule); - } - - ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId) - { - return aznew ProximityInterestAttribute(this, replicaId); - } - - void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib) - { - //TODO: should be pool-allocated - delete attrib; - } - - void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib) - { - attrib->m_bbox = AZ::Aabb::CreateNull(); - m_attributes.insert(attrib); - } - - void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib) - { - m_attributes.insert(attrib); - } - - void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - if (chunk != m_rulesReplica) // non-local - { - m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk)); - - for (auto& rule : m_localRules) - { - chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get()); - } - } - } - - void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer) - { - (void)chunk; - m_peerChunks.erase(peer->GetId()); - } - - RuleNetworkId ProximityInterestHandler::GetNewRuleNetId() - { - ++m_lastRuleNetId; - return m_rulesReplica->GetReplicaId() | (static_cast(m_lastRuleNetId) << 32); - } - - ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId) - { - auto it = m_peerChunks.find(peerId); - if (it == m_peerChunks.end()) - { - return nullptr; - } - else - { - return it->second; - } - } - - const InterestMatchResult& ProximityInterestHandler::GetLastResult() - { - return m_resultCache; - } - - void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result) - { - ////////////////////////////////////////////// - // just recalculating the whole state for now - for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ) - { - ProximityInterestAttribute* attr = *attrIt; - - auto resultIt = result.insert(attr->GetReplicaId()); - for (auto ruleIt = m_rules.begin(); ruleIt != m_rules.end(); ++ruleIt) - { - ProximityInterestRule* rule = *ruleIt; - if (rule->m_bbox.Overlaps(attr->m_bbox)) - { - resultIt.first->second.insert(rule->GetPeerId()); - } - } - - if ((*attrIt)->IsDeleted()) - { - attrIt = m_attributes.erase(attrIt); - delete attr; - } - else - { - ++attrIt; - } - } - - for (auto ruleIt = m_rules.begin(); ruleIt != m_rules.end(); ) - { - ProximityInterestRule* rule = *ruleIt; - - if (rule->IsDeleted()) - { - ruleIt = m_rules.erase(ruleIt); - delete rule; - } - else - { - ++ruleIt; - } - } - ////////////////////////////////////////////// - } - - void ProximityInterestHandler::Update() - { - m_resultCache.clear(); - - UpdateInternal(m_resultCache); - } - - void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager) - { - AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager); - AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n"); - AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n"); - m_im = manager; - m_rm = m_im->GetReplicaManager(); - m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this); - - auto replica = Replica::CreateReplica("ProximityInterestHandlerRules"); - m_rulesReplica = CreateAndAttachReplicaChunk(replica); - m_rm->AddPrimary(replica); - } - - void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager) - { - (void)manager; - AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im); - AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n"); - m_rulesReplica = nullptr; - m_im = nullptr; - m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)); - m_rm = nullptr; - - for (auto& chunk : m_peerChunks) - { - chunk.second->m_interestHandler = nullptr; - } - m_peerChunks.clear(); - m_localRules.clear(); - - for (ProximityInterestRule* rule : m_rules) - { - delete rule; - } - - for (ProximityInterestAttribute* attr : m_attributes) - { - delete attr; - } - - m_attributes.clear(); - m_rules.clear(); - - m_resultCache.clear(); - } - /////////////////////////////////////////////////////////////////////////// -} -#else -// Optimized spatial handler. -#include "GridMate/Replica/Interest/ProximityInterestHandler.h" -#endif - -namespace UnitTest { - -/* - * Helper class to capture performance of various Interest Managers - */ -class PerfForInterestManager -{ -public: - void Reset(); - - void PreUpdate(); - void PostUpdate(); - - AZ::u32 GetTotalFrames() const; - float GetAverageFrame() const; - float GetWorstFrame() const; - float GetBestFrame() const; - -private: - AZ::Debug::Timer m_timer; - AZ::u32 m_frameCount = 0; - float m_totalUpdateTime = 0.f; - float m_fastestFrame = 100.f; - float m_slowestFrame = 0.f; -}; - -PerfForInterestManager g_PerfIM = PerfForInterestManager(); -PerfForInterestManager g_PerfUpdatingAttributes = PerfForInterestManager(); - -/* -* Utility function to tick the replica manager -*/ -static void UpdateReplicas(ReplicaManager* replicaManager, InterestManager* interestManager) -{ - if (interestManager) - { - // Measuring time it takes to execute an update. - g_PerfIM.PreUpdate(); - interestManager->Update(); - g_PerfIM.PostUpdate(); - } - - if (replicaManager) - { - replicaManager->Unmarshal(); - replicaManager->UpdateFromReplicas(); - replicaManager->UpdateReplicas(); - replicaManager->Marshal(); - } -} - -class Integ_InterestTest - : public GridMateMPTestFixture -{ - /////////////////////////////////////////////////////////////////// - class InterestTestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(InterestTestChunk); - - InterestTestChunk() - : m_data("Data", 0) - , m_bitmaskAttributeData("BitmaskAttributeData") - , m_attribute(nullptr) - { - } - - /////////////////////////////////////////////////////////////////// - typedef AZStd::intrusive_ptr Ptr; - static const char* GetChunkName() { return "InterestTestChunk"; } - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override - { - return false; - } - /////////////////////////////////////////////////////////////////// - - void OnReplicaActivate(const ReplicaContext& rc) override - { - AZ_Printf("GridMate", "InterestTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - - BitmaskInterestHandler* ih = static_cast(rc.m_rm->GetUserContext(AZ_CRC("BitmaskInterestHandler", 0x5bf5d75b))); - if (ih) - { - m_attribute = ih->CreateAttribute(GetReplicaId()); - m_attribute->Set(m_bitmaskAttributeData.Get()); - } - } - - void OnReplicaDeactivate(const ReplicaContext& rc) override - { - AZ_Printf("GridMate", "InterestTestChunk::OnReplicaDeactivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - - m_attribute = nullptr; - } - - void BitmaskHandler(const InterestBitmask& bitmask, const TimeContext&) - { - if (m_attribute) - { - m_attribute->Set(bitmask); - } - } - - DataSet m_data; - DataSet::BindInterface m_bitmaskAttributeData; - BitmaskInterestAttribute::Ptr m_attribute; - }; - /////////////////////////////////////////////////////////////////// - - class TestPeerInfo - : public SessionEventBus::Handler - { - public: - TestPeerInfo() - : m_gridMate(nullptr) - , m_lanSearch(nullptr) - , m_session(nullptr) - , m_im(nullptr) - , m_bitmaskHandler(nullptr) - , m_num(0) - { - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - - void CreateTestReplica() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_bitmaskHandler = aznew BitmaskInterestHandler(); - m_im->RegisterHandler(m_bitmaskHandler); - - m_rule = m_bitmaskHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(1 << m_num); - - auto r = Replica::CreateReplica("InterestTestReplica"); - m_replica = CreateAndAttachReplicaChunk(r); - - // Initializing attribute - // Shifing all by one - peer0 will recv from peer1, peer2 will recv from peer2, peer2 will recv from peer0 - unsigned i = (m_num + 2) % Integ_InterestTest::k_numMachines; - m_replica->m_data.Set(m_num); - m_replica->m_bitmaskAttributeData.Set(1 << i); - - m_session->GetReplicaMgr()->AddPrimary(r); - } - - void UpdateAttribute() - { - // Shifing all by one - peer0 will recv from peer2, peer1 will recv from peer0, peer2 will recv from peer1 - unsigned i = (m_num + 1) % Integ_InterestTest::k_numMachines; - m_replica->m_bitmaskAttributeData.Set(1 << i); - m_replica->m_attribute->Set(1 << i); - } - - void DeleteAttribute() - { - m_replica->m_attribute = nullptr; - } - - void UpdateRule() - { - m_rule->Set(0xffff); - } - - void DeleteRule() - { - m_rule = nullptr; - } - - void CreateRule() - { - m_rule = m_bitmaskHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(0xffff); - } - - void OnSessionCreated(GridSession* session) override - { - m_session = session; - if (m_session->IsHost()) - { - CreateTestReplica(); - } - } - - void OnSessionJoined(GridSession* session) override - { - m_session = session; - CreateTestReplica(); - } - - void OnSessionDelete(GridSession* session) override - { - if (session == m_session) - { - m_rule = nullptr; - m_session = nullptr; - m_im->UnregisterHandler(m_bitmaskHandler); - delete m_bitmaskHandler; - delete m_im; - m_im = nullptr; - m_bitmaskHandler = nullptr; - } - } - - void OnSessionError(GridSession* session, const string& errorMsg) override - { - (void)session; - (void)errorMsg; - AZ_TracePrintf("GridMate", "Session error: %s\n", errorMsg.c_str()); - } - - IGridMate* m_gridMate; - GridSearch* m_lanSearch; - GridSession* m_session; - InterestManager* m_im; - BitmaskInterestHandler* m_bitmaskHandler; - - BitmaskInterestRule::Ptr m_rule; - unsigned m_num; - InterestTestChunk::Ptr m_replica; - }; - -public: - Integ_InterestTest() - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - - ////////////////////////////////////////////////////////////////////////// - // Create all grid mates - m_peers[0].m_gridMate = m_gridMate; - m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate); - m_peers[0].m_num = 0; - for (int i = 1; i < k_numMachines; ++i) - { - GridMateDesc desc; - m_peers[i].m_gridMate = GridMateCreate(desc); - AZ_TEST_ASSERT(m_peers[i].m_gridMate); - - m_peers[i].m_num = i; - m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate); - } - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - // start the multiplayer service (session mgr, extra allocator, etc.) - StartGridMateService(m_peers[i].m_gridMate, SessionServiceDesc()); - AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr); - } - } - virtual ~Integ_InterestTest() - { - StopGridMateService(m_peers[0].m_gridMate); - - for (int i = 1; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].SessionEventBus::Handler::BusDisconnect(); - GridMateDestroy(m_peers[i].m_gridMate); - } - } - - // this will stop the first IGridMate which owns the memory allocators. - m_peers[0].SessionEventBus::Handler::BusDisconnect(); - } - - void run() - { - TestCarrierDesc carrierDesc; - carrierDesc.m_enableDisconnectDetection = false;// true; - carrierDesc.m_threadUpdateTimeMS = 10; - carrierDesc.m_familyType = Driver::BSD_AF_INET; - - - LANSessionParams sp; - sp.m_topology = ST_PEER_TO_PEER; - sp.m_numPublicSlots = 64; - sp.m_port = k_hostPort; - EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session, m_peers[k_host].m_gridMate, LANSessionServiceBus, HostSession, sp, carrierDesc); - m_peers[k_host].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - int listenPort = k_hostPort; - for (int i = 0; i < k_numMachines; ++i) - { - if (i == k_host) - { - continue; - } - - LANSearchParams searchParams; - searchParams.m_serverPort = k_hostPort; - searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port, the rest specify return ports - searchParams.m_familyType = Driver::BSD_AF_INET; - EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch, m_peers[i].m_gridMate, LANSessionServiceBus, StartGridSearch, searchParams); - } - - - static const int maxNumUpdates = 300; - int numUpdates = 0; - TimeStamp time = AZStd::chrono::system_clock::now(); - - while (numUpdates <= maxNumUpdates) - { - if (numUpdates == 100) - { - // Checking everybody received only one replica: - // peer0 -> rep1, peer1 -> rep2, peer2 -> rep0 - for (int i = 0; i < k_numMachines; ++i) - { - ReplicaId repId = m_peers[(i + 1) % k_numMachines].m_replica->GetReplicaId(); - auto repRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repRecv != nullptr); - - repId = m_peers[(i + 2) % k_numMachines].m_replica->GetReplicaId(); - auto repNotRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repNotRecv == nullptr); - - // rotating mask left - m_peers[i].UpdateAttribute(); - } - } - - if (numUpdates == 150) - { - // Checking everybody received only one replica: - // peer0 -> rep2, peer1 -> rep0, peer2 -> rep1 - for (int i = 0; i < k_numMachines; ++i) - { - ReplicaId repId = m_peers[(i + 2) % k_numMachines].m_replica->GetReplicaId(); - auto repRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repRecv != nullptr); - - repId = m_peers[(i + 1) % k_numMachines].m_replica->GetReplicaId(); - auto repNotRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(repNotRecv == nullptr); - - // setting rules to accept all replicas - m_peers[i].UpdateRule(); - } - } - - if (numUpdates == 200) - { - // Checking everybody received all replicas - for (int i = 0; i < k_numMachines; ++i) - { - for (int j = 0; j < k_numMachines; ++j) - { - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep); - } - - // Deleting all attributes - m_peers[i].DeleteAttribute(); - } - } - - if (numUpdates == 250) - { - // Checking everybody lost all replicas (except primary) - for (int i = 0; i < k_numMachines; ++i) - { - for (int j = 0; j < k_numMachines; ++j) - { - if (i == j) - { - continue; - } - - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep == nullptr); - } - - // deleting all rules - m_peers[i].DeleteRule(); - } - } - - ////////////////////////////////////////////////////////////////////////// - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].m_gridMate->Update(); - if (m_peers[i].m_session) - { - UpdateReplicas(m_peers[i].m_session->GetReplicaMgr(), m_peers[i].m_im); - } - } - } - Update(); - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone()) - { - AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1); - JoinParams jp; - EBUS_EVENT_ID_RESULT(m_peers[i].m_session, m_peers[i].m_gridMate, LANSessionServiceBus, JoinSessionBySearchInfo, static_cast(*m_peers[i].m_lanSearch->GetResult(0)), jp, carrierDesc); - m_peers[i].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - m_peers[i].m_lanSearch->Release(); - m_peers[i].m_lanSearch = nullptr; - } - } - - ////////////////////////////////////////////////////////////////////////// - // Debug Info - TimeStamp now = AZStd::chrono::system_clock::now(); - if (AZStd::chrono::milliseconds(now - time).count() > 1000) - { - time = now; - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_session == nullptr) - { - continue; - } - - if (m_peers[i].m_session->IsHost()) - { - AZ_Printf("GridMate", "------ Host %d ------\n", i); - } - else - { - AZ_Printf("GridMate", "------ Client %d ------\n", i); - } - - AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime()); - for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember) - { - GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember); - AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no"); - } - AZ_Printf("GridMate", "\n"); - } - } - ////////////////////////////////////////////////////////////////////////// - - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30)); - numUpdates++; - } - } - - static const int k_numMachines = 3; - static const int k_host = 0; - static const int k_hostPort = 5450; - - TestPeerInfo m_peers[k_numMachines]; -}; - -/* - * Testing worse case performance of thousands of replicas and a few peers where all replicas/attributes change every frame - * and peers/rules change every frame as well. - */ -class LargeWorldTest - : public GridMateMPTestFixture -{ - /////////////////////////////////////////////////////////////////// - class LargeWorldTestChunk - : public ReplicaChunk - { - public: - GM_CLASS_ALLOCATOR(LargeWorldTestChunk); - - LargeWorldTestChunk() - : m_data("Data", 0) - , m_proximityAttributeData("LargeWorldAttributeData") - , m_attribute(nullptr) - { - } - - /////////////////////////////////////////////////////////////////// - typedef AZStd::intrusive_ptr Ptr; - static const char* GetChunkName() { return "LargeWorldTestChunk"; } - bool IsReplicaMigratable() override { return false; } - bool IsBroadcast() override - { - return false; - } - /////////////////////////////////////////////////////////////////// - - void OnReplicaActivate(const ReplicaContext& rc) override - { - /*if (!IsPrimary())*/ - /*{ - AZ_Printf("GridMate", "LargeWorldTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n", - GetReplicaId(), - IsPrimary() ? "primary" : "proxy", - rc.m_peer ? rc.m_peer->GetId() : 0, - rc.m_rm->GetLocalPeerId()); - }*/ - - if (ProximityInterestHandler* ih = static_cast(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)))) - { - m_attribute = ih->CreateAttribute(GetReplicaId()); - m_attribute->Set(m_proximityAttributeData.Get()); - } - } - - void OnReplicaDeactivate(const ReplicaContext& /*rc*/) override - { - m_attribute = nullptr; - } - - void ProximityHandler(const AZ::Aabb& bounds, const TimeContext&) - { - if (m_attribute) - { - m_attribute->Set(bounds); - } - } - - DataSet m_data; - DataSet::BindInterface m_proximityAttributeData; - ProximityInterestAttribute::Ptr m_attribute; - }; - /////////////////////////////////////////////////////////////////// - - struct LargeWorldParams - { - AZ::u32 index = 0; - - const float commonSize = 50; - const AZ::Aabb box = AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateZero(), - AZ::Vector3::CreateOne() * commonSize); - const float commonStep = commonSize + 1; - }; - - static LargeWorldParams& GetWorldParams() - { - static LargeWorldParams worldParams; - return worldParams; - } - - /* - * Create a chain of boxes in spaces along X axis - */ - static AZ::Aabb CreateNextRuleSpace() - { - auto offset = GetWorldParams().commonStep * aznumeric_cast(GetWorldParams().index); - - float min[] = { offset, 0, 0 }; - float max[] = { - GetWorldParams().commonSize + offset, - GetWorldParams().commonSize, - GetWorldParams().commonSize }; - - auto bounds = AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateFromFloat3(min), - AZ::Vector3::CreateFromFloat3(max)); - - GetWorldParams().index++; - return bounds; - } - - class LargeWorldTestPeerInfo - : public SessionEventBus::Handler - { - public: - LargeWorldTestPeerInfo() - : m_gridMate(nullptr) - , m_lanSearch(nullptr) - , m_session(nullptr) - , m_im(nullptr) - , m_proximityHandler(nullptr) - , m_num(0) - { - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - } - - ~LargeWorldTestPeerInfo() - { - SessionEventBus::Handler::BusDisconnect(); - } - - void CreateHostRuleHandler() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_proximityHandler = aznew ProximityInterestHandler(); - m_im->RegisterHandler(m_proximityHandler); - - m_rule = m_proximityHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(AZ::Aabb::CreateNull()); // host rule doesn't matter in this test - } - - void CreateRuleHandler() - { - m_im = aznew InterestManager(); - InterestManagerDesc desc; - desc.m_rm = m_session->GetReplicaMgr(); - - m_im->Init(desc); - - m_proximityHandler = aznew ProximityInterestHandler(); - m_im->RegisterHandler(m_proximityHandler); - - m_rule = m_proximityHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId()); - m_rule->Set(CreateNextRuleSpace()); - } - - void CreateTestReplica(const AZ::Aabb& bounds) - { - auto r = Replica::CreateReplica("LargeWorldTestReplica"); - auto replica = CreateAndAttachReplicaChunk(r); - - // Initializing attribute - replica->m_data.Set(m_num); - replica->m_proximityAttributeData.Set(bounds); - - m_replicas.push_back(replica); - - m_session->GetReplicaMgr()->AddPrimary(r); - } - - void PopulateWorld() - { - AZ_Printf("GridMate", "LargeWorldTestChunk::PopulateWorld() starting...\n"); - - const float worldSizeInBoxes = 50; - const auto oneBox = AZ::Vector3::CreateOne(); - const auto thickness = 1; - for (float dx = 0; dx < worldSizeInBoxes; ++dx) - { - for (float dy = 0; dy < thickness; ++dy) - { - for (float dz = 0; dz < thickness; ++dz) - { - auto aabb = AZ::Aabb::CreateFromMinMax( - AZ::Vector3(50 * dx + 5, dy, dz), - AZ::Vector3(50 * dx + 5, dy, dz) + oneBox); - - CreateTestReplica(aabb); - } - } - } - - AZ_Printf("GridMate", "LargeWorldTestChunk::PopulateWorld() ... DONE\n"); - } - - void UpdateAttribute(LargeWorldTestChunk::Ptr replica) - { - if (replica && replica->m_attribute) - { - auto sameValue = replica->m_attribute->Get(); - - replica->m_proximityAttributeData.Set(sameValue); - replica->m_attribute->Set(sameValue); - } - } - - void UpdateRule() - { - // just make it dirty for now - if (m_rule) - { - auto sameValue = m_rule->Get(); - m_rule->Set(sameValue); - } - } - - void DeleteRule() - { - m_rule = nullptr; - } - - void OnSessionCreated(GridSession* session) override - { - m_session = session; - if (m_session->IsHost()) - { - CreateHostRuleHandler(); - PopulateWorld(); - } - } - - void OnSessionJoined(GridSession* session) override - { - m_session = session; - CreateRuleHandler(); - } - - void OnSessionDelete(GridSession* session) override - { - if (session == m_session) - { - m_rule = nullptr; - m_session = nullptr; - m_im->UnregisterHandler(m_proximityHandler); - delete m_proximityHandler; - delete m_im; - m_im = nullptr; - m_proximityHandler = nullptr; - } - } - - void OnSessionError(GridSession* session, const string& errorMsg) override - { - (void)session; - (void)errorMsg; - AZ_TracePrintf("GridMate", "Session error: %s\n", errorMsg.c_str()); - } - - IGridMate* m_gridMate; - GridSearch* m_lanSearch; - GridSession* m_session; - InterestManager* m_im; - ProximityInterestHandler* m_proximityHandler; - - ProximityInterestRule::Ptr m_rule; - unsigned m_num; - - AZStd::vector m_replicas; - }; - -public: - LargeWorldTest() : UnitTest::GridMateMPTestFixture(500u * 1024u * 1024u) // 500Mb - { - ReplicaChunkDescriptorTable::Get().RegisterChunkType(); - - ////////////////////////////////////////////////////////////////////////// - // Create all grid mates - m_peers[0].m_gridMate = m_gridMate; - m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate); - m_peers[0].m_num = 0; - for (int i = 1; i < k_numMachines; ++i) - { - GridMateDesc desc; - m_peers[i].m_gridMate = GridMateCreate(desc); - AZ_TEST_ASSERT(m_peers[i].m_gridMate); - - m_peers[i].m_num = i; - m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate); - } - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - // start the multiplayer service (session mgr, extra allocator, etc.) - StartGridMateService(m_peers[i].m_gridMate, SessionServiceDesc()); - AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr); - } - } - virtual ~LargeWorldTest() - { - StopGridMateService(m_peers[0].m_gridMate); - - for (int i = 1; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].SessionEventBus::Handler::BusDisconnect(); - GridMateDestroy(m_peers[i].m_gridMate); - } - } - - // this will stop the first IGridMate which owns the memory allocators. - m_peers[0].SessionEventBus::Handler::BusDisconnect(); - } - - void run() - { - g_PerfIM.Reset(); - g_PerfUpdatingAttributes.Reset(); - - TestCarrierDesc carrierDesc; - carrierDesc.m_enableDisconnectDetection = false; - carrierDesc.m_threadUpdateTimeMS = 10; - carrierDesc.m_familyType = Driver::BSD_AF_INET; - - LANSessionParams sp; - sp.m_topology = ST_PEER_TO_PEER; - sp.m_numPublicSlots = 64; - sp.m_port = k_hostPort; - EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session, m_peers[k_host].m_gridMate, LANSessionServiceBus, HostSession, sp, carrierDesc); - m_peers[k_host].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - int listenPort = k_hostPort; - for (int i = 0; i < k_numMachines; ++i) - { - if (i == k_host) - { - continue; - } - - LANSearchParams searchParams; - searchParams.m_serverPort = k_hostPort; - searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port, the rest specify return ports - searchParams.m_familyType = Driver::BSD_AF_INET; - EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch, m_peers[i].m_gridMate, LANSessionServiceBus, StartGridSearch, searchParams); - } - - - static const int maxNumUpdates = 300; - int numUpdates = 0; - TimeStamp time = AZStd::chrono::system_clock::now(); - - while (numUpdates <= maxNumUpdates) - { - g_PerfUpdatingAttributes.PreUpdate(); - for (LargeWorldTestChunk::Ptr replica : m_peers[0].m_replicas) - { - m_peers[0].UpdateAttribute(replica); - } - g_PerfUpdatingAttributes.PostUpdate(); - - for (auto peer : m_peers) - { - peer.UpdateRule(); - } - - //if (numUpdates == 100) - //{ - // // check how many replicas each client got - // for (AZ::u32 i = 1; i < k_numMachines; ++i) - // { - // AZ::u32 count = 0; - // for (auto& replica : m_peers[0].m_replicas) - // { - // ReplicaId repId = replica->GetReplicaId(); - // if (auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId)) - // { - // count++; - // } - // } - - // const AZ::Aabb& bounds = m_peers[i].m_rule->Get(); - - // AZ_Printf("GridMate", "Session %s Members: %d Bounds: %f.%f.%f-%f.%f.%f Replicas: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), - // static_cast(bounds.GetMin().GetX()), - // static_cast(bounds.GetMin().GetY()), - // static_cast(bounds.GetMin().GetZ()), - // static_cast(bounds.GetMax().GetX()), - // static_cast(bounds.GetMax().GetY()), - // static_cast(bounds.GetMax().GetZ()), count); - - // AZ_Assert(count == 1, "Should have at least some replicas to start with"); - // } - //} - - if (numUpdates == 200) - { - // Deleting all attributes - for (auto& replica : m_peers[0].m_replicas) - { - replica->m_attribute = nullptr; - } - } - - if (numUpdates == 250) - { - // Checking everybody lost all replicas (except primary) - for (int i = 0; i < k_numMachines; ++i) - { - /*for (int j = 0; j < k_numMachines; ++j) - { - if (i == j) - { - continue; - } - - ReplicaId repId = m_peers[j].m_replica->GetReplicaId(); - auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId); - AZ_TEST_ASSERT(rep == nullptr); - }*/ - - // deleting all rules - m_peers[i].DeleteRule(); - } - } - - ////////////////////////////////////////////////////////////////////////// - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_gridMate) - { - m_peers[i].m_gridMate->Update(); - if (m_peers[i].m_session) - { - UpdateReplicas(m_peers[i].m_session->GetReplicaMgr(), m_peers[i].m_im); - } - } - } - Update(); - ////////////////////////////////////////////////////////////////////////// - - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone()) - { - AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1); - JoinParams jp; - EBUS_EVENT_ID_RESULT(m_peers[i].m_session, m_peers[i].m_gridMate, LANSessionServiceBus, JoinSessionBySearchInfo, static_cast(*m_peers[i].m_lanSearch->GetResult(0)), jp, carrierDesc); - m_peers[i].m_session->GetReplicaMgr()->SetAutoBroadcast(false); - - m_peers[i].m_lanSearch->Release(); - m_peers[i].m_lanSearch = nullptr; - } - } - - ////////////////////////////////////////////////////////////////////////// - // Debug Info - TimeStamp now = AZStd::chrono::system_clock::now(); - if (AZStd::chrono::milliseconds(now - time).count() > 1000) - { - time = now; - for (int i = 0; i < k_numMachines; ++i) - { - if (m_peers[i].m_session == nullptr) - { - continue; - } - - if (m_peers[i].m_session->IsHost()) - { - AZ_Printf("GridMate", "------ Host %d ------\n", i); - } - else - { - AZ_Printf("GridMate", "------ Client %d ------\n", i); - } - - AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime()); - for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember) - { - GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember); - AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no"); - } - AZ_Printf("GridMate", "\n"); - } - } - ////////////////////////////////////////////////////////////////////////// - - //AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30)); - numUpdates++; - } - - auto averageFrame = g_PerfIM.GetAverageFrame(); - auto bestFrame = g_PerfIM.GetBestFrame(); - auto worstFrame = g_PerfIM.GetWorstFrame(); - auto frames = g_PerfIM.GetTotalFrames(); - AZ_Printf("GridMate", "Interest manager performance: average_frame = %f sec, frames = %d, best= %f sec, worst= %f sec\n", - averageFrame, frames, bestFrame, worstFrame); - - AZ_Printf("GridMate", "Updating attributes: average_frame = %f sec, frames = %d, best= %f sec, worst= %f sec\n", - g_PerfUpdatingAttributes.GetAverageFrame(), - g_PerfUpdatingAttributes.GetTotalFrames(), - g_PerfUpdatingAttributes.GetBestFrame(), - g_PerfUpdatingAttributes.GetWorstFrame()); - } - - static const int k_numMachines = 3; - static const int k_host = 0; - static const int k_hostPort = 5450; - - LargeWorldTestPeerInfo m_peers[k_numMachines]; -}; - -void PerfForInterestManager::Reset() -{ - m_frameCount = 0; - m_totalUpdateTime = 0; - m_slowestFrame = 0; - m_fastestFrame = 100.f; -} - -void PerfForInterestManager::PreUpdate() -{ - m_timer.Stamp(); -} - -void PerfForInterestManager::PostUpdate() -{ - auto frameTime = m_timer.StampAndGetDeltaTimeInSeconds(); - m_totalUpdateTime += frameTime; - m_frameCount++; - - m_slowestFrame = AZStd::max(m_slowestFrame, frameTime); - m_fastestFrame = AZStd::min(m_fastestFrame, frameTime); -} - -AZ::u32 PerfForInterestManager::GetTotalFrames() const -{ - return m_frameCount; -} - -float PerfForInterestManager::GetAverageFrame() const -{ - if (m_frameCount > 0) - { - return m_totalUpdateTime / m_frameCount; - } - - return 0; -} - -float PerfForInterestManager::GetWorstFrame() const -{ - return m_slowestFrame; -} - -float PerfForInterestManager::GetBestFrame() const -{ - return m_fastestFrame; -} - -class ProximityHandlerTests - : public GridMateMPTestFixture -{ -public: - struct xyz - { - float x, y, z; - }; - - static AZ::Aabb CreateBox(xyz min, float size) - { - return AZ::Aabb::CreateFromMinMax( - AZ::Vector3::CreateFromFloat3(&min.x), - AZ::Vector3::CreateFromFloat3(&min.x) + AZ::Vector3::CreateOne() * size); - } - - static void run() - { - SimpleFirstUpdate(); - SecondUpdateAfterNoChanges(); - SimpleOutsideOfRule(); - AttributeMovingOutsideOfRule(); - RuleMovingAndAttributeIsOut(); - RuleDestroyed(); - AttributeDestroyed(); - } - - static void RuleMovingAndAttributeIsOut() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "1st"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - - // now move the attribute outside of the rule - rule1->Set(CreateBox({ 1000, 0, 0 }, 100)); - - handler->Update(); - results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "2nd"); - - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void AttributeMovingOutsideOfRule() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "1st"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - - // now move the attribute outside of the rule - attribute1->Set(CreateBox({ -1000, 0, 0 }, 10)); - - handler->Update(); - results = handler->GetLastResult(); - //ProximityInterestHandler::DebugPrint(results, "2nd"); - - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void SimpleFirstUpdate() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results[1].size() == 1); - AZ_TEST_ASSERT(results[1].find(100) != results[1].end()); - } - - static void SecondUpdateAfterNoChanges() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results.size() == 0); - } - - static void SimpleOutsideOfRule() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ -1000, 0, 0 }, 10)); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - - //ProximityInterestHandler::PrintMatchResult(results, "test"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void RuleDestroyed() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - { - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 1); - } - - // rule1 should have been destroyed by now - - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - //ProximityInterestHandler::PrintMatchResult(results, "last"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - } - - static void AttributeDestroyed() - { - AZStd::unique_ptr handler(aznew ProximityInterestHandler()); - - auto rule1 = handler->CreateRule(100); - rule1->Set(CreateBox({ 0, 0, 0 }, 100)); - - { - auto attribute1 = handler->CreateAttribute(1); - attribute1->Set(CreateBox({ 0, 0, 0 }, 10)); - - handler->Update(); - - InterestMatchResult results = handler->GetLastResult(); - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 1); - } - - // attribute1 should have been destroyed by now, but it will show up once to remove it from affected peers - handler->Update(); - InterestMatchResult results = handler->GetLastResult(); - results.PrintMatchResult("last"); - - AZ_TEST_ASSERT(results.size() == 1); - AZ_TEST_ASSERT(results[1].size() == 0); - - // and now attribute1 should not show up in the changes - handler->Update(); - results = handler->GetLastResult(); - results.PrintMatchResult("last"); - - AZ_TEST_ASSERT(results.size() == 0); - } -}; - -}; // namespace UnitTest - -GM_TEST_SUITE(InterestSuite) - GM_TEST(Integ_InterestTest); -#if AZ_TRAIT_GRIDMATE_TEST_EXCLUDE_LARGEWORLDTEST != 0 - GM_TEST(LargeWorldTest); -#endif - GM_TEST(ProximityHandlerTests); -GM_TEST_SUITE_END() diff --git a/Code/Framework/GridMate/Tests/gridmate_test_files.cmake b/Code/Framework/GridMate/Tests/gridmate_test_files.cmake index a8b1dbd120..90ccbbf9a9 100644 --- a/Code/Framework/GridMate/Tests/gridmate_test_files.cmake +++ b/Code/Framework/GridMate/Tests/gridmate_test_files.cmake @@ -24,5 +24,4 @@ set(FILES StreamSocketDriverTests.cpp CarrierStreamSocketDriverTests.cpp Carrier.cpp - Interest.cpp ) diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index c73cdd1981..09a9e2f990 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -46,20 +46,25 @@ extern "C" void CreateStaticModules(AZStd::vector& modulesOut); # define REMOTE_ASSET_PROCESSOR #endif +void CVar_OnViewportPosition(const AZ::Vector2& value); + namespace { - void OnViewportResize(const AZ::Vector2& value); + void CVar_OnViewportResize(const AZ::Vector2& value); - AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate, + AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), CVar_OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate, "The default size for the launcher viewport, 0 0 means full screen"); - void OnViewportResize(const AZ::Vector2& value) + void CVar_OnViewportResize(const AZ::Vector2& value) { AzFramework::NativeWindowHandle windowHandle = nullptr; AzFramework::WindowSystemRequestBus::BroadcastResult(windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle); AzFramework::WindowSize newSize = AzFramework::WindowSize(aznumeric_cast(value.GetX()), aznumeric_cast(value.GetY())); AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::ResizeClientArea, newSize); } + + AZ_CVAR(AZ::Vector2, r_viewportPos, AZ::Vector2::CreateZero(), CVar_OnViewportPosition, AZ::ConsoleFunctorFlags::DontReplicate, + "The default position for the launcher viewport, 0 0 means top left corner of your main desktop"); void ExecuteConsoleCommandFile(AzFramework::Application& application) { diff --git a/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp b/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp index 6455373e58..fa26b9d204 100644 --- a/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp +++ b/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp @@ -433,3 +433,5 @@ void android_main(android_app* appState) MAIN_EXIT_FAILURE(appState, GetReturnCodeString(status)); } } + +void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {} diff --git a/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp b/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp index 0c422877b1..3d283ce77f 100644 --- a/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp +++ b/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp @@ -113,3 +113,5 @@ int main(int argc, char** argv) return static_cast(status); } + +void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {} diff --git a/Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm b/Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm index 1a491d66ad..2886bb16b1 100644 --- a/Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm +++ b/Code/LauncherUnified/Platform/Mac/Launcher_Mac.mm @@ -63,3 +63,5 @@ int main(int argc, char* argv[]) } #endif // AZ_TESTS_ENABLED + +void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {} diff --git a/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp b/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp index 8f8d310475..494779dcf8 100644 --- a/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp +++ b/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp @@ -69,3 +69,14 @@ int APIENTRY WinMain([[maybe_unused]] HINSTANCE hInstance, [[maybe_unused]] HINS return static_cast(status); } + +void CVar_OnViewportPosition(const AZ::Vector2& value) +{ + if (HWND windowHandle = GetActiveWindow()) + { + SetWindowPos(windowHandle, nullptr, + value.GetX(), + value.GetY(), + 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE); + } +} diff --git a/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm b/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm index 52c616fe64..05857842a7 100644 --- a/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm +++ b/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm @@ -23,3 +23,5 @@ int main(int argc, char* argv[]) [pool release]; return 0; } + +void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {} diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake index 36cd3c5899..5fa0f7a0e3 100644 --- a/Code/LauncherUnified/launcher_generator.cmake +++ b/Code/LauncherUnified/launcher_generator.cmake @@ -196,6 +196,16 @@ function(ly_delayed_generate_static_modules_inl) ly_get_gem_load_dependencies(all_game_gem_dependencies ${project_name}.GameLauncher) foreach(game_gem_dependency ${all_game_gem_dependencies}) + # Sometimes, a gem's Client variant may be an interface library + # which dependes on multiple gem targets. The interface libraries + # should be skipped; the real dependencies of the interface will be processed + if(TARGET ${game_gem_dependency}) + get_target_property(target_type ${game_gem_dependency} TYPE) + if(${target_type} STREQUAL "INTERFACE_LIBRARY") + continue() + endif() + endif() + # To match the convention on how gems targets vs gem modules are named, # we remove the ".Static" from the suffix # Replace "." with "_" @@ -224,6 +234,14 @@ function(ly_delayed_generate_static_modules_inl) list(APPEND all_server_gem_dependencies ${server_gem_load_dependencies} ${server_gem_dependency}) endforeach() foreach(server_gem_dependency ${all_server_gem_dependencies}) + # Skip interface libraries + if(TARGET ${server_gem_dependency}) + get_target_property(target_type ${server_gem_dependency} TYPE) + if(${target_type} STREQUAL "INTERFACE_LIBRARY") + continue() + endif() + endif() + # Replace "." with "_" string(REPLACE "." "_" server_gem_dependency ${server_gem_dependency}) diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 0b23739bb8..1bc8b9d324 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -1,24 +1,26 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorDefs.h" #include "AzAssetBrowserWindow.h" // AzToolsFramework +#include #include #include -#include #include +#include +#include // AzQtComponents #include @@ -31,6 +33,7 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING #include AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING +AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView); class ListenerForShowAssetEditorEvent : public QObject @@ -66,32 +69,75 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) : QWidget(parent) , m_ui(new Ui::AzAssetBrowserWindowClass()) , m_filterModel(new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent)) + , m_tableModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableModel(parent)) { m_ui->setupUi(this); m_ui->m_searchWidget->Setup(true, true); - using namespace AzToolsFramework::AssetBrowser; - AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel); + namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; + + AzAssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AzAssetBrowser::AssetBrowserComponentRequests::GetAssetBrowserModel); AZ_Assert(m_assetBrowserModel, "Failed to get filebrowser model"); m_filterModel->setSourceModel(m_assetBrowserModel); m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter()); + m_ui->m_viewSwitcherCheckBox->setVisible(false); + m_ui->m_assetBrowserTableViewWidget->setVisible(false); + if (ed_useNewAssetBrowserTableView) + { + m_ui->m_viewSwitcherCheckBox->setVisible(true); + m_tableModel->setFilterRole(Qt::DisplayRole); + m_tableModel->setSourceModel(m_filterModel.data()); + m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data()); + connect( + m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, m_tableModel.data(), + &AzAssetBrowser::AssetBrowserTableModel::UpdateTableModelMaps); + connect( + m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this, + &AzAssetBrowserWindow::SelectionChangedSlot); + connect( + m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, + &AzAssetBrowserWindow::DoubleClickedItem); + connect( + m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, + &AzAssetBrowser::SearchWidget::ClearStringFilter); + connect( + m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, + &AzAssetBrowser::SearchWidget::ClearTypeFilter); + + m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); + + connect(m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView); + } + m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data()); - connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, - m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot); - connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, [this]() - { - const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); - const bool selectFirstFilteredIndex = false; - m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); - }); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, - this, &AzAssetBrowserWindow::SelectionChangedSlot); + connect( + m_ui->m_searchWidget->GetFilter().data(), &AzAssetBrowser::AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), + &AzAssetBrowser::AssetBrowserFilterModel::filterUpdatedSlot); + connect( + m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this, + [this]() + { + const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); + const bool selectFirstFilteredIndex = false; + m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); + }); + + connect( + m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::selectionChangedSignal, this, + &AzAssetBrowserWindow::SelectionChangedSlot); + connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter); + connect( + m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, + &AzAssetBrowser::SearchWidget::ClearStringFilter); + connect( + m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, + &AzAssetBrowser::SearchWidget::ClearTypeFilter); + m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); } @@ -117,7 +163,10 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa void AzAssetBrowserWindow::UpdatePreview() const { - auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); + const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible() + ? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets() + : m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); + if (selectedAssets.size() != 1) { m_ui->m_previewerFrame->Clear(); @@ -148,8 +197,6 @@ static void ExpandTreeToIndex(QTreeView* treeView, const QModelIndex& index) void AzAssetBrowserWindow::SelectAsset(const QString& assetPath) { - using namespace AzToolsFramework::AssetBrowser; - QModelIndex index = m_assetBrowserModel->findIndex(assetPath); if (index.isValid()) { @@ -161,18 +208,21 @@ void AzAssetBrowserWindow::SelectAsset(const QString& assetPath) // interferes with the update from the select and expand, and if you don't // queue it, the tree doesn't expand reliably. - QTimer::singleShot(0, this, [this, filteredIndex = index] { - // the treeview has a filter model so we have to backwards go from that - QModelIndex index = m_filterModel->mapFromSource(filteredIndex); + QTimer::singleShot( + 0, this, + [this, filteredIndex = index] + { + // the treeview has a filter model so we have to backwards go from that + QModelIndex index = m_filterModel->mapFromSource(filteredIndex); - QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget; - ExpandTreeToIndex(treeView, index); + QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget; + ExpandTreeToIndex(treeView, index); - treeView->scrollTo(index); - treeView->setCurrentIndex(index); + treeView->scrollTo(index); + treeView->setCurrentIndex(index); - treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); - }); + treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); + }); } } @@ -185,31 +235,34 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected // just becuase on some OS clicking once is activation. void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element) { - using namespace AzToolsFramework; - using namespace AzToolsFramework::AssetBrowser; - // assumption: Double clicking an item selects it before telling us we double clicked it. - auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); - for (const AssetBrowserEntry* entry : selectedAssets) + namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; + + const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible() + ? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets() + : m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets(); + + for (const AzAssetBrowser::AssetBrowserEntry* entry : selectedAssets) { AZ::Data::AssetId assetIdToOpen; AZStd::string fullFilePath; - if (const ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) + if (const AzAssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry)) { assetIdToOpen = productEntry->GetAssetId(); fullFilePath = entry->GetFullPath(); } - else if (const SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) + else if (const AzAssetBrowser::SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry)) { // manufacture an empty AssetID with the source's UUID assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0); fullFilePath = entry->GetFullPath(); } - + bool handledBySomeone = false; if (assetIdToOpen.IsValid()) { - AssetBrowserInteractionNotificationBus::Broadcast(&AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); + AzAssetBrowser::AssetBrowserInteractionNotificationBus::Broadcast( + &AzAssetBrowser::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone); } if (!handledBySomeone && !fullFilePath.empty()) @@ -217,7 +270,27 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& AzAssetBrowserRequestHandler::OpenWithOS(fullFilePath); } } +} +void AzAssetBrowserWindow::SwitchDisplayView(bool state) +{ + m_ui->m_assetBrowserTableViewWidget->setVisible(state); + m_ui->m_assetBrowserTreeViewWidget->setVisible(!state); +} + +void AzAssetBrowserWindow::LockToDefaultView(bool state) +{ + using AzToolsFramework::AssetBrowser::AssetBrowserFilterModel; + SwitchDisplayView(!state); + if (state == true) + { + disconnect( + m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + } + else + { + connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView); + } } #include diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 263712dfe2..57a5371ab9 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -29,7 +29,9 @@ namespace AzToolsFramework namespace AssetBrowser { class AssetBrowserFilterModel; + class AssetBrowserTableModel; class AssetBrowserModel; + class AssetBrowserTableFilterModel; } } @@ -53,6 +55,7 @@ private: QScopedPointer m_ui; QScopedPointer m_filterModel; + QScopedPointer m_tableModel; AzToolsFramework::AssetBrowser::AssetBrowserModel* m_assetBrowserModel; void UpdatePreview() const; @@ -60,6 +63,8 @@ private: private Q_SLOTS: void SelectionChangedSlot(const QItemSelection& selected, const QItemSelection& deselected) const; void DoubleClickedItem(const QModelIndex& element); + void SwitchDisplayView(bool state); + void LockToDefaultView(bool state); }; extern const char* AZ_ASSET_BROWSER_PREVIEW_NAME; diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui index d6a6b4b8df..37eb1521f3 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui @@ -65,6 +65,13 @@ + + + + Switch View + + + @@ -107,6 +114,49 @@ 0 + + + + + 0 + 0 + + + + QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + + + false + + + QAbstractItemView::DragOnly + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + false + + + true + + + false + + + true + + + false + + + @@ -162,6 +212,11 @@
AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h
1 + + AzToolsFramework::AssetBrowser::AssetBrowserTableView + QTableView +
AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
+
diff --git a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp index 502f396bf1..faabaebb05 100644 --- a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp +++ b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp @@ -715,10 +715,11 @@ QMenu* LevelEditorMenuHandler::CreateViewMenu() { return view.IsViewportPane(); }); -#endif viewportViewsMenuWrapper.AddSeparator(); +#endif + if (CViewManager::IsMultiViewportEnabled()) { viewportViewsMenuWrapper.AddAction(ID_VIEW_CONFIGURELAYOUT); diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index 4a0b9cdc3d..4169bf93a8 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -53,6 +53,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include // AzFramework #include @@ -356,6 +357,8 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToM for (int i = idStart; i <= idEnd; ++i) \ ON_COMMAND(i, method); +AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); + void CCryEditApp::RegisterActionHandlers() { ON_COMMAND(ID_APP_ABOUT, OnAppAbout) @@ -376,6 +379,10 @@ void CCryEditApp::RegisterActionHandlers() ON_COMMAND(ID_EDIT_FETCH, OnEditFetch) ON_COMMAND(ID_FILE_EXPORTTOGAMENOSURFACETEXTURE, OnFileExportToGameNoSurfaceTexture) ON_COMMAND(ID_VIEW_SWITCHTOGAME, OnViewSwitchToGame) + MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_VIEW_SWITCHTOGAME_FULLSCREEN, [this]() { + ed_previewGameInFullscreen_once = true; + OnViewSwitchToGame(); + }); ON_COMMAND(ID_MOVE_OBJECT, OnMoveObject) ON_COMMAND(ID_RENAME_OBJ, OnRenameObj) ON_COMMAND(ID_EDITMODE_MOVE, OnEditmodeMove) @@ -2886,7 +2893,7 @@ void CCryEditApp::OpenProjectManager(const AZStd::string& screen) { // provide the current project path for in case we want to update the project AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath(); - const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project_path %s", screen.c_str(), projectPath.c_str()); + const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project-path %s", screen.c_str(), projectPath.c_str()); bool launchSuccess = AzFramework::ProjectManager::LaunchProjectManager(commandLineOptions); if (!launchSuccess) { diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 5a444f0521..1a7c54967b 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include // AzFramework @@ -82,6 +83,8 @@ #include "AnimationContext.h" #include "Objects/SelectionGroup.h" #include "Core/QtEditorApplication.h" +#include "MainWindow.h" +#include "LayoutWnd.h" // ComponentEntityEditorPlugin #include @@ -513,18 +516,28 @@ void EditorViewportWidget::Update() // Disable rendering to avoid recursion into Update() PushDisableRendering(); + + //get debug display interface for the viewport + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, GetViewportId()); + AZ_Assert(debugDisplayBus, "Invalid DebugDisplayRequestBus."); + + AzFramework::DebugDisplayRequests* debugDisplay = + AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + + // draw debug visualizations - if (m_debugDisplay) + if (debugDisplay) { - const AZ::u32 prevState = m_debugDisplay->GetState(); - m_debugDisplay->SetState( + const AZ::u32 prevState = debugDisplay->GetState(); + debugDisplay->SetState( e_Mode3D | e_AlphaBlended | e_FillModeSolid | e_CullModeBack | e_DepthWriteOn | e_DepthTestOn); AzFramework::EntityDebugDisplayEventBus::Broadcast( &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport, - AzFramework::ViewportInfo{ GetViewportId() }, *m_debugDisplay); + AzFramework::ViewportInfo{ GetViewportId() }, *debugDisplay); - m_debugDisplay->SetState(prevState); + debugDisplay->SetState(prevState); } QtViewport::Update(); @@ -694,6 +707,11 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) } } SetCurrentCursor(STD_CURSOR_GAME); + + if (ShouldPreviewFullscreen()) + { + StartFullscreenPreview(); + } } if (m_renderViewport) @@ -712,6 +730,11 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) m_bInOrbitMode = false; m_bInZoomMode = false; + if (m_inFullscreenPreview) + { + StopFullscreenPreview(); + } + RestoreViewportAfterGameMode(); } @@ -1368,11 +1391,35 @@ void EditorViewportWidget::SetViewportId(int id) { CViewport::SetViewportId(id); + // Clear the cached debugdisplay pointer. we're about to delete that render viewport, and deleting the render + // viewport invalidates the debugdisplay. + m_debugDisplay = nullptr; + + // First delete any existing layout + // This also deletes any existing render viewport widget (since it will be added to the layout) + // Below is the typical method of clearing a QLayout, see e.g. https://doc.qt.io/qt-5/qlayout.html#takeAt + if (QLayout* thisLayout = layout()) + { + QLayoutItem* item; + while ((item = thisLayout->takeAt(0)) != nullptr) + { + if (QWidget* widget = item->widget()) + { + delete widget; + } + thisLayout->removeItem(item); + delete item; + } + delete thisLayout; + } + // Now that we have an ID, we can initialize our viewport. m_renderViewport = new AtomToolsFramework::RenderViewportWidget(this, false); if (!m_renderViewport->InitializeViewportContext(id)) { AZ_Warning("EditorViewportWidget", false, "Failed to initialize RenderViewportWidget's ViewportContext"); + delete m_renderViewport; + m_renderViewport = nullptr; return; } auto viewportContext = m_renderViewport->GetViewportContext(); @@ -3027,4 +3074,116 @@ float EditorViewportSettings::AngleStep() const return SandboxEditor::AngleSnappingSize(); } +AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); + +bool EditorViewportWidget::ShouldPreviewFullscreen() const +{ + CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout(); + if (!layout) + { + AZ_Assert(false, "CRenderViewport: No View Manager layout"); + return false; + } + + // Doesn't work with split layout + if (layout->GetLayout() != EViewLayout::ET_Layout0) + { + return false; + } + + // Not supported in VR + if (gSettings.bEnableGameModeVR) + { + return false; + } + + // If level not loaded, don't preview in fullscreen (preview shouldn't work at all without a level, but it does) + if (auto ge = GetIEditor()->GetGameEngine()) + { + if (!ge->IsLevelLoaded()) + { + return false; + } + } + + // Check 'ed_previewGameInFullscreen_once' + if (ed_previewGameInFullscreen_once) + { + ed_previewGameInFullscreen_once = true; + return true; + } + else + { + return false; + } +} + +void EditorViewportWidget::StartFullscreenPreview() +{ + AZ_Assert(!m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when already in full screen preview"); + m_inFullscreenPreview = true; + + // Pick the screen on which the main window lies to use as the screen for the full screen preview + const QScreen* screen = MainWindow::instance()->screen(); + const QRect screenGeometry = screen->geometry(); + + // Unparent this and show it, which turns it into a free floating window + // Also set style to frameless and disable resizing by user + setParent(nullptr); + setWindowFlag(Qt::FramelessWindowHint, true); + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, true); + setFixedSize(screenGeometry.size()); + move(QPoint(screenGeometry.x(), screenGeometry.y())); + showMaximized(); + + // This must be done after unparenting this widget above + MainWindow::instance()->hide(); +} + +void EditorViewportWidget::StopFullscreenPreview() +{ + AZ_Assert(m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when not in full screen preview"); + m_inFullscreenPreview = false; + + // Unset frameless window flags + setWindowFlag(Qt::FramelessWindowHint, false); + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, false); + + // Unset fixed size (note that 50x50 is the minimum set in the constructor) + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + setMinimumSize(50, 50); + + // Attach this viewport to the primary view pane (whose index is 0). + if (CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout()) + { + if (CLayoutViewPane* viewPane = layout->GetViewPaneByIndex(0)) + { + // Force-reattach this viewport to its view pane by first detaching + viewPane->DetachViewport(); + viewPane->AttachViewport(this); + + // Set the main widget of the layout, which causes this widgets size to be bound to the layout + // and the viewport title bar to be displayed + layout->SetMainWidget(viewPane); + } + else + { + AZ_Assert(false, "CRenderViewport: No view pane with ID 0 (primary view pane)"); + } + } + else + { + AZ_Assert(false, "CRenderViewport: No View Manager layout"); + } + + // Set this as the selected viewport + GetIEditor()->GetViewManager()->SelectViewport(this); + + // Show this widget (setting flags may hide it) + showNormal(); + + // Show the main window + MainWindow::instance()->show(); +} + #include diff --git a/Code/Sandbox/Editor/EditorViewportWidget.h b/Code/Sandbox/Editor/EditorViewportWidget.h index 6b2db65847..511a7910c6 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.h +++ b/Code/Sandbox/Editor/EditorViewportWidget.h @@ -385,6 +385,11 @@ protected: }; void ResetToViewSourceType(const ViewSourceType& viewSourType); + bool ShouldPreviewFullscreen() const; + void StartFullscreenPreview(); + void StopFullscreenPreview(); + + bool m_inFullscreenPreview = false; bool m_bRenderContextCreated = false; bool m_bInRotateMode = false; bool m_bInMoveMode = false; diff --git a/Code/Sandbox/Editor/LayoutWnd.h b/Code/Sandbox/Editor/LayoutWnd.h index 2af56f907c..66c9a9e889 100644 --- a/Code/Sandbox/Editor/LayoutWnd.h +++ b/Code/Sandbox/Editor/LayoutWnd.h @@ -113,6 +113,8 @@ public: //! Switch 2D viewports. void Cycle2DViewport(); + using AzQtComponents::ToolBarArea::SetMainWidget; + public slots: void ResetLayout(); diff --git a/Code/Sandbox/Editor/MainWindow.cpp b/Code/Sandbox/Editor/MainWindow.cpp index ddc8ff5058..1ad97f31de 100644 --- a/Code/Sandbox/Editor/MainWindow.cpp +++ b/Code/Sandbox/Editor/MainWindow.cpp @@ -949,6 +949,12 @@ void MainWindow::InitActions() .SetApplyHoverEffect() .SetCheckable(true) .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame); + am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play &Game (Maximized)")) + .SetShortcut(tr("Ctrl+Shift+G")) + .SetStatusTip(tr("Activate the game input mode (maximized)")) + .SetIcon(Style::icon("Play")) + .SetApplyHoverEffect() + .SetCheckable(true); am->AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, tr("Play Controls")) .SetText(tr("Play Controls")); am->AddAction(ID_SWITCH_PHYSICS, tr("Simulate")) @@ -1266,10 +1272,25 @@ void MainWindow::OnGameModeChanged(bool inGameMode) { menuBar()->setDisabled(inGameMode); m_toolbarManager->SetEnabled(!inGameMode); - QAction* action = m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME); - action->blockSignals(true); // avoid a loop - action->setChecked(inGameMode); - action->blockSignals(false); + + // block signals on the switch to game actions before setting the checked state, as + // setting the checked state triggers the action, which will re-enter this function + // and result in an infinite loop + AZStd::vector actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME), m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN) }; + for (auto action : actions) + { + action->blockSignals(true); + } + + for (auto action : actions) + { + action->setChecked(inGameMode); + } + + for (auto action : actions) + { + action->blockSignals(false); + } } void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev) diff --git a/Code/Sandbox/Editor/Resource.h b/Code/Sandbox/Editor/Resource.h index bd6ae94184..8ef6c0b906 100644 --- a/Code/Sandbox/Editor/Resource.h +++ b/Code/Sandbox/Editor/Resource.h @@ -111,6 +111,7 @@ #define ID_EDIT_FETCH 33465 #define ID_FILE_EXPORTTOGAMENOSURFACETEXTURE 33473 #define ID_VIEW_SWITCHTOGAME 33477 +#define ID_VIEW_SWITCHTOGAME_FULLSCREEN 33478 #define ID_EDIT_DELETE 33480 #define ID_MOVE_OBJECT 33481 #define ID_RENAME_OBJ 33483 diff --git a/Code/Sandbox/Editor/Settings.cpp b/Code/Sandbox/Editor/Settings.cpp index 15d3e793a5..f805e2e84b 100644 --- a/Code/Sandbox/Editor/Settings.cpp +++ b/Code/Sandbox/Editor/Settings.cpp @@ -26,6 +26,7 @@ #include #include #include +#include // AzFramework #include @@ -926,6 +927,8 @@ void SEditorSettings::Load() } ////////////////////////////////////////////////////////////////////////// +AZ_CVAR(bool, ed_previewGameInFullscreen_once, false, nullptr, AZ::ConsoleFunctorFlags::IsInvisible, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen once"); + void SEditorSettings::PostInitApply() { if (!gEnv || !gEnv->pConsole) diff --git a/Code/Sandbox/Editor/ToolbarManager.cpp b/Code/Sandbox/Editor/ToolbarManager.cpp index 137057a4fa..0c68797b6a 100644 --- a/Code/Sandbox/Editor/ToolbarManager.cpp +++ b/Code/Sandbox/Editor/ToolbarManager.cpp @@ -603,6 +603,7 @@ AmazonToolbar ToolbarManager::GetPlayConsoleToolbar() const t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_VIEW_SWITCHTOGAME, TOOLBARS_WITH_PLAY_GAME); + t.AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, TOOLBARS_WITH_PLAY_GAME); t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_SWITCH_PHYSICS, TOOLBARS_WITH_PLAY_GAME); return t; diff --git a/Code/Sandbox/Editor/ViewportTitleDlg.cpp b/Code/Sandbox/Editor/ViewportTitleDlg.cpp index 48075597cd..2c4b29773d 100644 --- a/Code/Sandbox/Editor/ViewportTitleDlg.cpp +++ b/Code/Sandbox/Editor/ViewportTitleDlg.cpp @@ -181,7 +181,8 @@ void CViewportTitleDlg::SetupCameraDropdownMenu() auto comboBoxTextChanged = static_cast(&QComboBox::currentTextChanged); SetSpeedComboBox(cameraMoveSpeed); - m_cameraSpeed->setInsertPolicy(QComboBox::NoInsert); + m_cameraSpeed->setInsertPolicy(QComboBox::InsertAtBottom); + m_cameraSpeed->setDuplicatesEnabled(false); connect(m_cameraSpeed, comboBoxTextChanged, this, &CViewportTitleDlg::OnUpdateMoveSpeedText); connect(m_cameraSpeed->lineEdit(), &QLineEdit::returnPressed, this, &CViewportTitleDlg::OnSpeedComboBoxEnter); diff --git a/Code/Sandbox/Editor/ViewportTitleDlg.h b/Code/Sandbox/Editor/ViewportTitleDlg.h index 255354dcbb..7978277ce0 100644 --- a/Code/Sandbox/Editor/ViewportTitleDlg.h +++ b/Code/Sandbox/Editor/ViewportTitleDlg.h @@ -117,11 +117,11 @@ protected: // Speed combobox/lineEdit settings double m_minSpeed = 0.01; double m_maxSpeed = 100.0; - double m_speedStep = 0.01; + double m_speedStep = 0.001; int m_numDecimals = 3; // Speed presets - float m_speedPresetValues[3] = { 0.1f, 1.0f, 10.0f }; + float m_speedPresetValues[4] = { 0.01f, 0.1f, 1.0f, 10.0f }; double m_fieldWidthMultiplier = 1.8; diff --git a/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake b/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake index 194e99b247..1e78d1dc6c 100644 --- a/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake +++ b/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake @@ -19,6 +19,7 @@ set(FILES native/AssetManager/AssetRequestHandler.cpp native/AssetManager/AssetRequestHandler.h native/AssetManager/assetScanFolderInfo.h + native/AssetManager/assetScanFolderInfo.cpp native/AssetManager/assetScanner.cpp native/AssetManager/assetScanner.h native/AssetManager/assetScannerWorker.cpp diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.cpp b/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.cpp new file mode 100644 index 0000000000..f849b4ee49 --- /dev/null +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.cpp @@ -0,0 +1,42 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AssetProcessor +{ + ScanFolderInfo::ScanFolderInfo( + QString path, + QString displayName, + QString portableKey, + bool isRoot, + bool recurseSubFolders, + AZStd::vector platforms, + int order, + AZ::s64 scanFolderID, + bool canSaveNewAssets) + : m_scanPath(path) + , m_displayName(displayName) + , m_portableKey (portableKey) + , m_isRoot(isRoot) + , m_recurseSubFolders(recurseSubFolders) + , m_order(order) + , m_scanFolderID(scanFolderID) + , m_platforms(platforms) + , m_canSaveNewAssets(canSaveNewAssets) + { + m_scanPath = AssetUtilities::NormalizeFilePath(m_scanPath); + // note that m_scanFolderID is 0 unless its filled in from the DB. + } + +} // end namespace AssetProcessor diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.h b/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.h index 5767822451..5fbda237d7 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetScanFolderInfo.h @@ -33,19 +33,7 @@ namespace AssetProcessor AZStd::vector platforms = AZStd::vector{}, int order = 0, AZ::s64 scanFolderID = 0, - bool canSaveNewAssets = false) - : m_scanPath(path) - , m_displayName(displayName) - , m_portableKey (portableKey) - , m_isRoot(isRoot) - , m_recurseSubFolders(recurseSubFolders) - , m_order(order) - , m_scanFolderID(scanFolderID) - , m_platforms(platforms) - , m_canSaveNewAssets(canSaveNewAssets) - { - // note that m_scanFolderID is 0 unless its filled in from the DB. - } + bool canSaveNewAssets = false); ScanFolderInfo() = default; ScanFolderInfo(const ScanFolderInfo& other) = default; diff --git a/Code/Tools/AzTestRunner/CMakeLists.txt b/Code/Tools/AzTestRunner/CMakeLists.txt index e6dd09e15b..fcff173b01 100644 --- a/Code/Tools/AzTestRunner/CMakeLists.txt +++ b/Code/Tools/AzTestRunner/CMakeLists.txt @@ -13,7 +13,7 @@ ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${P include(${pal_dir}/platform_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) -if(PAL_TRAIT_AZTESTRUNNER_SUPPORTED) +if(PAL_TRAIT_AZTESTRUNNER_SUPPORTED AND NOT LY_MONOLITHIC_GAME) ly_add_target( NAME AzTestRunner ${PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE} diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index aeb7be9793..434ce1424e 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -20,12 +20,11 @@ if (NOT python_package_name) message(WARNING "Python was not found in the package assocation list. Did someone call ly_associate_package(xxxxxxx Python) ?") endif() + ly_add_target( - NAME ProjectManager APPLICATION - OUTPUT_NAME o3de + NAME ProjectManager.Static STATIC NAMESPACE AZ AUTOMOC - AUTORCC FILES_CMAKE project_manager_files.cmake Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake @@ -47,6 +46,60 @@ ly_add_target( 3rdParty::pybind11 AZ::AzCore AZ::AzFramework - AZ::AzToolsFramework AZ::AzQtComponents -) \ No newline at end of file +) + +ly_add_target( + NAME ProjectManager APPLICATION + OUTPUT_NAME o3de + NAMESPACE AZ + AUTORCC + FILES_CMAKE + project_manager_app_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::Qt::Core + 3rdParty::Qt::Concurrent + 3rdParty::Qt::Widgets + 3rdParty::Python + 3rdParty::pybind11 + AZ::AzCore + AZ::AzFramework + AZ::AzQtComponents + AZ::ProjectManager.Static +) + +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME ProjectManager.Tests EXECUTABLE + NAMESPACE AZ + AUTORCC + FILES_CMAKE + project_manager_tests_files.cmake + Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + Platform/${PAL_PLATFORM_NAME} + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::Qt::Core + 3rdParty::Qt::Concurrent + 3rdParty::Qt::Widgets + 3rdParty::Python + 3rdParty::pybind11 + AZ::AzTest + AZ::AzFramework + AZ::AzFrameworkTestShared + AZ::ProjectManager.Static + ) + + ly_add_googletest( + NAME AZ::ProjectManager.Tests + TEST_COMMAND $ --unittest + ) + +endif() diff --git a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake new file mode 100644 index 0000000000..e79da7183d --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Linux.h +) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h new file mode 100644 index 0000000000..c8b428a1c2 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS true diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..639ef8a387 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake new file mode 100644 index 0000000000..a2d480de40 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Mac.h +) diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h new file mode 100644 index 0000000000..053db745ea --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..af8817998f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake new file mode 100644 index 0000000000..00d9da3db3 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Windows.h +) diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..915a86644a --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h new file mode 100644 index 0000000000..053db745ea --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h @@ -0,0 +1,15 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 80470591a8..efc01802b7 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -7,6 +7,11 @@ QMainWindow { margin:0; } +#ScreensCtrl { + min-width:1200px; + min-height:800px; +} + QPushButton:focus { outline: none; border:1px solid #1e70eb; diff --git a/Code/Tools/ProjectManager/Source/Application.cpp b/Code/Tools/ProjectManager/Source/Application.cpp new file mode 100644 index 0000000000..c566e76ef1 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/Application.cpp @@ -0,0 +1,186 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace O3DE::ProjectManager +{ + Application::~Application() + { + TearDown(); + } + + bool Application::Init(bool interactive) + { + constexpr const char* applicationName { "O3DE" }; + + QApplication::setOrganizationName(applicationName); + QApplication::setOrganizationDomain("o3de.org"); + + QCoreApplication::setApplicationName(applicationName); + QCoreApplication::setApplicationVersion("1.0"); + + // Use the LogComponent for non-dev logging log + RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor()); + + // set the log alias to .o3de/Logs instead of the default user/logs + AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory(); + + // DevWriteStorage is where the event log is written during development + m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage, path.LexicallyNormal().Native()); + + // Save event logs to .o3de/Logs/eventlogger/EventLogO3DE.azsl + m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::BuildTargetNameKey, applicationName); + + Start(AzFramework::Application::Descriptor()); + + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + + QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); + + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); + + // Create the actual Qt Application - this needs to happen before using QMessageBox + m_app.reset(new QApplication(*GetArgC(), *GetArgV())); + + if(!InitLog(applicationName)) + { + AZ_Warning("ProjectManager", false, "Failed to init logging"); + } + + m_pythonBindings = AZStd::make_unique(GetEngineRoot()); + if (!m_pythonBindings || !m_pythonBindings->PythonStarted()) + { + if (interactive) + { + QMessageBox::critical(nullptr, QObject::tr("Failed to start Python"), + QObject::tr("This tool requires an O3DE engine with a Python runtime, " + "but either Python is missing or mis-configured. Please rename " + "your python/runtime folder to python/runtime_bak, then run " + "python/get_python.bat to restore the Python runtime folder.")); + } + return false; + } + + const AZ::CommandLine* commandLine = GetCommandLine(); + AZ_Assert(commandLine, "Failed to get command line"); + + ProjectManagerScreen startScreen = ProjectManagerScreen::Projects; + if (size_t screenSwitchCount = commandLine->GetNumSwitchValues("screen"); screenSwitchCount > 0) + { + QString screenOption = commandLine->GetSwitchValue("screen", screenSwitchCount - 1).c_str(); + ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption); + if (screen != ProjectManagerScreen::Invalid) + { + startScreen = screen; + } + } + + AZ::IO::FixedMaxPath projectPath; + if (size_t projectSwitchCount = commandLine->GetNumSwitchValues("project-path"); projectSwitchCount > 0) + { + projectPath = commandLine->GetSwitchValue("project-path", projectSwitchCount - 1).c_str(); + } + + m_mainWindow.reset(new ProjectManagerWindow(nullptr, projectPath, startScreen)); + + return true; + } + + bool Application::InitLog(const char* logName) + { + if (!m_entity) + { + // override the log alias to the O3de Logs directory instead of the default project user/Logs folder + AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory(); + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ_Assert(fileIO, "Failed to get FileIOBase instance"); + + fileIO->SetAlias("@log@", path.LexicallyNormal().Native().c_str()); + + // this entity exists because we need a home for LogComponent + // and cannot use the system entity because we need to be able to call SetLogFileBaseName + // so the log will be named O3DE.log + m_entity = aznew AZ::Entity("Application Entity"); + if (m_entity) + { + AzFramework::LogComponent* logger = aznew AzFramework::LogComponent(); + AZ_Assert(logger, "Failed to create LogComponent"); + logger->SetLogFileBaseName(logName); + m_entity->AddComponent(logger); + m_entity->Init(); + m_entity->Activate(); + } + } + + return m_entity != nullptr; + } + + void Application::TearDown() + { + if (m_entity) + { + m_entity->Deactivate(); + delete m_entity; + m_entity = nullptr; + } + + m_pythonBindings.reset(); + m_mainWindow.reset(); + m_app.reset(); + } + + bool Application::Run() + { + // Set up the Style Manager + AzQtComponents::StyleManager styleManager(qApp); + styleManager.initialize(qApp, GetEngineRoot()); + + // setup stylesheets and hot reloading + AZ::IO::FixedMaxPath engineRoot(GetEngineRoot()); + QDir rootDir(engineRoot.c_str()); + const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); + const auto qrcPath = QStringLiteral(":/ProjectManager/style"); + AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRoot); + + // 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 + auto wrapper = new AzQtComponents::WindowDecorationWrapper(); + wrapper->setGuest(m_mainWindow.data()); + wrapper->show(); + m_mainWindow->show(); + + qApp->setQuitOnLastWindowClosed(true); + + // Run the application + return qApp->exec(); + } + +} diff --git a/Code/Tools/ProjectManager/Source/Application.h b/Code/Tools/ProjectManager/Source/Application.h new file mode 100644 index 0000000000..d4e94e8dd4 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/Application.h @@ -0,0 +1,48 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#include +#endif + +namespace AZ +{ + class Entity; +} + +namespace O3DE::ProjectManager +{ + class Application + : public AzFramework::Application + { + public: + using AzFramework::Application::Application; + virtual ~Application(); + + bool Init(bool interactive = true); + bool Run(); + void TearDown(); + + private: + bool InitLog(const char* logName); + + AZStd::unique_ptr m_pythonBindings; + QSharedPointer m_app; + QSharedPointer m_mainWindow; + + AZ::Entity* m_entity = nullptr; + }; +} diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index 6342041da4..cf597745ea 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -25,7 +25,7 @@ namespace O3DE::ProjectManager EngineSettingsScreen::EngineSettingsScreen(QWidget* parent) : ScreenWidget(parent) { - auto* layout = new QVBoxLayout(this); + auto* layout = new QVBoxLayout(); layout->setAlignment(Qt::AlignTop); setObjectName("engineSettingsScreen"); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 4424767c0b..2c92af4e51 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -82,6 +82,8 @@ namespace O3DE::ProjectManager m_headerWidget->ReinitForProject(); + connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter); + // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ QModelIndex firstModelIndex = m_gemListView->model()->index(0,0); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index a6a4e95ff9..7c6150ff99 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -26,6 +26,7 @@ namespace O3DE::ProjectManager const QVector& elementNames, const QVector& elementCounts, bool showAllLessButton, + bool collapsed, int defaultShowCount, QWidget* parent) : QWidget(parent) @@ -40,6 +41,7 @@ namespace O3DE::ProjectManager QHBoxLayout* collapseLayout = new QHBoxLayout(); m_collapseButton = new QPushButton(); m_collapseButton->setCheckable(true); + m_collapseButton->setChecked(collapsed); m_collapseButton->setFlat(true); m_collapseButton->setFocusPolicy(Qt::NoFocus); m_collapseButton->setFixedWidth(s_collapseButtonSize); @@ -178,6 +180,11 @@ namespace O3DE::ProjectManager return m_buttonGroup; } + bool FilterCategoryWidget::IsCollapsed() + { + return m_collapseButton->isChecked(); + } + GemFilterWidget::GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent) : QScrollArea(parent) , m_filterProxyModel(filterProxyModel) @@ -193,20 +200,106 @@ namespace O3DE::ProjectManager QWidget* mainWidget = new QWidget(); setWidget(mainWidget); - m_mainLayout = new QVBoxLayout(); - m_mainLayout->setAlignment(Qt::AlignTop); - mainWidget->setLayout(m_mainLayout); + QVBoxLayout* mainLayout = new QVBoxLayout(); + mainLayout->setAlignment(Qt::AlignTop); + mainWidget->setLayout(mainLayout); QLabel* filterByLabel = new QLabel("Filter by"); filterByLabel->setStyleSheet("font-size: 16px;"); - m_mainLayout->addWidget(filterByLabel); + mainLayout->addWidget(filterByLabel); + QWidget* filterSection = new QWidget(this); + mainLayout->addWidget(filterSection); + + m_filterLayout = new QVBoxLayout(); + m_filterLayout->setAlignment(Qt::AlignTop); + m_filterLayout->setContentsMargins(0, 0, 0, 0); + filterSection->setLayout(m_filterLayout); + + ResetGemStatusFilter(); AddGemOriginFilter(); AddTypeFilter(); AddPlatformFilter(); AddFeatureFilter(); } + void GemFilterWidget::ResetGemStatusFilter() + { + QVector elementNames; + QVector elementCounts; + const int totalGems = m_gemModel->rowCount(); + const int selectedGemTotal = m_gemModel->TotalAddedGems(); + + elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Unselected)); + elementCounts.push_back(totalGems - selectedGemTotal); + + elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Selected)); + elementCounts.push_back(selectedGemTotal); + + bool wasCollapsed = false; + if (m_statusFilter) + { + wasCollapsed = m_statusFilter->IsCollapsed(); + } + + FilterCategoryWidget* filterWidget = + new FilterCategoryWidget("Status", elementNames, elementCounts, /*showAllLessButton=*/false, /*collapsed*/wasCollapsed); + if (m_statusFilter) + { + m_filterLayout->replaceWidget(m_statusFilter, filterWidget); + } + else + { + m_filterLayout->addWidget(filterWidget); + } + + m_statusFilter->deleteLater(); + m_statusFilter = filterWidget; + + const GemSortFilterProxyModel::GemStatus currentFilterState = m_filterProxyModel->GetGemStatus(); + const QList buttons = m_statusFilter->GetButtonGroup()->buttons(); + for (int statusFilterIndex = 0; statusFilterIndex < buttons.size(); ++statusFilterIndex) + { + const GemSortFilterProxyModel::GemStatus gemStatus = static_cast(statusFilterIndex); + QAbstractButton* button = buttons[statusFilterIndex]; + + if (static_cast(statusFilterIndex) == currentFilterState) + { + button->setChecked(true); + } + + connect( + button, &QAbstractButton::toggled, this, + [=](bool checked) + { + GemSortFilterProxyModel::GemStatus filterStatus = m_filterProxyModel->GetGemStatus(); + if (checked) + { + if (filterStatus == GemSortFilterProxyModel::GemStatus::NoFilter) + { + filterStatus = gemStatus; + } + else + { + filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter; + } + } + else + { + if (filterStatus != gemStatus) + { + filterStatus = static_cast(!gemStatus); + } + else + { + filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter; + } + } + m_filterProxyModel->SetGemStatus(filterStatus); + }); + } + } + void GemFilterWidget::AddGemOriginFilter() { QVector elementNames; @@ -233,7 +326,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Provider", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -283,7 +376,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Type", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -333,7 +426,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Supported Platforms", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -388,8 +481,8 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Features", elementNames, elementCounts, - /*showAllLessButton=*/true, /*defaultShowCount=*/5); - m_mainLayout->addWidget(filterWidget); + /*showAllLessButton=*/true, false, /*defaultShowCount=*/5); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h index 017eadc020..520370eb44 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h @@ -37,11 +37,14 @@ namespace O3DE::ProjectManager const QVector& elementNames, const QVector& elementCounts, bool showAllLessButton = true, + bool collapsed = false, int defaultShowCount = 4, QWidget* parent = nullptr); QButtonGroup* GetButtonGroup(); + bool IsCollapsed(); + private: void UpdateCollapseState(); void UpdateSeeMoreLess(); @@ -66,14 +69,18 @@ namespace O3DE::ProjectManager explicit GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); ~GemFilterWidget() = default; + public slots: + void ResetGemStatusFilter(); + private: void AddGemOriginFilter(); void AddTypeFilter(); void AddPlatformFilter(); void AddFeatureFilter(); - QVBoxLayout* m_mainLayout = nullptr; + QVBoxLayout* m_filterLayout = nullptr; GemModel* m_gemModel = nullptr; GemSortFilterProxyModel* m_filterProxyModel = nullptr; + FilterCategoryWidget* m_statusFilter = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 8aa68fb7a2..03787de7e8 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -204,7 +204,6 @@ namespace O3DE::ProjectManager painter->save(); const QRect buttonRect = CalcButtonRect(contentRect); QPoint circleCenter; - QString buttonText; const bool isAdded = GemModel::IsAdded(modelIndex); if (isAdded) @@ -213,34 +212,15 @@ namespace O3DE::ProjectManager painter->setPen(m_buttonEnabledColor); circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1); - buttonText = "Added"; } else { circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius, 1); - buttonText = "Get"; } // Rounded rect painter->drawRoundedRect(buttonRect, s_buttonBorderRadius, s_buttonBorderRadius); - // Text - QFont font; - QRect textRect = GetTextRect(font, buttonText, s_buttonFontSize); - if (isAdded) - { - textRect = QRect(buttonRect.left(), buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height()); - } - else - { - textRect = QRect(buttonRect.left() + s_buttonCircleRadius * 2.0, buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height()); - } - - font.setPixelSize(s_buttonFontSize); - painter->setFont(font); - painter->setPen(m_textColor); - painter->drawText(textRect, Qt::AlignCenter, buttonText); - // Circle painter->setBrush(m_textColor); painter->drawEllipse(circleCenter, s_buttonCircleRadius, s_buttonCircleRadius); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index bc287e3c61..ad1b57a27c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -74,6 +75,15 @@ namespace O3DE::ProjectManager gemSummaryLabel->setStyleSheet("font-size: 12px;"); columnHeaderLayout->addWidget(gemSummaryLabel); + QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + columnHeaderLayout->addSpacerItem(horizontalSpacer); + + QLabel* gemSelectedLabel = new QLabel(tr("Selected")); + gemSelectedLabel->setStyleSheet("font-size: 12px;"); + columnHeaderLayout->addWidget(gemSelectedLabel); + + columnHeaderLayout->addSpacing(60); + vLayout->addLayout(columnHeaderLayout); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 6c09c95572..5dc40723c9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -235,4 +235,19 @@ namespace O3DE::ProjectManager } return result; } + + int GemModel::TotalAddedGems() const + { + int result = 0; + for (int row = 0; row < rowCount(); ++row) + { + const QModelIndex modelIndex = index(row, 0); + if (IsAdded(modelIndex)) + { + ++result; + } + } + return result; + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 77f973a91c..2e05472cdf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -63,6 +63,8 @@ namespace O3DE::ProjectManager QVector GatherGemsToBeAdded() const; QVector GatherGemsToBeRemoved() const; + int TotalAddedGems() const; + private: enum UserRole { diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index d8f41c077e..c1360cabc6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -37,6 +37,16 @@ namespace O3DE::ProjectManager return false; } + // Gem status + if (m_gemStatusFilter != GemStatus::NoFilter) + { + const GemStatus sourceGemStatus = static_cast(GemModel::IsAdded(sourceIndex)); + if (m_gemStatusFilter != sourceGemStatus) + { + return false; + } + } + // Gem origins if (m_gemOriginFilter) { @@ -125,6 +135,19 @@ namespace O3DE::ProjectManager return true; } + QString GemSortFilterProxyModel::GetGemStatusString(GemStatus status) + { + switch (status) + { + case Unselected: + return "Unselected"; + case Selected: + return "Selected"; + default: + return ""; + } + } + void GemSortFilterProxyModel::InvalidateFilter() { invalidate(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index f24a724ecf..fcde226f40 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -29,8 +29,17 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: + enum GemStatus + { + NoFilter = -1, + Unselected, + Selected + }; + GemSortFilterProxyModel(GemModel* sourceModel, QObject* parent = nullptr); + static QString GetGemStatusString(GemStatus status); + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; GemModel* GetSourceModel() const { return m_sourceModel; } @@ -38,6 +47,9 @@ namespace O3DE::ProjectManager void SetSearchString(const QString& searchString) { m_searchString = searchString; InvalidateFilter(); } + GemStatus GetGemStatus() const { return m_gemStatusFilter; } + void SetGemStatus(GemStatus gemStatus) { m_gemStatusFilter = gemStatus; InvalidateFilter(); } + GemInfo::GemOrigins GetGemOrigins() const { return m_gemOriginFilter; } void SetGemOrigins(const GemInfo::GemOrigins& gemOrigins) { m_gemOriginFilter = gemOrigins; InvalidateFilter(); } @@ -61,6 +73,7 @@ namespace O3DE::ProjectManager AzQtComponents::SelectionProxyModel* m_selectionProxyModel = nullptr; QString m_searchString; + GemStatus m_gemStatusFilter = GemStatus::NoFilter; GemInfo::GemOrigins m_gemOriginFilter = {}; GemInfo::Platforms m_platformFilter = {}; GemInfo::Types m_typeFilter = {}; diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index 761572ffa5..3bde0a310d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -33,7 +33,7 @@ namespace O3DE::ProjectManager { setObjectName("labelButton"); - QVBoxLayout* vLayout = new QVBoxLayout(this); + QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setContentsMargins(0, 0, 0, 0); vLayout->setSpacing(5); diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index cb1398cc61..59be0aaa35 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -13,21 +13,11 @@ #include #include -#include -#include -#include -#include -#include - -#include - namespace O3DE::ProjectManager { - ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen) + ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen) : QMainWindow(parent) { - m_pythonBindings = AZStd::make_unique(engineRootPath); - setWindowTitle(tr("O3DE Project Manager")); ScreensCtrl* screensCtrl = new ScreensCtrl(); @@ -44,15 +34,6 @@ namespace O3DE::ProjectManager setCentralWidget(screensCtrl); - // setup stylesheets and hot reloading - QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast(engineRootPath.Native().size())); - const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); - const auto qrcPath = QStringLiteral(":/ProjectManager/style"); - AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath); - - // set stylesheet after creating the screens or their styles won't get updated - AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss")); - // always push the projects screen first so we have something to come back to if (startScreen != ProjectManagerScreen::Projects) { @@ -66,10 +47,4 @@ namespace O3DE::ProjectManager emit screensCtrl->NotifyCurrentProject(path); } } - - ProjectManagerWindow::~ProjectManagerWindow() - { - m_pythonBindings.reset(); - } - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h index 758af8fc00..db2b1fd304 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h @@ -13,7 +13,7 @@ #if !defined(Q_MOC_RUN) #include -#include +#include #include #endif @@ -25,12 +25,8 @@ namespace O3DE::ProjectManager Q_OBJECT public: - explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, + explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen = ProjectManagerScreen::Projects); - ~ProjectManagerWindow(); - - private: - AZStd::unique_ptr m_pythonBindings; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp index 26711753d4..b198724353 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp @@ -37,7 +37,7 @@ namespace O3DE::ProjectManager // if we don't set this in a frame (just use a sub-layout) all the content will align incorrectly horizontally QFrame* projectSettingsFrame = new QFrame(this); projectSettingsFrame->setObjectName("projectSettings"); - m_verticalLayout = new QVBoxLayout(this); + m_verticalLayout = new QVBoxLayout(); // you cannot remove content margins in qss m_verticalLayout->setContentsMargins(0, 0, 0, 0); diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index 8e41e52643..6633558406 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -85,7 +85,7 @@ namespace O3DE::ProjectManager QFrame* frame = new QFrame(this); frame->setObjectName("firstTimeContent"); { - QVBoxLayout* layout = new QVBoxLayout(this); + QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setAlignment(Qt::AlignTop); frame->setLayout(layout); @@ -100,7 +100,7 @@ namespace O3DE::ProjectManager "available by downloading our sample project.")); layout->addWidget(introLabel); - QHBoxLayout* buttonLayout = new QHBoxLayout(this); + QHBoxLayout* buttonLayout = new QHBoxLayout(); buttonLayout->setAlignment(Qt::AlignLeft); buttonLayout->setSpacing(s_spacerSize); diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index bb6c05a472..0e00319b6b 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -226,7 +226,7 @@ namespace O3DE::ProjectManager PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath) : m_enginePath(enginePath) { - StartPython(); + m_pythonStarted = StartPython(); } PythonBindings::~PythonBindings() @@ -234,6 +234,11 @@ namespace O3DE::ProjectManager StopPython(); } + bool PythonBindings::PythonStarted() + { + return m_pythonStarted && Py_IsInitialized(); + } + bool PythonBindings::StartPython() { if (Py_IsInitialized()) @@ -246,7 +251,7 @@ namespace O3DE::ProjectManager AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, m_enginePath.c_str()); if (!AZ::IO::SystemFile::Exists(pyBasePath.c_str())) { - AZ_Assert(false, "Python home path must exist. path:%s", pyBasePath.c_str()); + AZ_Error("python", false, "Python home path does not exist: %s", pyBasePath.c_str()); return false; } @@ -351,6 +356,11 @@ namespace O3DE::ProjectManager AZ::Outcome PythonBindings::ExecuteWithLockErrorHandling(AZStd::function executionCallback) { + if (!Py_IsInitialized()) + { + return AZ::Failure("Python is not initialized"); + } + AZStd::lock_guard lock(m_lock); pybind11::gil_scoped_release release; pybind11::gil_scoped_acquire acquire; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 5700ede850..065867a130 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -34,6 +34,8 @@ namespace O3DE::ProjectManager ~PythonBindings() override; // PythonBindings overrides + bool PythonStarted() override; + // Engine AZ::Outcome GetEngineInfo() override; bool SetEngineInfo(const EngineInfo& engineInfo) override; @@ -70,6 +72,8 @@ namespace O3DE::ProjectManager bool StopPython(); + bool m_pythonStarted = false; + AZ::IO::FixedMaxPath m_enginePath; pybind11::handle m_engineTemplate; AZStd::recursive_mutex m_lock; diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index bc20d8e3f0..6d72bfee0c 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -34,6 +34,12 @@ namespace O3DE::ProjectManager IPythonBindings() = default; virtual ~IPythonBindings() = default; + /** + * Get whether Python was started or not. All Python functionality will fail if Python + * failed to start. + * @return true if Python was started successfully, false on failure + */ + virtual bool PythonStarted() = 0; // Engine diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index be1f0e5529..65f73accd1 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -189,11 +189,13 @@ namespace O3DE::ProjectManager { if (m_stack->currentIndex() == ScreenOrder::Gems) { - m_header->setSubTitle(QString(tr("Configure Gems for \"%1\"")).arg(m_projectInfo.m_projectName)); - m_nextButton->setText(tr("Confirm")); + m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName)); + m_header->setSubTitle(QString(tr("Configure Gems"))); + m_nextButton->setText(tr("Finalize")); } else { + m_header->setTitle(""); m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName)); m_nextButton->setText(tr("Save")); } diff --git a/Code/Tools/ProjectManager/Source/main.cpp b/Code/Tools/ProjectManager/Source/main.cpp index c597b8a729..1f4cadfb14 100644 --- a/Code/Tools/ProjectManager/Source/main.cpp +++ b/Code/Tools/ProjectManager/Source/main.cpp @@ -10,85 +10,26 @@ * */ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -using namespace O3DE::ProjectManager; +#include +#include int main(int argc, char* argv[]) { - QApplication::setOrganizationName("O3DE"); - QApplication::setOrganizationDomain("o3de.org"); - QCoreApplication::setApplicationName("ProjectManager"); - QCoreApplication::setApplicationVersion("1.0"); - - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); - QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); - AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); - - AZ::AllocatorInstance::Create(); int runSuccess = 0; + + // Call before using any Qt, or the app may not be able to locate Qt libs + AzQtComponents::PrepareQtPaths(); + + O3DE::ProjectManager::Application application(&argc, &argv); + if (!application.Init()) { - QApplication app(argc, argv); - - // Need to use settings registry to get EngineRootFolder - AZ::IO::FixedMaxPath engineRootPath; - { - AZ::ComponentApplication componentApplication; - auto settingsRegistry = AZ::SettingsRegistry::Get(); - settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); - } - - AzQtComponents::StyleManager styleManager(&app); - styleManager.initialize(&app, engineRootPath); - - // Get the initial start screen if one is provided via command line - constexpr char optionPrefix[] = "--"; - AZ::CommandLine commandLine(optionPrefix); - commandLine.Parse(argc, argv); - - ProjectManagerScreen startScreen = ProjectManagerScreen::Projects; - if(commandLine.HasSwitch("screen")) - { - QString screenOption = commandLine.GetSwitchValue("screen", 0).c_str(); - ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption); - if (screen != ProjectManagerScreen::Invalid) - { - startScreen = screen; - } - } - - AZ::IO::FixedMaxPath projectPath; - if (commandLine.HasSwitch("project-path")) - { - projectPath = commandLine.GetSwitchValue("project-path", 0).c_str(); - } - - ProjectManagerWindow window(nullptr, engineRootPath, projectPath, startScreen); - window.show(); - - // somethings is preventing us from moving the window to the center of the - // primary screen - likely an Az style or component helper - constexpr int width = 1200; - constexpr int height = 800; - window.resize(width, height); - - runSuccess = app.exec(); + AZ_Error("ProjectManager", false, "Failed to initialize"); + runSuccess = 1; + } + else + { + runSuccess = application.Run() ? 0 : 1; } - AZ::AllocatorInstance::Destroy(); return runSuccess; } diff --git a/Code/Tools/ProjectManager/project_manager_app_files.cmake b/Code/Tools/ProjectManager/project_manager_app_files.cmake new file mode 100644 index 0000000000..223683ddd9 --- /dev/null +++ b/Code/Tools/ProjectManager/project_manager_app_files.cmake @@ -0,0 +1,17 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Resources/ProjectManager.rc + Resources/ProjectManager.qrc + Resources/ProjectManager.qss + Source/main.cpp +) diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 633824f995..587b5907bb 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -1,4 +1,5 @@ # +# # All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or # its licensors. # @@ -10,10 +11,8 @@ # set(FILES - Resources/ProjectManager.rc - Resources/ProjectManager.qrc - Resources/ProjectManager.qss - Source/main.cpp + Source/Application.h + Source/Application.cpp Source/ScreenDefs.h Source/ScreenFactory.h Source/ScreenFactory.cpp diff --git a/Code/Tools/ProjectManager/project_manager_tests_files.cmake b/Code/Tools/ProjectManager/project_manager_tests_files.cmake new file mode 100644 index 0000000000..e1e84a43a7 --- /dev/null +++ b/Code/Tools/ProjectManager/project_manager_tests_files.cmake @@ -0,0 +1,17 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Resources/ProjectManager.qrc + Resources/ProjectManager.qss + tests/ApplicationTests.cpp + tests/main.cpp +) diff --git a/Code/Tools/ProjectManager/tests/ApplicationTests.cpp b/Code/Tools/ProjectManager/tests/ApplicationTests.cpp new file mode 100644 index 0000000000..c98b1a3a6f --- /dev/null +++ b/Code/Tools/ProjectManager/tests/ApplicationTests.cpp @@ -0,0 +1,47 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + class ProjectManagerApplicationTests + : public ::UnitTest::ScopedAllocatorSetupFixture + { + public: + + ProjectManagerApplicationTests() + { + m_application = AZStd::make_unique(); + } + + ~ProjectManagerApplicationTests() + { + m_application.reset(); + } + + AZStd::unique_ptr m_application; + }; + +#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS + TEST_F(ProjectManagerApplicationTests, DISABLED_Application_Init_Succeeds) +#else + TEST_F(ProjectManagerApplicationTests, Application_Init_Succeeds) +#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS + { + // we don't want to interact with actual GUI or display it + EXPECT_TRUE(m_application->Init(/*interactive=*/false)); + } +} diff --git a/Code/Tools/ProjectManager/tests/main.cpp b/Code/Tools/ProjectManager/tests/main.cpp new file mode 100644 index 0000000000..191bef846a --- /dev/null +++ b/Code/Tools/ProjectManager/tests/main.cpp @@ -0,0 +1,35 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +DECLARE_AZ_UNIT_TEST_MAIN(); + +int runDefaultRunner(int argc, char* argv[]) +{ + INVOKE_AZ_UNIT_TEST_MAIN(nullptr) + return 0; +} + +int main(int argc, char* argv[]) +{ + if (argc == 1) + { + // if no parameters are provided, add the --unittests parameter + constexpr int defaultArgc = 2; + char unittest_arg[] = "--unittests"; // Conversion from string literal to char* is not allowed per ISO C++11 + char* defaultArgv[defaultArgc] = { argv[0], unittest_arg }; + return runDefaultRunner(defaultArgc, defaultArgv); + } + INVOKE_AZ_UNIT_TEST_MAIN(nullptr); + return 0; +} diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp index 5357c32fa9..bcc007e3a7 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp @@ -46,22 +46,69 @@ namespace AZ serializeContext->Class()->Version(1); } } - + + void GetAllBones(const aiScene* scene, AZStd::unordered_map& boneLookup) + { + for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex) + { + const aiMesh* mesh = scene->mMeshes[meshIndex]; + + for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) + { + const aiBone* bone = mesh->mBones[boneIndex]; + + boneLookup[bone->mName.C_Str()] = bone; + } + } + } + Events::ProcessingResult AssImpTransformImporter::ImportTransform(AssImpSceneNodeAppendedContext& context) { AZ_TraceContext("Importer", "transform"); const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); - + if (currentNode == scene->mRootNode || IsPivotNode(currentNode->mName)) { return Events::ProcessingResult::Ignored; } - aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode); + AZStd::unordered_map boneLookup; + GetAllBones(scene, boneLookup); + + auto boneIterator = boneLookup.find(currentNode->mName.C_Str()); + const bool isBone = boneIterator != boneLookup.end(); + + aiMatrix4x4 combinedTransform; + + if (isBone) + { + auto parentNode = currentNode->mParent; + + aiMatrix4x4 offsetMatrix = boneIterator->second->mOffsetMatrix; + aiMatrix4x4 parentOffset {}; + + auto parentBoneIterator = boneLookup.find(parentNode->mName.C_Str()); + + if (parentNode && parentBoneIterator != boneLookup.end()) + { + const auto& parentBone = parentBoneIterator->second; + + parentOffset = parentBone->mOffsetMatrix; + } + + auto inverseOffset = offsetMatrix; + inverseOffset.Inverse(); + + combinedTransform = parentOffset * inverseOffset; + } + else + { + combinedTransform = GetConcatenatedLocalTransform(currentNode); + } DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform); - + context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform); context.m_sourceSceneSystem.ConvertUnit(localTransform); @@ -105,9 +152,7 @@ namespace AZ } else { - bool addedData = context.m_scene.GetGraph().SetContent( - context.m_currentGraphPosition, - transformData); + bool addedData = context.m_scene.GetGraph().SetContent(context.m_currentGraphPosition, transformData); AZ_Error(SceneAPI::Utilities::ErrorWindow, addedData, "Failed to add node data"); return addedData ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure; diff --git a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp index 336cc1b172..3c01255cb9 100644 --- a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp +++ b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp @@ -69,13 +69,14 @@ namespace AZ // aiProcess_JoinIdenticalVertices is not enabled because O3DE has a mesh optimizer that also does this, // this flag is disabled to keep AssImp output similar to FBX SDK to reduce downstream bugs for the initial AssImp release. // There's currently a minimum of properties and flags set to maximize compatibility with the existing node graph. + + // aiProcess_LimitBoneWeights is not enabled because it will remove bones which are not associated with a mesh. + // This results in the loss of the offset matrix data for nodes without a mesh which is required for the Transform Importer. m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, false); m_sceneFileName = fileName; m_assImpScene = m_importer.ReadFile(fileName, aiProcess_Triangulate //Triangulates all faces of all meshes - | aiProcess_LimitBoneWeights //Limits the number of bones that can affect a vertex to a maximum value - //dropping the least important and re-normalizing | aiProcess_GenNormals); //Generate normals for meshes #if AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index 9fba060960..e7dc49d5b3 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -177,9 +177,10 @@ namespace AZ AZ::Entity* rootEntity = reinterpret_cast(classPtr); bool convertResult = ConvertSliceToPrefab(context, outputPath, isDryRun, rootEntity); - // Clear out the references to any nested slices so that the nested assets get unloaded correctly at the end of - // the conversion. - ClearSliceAssetReferences(rootEntity); + + // Delete the root entity pointer. Otherwise, it will leak itself along with all of the slice asset references held + // within it. + delete rootEntity; return convertResult; }; @@ -229,8 +230,12 @@ namespace AZ return false; } - // Get all of the entities from the slice. + // Get all of the entities from the slice. We're taking ownership of them, so we also remove them from the slice component + // without deleting them. + constexpr bool deleteEntities = false; + constexpr bool removeEmptyInstances = true; SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities(); + sliceComponent->RemoveAllEntities(deleteEntities, removeEmptyInstances); AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size()); // Create the Prefab with the entities from the slice. @@ -273,6 +278,12 @@ namespace AZ } } + // Save off a mapping of the slice's metadata entity ID as well, even though we never converted the entity itself. + // This will help us better detect entity ID mapping errors for nested slice instances. + AZ::Entity* metadataEntity = sliceComponent->GetMetadataEntity(); + constexpr bool isMetadataEntity = true; + m_aliasIdMapper.emplace(metadataEntity->GetId(), SliceEntityMappingInfo(templateId, "MetadataEntity", isMetadataEntity)); + // Update the prefab template with the fixed-up data in our prefab instance. AzToolsFramework::Prefab::PrefabDom prefabDom; bool storeResult = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefabDom); @@ -402,6 +413,21 @@ namespace AZ "Convert-Slice", " Attaching %zu instances of nested slice '%s'.\n", instances.size(), nestedPrefabPath.Native().c_str()); + // Before processing any further, save off all the known entity IDs from all the instances and how they map back to + // the base nested prefab that they've come from (i.e. this one). As we proceed up the chain of nesting, this will + // build out a hierarchical list of owning instances for each entity that we can trace upwards to know where to add + // the entity into our nested prefab instance. + // This step needs to occur *before* converting the instances themselves, because while converting instances, they + // might have entity ID references that point to other instances. By having the full instance entity ID map in place + // before conversion, we'll be able to fix them up appropriately. + + for (auto& instance : instances) + { + AZStd::string instanceAlias = GetInstanceAlias(instance); + UpdateSliceEntityInstanceMappings(instance.GetEntityIdToBaseMap(), instanceAlias); + } + + // Now that we have all the entity ID mappings, convert all the instances. for (auto& instance : instances) { bool instanceConvertResult = ConvertSliceInstance(instance, sliceAsset, nestedTemplate, sourceInstance); @@ -415,6 +441,28 @@ namespace AZ return true; } + AZStd::string SliceConverter::GetInstanceAlias(const AZ::SliceComponent::SliceInstance& instance) + { + // When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one + // will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce + // the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains + // a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the + // unique instance ID. + AZStd::string instanceAlias; + auto entityIdMap = instance.GetEntityIdMap(); + if (!entityIdMap.empty()) + { + instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str()); + } + else + { + AZ_Error("Convert-Slice", false, " Couldn't create deterministic instance alias."); + instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str()); + } + return instanceAlias; + } + + bool SliceConverter::ConvertSliceInstance( AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset& sliceAsset, @@ -438,27 +486,7 @@ namespace AZ auto instanceToTemplateInterface = AZ::Interface::Get(); auto prefabSystemComponentInterface = AZ::Interface::Get(); - // When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one - // will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce - // the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains - // a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the - // unique instance ID. - AZStd::string instanceAlias; - auto entityIdMap = instance.GetEntityIdMap(); - if (!entityIdMap.empty()) - { - instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str()); - } - else - { - instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str()); - } - - // Before processing any further, save off all the known entity IDs from this instance and how they map back to the base - // nested prefab that they've come from (i.e. this one). As we proceed up the chain of nesting, this will build out a - // hierarchical list of owning instances for each entity that we can trace upwards to know where to add the entity into - // our nested prefab instance. - UpdateSliceEntityInstanceMappings(instance.GetEntityIdToBaseMap(), instanceAlias); + AZStd::string instanceAlias = GetInstanceAlias(instance); // Create a new unmodified prefab Instance for the nested slice instance. auto nestedInstance = AZStd::make_unique(); @@ -619,6 +647,10 @@ namespace AZ SetParentEntity(containerEntity->get(), topLevelInstance->GetContainerEntityId(), onlySetIfInvalid); } + // After doing all of the above, run through entity references in any of the patched entities, and fix up the entity IDs to + // match the new ones in our prefabs. + RemapIdReferences(m_aliasIdMapper, topLevelInstance, nestedInstance.get(), instantiated, dependentSlice->GetSerializeContext()); + // Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance, // create a patch out of it, and patch the top-level prefab template. @@ -750,17 +782,6 @@ namespace AZ AZ_Error("Convert-Slice", disconnected, "Asset Processor failed to disconnect successfully."); } - void SliceConverter::ClearSliceAssetReferences(AZ::Entity* rootEntity) - { - SliceComponent* sliceComponent = AZ::EntityUtils::FindFirstDerivedComponent(rootEntity); - // Make a copy of the slice list and remove all of them from the loaded component. - AZ::SliceComponent::SliceList slices = sliceComponent->GetSlices(); - for (auto& slice : slices) - { - sliceComponent->RemoveSlice(&slice); - } - } - void SliceConverter::UpdateSliceEntityInstanceMappings( const AZ::SliceComponent::EntityIdToEntityIdMap& sliceEntityIdMap, const AZStd::string& currentInstanceAlias) { @@ -789,9 +810,108 @@ namespace AZ AZ_Assert(oldId == newId, "The same entity instance ID has unexpectedly appeared twice in the same nested prefab."); } } + else + { + AZ_Warning("Convert-Slice", false, " Couldn't find an entity ID conversion for %s.", oldId.ToString().c_str()); + } } } + void SliceConverter::RemapIdReferences( + const AZStd::unordered_map& idMapper, + AzToolsFramework::Prefab::Instance* topLevelInstance, + AzToolsFramework::Prefab::Instance* nestedInstance, + SliceComponent::InstantiatedContainer* instantiatedEntities, + SerializeContext* context) + { + // Given a set of instantiated entities, run through all of them, look for entity references, and replace the entity IDs with + // new ones that match up with our prefabs. + + IdUtils::Remapper::ReplaceIdsAndIdRefs( + instantiatedEntities, + [idMapper, &topLevelInstance, &nestedInstance]( + const EntityId& sourceId, bool isEntityId, [[maybe_unused]] const AZStd::function& idGenerator) -> EntityId + { + EntityId newId = sourceId; + + // Only convert valid entity references. Actual entity IDs have already been taken care of elsewhere, so ignore them. + if (!isEntityId && sourceId.IsValid()) + { + auto entityEntry = idMapper.find(sourceId); + + // Since we've already remapped transform hierarchies to include container entities, it's possible that our entity + // reference is pointing to a container, which means it won't be in our slice mapping table. In that case, just + // return it as-is. + if (entityEntry == idMapper.end()) + { + return sourceId; + } + + // We've got a slice->prefab mapping entry, so now we need to use it. + auto& mappingStruct = entityEntry->second; + + if (mappingStruct.m_nestedInstanceAliases.empty()) + { + // If we don't have a chain of nested instance aliases, then this entity reference is either within the + // current nested instance or it's pointing to an entity in the top-level instance. We'll try them both + // to look for a match. + + EntityId prefabId = nestedInstance->GetEntityId(mappingStruct.m_entityAlias); + if (!prefabId.IsValid()) + { + prefabId = topLevelInstance->GetEntityId(mappingStruct.m_entityAlias); + } + + if (prefabId.IsValid()) + { + newId = prefabId; + } + else + { + AZ_Error("Convert-Slice", false, " Couldn't find source ID %s", sourceId.ToString().c_str()); + } + } + else + { + // We *do* have a chain of nested instance aliases. This chain could either be relative to the nested instance + // or the top-level instance. We can tell which one it is by which one can find the first nested instance + // alias. + + AzToolsFramework::Prefab::Instance* entityInstance = nestedInstance; + auto it = mappingStruct.m_nestedInstanceAliases.rbegin(); + if (!entityInstance->FindNestedInstance(*it).has_value()) + { + entityInstance = topLevelInstance; + } + + // Now that we've got a starting point, iterate through the chain of nested instance aliases to find the + // correct instance to get the entity ID for. We have to go from slice IDs -> entity aliases -> entity IDs + // because prefab instance creation can change some of our entity IDs along the way. + for (; it != mappingStruct.m_nestedInstanceAliases.rend(); it++) + { + auto foundInstance = entityInstance->FindNestedInstance(*it); + if (foundInstance.has_value()) + { + entityInstance = &(foundInstance->get()); + } + else + { + AZ_Assert(false, "Couldn't find nested instance %s", it->c_str()); + } + } + + EntityId prefabId = entityInstance->GetEntityId(mappingStruct.m_entityAlias); + if (prefabId.IsValid()) + { + newId = prefabId; + } + } + } + + return newId; + }, + context); + } } // namespace SerializeContextTools } // namespace AZ diff --git a/Code/Tools/SerializeContextTools/SliceConverter.h b/Code/Tools/SerializeContextTools/SliceConverter.h index ee0bb0a539..31c8306477 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.h +++ b/Code/Tools/SerializeContextTools/SliceConverter.h @@ -42,6 +42,28 @@ namespace AZ bool ConvertSliceFiles(Application& application); private: + // When converting slice entities, especially for nested slices, we need to keep track of the original + // entity ID, the entity alias it uses in the prefab, and which template and nested instance path it maps to. + // As we encounter each instanced entity ID, we can look it up in this structure and use this to determine how to properly + // add it to the correct place in the hierarchy. + struct SliceEntityMappingInfo + { + SliceEntityMappingInfo( + AzToolsFramework::Prefab::TemplateId templateId, + AzToolsFramework::Prefab::EntityAlias entityAlias, + bool isMetadataEntity = false) + : m_templateId(templateId) + , m_entityAlias(entityAlias) + , m_isMetadataEntity(isMetadataEntity) + { + } + + AzToolsFramework::Prefab::TemplateId m_templateId; + AzToolsFramework::Prefab::EntityAlias m_entityAlias; + AZStd::vector m_nestedInstanceAliases; + bool m_isMetadataEntity{ false }; + }; + bool ConnectToAssetProcessor(); void DisconnectFromAssetProcessor(); @@ -58,27 +80,17 @@ namespace AZ void SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid); void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId); bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId); - void ClearSliceAssetReferences(AZ::Entity* rootEntity); void UpdateSliceEntityInstanceMappings( const AZ::SliceComponent::EntityIdToEntityIdMap& sliceEntityIdMap, const AZStd::string& currentInstanceAlias); + AZStd::string GetInstanceAlias(const AZ::SliceComponent::SliceInstance& instance); - // When converting slice entities, especially for nested slices, we need to keep track of the original - // entity ID, the entity alias it uses in the prefab, and which template and nested instance path it maps to. - // As we encounter each instanced entity ID, we can look it up in this structure and use this to determine how to properly - // add it to the correct place in the hierarchy. - struct SliceEntityMappingInfo - { - SliceEntityMappingInfo(AzToolsFramework::Prefab::TemplateId templateId, AzToolsFramework::Prefab::EntityAlias entityAlias) - : m_templateId(templateId) - , m_entityAlias(entityAlias) - { - } - - AzToolsFramework::Prefab::TemplateId m_templateId; - AzToolsFramework::Prefab::EntityAlias m_entityAlias; - AZStd::vector m_nestedInstanceAliases; - }; + void RemapIdReferences( + const AZStd::unordered_map& idMapper, + AzToolsFramework::Prefab::Instance* topLevelInstance, + AzToolsFramework::Prefab::Instance* nestedInstance, + SliceComponent::InstantiatedContainer* instantiatedEntities, + SerializeContext* context); // Track all of the entity IDs created and associate them with enough conversion information to know how to place the // entities in the correct place in the prefab hierarchy and fix up parent entity ID mappings to work with the nested diff --git a/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h b/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h index 28c8c3d762..b8b6a8ccce 100644 --- a/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h +++ b/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h @@ -122,29 +122,22 @@ namespace AWSMetrics //! @return Outcome of the operation. AZ::Outcome SendMetricsToFile(AZStd::shared_ptr metricsQueue); - //! Check whether the consumer should flush the metrics queue. - //! @return whether the limit is hit. - bool ShouldSendMetrics(); - //! Push metrics events to the front of the queue for retry. //! @param metricsEventsForRetry Metrics events for retry. void PushMetricsForRetry(MetricsQueue& metricsEventsForRetry); void SubmitLocalMetricsAsync(); - //////////////////////////////////////////// - // These data are protected by m_metricsMutex. - AZStd::mutex m_metricsMutex; - AZStd::chrono::system_clock::time_point m_lastSendMetricsTime; - MetricsQueue m_metricsQueue; - //////////////////////////////////////////// + AZStd::mutex m_metricsMutex; //!< Mutex to protect the metrics queue + MetricsQueue m_metricsQueue; //!< Queue fo buffering the metrics events - AZStd::mutex m_metricsFileMutex; //!< Local metrics file is protected by m_metricsFileMutex + AZStd::mutex m_metricsFileMutex; //!< Mutex to protect the local metrics file AZStd::atomic m_sendMetricsId;//!< Request ID for sending metrics - AZStd::thread m_consumerThread; //!< Thread to monitor and consume the metrics queue - AZStd::atomic m_consumerTerminated; + AZStd::thread m_monitorThread; //!< Thread to monitor and consume the metrics queue + AZStd::atomic m_monitorTerminated; + AZStd::binary_semaphore m_waitEvent; // Client Configurations. AZStd::unique_ptr m_clientConfiguration; diff --git a/Gems/AWSMetrics/Code/Source/MetricsManager.cpp b/Gems/AWSMetrics/Code/Source/MetricsManager.cpp index 2f4f1fb5e5..d48ec1a041 100644 --- a/Gems/AWSMetrics/Code/Source/MetricsManager.cpp +++ b/Gems/AWSMetrics/Code/Source/MetricsManager.cpp @@ -29,7 +29,7 @@ namespace AWSMetrics MetricsManager::MetricsManager() : m_clientConfiguration(AZStd::make_unique()) , m_clientIdProvider(IdentityProvider::CreateIdentityProvider()) - , m_consumerTerminated(true) + , m_monitorTerminated(true) , m_sendMetricsId(0) { } @@ -53,31 +53,27 @@ namespace AWSMetrics void MetricsManager::StartMetrics() { - if (!m_consumerTerminated) + if (!m_monitorTerminated) { // The background thread has been started. return; } - - m_consumerTerminated = false; - - AZStd::lock_guard lock(m_metricsMutex); - m_lastSendMetricsTime = AZStd::chrono::system_clock::now(); + m_monitorTerminated = false; // Start a separate thread to monitor and consume the metrics queue. // Avoid using the job system since the worker is long-running over multiple frames - m_consumerThread = AZStd::thread(AZStd::bind(&MetricsManager::MonitorMetricsQueue, this)); + m_monitorThread = AZStd::thread(AZStd::bind(&MetricsManager::MonitorMetricsQueue, this)); } void MetricsManager::MonitorMetricsQueue() { - while (!m_consumerTerminated) + // Continue to loop until the monitor is terminated. + while (!m_monitorTerminated) { - if (ShouldSendMetrics()) - { - // Flush the metrics queue when the accumulated metrics size or time period hits the limit - FlushMetricsAsync(); - } + // The thread will wake up either when the metrics event queue is full (try_acquire_for call returns true), + // or the flush period limit is hit (try_acquire_for call returns false). + m_waitEvent.try_acquire_for(AZStd::chrono::seconds(m_clientConfiguration->GetQueueFlushPeriodInSeconds())); + FlushMetricsAsync(); } } @@ -114,6 +110,12 @@ namespace AWSMetrics AZStd::lock_guard lock(m_metricsMutex); m_metricsQueue.AddMetrics(metricsEvent); + if (m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes()) + { + // Flush the metrics queue when the accumulated metrics size hits the limit + m_waitEvent.release(); + } + return true; } @@ -348,9 +350,6 @@ namespace AWSMetrics void MetricsManager::FlushMetricsAsync() { AZStd::lock_guard lock(m_metricsMutex); - - m_lastSendMetricsTime = AZStd::chrono::system_clock::now(); - if (m_metricsQueue.GetNumMetrics() == 0) { return; @@ -363,34 +362,20 @@ namespace AWSMetrics SendMetricsAsync(metricsToFlush); } - bool MetricsManager::ShouldSendMetrics() - { - AZStd::lock_guard lock(m_metricsMutex); - - auto secondsSinceLastFlush = AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now() - m_lastSendMetricsTime); - if (secondsSinceLastFlush >= AZStd::chrono::seconds(m_clientConfiguration->GetQueueFlushPeriodInSeconds()) || - m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes()) - { - return true; - } - - return false; - } - void MetricsManager::ShutdownMetrics() { - if (m_consumerTerminated) + if (m_monitorTerminated) { return; } - // Terminate the consumer thread - m_consumerTerminated = true; - FlushMetricsAsync(); + // Terminate the monitor thread + m_monitorTerminated = true; + m_waitEvent.release(); - if (m_consumerThread.joinable()) + if (m_monitorThread.joinable()) { - m_consumerThread.join(); + m_monitorThread.join(); } } @@ -449,6 +434,12 @@ namespace AWSMetrics { AZStd::lock_guard lock(m_metricsMutex); m_metricsQueue.AddMetrics(offlineRecords[index]); + + if (m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes()) + { + // Flush the metrics queue when the accumulated metrics size hits the limit + m_waitEvent.release(); + } } // Remove the local metrics file after reading all its content. diff --git a/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp b/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp index 82385b8bdd..bd7ecb722e 100644 --- a/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp +++ b/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp @@ -355,6 +355,9 @@ namespace AWSMetrics TEST_F(MetricsManagerTest, FlushMetrics_NonEmptyQueue_Success) { + ResetClientConfig(true, (double)TestMetricsEventSizeInBytes * (MaxNumMetricsEvents + 1) / MbToBytes, + DefaultFlushPeriodInSeconds, 1); + for (int index = 0; index < MaxNumMetricsEvents; ++index) { AZStd::vector metricsAttributes; @@ -377,7 +380,7 @@ namespace AWSMetrics TEST_F(MetricsManagerTest, ResetOfflineRecordingStatus_ResubmitLocalMetrics_Success) { // Disable offline recording in the config file. - ResetClientConfig(false, 0.0, 0, 0); + ResetClientConfig(false, (double)TestMetricsEventSizeInBytes * 2 / MbToBytes, 0, 0); // Enable offline recording after initialize the metric manager. m_metricsManager->UpdateOfflineRecordingStatus(true); diff --git a/Gems/Atom/Feature/Common/Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset b/Gems/Atom/Feature/Common/Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset index 7e71111ef1..3c88544e62 100644 --- a/Gems/Atom/Feature/Common/Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset +++ b/Gems/Atom/Feature/Common/Assets/Config/Platform/Windows/DX12/PlatformLimits.azasset @@ -8,7 +8,7 @@ "$type": "DX12::PlatformLimitsDescriptor", "m_descriptorHeapLimits": { - "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [16384, 262144], + "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [1000000, 1000000], "DESCRIPTOR_HEAP_TYPE_SAMPLER": [2048, 2048], "DESCRIPTOR_HEAP_TYPE_RTV": [2048, 0], "DESCRIPTOR_HEAP_TYPE_DSV": [2048, 0] diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass index 9317fc6396..341893f1d5 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass @@ -322,7 +322,14 @@ "Pass": "AuxGeomPass", "Attachment": "ColorInputOutput" } - } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } + } ] }, { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass index b2e0cf088e..314d999e4d 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass @@ -427,6 +427,13 @@ "Pass": "DebugOverlayPass", "Attachment": "InputOutput" } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } } ] }, diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass index 1d20382408..e2fde2d4ef 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass @@ -24,9 +24,9 @@ }, "ImageDescriptor": { "Format": "R16G16B16A16_FLOAT", - "MipLevels": "8", "SharedQueueMask": "Graphics" - } + }, + "GenerateFullMipChain": true } ], "Connections": [ diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass index 80c4e8987b..5443c32406 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass @@ -5,7 +5,7 @@ "ClassData": { "PassTemplate": { "Name": "ReflectionScreenSpaceCompositePassTemplate", - "PassClass": "FullScreenTriangle", + "PassClass": "ReflectionScreenSpaceCompositePass", "Slots": [ { "Name": "TraceInput", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/UI.pass b/Gems/Atom/Feature/Common/Assets/Passes/UI.pass index 569fe1d722..ac43f17c11 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/UI.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/UI.pass @@ -7,6 +7,23 @@ "Name": "UIPassTemplate", "PassClass": "RasterPass", "Slots": [ + { + "Name": "DepthInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil", + "LoadStoreAction": { + "ClearValue": { + "Type": "DepthStencil", + "Value": [ + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "LoadActionStencil": "Clear" + } + }, { "Name": "InputOutput", "SlotType": "InputOutput", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass index 0069e89401..4ae67b9b09 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass @@ -10,6 +10,11 @@ { "Name": "InputOutput", "SlotType": "InputOutput" + }, + { + "Name": "DepthInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil" } ], "PassRequests": [ @@ -24,6 +29,13 @@ "Pass": "Parent", "Attachment": "InputOutput" } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "DepthInputOutput" + } } ], "PassData": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader index 95ffc36a11..08b1e7c298 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader @@ -10,7 +10,5 @@ "type": "Compute" } ] - }, - "DisabledRHIBackends": ["metal"] - + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli index 7ec5b43368..f4b1beeb3e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli @@ -16,7 +16,10 @@ ShaderResourceGroup MorphTargetPassSrg : SRG_PerPass { - RWBuffer m_accumulatedDeltas; + //Since we do Interlocked atomic operations on this buffer it can not be RWBuffer due to broken MetalSL generation. + //It stems from the fact that typed buffers gets converted to textures and that breaks with atomic operations. + //In future we can handle this under the hood via our metal shader pipeline + RWStructuredBuffer m_accumulatedDeltas; } // This class represents the data that is passed to the morph target compute shader of an individual delta diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl index df92e36f9a..4238392004 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl @@ -37,7 +37,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_sceneLuminance; // This should be of size NUM_HISTOGRAM_BINS. - Buffer m_histogram; + StructuredBuffer m_histogram; Sampler LinearSampler { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl index 05cc870eea..ee95515a2a 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl @@ -20,7 +20,11 @@ ShaderResourceGroup PassSrg : SRG_PerPass { Texture2D m_inputTexture; - RWBuffer m_outputTexture; + + //Since we do Interlocked atomic operations on this buffer it can not be RWBuffer due to broken MetalSL generation. + //It stems from the fact that typed buffers gets converted to textures and that breaks with atomic operations. + //In future we can handle this under the hood via our metal shader pipeline + RWStructuredBuffer m_outputTexture; } groupshared uint shared_histogramBins[NUM_HISTOGRAM_BINS]; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader index 566144bab8..f3dd11e11a 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader @@ -12,7 +12,5 @@ "type": "Compute" } ] - }, - "DisabledRHIBackends": ["metal"] - + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl index fa3885f180..c5c724e106 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl @@ -37,6 +37,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // the max roughness mip level for sampling the previous frame image + uint m_maxMipLevel; } #include @@ -69,10 +72,6 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) float4 positionWS = mul(ViewSrg::m_viewProjectionInverseMatrix, projectedPos); positionWS /= positionWS.w; - //float4 positionVS = mul(ViewSrg::m_projectionMatrixInverse, projectedPos); - //positionVS /= positionVS.w; - //float4 positionWS = mul(ViewSrg::m_viewMatrixInverse, positionVS); - // compute ray from camera to surface position float3 cameraToPositionWS = normalize(positionWS.xyz - ViewSrg::m_worldPosition); @@ -103,8 +102,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute the roughness mip to use in the previous frame image // remap the roughness mip into a lower range to more closely match the material roughness values const float MaxRoughness = 0.5f; - const float MaxRoughnessMip = 7; - float mip = saturate(roughness / MaxRoughness) * MaxRoughnessMip; + float mip = saturate(roughness / MaxRoughness) * PassSrg::m_maxMipLevel; // sample reflection value from the roughness mip float4 reflectionColor = float4(PassSrg::m_previousFrame.SampleLevel(PassSrg::LinearSampler, tracePrevUV, mip).rgb, 1.0f); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli index 5a9e44bade..e5407d2d9e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli @@ -16,7 +16,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass { - RWBuffer m_skinnedMeshOutputStream; + RWStructuredBuffer m_skinnedMeshOutputStream; } ShaderResourceGroup InstanceSrg : SRG_PerDraw diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h index 88faac1728..ce50cea17f 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h @@ -23,7 +23,9 @@ namespace AZ { Low, Medium, - High + High, + + Count }; //! This class provides general features and configuration for the diffuse global illumination environment, diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h index 0d61ef82d1..24c0658c61 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h @@ -95,6 +95,8 @@ namespace AZ TransformServiceFeatureProcessorInterface::ObjectId m_objectId; + Aabb m_aabb = Aabb::CreateNull(); + bool m_cullBoundsNeedsUpdate = false; bool m_cullableNeedsRebuild = false; bool m_objectSrgNeedsUpdate = true; @@ -160,6 +162,9 @@ namespace AZ Transform GetTransform(const MeshHandle& meshHandle) override; Vector3 GetNonUniformScale(const MeshHandle& meshHandle) override; + void SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) override; + AZ::Aabb GetLocalAabb(const MeshHandle& meshHandle) const override; + void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) override; RHI::DrawItemSortKey GetSortKey(const MeshHandle& meshHandle) override; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h index fb5bff5584..fdc6ea3cc8 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h @@ -86,6 +86,10 @@ namespace AZ virtual Transform GetTransform(const MeshHandle& meshHandle) = 0; //! Gets the non-uniform scale for a given mesh handle. virtual Vector3 GetNonUniformScale(const MeshHandle& meshHandle) = 0; + //! Sets the local space bbox for a given mesh handle. You don't need to call this for static models, only skinned/animated models + virtual void SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) = 0; + //! Gets the local space bbox for a given mesh handle. Unless SetLocalAabb has been called before, this will be the bbox of the model asset + virtual AZ::Aabb GetLocalAabb(const MeshHandle& meshHandle) const = 0; //! Sets the sort key for a given mesh handle. virtual void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) = 0; //! Gets the sort key for a given mesh handle. diff --git a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h index 418ee0cfb8..e6635906e9 100644 --- a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h @@ -33,6 +33,8 @@ namespace UnitTest MOCK_METHOD2(SetMaterialAssignmentMap, void(const MeshHandle&, const AZ::Render::MaterialAssignmentMap&)); MOCK_METHOD1(GetTransform, AZ::Transform(const MeshHandle&)); MOCK_METHOD1(GetNonUniformScale, AZ::Vector3(const MeshHandle&)); + MOCK_METHOD2(SetLocalAabb, void(const MeshHandle&, const AZ::Aabb&)); + MOCK_CONST_METHOD1(GetLocalAabb, AZ::Aabb(const MeshHandle&)); MOCK_METHOD2(SetSortKey, void (const MeshHandle&, AZ::RHI::DrawItemSortKey)); MOCK_METHOD1(GetSortKey, AZ::RHI::DrawItemSortKey(const MeshHandle&)); MOCK_METHOD2(SetLodOverride, void(const MeshHandle&, AZ::RPI::Cullable::LodOverride)); diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index af28624357..1866da63e5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -103,6 +103,7 @@ #include #include #include +#include #include #include @@ -283,6 +284,7 @@ namespace AZ // Add Reflection passes passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurPass"), &Render::ReflectionScreenSpaceBlurPass::Create); passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurChildPass"), &Render::ReflectionScreenSpaceBlurChildPass::Create); + passSystem->AddPassCreator(Name("ReflectionScreenSpaceCompositePass"), &Render::ReflectionScreenSpaceCompositePass::Create); passSystem->AddPassCreator(Name("ReflectionCopyFrameBufferPass"), &Render::ReflectionCopyFrameBufferPass::Create); // Add RayTracing pas diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index ae8280fc16..2f8f874f38 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -1070,7 +1072,18 @@ namespace AZ segment.m_pipelineViewTag = viewTag; if (!segment.m_view || segment.m_view->GetName() != viewName) { - segment.m_view = RPI::View::CreateView(viewName, RPI::View::UsageShadow); + RPI::View::UsageFlags usageFlags = RPI::View::UsageShadow; + + // if the shadow is rendering in an EnvironmentCubeMapPass it also needs to be a ReflectiveCubeMap view, + // to filter out shadows from objects that are excluded from the cubemap + RPI::PassClassFilter passFilter; + AZStd::vector cubeMapPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); + if (!cubeMapPasses.empty()) + { + usageFlags |= RPI::View::UsageReflectiveCubeMap; + } + + segment.m_view = RPI::View::CreateView(viewName, usageFlags); } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp index 5040665456..1c28e18e0e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -43,6 +43,12 @@ namespace AZ void DiffuseGlobalIlluminationFeatureProcessor::SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) { + if (qualityLevel >= DiffuseGlobalIlluminationQualityLevel::Count) + { + AZ_Assert(false, "SetQualityLevel called with invalid quality level [%d]", qualityLevel); + return; + } + m_qualityLevel = qualityLevel; UpdatePasses(); diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp index a064b61a1a..ae48c7defb 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp @@ -50,6 +50,8 @@ namespace AZ if (auto behaviorContext = azrtti_cast(context)) { + behaviorContext->Class("OutputDeviceTransformType"); + behaviorContext->Class("AcesParameterOverrides") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Category, "render") @@ -58,6 +60,16 @@ namespace AZ ->Method("LoadPreset", &AcesParameterOverrides::LoadPreset) ->Property("overrideDefaults", BehaviorValueProperty(&AcesParameterOverrides::m_overrideDefaults)) ->Property("preset", BehaviorValueProperty(&AcesParameterOverrides::m_preset)) + ->Enum(OutputDeviceTransformType::NumOutputDeviceTransformTypes)>( + "OutputDeviceTransformType_NumOutputDeviceTransformTypes") + ->Enum(OutputDeviceTransformType::OutputDeviceTransformType_48Nits)>( + "OutputDeviceTransformType_48Nits") + ->Enum(OutputDeviceTransformType::OutputDeviceTransformType_1000Nits)>( + "OutputDeviceTransformType_1000Nits") + ->Enum(OutputDeviceTransformType::OutputDeviceTransformType_2000Nits)>( + "OutputDeviceTransformType_2000Nits") + ->Enum(OutputDeviceTransformType::OutputDeviceTransformType_4000Nits)>( + "OutputDeviceTransformType_4000Nits") ->Property("alterSurround", BehaviorValueProperty(&AcesParameterOverrides::m_alterSurround)) ->Property("applyDesaturation", BehaviorValueProperty(&AcesParameterOverrides::m_applyDesaturation)) ->Property("applyCATD60toD65", BehaviorValueProperty(&AcesParameterOverrides::m_applyCATD60toD65)) diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index b087269089..6fe6cf1f88 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -304,6 +304,30 @@ namespace AZ } } + void MeshFeatureProcessor::SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) + { + if (meshHandle.IsValid()) + { + MeshDataInstance& meshData = *meshHandle; + meshData.m_aabb = localAabb; + meshData.m_cullBoundsNeedsUpdate = true; + meshData.m_objectSrgNeedsUpdate = true; + } + }; + + AZ::Aabb MeshFeatureProcessor::GetLocalAabb(const MeshHandle& meshHandle) const + { + if (meshHandle.IsValid()) + { + return meshHandle->m_aabb; + } + else + { + AZ_Assert(false, "Invalid mesh handle"); + return Aabb::CreateNull(); + } + } + Transform MeshFeatureProcessor::GetTransform(const MeshHandle& meshHandle) { if (meshHandle.IsValid()) @@ -603,6 +627,8 @@ namespace AZ SetRayTracingData(); } + m_aabb = model->GetModelAsset()->GetAabb(); + m_cullableNeedsRebuild = true; m_cullBoundsNeedsUpdate = true; m_objectSrgNeedsUpdate = true; @@ -996,7 +1022,7 @@ namespace AZ RPI::Cullable::CullData& cullData = m_cullable.m_cullData; RPI::Cullable::LodData& lodData = m_cullable.m_lodData; - const Aabb& localAabb = m_model->GetAabb(); + const Aabb& localAabb = m_aabb; lodData.m_lodSelectionRadius = 0.5f*localAabb.GetExtents().GetMaxElement(); const size_t modelLodCount = m_model->GetLodCount(); @@ -1077,7 +1103,7 @@ namespace AZ Vector3 center; float radius; - Aabb localAabb = m_model->GetAabb(); + Aabb localAabb = m_aabb; localAabb.MultiplyByScale(nonUniformScale); localAabb.GetTransformedAabb(localToWorld).GetAsSphere(center, radius); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp index 55c64c1f33..fb430ac3d3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp @@ -67,7 +67,7 @@ namespace AZ desc.m_bufferName = "LuminanceHistogramBuffer"; desc.m_elementSize = sizeof(uint32_t); desc.m_byteCount = NumHistogramBins * sizeof(uint32_t); - desc.m_elementFormat = RHI::Format::R32_UINT; + desc.m_elementFormat = RHI::Format::Unknown; m_histogram = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); AZ_Assert(m_histogram != nullptr, "Unable to allocate buffer"); } diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h index 4a2ccce1d4..be8dc6d596 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h @@ -37,6 +37,9 @@ namespace AZ //! to store the previous frame image Data::Instance& GetFrameBufferImageAttachment() { return m_frameBufferImageAttachment; } + //! Returns the number of mip levels in the blur + uint32_t GetNumBlurMips() const { return m_numBlurMips; } + private: explicit ReflectionScreenSpaceBlurPass(const RPI::PassDescriptor& descriptor); diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp new file mode 100644 index 0000000000..ab09d7175a --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -0,0 +1,58 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "ReflectionScreenSpaceCompositePass.h" +#include "ReflectionScreenSpaceBlurPass.h" +#include +#include + +namespace AZ +{ + namespace Render + { + RPI::Ptr ReflectionScreenSpaceCompositePass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew ReflectionScreenSpaceCompositePass(descriptor); + return AZStd::move(pass); + } + + ReflectionScreenSpaceCompositePass::ReflectionScreenSpaceCompositePass(const RPI::PassDescriptor& descriptor) + : RPI::FullscreenTrianglePass(descriptor) + { + } + + void ReflectionScreenSpaceCompositePass::CompileResources([[maybe_unused]] const RHI::FrameGraphCompileContext& context) + { + if (!m_shaderResourceGroup) + { + return; + } + + RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); + const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); + if (!passes.empty()) + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); + + // compute the max mip level based on the available mips in the previous frame image, and capping it + // to stay within a range that has reasonable data + const uint32_t MaxNumRoughnessMips = 8; + uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + + auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); + m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); + } + + FullscreenTrianglePass::CompileResources(context); + } + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h new file mode 100644 index 0000000000..110673541e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + //! This pass composites the screenspace reflection trace onto the reflection buffer. + class ReflectionScreenSpaceCompositePass + : public RPI::FullscreenTrianglePass + { + AZ_RPI_PASS(ReflectionScreenSpaceCompositePass); + + public: + AZ_RTTI(Render::ReflectionScreenSpaceCompositePass, "{88739CC9-C3F1-413A-A527-9916C697D93A}", FullscreenTrianglePass); + AZ_CLASS_ALLOCATOR(Render::ReflectionScreenSpaceCompositePass, SystemAllocator, 0); + + //! Creates a new pass without a PassTemplate + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + private: + explicit ReflectionScreenSpaceCompositePass(const RPI::PassDescriptor& descriptor); + + // Pass Overrides... + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + }; + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp index 343370ae35..a87d272448 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp @@ -629,6 +629,11 @@ namespace AZ Data::Asset lodAsset; modelLodCreator.End(lodAsset); + if (!lodAsset.IsReady()) + { + // [GFX TODO] During mesh reload the modelLodCreator could report errors and result in the lodAsset not ready. + return nullptr; + } modelCreator.AddLodAsset(AZStd::move(lodAsset)); lodIndex++; diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp index 1cb782d8b1..1b14611e84 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp @@ -67,8 +67,8 @@ namespace AZ creator.SetBuffer(nullptr, 0, bufferDescriptor); RHI::BufferViewDescriptor viewDescriptor; - viewDescriptor.m_elementFormat = RHI::Format::R32_FLOAT; - viewDescriptor.m_elementSize = RHI::GetFormatSize(viewDescriptor.m_elementFormat); + viewDescriptor.m_elementFormat = RHI::Format::Unknown; + viewDescriptor.m_elementSize = sizeof(float); viewDescriptor.m_elementCount = aznumeric_cast(m_sizeInBytes) / viewDescriptor.m_elementSize; viewDescriptor.m_elementOffset = 0; creator.SetBufferViewDescriptor(viewDescriptor); diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index a759de77fa..a656558abf 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -267,6 +267,8 @@ set(FILES Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurChildPass.cpp Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurChildPass.h + Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp + Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h Source/ScreenSpace/DeferredFogSettings.cpp diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h index 51b15c4bcf..651be829b5 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h @@ -160,6 +160,16 @@ namespace AZ StencilState m_stencil; }; + enum class WriteChannelMask : uint8_t + { + ColorWriteMaskNone = 0, + ColorWriteMaskRed = AZ_BIT(0), + ColorWriteMaskGreen = AZ_BIT(1), + ColorWriteMaskBlue = AZ_BIT(2), + ColorWriteMaskAlpha = AZ_BIT(3), + ColorWriteMaskAll = ColorWriteMaskRed | ColorWriteMaskGreen | ColorWriteMaskBlue | ColorWriteMaskAlpha + }; + struct TargetBlendState { AZ_TYPE_INFO(TargetBlendState, "{2CDF00FE-614D-44FC-929F-E6B50C348578}"); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h index ce2c6c77ec..afcab28a1d 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h @@ -410,7 +410,6 @@ namespace AZ // For any other type the buffer view's element size should match the stride. if (shaderInputBuffer.m_strideSize != bufferViewDescriptor.m_elementSize) { - // [GFX TODO][ATOM-5735][AZSL] ByteAddressBuffer shader input is setting a stride of 16 instead of 4 AZ_Error("ShaderResourceGroupData", false, "Buffer Input '%s[%d]': Does not match expected stride size %d", shaderInputBuffer.m_name.GetCStr(), arrayIndex, bufferViewDescriptor.m_elementSize); return false; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h index a6fe57f6d8..6f09ea1d5f 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h @@ -271,6 +271,12 @@ namespace AZ ShaderResourceBindings& bindings = GetShaderResourceBindingsByPipelineType(pipelineType); const PipelineState* pipelineState = static_cast(item.m_pipelineState); + if(!pipelineState) + { + AZ_Assert(false, "Pipeline state not provided"); + return false; + } + bool updatePipelineState = m_state.m_pipelineState != pipelineState; // The pipeline state gets set first. if (updatePipelineState) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp index a3d14e3803..a29c7ae402 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp @@ -10,6 +10,7 @@ * */ #include "RHI/Atom_RHI_DX12_precompiled.h" +#include #include #include #include @@ -1268,7 +1269,7 @@ namespace AZ dst.BlendOpAlpha = ConvertBlendOp(src.m_blendAlphaOp); dst.DestBlend = ConvertBlendFactor(src.m_blendDest); dst.DestBlendAlpha = ConvertBlendFactor(src.m_blendAlphaDest); - dst.RenderTargetWriteMask = src.m_writeMask; + dst.RenderTargetWriteMask = ConvertColorWriteMask(src.m_writeMask); dst.SrcBlend = ConvertBlendFactor(src.m_blendSource); dst.SrcBlendAlpha = ConvertBlendFactor(src.m_blendAlphaSource); dst.LogicOp = D3D12_LOGIC_OP_CLEAR; @@ -1355,6 +1356,38 @@ namespace AZ }; return table[(uint32_t)mask]; } + + uint8_t ConvertColorWriteMask(uint8_t writeMask) + { + uint8_t dflags = 0; + if(writeMask == 0) + { + return dflags; + } + + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + { + return D3D12_COLOR_WRITE_ENABLE_ALL; + } + + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_RED; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_GREEN; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_BLUE; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_ALPHA; + } + return dflags; + } D3D12_DEPTH_STENCIL_DESC ConvertDepthStencilState(const RHI::DepthStencilState& depthStencil) { diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h index d8385203f8..640a5fef31 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h @@ -164,5 +164,7 @@ namespace AZ uint32_t shaderRegisterSpace, D3D12_SHADER_VISIBILITY shaderVisibility, D3D12_STATIC_SAMPLER_DESC& staticSamplerDesc); + + uint8_t ConvertColorWriteMask(uint8_t writeMask); } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp b/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp index cc8c4c80c0..b81c51a57b 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp @@ -94,7 +94,7 @@ namespace Platform void ResizeInternal(RHIMetalView* metalView, CGSize viewSize) { - [metalView resizeSubviewsWithOldSize:viewSize]; + [metalView.metalLayer setDrawableSize: viewSize]; } RHIMetalView* GetMetalView(NativeWindowType* nativeWindow) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 6ffd15e0bc..c433a6f9cf 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -386,35 +386,33 @@ namespace AZ void ArgumentBuffer::AddUntrackedResourcesToEncoder(id commandEncoder, const ShaderResourceGroupVisibility& srgResourcesVisInfo) const { + //Map to cache all the resources based on the usage as we can batch all the resources for a given usage + ComputeResourcesToMakeResidentMap resourcesToMakeResidentCompute; + //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage + GraphicsResourcesToMakeResidentMap resourcesToMakeResidentGraphics; + + //Cache the constant buffer associated with a srg if (m_constantBufferSize) { uint8_t numBitsSet = RHI::CountBitsSet(static_cast(srgResourcesVisInfo.m_constantDataStageMask)); if( numBitsSet > 0) { + id mtlconstantBufferResource = m_constantBuffer.GetGpuAddress>(); if(RHI::CheckBitsAny(srgResourcesVisInfo.m_constantDataStageMask, RHI::ShaderStageMask::Compute)) { - [static_cast>(commandEncoder) useResource:m_constantBuffer.GetGpuAddress>() usage:MTLResourceUsageRead]; + resourcesToMakeResidentCompute[MTLResourceUsageRead].emplace(mtlconstantBufferResource); } else { MTLRenderStages mtlRenderStages = GetRenderStages(srgResourcesVisInfo.m_constantDataStageMask); - [static_cast>(commandEncoder) useResource:m_constantBuffer.GetGpuAddress>() - usage:MTLResourceUsageRead - stages:mtlRenderStages]; + AZStd::pair key = AZStd::make_pair(MTLResourceUsageRead, mtlRenderStages); + resourcesToMakeResidentGraphics[key].emplace(mtlconstantBufferResource); } - } } - ApplyUseResource(commandEncoder, m_resourceBindings, srgResourcesVisInfo); - } - - void ArgumentBuffer::ApplyUseResource(id encoder, - const ResourceBindingsMap& resourceMap, - const ShaderResourceGroupVisibility& srgResourcesVisInfo) const - { - - CommandEncoderType encodeType = CommandEncoderType::Invalid; - for (const auto& it : resourceMap) + + //Cach all the resources within a srg that are used by the shader based on the visibility information + for (const auto& it : m_resourceBindings) { //Extract the visibility mask for the give resource auto visMaskIt = srgResourcesVisInfo.m_resourcesStageMask.find(it.first); @@ -426,75 +424,55 @@ namespace AZ { if(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Compute)) { - //Call UseResource on all resources for Compute stage - ApplyUseResourceToCompute(encoder, it.second); - encodeType = CommandEncoderType::Compute; + CollectResourcesForCompute(commandEncoder, it.second, resourcesToMakeResidentCompute); } else { - //Call UseResource on all resources for Vertex and Fragment stages - AZ_Assert(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Vertex) || RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Fragment), "The visibility mask %i is not set for Vertex or fragment stage", visMaskIt->second); - ApplyUseResourceToGraphic(encoder, visMaskIt->second, it.second); - encodeType = CommandEncoderType::Render; + bool isBoundToGraphics = RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Vertex) || RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Fragment); + AZ_Assert(isBoundToGraphics, "The visibility mask %i is not set for Vertex or fragment stage", visMaskIt->second); + CollectResourcesForGraphics(commandEncoder, visMaskIt->second, it.second, resourcesToMakeResidentGraphics); } } } - } - - void ArgumentBuffer::ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingDataSet) const - { - for (const auto& resourceBindingData : resourceBindingDataSet) - { - ResourceType rescType = resourceBindingData.m_resourcPtr->GetResourceType(); - switch(rescType) - { - case ResourceType::MtlTextureType: - { - MTLResourceUsage resourceUsage = GetImageResourceUsage(resourceBindingData.m_imageAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() usage:resourceUsage]; - - break; - } - case ResourceType::MtlBufferType: - { - MTLResourceUsage resourceUsage = GetBufferResourceUsage(resourceBindingData.m_bufferAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() usage:resourceUsage]; - - break; - } - default: - { - AZ_Assert(false, "Undefined Resource type"); - } - } - } - } - - void ArgumentBuffer::ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet) const - { - MTLRenderStages mtlRenderStages = GetRenderStages(visShaderMask); + //Call UseResource on all resources for Compute stage + for (const auto& key : resourcesToMakeResidentCompute) + { + AZStd::vector> resourcesToProcessVec(key.second.begin(), key.second.end()); + [static_cast>(commandEncoder) useResources: &resourcesToProcessVec[0] + count: resourcesToProcessVec.size() + usage: key.first]; + } + + //Call UseResource on all resources for Vertex and Fragment stages + for (const auto& key : resourcesToMakeResidentGraphics) + { + AZStd::vector> resourcesToProcessVec(key.second.begin(), key.second.end()); + [static_cast>(commandEncoder) useResources: &resourcesToProcessVec[0] + count: resourcesToProcessVec.size() + usage: key.first.first + stages: key.first.second]; + } + } + + void ArgumentBuffer::CollectResourcesForCompute(id encoder, + const ResourceBindingsSet& resourceBindingDataSet, + ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const + { for (const auto& resourceBindingData : resourceBindingDataSet) { ResourceType rescType = resourceBindingData.m_resourcPtr->GetResourceType(); + MTLResourceUsage resourceUsage = MTLResourceUsageRead; switch(rescType) { case ResourceType::MtlTextureType: { - MTLResourceUsage resourceUsage = GetImageResourceUsage(resourceBindingData.m_imageAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() - usage:resourceUsage - stages:mtlRenderStages]; - + resourceUsage |= GetImageResourceUsage(resourceBindingData.m_imageAccess); break; } case ResourceType::MtlBufferType: { - MTLResourceUsage resourceUsage = GetBufferResourceUsage(resourceBindingData.m_bufferAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() - usage:resourceUsage - stages:mtlRenderStages]; - + resourceUsage |= GetBufferResourceUsage(resourceBindingData.m_bufferAccess); break; } default: @@ -502,8 +480,45 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } + + id mtlResourceToBind = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + resourcesToMakeResidentMap[resourceUsage].emplace(mtlResourceToBind); + } + } + + void ArgumentBuffer::CollectResourcesForGraphics(id encoder, + RHI::ShaderStageMask visShaderMask, + const ResourceBindingsSet& resourceBindingDataSet, + GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const + { + + MTLRenderStages mtlRenderStages = GetRenderStages(visShaderMask); + MTLResourceUsage resourceUsage = MTLResourceUsageRead; + for (const auto& resourceBindingData : resourceBindingDataSet) + { + ResourceType rescType = resourceBindingData.m_resourcPtr->GetResourceType(); + switch(rescType) + { + case ResourceType::MtlTextureType: + { + resourceUsage |= GetImageResourceUsage(resourceBindingData.m_imageAccess); + break; + } + case ResourceType::MtlBufferType: + { + resourceUsage |= GetBufferResourceUsage(resourceBindingData.m_bufferAccess); + break; + } + default: + { + AZ_Assert(false, "Undefined Resource type"); + } + } + + AZStd::pair key = AZStd::make_pair(resourceUsage, mtlRenderStages); + id mtlResourceToBind = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + resourcesToMakeResidentMap[key].emplace(mtlResourceToBind); } } - } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index a5a8e00e69..29d7d5e239 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -119,8 +119,19 @@ namespace AZ using ResourceBindingsMap = AZStd::unordered_map; ResourceBindingsMap m_resourceBindings; - void ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingData) const; - void ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet) const; + static const int MaxEntriesInArgTable = 31; + //Map to cache all the resources based on the usage as we can batch all the resources for a given usage. + using ComputeResourcesToMakeResidentMap = AZStd::unordered_map>>; + //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage. + using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, AZStd::unordered_set>>; + + void CollectResourcesForCompute(id encoder, + const ResourceBindingsSet& resourceBindingData, + ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; + void CollectResourcesForGraphics(id encoder, + RHI::ShaderStageMask visShaderMask, + const ResourceBindingsSet& resourceBindingDataSet, + GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; //! Use visibility information to call UseResource on all resources for this Argument Buffer void ApplyUseResource(id encoder, const ResourceBindingsMap& resourceMap, @@ -144,8 +155,6 @@ namespace AZ #endif ShaderResourceGroupPool* m_srgPool = nullptr; - - static const int MaxEntriesInArgTable = 31; NSCache* m_samplerCache; }; } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp index 594a4931b9..29e0b19b75 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp @@ -85,16 +85,36 @@ namespace AZ uint64_t AsyncUploadQueue::QueueUpload(const RHI::BufferStreamRequest& uploadRequest) { - uint64_t queueValue = m_uploadFence.Increment(); + Buffer& destBuffer = static_cast(*uploadRequest.m_buffer); + const MemoryView& destMemoryView = destBuffer.GetMemoryView(); + MTLStorageMode mtlStorageMode = destBuffer.GetMemoryView().GetStorageMode(); + RHI::BufferPool& bufferPool = static_cast(*destBuffer.GetPool()); + + // No need to use staging buffers since it's host memory. + // We just map, copy and then unmap. + if(mtlStorageMode == MTLStorageModeShared || mtlStorageMode == GetCPUGPUMemoryMode()) + { + RHI::BufferMapRequest mapRequest; + mapRequest.m_buffer = uploadRequest.m_buffer; + mapRequest.m_byteCount = uploadRequest.m_byteCount; + mapRequest.m_byteOffset = uploadRequest.m_byteOffset; + RHI::BufferMapResponse mapResponse; + bufferPool.MapBuffer(mapRequest, mapResponse); + ::memcpy(mapResponse.m_data, uploadRequest.m_sourceData, uploadRequest.m_byteCount); + bufferPool.UnmapBuffer(*uploadRequest.m_buffer); + if (uploadRequest.m_fenceToSignal) + { + uploadRequest.m_fenceToSignal->SignalOnCpu(); + } + return m_uploadFence.GetPendingValue(); + } - const MemoryView& memoryView = static_cast(*uploadRequest.m_buffer).GetMemoryView(); - RHI::Ptr buffer = memoryView.GetMemory(); - Fence* fenceToSignal = nullptr; uint64_t fenceToSignalValue = 0; - size_t byteCount = uploadRequest.m_byteCount; - size_t byteOffset = memoryView.GetOffset() + uploadRequest.m_byteOffset; + size_t byteOffset = destMemoryView.GetOffset() + uploadRequest.m_byteOffset; + uint64_t queueValue = m_uploadFence.Increment(); + const uint8_t* sourceData = reinterpret_cast(uploadRequest.m_sourceData); if (uploadRequest.m_fenceToSignal) @@ -125,11 +145,11 @@ namespace AZ } id blitEncoder = [framePacket->m_mtlCommandBuffer blitCommandEncoder]; - [blitEncoder copyFromBuffer:framePacket->m_stagingResource - sourceOffset:0 - toBuffer:buffer->GetGpuAddress>() - destinationOffset:byteOffset + pendingByteOffset - size:bytesToCopy]; + [blitEncoder copyFromBuffer: framePacket->m_stagingResource + sourceOffset: 0 + toBuffer: destMemoryView.GetGpuAddress>() + destinationOffset: byteOffset + pendingByteOffset + size: bytesToCopy]; [blitEncoder endEncoding]; blitEncoder = nil; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp index 2ca8303dc9..b986b8ea75 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.cpp @@ -40,9 +40,8 @@ namespace AZ buffer->m_pendingResolves++; uploadRequest.m_attachmentBuffer = buffer; - uploadRequest.m_byteOffset = request.m_byteOffset; + uploadRequest.m_byteOffset = buffer->GetMemoryView().GetOffset() + request.m_byteOffset; uploadRequest.m_stagingBuffer = stagingBuffer; - uploadRequest.m_byteSize = request.m_byteCount; return stagingBuffer->GetMemoryView().GetCpuAddress(); } @@ -64,12 +63,15 @@ namespace AZ AZ_Assert(stagingBuffer, "Staging Buffer is null."); AZ_Assert(destBuffer, "Attachment Buffer is null."); + //Inform the GPU that the CPU has modified the staging buffer. + Platform::SynchronizeBufferOnCPU(stagingBuffer->GetMemoryView().GetGpuAddress>(), stagingBuffer->GetMemoryView().GetOffset(), stagingBuffer->GetMemoryView().GetSize()); + RHI::CopyBufferDescriptor copyDescriptor; copyDescriptor.m_sourceBuffer = stagingBuffer; - copyDescriptor.m_sourceOffset = 0; + copyDescriptor.m_sourceOffset = stagingBuffer->GetMemoryView().GetOffset(); copyDescriptor.m_destinationBuffer = destBuffer; copyDescriptor.m_destinationOffset = static_cast(packet.m_byteOffset); - copyDescriptor.m_size = static_cast(packet.m_byteSize); + copyDescriptor.m_size = stagingBuffer->GetMemoryView().GetSize(); commandList.Submit(RHI::CopyItem(copyDescriptor)); device.QueueForRelease(stagingBuffer->GetMemoryView()); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.h index c62d9494db..3e60bb31fa 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/BufferPoolResolver.h @@ -54,7 +54,6 @@ namespace AZ Buffer* m_attachmentBuffer = nullptr; RHI::Ptr m_stagingBuffer; size_t m_byteOffset = 0; - size_t m_byteSize = 0; }; AZStd::mutex m_uploadPacketsLock; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp index 3eca8dabd8..7f65c9ea47 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -249,66 +250,191 @@ namespace AZ ShaderResourceBindings& bindings = GetShaderResourceBindingsByPipelineType(stateType); const PipelineLayout& pipelineLayout = pipelineState->GetPipelineLayout(); - for (uint32_t srgIndex = 0; srgIndex < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++srgIndex) + uint32_t bufferVertexRegisterIdMin = RHI::Limits::Pipeline::ShaderResourceGroupCountMax; + uint32_t bufferFragmentOrComputeRegisterIdMin = RHI::Limits::Pipeline::ShaderResourceGroupCountMax; + uint32_t bufferVertexRegisterIdMax = 0; + uint32_t bufferFragmentOrComputeRegisterIdMax = 0; + + //Arrays to cache all the buffers and offsets in order to make batch calls + MetalArgumentBufferArray mtlVertexArgBuffers; + MetalArgumentBufferArrayOffsets mtlVertexArgBufferOffsets; + MetalArgumentBufferArray mtlFragmentOrComputeArgBuffers; + MetalArgumentBufferArrayOffsets mtlFragmentOrComputeArgBufferOffsets; + + mtlVertexArgBuffers.fill(nil); + mtlFragmentOrComputeArgBuffers.fill(nil); + mtlVertexArgBufferOffsets.fill(0); + mtlFragmentOrComputeArgBufferOffsets.fill(0); + + for (uint32_t slot = 0; slot < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++slot) { - const ShaderResourceGroup* shaderResourceGroup = bindings.m_srgsBySlot[srgIndex]; - uint32_t slotIndex = pipelineLayout.GetSlotByIndex(srgIndex); + const ShaderResourceGroup* shaderResourceGroup = bindings.m_srgsBySlot[slot]; + uint32_t slotIndex = pipelineLayout.GetIndexBySlot(slot); if(!shaderResourceGroup || slotIndex == RHI::Limits::Pipeline::ShaderResourceGroupCountMax) { continue; } - if (bindings.m_srgsByIndex[srgIndex] != shaderResourceGroup) + uint32_t srgVisIndex = pipelineLayout.GetIndexBySlot(shaderResourceGroup->GetBindingSlot()); + const RHI::ShaderStageMask& srgVisInfo = pipelineLayout.GetSrgVisibility(srgVisIndex); + + bool isSrgUpdatd = bindings.m_srgsByIndex[slot] != shaderResourceGroup; + if(isSrgUpdatd) { - bindings.m_srgsByIndex[srgIndex] = shaderResourceGroup; + bindings.m_srgsByIndex[slot] = shaderResourceGroup; auto& compiledArgBuffer = shaderResourceGroup->GetCompiledArgumentBuffer(); - id argBuffer = compiledArgBuffer.GetArgEncoderBuffer(); size_t argBufferOffset = compiledArgBuffer.GetOffset(); - - uint32_t srgVisIndex = pipelineLayout.GetSlotByIndex(shaderResourceGroup->GetBindingSlot()); - const RHI::ShaderStageMask& srgVisInfo = pipelineLayout.GetSrgVisibility(srgVisIndex); - + if(srgVisInfo != RHI::ShaderStageMask::None) { - const ShaderResourceGroupVisibility& srgResourcesVisInfo = pipelineLayout.GetSrgResourcesVisibility(srgVisIndex); - - //For graphics and compute encoder bind the argument buffer and - //make the resource resident for the duration of the work associated with the current scope - //and ensure that it's in a format compatible with the appropriate metal function. + //For graphics and compute shader stages, cache all the argument buffers, offsets and track the min/max indices if(m_commandEncoderType == CommandEncoderType::Render) { id renderEncoder = GetEncoder>(); uint8_t numBitsSet = RHI::CountBitsSet(static_cast(srgVisInfo)); if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Vertex) { - [renderEncoder setVertexBuffer:argBuffer - offset:argBufferOffset - atIndex:slotIndex]; + mtlVertexArgBuffers[slotIndex] = argBuffer; + mtlVertexArgBufferOffsets[slotIndex] = argBufferOffset; + bufferVertexRegisterIdMin = AZStd::min(slotIndex, bufferVertexRegisterIdMin); + bufferVertexRegisterIdMax = AZStd::max(slotIndex, bufferVertexRegisterIdMax); } if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Fragment) { - [renderEncoder setFragmentBuffer:argBuffer - offset:argBufferOffset - atIndex:slotIndex]; + mtlFragmentOrComputeArgBuffers[slotIndex] = argBuffer; + mtlFragmentOrComputeArgBufferOffsets[slotIndex] = argBufferOffset; + bufferFragmentOrComputeRegisterIdMin = AZStd::min(slotIndex, bufferFragmentOrComputeRegisterIdMin); + bufferFragmentOrComputeRegisterIdMax = AZStd::max(slotIndex, bufferFragmentOrComputeRegisterIdMax); } + } + else if(m_commandEncoderType == CommandEncoderType::Compute) + { + mtlFragmentOrComputeArgBuffers[slotIndex] = argBuffer; + mtlFragmentOrComputeArgBufferOffsets[slotIndex] = argBufferOffset; + bufferFragmentOrComputeRegisterIdMin = AZStd::min(slotIndex, bufferFragmentOrComputeRegisterIdMin); + bufferFragmentOrComputeRegisterIdMax = AZStd::max(slotIndex, bufferFragmentOrComputeRegisterIdMax); + } + } + } + + //Check if the srg has been updated or if the srg resources visibility hash has been updated + //as it is possible for draw items to have different PSOs in the same pass. + const AZ::HashValue64 srgResourcesVisHash = pipelineLayout.GetSrgResourcesVisibilityHash(srgVisIndex); + if(bindings.m_srgVisHashByIndex[slot] != srgResourcesVisHash || isSrgUpdatd) + { + bindings.m_srgVisHashByIndex[slot] = srgResourcesVisHash; + if(srgVisInfo != RHI::ShaderStageMask::None) + { + const ShaderResourceGroupVisibility& srgResourcesVisInfo = pipelineLayout.GetSrgResourcesVisibility(srgVisIndex); + + //For graphics and compute encoder make the resource resident (call UseResource) for the duration + //of the work associated with the current scope and ensure that it's in a + //format compatible with the appropriate metal function. + if(m_commandEncoderType == CommandEncoderType::Render) + { shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo); } else if(m_commandEncoderType == CommandEncoderType::Compute) { - id computeEncoder = GetEncoder>(); - [computeEncoder setBuffer:argBuffer - offset:argBufferOffset - atIndex:pipelineLayout.GetSlotByIndex(srgIndex)]; shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo); } } } } - + + //For graphics and compute encoder bind all the argument buffers + if(m_commandEncoderType == CommandEncoderType::Render) + { + BindArgumentBuffers(RHI::ShaderStage::Vertex, + bufferVertexRegisterIdMin, + bufferVertexRegisterIdMax, + mtlVertexArgBuffers, + mtlVertexArgBufferOffsets); + + BindArgumentBuffers(RHI::ShaderStage::Fragment, + bufferFragmentOrComputeRegisterIdMin, + bufferFragmentOrComputeRegisterIdMax, + mtlFragmentOrComputeArgBuffers, + mtlFragmentOrComputeArgBufferOffsets); + } + else if(m_commandEncoderType == CommandEncoderType::Compute) + { + BindArgumentBuffers(RHI::ShaderStage::Compute, + bufferFragmentOrComputeRegisterIdMin, + bufferFragmentOrComputeRegisterIdMax, + mtlFragmentOrComputeArgBuffers, + mtlFragmentOrComputeArgBufferOffsets); + } + return true; } + + void CommandList::BindArgumentBuffers(RHI::ShaderStage shaderStage, + uint16_t registerIdMin, + uint16_t registerIdMax, + MetalArgumentBufferArray& mtlArgBuffers, + MetalArgumentBufferArrayOffsets mtlArgBufferOffsets) + { + //Metal Api only lets you bind multiple argument buffers in an array as long as there are no gaps in the array + //In order to accomodate that we break up the calls when a gap is noticed in the array and reconfigure the NSRange. + uint16_t startingIndex = registerIdMin; + bool trackingRange = true; + for(int i = registerIdMin; i <= registerIdMax+1; i++) + { + if(trackingRange) + { + if(mtlArgBuffers[i] == nil) + { + NSRange range = { startingIndex, i-startingIndex }; + + switch(shaderStage) + { + case RHI::ShaderStage::Vertex: + { + id renderEncoder = GetEncoder>(); + [renderEncoder setVertexBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + case RHI::ShaderStage::Fragment: + { + id renderEncoder = GetEncoder>(); + [renderEncoder setFragmentBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + case RHI::ShaderStage::Compute: + { + id computeEncoder = GetEncoder>(); + [computeEncoder setBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + default: + { + AZ_Assert(false, "Not supported"); + } + } + + trackingRange = false; + + } + } + else + { + if(mtlArgBuffers[i] != nil) + { + startingIndex = i; + trackingRange = true; + } + } + } + } void CommandList::Submit(const RHI::DrawItem& drawItem) { @@ -447,6 +573,7 @@ namespace AZ for (size_t i = 0; i < bindings.m_srgsByIndex.size(); ++i) { bindings.m_srgsByIndex[i] = nullptr; + bindings.m_srgVisHashByIndex[i] = AZ::HashValue64{0}; } const PipelineLayout& pipelineLayout = pipelineState->GetPipelineLayout(); @@ -469,6 +596,10 @@ namespace AZ void CommandList::SetStreamBuffers(const RHI::StreamBufferView* streams, uint32_t count) { + uint16_t bufferArrayLen = 0; + AZStd::array, METAL_MAX_ENTRIES_BUFFER_ARG_TABLE> mtlStreamBuffers; + AZStd::array mtlStreamBufferOffsets; + AZ::HashValue64 streamsHash = AZ::HashValue64{0}; for (uint32_t i = 0; i < count; ++i) { @@ -479,18 +610,25 @@ namespace AZ { m_state.m_streamsHash = streamsHash; AZ_Assert(count <= METAL_MAX_ENTRIES_BUFFER_ARG_TABLE , "Slots needed cannot exceed METAL_MAX_ENTRIES_BUFFER_ARG_TABLE"); - for (uint32_t i = 0; i < count; ++i) + + NSRange range = {METAL_MAX_ENTRIES_BUFFER_ARG_TABLE - count, count}; + //The stream buffers are populated from bottom to top as the top slots are taken by argument buffers + for (int i = count-1; i >= 0; --i) { if (streams[i].GetBuffer()) { const Buffer * buff = static_cast(streams[i].GetBuffer()); id mtlBuff = buff->GetMemoryView().GetGpuAddress>(); - uint32_t VBIndex = (METAL_MAX_ENTRIES_BUFFER_ARG_TABLE - 1) - i; uint32_t offset = streams[i].GetByteOffset() + buff->GetMemoryView().GetOffset(); - id renderEncoder = GetEncoder>(); - [renderEncoder setVertexBuffer: mtlBuff offset: offset atIndex: VBIndex]; + mtlStreamBuffers[bufferArrayLen] = mtlBuff; + mtlStreamBufferOffsets[bufferArrayLen] = offset; + bufferArrayLen++; } } + id renderEncoder = GetEncoder>(); + [renderEncoder setVertexBuffers: mtlStreamBuffers.data() + offsets: mtlStreamBufferOffsets.data() + withRange: range]; } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h index 6660beb2c4..8674124cc2 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h @@ -99,8 +99,17 @@ namespace AZ { AZStd::array m_srgsByIndex; AZStd::array m_srgsBySlot; + AZStd::array m_srgVisHashByIndex; }; + using MetalArgumentBufferArray = AZStd::array, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>; + using MetalArgumentBufferArrayOffsets = AZStd::array; + void BindArgumentBuffers(RHI::ShaderStage shaderStage, + uint16_t registerIdMin, + uint16_t registerIdMax, + MetalArgumentBufferArray& mtlArgBuffers, + MetalArgumentBufferArrayOffsets mtlArgBufferOffsets); + ShaderResourceBindings& GetShaderResourceBindingsByPipelineType(RHI::PipelineStateType pipelineType); //! This is kept as a separate struct so that we can robustly reset it. Every property diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp index f95e871d33..81e589e62e 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp @@ -12,6 +12,7 @@ #include "Atom_RHI_Metal_precompiled.h" #include +#include #include #include #include @@ -456,8 +457,35 @@ namespace AZ MTLColorWriteMask ConvertColorWriteMask(AZ::u8 writeMask) { - //todo::Based on the mask set the correct writemask - return MTLColorWriteMaskAll; + MTLColorWriteMask colorMask = MTLColorWriteMaskNone; + if(writeMask == 0) + { + return colorMask; + } + + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + { + return MTLColorWriteMaskAll; + } + + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + { + colorMask |= MTLColorWriteMaskRed; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + { + colorMask |= MTLColorWriteMaskGreen; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + { + colorMask |= MTLColorWriteMaskBlue; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + { + colorMask |= MTLColorWriteMaskAlpha; + } + + return colorMask; } MTLVertexFormat ConvertVertexFormat(RHI::Format format) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp index 60581aeb2c..88aa4b4ed1 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/MemoryPageAllocator.cpp @@ -44,7 +44,7 @@ namespace AZ if (memoryView.IsValid()) { heapMemoryUsage.m_residentInBytes += m_descriptor.m_pageSizeInBytes; - memoryView.SetName("BufferPage"); + memoryView.SetName(AZStd::string::format("BufferPage_%s", AZ::Uuid::CreateRandom().ToString().c_str())); } else { diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp index f237884aab..9a30a04deb 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp @@ -70,6 +70,7 @@ namespace AZ m_srgVisibilities.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); m_srgResourcesVisibility.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); + m_srgResourcesVisibilityHash.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); for (uint32_t srgLayoutIdx = 0; srgLayoutIdx < groupLayoutCount; ++srgLayoutIdx) { const RHI::ShaderResourceGroupLayout& srgLayout = *descriptor.GetShaderResourceGroupLayout(srgLayoutIdx); @@ -111,6 +112,7 @@ namespace AZ m_srgVisibilities[srgIndex] = mask; m_srgResourcesVisibility[srgIndex] = srgVis; + m_srgResourcesVisibilityHash[srgIndex] = srgVis.GetHash(); } // Cache the inline constant size and slot index @@ -123,12 +125,12 @@ namespace AZ size_t PipelineLayout::GetSlotByIndex(size_t index) const { - return m_slotToIndexTable[index]; + return m_indexToSlotTable[index]; } size_t PipelineLayout::GetIndexBySlot(size_t slot) const { - return m_indexToSlotTable[slot]; + return m_slotToIndexTable[slot]; } const RHI::ShaderStageMask& PipelineLayout::GetSrgVisibility(uint32_t index) const @@ -141,6 +143,11 @@ namespace AZ return m_srgResourcesVisibility[index]; } + const AZ::HashValue64 PipelineLayout::GetSrgResourcesVisibilityHash(uint32_t index) const + { + return m_srgResourcesVisibilityHash[index]; + } + uint32_t PipelineLayout::GetRootConstantsSize() const { return m_rootConstantsSize; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h index e8cd249393..6fd06ea29b 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h @@ -57,6 +57,9 @@ namespace AZ /// Returns srgVisibility data const ShaderResourceGroupVisibility& GetSrgResourcesVisibility(uint32_t index) const; + /// Returns srgVisibility hash + const AZ::HashValue64 GetSrgResourcesVisibilityHash(uint32_t index) const; + /// Returns the root constant specific layout information uint32_t GetRootConstantsSize() const; uint32_t GetRootConstantsSlotIndex() const; @@ -84,6 +87,9 @@ namespace AZ /// Cache Visibility across all the resources within the SRG AZStd::fixed_vector m_srgResourcesVisibility; + /// Cache Visibility hash across all the resources within the SRG + AZStd::fixed_vector m_srgResourcesVisibilityHash; + uint32_t m_rootConstantSlotIndex = (uint32_t)-1; uint32_t m_rootConstantsSize = 0; }; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp index 1f870548cc..94599bc390 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp @@ -73,6 +73,10 @@ namespace AZ m_metalView.metalLayer.drawableSize = CGSizeMake(descriptor.m_dimensions.m_imageWidth, descriptor.m_dimensions.m_imageHeight); } + else + { + AddSubView(); + } m_drawables.resize(descriptor.m_dimensions.m_imageCount); @@ -83,6 +87,20 @@ namespace AZ return RHI::ResultCode::Success; } + void SwapChain::AddSubView() + { + NativeViewType* superView = reinterpret_cast(m_nativeWindow); + + CGFloat screenScale = Platform::GetScreenScale(); + CGRect screenBounds = [superView bounds]; + m_metalView = [[RHIMetalView alloc] initWithFrame: screenBounds + scale: screenScale + device: m_mtlDevice]; + + [m_metalView retain]; + [superView addSubview: m_metalView]; + } + void SwapChain::ShutdownInternal() { if (m_viewController) @@ -161,16 +179,7 @@ namespace AZ } else { - NativeViewType* superView = reinterpret_cast(m_nativeWindow); - - CGFloat screenScale = Platform::GetScreenScale(); - CGRect screenBounds = [superView bounds]; - m_metalView = [[RHIMetalView alloc] initWithFrame: screenBounds - scale: screenScale - device: m_mtlDevice]; - - [m_metalView retain]; - [superView addSubview: m_metalView]; + AddSubView(); } } return RHI::ResultCode::Success; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h index d455f3dd0c..dfe8b3365d 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h @@ -49,6 +49,8 @@ namespace AZ RHI::ResultCode ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) override; ////////////////////////////////////////////////////////////////////////// + void AddSubView(); + id m_mtlCommandBuffer; RHIMetalView* m_metalView = nullptr; NativeViewControllerType* m_viewController = nullptr; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp index accc18b5ec..8cad9b1f56 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp @@ -334,19 +334,30 @@ namespace AZ VkColorComponentFlags ConvertComponentFlags(uint8_t sflags) { VkColorComponentFlags dflags = 0; - if (RHI::CheckBitsAny(sflags, static_cast(1))) + + if(sflags == 0) + { + return dflags; + } + + if(RHI::CheckBitsAll(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + { + return VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + } + + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { dflags |= VK_COLOR_COMPONENT_R_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(2))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) { dflags |= VK_COLOR_COMPONENT_G_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(4))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) { dflags |= VK_COLOR_COMPONENT_B_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(8))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) { dflags |= VK_COLOR_COMPONENT_A_BIT; } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp index 605f61fc33..ec58b5c940 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ImageView.cpp @@ -55,7 +55,11 @@ namespace AZ const auto& image = static_cast(resourceBase); const RHI::ImageViewDescriptor& descriptor = GetDescriptor(); - AZ_Assert(image.GetNativeImage() != VK_NULL_HANDLE, "Image has not been initialized."); + // this can happen when image has been invalidated/released right before re-compiling the image + if (image.GetNativeImage() == VK_NULL_HANDLE) + { + return RHI::ResultCode::Fail; + } RHI::Format viewFormat = descriptor.m_overrideFormat; // If an image is not owner of native image, it is a swapchain image. diff --git a/Gems/Atom/RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h b/Gems/Atom/RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h index a6ee9e8634..33d4b64082 100644 --- a/Gems/Atom/RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h +++ b/Gems/Atom/RHI/Vulkan/External/glad/2.0.0-beta/include/glad/vulkan.h @@ -12691,8 +12691,7 @@ static int glad_vk_find_extensions_vulkan( VkPhysicalDevice physical_device) { #endif GLAD_VK_KHR_push_descriptor = glad_vk_has_extension("VK_KHR_push_descriptor", extension_count, extensions); GLAD_VK_KHR_ray_tracing = (glad_vk_has_extension("VK_KHR_acceleration_structure", extension_count, extensions) - && glad_vk_has_extension("VK_KHR_ray_tracing_pipeline", extension_count, extensions) - && glad_vk_has_extension("VK_KHR_ray_query", extension_count, extensions)); + && glad_vk_has_extension("VK_KHR_ray_tracing_pipeline", extension_count, extensions)); GLAD_VK_KHR_relaxed_block_layout = glad_vk_has_extension("VK_KHR_relaxed_block_layout", extension_count, extensions); GLAD_VK_KHR_sampler_mirror_clamp_to_edge = glad_vk_has_extension("VK_KHR_sampler_mirror_clamp_to_edge", extension_count, extensions); GLAD_VK_KHR_sampler_ycbcr_conversion = glad_vk_has_extension("VK_KHR_sampler_ycbcr_conversion", extension_count, extensions); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h index d9afc54809..846e40ae02 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h @@ -138,6 +138,12 @@ namespace AZ //! Without per draw viewport, the viewport setup in pass is usually used. void UnsetViewport(); + //! Set stencil reference for following draws which are added to this DynamicDrawContext + void SetStencilReference(uint8_t stencilRef); + + //! Get the current stencil reference. + uint8_t GetStencilReference() const; + //! Draw Indexed primitives with vertex and index data and per draw srg //! The per draw srg need to be provided if it's required by shader. void DrawIndexed(void* vertexData, uint32_t vertexCount, void* indexData, uint32_t indexCount, RHI::IndexFormat indexFormat, Data::Instance < ShaderResourceGroup> drawSrg = nullptr); @@ -204,10 +210,13 @@ namespace AZ bool m_useScissor = false; RHI::Scissor m_scissor; - // current scissor + // current viewport bool m_useViewport = false; RHI::Viewport m_viewport; + // Current stencil reference value + uint8_t m_stencilRef = 0; + // Cached RHI pipeline states for different combination of render states AZStd::unordered_map m_cachedRhiPipelineStates; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h index 514e3e37a5..b403a86f00 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h @@ -32,6 +32,7 @@ namespace AZ : public Data::InstanceData { friend class ModelSystem; + public: AZ_INSTANCE_DATA(Model, "{C30F5522-B381-4B38-BBAF-6E0B1885C8B9}"); AZ_CLASS_ALLOCATOR(Model, AZ::SystemAllocator, 0); @@ -53,8 +54,6 @@ namespace AZ //! Returns whether a buffer upload is pending. bool IsUploadPending() const; - const AZ::Aabb& GetAabb() const; - const Data::Asset& GetModelAsset() const; //! Checks a ray for intersection against this model. The ray must be in the same coordinate space as the model. @@ -105,8 +104,6 @@ namespace AZ // Tracks whether buffers have all been streamed up to the GPU. bool m_isUploadPending = false; - - AZ::Aabb m_aabb; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index 9d0b36d126..a4a26c3070 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -265,6 +265,12 @@ namespace AZ // Update all bindings on this pass that are connected to bindings on other passes void UpdateConnectedBindings(); + // Update input and input/output bindings on this pass that are connected to bindings on other passes + void UpdateConnectedInputBindings(); + + // Update output bindings on this pass that are connected to bindings on other passes + void UpdateConnectedOutputBindings(); + protected: explicit Pass(const PassDescriptor& descriptor); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h index dfda93dca3..8220d25eb8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h @@ -57,6 +57,20 @@ namespace AZ } #endif } + + //! Prints a generic message at the appropriate indent level. + template + static void Printf([[maybe_unused]] const char* format, [[maybe_unused]] Args... args) + { +#ifdef AZ_ENABLE_SHADER_RELOAD_DEBUG_TRACKER + if (IsEnabled()) + { + const AZStd::string message = AZStd::string::format(format, args...); + + AZ_TracePrintf("ShaderReloadDebug", "%*s %s \n", s_indent, "", message.c_str()); + } +#endif + } //! Use this utility to call BeginSection(), and automatically call EndSection() when the object goes out of scope. class ScopedSection final diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp index 1a575c9385..2b6eb1f690 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp @@ -382,6 +382,16 @@ namespace AZ m_useViewport = false; } + void DynamicDrawContext::SetStencilReference(uint8_t stencilRef) + { + m_stencilRef = stencilRef; + } + + uint8_t DynamicDrawContext::GetStencilReference() const + { + return m_stencilRef; + } + void DynamicDrawContext::SetShaderVariant(ShaderVariantId shaderVariantId) { AZ_Assert( m_initialized && m_supportShaderVariants, "DynamicDrawContext is not initialized or unable to support shader variants. " @@ -475,6 +485,9 @@ namespace AZ drawItem.m_viewports = &m_viewport; } + // Set stencil reference. Used when stencil is enabled. + drawItem.m_stencilRef = m_stencilRef; + drawItemInfo.m_sortKey = m_sortKey++; m_cachedDrawItems.emplace_back(drawItemInfo); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index 4e65d68d6c..c498632393 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -97,6 +97,7 @@ namespace AZ ShaderReloadNotificationBus::MultiHandler::BusDisconnect(); for (auto& shaderItem : m_shaderCollection) { + ShaderReloadDebugTracker::Printf("(Material has ShaderAsset %p)", shaderItem.GetShaderAsset().Get()); ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId()); } @@ -226,7 +227,7 @@ namespace AZ // AssetBus overrides... void Material::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset newMaterialAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; @@ -241,7 +242,7 @@ namespace AZ // MaterialReloadNotificationBus overrides... void Material::OnMaterialAssetReinitialized(const Data::Asset& materialAsset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnMaterialAssetReinitialized %s", materialAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnMaterialAssetReinitialized %s", this, materialAsset.GetHint().c_str()); OnAssetReloaded(materialAsset); } @@ -249,7 +250,7 @@ namespace AZ // ShaderReloadNotificationBus overrides... void Material::OnShaderReinitialized([[maybe_unused]] const Shader& shader) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderReinitialized %s", shader.GetAsset().GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderReinitialized %s", this, shader.GetAsset().GetHint().c_str()); // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be // updated to correctly handle the reload, so it's safer to just reinitialize the whole material. @@ -260,7 +261,7 @@ namespace AZ { // TODO: I think we should make Shader handle OnShaderAssetReinitialized and treat it just like the shader reloaded. - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderAssetReinitialized %s", shaderAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderAssetReinitialized %s", this, shaderAsset.GetHint().c_str()); // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be // updated to correctly handle the reload, so it's safer to just reinitialize the whole material. @@ -269,7 +270,7 @@ namespace AZ void Material::OnShaderVariantReinitialized(const Shader& shader, const ShaderVariantId& /*shaderVariantId*/, ShaderVariantStableId shaderVariantStableId) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderVariantReinitialized %s variant %u", shader.GetAsset().GetHint().c_str(), shaderVariantStableId.GetIndex()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderVariantReinitialized %s variant %u", this, shader.GetAsset().GetHint().c_str(), shaderVariantStableId.GetIndex()); // Note that it would be better to check the shaderVariantId to see if that variant is relevant to this particular material before reinitializing it. // There could be hundreds or even thousands of variants for a shader, but only one of those variants will be used by any given material. So we could diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp index 17ff2c64c8..8809225150 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp @@ -62,8 +62,6 @@ namespace AZ { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - m_aabb = modelAsset.GetAabb(); - m_lods.resize(modelAsset.GetLodAssets().size()); for (size_t lodIndex = 0; lodIndex < m_lods.size(); ++lodIndex) @@ -127,11 +125,6 @@ namespace AZ return m_isUploadPending; } - const AZ::Aabb& Model::GetAabb() const - { - return m_aabb; - } - const Data::Asset& Model::GetModelAsset() const { return m_modelAsset; @@ -140,9 +133,16 @@ namespace AZ bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); + + if (!GetModelAsset()) + { + AZ_Assert(false, "Invalid Model - not created from a ModelAsset?"); + return false; + } + float start; float end; - const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), m_aabb, start, end); + const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), GetModelAsset()->GetAabb(), start, end); if (Intersect::ISECT_RAY_AABB_NONE != result) { if (ModelAsset* modelAssetPtr = m_modelAsset.Get()) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp index b213e84bca..a4e5d1ba10 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp @@ -49,7 +49,7 @@ namespace AZ With that percentage we can determine which Lod we want to use. */ - Aabb modelAabb = model.GetAabb(); + Aabb modelAabb = model.GetModelAsset()->GetAabb(); modelAabb.Translate(position); Vector3 center; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index 99a6e23914..865a13e13d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -1036,6 +1036,26 @@ namespace AZ } } + void Pass::UpdateConnectedInputBindings() + { + for (uint8_t idx : m_inputBindingIndices) + { + UpdateConnectedBinding(m_attachmentBindings[idx]); + } + for (uint8_t idx : m_inputOutputBindingIndices) + { + UpdateConnectedBinding(m_attachmentBindings[idx]); + } + } + + void Pass::UpdateConnectedOutputBindings() + { + for (uint8_t idx : m_outputBindingIndices) + { + UpdateConnectedBinding(m_attachmentBindings[idx]); + } + } + // --- Queuing functions with PassSystem --- void Pass::QueueForBuildAndInitialization() @@ -1264,7 +1284,7 @@ namespace AZ AZ_Assert(m_state == PassState::Idle, "Pass::FrameBegin - Pass [%s] is attempting to render, but is not in the Idle state.", m_path.GetCStr()); m_state = PassState::Rendering; - UpdateConnectedBindings(); + UpdateConnectedInputBindings(); UpdateOwnedAttachments(); CreateTransientAttachments(params.m_frameGraphBuilder->GetAttachmentDatabase()); @@ -1273,6 +1293,8 @@ namespace AZ // FrameBeginInternal needs to be the last function be called in FrameBegin because its implementation expects // all the attachments are imported to database (for example, ImageAttachmentPreview) FrameBeginInternal(params); + + UpdateConnectedOutputBindings(); } void Pass::FrameEnd() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp index d74ffbfca2..2c06755d78 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp @@ -147,7 +147,7 @@ namespace AZ // AssetBus overrides void Shader::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Shader::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Shader::OnAssetReloaded %s", this, asset.GetHint().c_str()); if (asset->GetId() == m_asset->GetId()) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index 0afc174ad3..4d9e150a42 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -139,7 +139,7 @@ namespace AZ void MaterialAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("MaterialAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset newMaterialTypeAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp index 145eedf2e6..b62ab02023 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp @@ -189,7 +189,7 @@ namespace AZ void MaterialTypeAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("MaterialTypeAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialTypeAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); // The order of asset reloads is non-deterministic. If the MaterialTypeAsset reloads before these // dependency assets, this will make sure the MaterialTypeAsset gets the latest ones when they reload. diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp index 08d0cab935..3d1b99df46 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -520,7 +520,7 @@ namespace AZ // AssetBus overrides... void ShaderAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("ShaderAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset shaderVariantAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; AZ_Assert(shaderVariantAsset->GetStableId() == RootShaderVariantStableId, @@ -536,7 +536,7 @@ namespace AZ /// ShaderVariantFinderNotificationBus overrides void ShaderAsset::OnShaderVariantTreeAssetReady(Data::Asset shaderVariantTreeAsset, bool isError) { - ShaderReloadDebugTracker::ScopedSection reloadSection("ShaderAsset::OnShaderVariantTreeAssetReady %s", shaderVariantTreeAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnShaderVariantTreeAssetReady %s", this, shaderVariantTreeAsset.GetHint().c_str()); AZStd::unique_lock lock(m_variantTreeMutex); if (isError) diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material index 27540c587e..da35fda141 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material @@ -14,13 +14,14 @@ }, "clearCoat": { "enable": true, - "normalMap": "EngineAssets/Textures/perlinNoiseNormal_ddn.tif" + "normalMap": "EngineAssets/Textures/perlinNoiseNormal_ddn.tif", + "normalStrength": 0.10000000149011612 }, "general": { "applySpecularAA": true }, "metallic": { - "factor": 0.5 + "factor": 0.10000000149011612 }, "normal": { "factor": 0.05000000074505806, @@ -31,6 +32,9 @@ }, "roughness": { "factor": 0.0 + }, + "specularF0": { + "enableMultiScatterCompensation": true } } -} +} \ No newline at end of file diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index 941e7dea20..b043cbbaca 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -8,7 +8,7 @@ "Gem" ], "user_tags": [ - "AtomConent" + "AtomContent" ], "icon_path": "preview.png" } diff --git a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist index 79bb726c9f..c7eddb10f2 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist +++ b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist @@ -4,7 +4,7 @@ { "StableId": 1, "Options": { - "o_preMultiplyAlpha": "true", + "o_preMultiplyAlpha": "false", "o_alphaTest": "false", "o_srgbWrite": "true", "o_modulate": "Modulate::None" @@ -14,7 +14,7 @@ "StableId": 2, "Options": { "o_preMultiplyAlpha": "false", - "o_alphaTest": "false", + "o_alphaTest": "true", "o_srgbWrite": "true", "o_modulate": "Modulate::None" } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice index c20c6cde0b..b4c9eac10f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice +++ b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice @@ -960,7 +960,7 @@ - + @@ -968,10 +968,11 @@ - + + @@ -1109,4 +1110,3 @@ - diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp index f14340b4c9..e7f2a98a71 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp @@ -118,8 +118,7 @@ namespace AZ void EditorAttachmentComponent::Activate() { Base::Activate(); - m_boneFollower.Activate(GetEntity(), CreateAttachmentConfiguration(), - false); // Entity's don't animate in Editor + m_boneFollower.Activate(GetEntity(), CreateAttachmentConfiguration(), /*targetCanAnimate=*/true); } void EditorAttachmentComponent::Deactivate() diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index 69bec21a6c..992b6319d8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp @@ -112,13 +112,13 @@ namespace AZ ->DataElement(Edit::UIHandlers::Default, &AreaLightComponentConfig::m_enableShutters, "Enable shutters", "Restrict the light to a specific beam angle depending on shape.") ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::ShuttersMustBeEnabled) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_innerShutterAngleDegrees, "Inner angle", "The inner angle of the shutters where the light beam begins to be occluded.") - ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 180.0f) + ->Attribute(Edit::Attributes::Min, 0.5f) + ->Attribute(Edit::Attributes::Max, 90.0f) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShuttersDisabled) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_outerShutterAngleDegrees, "Outer angle", "The outer angle of the shutters where the light beam is completely occluded.") - ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 180.0f) + ->Attribute(Edit::Attributes::Min, 0.5f) + ->Attribute(Edit::Attributes::Max, 90.0f) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShuttersDisabled) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h index fb99a99e1a..8f1b216af5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h @@ -29,7 +29,7 @@ namespace AZ static void Reflect(ReflectContext* context); - DiffuseGlobalIlluminationQualityLevel m_qualityLevel; + DiffuseGlobalIlluminationQualityLevel m_qualityLevel = DiffuseGlobalIlluminationQualityLevel::Low; }; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp index bdb5686e89..7df965d831 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp @@ -36,7 +36,7 @@ namespace AZ ->Attribute(Edit::Attributes::Category, "Atom") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png") - ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c) })) + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13) })) ->Attribute(Edit::Attributes::AutoExpand, true) ->Attribute(Edit::Attributes::HelpPageURL, "https://") ; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index e7eecd3c7f..90d3763067 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -460,10 +460,10 @@ namespace AZ Aabb MeshComponentController::GetLocalBounds() { - const Data::Instance model = GetModel(); - if (model) + if (m_meshHandle.IsValid() && m_meshFeatureProcessor) { - Aabb aabb = model->GetAabb(); + Aabb aabb = m_meshFeatureProcessor->GetLocalAabb(m_meshHandle); + aabb.MultiplyByScale(m_cachedNonUniformScale); return aabb; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp index 9f68a7d12c..1c4657d8df 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp @@ -311,7 +311,8 @@ namespace AZ Data::Asset modelAsset = actor->GetMeshAsset(); if (!modelAsset.IsReady()) { - AZ_Error("CreateSkinnedMeshInputFromActor", false, "Attempting to create skinned mesh input buffers for an actor that doesn't have a loaded model."); + AZ_Warning("CreateSkinnedMeshInputFromActor", false, "Check if the actor has a mesh added. Right click the source file in the asset browser, click edit settings, " + "and navigate to the Meshes tab. Add a mesh if it's missing."); return nullptr; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 532c8720b5..9337d5aad7 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -83,12 +83,20 @@ namespace AZ void AtomActorInstance::UpdateBounds() { // Update RenderActorInstance world bounding box - // The bounding box is moving with the actor instance. It is static in the way that it does not change shape. + // The bounding box is moving with the actor instance. // The entity and actor transforms are kept in sync already. m_worldAABB = AZ::Aabb::CreateFromMinMax(m_actorInstance->GetAABB().GetMin(), m_actorInstance->GetAABB().GetMax()); // Update RenderActorInstance local bounding box - m_localAABB = AZ::Aabb::CreateFromMinMax(m_actorInstance->GetStaticBasedAABB().GetMin(), m_actorInstance->GetStaticBasedAABB().GetMax()); + // NB: computing the local bbox from the world bbox makes the local bbox artifically larger than it should be + // instead EMFX should support getting the local bbox from the actor instance directly + m_localAABB = m_worldAABB.GetTransformedAabb(m_transformInterface->GetWorldTM().GetInverse()); + + // Update bbox on mesh instance if it exists + if (m_meshFeatureProcessor && m_meshHandle && m_meshHandle->IsValid() && m_skinnedMeshInstance) + { + m_meshFeatureProcessor->SetLocalAabb(*m_meshHandle, m_localAABB); + } AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(m_entityId); } @@ -456,9 +464,8 @@ namespace AZ void AtomActorInstance::Create() { Destroy(); - m_skinnedMeshInputBuffers = GetRenderActor()->FindOrCreateSkinnedMeshInputBuffers(); - AZ_Error("AtomActorInstance", m_skinnedMeshInputBuffers, "Failed to get SkinnedMeshInputBuffers from Actor."); + AZ_Warning("AtomActorInstance", m_skinnedMeshInputBuffers, "Failed to create SkinnedMeshInputBuffers from Actor. It is likely that this actor doesn't have any meshes"); if (m_skinnedMeshInputBuffers) { m_boneTransforms = CreateBoneTransformBufferFromActorInstance(m_actorInstance, GetSkinningMethod()); diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr index d57e91f35d..aa7a8c4bd9 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr @@ -69,10 +69,16 @@ proj.launch-config = {loc('../../SDK/Atom/Scripts/Python/DCC_Materials/maya_mate loc('../../azpy/__init__.py'): ('custom', (u'', 'launch-oobMrvXFf1SwtYBg')), + loc('../../azpy/constants.py'): ('custom', + (u'', + 'launch-GeaM41WYMGA1sEfm')), loc('../../azpy/env_base.py'): ('project', (u'', 'launch-GeaM41WYMGA1sEfm')), loc('../../azpy/maya/callbacks/node_message_callback_handler.py'): ('c'\ 'ustom', + (u'', + 'launch-GeaM41WYMGA1sEfm')), + loc('../../config.py'): ('custom', (u'', 'launch-GeaM41WYMGA1sEfm'))} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py index 0a0c6b8337..5b5c5aa079 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py @@ -165,12 +165,14 @@ def get_current_project(): bootstrap_box = None try: - bootstrap_box = Box.from_json(filename=PATH_USER_O3DE_BOOTSTRAP, + bootstrap_box = Box.from_json(filename=str(Path(PATH_USER_O3DE_BOOTSTRAP).resolve()), encoding="utf-8", errors="strict", object_pairs_hook=OrderedDict) - except FileExistsError as e: - _LOGGER.error('File does not exist: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) + except Exception as e: + # this file runs in py2.7 for Maya 2020, FileExistsError is not defined + _LOGGER.error('FileExistsError: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) + _LOGGER.error('exception is: {}'.format(e)) if bootstrap_box: # this seems fairly hard coded - what if the data changes? diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py index e10221d324..7f6b0e4523 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py @@ -226,7 +226,18 @@ TAG_DEFAULT_PY = str('Launch_pyBASE.bat') FILENAME_DEFAULT_CONFIG = str('DCCSI_config.json') # new o3de related paths -PATH_USER_O3DE = str('{home}\\{o3de}').format(home=expanduser("~"), +# os.path.expanduser("~") returns different values in py2.7 vs 3 +PATH_USER_HOME = expanduser("~") +_LOGGER.debug('user home: {}'.format(PATH_USER_HOME)) + +# special case, make sure didn't return \documents +parts = os.path.split(PATH_USER_HOME) + +if str(parts[1].lower()) == 'documents': + PATH_USER_HOME = parts[0] + _LOGGER.debug('user home CORRECTED: {}'.format(PATH_USER_HOME)) + +PATH_USER_O3DE = str('{home}\\{o3de}').format(home=PATH_USER_HOME, o3de=TAG_O3DE_FOLDER) PATH_USER_O3DE_REGISTRY = str('{0}\\Registry').format(PATH_USER_O3DE) PATH_USER_O3DE_BOOTSTRAP = str('{reg}\\{file}').format(reg=PATH_USER_O3DE_REGISTRY, diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp index 873ef7248d..0133690cea 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp @@ -44,7 +44,7 @@ namespace Blast ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute( AZ::Edit::Attributes::HelpPageURL, - "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-blast-actor.html") + "https://docs.o3de.org/docs/user-guide/components/reference/blast-family/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::Default, &EditorBlastFamilyComponent::m_blastAsset, "Blast asset", diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp index 77788b6aee..4a6f41331b 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp @@ -67,7 +67,7 @@ namespace Blast ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute( AZ::Edit::Attributes::HelpPageURL, - "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-blast-actor.html") + "https://docs.o3de.org/docs/user-guide/components/reference/blast-family-mesh-data/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::CheckBox, &EditorBlastMeshDataComponent::m_showMeshAssets, diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index 157e8c0136..4ba6236a36 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -508,8 +508,7 @@ namespace EMotionFX void EditorActorComponent::OnAssetReady(AZ::Data::Asset asset) { m_actorAsset = asset; - Actor* actor = m_actorAsset->GetActor(); - AZ_Assert(m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid."); + AZ_Assert(m_actorAsset.IsReady() && m_actorAsset->GetActor(), "Actor asset should be loaded and actor valid."); CheckActorCreation(); } diff --git a/Gems/LyShine/Code/Source/RenderGraph.cpp b/Gems/LyShine/Code/Source/RenderGraph.cpp index d5a1df7b15..ad2c46f8e6 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.cpp +++ b/Gems/LyShine/Code/Source/RenderGraph.cpp @@ -137,7 +137,11 @@ namespace LyShine AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); const UiRenderer::UiShaderData& uiShaderData = uiRenderer->GetUiShaderData(); - dynamicDraw->SetShaderVariant(uiShaderData.m_shaderVariantDefault); + // Set render state + dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState); + dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState); + + dynamicDraw->SetShaderVariant(uiRenderer->GetCurrentShaderVariant()); // Set up per draw SRG AZ::Data::Instance drawSrg = dynamicDraw->NewDrawSrg(); @@ -307,7 +311,7 @@ namespace LyShine //////////////////////////////////////////////////////////////////////////////////////////////////// void MaskRenderNode::Render(UiRenderer* uiRenderer) { - int priorBaseState = uiRenderer->GetBaseState(); + UiRenderer::BaseState priorBaseState = uiRenderer->GetBaseState(); if (m_isMaskingEnabled || m_drawBehind) { @@ -369,68 +373,61 @@ namespace LyShine #endif //////////////////////////////////////////////////////////////////////////////////////////////////// - void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState) + void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState) { + UiRenderer::BaseState curBaseState = priorBaseState; + // If using alpha test for drawing the renderable components on this element then we turn on // alpha test as a pre-render step - int alphaTest = 0; - if (m_useAlphaTest) - { - alphaTest = GS_ALPHATEST_GREATER; - } + curBaseState.m_useAlphaTest = m_useAlphaTest; // if either of the draw flags are checked then we may want to draw the renderable component(s) // on this element, otherwise use the color mask to stop them rendering - int colorMask = GS_COLMASK_NONE; + curBaseState.m_blendState.m_enable = false; + curBaseState.m_blendState.m_writeMask = 0x0; if ((m_drawBehind && firstPass) || (m_drawInFront && !firstPass)) { - colorMask = 0; // mask everything, don't write color or alpha, we just write to stencil buffer + curBaseState.m_blendState.m_enable = true; + curBaseState.m_blendState.m_writeMask = 0xF; } - if (m_isMaskingEnabled) + if (m_isMaskingEnabled) { + AZ::RHI::StencilOpState stencilOpState; + stencilOpState.m_func = AZ::RHI::ComparisonFunc::Equal; + // masking is enabled so we want to setup to increment (first pass) or decrement (second pass) // the stencil buff when rendering the renderable component(s) on this element - int passOp = 0; if (firstPass) { - passOp = STENCOP_PASS(FSS_STENCOP_INCR); - gEnv->pRenderer->PushProfileMarker(s_maskIncrProfileMarker); + stencilOpState.m_passOp = AZ::RHI::StencilOp::Increment; } else { - passOp = STENCOP_PASS(FSS_STENCOP_DECR); - gEnv->pRenderer->PushProfileMarker(s_maskDecrProfileMarker); + stencilOpState.m_passOp = AZ::RHI::StencilOp::Decrement; } - // set up for stencil write - const uint32 stencilRef = uiRenderer->GetStencilRef(); - const uint32 stencilMask = 0xFF; - const uint32 stencilWriteMask = 0xFF; - const int32 stencilState = STENC_FUNC(FSS_STENCFUNC_EQUAL) - | STENCOP_FAIL(FSS_STENCOP_KEEP) | STENCOP_ZFAIL(FSS_STENCOP_KEEP) | passOp; - gEnv->pRenderer->SetStencilState(stencilState, stencilRef, stencilMask, stencilWriteMask); + curBaseState.m_stencilState.m_frontFace = stencilOpState; + curBaseState.m_stencilState.m_backFace = stencilOpState; - // Set the base state that should be used when rendering the renderable component(s) on this - // element - uiRenderer->SetBaseState(priorBaseState | GS_STENCIL | alphaTest | colorMask); + // set up for stencil write + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + dynamicDraw->SetStencilReference(uiRenderer->GetStencilRef()); + curBaseState.m_stencilState.m_enable = true; + curBaseState.m_stencilState.m_writeMask = 0xFF; } else { // masking is not enabled - - // Even if not masking we still use alpha test (if checked). This is primarily to help the user to - // visualize what their alpha tested mask looks like. - if (colorMask || alphaTest) - { - uiRenderer->SetBaseState(priorBaseState | colorMask | alphaTest); - } + curBaseState.m_stencilState.m_enable = false; } + + uiRenderer->SetBaseState(curBaseState); } //////////////////////////////////////////////////////////////////////////////////////////////////// - void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState) + void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState) { if (m_isMaskingEnabled) { @@ -442,26 +439,29 @@ namespace LyShine if (firstPass) { uiRenderer->IncrementStencilRef(); - gEnv->pRenderer->PopProfileMarker(s_maskIncrProfileMarker); } else { uiRenderer->DecrementStencilRef(); - gEnv->pRenderer->PopProfileMarker(s_maskDecrProfileMarker); } - // turn off stencil write and turn on stencil test - const uint32 stencilRef = uiRenderer->GetStencilRef(); - const uint32 stencilMask = 0xFF; - const uint32 stencilWriteMask = 0x00; - const int32 stencilState = STENC_FUNC(FSS_STENCFUNC_EQUAL) - | STENCOP_FAIL(FSS_STENCOP_KEEP) | STENCOP_ZFAIL(FSS_STENCOP_KEEP) | STENCOP_PASS(FSS_STENCOP_KEEP); - gEnv->pRenderer->SetStencilState(stencilState, stencilRef, stencilMask, stencilWriteMask); + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + dynamicDraw->SetStencilReference(uiRenderer->GetStencilRef()); if (firstPass) { - // first pass, turn on stencil test for drawing children - uiRenderer->SetBaseState(priorBaseState | GS_STENCIL); + UiRenderer::BaseState curBaseState = priorBaseState; + + // turn off stencil write and turn on stencil test + curBaseState.m_stencilState.m_enable = true; + curBaseState.m_stencilState.m_writeMask = 0x00; + + AZ::RHI::StencilOpState stencilOpState; + stencilOpState.m_func = AZ::RHI::ComparisonFunc::Equal; + curBaseState.m_stencilState.m_frontFace = stencilOpState; + curBaseState.m_stencilState.m_backFace = stencilOpState; + + uiRenderer->SetBaseState(curBaseState); } else { @@ -475,7 +475,6 @@ namespace LyShine // remove any color mask or alpha test that we set in pre-render uiRenderer->SetBaseState(priorBaseState); } - } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -637,35 +636,24 @@ namespace LyShine //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::BeginMask(bool isMaskingEnabled, bool useAlphaTest, bool drawBehind, bool drawInFront) { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) - // this uses pool allocator MaskRenderNode* maskRenderNode = new MaskRenderNode(m_currentMask, isMaskingEnabled, useAlphaTest, drawBehind, drawInFront); m_currentMask = maskRenderNode; m_renderNodeListStack.push(&maskRenderNode->GetMaskRenderNodeList()); -#else - AZ_UNUSED(drawInFront); - AZ_UNUSED(drawBehind); - AZ_UNUSED(useAlphaTest); - AZ_UNUSED(isMaskingEnabled); -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::StartChildrenForMask() { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) AZ_Assert(m_currentMask, "Calling StartChildrenForMask while not defining a mask"); m_renderNodeListStack.pop(); m_renderNodeListStack.push(&m_currentMask->GetContentRenderNodeList()); -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::EndMask() { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) AZ_Assert(m_currentMask, "Calling EndMask while not defining a mask"); if (m_currentMask) { @@ -686,7 +674,6 @@ namespace LyShine m_renderNodeListStack.top()->push_back(newMaskRenderNode); } } -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -996,6 +983,12 @@ namespace LyShine // LYSHINE_ATOM_TODO - will probably need to support this when converting UI Editor to use Atom AZ_UNUSED(viewportSize); + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + + // Disable stencil and enable blend/color write + dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState); + dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState); + // First render the render targets, they are sorted so that more deeply nested ones are rendered first. #ifdef LYSHINE_ATOM_TODO // keeping this code for reference for future phase (render targets) diff --git a/Gems/LyShine/Code/Source/RenderGraph.h b/Gems/LyShine/Code/Source/RenderGraph.h index 2f1586e857..355616a29a 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.h +++ b/Gems/LyShine/Code/Source/RenderGraph.h @@ -22,12 +22,11 @@ #include #include +#include "UiRenderer.h" #ifndef _RELEASE #include "LyShineDebug.h" #endif -class UiRenderer; - namespace LyShine { enum RenderNodeType @@ -157,8 +156,8 @@ namespace LyShine #endif private: // functions - void SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState); - void SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState); + void SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState); + void SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState); private: // data AZStd::vector m_maskRenderNodes; //!< The render nodes used to render the mask shape diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 6971aa3fc9..b1348d9ea7 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -20,7 +20,6 @@ #include #include #include -#include // LYSHINE_ATOM_TODO - remove when GS_DEPTHFUNC_LEQUAL reference is removed with LyShine render target Atom conversion #include #include @@ -33,9 +32,7 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// UiRenderer::UiRenderer(AZ::RPI::ViewportContextPtr viewportContext) - : m_baseState(GS_DEPTHFUNC_LEQUAL) - , m_stencilRef(0) - , m_viewportContext(viewportContext) + : m_viewportContext(viewportContext) { // Use bootstrap scene event to indicate when the RPI has fully // initialized with all assets loaded and is ready to be used @@ -127,6 +124,8 @@ void UiRenderer::CreateDynamicDrawContext(AZ::RPI::ScenePtr scene, AZ::Data::Ins { "TEXCOORD", AZ::RHI::Format::R32G32_FLOAT }, { "BLENDINDICES", AZ::RHI::Format::R16G16_UINT } } ); + m_dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::StencilState + | AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode); m_dynamicDraw->EndInit(); } @@ -170,25 +169,24 @@ void UiRenderer::CacheShaderData(const AZ::RHI::Ptr shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true"))); shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None"))); m_uiShaderData.m_shaderVariantDefault = dynamicDraw->UseShaderVariant(shaderOptionsDefault); + AZ::RPI::ShaderOptionList shaderOptionsAlphaTest; + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_preMultiplyAlpha"), AZ::Name("false"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("true"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None"))); + m_uiShaderData.m_shaderVariantAlphaTest = dynamicDraw->UseShaderVariant(shaderOptionsAlphaTest); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiRenderer::BeginUiFrameRender() { -#ifdef LYSHINE_ATOM_TODO - m_renderer = gEnv->pRenderer; - - // we are rendering at the end of the frame, after tone mapping, so we should be writing sRGB values - m_renderer->SetSrgbWrite(true); - #ifndef _RELEASE if (m_debugTextureDataRecordLevel > 0) { m_texturesUsedInFrame.clear(); } #endif -#endif - + // Various platform drivers expect all texture slots used in the shader to be bound BindNullTexture(); } @@ -204,18 +202,10 @@ void UiRenderer::EndUiFrameRender() //////////////////////////////////////////////////////////////////////////////////////////////////// void UiRenderer::BeginCanvasRender() { -#ifdef LYSHINE_ATOM_TODO - m_baseState = GS_NODEPTHTEST; - m_stencilRef = 0; - // Set default starting state - IRenderer* renderer = gEnv->pRenderer; - - renderer->SetCullMode(R_CULL_DISABLE); - renderer->SetColorOp(eCO_MODULATE, eCO_MODULATE, DEF_TEXARG0, DEF_TEXARG0); - renderer->SetState(GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA | GS_NODEPTHTEST); -#endif + // Set base state + m_baseState.ResetToDefault(); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -229,11 +219,13 @@ AZ::RHI::Ptr UiRenderer::GetDynamicDrawContext() return m_dynamicDraw; } +//////////////////////////////////////////////////////////////////////////////////////////////////// const UiRenderer::UiShaderData& UiRenderer::GetUiShaderData() { return m_uiShaderData; } +//////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Matrix4x4 UiRenderer::GetModelViewProjectionMatrix() { auto viewportContext = GetViewportContext(); @@ -253,6 +245,7 @@ AZ::Matrix4x4 UiRenderer::GetModelViewProjectionMatrix() return modelViewProjMat; } +//////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Vector2 UiRenderer::GetViewportSize() { auto viewportContext = GetViewportContext(); @@ -267,17 +260,30 @@ AZ::Vector2 UiRenderer::GetViewportSize() } //////////////////////////////////////////////////////////////////////////////////////////////////// -int UiRenderer::GetBaseState() +UiRenderer::BaseState UiRenderer::GetBaseState() { return m_baseState; } //////////////////////////////////////////////////////////////////////////////////////////////////// -void UiRenderer::SetBaseState(int state) +void UiRenderer::SetBaseState(BaseState state) { m_baseState = state; } +//////////////////////////////////////////////////////////////////////////////////////////////////// +AZ::RPI::ShaderVariantId UiRenderer::GetCurrentShaderVariant() +{ + AZ::RPI::ShaderVariantId variantId = m_uiShaderData.m_shaderVariantDefault; + + if (m_baseState.m_useAlphaTest) + { + variantId = m_uiShaderData.m_shaderVariantAlphaTest; + } + + return variantId; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// uint32 UiRenderer::GetStencilRef() { @@ -354,6 +360,7 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) { if (recordingOption > 0) { +#ifdef LYSHINE_ATOM_TODO // Convert debug to use Atom images // compute the total area of all the textures, also create a vector that we can sort by area AZStd::vector textures; int totalArea = 0; @@ -431,6 +438,7 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) texture->GetWidth(), texture->GetHeight(), texture->GetDataSize(), texture->GetFormatName(), texture->GetName()); WriteLine(buffer, white); } +#endif } } diff --git a/Gems/LyShine/Code/Source/UiRenderer.h b/Gems/LyShine/Code/Source/UiRenderer.h index 888c88586a..413e8c45cf 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.h +++ b/Gems/LyShine/Code/Source/UiRenderer.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #ifndef _RELEASE @@ -38,6 +39,36 @@ public: // types AZ::RHI::ShaderInputConstantIndex m_isClampInputIndex; AZ::RPI::ShaderVariantId m_shaderVariantDefault; + AZ::RPI::ShaderVariantId m_shaderVariantAlphaTest; + }; + + // Base state + struct BaseState + { + BaseState() + { + ResetToDefault(); + } + + void ResetToDefault() + { + // Enable blend/color write + m_blendState.m_enable = true; + m_blendState.m_writeMask = 0xF; + m_blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource; + m_blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse; + m_blendState.m_blendOp = AZ::RHI::BlendOp::Add; + + // Disable stencil + m_stencilState = AZ::RHI::StencilState(); + m_stencilState.m_enable = 0; + + m_useAlphaTest = false; + } + + AZ::RHI::TargetBlendState m_blendState; + AZ::RHI::StencilState m_stencilState; + bool m_useAlphaTest = false; }; public: // member functions @@ -74,10 +105,13 @@ public: // member functions AZ::Vector2 GetViewportSize(); //! Get the current base state - int GetBaseState(); + BaseState GetBaseState(); //! Set the base state - void SetBaseState(int state); + void SetBaseState(BaseState state); + + //! Get the shader variant based on current render properties + AZ::RPI::ShaderVariantId GetCurrentShaderVariant(); //! Get the current stencil test reference value uint32 GetStencilRef(); @@ -126,8 +160,8 @@ protected: // attributes static constexpr char LogName[] = "UiRenderer"; - int m_baseState; - uint32 m_stencilRef; + BaseState m_baseState; + uint32 m_stencilRef = 0; UiShaderData m_uiShaderData; AZ::RHI::Ptr m_dynamicDraw; diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif new file mode 100644 index 0000000000..fd29516609 --- /dev/null +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8474b897fe02f70ed8d0e5c47cae8c816c832a7a5739b8c32317737fd275774f +size 1069752 diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 06088ec0b5..cd980456f3 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -180,7 +180,10 @@ namespace Multiplayer AZ::TickBus::Handler::BusConnect(); AzFramework::SessionNotificationBus::Handler::BusConnect(); m_networkInterface = AZ::Interface::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this); - m_consoleCommandHandler.Connect(AZ::Interface::Get()->GetConsoleCommandInvokedEvent()); + if (AZ::Interface::Get()) + { + m_consoleCommandHandler.Connect(AZ::Interface::Get()->GetConsoleCommandInvokedEvent()); + } AZ::Interface::Register(this); AZ::Interface::Register(this); @@ -192,6 +195,8 @@ namespace Multiplayer { AZ::Interface::Unregister(this); AZ::Interface::Unregister(this); + m_consoleCommandHandler.Disconnect(); + AZ::Interface::Get()->DestroyNetworkInterface(AZ::Name(MPNetworkInterfaceName)); AzFramework::SessionNotificationBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); } @@ -200,19 +205,10 @@ namespace Multiplayer { AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); + m_pendingConnectionTickets.push(config.m_playerSessionId); AZStd::string hostname = config.m_dnsName.empty() ? config.m_ipAddress : config.m_dnsName; const IpAddress ipAddress(hostname.c_str(), config.m_port, m_networkInterface->GetType()); - ConnectionId connectionId = m_networkInterface->Connect(ipAddress); - - AzNetworking::IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId); - if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so - { - connection->SetUserData(new ClientToServerConnectionData(connection, *this, config.m_playerSessionId)); - } - else - { - reinterpret_cast(connection->GetUserData())->SetProviderTicket(config.m_playerSessionId); - } + m_networkInterface->Connect(ipAddress); return true; } @@ -585,15 +581,16 @@ namespace Multiplayer datum.m_isInvited = false; datum.m_agentType = MultiplayerAgentType::Client; + AZStd::string providerTicket; if (connection->GetConnectionRole() == ConnectionRole::Connector) { AZLOG_INFO("New outgoing connection to remote address: %s", connection->GetRemoteAddress().GetString().c_str()); - AZ::CVarFixedString providerTicket; - if (connection->GetUserData() != nullptr) + if (!m_pendingConnectionTickets.empty()) { - providerTicket = reinterpret_cast(connection->GetUserData())->GetProviderTicket(); + providerTicket = m_pendingConnectionTickets.front(); + m_pendingConnectionTickets.pop(); } - connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket)); + connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket.c_str())); } else { @@ -623,7 +620,11 @@ namespace Multiplayer { if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so { - connection->SetUserData(new ClientToServerConnectionData(connection, *this)); + connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket)); + } + else + { + reinterpret_cast(connection->GetUserData())->SetProviderTicket(providerTicket); } AZStd::unique_ptr window = AZStd::make_unique(); @@ -647,14 +648,10 @@ namespace Multiplayer AZStd::string reasonString = ToString(reason); AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str()); - if (connection->GetConnectionRole() == ConnectionRole::Acceptor) + // The client is disconnecting + if (GetAgentType() == MultiplayerAgentType::Client) { - // The authority is shutting down its connection - m_shutdownEvent.Signal(m_networkInterface); - } - else if (GetAgentType() == MultiplayerAgentType::Client && connection->GetConnectionRole() == ConnectionRole::Connector) - { - // The client is disconnecting + AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Connector, "Client connection role should only ever be Connector"); m_clientDisconnectedEvent.Signal(); } @@ -670,7 +667,7 @@ namespace Multiplayer if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer) { if (AZ::Interface::Get() != nullptr && - connection->GetConnectionRole() == ConnectionRole::Connector) + connection->GetConnectionRole() == ConnectionRole::Acceptor) { AzFramework::PlayerConnectionConfig config; config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); @@ -681,12 +678,15 @@ namespace Multiplayer // Signal to session management when there are no remaining players in a dedicated server for potential cleanup // We avoid this for client server as the host itself is a user - if (m_agentType == MultiplayerAgentType::DedicatedServer && connection->GetConnectionRole() == ConnectionRole::Connector) + if (m_agentType == MultiplayerAgentType::DedicatedServer && connection->GetConnectionRole() == ConnectionRole::Acceptor) { - if (AZ::Interface::Get() != nullptr - && m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) + if (m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) { - AZ::Interface::Get()->HandleDestroySession(); + m_shutdownEvent.Signal(m_networkInterface); + if (AZ::Interface::Get() != nullptr) + { + AZ::Interface::Get()->HandleDestroySession(); + } } } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 0efef3ebe4..00313c6b65 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -146,6 +146,8 @@ namespace Multiplayer ConnectionAcquiredEvent m_connAcquiredEvent; ClientDisconnectedEvent m_clientDisconnectedEvent; + AZStd::queue m_pendingConnectionTickets; + AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId; diff --git a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp index 7b09b65de4..9579c84fc1 100644 --- a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp +++ b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp @@ -35,14 +35,16 @@ namespace UnitTest m_initHandler = Multiplayer::SessionInitEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestInitEvent(value); }); m_mpComponent->AddSessionInitHandler(m_initHandler); - m_shutdownHandler = Multiplayer::SessionInitEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestShutdownEvent(value); }); + m_shutdownHandler = Multiplayer::SessionShutdownEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestShutdownEvent(value); }); m_mpComponent->AddSessionShutdownHandler(m_shutdownHandler); m_connAcquiredHandler = Multiplayer::ConnectionAcquiredEvent::Handler([this](Multiplayer::MultiplayerAgentDatum value) { TestConnectionAcquiredEvent(value); }); m_mpComponent->AddConnectionAcquiredHandler(m_connAcquiredHandler); + m_mpComponent->Activate(); } void TearDown() override { + m_mpComponent->Deactivate(); delete m_mpComponent; delete m_netComponent; AZ::NameDictionary::Destroy(); @@ -86,6 +88,7 @@ namespace UnitTest TEST_F(MultiplayerSystemTests, TestShutdownEvent) { + m_mpComponent->InitializeMultiplayer(Multiplayer::MultiplayerAgentType::DedicatedServer); IMultiplayerConnectionMock connMock1 = IMultiplayerConnectionMock(AzNetworking::ConnectionId(), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Acceptor); IMultiplayerConnectionMock connMock2 = IMultiplayerConnectionMock(AzNetworking::ConnectionId(), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Connector); m_mpComponent->OnDisconnect(&connMock1, AzNetworking::DisconnectReason::None, AzNetworking::TerminationEndpoint::Local); diff --git a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp index 7da677a7eb..7eb07f46d7 100644 --- a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp +++ b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp @@ -52,7 +52,7 @@ namespace NvCloth ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Cloth.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Cloth.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-cloth.html") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/cloth/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->UIElement(AZ::Edit::UIHandlers::CheckBox, "Simulate in editor", diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 0671e3e77e..3cb931d148 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -194,7 +194,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/PhysXCollider.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "http://docs.aws.amazon.com/console/lumberyard/component/physx/collider") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/physx-collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) diff --git a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp index 770200cda0..b783ea3185 100644 --- a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp @@ -176,7 +176,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/ForceRegion.png") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/ForceRegion.png") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/console/lumberyard/physx/force-region") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/physx-force-region/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC("PhysXTriggerService", 0x3a117d7b)) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport") diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index f68c4d17d8..588b1d918f 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -309,7 +309,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/PhysXRigidBody.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/console/lumberyard/components/physx/rigid-body") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/physx-rigid-body-physics/") ->DataElement(0, &EditorRigidBodyComponent::m_config, "Configuration", "Configuration for rigid body physics.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRigidBodyComponent::CreateEditorWorldRigidBody) diff --git a/Gems/PhysXSamples/Assets/ScriptCanvas/Weapons/Revolver/Tracer_FX.scriptcanvas b/Gems/PhysXSamples/Assets/ScriptCanvas/Weapons/Revolver/Tracer_FX.scriptcanvas index eebc79585d..f0fff5e69f 100644 --- a/Gems/PhysXSamples/Assets/ScriptCanvas/Weapons/Revolver/Tracer_FX.scriptcanvas +++ b/Gems/PhysXSamples/Assets/ScriptCanvas/Weapons/Revolver/Tracer_FX.scriptcanvas @@ -3,12 +3,12 @@ - + - + - - + + @@ -16,496 +16,19 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + @@ -539,8 +62,11 @@ + - + + + @@ -574,8 +100,11 @@ + - + + + @@ -609,8 +138,11 @@ + - + + + @@ -644,8 +176,11 @@ + - + + + @@ -679,8 +214,11 @@ + - + + + @@ -714,11 +252,11 @@ + - - + @@ -760,1832 +298,26 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + + + - + @@ -2617,10 +349,13 @@ + - + + + - + @@ -2652,10 +387,13 @@ + - + + + - + @@ -2670,7 +408,7 @@ - + @@ -2692,10 +430,13 @@ + - + + + - + @@ -2705,10 +446,10 @@ - + - + @@ -2727,57 +468,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + - + - - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + - + @@ -2809,10 +896,13 @@ + - + + + - + @@ -2844,10 +934,13 @@ + - + + + - + @@ -2879,10 +972,13 @@ + - + + + - + @@ -2914,10 +1010,13 @@ + - + + + - + @@ -2949,10 +1048,13 @@ + - + + + - + @@ -2984,22 +1086,22 @@ + - - + - + - + - + @@ -3009,7 +1111,7 @@ - + @@ -3019,7 +1121,7 @@ - + @@ -3030,986 +1132,24 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + @@ -4043,8 +1183,11 @@ + - + + + @@ -4078,8 +1221,11 @@ + - + + + @@ -4113,8 +1259,11 @@ + - + + + @@ -4148,8 +1297,11 @@ + - + + + @@ -4183,8 +1335,11 @@ + - + + + @@ -4218,11 +1373,11 @@ + - - + @@ -4264,24 +1419,26 @@ - + - + - + - + - + - + + + - + @@ -4318,10 +1475,13 @@ + - + + + - + @@ -4358,10 +1518,56 @@ + - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4393,10 +1599,13 @@ + - + + + - + @@ -4428,6 +1637,7 @@ + @@ -4453,42 +1663,366 @@ - + - + + + + + + + + + + + + + - - + - - + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + @@ -4522,8 +2056,11 @@ + - + + + @@ -4557,8 +2094,11 @@ + - + + + @@ -4592,8 +2132,11 @@ + - + + + @@ -4627,8 +2170,11 @@ + - + + + @@ -4662,8 +2208,11 @@ + - + + + @@ -4714,8 +2263,11 @@ + - + + + @@ -4749,8 +2301,11 @@ + - + + + @@ -4775,7 +2330,7 @@ - + @@ -4784,8 +2339,11 @@ + - + + + @@ -4819,8 +2377,11 @@ + - + + + @@ -4845,7 +2406,7 @@ - + @@ -4854,6 +2415,7 @@ + @@ -4872,8 +2434,7 @@ - - + @@ -4932,151 +2493,26 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + + + - + @@ -5108,10 +2544,13 @@ + - + + + - + @@ -5143,10 +2582,13 @@ + - + + + - + @@ -5178,10 +2620,13 @@ + - + + + - + @@ -5213,10 +2658,13 @@ + - + + + - + @@ -5248,10 +2696,13 @@ + - + + + - + @@ -5283,22 +2734,22 @@ + - - + - + - + @@ -5308,7 +2759,7 @@ - + @@ -5318,7 +2769,7 @@ - + @@ -5329,22 +2780,590 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + @@ -5378,8 +3397,11 @@ + - + + + @@ -5413,8 +3435,11 @@ + - + + + @@ -5448,8 +3473,11 @@ + - + + + @@ -5483,8 +3511,11 @@ + - + + + @@ -5518,8 +3549,11 @@ + - + + + @@ -5553,11 +3587,11 @@ + - - + @@ -5599,868 +3633,24 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + @@ -6494,8 +3684,11 @@ + - + + + @@ -6529,8 +3722,11 @@ + - + + + @@ -6564,8 +3760,11 @@ + - + + + @@ -6599,8 +3798,11 @@ + - + + + @@ -6634,8 +3836,11 @@ + - + + + @@ -6669,11 +3874,11 @@ + - - + @@ -6715,24 +3920,1295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + + + - + @@ -6764,10 +5240,13 @@ + - + + + - + @@ -6799,10 +5278,13 @@ + - + + + - + @@ -6834,10 +5316,13 @@ + - + + + - + @@ -6869,10 +5354,13 @@ + - + + + - + @@ -6904,10 +5392,13 @@ + - + + + - + @@ -6939,22 +5430,22 @@ + - - + - + - + - + @@ -6964,7 +5455,7 @@ - + @@ -6974,7 +5465,7 @@ - + @@ -6985,22 +5476,1868 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + @@ -7039,8 +7376,11 @@ + - + + + @@ -7086,8 +7426,11 @@ + - + + + @@ -7121,8 +7464,11 @@ + - + + + @@ -7156,6 +7502,7 @@ + @@ -7186,8 +7533,7 @@ - - + @@ -7201,24 +7547,26 @@ - + - + - + - - + + - + - + + + - + @@ -7229,7 +7577,7 @@ - + @@ -7250,10 +7598,13 @@ + - + + + - + @@ -7264,7 +7615,7 @@ - + @@ -7285,10 +7636,13 @@ + - + + + - + @@ -7297,23 +7651,18 @@ - - - - - - + - + - + @@ -7325,10 +7674,117 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7360,10 +7816,13 @@ + - + + + - + @@ -7395,10 +7854,13 @@ + - + + + - + @@ -7430,10 +7892,13 @@ + - + + + - + @@ -7465,38 +7930,22 @@ + - - - - - - - - - - - - - - - - + + - - - - + - + - + @@ -7506,7 +7955,7 @@ - + @@ -7516,7 +7965,7 @@ - + @@ -7527,24 +7976,26 @@ - + - + - + - - + + - + - + + + - + @@ -7555,7 +8006,7 @@ - + @@ -7576,10 +8027,13 @@ + - + + + - + @@ -7590,7 +8044,7 @@ - + @@ -7611,50 +8065,13 @@ + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7686,10 +8103,13 @@ + - + + + - + @@ -7721,10 +8141,13 @@ + - + + + - + @@ -7756,10 +8179,13 @@ + - + + + - + @@ -7791,38 +8217,22 @@ + - - - - - - - - - - - - - - - - + + - - - - + - + - + @@ -7832,7 +8242,7 @@ - + @@ -7842,7 +8252,7 @@ - + @@ -7853,24 +8263,168 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + - + @@ -7907,10 +8461,13 @@ + - + + + - + @@ -7919,6 +8476,13 @@ + + + + + + + @@ -7947,50 +8511,13 @@ + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -8022,10 +8549,13 @@ + - + + + - + @@ -8057,6 +8587,7 @@ + @@ -8084,235 +8615,13 @@ - - - - - - - - - - - - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -8323,24 +8632,802 @@ - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -8372,165 +9459,58 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - + - + + + + + + + + + + + + + - - + - - - - - - - - - - + - + - + - + - + + + @@ -8564,8 +9544,11 @@ + - + + + @@ -8599,8 +9582,11 @@ + - + + + @@ -8634,8 +9620,11 @@ + - + + + @@ -8669,8 +9658,11 @@ + - + + + @@ -8704,8 +9696,11 @@ + - + + + @@ -8739,11 +9734,11 @@ + - - + @@ -8785,24 +9780,337 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + - + @@ -8834,10 +10142,13 @@ + - + + + - + @@ -8869,10 +10180,13 @@ + - + + + - + @@ -8887,7 +10201,7 @@ - + @@ -8909,10 +10223,13 @@ + - + + + - + @@ -8922,10 +10239,10 @@ - + - + @@ -8944,53 +10261,546 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + @@ -9029,8 +10839,11 @@ + - + + + @@ -9069,8 +10882,11 @@ + - + + + @@ -9104,8 +10920,11 @@ + - + + + @@ -9139,6 +10958,7 @@ + @@ -9164,13 +10984,12 @@ - + - - + @@ -9184,196 +11003,686 @@ - + - + - + - - - - - - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -9381,210 +11690,20 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + + + - + + + @@ -9618,8 +11737,11 @@ + - + + + @@ -9653,8 +11775,11 @@ + - + + + @@ -9688,8 +11813,11 @@ + - + + + @@ -9723,8 +11851,11 @@ + - + + + @@ -9758,8 +11889,11 @@ + - + + + @@ -9793,8 +11927,11 @@ + - + + + @@ -9819,7 +11956,7 @@ - + @@ -9828,8 +11965,11 @@ + - + + + @@ -9863,8 +12003,11 @@ + - + + + @@ -9889,7 +12032,7 @@ - + @@ -9898,8 +12041,11 @@ + - + + + @@ -9933,8 +12079,11 @@ + - + + + @@ -9968,8 +12117,11 @@ + - + + + @@ -10003,8 +12155,11 @@ + - + + + @@ -10038,8 +12193,11 @@ + - + + + @@ -10073,8 +12231,11 @@ + - + + + @@ -10108,8 +12269,11 @@ + - + + + @@ -10143,8 +12307,11 @@ + - + + + @@ -10169,7 +12336,7 @@ - + @@ -10178,11 +12345,11 @@ + - - + @@ -10282,33 +12449,15 @@ - + - + - + - - - - - - - - - - - - - - - - - - - + @@ -10342,15 +12491,33 @@ - + - + - + - + + + + + + + + + + + + + + + + + + + @@ -10358,32 +12525,34 @@ - + - + - + - + - + - - + + - + - + + + - + @@ -10393,45 +12562,10 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -10439,21 +12573,24 @@ - + - + + - + + + - + @@ -10468,7 +12605,7 @@ - + @@ -10490,10 +12627,13 @@ + - + + + - + @@ -10503,32 +12643,35 @@ - + - + - - + + - + + - + + + - + @@ -10538,10 +12681,10 @@ - + - + @@ -10549,165 +12692,68 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + + + - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - + - + - + - + + + @@ -10741,8 +12787,11 @@ + - + + + @@ -10776,8 +12825,11 @@ + - + + + @@ -10811,11 +12863,11 @@ + - - + @@ -10826,2326 +12878,24 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + + + @@ -13179,8 +12929,11 @@ + - + + + @@ -13214,8 +12967,11 @@ + - + + + @@ -13249,11 +13005,11 @@ + - - + @@ -13264,24 +13020,24 @@ - + - + - + - + @@ -13289,7 +13045,7 @@ - + @@ -13302,17 +13058,17 @@ - + - + - + @@ -13320,7 +13076,7 @@ - + @@ -13333,17 +13089,17 @@ - + - + - + @@ -13351,7 +13107,7 @@ - + @@ -13364,17 +13120,17 @@ - + - + - + @@ -13382,7 +13138,7 @@ - + @@ -13395,17 +13151,17 @@ - + - + - + @@ -13413,7 +13169,7 @@ - + @@ -13426,17 +13182,17 @@ - + - + - + @@ -13444,7 +13200,7 @@ - + @@ -13457,17 +13213,17 @@ - + - + - + @@ -13475,7 +13231,7 @@ - + @@ -13488,17 +13244,17 @@ - + - + - + @@ -13506,7 +13262,7 @@ - + @@ -13519,17 +13275,17 @@ - + - + - + @@ -13537,7 +13293,7 @@ - + @@ -13550,17 +13306,17 @@ - + - + - + @@ -13568,7 +13324,7 @@ - + @@ -13581,17 +13337,17 @@ - + - + - + @@ -13599,7 +13355,7 @@ - + @@ -13612,17 +13368,17 @@ - + - + - + @@ -13630,7 +13386,7 @@ - + @@ -13643,17 +13399,17 @@ - + - + - + @@ -13661,7 +13417,7 @@ - + @@ -13674,17 +13430,17 @@ - + - + - + @@ -13692,7 +13448,7 @@ - + @@ -13705,17 +13461,17 @@ - + - + - + @@ -13723,7 +13479,7 @@ - + @@ -13736,17 +13492,17 @@ - + - + - + @@ -13754,7 +13510,7 @@ - + @@ -13767,17 +13523,17 @@ - + - + - + @@ -13785,7 +13541,7 @@ - + @@ -13798,17 +13554,17 @@ - + - + - + @@ -13816,7 +13572,7 @@ - + @@ -13829,17 +13585,17 @@ - + - + - + @@ -13847,7 +13603,7 @@ - + @@ -13860,48 +13616,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -13909,7 +13634,7 @@ - + @@ -13922,110 +13647,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -14033,7 +13665,7 @@ - + @@ -14046,110 +13678,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -14157,7 +13696,7 @@ - + @@ -14170,17 +13709,17 @@ - + - + - + @@ -14188,7 +13727,7 @@ - + @@ -14201,48 +13740,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -14250,7 +13758,7 @@ - + @@ -14263,48 +13771,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -14312,7 +13789,7 @@ - + @@ -14325,17 +13802,17 @@ - + - + - + @@ -14343,7 +13820,7 @@ - + @@ -14356,17 +13833,17 @@ - + - + - + @@ -14374,7 +13851,7 @@ - + @@ -14387,17 +13864,17 @@ - + - + - + @@ -14405,7 +13882,7 @@ - + @@ -14418,172 +13895,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -14591,7 +13913,7 @@ - + @@ -14604,17 +13926,17 @@ - + - + - + @@ -14622,7 +13944,7 @@ - + @@ -14635,17 +13957,17 @@ - + - + - + @@ -14653,7 +13975,7 @@ - + @@ -14666,17 +13988,17 @@ - + - + - + @@ -14684,7 +14006,7 @@ - + @@ -14697,17 +14019,17 @@ - + - + - + @@ -14715,7 +14037,7 @@ - + @@ -14728,17 +14050,17 @@ - + - + - + @@ -14746,7 +14068,7 @@ - + @@ -14759,79 +14081,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -14839,7 +14099,7 @@ - + @@ -14852,17 +14112,17 @@ - + - + - + @@ -14870,7 +14130,7 @@ - + @@ -14883,17 +14143,17 @@ - + - + - + @@ -14901,7 +14161,7 @@ - + @@ -14914,79 +14174,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -14994,7 +14192,7 @@ - + @@ -15007,79 +14205,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -15087,7 +14223,7 @@ - + @@ -15100,17 +14236,17 @@ - + - + - + @@ -15118,7 +14254,7 @@ - + @@ -15131,17 +14267,17 @@ - + - + - + @@ -15149,7 +14285,7 @@ - + @@ -15162,17 +14298,17 @@ - + - + - + @@ -15180,7 +14316,7 @@ - + @@ -15193,17 +14329,17 @@ - + - + - + @@ -15211,7 +14347,7 @@ - + @@ -15224,17 +14360,17 @@ - + - + - + @@ -15242,7 +14378,7 @@ - + @@ -15255,17 +14391,17 @@ - + - + - + @@ -15273,7 +14409,7 @@ - + @@ -15286,17 +14422,17 @@ - + - + - + @@ -15304,7 +14440,7 @@ - + @@ -15317,17 +14453,17 @@ - + - + - + @@ -15335,7 +14471,7 @@ - + @@ -15348,17 +14484,17 @@ - + - + - + @@ -15366,7 +14502,7 @@ - + @@ -15379,17 +14515,17 @@ - + - + - + @@ -15397,7 +14533,7 @@ - + @@ -15410,17 +14546,17 @@ - + - + - + @@ -15428,7 +14564,7 @@ - + @@ -15441,17 +14577,17 @@ - + - + - + @@ -15459,7 +14595,7 @@ - + @@ -15472,17 +14608,17 @@ - + - + - + @@ -15490,7 +14626,7 @@ - + @@ -15503,17 +14639,17 @@ - + - + - + @@ -15521,7 +14657,7 @@ - + @@ -15534,17 +14670,17 @@ - + - + - + @@ -15552,7 +14688,7 @@ - + @@ -15565,48 +14701,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -15614,7 +14719,7 @@ - + @@ -15627,17 +14732,17 @@ - + - + - + @@ -15645,7 +14750,7 @@ - + @@ -15658,141 +14763,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -15800,7 +14781,7 @@ - + @@ -15813,17 +14794,17 @@ - + - + - + @@ -15831,7 +14812,7 @@ - + @@ -15842,24 +14823,495 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - + @@ -15867,7 +15319,7 @@ - + @@ -15887,57 +15339,21 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -15945,7 +15361,7 @@ - + @@ -15965,7 +15381,13 @@ - + + + + + + + @@ -15973,7 +15395,93 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -16009,91 +15517,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -16101,1004 +15525,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -17109,10 +15536,22 @@ - - + + - + + + + + + + + + + + + + @@ -17120,7 +15559,7 @@ - + @@ -17128,7 +15567,7 @@ - + @@ -17148,7 +15587,7 @@ - + @@ -17156,7 +15595,7 @@ - + @@ -17168,6 +15607,13 @@ + + + + + + + @@ -17209,9 +15655,9 @@ - - - + + + @@ -17221,49 +15667,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -17271,91 +15675,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -17366,10 +15686,16 @@ - - + + - + + + + + + + @@ -17377,125 +15703,83 @@ - + - - - - - - - - - - - - - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - + + - + - + - - + + - + + + + + + + + + + + + + @@ -17503,7 +15787,121 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -17539,7 +15937,7 @@ - + @@ -17547,77 +15945,41 @@ - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -17625,7 +15987,127 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -17645,7 +16127,7 @@ - + @@ -17653,7 +16135,475 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -17689,27 +16639,15 @@ - + - - - - - - - - - - - - - - + + - + @@ -17720,10 +16658,16 @@ - - + + - + + + + + + + @@ -17731,7 +16675,372 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -17767,49 +17076,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -17817,63 +17084,27 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -17894,19 +17125,15 @@ - + + + + + - - - - - - - - - + @@ -17914,23 +17141,47 @@ - + - + + + + + - + + + + + - - + + - + + + + + + + + + + + + + + + + + @@ -17941,30 +17192,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -17974,19 +17201,23 @@ - + - + - + - + + + + + @@ -17994,39 +17225,27 @@ - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + @@ -18042,20 +17261,240 @@ - + - - - - + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -18068,25 +17507,27 @@ + + + + + + + + + + + + + - - - - - - - - - - - - + @@ -18101,224 +17542,20 @@ + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/Gems/ScriptCanvas/Code/Editor/Model/UnitTestBrowserFilterModel.cpp b/Gems/ScriptCanvas/Code/Editor/Model/UnitTestBrowserFilterModel.cpp index 665c81fa57..1a4d95c585 100644 --- a/Gems/ScriptCanvas/Code/Editor/Model/UnitTestBrowserFilterModel.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Model/UnitTestBrowserFilterModel.cpp @@ -34,7 +34,7 @@ namespace ScriptCanvasEditor { setDynamicSortFilter(true); - m_showColumn.insert(AssetBrowserModel::m_column); + m_showColumn.insert(aznumeric_cast(AssetBrowserEntry::Column::DisplayName)); UnitTestWidgetNotificationBus::Handler::BusConnect(); diff --git a/README.md b/README.md index 0c59837a62..7a9751529f 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ If you have the Git credential manager core or other credential helpers installe * Game Development with C++ * MSVC v142 - VS 2019 C++ x64/x86 * C++ 2019 redistributable update -* CMake 3.19.1 minimum: [https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi](https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi) +* CMake 3.20 minimum: [https://cmake.org/download/](https://cmake.org/download/) #### Optional @@ -105,7 +105,7 @@ If you have the Git credential manager core or other credential helpers installe 1. Install the following redistributables to the following: - Visual Studio and VC++ redistributable can be installed to any location - - CMake can be installed to any location, as long as it's available in the system path, otherwise it can be installed to: `<3rdParty Path>\CMake\3.19.1` + - CMake can be installed to any location, as long as it's available in the system path - WWise can be installed anywhere, but you will need to set an environment variable for CMake to detect it: `set LY_WWISE_INSTALL_PATH=` 1. Navigate into the repo folder, then download the python runtime with this command diff --git a/Registry/setregbuilder.assetprocessor.setreg b/Registry/setregbuilder.assetprocessor.setreg index 4be46a9d51..5dc6f42446 100644 --- a/Registry/setregbuilder.assetprocessor.setreg +++ b/Registry/setregbuilder.assetprocessor.setreg @@ -22,7 +22,8 @@ // members or entries will be recursively ignored as well. "Excludes": [ - "/Amazon/AzCore/Runtime" + "/Amazon/AzCore/Runtime", + "/Amazon/AzCore/Bootstrap/project_path" ] } } diff --git a/Templates/DefaultProject/Template/CMakeLists.txt b/Templates/DefaultProject/Template/CMakeLists.txt index 50e3a528e6..4dcfc5325b 100644 --- a/Templates/DefaultProject/Template/CMakeLists.txt +++ b/Templates/DefaultProject/Template/CMakeLists.txt @@ -12,7 +12,7 @@ # {END_LICENSE} if(NOT PROJECT_NAME) - cmake_minimum_required(VERSION 3.19) + cmake_minimum_required(VERSION 3.20) project(${Name} LANGUAGES C CXX VERSION 1.0.0.0 diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index f85048d13e..3908d21ecf 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -26,7 +26,7 @@ ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) +ly_associate_package(PACKAGE_NAME azslc-1.7.22-rev1-multiplatform TARGETS azslc PACKAGE_HASH 71b4545d221d4fcd564ccc121c249a8f8f164bcc616faf146f926c3d5c78d527) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 1a2cfa4049..19e71f726c 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -26,7 +26,7 @@ ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) +ly_associate_package(PACKAGE_NAME azslc-1.7.22-rev1-multiplatform TARGETS azslc PACKAGE_HASH 71b4545d221d4fcd564ccc121c249a8f8f164bcc616faf146f926c3d5c78d527) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) diff --git a/cmake/Platform/Android/PAL_android.cmake b/cmake/Platform/Android/PAL_android.cmake index 5f35767f98..dd61e35e53 100644 --- a/cmake/Platform/Android/PAL_android.cmake +++ b/cmake/Platform/Android/PAL_android.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED FALSE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED FALSE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Platform/Linux/PAL_linux.cmake b/cmake/Platform/Linux/PAL_linux.cmake index 40b73adcec..c15f2bada9 100644 --- a/cmake/Platform/Linux/PAL_linux.cmake +++ b/cmake/Platform/Linux/PAL_linux.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED FALSE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED TRUE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Platform/Mac/PAL_mac.cmake b/cmake/Platform/Mac/PAL_mac.cmake index 988d36af14..f49578a83a 100644 --- a/cmake/Platform/Mac/PAL_mac.cmake +++ b/cmake/Platform/Mac/PAL_mac.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED FALSE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED TRUE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Platform/Windows/PAL_windows.cmake b/cmake/Platform/Windows/PAL_windows.cmake index ddcc3a27c7..fbf65db63f 100644 --- a/cmake/Platform/Windows/PAL_windows.cmake +++ b/cmake/Platform/Windows/PAL_windows.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED TRUE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED TRUE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED TRUE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Platform/Windows/Packaging/Bootstrapper.wxs b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs index 55e8a8cd95..fd8aa68b77 100644 --- a/cmake/Platform/Windows/Packaging/Bootstrapper.wxs +++ b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs @@ -22,6 +22,8 @@ @@ -29,6 +31,8 @@ diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in new file mode 100644 index 0000000000..fe125cfdfe --- /dev/null +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in @@ -0,0 +1,70 @@ + + + + + [WixBundleName] Setup + [WixBundleName] + Version [WixBundleVersion] + Are you sure you want to cancel? + + + Welcome + +Setup will install [WixBundleName] on your computer. Click install to continue, options to set the install directory or Close to exit. + + [WixBundleName] <a href="#">license terms</a>. + I &agree to the license terms and conditions + &Options + &Install + &Close + + + Setup Options + Install location: + &Browse + &OK + &Cancel + + + Modify Setup + &Repair + &Uninstall + &Close + + + Setup Progress + Processing: + Initializing... + &Cancel + + + Setup Successful + Installation Successfully Completed + Repair Successfully Completed + Uninstall Successfully Completed + &Launch + &Close + + + Setup Failed + Setup Failed + Uninstall Failed + Repair Failed + +One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. + + &Close + + + Setup Help + +/install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or creates a complete local copy of the bundle in directory. Install is the default. + +/passive | /quiet - displays minimal UI with no prompts or displays no UI and no prompts. By default UI and all prompts are displayed. + +/log log.txt - logs to a specific file. By default a log file is created in %TEMP%. + + &Close + diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in new file mode 100644 index 0000000000..61b338cc13 --- /dev/null +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in @@ -0,0 +1,87 @@ + + + + #(loc.WindowTitle) + + Segoe UI + Segoe UI + Segoe UI + Segoe UI + + + + #(loc.Title) + + + + @WIX_THEME_INSTALL_LICENSE_ELEMENT@ + + #(loc.InstallAcceptCheckbox) + + + + + + + + #(loc.OptionsHeader) + + #(loc.OptionsLocationLabel) + + + + + + + + + + #(loc.ModifyHeader) + + + + + + + + + #(loc.ProgressHeader) + + #(loc.ProgressLabel) + #(loc.OverallProgressPackageText) + + + + + + + + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) + + + + + + + + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) + + #(loc.FailureHyperlinkLogText) + + + + + + + + #(loc.HelpHeader) + #(loc.HelpText) + + + diff --git a/cmake/Platform/Windows/PackagingPostBuild.cmake b/cmake/Platform/Windows/PackagingPostBuild.cmake index 89b3efb44b..1dcbedcba7 100644 --- a/cmake/Platform/Windows/PackagingPostBuild.cmake +++ b/cmake/Platform/Windows/PackagingPostBuild.cmake @@ -25,6 +25,7 @@ set(_ext_flags ) set(_addtional_defines + -dCPACK_BOOTSTRAP_THEME_FILE=${CPACK_BINARY_DIR}/BootstrapperTheme -dCPACK_BOOTSTRAP_UPGRADE_GUID=${CPACK_WIX_BOOTSTRAP_UPGRADE_GUID} -dCPACK_DOWNLOAD_SITE=${CPACK_DOWNLOAD_SITE} -dCPACK_LOCAL_INSTALLER_DIR=${_cpack_wix_out_dir} diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 2fd281ad51..aec0edeee2 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -92,6 +92,28 @@ set(CPACK_WIX_EXTENSIONS set(_embed_artifacts "yes") if(LY_INSTALLER_DOWNLOAD_URL) + + if(LY_INSTALLER_LICENSE_URL) + set(WIX_THEME_INSTALL_LICENSE_ELEMENT + "#(loc.InstallLicenseLinkText)" + ) + else() + set(WIX_THEME_INSTALL_LICENSE_ELEMENT + "" + ) + endif() + + configure_file( + "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/BootstrapperTheme.xml.in" + "${CPACK_BINARY_DIR}/BootstrapperTheme.xml" + @ONLY + ) + configure_file( + "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/BootstrapperTheme.wxl.in" + "${CPACK_BINARY_DIR}/BootstrapperTheme.wxl" + @ONLY + ) + set(_embed_artifacts "no") # the bootstrapper will at the very least need a different upgrade guid diff --git a/cmake/Platform/Windows/platform_windows_files.cmake b/cmake/Platform/Windows/platform_windows_files.cmake index 3ce53fbcea..b3cdc8a4ff 100644 --- a/cmake/Platform/Windows/platform_windows_files.cmake +++ b/cmake/Platform/Windows/platform_windows_files.cmake @@ -26,6 +26,8 @@ set(FILES Packaging_windows.cmake PackagingPostBuild.cmake Packaging/Bootstrapper.wxs + Packaging/BootstrapperTheme.wxl.in + Packaging/BootstrapperTheme.xml.in Packaging/Shortcuts.wxs Packaging/Template.wxs.in ) diff --git a/cmake/Platform/iOS/PAL_ios.cmake b/cmake/Platform/iOS/PAL_ios.cmake index a6828a1bc7..981bb9cab1 100644 --- a/cmake/Platform/iOS/PAL_ios.cmake +++ b/cmake/Platform/iOS/PAL_ios.cmake @@ -24,6 +24,7 @@ ly_set(PAL_TRAIT_BUILD_CPACK_SUPPORTED FALSE) # Test library support ly_set(PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_GOOGLE_BENCHMARK_SUPPORTED FALSE) +ly_set(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_PYTEST_SUPPORTED FALSE) ly_set(PAL_TRAIT_TEST_TARGET_TYPE MODULE) diff --git a/cmake/Tools/Platform/Android/android_deployment.py b/cmake/Tools/Platform/Android/android_deployment.py index eb8361ec18..82d14a2746 100755 --- a/cmake/Tools/Platform/Android/android_deployment.py +++ b/cmake/Tools/Platform/Android/android_deployment.py @@ -184,11 +184,15 @@ class AndroidDeployment(object): call_arguments.extend(arg_list) - output = subprocess.check_output(call_arguments, - shell=True, - stderr=subprocess.DEVNULL).decode(common.DEFAULT_TEXT_READ_ENCODING, - common.ENCODING_ERROR_HANDLINGS) - return output + try: + output = subprocess.check_output(call_arguments, + shell=True, + stderr=subprocess.PIPE).decode(common.DEFAULT_TEXT_READ_ENCODING, + common.ENCODING_ERROR_HANDLINGS) + return output + except subprocess.CalledProcessError as err: + raise common.LmbrCmdError(err.stderr.decode(common.DEFAULT_TEXT_READ_ENCODING, + common.ENCODING_ERROR_HANDLINGS)) def adb_shell(self, command, device_id): """ diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index d25b62dde8..5a52ac385e 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -48,7 +48,7 @@ def verify_gradle(override_gradle_path=None): CMAKE_ARGUMENT_NAME = '--cmake-install-path' -CMAKE_MIN_VERSION = LooseVersion('3.19.0') +CMAKE_MIN_VERSION = LooseVersion('3.20.0') CMAKE_VERSION_REGEX = re.compile(r'cmake version (\d+.\d+.?\d*)') CMAKE_EXECUTABLE = 'cmake' diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index 1990041c86..cf4631c227 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -274,6 +274,8 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too :return: Tuple of the resolved tool version and the resolved override tool path if provided """ + tool_source = tool_name + try: # Use either the provided gradle override or the gradle in the path environment if override_tool_path: @@ -306,18 +308,17 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too tool_desc = f"{tool_name} path provided in the command line argument '{argument_name}={override_tool_path}' " else: resolved_override_tool_path = None - tool_source = tool_name tool_desc = f"installed {tool_name} in the system path" # Extract the version and verify version_output = subprocess.check_output([tool_source, tool_version_argument], - shell=True).decode(DEFAULT_TEXT_READ_ENCODING, - ENCODING_ERROR_HANDLINGS) + shell=True, + stderr=subprocess.PIPE).decode(DEFAULT_TEXT_READ_ENCODING, + ENCODING_ERROR_HANDLINGS) version_match = tool_version_regex.search(version_output) if not version_match: raise RuntimeError() - # Since we are doing a compare, strip out any non-numeric and non . character from the version otherwise we will get a TypeError on the LooseVersion comparison result_version_str = re.sub(r"[^\.0-9]", "", str(version_match.group(1)).strip()) result_version = LooseVersion(result_version_str) @@ -331,7 +332,14 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too return result_version, resolved_override_tool_path - except (CalledProcessError, WindowsError, RuntimeError) as e: + except CalledProcessError as e: + error_msg = e.output.decode(DEFAULT_TEXT_READ_ENCODING, + ENCODING_ERROR_HANDLINGS) + raise LmbrCmdError(f"{tool_name} cannot be resolved or there was a problem determining its version number. " + f"Either make sure its in the system path environment or a valid path is passed in " + f"through the {argument_name} argument.\n{error_msg}", + ERROR_CODE_ERROR_NOT_SUPPORTED) + except (WindowsError, RuntimeError) as e: logging.error(f"Call to '{tool_source}' resulted in error: {e}") raise LmbrCmdError(f"{tool_name} cannot be resolved or there was a problem determining its version number. " f"Either make sure its in the system path environment or a valid path is passed in " diff --git a/python/get_python.bat b/python/get_python.bat index e11c4ab92f..b3c3bf9cfb 100644 --- a/python/get_python.bat +++ b/python/get_python.bat @@ -32,18 +32,15 @@ IF !ERRORLEVEL!==0 ( cd /D %CMD_DIR%\.. REM IF you update this logic, update it in scripts/build/Platform/Windows/env_windows.cmd -REM If cmake is not found on path, try a known location, using LY_CMAKE_PATH as the first fallback +REM If cmake is not found on path, try a known location at LY_CMAKE_PATH where /Q cmake IF NOT !ERRORLEVEL!==0 ( IF "%LY_CMAKE_PATH%"=="" ( - IF "%LY_3RDPARTY_PATH%"=="" ( - ECHO ERROR: CMake was not found on the PATH and LY_3RDPARTY_PATH is not defined. - ECHO Please ensure CMake is on the path or set LY_3RDPARTY_PATH or LY_CMAKE_PATH. - EXIT /b 1 - ) - SET LY_CMAKE_PATH=!LY_3RDPARTY_PATH!\CMake\3.19.1\Windows\bin - echo CMake was not found on the path, will use known location: !LY_CMAKE_PATH! + ECHO ERROR: CMake was not found on the PATH and LY_CMAKE_PATH is not defined. + ECHO Please ensure CMake is on the path or set LY_CMAKE_PATH. + EXIT /b 1 ) + PATH !LY_CMAKE_PATH!;!PATH! where /Q cmake if NOT !ERRORLEVEL!==0 ( diff --git a/python/get_python.cmake b/python/get_python.cmake index 5d6bf6fd96..198d3f0ba3 100644 --- a/python/get_python.cmake +++ b/python/get_python.cmake @@ -16,7 +16,7 @@ # example: # cmake -DPAL_PLATFORM_NAME:string=Windows -DLY_3RDPARTY_PATH:string=%CMD_DIR% -P get_python.cmake -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) if(LY_3RDPARTY_PATH) file(TO_CMAKE_PATH ${LY_3RDPARTY_PATH} LY_3RDPARTY_PATH) diff --git a/python/get_python.sh b/python/get_python.sh index 780f1bbdd8..9b774d91fc 100755 --- a/python/get_python.sh +++ b/python/get_python.sh @@ -42,31 +42,21 @@ then CMAKE_FOLDER_RELATIVE_TO_ROOT=CMake.app/Contents/bin else PAL=Linux - CMAKE_FOLDER_RELATIVE_TO_ROOT=bin + CMAKE_FOLDER_RELATIVE_TO_ROOT=bin fi if ! [ -x "$(command -v cmake)" ]; then - # Note that LY_3RDPARTY_PATH is only required here if you have no cmake in your PATH. if [ -z ${LY_CMAKE_PATH} ]; then - if [ -z ${LY_3RDPARTY_PATH} ]; then - echo "ERROR: Could not find cmake on the PATH and LY_3RDPARTY_PATH is not defined, cannot continue." - echo "Please add cmake to your PATH, or define $LY_3RDPARTY_PATH" - exit 1 - fi - LY_CMAKE_PATH=$LY_3RDPARTY_PATH/CMake/3.19.1/$PAL/$CMAKE_FOLDER_RELATIVE_TO_ROOT - # if you change the version number, change it also in: - # scripts/build/Platform/Mac/env_mac.sh - # and - # scripts/build/Platform/Linux/env_linux.sh + echo "ERROR: Could not find cmake on the PATH and LY_CMAKE_PATH is not defined, cannot continue." + echo "Please add cmake to your PATH, or define LY_CMAKE_PATH" + exit 1 fi - + export PATH=$LY_CMAKE_PATH:$PATH if ! [ -x "$(command -v cmake)" ]; then - echo "ERROR: Could not find cmake on the PATH or at the known location: $CMAKE_KNOWN_LOCATION" + echo "ERROR: Could not find cmake on the PATH or at the known location: $LY_CMAKE_PATH" echo "Please add cmake to the environment PATH or place it at the above known location." exit 1 - else - echo "CMake not found on path, but was found in the known 3rd Party location." fi fi diff --git a/scripts/build/Platform/Android/build_and_run_unit_tests.cmd b/scripts/build/Platform/Android/build_and_run_unit_tests.cmd index fbb31f95ec..5a51621f40 100644 --- a/scripts/build/Platform/Android/build_and_run_unit_tests.cmd +++ b/scripts/build/Platform/Android/build_and_run_unit_tests.cmd @@ -20,9 +20,6 @@ IF NOT EXIST "%LY_3RDPARTY_PATH%" ( GOTO :error ) -IF NOT EXIST "%LY_ANDROID_SDK%" ( - SET LY_ANDROID_SDK=!LY_3RDPARTY_PATH!/android-sdk/platform-29 -) IF NOT EXIST "%LY_ANDROID_SDK%" ( ECHO [ci_build] FAIL: LY_ANDROID_SDK=!LY_ANDROID_SDK! GOTO :error diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index 660bc50da6..59b72ec4b0 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -83,7 +83,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" } }, "test_profile_nounity": { @@ -95,7 +95,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" } }, "asset_profile": { diff --git a/scripts/build/Platform/Linux/env_linux.sh b/scripts/build/Platform/Linux/env_linux.sh index 1d7324778c..85bdf0a745 100755 --- a/scripts/build/Platform/Linux/env_linux.sh +++ b/scripts/build/Platform/Linux/env_linux.sh @@ -13,27 +13,11 @@ set -o errexit # exit on the first failure encountered if ! command -v cmake &> /dev/null; then - if [[ -z $LY_CMAKE_PATH ]]; then LY_CMAKE_PATH=${LY_3RDPARTY_PATH}/CMake/3.19.1/Linux/bin; fi - if [[ ! -d $LY_CMAKE_PATH ]]; then - echo "[ci_build] CMake path not found" - exit 1 - fi - PATH=${LY_CMAKE_PATH}:${PATH} - if ! command -v cmake &> /dev/null; then - echo "[ci_build] CMake not found" - exit 1 - fi + echo "[ci_build] CMake not found" + exit 1 fi if ! command -v ninja &> /dev/null; then - if [[ -z $LY_NINJA_PATH ]]; then LY_NINJA_PATH=${LY_3RDPARTY_PATH}/ninja/1.10.1/Linux; fi - if [[ ! -d $LY_NINJA_PATH ]]; then - echo "[ci_build] Ninja path not found" - exit 1 - fi - PATH=${LY_NINJA_PATH}:${PATH} - if ! command -v ninja &> /dev/null; then - echo "[ci_build] Ninja not found" - exit 1 - fi + echo "[ci_build] Ninja not found" + exit 1 fi diff --git a/scripts/build/Platform/Mac/env_mac.sh b/scripts/build/Platform/Mac/env_mac.sh index 8f38d1d90b..917329b6af 100755 --- a/scripts/build/Platform/Mac/env_mac.sh +++ b/scripts/build/Platform/Mac/env_mac.sh @@ -13,14 +13,6 @@ set -o errexit # exit on the first failure encountered if ! command -v cmake &> /dev/null; then - if [[ -z $LY_CMAKE_PATH ]]; then LY_CMAKE_PATH=${LY_3RDPARTY_PATH}/CMake/3.19.1/Mac/CMake.app/Contents/bin; fi - if [[ ! -d $LY_CMAKE_PATH ]]; then - echo "[ci_build] CMake path not found" - exit 1 - fi - PATH=${LY_CMAKE_PATH}:${PATH} - if ! command -v cmake &> /dev/null; then - echo "[ci_build] CMake not found" - exit 1 - fi + echo "[ci_build] CMake not found" + exit 1 fi diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 71abf9021f..abf163a2e7 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -177,7 +177,8 @@ "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", "CTEST_OPTIONS": "-L \"(SUITE_smoke_REQUIRES_gpu|SUITE_main_REQUIRES_gpu)\" -T Test", "TEST_METRICS": "True", - "TEST_RESULTS": "True" + "TEST_RESULTS": "True", + "TEST_SCREENSHOTS": "True" } }, "asset_profile_vs2019": { diff --git a/scripts/build/Platform/Windows/build_ninja_windows.cmd b/scripts/build/Platform/Windows/build_ninja_windows.cmd index b782cc2357..e1a802e254 100644 --- a/scripts/build/Platform/Windows/build_ninja_windows.cmd +++ b/scripts/build/Platform/Windows/build_ninja_windows.cmd @@ -12,19 +12,10 @@ REM SETLOCAL EnableDelayedExpansion -IF NOT EXIST "%LY_NINJA_PATH%" ( - SET LY_NINJA_PATH=%LY_3RDPARTY_PATH%/ninja/1.10.1/Windows -) -IF NOT EXIST "%LY_NINJA_PATH%" ( - ECHO [ci_build] FAIL: LY_NINJA_PATH=%LY_NINJA_PATH% - GOTO :error -) -PATH %LY_NINJA_PATH%;%PATH% - CALL "%~dp0build_windows.cmd" IF NOT %ERRORLEVEL%==0 GOTO :error EXIT /b 0 :error -EXIT /b 1 \ No newline at end of file +EXIT /b 1 diff --git a/scripts/build/Platform/Windows/env_windows.cmd b/scripts/build/Platform/Windows/env_windows.cmd index 592b2867cd..3ef161c2ef 100644 --- a/scripts/build/Platform/Windows/env_windows.cmd +++ b/scripts/build/Platform/Windows/env_windows.cmd @@ -12,17 +12,8 @@ REM where /Q cmake IF NOT %ERRORLEVEL%==0 ( - IF "%LY_CMAKE_PATH%"=="" (SET LY_CMAKE_PATH=%LY_3RDPARTY_PATH%/CMake/3.19.1/Windows/bin) - IF NOT EXIST !LY_CMAKE_PATH! ( - ECHO [ci_build] CMake path not found - GOTO :error - ) - PATH !LY_CMAKE_PATH!;!PATH! - where /Q cmake - IF NOT !ERRORLEVEL!==0 ( - ECHO [ci_build] CMake not found - GOTO :error - ) + ECHO [ci_build] CMake not found + GOTO :error ) EXIT /b 0 diff --git a/scripts/build/tools/upload_to_s3.py b/scripts/build/tools/upload_to_s3.py index 5dfe5eb66e..5666e6ec6b 100755 --- a/scripts/build/tools/upload_to_s3.py +++ b/scripts/build/tools/upload_to_s3.py @@ -17,12 +17,16 @@ python upload_to_s3.py --base_dir %WORKSPACE% --file_regex "(.*zip$|.*MD5$)" --b Use profile to upload all .zip and .MD5 files in %WORKSPACE% folder to bucket ly-packages-mainline: python upload_to_s3.py --base_dir %WORKSPACE% --profile profile --file_regex "(.*zip$|.*MD5$)" --bucket ly-packages-mainline +Another example usage for uploading all .png and .ppm files inside base_dir and only subdirectories within base_dir: +python upload_to_s3.py --base_dir %WORKSPACE%/path/to/files --file_regex "(.*png$|.*ppm$)" --bucket screenshot-test-bucket --search_subdirectories True --key_prefix Test + ''' import os import re import json +import time import boto3 from optparse import OptionParser @@ -34,6 +38,8 @@ def parse_args(): parser.add_option("--profile", dest="profile", default=None, help="The name of a profile to use. If not given, then the default profile is used.") parser.add_option("--bucket", dest="bucket", default=None, help="S3 bucket the files are uploaded to.") parser.add_option("--key_prefix", dest="key_prefix", default='', help="Object key prefix.") + parser.add_option("--search_subdirectories", dest="search_subdirectories", action='store_true', + help="Toggle for searching for files in subdirectories beneath base_dir, defaults to False") ''' ExtraArgs used to call s3.upload_file(), should be in json format. extra_args key must be one of: ACL, CacheControl, ContentDisposition, ContentEncoding, ContentLanguage, ContentType, Expires, GrantFullControl, GrantRead, GrantReadACP, GrantWriteACP, Metadata, RequestPayer, ServerSideEncryption, StorageClass, @@ -62,48 +68,82 @@ def get_client(service_name, profile_name): return client -def get_files_to_upload(base_dir, regex): +def get_files_to_upload(base_dir, regex, search_subdirectories): + """ + Uses a regex expression pattern to return a list of file paths for files to upload to the s3 bucket. + :param base_dir: path for the base directory, if using search_subdirectories=True ensure this is the parent. + :param regex: pattern to use for regex searching, ex. "(.*zip$|.*MD5$)" + :param search_subdirectories: boolean False for only getting files in base_dir, True to get all files in base_dir + and any subdirectory inside base_dir, defaults to False from the parse_args() function. + :return: a list of string file paths for files to upload to the s3 bucket matching the regex expression. + """ # Get all file names in base directory - files = [x for x in os.listdir(base_dir) if os.path.isfile(os.path.join(base_dir, x))] - # strip the surround quotes, if they exist + files = [os.path.join(base_dir, x) for x in os.listdir(base_dir) if os.path.isfile(os.path.join(base_dir, x))] + if search_subdirectories: # Get all file names in base directory and any subdirectories. + for subdirectory in os.walk(base_dir): + # Example output for subdirectory: + # ('C:\path\to\base_dir\', ['Subfolder1', 'Subfolder2'], ['file1', 'file2']) + subdirectory_file_path = subdirectory[0] + subdirectory_files = subdirectory[2] + if subdirectory_files: + subdirectory_file_paths = _build_file_paths(subdirectory_file_path, subdirectory_files) + files.extend(subdirectory_file_paths) + try: - regex = json.loads(regex) + regex = json.loads(regex) # strip the surround quotes, if they exist except: + print(f'WARNING: failed to call json.loads() for regex: "{regex}"') pass # Get all file names matching the regular expression, those file will be uploaded to S3 - files_to_upload = [x for x in files if re.match(regex, x)] - return files_to_upload + regex_files_to_upload = [x for x in files if re.match(regex, x)] + + return regex_files_to_upload -def s3_upload_file(client, base_dir, file, bucket, key_prefix=None, extra_args=None, max_retry=1): - print(('Uploading file {} to bucket {}.'.format(file, bucket))) - key = file if key_prefix is None else '{}/{}'.format(key_prefix, file) +def s3_upload_file(client, file, bucket, key_prefix=None, extra_args=None, max_retry=1): + key = file if key_prefix is None else f'{key_prefix}/{file}' + error_message = None + for x in range(max_retry): try: - client.upload_file( - os.path.join(base_dir, file), bucket, key, - ExtraArgs=extra_args - ) - print('Upload succeeded') + client.upload_file(file, bucket, key, ExtraArgs=extra_args) return True except Exception as err: - print(('exception while uploading: {}'.format(err))) - print('Retrying upload...') - print('Upload failed') + time.sleep(0.1) # Sleep for 100 milliseconds between retries. + error_message = err + + print(f'Upload failed - Exception while uploading: {error_message}') return False +def _build_file_paths(path_to_files, files_in_path): + """ + Given a path containing files, returns a list of strings representing complete paths to each file. + :param path_to_files: path to the location storing the files to create string paths for + :param files_in_path: list of files that are inside the path_to_files path string + :return: list of fully parsed file path strings from path_to_files path. + """ + parsed_file_paths = [] + + for file_in_path in files_in_path: + complete_file_path = os.path.join(path_to_files, file_in_path) + if os.path.isfile(complete_file_path): + parsed_file_paths.append(complete_file_path) + + return parsed_file_paths + + if __name__ == "__main__": options = parse_args() client = get_client('s3', options.profile) - files_to_upload = get_files_to_upload(options.base_dir, options.file_regex) + files_to_upload = get_files_to_upload(options.base_dir, options.file_regex, options.search_subdirectories) extra_args = json.loads(options.extra_args) if options.extra_args else None print(('Uploading {} files to bucket {}.'.format(len(files_to_upload), options.bucket))) failure = [] success = [] for file in files_to_upload: - if not s3_upload_file(client, options.base_dir, file, options.bucket, options.key_prefix, extra_args, 2): + if not s3_upload_file(client, file, options.bucket, options.key_prefix, extra_args, 2): failure.append(file) else: success.append(file) diff --git a/scripts/ctest/CMakeLists.txt b/scripts/ctest/CMakeLists.txt index 575be53cc7..c07aaf4bff 100644 --- a/scripts/ctest/CMakeLists.txt +++ b/scripts/ctest/CMakeLists.txt @@ -20,20 +20,22 @@ endif() # Tests ################################################################################ -foreach(suite_name ${LY_TEST_GLOBAL_KNOWN_SUITE_NAMES}) - ly_add_pytest( - NAME pytest_sanity_${suite_name}_no_gpu - PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py - TEST_SUITE ${suite_name} - ) +if(PAL_TRAIT_TEST_LYTESTTOOLS_SUPPORTED) + foreach(suite_name ${LY_TEST_GLOBAL_KNOWN_SUITE_NAMES}) + ly_add_pytest( + NAME pytest_sanity_${suite_name}_no_gpu + PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py + TEST_SUITE ${suite_name} + ) - ly_add_pytest( - NAME pytest_sanity_${suite_name}_requires_gpu - PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py - TEST_SUITE ${suite_name} - TEST_REQUIRES gpu - ) -endforeach() + ly_add_pytest( + NAME pytest_sanity_${suite_name}_requires_gpu + PATH ${CMAKE_CURRENT_LIST_DIR}/sanity_test.py + TEST_SUITE ${suite_name} + TEST_REQUIRES gpu + ) + endforeach() +endif() # EPB Sanity test is being registered here to validate that the ly_add_editor_python_test function works. #if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS_TARGET_NAME)