diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py index e4575d4d17..5fa8130302 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py @@ -95,7 +95,8 @@ def EntityOutliner_EntityOrdering(): entity_outliner_model.dropMimeData( mime_data, QtCore.Qt.MoveAction, target_row, 0, target_index.parent() ) - QtWidgets.QApplication.processEvents() + # Wait after move to let events (i.e. prefab propagation) process + general.idle_wait(1.0) # Move an entity before another entity in the order by dragging the source above the target move_entity_before = lambda source_name, target_name: _move_entity( @@ -119,24 +120,24 @@ def EntityOutliner_EntityOrdering(): # Our new entity should be given a name with a number automatically new_entity = f"Entity{i+1}" - # The new entity should be added to the top of its parent entity - expected_order = [new_entity] + expected_order + # The new entity should be added to the bottom of its parent entity + expected_order = expected_order + [new_entity] verify_entities_sorted(expected_order) - # 3) Move "Entity1" to the top of the order - move_entity_before("Entity1", "Entity5") - expected_order = ["Entity1", "Entity5", "Entity4", "Entity3", "Entity2"] + # 3) Move "Entity5" to the top of the order + move_entity_before("Entity5", "Entity1") + expected_order = ["Entity5", "Entity1", "Entity2", "Entity3", "Entity4"] verify_entities_sorted(expected_order) - # 4) Move "Entity4" to the bottom of the order - move_entity_after("Entity4", "Entity2") - expected_order = ["Entity1", "Entity5", "Entity3", "Entity2", "Entity4"] + # 4) Move "Entity2" to the bottom of the order + move_entity_after("Entity2", "Entity4") + expected_order = ["Entity5", "Entity1", "Entity3", "Entity4", "Entity2"] verify_entities_sorted(expected_order) # 5) Add another new entity, ensure the rest of the order is unchanged create_entity() - expected_order = ["Entity6", "Entity1", "Entity5", "Entity3", "Entity2", "Entity4"] + expected_order = ["Entity5", "Entity1", "Entity3", "Entity4", "Entity2", "Entity6"] verify_entities_sorted(expected_order) diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index 693799e08b..3358b49dce 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -131,7 +131,7 @@ ly_add_source_properties( PROPERTY COMPILE_DEFINITIONS VALUES O3DE_COPYRIGHT_YEAR=${LY_VERSION_COPYRIGHT_YEAR} - LY_BUILD=${LY_VERSION_BUILD_NUMBER} + LY_VERSION_BUILD_NUMBER=${LY_VERSION_BUILD_NUMBER} ${LY_PAL_TOOLS_DEFINES} ) ly_add_source_properties( diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index ad04e355f5..4b725ac3d7 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -911,13 +911,9 @@ namespace QWidget* g_splashScreen = nullptr; } -QString FormatVersion(const SFileVersion& v) +QString FormatVersion([[maybe_unused]] const SFileVersion& v) { -#if defined(LY_BUILD) - return QObject::tr("Version %1.%2.%3.%4 - Build %5").arg(v[3]).arg(v[2]).arg(v[1]).arg(v[0]).arg(LY_BUILD); -#else - return QObject::tr("Version %1.%2.%3.%4").arg(v[3]).arg(v[2]).arg(v[1]).arg(v[0]); -#endif + return QObject::tr("Version %1").arg(LY_VERSION_BUILD_NUMBER); } QString FormatRichTextCopyrightNotice() diff --git a/Code/Framework/AzCore/AzCore/std/containers/fixed_vector.h b/Code/Framework/AzCore/AzCore/std/containers/fixed_vector.h index 7c0cca0308..5a54c2a589 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/fixed_vector.h +++ b/Code/Framework/AzCore/AzCore/std/containers/fixed_vector.h @@ -130,10 +130,11 @@ namespace AZStd::Internal //! Constructors - constexpr fixed_trivial_storage() = default; + fixed_trivial_storage() = default; template >> - constexpr fixed_trivial_storage(AZStd::initializer_list ilist) noexcept + fixed_trivial_storage(AZStd::initializer_list ilist) noexcept + : m_size(aznumeric_caster(ilist.size())) { AZSTD_CONTAINER_ASSERT(ilist.size() <= capacity(), "Initializer list cannot be larger than storage capacity"); size_t index{}; @@ -141,20 +142,19 @@ namespace AZStd::Internal { m_data[index++] = element; } - resize_no_construct(ilist.size()); } - constexpr pointer data() noexcept + pointer data() noexcept { return m_data; } - constexpr const_pointer data() const noexcept + const_pointer data() const noexcept { return m_data; } //! Number of elements currently stored. - constexpr size_type size() const noexcept + size_type size() const noexcept { return m_size; } @@ -164,12 +164,12 @@ namespace AZStd::Internal return Capacity; } //! Is the storage empty? - constexpr bool empty() const noexcept + bool empty() const noexcept { return size() == 0; } //! Is the storage full? - constexpr bool full() const noexcept + bool full() const noexcept { return size() == capacity(); } @@ -186,7 +186,7 @@ namespace AZStd::Internal //! Increases size of the storage by one. //! Always fails for empty storage. template >> - constexpr reference emplace_back(Args&&... args) noexcept + reference emplace_back(Args&&... args) noexcept { AZSTD_CONTAINER_ASSERT(!full(), "emplace_back cannot be invoked on full storage"); reference new_element = *(data() + size()); @@ -196,7 +196,7 @@ namespace AZStd::Internal } //! Removes the last element of the storage. //! Precondition: size is not empty - constexpr void pop_back() noexcept + void pop_back() noexcept { AZSTD_CONTAINER_ASSERT(!empty(), "pop_back cannot be invoked on empty storage"); resize_no_construct(size() - 1); @@ -205,7 +205,7 @@ namespace AZStd::Internal //! removing elements (unsafe). //! //! Updates the size of the container while checking that the new size is less than capacity - constexpr void resize_no_construct(size_t new_size) noexcept + void resize_no_construct(size_t new_size) noexcept { AZSTD_CONTAINER_ASSERT(new_size <= capacity(), "New size cannot be larger than capacity"); m_size = aznumeric_cast(new_size); @@ -215,19 +215,19 @@ namespace AZStd::Internal //! This does not modify the size of the storage //! This is a no-op for trivial types template >> - constexpr void unsafe_destroy(InputIt, InputIt) noexcept + void unsafe_destroy(InputIt, InputIt) noexcept { } //! Destructs all elements of the storage. //! This does not modify the size of the storage //! This is a no-op for trivial types - constexpr void unsafe_destroy_all() noexcept + void unsafe_destroy_all() noexcept { } private: - T m_data[Capacity]{}; + T m_data[Capacity]; size_type m_size{}; }; @@ -245,7 +245,7 @@ namespace AZStd::Internal using reference = T&; using const_reference = const T&; - constexpr fixed_non_trivial_storage() = default; + fixed_non_trivial_storage() = default; ~fixed_non_trivial_storage() noexcept { @@ -253,7 +253,7 @@ namespace AZStd::Internal } template >> - constexpr fixed_non_trivial_storage(AZStd::initializer_list ilist) noexcept(noexcept(emplace_back(AZStd::declval()))) + fixed_non_trivial_storage(AZStd::initializer_list ilist) noexcept(noexcept(emplace_back(AZStd::declval()))) { AZSTD_CONTAINER_ASSERT(ilist.size() <= capacity(), "Initializer list cannot be larger than storage capacity"); for (const U& element : ilist) @@ -272,7 +272,7 @@ namespace AZStd::Internal } //! Number of elements currently stored. - constexpr size_type size() const noexcept + size_type size() const noexcept { return m_size; } @@ -282,12 +282,12 @@ namespace AZStd::Internal return Capacity; } //! Is the storage empty? - constexpr bool empty() const noexcept + bool empty() const noexcept { return size() == 0; } //! Is the storage full? - constexpr bool full() const noexcept + bool full() const noexcept { return size() == capacity(); } @@ -325,7 +325,7 @@ namespace AZStd::Internal //! removing elements (unsafe). //! //! Updates the size of the container while checking that the new size is less than capacity - constexpr void resize_no_construct(size_t new_size) noexcept + void resize_no_construct(size_t new_size) noexcept { AZSTD_CONTAINER_ASSERT(new_size <= capacity(), "New size cannot be larger than capacity"); m_size = aznumeric_cast(new_size); @@ -402,23 +402,23 @@ namespace AZStd ////////////////////////////////////////////////////////////////////////// // 23.2.4.1 construct/copy/destroy - constexpr fixed_vector() = default; + fixed_vector() = default; - constexpr explicit fixed_vector(size_type numElements, const_reference value = value_type()) + explicit fixed_vector(size_type numElements, const_reference value = value_type()) { resize_no_construct(numElements); AZStd::uninitialized_fill_n(data(), numElements, value); } template >> - constexpr fixed_vector(InputIt first, InputIt last) + fixed_vector(InputIt first, InputIt last) { resize_no_construct(AZStd::distance(first, last)); AZStd::uninitialized_copy(first, last, data()); } - constexpr fixed_vector(const fixed_vector& rhs) + fixed_vector(const fixed_vector& rhs) { resize_no_construct(rhs.size()); AZStd::uninitialized_copy(rhs.data(), rhs.data() + rhs.size(), data()); @@ -428,7 +428,7 @@ namespace AZStd // It performs an AZStd::move on each of the fixed_vector elements instead // of swapping pointers to the allocted memory address // as it is unable to perform that operations due to the storage being baked into the container - constexpr fixed_vector(fixed_vector&& rhs) + fixed_vector(fixed_vector&& rhs) { resize_no_construct(rhs.size()); AZStd::uninitialized_move(rhs.data(), rhs.data() + rhs.size(), data()); @@ -440,7 +440,7 @@ namespace AZStd // into a fixed_vector given that the type in question isn't the same type as this fixed_vector type template && !AZStd::is_convertible_v>> - constexpr fixed_vector(VectorContainer&& rhs) + fixed_vector(VectorContainer&& rhs) { constexpr bool is_const_or_lvalue_reference = AZStd::is_lvalue_reference_v || AZStd::is_const_v; @@ -459,12 +459,12 @@ namespace AZStd } } - constexpr fixed_vector(AZStd::initializer_list ilist) + fixed_vector(AZStd::initializer_list ilist) : base_type(ilist) { } - constexpr fixed_vector& operator=(const fixed_vector& rhs) + fixed_vector& operator=(const fixed_vector& rhs) { if (this == &rhs) { @@ -475,7 +475,7 @@ namespace AZStd return assign_helper(rhs); } - constexpr fixed_vector& operator=(fixed_vector&& rhs) + fixed_vector& operator=(fixed_vector&& rhs) { if (this == &rhs) { @@ -487,23 +487,23 @@ namespace AZStd } template - constexpr AZStd::enable_if_t, fixed_vector>, fixed_vector>& operator=(VectorContainer&& rhs) + AZStd::enable_if_t, fixed_vector>, fixed_vector>& operator=(VectorContainer&& rhs) { return assign_helper(AZStd::forward(rhs)); } - constexpr iterator begin() { return iterator(data()); } - constexpr const_iterator begin() const { return const_iterator(data()); } - constexpr const_iterator cbegin() const { return const_iterator(data()); } - constexpr iterator end() { return iterator(data() + size()); } - constexpr const_iterator end() const { return const_iterator(data() + size()); } - constexpr const_iterator cend() const { return const_iterator(data() + size()); } - constexpr reverse_iterator rbegin() { return reverse_iterator(end()); } - constexpr const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } - constexpr reverse_iterator rend() { return reverse_iterator(begin()); } - constexpr const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - constexpr const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } + iterator begin() { return iterator(data()); } + const_iterator begin() const { return const_iterator(data()); } + const_iterator cbegin() const { return const_iterator(data()); } + iterator end() { return iterator(data() + size()); } + const_iterator end() const { return const_iterator(data() + size()); } + const_iterator cend() const { return const_iterator(data() + size()); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } // bring in fixed_vector_storage functions into scope using base_type::data; @@ -514,7 +514,7 @@ namespace AZStd // extension method using base_type::resize_no_construct; - constexpr size_type size() const noexcept + size_type size() const noexcept { return base_type::size(); } @@ -527,12 +527,12 @@ namespace AZStd return base_type::max_size(); } - constexpr void resize(size_type newSize) + void resize(size_type newSize) { return resize(newSize, value_type{}); } - constexpr void resize(size_type newSize, const_reference value) + void resize(size_type newSize, const_reference value) { size_type dataSize = size(); if (dataSize < newSize) @@ -547,7 +547,7 @@ namespace AZStd // Removes unused capacity - For fixed_vector this only asserts // that the supplied capacity is not longer than the fixed_vector capacity - constexpr void reserve(size_type newCapacity) + void reserve(size_type newCapacity) { // No-op - Implemented to provide consistent std::vector AZSTD_CONTAINER_ASSERT(newCapacity <= capacity(), @@ -556,79 +556,79 @@ namespace AZStd } // Removes unused capacity - For fixed_vector this does nothing - constexpr void shrink_to_fit() + void shrink_to_fit() { // No-op - Implemented to provide consistent std::vector } - constexpr reference at(size_type position) + reference at(size_type position) { AZSTD_CONTAINER_ASSERT(position < size(), "AZStd::fixed_vector<>::at - position is out of range"); return *(data() + position); } - constexpr const_reference at(size_type position) const + const_reference at(size_type position) const { AZSTD_CONTAINER_ASSERT(position < size(), "AZStd::fixed_vector<>::at - position is out of range"); return *(data() + position); } - constexpr reference operator[](size_type position) + reference operator[](size_type position) { AZSTD_CONTAINER_ASSERT(position < size(), "AZStd::fixed_vector<>::at - position is out of range"); return *(data() + position); } - constexpr const_reference operator[](size_type position) const + const_reference operator[](size_type position) const { AZSTD_CONTAINER_ASSERT(position < size(), "AZStd::fixed_vector<>::at - position is out of range"); return *(data() + position); } - constexpr reference front() + reference front() { AZSTD_CONTAINER_ASSERT(!empty(), "AZStd::fixed_vector<>::front - container is empty!"); return *data(); } - constexpr const_reference front() const + const_reference front() const { AZSTD_CONTAINER_ASSERT(!empty(), "AZStd::fixed_vector<>::front - container is empty!"); return *data(); } - constexpr reference back() + reference back() { AZSTD_CONTAINER_ASSERT(!empty(), "AZStd::fixed_vector<>::back - container is empty!"); return *(data() + size() - 1); } - constexpr const_reference back() const + const_reference back() const { AZSTD_CONTAINER_ASSERT(!empty(), "AZStd::fixed_vector<>::back - container is empty!"); return *(data() + size() - 1); } - constexpr void push_back(const_reference value) + void push_back(const_reference value) { emplace_back(value); } - constexpr void assign(size_type numElements, const_reference value) + void assign(size_type numElements, const_reference value) { clear(); insert(end(), numElements, value); } template >> - constexpr void assign(InputIt first, InputIt last) + void assign(InputIt first, InputIt last) { clear(); insert(end(), first, last); } - constexpr void assign(AZStd::initializer_list ilist) + void assign(AZStd::initializer_list ilist) { assign(ilist.begin(), ilist.end()); } template >> - constexpr iterator emplace(const_iterator insertPos, Args&&... args) + iterator emplace(const_iterator insertPos, Args&&... args) { AZSTD_CONTAINER_ASSERT(!full(), "Cannot emplace on a full fixed_vector"); AZSTD_CONTAINER_ASSERT(insertPos >= cbegin() && insertPos <= cend(), "insert position must be in range of container"); @@ -645,18 +645,18 @@ namespace AZStd AZStd::construct_at(insertPosPtr, AZStd::forward(args)...); return iterator(insertPosPtr); } - constexpr iterator insert(const_iterator insertPos, const_reference value) + iterator insert(const_iterator insertPos, const_reference value) { AZSTD_CONTAINER_ASSERT(insertPos >= cbegin() && insertPos <= cend(), "insert position must be in range of container"); return emplace(insertPos, value); } - constexpr iterator insert(const_iterator insertPos, value_type&& value) + iterator insert(const_iterator insertPos, value_type&& value) { AZSTD_CONTAINER_ASSERT(insertPos >= cbegin() && insertPos <= cend(), "insert position must be in range of container"); return emplace(insertPos, AZStd::move(value)); } - constexpr void insert(const_iterator insertPos, size_type numElements, const_reference value) + void insert(const_iterator insertPos, size_type numElements, const_reference value) { if (numElements == 0) { @@ -708,24 +708,24 @@ namespace AZStd } template>> - constexpr void insert(const_iterator insertPos, InputIt first, InputIt last) + void insert(const_iterator insertPos, InputIt first, InputIt last) { // specialize for iterator categories. AZSTD_CONTAINER_ASSERT(insertPos >= cbegin() && insertPos <= cend(), "insert position must be in range of container"); insert_iter(insertPos, first, last, typename iterator_traits::iterator_category()); }; - constexpr void insert(const_iterator insertPos, AZStd::initializer_list ilist) + void insert(const_iterator insertPos, AZStd::initializer_list ilist) { insert(insertPos, ilist.begin(), ilist.end()); } - constexpr iterator erase(const_iterator elementIter) + iterator erase(const_iterator elementIter) { return erase(elementIter, elementIter + 1); } - constexpr iterator erase(const_iterator first, const_iterator last) + iterator erase(const_iterator first, const_iterator last) { AZSTD_CONTAINER_ASSERT(first >= cbegin() && last <= cend(), "erase iterator must be inside the range of fixed_vector container"); iterator dataStart = begin(); @@ -741,12 +741,12 @@ namespace AZStd return dataStart + offset; } - constexpr void clear() + void clear() { base_type::unsafe_destroy_all(); resize_no_construct(0); } - constexpr void swap(fixed_vector& rhs) + void swap(fixed_vector& rhs) { // Fixed containers cannot swap pointers, they need to do full copies. // The strategy is to extend the smaller fixed_vector to be the size @@ -776,12 +776,12 @@ namespace AZStd } // Validate container status. - constexpr bool validate() const + bool validate() const { return size() <= max_size(); } // Validate iterator. - constexpr int validate_iterator(const_iterator iter) const + int validate_iterator(const_iterator iter) const { const_pointer start = data(); const_pointer end = data() + size(); @@ -799,19 +799,19 @@ namespace AZStd } // pushes back an empty without a provided instance. - constexpr void push_back() + void push_back() { emplace_back(); } - constexpr void leak_and_reset() + void leak_and_reset() { resize_no_construct(0); } private: template - constexpr fixed_vector& assign_helper(VectorContainer&& rhs) + fixed_vector& assign_helper(VectorContainer&& rhs) { constexpr bool is_const_or_lvalue_reference = AZStd::is_lvalue_reference_v || AZStd::is_const_v; @@ -872,7 +872,7 @@ namespace AZStd } template - constexpr void insert_iter(const_iterator insertPos, Iterator first, Iterator last, const forward_iterator_tag&) + void insert_iter(const_iterator insertPos, Iterator first, Iterator last, const forward_iterator_tag&) { size_type numElements = AZStd::distance(first, last); if (numElements == 0) @@ -923,7 +923,7 @@ namespace AZStd } template - constexpr void insert_iter(const_iterator insertPos, Iterator first, Iterator last, const input_iterator_tag&) + void insert_iter(const_iterator insertPos, Iterator first, Iterator last, const input_iterator_tag&) { iterator dataStart = data(); size_type offset = AZStd::distance(dataStart, insertPos); diff --git a/Code/Framework/AzCore/Tests/AZStd/VectorAndArray.cpp b/Code/Framework/AzCore/Tests/AZStd/VectorAndArray.cpp index 47c3630091..b3eab244c4 100644 --- a/Code/Framework/AzCore/Tests/AZStd/VectorAndArray.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/VectorAndArray.cpp @@ -753,7 +753,7 @@ namespace UnitTest TEST_F(Arrays, FixedVectorCanCopyAndMoveWithDifferentCapacity) { - constexpr AZStd::fixed_vector sourceVector{ 1,2,3,4,5 }; + AZStd::fixed_vector sourceVector{ 1,2,3,4,5 }; AZStd::fixed_vector copyConstructVector{ sourceVector }; EXPECT_EQ(sourceVector, copyConstructVector); @@ -768,32 +768,32 @@ namespace UnitTest AZStd::fixed_vector moveAssignVector = AZStd::move(moveConstructVector); - constexpr AZStd::fixed_vector expectedVector{ 1,2,3,4,5,6 }; + AZStd::fixed_vector expectedVector{ 1,2,3,4,5,6 }; EXPECT_EQ(expectedVector, moveAssignVector); } TEST_F(Arrays, FixedVectorComparisonOperatorsSucceedAsExpected) { - constexpr AZStd::fixed_vector testVector{ 1,2,3,4,5 }; - constexpr AZStd::fixed_vector equalVector{ 1,2,3,4,5 }; - constexpr AZStd::fixed_vector notEqualVectorDifferentSize{ 1,2,3,4,5,6 }; - constexpr AZStd::fixed_vector lessVector{ 1,2,3,4,4 }; - constexpr AZStd::fixed_vector greaterVectorDifferentSize{ 1,2,3,4,5, 1 }; + AZStd::fixed_vector testVector{ 1,2,3,4,5 }; + AZStd::fixed_vector equalVector{ 1,2,3,4,5 }; + AZStd::fixed_vector notEqualVectorDifferentSize{ 1,2,3,4,5,6 }; + AZStd::fixed_vector lessVector{ 1,2,3,4,4 }; + AZStd::fixed_vector greaterVectorDifferentSize{ 1,2,3,4,5, 1 }; - static_assert(testVector == equalVector); - static_assert(testVector != notEqualVectorDifferentSize); - static_assert(testVector != lessVector); - static_assert(lessVector < testVector); - static_assert(lessVector < greaterVectorDifferentSize); - static_assert(lessVector <= lessVector); - static_assert(lessVector <= testVector); - static_assert(lessVector <= greaterVectorDifferentSize); - static_assert(testVector > lessVector); - static_assert(testVector > lessVector); - static_assert(notEqualVectorDifferentSize > testVector); - static_assert(testVector >= testVector); - static_assert(testVector >= lessVector); - static_assert(greaterVectorDifferentSize > lessVector); + EXPECT_EQ(testVector, equalVector); + EXPECT_NE(testVector, notEqualVectorDifferentSize); + EXPECT_NE(testVector, lessVector); + EXPECT_LT(lessVector, testVector); + EXPECT_LT(lessVector, greaterVectorDifferentSize); + EXPECT_LE(lessVector, lessVector); + EXPECT_LE(lessVector, testVector); + EXPECT_LE(lessVector, greaterVectorDifferentSize); + EXPECT_GT(testVector, lessVector); + EXPECT_GT(testVector, lessVector); + EXPECT_GT(notEqualVectorDifferentSize, testVector); + EXPECT_GE(testVector, testVector); + EXPECT_GE(testVector, lessVector); + EXPECT_GT(greaterVectorDifferentSize, lessVector); } TEST_F(Arrays, VectorSwap) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index 0f0bbf2b41..e4057b31e7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -239,12 +239,14 @@ namespace AzToolsFramework , m_isInIsolationMode(false) { ToolsApplicationRequests::Bus::Handler::BusConnect(); + AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusConnect(); m_undoCache.RegisterToUndoCacheInterface(); } ToolsApplication::~ToolsApplication() { + AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusDisconnect(); ToolsApplicationRequests::Bus::Handler::BusDisconnect(); Stop(); } @@ -566,6 +568,12 @@ namespace AzToolsFramework void ToolsApplication::MarkEntitySelected(AZ::EntityId entityId) { AZ_PROFILE_FUNCTION(AzToolsFramework); + + if (m_freezeSelectionUpdates) + { + return; + } + AZ_Assert(entityId.IsValid(), "Invalid entity Id being marked as selected."); EntityIdList::iterator foundIter = AZStd::find(m_selectedEntities.begin(), m_selectedEntities.end(), entityId); @@ -585,6 +593,11 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AzToolsFramework); + if (m_freezeSelectionUpdates) + { + return; + } + EntityIdList entitiesSelected; entitiesSelected.reserve(entitiesToSelect.size()); @@ -608,6 +621,12 @@ namespace AzToolsFramework void ToolsApplication::MarkEntityDeselected(AZ::EntityId entityId) { AZ_PROFILE_FUNCTION(AzToolsFramework); + + if (m_freezeSelectionUpdates) + { + return; + } + auto foundIter = AZStd::find(m_selectedEntities.begin(), m_selectedEntities.end(), entityId); if (foundIter != m_selectedEntities.end()) { @@ -625,6 +644,11 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AzToolsFramework); + if (m_freezeSelectionUpdates) + { + return; + } + ToolsApplicationEvents::Bus::Broadcast(&ToolsApplicationEvents::BeforeEntitySelectionChanged); EntityIdSet entitySetToDeselect(entitiesToDeselect.begin(), entitiesToDeselect.end()); @@ -679,6 +703,11 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AzToolsFramework); + if (m_freezeSelectionUpdates) + { + return; + } + // We're setting the selection set as a batch from an external caller. // * Filter out any unselectable entities // * Calculate selection/deselection delta so we can notify specific entities only on change. @@ -1569,6 +1598,16 @@ namespace AzToolsFramework } } + void ToolsApplication::OnPrefabInstancePropagationBegin() + { + m_freezeSelectionUpdates = true; + } + + void ToolsApplication::OnPrefabInstancePropagationEnd() + { + m_freezeSelectionUpdates = false; + } + void ToolsApplication::CreateUndosForDirtyEntities() { AZ_PROFILE_FUNCTION(AzToolsFramework); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h index 5e9208b475..3dcb43f17d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h @@ -16,6 +16,7 @@ #include #include #include +#include #pragma once @@ -29,6 +30,7 @@ namespace AzToolsFramework class ToolsApplication : public AzFramework::Application , public ToolsApplicationRequests::Bus::Handler + , public AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler { public: AZ_RTTI(ToolsApplication, "{2895561E-BE90-4CC3-8370-DD46FCF74C01}", AzFramework::Application); @@ -169,6 +171,14 @@ namespace AzToolsFramework }; ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + // PrefabPublicNotificationBus::Handler + + void OnPrefabInstancePropagationBegin() override; + void OnPrefabInstancePropagationEnd() override; + + ////////////////////////////////////////////////////////////////////////// + void CreateUndosForDirtyEntities(); void ConsistencyCheckUndoCache(); AZ::Aabb m_selectionBounds; @@ -181,6 +191,7 @@ namespace AzToolsFramework bool m_isDuringUndoRedo; bool m_isInIsolationMode; EntityIdSet m_isolatedEntityIdSet; + bool m_freezeSelectionUpdates = false; EditorEntityAPI* m_editorEntityAPI = nullptr; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp index eaff64ba2e..e4d4f40bce 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp @@ -449,16 +449,23 @@ namespace AzToolsFramework AZStd::unordered_map>::const_iterator orderItr = m_savedOrderInfo.find(childId); if (orderItr != m_savedOrderInfo.end() && orderItr->second.first == parentId) { - bool sortOrderUpdated = AzToolsFramework::RecoverEntitySortInfo(parentId, childId, orderItr->second.second); - m_savedOrderInfo.erase(childId); - - // force notify the child sort order changed on the parent entity info, but only if the restore didn't actually modify - // the order internally (and sent ChildEntityOrderArrayUpdated). that may seem heavy handed, and it is, but necessary - // to combat scenarios when the initial override detection returns a false positive (see comment about IDH comparisons - // in OnChildSortOrderChanged) and the slice instance source-to-live mapping hasn't been fully reconstructed yet. - if (!sortOrderUpdated) + bool isPrefabEnabled = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult( + isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); + // If prefabs are enabled, rely on the component to do a sanity check instead of restoring the order from the model + if (!isPrefabEnabled) { - parentInfo.OnChildSortOrderChanged(); + bool sortOrderUpdated = AzToolsFramework::RecoverEntitySortInfo(parentId, childId, orderItr->second.second); + m_savedOrderInfo.erase(childId); + + // force notify the child sort order changed on the parent entity info, but only if the restore didn't actually modify + // the order internally (and sent ChildEntityOrderArrayUpdated). that may seem heavy handed, and it is, but necessary + // to combat scenarios when the initial override detection returns a false positive (see comment about IDH comparisons + // in OnChildSortOrderChanged) and the slice instance source-to-live mapping hasn't been fully reconstructed yet. + if (!sortOrderUpdated) + { + parentInfo.OnChildSortOrderChanged(); + } } } else diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.cpp index 6936397187..8ade8c470f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.cpp @@ -8,11 +8,17 @@ #include "EditorEntitySortComponent.h" #include "EditorEntityInfoBus.h" #include "EditorEntityHelpers.h" +#include #include #include +#include #include #include #include +#include +#include +#include +#include static_assert(sizeof(AZ::u64) == sizeof(AZ::EntityId), "We use AZ::EntityId for Persistent ID, which is a u64 under the hood. These must be the same size otherwise the persistent id will have to be rewritten"); @@ -51,6 +57,12 @@ namespace AzToolsFramework ; } } + + AZ::JsonRegistrationContext* jsonRegistration = azrtti_cast(context); + if (jsonRegistration) + { + jsonRegistration->Serializer()->HandlesType(); + } } void EditorEntitySortComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) @@ -167,9 +179,6 @@ namespace AzToolsFramework } MarkDirtyAndSendChangedEvent(); - - // Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId()); return true; } @@ -187,6 +196,10 @@ namespace AzToolsFramework else { EntityOrderArray::iterator insertPosition = GetFirstSelectedEntityPosition(); + if (insertPosition != m_childEntityOrderArray.end()) + { + ++insertPosition; + } retval = AddChildEntityInternal(entityId, false, insertPosition); } @@ -220,9 +233,6 @@ namespace AzToolsFramework MarkDirtyAndSendChangedEvent(); - // Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId()); - return true; } return false; @@ -272,6 +282,12 @@ namespace AzToolsFramework void EditorEntitySortComponent::OnPrefabInstancePropagationEnd() { m_ignoreIncomingOrderChanges = false; + + if (m_shouldSanityCheckStateAfterPropagation) + { + SanitizeOrderEntryArray(); + m_shouldSanityCheckStateAfterPropagation = false; + } } void EditorEntitySortComponent::MarkDirtyAndSendChangedEvent() @@ -280,14 +296,8 @@ namespace AzToolsFramework // one of the event listeners needs to build the InstanceDataHierarchy m_entityOrderIsDirty = true; - // Force an immediate update for prefabs, which won't receive PrepareSave - bool isPrefabEnabled = false; - AzFramework::ApplicationRequests::Bus::BroadcastResult( - isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); - if (isPrefabEnabled) - { - PrepareSave(); - } + // Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId()); EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated); } @@ -308,9 +318,8 @@ namespace AzToolsFramework isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); if (isPrefabEnabled) { - PostLoad(); + m_shouldSanityCheckStateAfterPropagation = true; } - // Send out that the order for our entity is now updated EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated); } @@ -336,6 +345,73 @@ namespace AzToolsFramework m_entityOrderIsDirty = false; } + void EditorEntitySortComponent::SanitizeOrderEntryArray() + { + bool shouldEmitDirtyState = false; + + // Remove invalid and duplicate entries that point at non-existent entities + AZStd::unordered_set duplicateIds; + for (auto it = m_childEntityOrderArray.begin(); it != m_childEntityOrderArray.end();) + { + if (!it->IsValid() || GetEntityById(*it) == nullptr || duplicateIds.contains(*it)) + { + it = m_childEntityOrderArray.erase(it); + shouldEmitDirtyState = true; + } + else + { + duplicateIds.insert(*it); + ++it; + } + } + + // Append any missing children + EntityIdList children; + AZ::TransformBus::EventResult(children, GetEntityId(), &AZ::TransformBus::Events::GetChildren); + for (auto it = m_childEntityOrderArray.begin(); it != m_childEntityOrderArray.end(); ++it) + { + if (auto removedChildrenIt = AZStd::remove(children.begin(), children.end(), *it); removedChildrenIt != children.end()) + { + children.erase(removedChildrenIt); + } + } + + AZStd::sort(children.begin(), children.end(), [](AZ::EntityId lhs, AZ::EntityId rhs) + { + return GetEntityById(lhs)->GetName() < GetEntityById(rhs)->GetName(); + }); + + if (!children.empty()) + { + shouldEmitDirtyState = true; + EntityOrderArray::iterator insertPosition = GetFirstSelectedEntityPosition(); + if (insertPosition != m_childEntityOrderArray.end()) + { + ++insertPosition; + } + m_childEntityOrderArray.insert(insertPosition, children.begin(), children.end()); + } + + // Clear out the vector to be rebuilt from persistent id + m_childEntityOrderEntryArray.resize(m_childEntityOrderArray.size()); + for (size_t i = 0; i < m_childEntityOrderArray.size(); ++i) + { + m_childEntityOrderEntryArray[i] = { + m_childEntityOrderArray[i], + static_cast(i) + }; + } + + RebuildEntityOrderCache(); + + if (shouldEmitDirtyState) + { + ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::Bus::Events::AddDirtyEntity, GetEntityId()); + } + + m_entityOrderIsDirty = false; + } + void EditorEntitySortComponent::PostLoad() { // Clear out the vector to be rebuilt from persistent id @@ -383,7 +459,7 @@ namespace AzToolsFramework firstSelectedEntityPos = selectedEntityPos < firstSelectedEntityPos ? selectedEntityPos : firstSelectedEntityPos; } - return firstSelectedEntityPos == m_childEntityOrderArray.end() ? m_childEntityOrderArray.begin() : firstSelectedEntityPos; + return firstSelectedEntityPos; } } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.h index 806e903c96..a4715fb041 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.h @@ -23,6 +23,8 @@ namespace AzToolsFramework , public EditorEntityContextNotificationBus::Handler , public AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler { + friend class JsonEditorEntitySortComponentSerializer; + public: AZ_COMPONENT(EditorEntitySortComponent, "{6EA1E03D-68B2-466D-97F7-83998C8C27F0}", EditorComponentBase); @@ -64,6 +66,8 @@ namespace AzToolsFramework void PrepareSave(); void PostLoad(); + void SanitizeOrderEntryArray(); + class EntitySortSerializationEvents : public AZ::SerializeContext::IEventHandler { @@ -112,6 +116,7 @@ namespace AzToolsFramework bool m_entityOrderIsDirty = true; ///< This flag indicates our stored serialization order data is out of date and must be rebuilt before serialization occurs bool m_ignoreIncomingOrderChanges = false; ///< This is set when prefab propagation occurs so that non-authored order changes can be ignored + bool m_shouldSanityCheckStateAfterPropagation = false; //< This is set after activation, to queue a cleanup of any invalid state after the next prefab propagation. }; } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponentSerializer.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponentSerializer.cpp new file mode 100644 index 0000000000..0ec13cedda --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponentSerializer.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include + +namespace AzToolsFramework::Components +{ + AZ_CLASS_ALLOCATOR_IMPL(JsonEditorEntitySortComponentSerializer, AZ::SystemAllocator, 0); + + AZ::JsonSerializationResult::Result JsonEditorEntitySortComponentSerializer::Load( + void* outputValue, + [[maybe_unused]] const AZ::Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, + AZ::JsonDeserializerContext& context) + { + namespace JSR = AZ::JsonSerializationResult; + + AZ_Assert( + azrtti_typeid() == outputValueTypeId, + "Unable to deserialize EditorEntitySortComponent from json because the provided type is %s.", + outputValueTypeId.ToString().c_str()); + + EditorEntitySortComponent* sortComponentInstance = reinterpret_cast(outputValue); + AZ_Assert(sortComponentInstance, "Output value for JsonEditorEntitySortComponentSerializer can't be null."); + + JSR::ResultCode result(JSR::Tasks::ReadField); + { + JSR::ResultCode componentIdLoadResult = ContinueLoadingFromJsonObjectField( + &sortComponentInstance->m_id, azrtti_typeidm_id)>(), inputValue, + "Id", context); + + result.Combine(componentIdLoadResult); + } + + { + sortComponentInstance->m_childEntityOrderArray.clear(); + JSR::ResultCode enryLoadResult = ContinueLoadingFromJsonObjectField( + &sortComponentInstance->m_childEntityOrderArray, + azrtti_typeidm_childEntityOrderArray)>(), inputValue, "Child Entity Order", + context); + + // Migrate ChildEntityOrderEntryArray -> ChildEntityOrderArray + if (sortComponentInstance->m_childEntityOrderArray.empty()) + { + enryLoadResult = ContinueLoadingFromJsonObjectField( + &sortComponentInstance->m_childEntityOrderEntryArray, + azrtti_typeidm_childEntityOrderEntryArray)>(), inputValue, + "ChildEntityOrderEntryArray", context); + + AZStd::sort( + sortComponentInstance->m_childEntityOrderEntryArray.begin(), + sortComponentInstance->m_childEntityOrderEntryArray.end(), + [](const EditorEntitySortComponent::EntityOrderEntry& lhs, + const EditorEntitySortComponent::EntityOrderEntry& rhs) -> bool + { + return lhs.m_sortIndex < rhs.m_sortIndex; + }); + + // Sort by index and copy to the order array, any duplicates or invalid entries will be cleaned up by the sanitization pass + sortComponentInstance->m_childEntityOrderArray.resize(sortComponentInstance->m_childEntityOrderEntryArray.size()); + for (size_t i = 0; i < sortComponentInstance->m_childEntityOrderEntryArray.size(); ++i) + { + sortComponentInstance->m_childEntityOrderArray[i] = sortComponentInstance->m_childEntityOrderEntryArray[i].m_entityId; + } + } + + sortComponentInstance->RebuildEntityOrderCache(); + + result.Combine(enryLoadResult); + } + + return context.Report( + result, + result.GetProcessing() != JSR::Processing::Halted ? "Successfully loaded EditorEntitySortComponent information." + : "Failed to load EditorEntitySortComponent information."); + } + + AZ::JsonSerializationResult::Result JsonEditorEntitySortComponentSerializer::Store( + rapidjson::Value& outputValue, + const void* inputValue, + const void* defaultValue, + [[maybe_unused]] const AZ::Uuid& valueTypeId, + AZ::JsonSerializerContext& context) + { + namespace JSR = AZ::JsonSerializationResult; + + AZ_Assert( + azrtti_typeid() == valueTypeId, + "Unable to Serialize EditorEntitySortComponent because the provided type is %s.", + valueTypeId.ToString().c_str()); + + const EditorEntitySortComponent* sortComponentInstance = reinterpret_cast(inputValue); + AZ_Assert(sortComponentInstance, "Input value for JsonEditorEntitySortComponentSerializer can't be null."); + const EditorEntitySortComponent* defaultsortComponentInstance = + reinterpret_cast(defaultValue); + + JSR::ResultCode result(JSR::Tasks::WriteValue); + { + AZ::ScopedContextPath subPathName(context, "m_id"); + const AZ::ComponentId* componentId = &sortComponentInstance->m_id; + const AZ::ComponentId* defaultComponentId = + defaultsortComponentInstance ? &defaultsortComponentInstance->m_id : nullptr; + + JSR::ResultCode resultComponentId = ContinueStoringToJsonObjectField( + outputValue, "Id", componentId, defaultComponentId, azrtti_typeidm_id)>(), + context); + + result.Combine(resultComponentId); + } + + { + AZ::ScopedContextPath subPathName(context, "m_childEntityOrderArray"); + const EntityOrderArray* childEntityOrderArray = &sortComponentInstance->m_childEntityOrderArray; + const EntityOrderArray* defaultChildEntityOrderArray = + defaultsortComponentInstance ? &defaultsortComponentInstance->m_childEntityOrderArray : nullptr; + + JSR::ResultCode resultParentEntityId = ContinueStoringToJsonObjectField( + outputValue, "Child Entity Order", childEntityOrderArray, defaultChildEntityOrderArray, + azrtti_typeidm_childEntityOrderArray)>(), context); + + result.Combine(resultParentEntityId); + } + + return context.Report( + result, + result.GetProcessing() != JSR::Processing::Halted ? "Successfully stored EditorEntitySortComponent information." + : "Failed to store EditorEntitySortComponent information."); + } +} // namespace AzToolsFramework::Components diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponentSerializer.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponentSerializer.h new file mode 100644 index 0000000000..29d1f9c14a --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponentSerializer.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AzToolsFramework::Components +{ + class JsonEditorEntitySortComponentSerializer + : public AZ::BaseJsonSerializer + { + public: + AZ_RTTI(JsonEditorEntitySortComponentSerializer, "{5104782E-B34F-4D87-B1DF-BDFB1AF20D58}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + AZ::JsonSerializationResult::Result Load( + void* outputValue, const AZ::Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + AZ::JsonDeserializerContext& context) override; + + AZ::JsonSerializationResult::Result Store( + rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const AZ::Uuid& valueTypeId, + AZ::JsonSerializerContext& context) override; + }; +} // namespace AzToolsFramework::Components diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp index d5cf7a9144..ea1af5cccb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace AzToolsFramework { @@ -244,10 +245,10 @@ namespace AzToolsFramework selectedEntityIds.erase(entityIdIterator--); } } - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds); - // Notify Propagation has ended + // Notify Propagation has ended, then update selection (which is frozen during propagation, so this order matters) PrefabPublicNotificationBus::Broadcast(&PrefabPublicNotifications::OnPrefabInstancePropagationEnd); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds); } m_updatingTemplateInstancesInQueue = false; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h index 7cab24ad9f..89d1a046e5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h @@ -28,6 +28,9 @@ namespace AzToolsFramework inline static const char* EntityIdName = "Id"; inline static const char* EntitiesName = "Entities"; inline static const char* ContainerEntityName = "ContainerEntity"; + inline static const char* ComponentsName = "Components"; + inline static const char* EntityOrderName = "Child Entity Order"; + inline static const char* TypeName = "$type"; /** * Find Prefab value from given parent value and target value's name. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 744c53ef5a..f20c11a1d8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -212,7 +212,17 @@ namespace AzToolsFramework::Prefab AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { - return m_focusedInstanceContainerEntityId; + if (m_focusedInstanceContainerEntityId.IsValid()) + { + return m_focusedInstanceContainerEntityId; + } + + if (auto instance = GetReferenceFromContainerEntityId(m_focusedInstanceContainerEntityId); instance.has_value()) + { + return instance->get().GetContainerEntityId(); + } + + return AZ::EntityId(); } bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const @@ -366,7 +376,7 @@ namespace AzToolsFramework::Prefab size_t index = 0; size_t maxIndex = m_instanceFocusHierarchy.size() - 1; - for (const AZ::EntityId containerEntityId : m_instanceFocusHierarchy) + for (const AZ::EntityId& containerEntityId : m_instanceFocusHierarchy) { InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId); if (instance.has_value()) @@ -404,7 +414,7 @@ namespace AzToolsFramework::Prefab return; } - for (const AZ::EntityId containerEntityId : instances) + for (const AZ::EntityId& containerEntityId : instances) { InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId); @@ -423,7 +433,7 @@ namespace AzToolsFramework::Prefab return; } - for (const AZ::EntityId containerEntityId : instances) + for (const AZ::EntityId& containerEntityId : instances) { InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 850b793513..1640ac1017 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include @@ -595,8 +597,41 @@ namespace AzToolsFramework Instance& entityOwningInstance = owningInstanceOfParentEntity->get(); + // Get the template for our owning instance from the root prefab DOM and use that to generate our patch + AZStd::vector pathOfInstances; + + InstanceOptionalReference rootInstance = owningInstanceOfParentEntity; + while (rootInstance->get().GetParentInstance() != AZStd::nullopt) + { + pathOfInstances.emplace_back(rootInstance); + rootInstance = rootInstance->get().GetParentInstance(); + } + + AZStd::string aliasPathResult = ""; + for (auto instanceIter = pathOfInstances.rbegin(); instanceIter != pathOfInstances.rend(); ++instanceIter) + { + aliasPathResult.append("/Instances/"); + aliasPathResult.append((*instanceIter)->get().GetInstanceAlias()); + } + + PrefabDomPath rootPrefabDomPath(aliasPathResult.c_str()); + + PrefabDom& rootPrefabTemplateDom = m_prefabSystemComponentInterface->FindTemplateDom(rootInstance->get().GetTemplateId()); + + auto instanceDomFromRootValue = rootPrefabDomPath.Get(rootPrefabTemplateDom); + if (!instanceDomFromRootValue) + { + return AZ::Failure("Could not load Instance DOM from the top level ancestor's DOM."); + } + + PrefabDomValueReference instanceDomFromRoot = *instanceDomFromRootValue; + if (!instanceDomFromRoot.has_value()) + { + return AZ::Failure("Could not load Instance DOM from the top level ancestor's DOM."); + } + PrefabDom instanceDomBeforeUpdate; - m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, entityOwningInstance); + instanceDomBeforeUpdate.CopyFrom(instanceDomFromRoot.value().get(), instanceDomBeforeUpdate.GetAllocator()); ScopedUndoBatch undoBatch("Add Entity"); @@ -674,6 +709,9 @@ namespace AzToolsFramework bool isInstanceContainerEntity = IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId); bool isNewParentOwnedByDifferentInstance = false; + bool isInFocusTree = m_prefabFocusPublicInterface->IsOwningPrefabInFocusHierarchy(entityId); + bool isOwnedByFocusedPrefabInstance = m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId); + if (beforeParentId != afterParentId) { // If the entity parent changed, verify if the owning instance changed too @@ -727,7 +765,7 @@ namespace AzToolsFramework } } - if (isInstanceContainerEntity) + if (isInFocusTree && !isOwnedByFocusedPrefabInstance) { if (isNewParentOwnedByDifferentInstance) { @@ -1648,6 +1686,144 @@ namespace AzToolsFramework return true; } + void PrefabPublicHandler::AddNewEntityToSortOrder( + Instance& owningInstance, + PrefabDom& domToAddEntityUnder, + const EntityAlias& parentEntityAlias, + const EntityAlias& entityToAddAlias) + { + // Find the parent entity to get its sort order component + auto findParentEntity = [&]() -> rapidjson::Value* + { + if (auto containerEntityIter = domToAddEntityUnder.FindMember(PrefabDomUtils::ContainerEntityName); + containerEntityIter != domToAddEntityUnder.MemberEnd()) + { + if (parentEntityAlias == containerEntityIter->value[PrefabDomUtils::EntityIdName].GetString()) + { + return &containerEntityIter->value; + } + } + + if (auto entitiesIter = domToAddEntityUnder.FindMember(PrefabDomUtils::EntitiesName); + entitiesIter != domToAddEntityUnder.MemberEnd()) + { + for (auto entityIter = entitiesIter->value.MemberBegin(); entityIter != entitiesIter->value.MemberEnd(); ++entityIter) + { + if (parentEntityAlias == entityIter->value[PrefabDomUtils::EntityIdName].GetString()) + { + return &entityIter->value; + } + } + } + + return nullptr; + }; + + rapidjson::Value* parentEntityValue = findParentEntity(); + if (parentEntityValue == nullptr) + { + return; + } + + // Get the list of selected entities, we'll insert our duplicated entities after the last selected + // sibling in their parent's list, e.g. for: + // - Entity1 + // - Entity2 (selected) + // - Entity3 + // - Entity4 (selected) + // - Entity5 + // Our duplicate selection command would create duplicate Entity2 and Entity4 and insert them after Entity4: + // - Entity1 + // - Entity2 + // - Entity3 + // - Entity4 + // - Entity2 (new, selected after duplicate) + // - Entity4 (new, selected after duplicate) + // - Entity5 + AzToolsFramework::EntityIdList selectedEntities; + AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult( + selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities); + + // Find the EditorEntitySortComponent DOM + auto componentsIter = parentEntityValue->FindMember(PrefabDomUtils::ComponentsName); + if (componentsIter == parentEntityValue->MemberEnd()) + { + return; + } + + for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentIter->value.MemberEnd(); + ++componentIter) + { + // Check the component type + auto typeFieldIter = componentIter->value.FindMember(PrefabDomUtils::TypeName); + if (typeFieldIter == componentIter->value.MemberEnd()) + { + continue; + } + + AZ::JsonDeserializerSettings jsonDeserializerSettings; + AZ::Uuid typeId = AZ::Uuid::CreateNull(); + AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value); + + if (typeId != azrtti_typeid()) + { + continue; + } + + // Check for the entity order field + auto orderMembersIter = componentIter->value.FindMember(PrefabDomUtils::EntityOrderName); + if (orderMembersIter == componentIter->value.MemberEnd() || !orderMembersIter->value.IsArray()) + { + continue; + } + + // Scan for the last selected entity in the list (if any) to determine where to add our entries + rapidjson::Value newOrder(rapidjson::kArrayType); + auto insertValuesAfter = orderMembersIter->value.End(); + for (auto orderMemberIter = orderMembersIter->value.Begin(); orderMemberIter != orderMembersIter->value.End(); + ++orderMemberIter) + { + if (!orderMemberIter->IsString()) + { + continue; + } + const char* value = orderMemberIter->GetString(); + for (AZ::EntityId selectedEntity : selectedEntities) + { + auto alias = owningInstance.GetEntityAlias(selectedEntity); + if (alias.has_value() && alias.value().get() == value) + { + insertValuesAfter = orderMemberIter; + break; + } + } + } + + // Construct our new array with the new order - insertion may happen at end, so check for that in the loop itself + for (auto orderMemberIter = orderMembersIter->value.Begin();; ++orderMemberIter) + { + if (orderMemberIter != orderMembersIter->value.End()) + { + newOrder.PushBack(orderMemberIter->Move(), domToAddEntityUnder.GetAllocator()); + } + if (orderMemberIter == insertValuesAfter) + { + newOrder.PushBack( + rapidjson::Value(entityToAddAlias.c_str(), domToAddEntityUnder.GetAllocator()), + domToAddEntityUnder.GetAllocator()); + } + if (orderMemberIter == orderMembersIter->value.End()) + { + break; + } + } + + // Replace the order with our newly constructed one + orderMembersIter->value.Swap(newOrder); + break; + } + } + void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance, const AZStd::vector& entities, PrefabDom& domToAddDuplicatedEntitiesUnder, EntityIdList& duplicatedEntityIds, AZStd::unordered_map& oldAliasToNewAliasMap) @@ -1705,6 +1881,73 @@ namespace AzToolsFramework PrefabDom entityDomAfter(&domToAddDuplicatedEntitiesUnder.GetAllocator()); entityDomAfter.Parse(newEntityDomString.toUtf8().constData()); + EntityAlias parentEntityAlias; + if (auto componentsIter = entityDomAfter.FindMember(PrefabDomUtils::ComponentsName); + componentsIter != entityDomAfter.MemberEnd()) + { + auto checkComponent = [&](const rapidjson::Value& value) -> bool + { + if (!value.IsObject()) + { + return false; + } + + // Check the component type + auto typeFieldIter = value.FindMember(PrefabDomUtils::TypeName); + if (typeFieldIter == value.MemberEnd()) + { + return false; + } + + AZ::JsonDeserializerSettings jsonDeserializerSettings; + AZ::Uuid typeId = AZ::Uuid::CreateNull(); + AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value); + + // Prefabs get serialized with the Editor transform component type, check for that + if (typeId != azrtti_typeid()) + { + return false; + } + + if (auto parentEntityIter = value.FindMember("Parent Entity"); + parentEntityIter != value.MemberEnd()) + { + parentEntityAlias = parentEntityIter->value.GetString(); + return true; + } + return false; + }; + + if (componentsIter->value.IsObject()) + { + for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentsIter->value.MemberEnd(); + ++componentIter) + { + if (checkComponent(componentIter->value)) + { + break; + } + } + } + else if (componentsIter->value.IsArray()) + { + for (auto componentIter = componentsIter->value.Begin(); componentIter != componentsIter->value.End(); + ++componentIter) + { + if (checkComponent(*componentIter)) + { + break; + } + } + } + } + + // Insert our entity into its parent's sort order + if (!parentEntityAlias.empty()) + { + AddNewEntityToSortOrder(commonOwningInstance, domToAddDuplicatedEntitiesUnder, parentEntityAlias, newEntityAlias); + } + // Add the new Entity DOM to the Entities member of the instance rapidjson::Value aliasName(newEntityAlias.c_str(), static_cast(newEntityAlias.length()), domToAddDuplicatedEntitiesUnder.GetAllocator()); entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, domToAddDuplicatedEntitiesUnder.GetAllocator()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 4961be9d77..dd071ac09f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -78,6 +78,8 @@ namespace AzToolsFramework InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; + void AddNewEntityToSortOrder(Instance& owningInstance, PrefabDom& domToAddEntityUnder, + const EntityAlias& parentEntityAlias, const EntityAlias& entityToAddAlias); /** * Duplicate a list of entities owned by a common owning instance by directly diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp index 9ecbdf5ffc..fa419d5f14 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp @@ -1599,7 +1599,6 @@ namespace AzToolsFramework for (size_t entityIndex = 1; entityIndex < m_selectedEntityIds.size(); ++entityIndex) { entity = GetSelectedEntityById(m_selectedEntityIds[entityIndex]); - AZ_Assert(entity, "Entity id selected for display but no such entity exists"); if (!entity) { continue; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyVectorCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyVectorCtrl.hxx index e5a6b5803c..cc61593154 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyVectorCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyVectorCtrl.hxx @@ -150,10 +150,7 @@ namespace AzToolsFramework TypeBeingHandled actualValue = instance; for (int idx = 0; idx < m_common.GetElementCount(); ++idx) { - if (elements[idx]->wasValueEditedByUser()) - { - actualValue.SetElement(idx, static_cast(elements[idx]->getValue())); - } + actualValue.SetElement(idx, static_cast(elements[idx]->getValue())); } instance = actualValue; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 65ef8e34af..c730df393a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -148,6 +148,8 @@ set(FILES Entity/EditorEntitySortBus.h Entity/EditorEntitySortComponent.cpp Entity/EditorEntitySortComponent.h + Entity/EditorEntitySortComponentSerializer.cpp + Entity/EditorEntitySortComponentSerializer.h Entity/EditorEntityTransformBus.h Entity/PrefabEditorEntityOwnershipInterface.h Entity/PrefabEditorEntityOwnershipService.h diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp index fdeaef93bb..5cade609d7 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp @@ -19,7 +19,7 @@ namespace O3DE::ProjectManager { // Attempt to use the Ninja build system if it is installed (described in the o3de documentation) if possible, // otherwise default to the the default for Linux (Unix Makefiles) - auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment()); + auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}); QString cmakeGenerator = (whichNinjaResult.IsSuccess()) ? "Ninja Multi-Config" : "Unix Makefiles"; bool compileProfileOnBuild = (whichNinjaResult.IsSuccess()); @@ -38,7 +38,7 @@ namespace O3DE::ProjectManager AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const { - auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment()); + auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}); bool compileProfileOnBuild = (whichNinjaResult.IsSuccess()); QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp index a7bd2dae08..cb8ea843e5 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp @@ -19,16 +19,15 @@ namespace O3DE::ProjectManager // The list of clang C/C++ compiler command lines to validate on the host Linux system const QStringList SupportedClangVersions = {"13", "12", "11", "10", "9", "8", "7", "6.0"}; - AZ::Outcome GetCommandLineProcessEnvironment() + AZ::Outcome SetupCommandLineProcessEnvironment() { - QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); - return AZ::Success(currentEnvironment); + return AZ::Success(); } AZ::Outcome FindSupportedCompilerForPlatform() { // Validate that cmake is installed and is in the command line - auto whichCMakeResult = ProjectUtils::ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, QProcessEnvironment::systemEnvironment()); + auto whichCMakeResult = ProjectUtils::ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}); if (!whichCMakeResult.IsSuccess()) { return AZ::Failure(QObject::tr("CMake not found.

" @@ -39,8 +38,8 @@ namespace O3DE::ProjectManager // Look for the first compatible version of clang. The list below will contain the known clang compilers that have been tested for O3DE. for (const QString& supportClangVersion : SupportedClangVersions) { - auto whichClangResult = ProjectUtils::ExecuteCommandResult("which", QStringList{QString("clang-%1").arg(supportClangVersion)}, QProcessEnvironment::systemEnvironment()); - auto whichClangPPResult = ProjectUtils::ExecuteCommandResult("which", QStringList{QString("clang++-%1").arg(supportClangVersion)}, QProcessEnvironment::systemEnvironment()); + auto whichClangResult = ProjectUtils::ExecuteCommandResult("which", QStringList{QString("clang-%1").arg(supportClangVersion)}); + auto whichClangPPResult = ProjectUtils::ExecuteCommandResult("which", QStringList{QString("clang++-%1").arg(supportClangVersion)}); if (whichClangResult.IsSuccess() && whichClangPPResult.IsSuccess()) { return AZ::Success(QString("clang-%1").arg(supportClangVersion)); @@ -54,7 +53,7 @@ namespace O3DE::ProjectManager AZ::Outcome OpenCMakeGUI(const QString& projectPath) { - AZ::Outcome processEnvResult = GetCommandLineProcessEnvironment(); + AZ::Outcome processEnvResult = SetupCommandLineProcessEnvironment(); if (!processEnvResult.IsSuccess()) { return AZ::Failure(processEnvResult.GetError()); @@ -68,7 +67,6 @@ namespace O3DE::ProjectManager } QProcess process; - process.setProcessEnvironment(processEnvResult.GetValue()); // if the project build path is relative, it should be relative to the project path process.setWorkingDirectory(projectPath); @@ -88,7 +86,6 @@ namespace O3DE::ProjectManager return ExecuteCommandResultModalDialog( QString("%1/python/get_python.sh").arg(engineRoot), {}, - QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp index ab412d84d8..2a8bbf4839 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp @@ -19,16 +19,14 @@ namespace O3DE::ProjectManager { AZ::Outcome QueryInstalledCmakeFullPath() { - auto environmentRequest = ProjectUtils::GetCommandLineProcessEnvironment(); + auto environmentRequest = ProjectUtils::SetupCommandLineProcessEnvironment(); if (!environmentRequest.IsSuccess()) { return AZ::Failure(environmentRequest.GetError()); } - auto currentEnvironment = environmentRequest.GetValue(); auto queryCmakeInstalled = ProjectUtils::ExecuteCommandResult("which", - QStringList{ProjectCMakeCommand}, - currentEnvironment); + QStringList{ProjectCMakeCommand}); if (!queryCmakeInstalled.IsSuccess()) { return AZ::Failure(QObject::tr("Unable to detect CMake on this host.")); diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp index 62011bf04b..07b08924f1 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp @@ -18,28 +18,36 @@ namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome GetCommandLineProcessEnvironment() + AZ::Outcome SetupCommandLineProcessEnvironment() { // For CMake on Mac, if its installed through home-brew, then it will be installed // under /usr/local/bin, which may not be in the system PATH environment. // Add that path for the command line process so that it will be able to locate // a home-brew installed version of CMake - QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); - QString pathValue = currentEnvironment.value("PATH"); - pathValue += ":/usr/local/bin"; - currentEnvironment.insert("PATH", pathValue); - return AZ::Success(currentEnvironment); + QString pathEnv = qEnvironmentVariable("PATH"); + QStringList pathEnvList = pathEnv.split(":"); + if (!pathEnvList.contains("/usr/local/bin")) + { + pathEnv += ":/usr/local/bin"; + if (!qputenv("PATH", pathEnv.toStdString().c_str())) + { + return AZ::Failure(QObject::tr("Failed to set PATH environment variable")); + } + } + + return AZ::Success(); } AZ::Outcome FindSupportedCompilerForPlatform() { - QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); - QString pathValue = currentEnvironment.value("PATH"); - pathValue += ":/usr/local/bin"; - currentEnvironment.insert("PATH", pathValue); + AZ::Outcome processEnvResult = SetupCommandLineProcessEnvironment(); + if (!processEnvResult.IsSuccess()) + { + return AZ::Failure(processEnvResult.GetError()); + } // Validate that we have cmake installed first - auto queryCmakeInstalled = ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, currentEnvironment); + auto queryCmakeInstalled = ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}); if (!queryCmakeInstalled.IsSuccess()) { return AZ::Failure(QObject::tr("Unable to detect CMake on this host.")); @@ -47,7 +55,7 @@ namespace O3DE::ProjectManager QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0]; // Query the version of the installed cmake - auto queryCmakeVersionQuery = ExecuteCommandResult(cmakeInstalledPath, QStringList{"-version"}, currentEnvironment); + auto queryCmakeVersionQuery = ExecuteCommandResult(cmakeInstalledPath, QStringList{"-version"}); if (!queryCmakeVersionQuery.IsSuccess()) { return AZ::Failure(QObject::tr("Unable to determine the version of CMake on this host.")); @@ -55,7 +63,7 @@ namespace O3DE::ProjectManager AZ_TracePrintf("Project Manager", "Cmake version %s detected.", queryCmakeVersionQuery.GetValue().split("\n")[0].toUtf8().constData()); // Query for the version of xcodebuild (if installed) - auto queryXcodeBuildVersion = ExecuteCommandResult("xcodebuild", QStringList{"-version"}, currentEnvironment); + auto queryXcodeBuildVersion = ExecuteCommandResult("xcodebuild", QStringList{"-version"}); if (!queryCmakeInstalled.IsSuccess()) { return AZ::Failure(QObject::tr("Unable to detect XCodeBuilder on this host.")); @@ -104,7 +112,6 @@ namespace O3DE::ProjectManager return ExecuteCommandResultModalDialog( QString("%1/python/get_python.sh").arg(engineRoot), {}, - QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp index d08da1d5e1..b8cfe65b1e 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp @@ -21,7 +21,7 @@ namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome GetCommandLineProcessEnvironment() + AZ::Outcome SetupCommandLineProcessEnvironment() { // Use the engine path to insert a path for cmake auto engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); @@ -31,26 +31,34 @@ namespace O3DE::ProjectManager } auto engineInfo = engineInfoResult.GetValue(); - QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); - - // Append cmake path to PATH incase it is missing + // Append cmake path to the current environment PATH incase it is missing, since if + // we are starting CMake itself the current application needs to find it using Path + // This also takes affect for all child processes. QDir cmakePath(engineInfo.m_path); cmakePath.cd("cmake/runtime/bin"); - QString pathValue = currentEnvironment.value("PATH"); - pathValue += ";" + cmakePath.path(); - currentEnvironment.insert("PATH", pathValue); - return AZ::Success(currentEnvironment); + QString pathEnv = qEnvironmentVariable("Path"); + QStringList pathEnvList = pathEnv.split(";"); + if (!pathEnvList.contains(cmakePath.path())) + { + pathEnv += ";" + cmakePath.path(); + if (!qputenv("Path", pathEnv.toStdString().c_str())) + { + return AZ::Failure(QObject::tr("Failed to set Path environment variable")); + } + } + + return AZ::Success(); } AZ::Outcome FindSupportedCompilerForPlatform() { // Validate that cmake is installed - auto cmakeProcessEnvResult = GetCommandLineProcessEnvironment(); + auto cmakeProcessEnvResult = SetupCommandLineProcessEnvironment(); if (!cmakeProcessEnvResult.IsSuccess()) { return AZ::Failure(cmakeProcessEnvResult.GetError()); } - auto cmakeVersionQueryResult = ExecuteCommandResult("cmake", QStringList{"--version"}, cmakeProcessEnvResult.GetValue()); + auto cmakeVersionQueryResult = ExecuteCommandResult("cmake", QStringList{"--version"}); if (!cmakeVersionQueryResult.IsSuccess()) { return AZ::Failure(QObject::tr("CMake not found. \n\n" @@ -104,7 +112,7 @@ namespace O3DE::ProjectManager AZ::Outcome OpenCMakeGUI(const QString& projectPath) { - AZ::Outcome processEnvResult = GetCommandLineProcessEnvironment(); + AZ::Outcome processEnvResult = SetupCommandLineProcessEnvironment(); if (!processEnvResult.IsSuccess()) { return AZ::Failure(processEnvResult.GetError()); @@ -118,7 +126,6 @@ namespace O3DE::ProjectManager } QProcess process; - process.setProcessEnvironment(processEnvResult.GetValue()); // if the project build path is relative, it should be relative to the project path process.setWorkingDirectory(projectPath); @@ -139,7 +146,6 @@ namespace O3DE::ProjectManager return ExecuteCommandResultModalDialog( "cmd.exe", QStringList{"/c", batPath}, - QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } @@ -157,7 +163,7 @@ namespace O3DE::ProjectManager .arg(shortcutPath) .arg(targetPath) .arg(arguments.join(' ')); - auto createShortcutResult = ExecuteCommandResult(cmd, QStringList{"-Command", arg}, QProcessEnvironment::systemEnvironment()); + auto createShortcutResult = ExecuteCommandResult(cmd, QStringList{"-Command", arg}); if (!createShortcutResult.IsSuccess()) { return AZ::Failure(QObject::tr("Failed to create desktop shortcut %1

" diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp index c6a6b20a1d..7ec691fd81 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp @@ -117,18 +117,16 @@ namespace O3DE::ProjectManager // Show some kind of progress with very approximate estimates UpdateProgress(++m_progressEstimate); - auto currentEnvironmentRequest = ProjectUtils::GetCommandLineProcessEnvironment(); + auto currentEnvironmentRequest = ProjectUtils::SetupCommandLineProcessEnvironment(); if (!currentEnvironmentRequest.IsSuccess()) { QStringToAZTracePrint(currentEnvironmentRequest.GetError()); return AZ::Failure(currentEnvironmentRequest.GetError()); } - QProcessEnvironment currentEnvironment = currentEnvironmentRequest.GetValue(); m_configProjectProcess = new QProcess(this); m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels); m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path); - m_configProjectProcess->setProcessEnvironment(currentEnvironment); auto cmakeGenerateArgumentsResult = ConstructCmakeGenerateProjectArguments(engineInfo.m_thirdPartyPath); if (!cmakeGenerateArgumentsResult.IsSuccess()) @@ -181,7 +179,6 @@ namespace O3DE::ProjectManager m_buildProjectProcess = new QProcess(this); m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels); m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path); - m_buildProjectProcess->setProcessEnvironment(currentEnvironment); auto cmakeBuildArgumentsResult = ConstructCmakeBuildCommandArguments(); if (!cmakeBuildArgumentsResult.IsSuccess()) diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index 4a0e1c153c..b7748d8aa2 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -520,12 +520,10 @@ namespace O3DE::ProjectManager AZ::Outcome ExecuteCommandResultModalDialog( const QString& cmd, const QStringList& arguments, - const QProcessEnvironment& processEnv, const QString& title) { QString resultOutput; QProcess execProcess; - execProcess.setProcessEnvironment(processEnv); execProcess.setProcessChannelMode(QProcess::MergedChannels); QProgressDialog dialog(title, QObject::tr("Cancel"), /*minimum=*/0, /*maximum=*/0); @@ -611,11 +609,9 @@ namespace O3DE::ProjectManager AZ::Outcome ExecuteCommandResult( const QString& cmd, const QStringList& arguments, - const QProcessEnvironment& processEnv, int commandTimeoutSeconds /*= ProjectCommandLineTimeoutSeconds*/) { QProcess execProcess; - execProcess.setProcessEnvironment(processEnv); execProcess.setProcessChannelMode(QProcess::MergedChannels); execProcess.start(cmd, arguments); if (!execProcess.waitForStarted()) diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index ee605b5117..d84b367d5b 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -47,7 +47,6 @@ namespace O3DE::ProjectManager AZ::Outcome ExecuteCommandResult( const QString& cmd, const QStringList& arguments, - const QProcessEnvironment& processEnv, int commandTimeoutSeconds = ProjectCommandLineTimeoutSeconds); /** @@ -61,10 +60,9 @@ namespace O3DE::ProjectManager AZ::Outcome ExecuteCommandResultModalDialog( const QString& cmd, const QStringList& arguments, - const QProcessEnvironment& processEnv, const QString& title); - AZ::Outcome GetCommandLineProcessEnvironment(); + AZ::Outcome SetupCommandLineProcessEnvironment(); AZ::Outcome GetProjectBuildPath(const QString& projectPath); AZ::Outcome OpenCMakeGUI(const QString& projectPath); AZ::Outcome RunGetPythonScript(const QString& enginePath); diff --git a/Gems/AWSCore/Code/CMakeLists.txt b/Gems/AWSCore/Code/CMakeLists.txt index 624f61d88a..be7d975016 100644 --- a/Gems/AWSCore/Code/CMakeLists.txt +++ b/Gems/AWSCore/Code/CMakeLists.txt @@ -60,6 +60,9 @@ ly_create_alias( ) if (PAL_TRAIT_BUILD_HOST_TOOLS) + + include(${CMAKE_CURRENT_SOURCE_DIR}/Platform/${PAL_PLATFORM_NAME}/PAL_traits_editor_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + ly_add_target( NAME AWSCore.Editor.Static STATIC NAMESPACE Gem @@ -97,22 +100,31 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AWSCore.Editor.Static ) - # This target is not a real gem module - # It is not meant to be loaded by the ModuleManager in C++ - ly_add_target( - NAME AWSCore.ResourceMappingTool MODULE - NAMESPACE Gem - OUTPUT_SUBDIRECTORY AWSCoreEditorQtBin - FILES_CMAKE - awscore_resourcemappingtool_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Include/Private - BUILD_DEPENDENCIES - PRIVATE - Gem::AWSCore.Editor.Static - ) - ly_add_dependencies(AWSCore.Editor AWSCore.ResourceMappingTool) + if (PAL_TRAIT_ENABLE_RESOURCE_MAPPING_TOOL) + + # This target is not a real gem module + # It is not meant to be loaded by the ModuleManager in C++ + ly_add_target( + NAME AWSCore.ResourceMappingTool MODULE + NAMESPACE Gem + OUTPUT_SUBDIRECTORY AWSCoreEditorQtBin + FILES_CMAKE + awscore_resourcemappingtool_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Include/Private + BUILD_DEPENDENCIES + PRIVATE + Gem::AWSCore.Editor.Static + RUNTIME_DEPENDENCIES + 3rdParty::pyside2 + + ) + ly_add_dependencies(AWSCore.Editor AWSCore.ResourceMappingTool) + + ly_install_directory(DIRECTORIES Tools/ResourceMappingTool) + + endif() # Builders and Tools (such as the Editor use AWSCore.Editor) use the .Editor module above. ly_create_alias( diff --git a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h index 1a4c428e68..a065773743 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h @@ -13,6 +13,8 @@ #include #include +#include "AWSCoreEditor_Traits_Platform.h" + namespace AWSCore { class AWSCoreResourceMappingToolAction @@ -22,7 +24,7 @@ namespace AWSCore static constexpr const char AWSCoreResourceMappingToolActionName[] = "AWSCoreResourceMappingToolAction"; static constexpr const char ResourceMappingToolDirectoryPath[] = "Gems/AWSCore/Code/Tools/ResourceMappingTool"; static constexpr const char ResourceMappingToolLogDirectoryPath[] = "user/log/"; - static constexpr const char EngineWindowsPythonEntryScriptPath[] = "python/python.cmd"; + static constexpr const char EngineWindowsPythonEntryScriptPath[] = AWSCORE_EDITOR_PYTHON_COMMAND; AWSCoreResourceMappingToolAction(const QString& text, QObject* parent = nullptr); diff --git a/Gems/AWSCore/Code/Platform/Linux/AWSCoreEditor_Traits_Linux.h b/Gems/AWSCore/Code/Platform/Linux/AWSCoreEditor_Traits_Linux.h index fb82911dd4..726d4cc86f 100644 --- a/Gems/AWSCore/Code/Platform/Linux/AWSCoreEditor_Traits_Linux.h +++ b/Gems/AWSCore/Code/Platform/Linux/AWSCoreEditor_Traits_Linux.h @@ -7,4 +7,6 @@ */ #pragma once -#define AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED 0 +#define AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED 1 +#define AWSCORE_EDITOR_PYTHON_DEBUG_ARGUMENT "" +#define AWSCORE_EDITOR_PYTHON_COMMAND "python/python.sh" diff --git a/Gems/AWSCore/Code/Platform/Linux/PAL_traits_editor_linux.cmake b/Gems/AWSCore/Code/Platform/Linux/PAL_traits_editor_linux.cmake new file mode 100644 index 0000000000..deaaa60506 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Linux/PAL_traits_editor_linux.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(PAL_TRAIT_ENABLE_RESOURCE_MAPPING_TOOL TRUE) diff --git a/Gems/AWSCore/Code/Platform/Mac/AWSCoreEditor_Traits_Mac.h b/Gems/AWSCore/Code/Platform/Mac/AWSCoreEditor_Traits_Mac.h index fb82911dd4..d815c8273e 100644 --- a/Gems/AWSCore/Code/Platform/Mac/AWSCoreEditor_Traits_Mac.h +++ b/Gems/AWSCore/Code/Platform/Mac/AWSCoreEditor_Traits_Mac.h @@ -8,3 +8,5 @@ #pragma once #define AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED 0 +#define AWSCORE_EDITOR_PYTHON_DEBUG_ARGUMENT "" +#define AWSCORE_EDITOR_PYTHON_COMMAND "python/python.sh" diff --git a/Gems/AWSCore/Code/Platform/Mac/PAL_traits_editor_mac.cmake b/Gems/AWSCore/Code/Platform/Mac/PAL_traits_editor_mac.cmake new file mode 100644 index 0000000000..e953c95955 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Mac/PAL_traits_editor_mac.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(PAL_TRAIT_ENABLE_RESOURCE_MAPPING_TOOL FALSE) diff --git a/Gems/AWSCore/Code/Platform/Windows/AWSCoreEditor_Traits_Windows.h b/Gems/AWSCore/Code/Platform/Windows/AWSCoreEditor_Traits_Windows.h index e1522db32c..6eca30a8ac 100644 --- a/Gems/AWSCore/Code/Platform/Windows/AWSCoreEditor_Traits_Windows.h +++ b/Gems/AWSCore/Code/Platform/Windows/AWSCoreEditor_Traits_Windows.h @@ -8,3 +8,5 @@ #pragma once #define AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED 1 +#define AWSCORE_EDITOR_PYTHON_DEBUG_ARGUMENT "debug " +#define AWSCORE_EDITOR_PYTHON_COMMAND "python/python.cmd" diff --git a/Gems/AWSCore/Code/Platform/Windows/PAL_traits_editor_windows.cmake b/Gems/AWSCore/Code/Platform/Windows/PAL_traits_editor_windows.cmake new file mode 100644 index 0000000000..deaaa60506 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Windows/PAL_traits_editor_windows.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(PAL_TRAIT_ENABLE_RESOURCE_MAPPING_TOOL TRUE) diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp index 858d30fa40..49f800dbea 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp @@ -58,7 +58,7 @@ namespace AWSCore if (m_isDebug) { return AZStd::string::format( - "\"%s\" debug -B \"%s\" --binaries-path \"%s\" --debug --profile \"%s\" --config-path \"%s\" --log-path \"%s\"", + "\"%s\" " AWSCORE_EDITOR_PYTHON_DEBUG_ARGUMENT "-B \"%s\" --binaries-path \"%s\" --debug --profile \"%s\" --config-path \"%s\" --log-path \"%s\"", m_enginePythonEntryPath.c_str(), m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str(), profileName.c_str(), m_toolConfigDirectoryPath.c_str(), m_toolLogDirectoryPath.c_str()); } diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/README.md b/Gems/AWSCore/Code/Tools/ResourceMappingTool/README.md index 38b7c48d2f..ba764dc993 100644 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/README.md +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/README.md @@ -39,6 +39,16 @@ Follow cmake instructions to configure your project, for example: ``` $ python\python.cmd debug Gems\AWSCore\Code\Tools\ResourceMappingTool\resource_mapping_tool.py --binaries_path \bin\debug\AWSCoreEditorQtBin ``` + * Linux + * release mode + ``` + $ python/python.sh Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py --binaries_path /bin/profile/AWSCoreEditorQtBin + ``` + * debug mode + ``` + $ python/python.sh Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py --binaries_path /bin/debug/AWSCoreEditorQtBin + ``` + * Note - Editor is integrated with the same engine python environment to launch Resource Mapping Tool. If it is failed to launch the tool in Editor, please follow above steps to make sure expected scripts/binaries are present. diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py index 7424854d2a..a3283cbb28 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py @@ -21,6 +21,7 @@ argument_parser.add_argument('--debug', action='store_true', help='Execute on de argument_parser.add_argument('--log-path', help='Path to resource mapping tool logging directory ' '(if not provided, logging file will be located at tool directory)') argument_parser.add_argument('--profile', default='default', help='Named AWS profile to use for querying AWS resources') + arguments: Namespace = argument_parser.parse_args() # logging setup diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_environment_utils.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_environment_utils.py index 756d6fdb10..3d36a56468 100644 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_environment_utils.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_environment_utils.py @@ -5,6 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ +import platform from typing import List from unittest import TestCase from unittest.mock import (ANY, call, MagicMock, patch) @@ -27,14 +28,24 @@ class TestEnvironmentUtils(TestCase): self.addCleanup(os_pathsep_patcher.stop) self._mock_os_pathsep: MagicMock = os_pathsep_patcher.start() - def test_setup_qt_environment_global_flag_is_set(self) -> None: + @patch('os.path.exists') + @patch('ctypes.CDLL') + def test_setup_qt_environment_global_flag_is_set(self, mock_os_path_exists, mock_ctype_cdll) -> None: + mock_os_path_exists.return_value = True environment_utils.setup_qt_environment("dummy") self._mock_os_environ.copy.assert_called_once() self._mock_os_pathsep.join.assert_called_once() assert environment_utils.is_qt_linked() is True + if platform.system() == 'Linux': + mock_os_path_exists.assert_called() - def test_cleanup_qt_environment_global_flag_is_set(self) -> None: + @patch('os.path.exists') + @patch('ctypes.CDLL') + def test_cleanup_qt_environment_global_flag_is_set(self, mock_os_path_exists, mock_ctype_cdll) -> None: + mock_os_path_exists.return_value = True environment_utils.setup_qt_environment("dummy") assert environment_utils.is_qt_linked() is True environment_utils.cleanup_qt_environment() assert environment_utils.is_qt_linked() is False + if platform.system() == 'Linux': + mock_os_path_exists.assert_called() diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/environment_utils.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/environment_utils.py index 8600fe31b5..b67f64e715 100644 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/environment_utils.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/environment_utils.py @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT import logging import os +import platform from typing import Dict from utils import file_utils @@ -38,6 +39,20 @@ def setup_qt_environment(bin_path: str) -> None: new_path = os.pathsep.join([binaries_path, path]) os.environ['PATH'] = new_path + # On Linux, we need to load pyside2 and related modules as well + if platform.system() == 'Linux': + import ctypes + + preload_shared_libs = [f'{bin_path}/libpyside2.abi3.so.5.14', + f'{bin_path}/libQt5Widgets.so.5'] + + for preload_shared_lib in preload_shared_libs: + if not os.path.exists(preload_shared_lib): + logger.error(f"Cannot find required shared library at {preload_shared_lib}") + return + else: + ctypes.CDLL(preload_shared_lib) + global qt_binaries_linked qt_binaries_linked = True diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp index 17ed4d9c0c..bcfedad8a8 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp @@ -8,12 +8,16 @@ #include #include +#include #include #include -#include +#include +#include +#include #include +#include #include -#include +#include namespace AZ::Prefab { @@ -21,6 +25,7 @@ namespace AZ::Prefab class PrefabGroupAssetHandler::AssetTypeInfoHandler final : public AZ::AssetTypeInfoBus::Handler + , protected AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler { public: AZ_CLASS_ALLOCATOR(AssetTypeInfoHandler, AZ::SystemAllocator, 0); @@ -31,15 +36,21 @@ namespace AZ::Prefab const char* GetGroup() const override; const char* GetBrowserIcon() const override; void GetAssetTypeExtensions(AZStd::vector& extensions) override; + + // AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler + void AddContextMenuActions(QWidget* caller, QMenu* menu, const AZStd::vector& entries) override; + bool SaveAsAuthoredPrefab(const AZ::Data::AssetId& assetId, const char* destinationFilename); }; PrefabGroupAssetHandler::AssetTypeInfoHandler::AssetTypeInfoHandler() { AZ::AssetTypeInfoBus::Handler::BusConnect(azrtti_typeid()); + AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusConnect(); } PrefabGroupAssetHandler::AssetTypeInfoHandler::~AssetTypeInfoHandler() { + AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusDisconnect(); AZ::AssetTypeInfoBus::Handler::BusDisconnect(azrtti_typeid()); } @@ -68,6 +79,84 @@ namespace AZ::Prefab extensions.push_back(PrefabGroupAssetHandler::s_Extension); } + void PrefabGroupAssetHandler::AssetTypeInfoHandler::AddContextMenuActions( + [[maybe_unused]] QWidget* caller, + QMenu* menu, + const AZStd::vector& entries) + { + using namespace AzToolsFramework::AssetBrowser; + auto entryIt = AZStd::find_if + ( + entries.begin(), + entries.end(), + [](const AssetBrowserEntry* entry) -> bool + { + return entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product; + } + ); + + if (entryIt == entries.end()) + { + return; + } + else if ((*entryIt)->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product) + { + ProductAssetBrowserEntry* product = azrtti_cast(*entryIt); + if (product->GetAssetType() == azrtti_typeid()) + { + AZ::Data::AssetId assetId = product->GetAssetId(); + menu->addAction("Save as Prefab...", [assetId, this]() + { + QString filePath = AzQtComponents::FileDialog::GetSaveFileName(nullptr, QString("Save to file"), "", QString("Prefab file (*.prefab)")); + if (filePath.isEmpty()) + { + return; + } + if (SaveAsAuthoredPrefab(assetId, filePath.toUtf8().data())) + { + AZ_Printf("Prefab", "Prefab was saved to a .prefab file %s", filePath.toUtf8().data()); + } + }); + } + } + } + + bool PrefabGroupAssetHandler::AssetTypeInfoHandler::SaveAsAuthoredPrefab(const AZ::Data::AssetId& assetId, const char* destinationFilename) + { + using namespace AzToolsFramework::Prefab; + using namespace AZ::Data; + + auto procPrefabAsset = AssetManager::Instance().GetAsset(assetId, AssetLoadBehavior::Default); + const auto status = AssetManager::Instance().BlockUntilLoadComplete(procPrefabAsset); + if (status != AssetData::AssetStatus::Ready) + { + return false; + } + + auto* prefabLoaderInterface = AZ::Interface::Get(); + if (!prefabLoaderInterface) + { + return false; + } + + const auto templateId = procPrefabAsset.GetAs()->GetTemplateId(); + AZStd::string outputJson; + if (prefabLoaderInterface->SaveTemplateToString(templateId, outputJson) == false) + { + return false; + } + + const auto fileMode = AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeText; + AZ::IO::FileIOStream outputFileStream; + if (outputFileStream.Open(destinationFilename, fileMode) == false) + { + return false; + } + + outputFileStream.Write(outputJson.size(), outputJson.data()); + return true; + } + // PrefabGroupAssetHandler AZStd::string_view PrefabGroupAssetHandler::s_Extension{ "procprefab" }; diff --git a/Gems/ScriptEvents/Code/Include/ScriptEvents/Components/ScriptEventReferencesComponent.cpp b/Gems/ScriptEvents/Code/Include/ScriptEvents/Components/ScriptEventReferencesComponent.cpp deleted file mode 100644 index 696d2af4d2..0000000000 --- a/Gems/ScriptEvents/Code/Include/ScriptEvents/Components/ScriptEventReferencesComponent.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include "ScriptEventReferencesComponent.h" - -namespace ScriptEvents -{ - namespace Components - { - void ScriptEventReferencesComponent::Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) - { - // The Script Event References component is no longer necessary, as all Script Event assets - // will be properly loaded as needed. - serializeContext->ClassDeprecate("ScriptEventReferencesComponent", "{D0F440AC-32D4-49EC-8B93-860B188266A6}"); - } - } - - void ScriptEventReferencesComponent::Activate() - { - for (auto& scriptEventReferences : m_scriptEventAssets) - { - const auto& asset = scriptEventReferences.GetAsset(); - if (asset) - { - if (!AZ::Data::AssetBus::MultiHandler::BusIsConnectedId(asset.GetId())) - { - AZ::Data::AssetBus::MultiHandler::BusConnect(asset.GetId()); - } - - // Load the asset if it's not ready - if (!asset.IsReady()) - { - AZ::Data::AssetInfo assetInfo; - AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, asset.GetId()); - if (assetInfo.m_assetId.IsValid()) - { - AZ::Data::AssetManager::Instance().GetAsset(asset.GetId(), azrtti_typeid(), AZ::Data::AssetLoadBehavior::Default) - .BlockUntilLoadComplete(); - } - } - } - else - { - AZ_Warning("Script Events", false, "ScriptEventReferencesComponent could not find Script Event asset: %s", scriptEventReferences.GetDefinition() ? scriptEventReferences.GetDefinition()->GetName().c_str() : scriptEventReferences.GetAsset().GetId().ToString().c_str()); - } - } - } - - void ScriptEventReferencesComponent::Deactivate() - { - for (auto& scriptEventReferences : m_scriptEventAssets) - { - const auto& asset = scriptEventReferences.GetAsset(); - if (asset) - { - AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); - } - } - } - - void ScriptEventReferencesComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC("ScriptEventReference", 0x3df92d40)); - } - - void ScriptEventReferencesComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) - { - incompatible.push_back(AZ_CRC("ScriptEventReference", 0x3df92d40)); - } - - void ScriptEventReferencesComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) - { - dependent.push_back(AZ_CRC("LuaScriptService", 0x21d76c4b)); - } - - void ScriptEventReferencesComponent::OnAssetReady(AZ::Data::Asset asset) - { - if (ScriptEventsAsset* scriptEventAsset = asset.GetAs()) - { - scriptEventAsset->m_definition.RegisterInternal(); - } - } - - } -} diff --git a/Gems/ScriptEvents/Code/Include/ScriptEvents/Components/ScriptEventReferencesComponent.h b/Gems/ScriptEvents/Code/Include/ScriptEvents/Components/ScriptEventReferencesComponent.h deleted file mode 100644 index 562cab9b52..0000000000 --- a/Gems/ScriptEvents/Code/Include/ScriptEvents/Components/ScriptEventReferencesComponent.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include - -namespace ScriptEvents -{ - namespace Components - { - class ScriptEventReferencesComponent - : public AZ::Component - , private AZ::Data::AssetBus::MultiHandler - - { - public: - - AZ_COMPONENT(ScriptEventReferencesComponent, "{D0F440AC-32D4-49EC-8B93-860B188266A6}", AZ::Component); - - ////////////////////////////////////////////////////////////////////////// - // AZ::Component - void Init() override {} - void Activate() override; - void Deactivate() override; - ////////////////////////////////////////////////////////////////////////// - - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); - - void OnAssetReady(AZ::Data::Asset asset) override; - static void Reflect(AZ::ReflectContext* reflection); - - AZStd::vector m_scriptEventAssets; - }; - } -} diff --git a/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsEditorGem.cpp b/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsEditorGem.cpp index 8ab014ec15..3dec167f73 100644 --- a/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsEditorGem.cpp +++ b/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsEditorGem.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -74,7 +73,6 @@ namespace ScriptEvents m_descriptors.insert(m_descriptors.end(), { ScriptEventsEditor::ScriptEventEditorSystemComponent::CreateDescriptor(), - ScriptEvents::Components::ScriptEventReferencesComponent::CreateDescriptor(), ScriptEventsBuilder::ScriptEventsBuilderComponent::CreateDescriptor(), }); } diff --git a/Gems/ScriptEvents/Code/Source/ScriptEventsGem.cpp b/Gems/ScriptEvents/Code/Source/ScriptEventsGem.cpp index e4fb44f548..bc46993649 100644 --- a/Gems/ScriptEvents/Code/Source/ScriptEventsGem.cpp +++ b/Gems/ScriptEvents/Code/Source/ScriptEventsGem.cpp @@ -12,8 +12,6 @@ #include -#include - namespace ScriptEvents { ScriptEventsModule::ScriptEventsModule() @@ -23,8 +21,7 @@ namespace ScriptEvents ScriptEventModuleConfigurationRequestBus::Handler::BusConnect(); m_descriptors.insert(m_descriptors.end(), { - ScriptEvents::ScriptEventsSystemComponent::CreateDescriptor(), - ScriptEvents::Components::ScriptEventReferencesComponent::CreateDescriptor(), + ScriptEvents::ScriptEventsSystemComponent::CreateDescriptor() }); } diff --git a/Gems/ScriptEvents/Code/scriptevents_common_files.cmake b/Gems/ScriptEvents/Code/scriptevents_common_files.cmake index a7ecd9e049..a038bb2c90 100644 --- a/Gems/ScriptEvents/Code/scriptevents_common_files.cmake +++ b/Gems/ScriptEvents/Code/scriptevents_common_files.cmake @@ -42,6 +42,4 @@ set(FILES Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventsBindingBus.h Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBinding.h Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBinding.cpp - Include/ScriptEvents/Components/ScriptEventReferencesComponent.h - Include/ScriptEvents/Components/ScriptEventReferencesComponent.cpp ) diff --git a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material index 7cf249a10d..53f6fd5b9e 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material +++ b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material @@ -2,5 +2,10 @@ "description": "", "materialType": "PbrTerrain.materialtype", "parentMaterial": "", - "propertyLayoutVersion": 1 + "propertyLayoutVersion": 1, + "properties": { + "baseColor": { + "color": [ 0.18, 0.18, 0.18 ] + } + } } diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype index a20ac23e17..1e7305cc11 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype @@ -88,6 +88,19 @@ } } ], + "baseColor": [ + { + "name": "color", + "displayName": "Color", + "description": "Color is displayed as sRGB but the values are stored as linear color.", + "type": "Color", + "defaultValue": [ 1.0, 1.0, 1.0 ], + "connection": { + "type": "ShaderInput", + "name": "m_baseColor" + } + } + ], "settings": [ { "id": "detailTextureMultiplier", diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli index f5af598435..770f877ea8 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli @@ -93,10 +93,6 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial { - float m_detailTextureMultiplier; - float m_detailFadeDistance; - float m_detailFadeLength; - Sampler m_sampler { AddressU = Wrap; @@ -109,22 +105,11 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial // Base Color float3 m_baseColor; - float m_baseColorFactor; - Texture2D m_baseColorMap; - // Normal - Texture2D m_normalMap; - bool m_flipNormalX; - bool m_flipNormalY; - float m_normalFactor; - - // Roughness - Texture2D m_roughnessMap; - float m_roughnessFactor; - - // Specular - Texture2D m_specularF0Map; - float m_specularF0Factor; + // Detail Material Properties + float m_detailTextureMultiplier; + float m_detailFadeDistance; + float m_detailFadeLength; } option bool o_useTerrainSmoothing = false; diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli index e5d5ff688d..33c817c0f9 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli @@ -57,7 +57,30 @@ DetailSurface GetDefaultDetailSurface() return surface; } +void WeightDetailSurface(inout DetailSurface surface, in float weight) +{ + surface.m_color *= weight; + surface.m_normal *= weight; + surface.m_roughness *= weight; + surface.m_specularF0 *= weight; + surface.m_metalness *= weight; + surface.m_occlusion *= weight; + surface.m_height *= weight; +} + +void AddDetailSurface(inout DetailSurface surface, in DetailSurface surfaceToAdd) +{ + surface.m_color += surfaceToAdd.m_color; + surface.m_normal += surfaceToAdd.m_normal; + surface.m_roughness += surfaceToAdd.m_roughness; + surface.m_specularF0 += surfaceToAdd.m_specularF0; + surface.m_metalness += surfaceToAdd.m_metalness; + surface.m_occlusion += surfaceToAdd.m_occlusion; + surface.m_height += surfaceToAdd.m_height; +} + // Detail material index getters + uint GetDetailColorIndex(TerrainSrg::DetailMaterialData materialData) { return materialData.m_colorNormalImageIndices & 0x0000FFFF; @@ -95,22 +118,22 @@ uint GetDetailHeightIndex(TerrainSrg::DetailMaterialData materialData) // Detail material value getters -float3 GetDetailColor(TerrainSrg::DetailMaterialData materialData, float2 uv) +float3 GetDetailColor(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) { float3 color = materialData.m_baseColor; if ((materialData.m_flags & DetailTextureFlags::UseTextureBaseColor) > 0) { - color = TerrainSrg::m_detailTextures[GetDetailColorIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).rgb; + color = TerrainSrg::m_detailTextures[GetDetailColorIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rgb; } return color * materialData.m_baseColorFactor; } -float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv) +float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) { float2 normal = float2(0.0, 0.0); if ((materialData.m_flags & DetailTextureFlags::UseTextureNormal) > 0) { - normal = TerrainSrg::m_detailTextures[GetDetailNormalIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).rg; + normal = TerrainSrg::m_detailTextures[GetDetailNormalIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rg; } // X and Y are inverted here to be consistent with SampleNormalXY in NormalInput.azsli. @@ -125,53 +148,53 @@ float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv) return GetTangentSpaceNormal(normal, materialData.m_normalFactor); } -float GetDetailRoughness(TerrainSrg::DetailMaterialData materialData, float2 uv) +float GetDetailRoughness(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) { float roughness = materialData.m_roughnessScale; if ((materialData.m_flags & DetailTextureFlags::UseTextureRoughness) > 0) { - roughness = TerrainSrg::m_detailTextures[GetDetailRoughnessIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + roughness = TerrainSrg::m_detailTextures[GetDetailRoughnessIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r; roughness = materialData.m_roughnessBias + roughness * materialData.m_roughnessScale; } return roughness; } -float GetDetailMetalness(TerrainSrg::DetailMaterialData materialData, float2 uv) +float GetDetailMetalness(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) { float metalness = 1.0; if ((materialData.m_flags & DetailTextureFlags::UseTextureMetallic) > 0) { - metalness = TerrainSrg::m_detailTextures[GetDetailMetalnessIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + metalness = TerrainSrg::m_detailTextures[GetDetailMetalnessIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r; } return metalness * materialData.m_metalFactor; } -float GetDetailSpecularF0(TerrainSrg::DetailMaterialData materialData, float2 uv) +float GetDetailSpecularF0(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) { float specularF0 = 1.0; if ((materialData.m_flags & DetailTextureFlags::UseTextureSpecularF0) > 0) { - specularF0 = TerrainSrg::m_detailTextures[GetDetailSpecularF0Index(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + specularF0 = TerrainSrg::m_detailTextures[GetDetailSpecularF0Index(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r; } return specularF0 * materialData.m_specularF0Factor; } -float GetDetailOcclusion(TerrainSrg::DetailMaterialData materialData, float2 uv) +float GetDetailOcclusion(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) { float occlusion = 1.0; if ((materialData.m_flags & DetailTextureFlags::UseTextureOcclusion) > 0) { - occlusion = TerrainSrg::m_detailTextures[GetDetailOcclusionIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + occlusion = TerrainSrg::m_detailTextures[GetDetailOcclusionIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r; } return occlusion * materialData.m_occlusionFactor; } -float GetDetailHeight(TerrainSrg::DetailMaterialData materialData, float2 uv) +float GetDetailHeight(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) { float height = materialData.m_heightFactor; if ((materialData.m_flags & DetailTextureFlags::UseTextureHeight) > 0) { - height = TerrainSrg::m_detailTextures[GetDetailHeightIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + height = TerrainSrg::m_detailTextures[GetDetailHeightIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r; height = materialData.m_heightOffset + height * materialData.m_heightFactor; } return height; @@ -181,15 +204,19 @@ void GetDetailSurfaceForMaterial(inout DetailSurface surface, uint materialId, f { TerrainSrg::DetailMaterialData detailMaterialData = TerrainSrg::m_detailMaterialData[materialId]; - surface.m_color = GetDetailColor(detailMaterialData, uv); - surface.m_normal = GetDetailNormal(detailMaterialData, uv); - surface.m_roughness = GetDetailRoughness(detailMaterialData, uv); - surface.m_specularF0 = GetDetailSpecularF0(detailMaterialData, uv); - surface.m_metalness = GetDetailMetalness(detailMaterialData, uv); - surface.m_occlusion = GetDetailOcclusion(detailMaterialData, uv); - surface.m_height = GetDetailHeight(detailMaterialData, uv); + float2 uvDdx = ddx(uv); + float2 uvDdy = ddy(uv); + + surface.m_color = GetDetailColor(detailMaterialData, uv, uvDdx, uvDdy); + surface.m_normal = GetDetailNormal(detailMaterialData, uv, uvDdx, uvDdy); + surface.m_roughness = GetDetailRoughness(detailMaterialData, uv, uvDdx, uvDdy); + surface.m_specularF0 = GetDetailSpecularF0(detailMaterialData, uv, uvDdx, uvDdy); + surface.m_metalness = GetDetailMetalness(detailMaterialData, uv, uvDdx, uvDdy); + surface.m_occlusion = GetDetailOcclusion(detailMaterialData, uv, uvDdx, uvDdy); + surface.m_height = GetDetailHeight(detailMaterialData, uv, uvDdx, uvDdy); } +// Debugs the detail material by choosing a random color per material ID and rendering it without blending. void GetDebugDetailSurface(inout DetailSurface surface, uint material1, uint material2, float blend, float2 idUv) { float3 material1Color = float3(0.1, 0.1, 0.1); @@ -210,7 +237,7 @@ void GetDebugDetailSurface(inout DetailSurface surface, uint material1, uint mat surface.m_color = lerp(material1Color, material2Color, blend); float seamBlend = 0.0; const float halfLineWidth = 1.0 / 2048.0; - if (any(abs(idUv) % 1.0 < halfLineWidth) || any(abs(idUv) % 1.0 > 1.0 - halfLineWidth)) + if (any(frac(abs(idUv)) < halfLineWidth) || any(frac(abs(idUv)) > 1.0 - halfLineWidth)) { seamBlend = 1.0; } @@ -225,26 +252,114 @@ void GetDebugDetailSurface(inout DetailSurface surface, uint material1, uint mat surface.m_height = 0.5; } -bool GetDetailSurface(inout DetailSurface surface, float2 idUv, float2 uv) +//Blend a single detail material sample (with two possible material ids) onto a DetailSurface. +void BlendDetailMaterial(inout DetailSurface surface, uint material1, uint material2, float blend, float2 detailUv, float weight) { - uint4 material1 = TerrainSrg::m_detailMaterialIdImage.GatherRed(TerrainSrg::DetailSampler, idUv, 0).xyzw; - uint4 material2 = TerrainSrg::m_detailMaterialIdImage.GatherGreen(TerrainSrg::DetailSampler, idUv, 0).xyzw; + DetailSurface tempSurface; + GetDetailSurfaceForMaterial(tempSurface, material1, detailUv); + WeightDetailSurface(tempSurface, weight * (1.0 - blend)); + AddDetailSurface(surface, tempSurface); + if (material2 != 0xFF) + { + GetDetailSurfaceForMaterial(tempSurface, material2, detailUv); + WeightDetailSurface(tempSurface, weight * blend); + AddDetailSurface(surface, tempSurface); + } +} - const float maxBlendAmount = 0xFF; +/* +Populates a DetailSurface with material data gathered form the 4 nearest samples to detailMaterialIdUv. The weight +of each detail material's contribution is calculated based on the distance to the center point for that sample (for +instance, if detailMaterialIdUv falls perfectly in-between all 4 samples, then each sample will be weighed at 25%). +Each sample can have two different detail materials defined with a blend value to determine their relative contribution. +The detailUv is used for sampling the textures of each detail material. +*/ +bool GetDetailSurface(inout DetailSurface surface, float2 detailMaterialIdUv, float2 detailUv) +{ + float2 textureSize; + TerrainSrg::m_detailMaterialIdImage.GetDimensions(textureSize.x, textureSize.y); + + float2 detailMaterialIdCoord = detailMaterialIdUv * textureSize; // uv -> pixel coordinate + + // detailMaterialIdCoord could be negative, so add textureSize to ensure it is positive + detailMaterialIdCoord += textureSize; + + // The detail material id texture wraps since the "center" point can be anywhere in the texture, so mod by texturesize + int2 detailMaterailIdTopLeft = int2(detailMaterialIdCoord) % textureSize; + int2 detailMaterailIdBottomRight = (int2(detailMaterialIdCoord) + 1) % textureSize; + + // Using Load() to gather the nearest 4 samples (Gather4() isn't used because of precision issues with uvs). + uint4 s1 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterailIdTopLeft.x, detailMaterailIdBottomRight.y, 0)); + uint4 s2 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterailIdBottomRight, 0)); + uint4 s3 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterailIdBottomRight.x, detailMaterailIdTopLeft.y, 0)); + uint4 s4 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterailIdTopLeft, 0)); + + uint4 material1 = uint4(s1.x, s2.x, s3.x, s4.x); + uint4 material2 = uint4(s1.y, s2.y, s3.y, s4.y); + // convert integer of 0-255 to float of 0-1. - float4 blends = float4(TerrainSrg::m_detailMaterialIdImage.GatherBlue(TerrainSrg::DetailSampler, idUv, 0).xyzw) / maxBlendAmount; + const float maxBlendAmount = 0xFF; + float4 blends = float4(s1.z, s2.z, s3.z, s4.z) / maxBlendAmount; + + // Calculate weight based on proximity to detail material samples + float2 gatherWeight = frac(detailMaterialIdCoord); + // Adjust the gather weight for better interpolation by (3x^2 - 2x^3). This helps avoid diamond-shaped artifacts in binlinear filtering. + gatherWeight = gatherWeight * gatherWeight * (3.0 - 2.0 * gatherWeight); if (o_debugDetailMaterialIds) { + float2 idUv = (detailMaterialIdCoord + gatherWeight - 0.5) / textureSize; GetDebugDetailSurface(surface, material1.x, material2.x, blends.x, idUv); return true; } - if (material1.x == 0xFF) + // If any sample has no materials, give up. + if (any(material1 == 0xFF)) { return false; } - GetDetailSurfaceForMaterial(surface, material1.x, uv); + if (all(material1.x == material1.yzw) && all(material2.x == material2.yzw)) + { + // Fast path for same material ids + GetDetailSurfaceForMaterial(surface, material1.x, detailUv); + if (material2.x != 0xFF) + { + float4 material2Blends = 1.0 - blends; + DetailSurface tempSurface; + float weight = + ((1.0 - gatherWeight.x) * gatherWeight.y * material2Blends.x) + + (gatherWeight.x * gatherWeight.y * material2Blends.y) + + (gatherWeight.x * (1.0 - gatherWeight.y) * material2Blends.z) + + ((1.0 - gatherWeight.x) * (1.0 - gatherWeight.y) * material2Blends.w); + WeightDetailSurface(surface, weight); + GetDetailSurfaceForMaterial(tempSurface, material2.x, detailUv); + WeightDetailSurface(tempSurface, 1.0 - weight); + AddDetailSurface(surface, tempSurface); + } + } + else + { + surface = (DetailSurface)0; + + // X + float weight = (1.0 - gatherWeight.x) * gatherWeight.y; + BlendDetailMaterial(surface, material1.x, material2.x, blends.x, detailUv, weight); + + // Y + weight = gatherWeight.x * gatherWeight.y; + BlendDetailMaterial(surface, material1.y, material2.y, blends.y, detailUv, weight); + + // Z + weight = gatherWeight.x * (1.0 - gatherWeight.y); + BlendDetailMaterial(surface, material1.z, material2.z, blends.z, detailUv, weight); + + // W + weight = (1.0 - gatherWeight.x) * (1.0 - gatherWeight.y); + BlendDetailMaterial(surface, material1.w, material2.w, blends.w, detailUv, weight); + } + + surface.m_normal = normalize(surface.m_normal); + return true; } diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl index 6b68336a3f..ab9064e740 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl @@ -109,9 +109,10 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) { bool flipX = ObjectSrg::m_macroMaterialData[i].m_flipNormalX; bool flipY = ObjectSrg::m_macroMaterialData[i].m_flipNormalY; - bool factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor; - macroNormal = GetNormalInputTS(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler, - macroUv, flipX, flipY, CreateIdentity3x3(), true, factor); + float factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor; + + float2 sampledValue = SampleNormalXY(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler, macroUv, flipX, flipY); + macroNormal = normalize(GetTangentSpaceNormal_Unnormalized(sampledValue.xy, factor)); } break; } @@ -129,7 +130,7 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) // Check to make sure we're inside the detail texture's bounds and within where detail textures should be drawn. if (detailFactor < 1.0 && all(detailRegionUv > TerrainSrg::m_detailHalfPixelUv) && all(detailRegionUv < 1.0 - TerrainSrg::m_detailHalfPixelUv)) { - detailRegionUv += TerrainSrg::m_detailMaterialIdImageCenter - (0.5); + detailRegionUv += TerrainSrg::m_detailMaterialIdImageCenter - 0.5; hasDetailSurface = GetDetailSurface(detailSurface, detailRegionUv, detailUv); } diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.shader b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.shader index 0e6f0beb1d..66072567ec 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.shader +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.shader @@ -1,6 +1,12 @@ { "Source" : "./TerrainPBR_ForwardPass.azsl", + "CompilerHints" : + { + "DisableOptimizations" : false, + "GenerateDebugInfo" : false + }, + "DepthStencilState" : { "Depth" : diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli index c8ab04a5bc..f980e02b9d 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli @@ -17,16 +17,6 @@ ShaderResourceGroupSemantic SRG_Terrain ShaderResourceGroup TerrainSrg : SRG_Terrain { - - Sampler DetailSampler - { - AddressU = Wrap; - AddressV = Wrap; - MinFilter = Point; - MagFilter = Point; - MipFilter = Point; - }; - struct DetailMaterialData { // Uv diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp index bd65cf6abc..8d6bf8e4b0 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp @@ -40,15 +40,17 @@ namespace Terrain ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_worldMin, "World Bounds (Min)", "") // Temporary constraint until the rest of the Terrain system is updated to support larger worlds. + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &TerrainWorldConfig::ValidateWorldMin) ->Attribute(AZ::Edit::Attributes::Min, -2048.0f) ->Attribute(AZ::Edit::Attributes::Max, 2048.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_worldMax, "World Bounds (Max)", "") // Temporary constraint until the rest of the Terrain system is updated to support larger worlds. + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &TerrainWorldConfig::ValidateWorldMax) ->Attribute(AZ::Edit::Attributes::Min, -2048.0f) ->Attribute(AZ::Edit::Attributes::Max, 2048.0f) ->DataElement( AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_heightQueryResolution, "Height Query Resolution (m)", "") - ; + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &TerrainWorldConfig::ValidateWorldHeight); } } } @@ -128,4 +130,42 @@ namespace Terrain } return false; } -} + + float TerrainWorldConfig::NumberOfSamples(AZ::Vector3* min, AZ::Vector3* max, AZ::Vector2* heightQuery) + { + float numberOfSamples = ((max->GetX() - min->GetX()) / heightQuery->GetX()) * ((max->GetY() - min->GetY()) / heightQuery->GetY()); + return numberOfSamples; + } + + AZ::Outcome TerrainWorldConfig::DetermineMessage(float numSamples) + { + const float maximumSamplesAllowed = 8.0f * 1024.0f * 1024.0f; + if (numSamples < maximumSamplesAllowed) + { + return AZ::Success(); + } + return AZ::Failure(AZStd::string("The number of samples exceeds the maximum allowed.")); + } + + AZ::Outcome TerrainWorldConfig::ValidateWorldMin(void* newValue, [[maybe_unused]]const AZ::Uuid& valueType) + { + AZ::Vector3 minValue = *static_cast(newValue); + + return DetermineMessage(NumberOfSamples(&minValue, &m_worldMax, &m_heightQueryResolution)); + } + + AZ::Outcome TerrainWorldConfig::ValidateWorldMax(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType) + { + AZ::Vector3 maxValue = *static_cast(newValue); + + return DetermineMessage(NumberOfSamples(&m_worldMin, &maxValue, &m_heightQueryResolution)); + } + + AZ::Outcome TerrainWorldConfig::ValidateWorldHeight(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType) + { + AZ::Vector2 heightValue = *static_cast(newValue); + + return DetermineMessage(NumberOfSamples(&m_worldMin, &m_worldMax, &heightValue)); + } + +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.h index 2dfe1135c8..a396bcefc8 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.h @@ -32,6 +32,14 @@ namespace Terrain AZ::Vector3 m_worldMin{ 0.0f, 0.0f, 0.0f }; AZ::Vector3 m_worldMax{ 1024.0f, 1024.0f, 1024.0f }; AZ::Vector2 m_heightQueryResolution{ 1.0f, 1.0f }; + + private: + AZ::Outcome ValidateWorldMin(void* newValue, const AZ::Uuid& valueType); + AZ::Outcome ValidateWorldMax(void* newValue, const AZ::Uuid& valueType); + AZ::Outcome ValidateWorldHeight(void* newValue, const AZ::Uuid& valueType); + float NumberOfSamples(AZ::Vector3* min, AZ::Vector3* max, AZ::Vector2* heightQuery); + AZ::Outcome DetermineMessage(float numSamples); + }; diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.h index 2433eda009..623d58f1bc 100644 --- a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.h +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.h @@ -27,6 +27,6 @@ namespace Terrain static constexpr const char* const s_componentDescription = "Provides height data for a region to the terrain system"; static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainHeight.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainHeight.svg"; - static constexpr const char* const s_helpUrl = ""; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/terrain/height_gradient_list/"; }; } diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h index 1eb3413d52..4018e0d377 100644 --- a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h @@ -27,6 +27,6 @@ namespace Terrain static constexpr const char* const s_componentDescription = "Defines a terrain region for use by the terrain system"; static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainLayerSpawner.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainLayerSpawner.svg"; - static constexpr const char* const s_helpUrl = ""; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/terrain/layer_spawner/"; }; } diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h index 3cf9e7fc47..58cb776823 100644 --- a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h @@ -27,6 +27,6 @@ namespace Terrain static constexpr const char* const s_componentDescription = "Provides a mapping between gradients and surface tags for use by the terrain system."; static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainSurfaceGradientList.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainSurfaceGradientList.svg"; - static constexpr const char* const s_helpUrl = ""; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/terrain/surface-gradient-list/"; }; } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.h index 7fe8c82522..973e85a6d2 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.h @@ -27,6 +27,6 @@ namespace Terrain static constexpr const char* const s_componentDescription = "Provides a mapping between surface tags and render materials."; static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainSurfaceMaterials.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainSurfaceMaterials.svg"; - static constexpr const char* const s_helpUrl = ""; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/terrain/surface-material-list/"; }; } diff --git a/cmake/Platform/Linux/runtime_dependencies_linux.cmake.in b/cmake/Platform/Linux/runtime_dependencies_linux.cmake.in index 4ccf123e27..16445ef8d9 100644 --- a/cmake/Platform/Linux/runtime_dependencies_linux.cmake.in +++ b/cmake/Platform/Linux/runtime_dependencies_linux.cmake.in @@ -10,6 +10,7 @@ cmake_policy(SET CMP0012 NEW) # new policy for the if that evaluates a boolean o function(ly_copy source_file target_directory) cmake_path(GET source_file FILENAME target_filename) + cmake_PATH(GET source_file EXTENSION target_filename_ext) cmake_path(APPEND target_file "${target_directory}" "${target_filename}") cmake_path(COMPARE "${source_file}" EQUAL "${target_file}" same_location) if(NOT ${same_location}) diff --git a/scripts/build/Platform/Windows/env_windows.cmd b/scripts/build/Platform/Windows/env_windows.cmd index a78946caf6..78b5ca6c1a 100644 --- a/scripts/build/Platform/Windows/env_windows.cmd +++ b/scripts/build/Platform/Windows/env_windows.cmd @@ -8,8 +8,7 @@ REM REM REM To get recursive folder creation -SETLOCAL EnableExtensions -SETLOCAL EnableDelayedExpansion +SETLOCAL EnableExtensions EnableDelayedExpansion where /Q cmake IF NOT %ERRORLEVEL%==0 ( @@ -22,21 +21,26 @@ IF NOT "%COMMAND_CWD%"=="" ( CD %COMMAND_CWD% ) -REM Jenkins reports MSB8029 when TMP/TEMP is not defined, define a dummy folder -IF NOT "%TMP%"=="" ( - IF NOT "%WORKSPACE_TMP%"=="" ( - SET TMP=%WORKSPACE_TMP% - SET TEMP=%WORKSPACE_TMP% +REM Ending the local environment to be able to propagate the TMP/TEMP variables to the calling script +ENDLOCAL + +REM Jenkins does not defined TMP +IF "%TMP%"=="" ( + IF "%WORKSPACE%"=="" ( + SET TMP=%APPDATA%\Local\Temp + SET TEMP=%APPDATA%\Local\Temp ) ELSE ( - SET TMP=%cd%/temp - SET TEMP=%cd%/temp + SET TMP=%WORKSPACE%\Temp + SET TEMP=%WORKSPACE%\Temp + REM This folder may not be created in the workspace + IF NOT EXIST "!TMP!" ( + MKDIR "!TMP!" + ) ) ) -IF NOT EXIST "!TMP!" ( - MKDIR "!TMP!" -) EXIT /b 0 :error +ENDLOCAL EXIT /b 1 \ No newline at end of file diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index bbde450973..8dc111c256 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -17,14 +17,6 @@ IF NOT EXIST %OUTPUT_DIRECTORY% ( ) PUSHD %OUTPUT_DIRECTORY% -REM Override the temporary directory used by wix to the workspace (if we have a WORKSPACE_TMP) -IF NOT "%WORKSPACE_TMP%"=="" ( - SET "WIX_TEMP=!WORKSPACE_TMP!/wix" - IF NOT EXIST "!WIX_TEMP!" ( - MKDIR "!WIX_TEMP!" - ) -) - REM Make sure we are using the CMake version of CPack and not the one that comes with chocolatey SET CPACK_PATH= IF "%LY_CMAKE_PATH%"=="" ( diff --git a/scripts/signer/Platform/Linux/o3de-releases.gpg b/scripts/signer/Platform/Linux/o3de-releases.gpg index 602d822673..b5fbb00392 100644 Binary files a/scripts/signer/Platform/Linux/o3de-releases.gpg and b/scripts/signer/Platform/Linux/o3de-releases.gpg differ diff --git a/scripts/signer/Platform/Linux/signer.sh b/scripts/signer/Platform/Linux/signer.sh new file mode 100644 index 0000000000..b090012c5a --- /dev/null +++ b/scripts/signer/Platform/Linux/signer.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set +x + +export GPG_TTY=$(tty) # Required to pass valid tty during ssh sessions +file=$1 + +# dpkg-sig depends on a valid and trusted GPG private key. This also assumes a private key password has already been cached via gpg-agent +# If you do need to pass a password, a gpg argument can be added to the command: +# dpkg-sig -k $fingerprint -g "--pinentry-mode loopback --passphrase $pass" --sign builder + +fingerprint=$(gpg --list-keys --with-colons | awk -F: '/fpr:/ {print $10}' | tail -n1) #Get the last certificate in the list, which is the signing cert +if [ -z $fingerprint ]; then + echo "No valid certs found. Exiting with 1" + exit 1 +fi +echo "Signing with $fingerprint" +dpkg-sig -k $fingerprint --sign builder $file +dpkg-sig --verify $file && echo "Signing $file complete!"