From 5ff65be3145c43215c4a9e635316016919bdcbfa Mon Sep 17 00:00:00 2001 From: nemerle <96597+nemerle@users.noreply.github.com> Date: Sun, 12 Dec 2021 23:48:47 +0100 Subject: [PATCH 01/61] This reduces non-unity build time by ~2% and build size by ~0.5%. This PR is a 'clean' version of #6199 updated to latest development Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- Code/Editor/GameEngine.cpp | 1 + .../AtomCore/Instance/InstanceDatabase.h | 5 + .../AzCore/AzCore/Asset/AssetCommon.h | 2 +- .../AzCore/AzCore/Asset/AssetDataStream.cpp | 98 +++-- .../AzCore/AzCore/Asset/AssetDataStream.h | 35 +- .../AzCore/AzCore/Asset/AssetManager.cpp | 1 + .../AzCore/AzCore/Asset/AssetManager.h | 1 - Code/Framework/AzCore/AzCore/IO/IStreamer.h | 6 +- .../AzCore/AzCore/IO/Streamer/BlockCache.cpp | 14 +- .../AzCore/AzCore/IO/Streamer/BlockCache.h | 8 +- .../AzCore/IO/Streamer/DedicatedCache.cpp | 20 +- .../AzCore/IO/Streamer/DedicatedCache.h | 23 +- .../AzCore/AzCore/IO/Streamer/FileRequest.cpp | 143 +++--- .../AzCore/AzCore/IO/Streamer/FileRequest.h | 407 ++++++++++-------- .../IO/Streamer/FullFileDecompressor.cpp | 46 +- .../AzCore/IO/Streamer/FullFileDecompressor.h | 7 +- .../AzCore/IO/Streamer/ReadSplitter.cpp | 10 +- .../AzCore/AzCore/IO/Streamer/Scheduler.cpp | 50 ++- .../AzCore/AzCore/IO/Streamer/Scheduler.h | 19 +- .../AzCore/IO/Streamer/StorageDrive.cpp | 42 +- .../AzCore/AzCore/IO/Streamer/StorageDrive.h | 8 +- .../AzCore/AzCore/IO/Streamer/Streamer.cpp | 7 +- .../AzCore/AzCore/IO/Streamer/Streamer.h | 8 +- .../AzCore/IO/Streamer/StreamerComponent.cpp | 3 +- .../AzCore/IO/Streamer/StreamerContext.cpp | 6 +- .../AzCore/IO/Streamer/StreamerContext.h | 13 +- .../IO/Streamer/StorageDrive_Windows.cpp | 52 +-- .../AzCore/IO/Streamer/StorageDrive_Windows.h | 11 +- .../Tests/Asset/AssetDataStreamTests.cpp | 1 + .../IO/Streamer/StorageDriveTests_Windows.cpp | 26 +- .../AzCore/Tests/Streamer/BlockCacheTests.cpp | 16 +- .../Tests/Streamer/FullDecompressorTests.cpp | 2 +- .../AzCore/Tests/Streamer/IStreamerMock.h | 1 + .../Tests/Streamer/ReadSplitterTests.cpp | 10 +- .../AzCore/Tests/Streamer/SchedulerTests.cpp | 7 +- .../StreamStackEntryConformityTests.h | 1 + Code/Framework/AzCore/Tests/StreamerTests.cpp | 1 + .../Asset/AssetSystemComponent.cpp | 1 + .../AzFramework/IO/RemoteStorageDrive.cpp | 42 +- .../AzFramework/IO/RemoteStorageDrive.h | 11 +- .../Physics/Common/PhysicsSimulatedBody.cpp | 1 + .../Common/PhysicsSimulatedBodyAutomation.cpp | 1 + .../Common/PhysicsSimulatedBodyEvents.cpp | 1 + .../AzFramework/Physics/PhysicsScene.cpp | 1 + .../AzFramework/Physics/PhysicsSystem.cpp | 1 + .../AzFramework/Script/ScriptComponent.cpp | 1 + .../ToolsAssetCatalogComponent.h | 1 + .../Model/AssetCompleterModel.h | 5 +- .../AssetBuilder/AssetBuilderComponent.cpp | 1 + .../Shader/ShaderVariantAsyncLoader.h | 6 +- .../Shader/PrecompiledShaderAssetSourceData.h | 1 + .../RPI/Code/Source/RPI.Public/Culling.cpp | 24 +- .../Shader/Metrics/ShaderMetricsSystem.cpp | 4 +- .../Tests/Common/AssetManagerTestFixture.cpp | 2 + .../Atom/Utils/AssetCollectionAsyncLoader.h | 1 + .../Code/Source/AtomActorInstance.cpp | 2 + .../Code/Rendering/SharedBuffer.cpp | 2 + .../Code/Source/Engine/ATLEntities.cpp | 18 + .../Code/Source/Engine/ATLEntities.h | 14 +- .../Code/Source/Engine/FileCacheManager.cpp | 1 + .../Code/Tests/AudioSystemTest.cpp | 1 + .../Code/Tests/Mocks/FileCacheManagerMock.h | 5 + .../Code/EMotionFX/Source/ActorInstance.cpp | 2 + .../Code/Tests/TestAssetCode/SimpleActors.cpp | 2 + .../Asset/AssetSystemDebugComponent.cpp | 2 + .../Audio/AudioAreaEnvironmentComponent.cpp | 1 + .../ClothComponentMesh/ActorClothColliders.h | 1 + Gems/NvCloth/Code/Source/Utils/AssetHelper.h | 1 + .../Joints/JointsSubComponentModeAngleCone.h | 1 + .../Joints/JointsSubComponentModeSnap.h | 1 + .../Code/Source/ForceRegionComponent.cpp | 2 + .../Code/Source/Joint/PhysXJointUtils.cpp | 14 +- .../PhysX/Code/Source/Joint/PhysXJointUtils.h | 7 + Gems/PhysX/Code/Source/JointComponent.cpp | 11 +- Gems/PhysX/Code/Source/Material.cpp | 3 +- .../PhysXCharacters/API/CharacterUtils.cpp | 11 +- .../PhysXCharacters/API/RagdollNode.cpp | 11 +- .../Pipeline/HeightFieldAssetHandler.cpp | 3 +- .../Code/Source/Pipeline/StreamWrapper.h | 1 + Gems/PhysX/Code/Source/Scene/PhysXScene.cpp | 18 +- Gems/PhysX/Code/Source/System/PhysXSystem.cpp | 16 +- Gems/PhysX/Code/Tests/PhysXTestUtil.cpp | 1 + .../Code/Source/SystemComponent.cpp | 23 +- .../Editor/Assets/ScriptCanvasMemoryAsset.cpp | 7 +- .../Windows/Tools/UpgradeTool/FileSaver.cpp | 2 + .../Code/Source/ScriptEventsSystemComponent.h | 1 + .../WhiteBoxVertexTranslationModifier.h | 1 + 87 files changed, 840 insertions(+), 562 deletions(-) diff --git a/Code/Editor/GameEngine.cpp b/Code/Editor/GameEngine.cpp index db4c587f54..3753b5fa37 100644 --- a/Code/Editor/GameEngine.cpp +++ b/Code/Editor/GameEngine.cpp @@ -18,6 +18,7 @@ // AzCore #include #include +#include #include #include diff --git a/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h b/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h index 4b4ad572c2..de98ce97b9 100644 --- a/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h +++ b/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h @@ -17,6 +17,11 @@ #include #include +namespace AZStd +{ + class any; +} + namespace AZ { namespace Data diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h index e4d2c7612e..697ca81a49 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.cpp index 4794b04626..7c9593fe3d 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.cpp @@ -7,11 +7,61 @@ */ #include +#include +#include +#include +#include +#include + +#include +#include namespace AZ::Data { + namespace Internal + { + struct AssetDataStreamPrivate + { + //! Optional data buffer that's been directly passed in through Open(), instead of reading data from a file. + AZStd::vector m_preloadedData; + //! The current active streamer read request - tracked in case we need to cancel it prematurely + AZ::IO::FileRequestPtr m_curReadRequest{ nullptr }; + + //! Synchronization for the read request, so that it's possible to block until completion. + AZStd::mutex m_readRequestMutex; + AZStd::condition_variable m_readRequestActive; + + void SetReadRequest(AZ::IO::FileRequestPtr&& req) + { + AZStd::scoped_lock lock(m_readRequestMutex); + // The read request finished, so stop tracking it. + m_curReadRequest = AZStd::move(req); + } + void BlockUntilReadComplete() + { + AZStd::unique_lock lock(m_readRequestMutex); + m_readRequestActive.wait( + lock, + [this] + { + return m_curReadRequest == nullptr; + }); + lock.unlock(); + } + void CancelRequest() + { + AZStd::scoped_lock lock(m_readRequestMutex); + if (m_curReadRequest) + { + auto streamer = Interface::Get(); + m_curReadRequest = streamer->Cancel(m_curReadRequest); + } + } + }; + } // namespace Internal AssetDataStream::AssetDataStream(AZ::IO::IStreamerTypes::RequestMemoryAllocator* bufferAllocator) : m_bufferAllocator(bufferAllocator ? bufferAllocator : &m_defaultAllocator) + , m_privateData(new Internal::AssetDataStreamPrivate) { ClearInternalStateData(); } @@ -22,6 +72,7 @@ namespace AZ::Data { Close(); } + delete m_privateData; } @@ -53,9 +104,9 @@ namespace AZ::Data OpenInternal(data.size(), "(mem buffer)"); // Directly take ownership of the provided buffer - m_preloadedData = AZStd::move(data); - m_buffer = m_preloadedData.data(); - m_loadedSize = m_preloadedData.size(); + m_privateData->m_preloadedData = AZStd::move(data); + m_buffer = m_privateData->m_preloadedData.data(); + m_loadedSize = m_privateData->m_preloadedData.size(); } void AssetDataStream::Open(const AZStd::string& filePath, size_t fileOffset, size_t assetSize, @@ -65,7 +116,7 @@ namespace AZ::Data AZ_PROFILE_FUNCTION(AzCore); AZ_Assert(!m_isOpen, "Attempting to open the stream when it is already open."); - AZ_Assert(!m_curReadRequest, "Queueing an asset stream load while one is still in progress."); + AZ_Assert(!m_privateData->m_curReadRequest, "Queueing an asset stream load while one is still in progress."); AZ_Assert(!filePath.empty(), "AssetDataStream::Open called without a valid file name."); // Initialize the state variables and start tracking the overall load timings @@ -97,11 +148,8 @@ namespace AZ::Data "Buffer for %s was expected to be %zu bytes, but is %zu bytes.", m_filePath.c_str(), m_requestedAssetSize, m_loadedSize); - { - AZStd::scoped_lock lock(m_readRequestMutex); - // The read request finished, so stop tracking it. - m_curReadRequest = nullptr; - } + // The read request finished, so stop tracking it. + m_privateData->SetReadRequest(nullptr); // Call the load callback to start processing the loaded data. if (loadCallback) @@ -115,21 +163,22 @@ namespace AZ::Data } // Notify that the load is complete, in case anyone is using BlockUntilLoadComplete to block. - m_readRequestActive.notify_one(); + m_privateData->m_readRequestActive.notify_one(); }; // Queue the raw file load with the file streamer. auto streamer = AZ::Interface::Get(); - m_curReadRequest = streamer->Read( + m_privateData->m_curReadRequest = + streamer->Read( m_filePath, *m_bufferAllocator, m_requestedAssetSize, deadline, priority, m_fileOffset); m_curDeadline = deadline; m_curPriority = priority; - streamer->SetRequestCompleteCallback(m_curReadRequest, streamerCallback); + streamer->SetRequestCompleteCallback(m_privateData->m_curReadRequest, streamerCallback); - streamer->QueueRequest(m_curReadRequest); + streamer->QueueRequest(m_privateData->m_curReadRequest); } else { @@ -139,19 +188,19 @@ namespace AZ::Data loadCallback(AZ::IO::IStreamerTypes::RequestStatus::Completed); } - m_readRequestActive.notify_one(); + m_privateData->m_readRequestActive.notify_one(); } } void AssetDataStream::Reschedule(AZStd::chrono::milliseconds newDeadline, AZ::IO::IStreamerTypes::Priority newPriority) { - if (m_curReadRequest && (newDeadline < m_curDeadline || newPriority > m_curPriority)) + if (m_privateData->m_curReadRequest && (newDeadline < m_curDeadline || newPriority > m_curPriority)) { auto deadline = AZStd::GetMin(m_curDeadline, newDeadline); auto priority = AZStd::GetMax(m_curPriority, newPriority); auto streamer = Interface::Get(); - m_curReadRequest = streamer->RescheduleRequest(m_curReadRequest, deadline, priority); + m_privateData->m_curReadRequest = streamer->RescheduleRequest(m_privateData->m_curReadRequest, deadline, priority); m_curDeadline = deadline; m_curPriority = priority; } @@ -159,15 +208,13 @@ namespace AZ::Data void AssetDataStream::BlockUntilLoadComplete() { - AZStd::unique_lock lock(m_readRequestMutex); - m_readRequestActive.wait(lock, [this] { return m_curReadRequest == nullptr; }); - lock.unlock(); + m_privateData->BlockUntilReadComplete(); } void AssetDataStream::ClearInternalStateData() { // Clear all our internal state data. - m_preloadedData.resize(0); + m_privateData->m_preloadedData.resize(0); m_buffer = nullptr; m_loadedSize = 0; m_requestedAssetSize = 0; @@ -204,10 +251,10 @@ namespace AZ::Data void AssetDataStream::Close() { AZ_Assert(m_isOpen, "Attempting to close a stream that hasn't been opened."); - AZ_Assert(m_curReadRequest == nullptr, "Attempting to close a stream with a read request in flight."); + AZ_Assert(m_privateData->m_curReadRequest == nullptr, "Attempting to close a stream with a read request in flight."); // Destroy the asset buffer and unlock the allocator, so the allocator itself knows that it is no longer needed. - if (m_buffer != m_preloadedData.data()) + if (m_buffer != m_privateData->m_preloadedData.data()) { m_bufferAllocator->Release(m_buffer); } @@ -221,12 +268,7 @@ namespace AZ::Data void AssetDataStream::RequestCancel() { - AZStd::scoped_lock lock(m_readRequestMutex); - if (m_curReadRequest) - { - auto streamer = Interface::Get(); - m_curReadRequest = streamer->Cancel(m_curReadRequest); - } + m_privateData->CancelRequest(); } void AssetDataStream::Seek(AZ::IO::OffsetType bytes, AZ::IO::GenericStream::SeekMode mode) diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h index 79822c8db2..f095e31562 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h @@ -9,17 +9,26 @@ #include #include -#include -#include -#include -#include -#include +#include + + +namespace AZStd +{ + template + class vector; +} namespace AZ::Data { + namespace Internal + { + struct AssetDataStreamPrivate; + } + class AssetDataStream : public AZ::IO::GenericStream { public: + using VectorDataSource = AZStd::vector; // The default Generic Stream APIs in this class will only allow for a single sequential pass // through the data, no seeking. Reads will block when pages aren't available yet, and // pages will be marked for recycling once reading has progressed beyond them. @@ -29,10 +38,10 @@ namespace AZ::Data ~AssetDataStream() override; // Open the AssetDataStream and make a copy of the provided memory buffer. - void Open(const AZStd::vector& data); + void Open(const VectorDataSource& data); // Open the AssetDataStream and directly take ownership of a pre-populated memory buffer. - void Open(AZStd::vector&& data); + void Open(VectorDataSource&& data); // Open the AssetDataStream and load it via file streaming using OnCompleteCallback = AZStd::function; @@ -91,6 +100,8 @@ namespace AZ::Data void ClearInternalStateData(); + Internal::AssetDataStreamPrivate* m_privateData; + //! The allocator to use for allocating / deallocating asset buffers AZ::IO::IStreamerTypes::RequestMemoryAllocator* m_bufferAllocator{ nullptr }; @@ -106,9 +117,6 @@ namespace AZ::Data //! The amount of data that's expected to be loaded. size_t m_requestedAssetSize{ 0 }; - //! Optional data buffer that's been directly passed in through Open(), instead of reading data from a file. - AZStd::vector m_preloadedData; - //! The buffer that will hold the raw data after it's loaded from the file. void* m_buffer{ nullptr }; @@ -119,19 +127,12 @@ namespace AZ::Data //! The current offset representing how far we've read into the buffer. size_t m_curOffset{ 0 }; - //! The current active streamer read request - tracked in case we need to cancel it prematurely - AZ::IO::FileRequestPtr m_curReadRequest{ nullptr }; - //! The current request deadline. Used to avoid requesting a reschedule to the same (current) deadline. AZStd::chrono::milliseconds m_curDeadline{ AZ::IO::IStreamerTypes::s_noDeadline }; //! The current request priority. Used to avoid requesting a reschedule to the same (current) priority. AZ::IO::IStreamerTypes::Priority m_curPriority{ AZ::IO::IStreamerTypes::s_priorityMedium }; - //! Synchronization for the read request, so that it's possible to block until completion. - AZStd::mutex m_readRequestMutex; - AZStd::condition_variable m_readRequestActive; - //! Track whether or not the stream is currently open bool m_isOpen{ false }; diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp index afe3330bd3..b4b1a0d81c 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManager.h b/Code/Framework/AzCore/AzCore/Asset/AssetManager.h index 9666d434c1..663de2c058 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetManager.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetManager.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include // used as allocator for most components #include diff --git a/Code/Framework/AzCore/AzCore/IO/IStreamer.h b/Code/Framework/AzCore/AzCore/IO/IStreamer.h index 438384e687..d59ee5007e 100644 --- a/Code/Framework/AzCore/AzCore/IO/IStreamer.h +++ b/Code/Framework/AzCore/AzCore/IO/IStreamer.h @@ -15,14 +15,18 @@ #include #include #include +#include // These Streamer includes need to be moved to Streamer internals/implementation, // and pull out only what we need for visibility at IStreamer.h interface declaration. #include -#include namespace AZ::IO { + class ExternalFileRequest; + class FileRequestHandle; + + using FileRequestPtr = AZStd::intrusive_ptr; /** * Data Streamer Interface */ diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.cpp index 6c873f3050..ca38414eaa 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.cpp @@ -137,18 +137,18 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { ReadFile(request, args); return; } else { - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } @@ -166,7 +166,7 @@ namespace AZ::IO { Section& delayed = m_delayedSections.front(); AZ_Assert(delayed.m_parent, "Delayed section doesn't have a reference to the original request."); - auto data = AZStd::get_if(&delayed.m_parent->GetCommand()); + auto data = AZStd::get_if(&delayed.m_parent->GetCommand()); AZ_Assert(data, "A request in the delayed queue of the BlockCache didn't have a parent with read data."); // This call can add the same section to the back of the queue if there's not // enough space. Because of this the entry needs to be removed from the delayed @@ -233,7 +233,7 @@ namespace AZ::IO } } - void BlockCache::ReadFile(FileRequest* request, FileRequest::ReadData& data) + void BlockCache::ReadFile(FileRequest* request, Requests::ReadData& data) { if (!m_next) { @@ -250,7 +250,7 @@ namespace AZ::IO m_numMetaDataRetrievalInProgress--; if (fileSizeRequest.GetStatus() == IStreamerTypes::RequestStatus::Completed) { - auto& requestInfo = AZStd::get(fileSizeRequest.GetCommand()); + auto& requestInfo = AZStd::get(fileSizeRequest.GetCommand()); if (requestInfo.m_found) { ContinueReadFile(request, requestInfo.m_fileSize); @@ -272,7 +272,7 @@ namespace AZ::IO Section main; Section epilog; - auto& data = AZStd::get(request->GetCommand()); + auto& data = AZStd::get(request->GetCommand()); if (!SplitRequest(prolog, main, epilog, data.m_path, fileLength, data.m_offset, data.m_size, reinterpret_cast(data.m_output))) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.h b/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.h index 90fb7ea193..68aa2da689 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.h @@ -21,6 +21,12 @@ namespace AZ::IO { + class RequestPath; + namespace Requests + { + struct ReadData; + } + struct BlockCacheConfig final : public IStreamerStackConfig { @@ -109,7 +115,7 @@ namespace AZ::IO using TimePoint = AZStd::chrono::system_clock::time_point; - void ReadFile(FileRequest* request, FileRequest::ReadData& data); + void ReadFile(FileRequest* request, Requests::ReadData& data); void ContinueReadFile(FileRequest* request, u64 fileLength); CacheResult ReadFromCache(FileRequest* request, Section& section, const RequestPath& filePath); CacheResult ReadFromCache(FileRequest* request, Section& section, u32 cacheBlock); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp index b80a1ea724..f155d26eef 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp @@ -101,12 +101,12 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { args.m_range = FileRange::CreateRangeForEntireFile(); m_context->PushPreparedRequest(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { args.m_range = FileRange::CreateRangeForEntireFile(); m_context->PushPreparedRequest(request); @@ -125,28 +125,28 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { ReadFile(request, args); return; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { CreateDedicatedCache(request, args); return; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { DestroyDedicatedCache(request, args); return; } else { - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } @@ -200,7 +200,7 @@ namespace AZ::IO } } - void DedicatedCache::ReadFile(FileRequest* request, FileRequest::ReadData& data) + void DedicatedCache::ReadFile(FileRequest* request, Requests::ReadData& data) { size_t index = FindCache(data.m_path, data.m_offset); if (index == s_fileNotFound) @@ -255,7 +255,7 @@ namespace AZ::IO StreamStackEntry::CollectStatistics(statistics); } - void DedicatedCache::CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data) + void DedicatedCache::CreateDedicatedCache(FileRequest* request, Requests::CreateDedicatedCacheData& data) { size_t index = FindCache(data.m_path, data.m_range); if (index == s_fileNotFound) @@ -276,7 +276,7 @@ namespace AZ::IO m_context->MarkRequestAsCompleted(request); } - void DedicatedCache::DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data) + void DedicatedCache::DestroyDedicatedCache(FileRequest* request, Requests::DestroyDedicatedCacheData& data) { size_t index = FindCache(data.m_path, data.m_range); if (index != s_fileNotFound) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h b/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h index 0ef2d879d3..a69dcdbd7f 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h @@ -11,15 +11,21 @@ #include #include #include -#include #include +#include #include -#include #include +#include #include namespace AZ::IO { + namespace Requests + { + struct CreateDedicatedCacheData; + struct DestroyDedicatedCacheData; + } // namespace Requests + struct DedicatedCacheConfig final : public IStreamerStackConfig { @@ -56,16 +62,19 @@ namespace AZ::IO void UpdateStatus(Status& status) const override; - void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector& internalPending, - StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override; + void UpdateCompletionEstimates( + AZStd::chrono::system_clock::time_point now, + AZStd::vector& internalPending, + StreamerContext::PreparedQueue::iterator pendingBegin, + StreamerContext::PreparedQueue::iterator pendingEnd) override; void CollectStatistics(AZStd::vector& statistics) const override; private: - void CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data); - void DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data); + void CreateDedicatedCache(FileRequest* request, Requests::CreateDedicatedCacheData& data); + void DestroyDedicatedCache(FileRequest* request, Requests::DestroyDedicatedCacheData& data); - void ReadFile(FileRequest* request, FileRequest::ReadData& data); + void ReadFile(FileRequest* request, AZ::IO::Requests::ReadData& data); size_t FindCache(const RequestPath& filename, FileRange range); size_t FindCache(const RequestPath& filename, u64 offset); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp index fc05b77b36..2f96d224e6 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp @@ -12,22 +12,30 @@ #include #include -namespace AZ::IO -{ - // - // Command structures. - // +// +// Command structures. +// - FileRequest::ExternalRequestData::ExternalRequestData(FileRequestPtr&& request) - : m_request(AZStd::move(request)) - {} - - FileRequest::RequestPathStoreData::RequestPathStoreData(RequestPath path) - : m_path(AZStd::move(path)) - {} +namespace AZ::IO::Requests +{ + ReadData::ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead) + : m_path(path) + , m_output(output) + , m_outputSize(outputSize) + , m_offset(offset) + , m_size(size) + , m_sharedRead(sharedRead) + { + } - FileRequest::ReadRequestData::ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size, - AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority) + ReadRequestData::ReadRequestData( + RequestPath path, + void* output, + u64 outputSize, + u64 offset, + u64 size, + AZStd::chrono::system_clock::time_point deadline, + IStreamerTypes::Priority priority) : m_path(AZStd::move(path)) , m_allocator(nullptr) , m_deadline(deadline) @@ -37,10 +45,16 @@ namespace AZ::IO , m_size(size) , m_priority(priority) , m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally. - {} + { + } - FileRequest::ReadRequestData::ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, - u64 offset, u64 size, AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority) + ReadRequestData::ReadRequestData( + RequestPath path, + IStreamerTypes::RequestMemoryAllocator* allocator, + u64 offset, + u64 size, + AZStd::chrono::system_clock::time_point deadline, + IStreamerTypes::Priority priority) : m_path(AZStd::move(path)) , m_allocator(allocator) , m_deadline(deadline) @@ -50,9 +64,10 @@ namespace AZ::IO , m_size(size) , m_priority(priority) , m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally. - {} + { + } - FileRequest::ReadRequestData::~ReadRequestData() + ReadRequestData::~ReadRequestData() { if (m_allocator != nullptr) { @@ -64,65 +79,81 @@ namespace AZ::IO } } - FileRequest::ReadData::ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead) - : m_output(output) - , m_outputSize(outputSize) - , m_path(path) - , m_offset(offset) - , m_size(size) - , m_sharedRead(sharedRead) - {} + CreateDedicatedCacheData::CreateDedicatedCacheData(RequestPath path, const FileRange& range) + : m_path(AZStd::move(path)) + , m_range(range) + { + } + + DestroyDedicatedCacheData::DestroyDedicatedCacheData(RequestPath path, const FileRange& range) + : m_path(AZStd::move(path)) + , m_range(range) + { + } - FileRequest::CompressedReadData::CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize) + ExternalRequestData::ExternalRequestData(FileRequestPtr&& request) + : m_request(AZStd::move(request)) + { + } + + RequestPathStoreData::RequestPathStoreData(RequestPath path) + : m_path(AZStd::move(path)) + { + } + + CompressedReadData::CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize) : m_compressionInfo(AZStd::move(compressionInfo)) , m_output(output) , m_readOffset(readOffset) , m_readSize(readSize) - {} + { + } - FileRequest::FileExistsCheckData::FileExistsCheckData(const RequestPath& path) + FileExistsCheckData::FileExistsCheckData(const RequestPath& path) : m_path(path) - {} + { + } - FileRequest::FileMetaDataRetrievalData::FileMetaDataRetrievalData(const RequestPath& path) + FileMetaDataRetrievalData::FileMetaDataRetrievalData(const RequestPath& path) : m_path(path) - {} + { + } - FileRequest::CancelData::CancelData(FileRequestPtr target) + CancelData::CancelData(FileRequestPtr target) : m_target(AZStd::move(target)) - {} + { + } - FileRequest::FlushData::FlushData(RequestPath path) + FlushData::FlushData(RequestPath path) : m_path(AZStd::move(path)) - {} + { + } - FileRequest::RescheduleData::RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, + RescheduleData::RescheduleData( + FileRequestPtr target, + AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority) : m_target(AZStd::move(target)) , m_newDeadline(newDeadline) , m_newPriority(newPriority) - {} - - FileRequest::CreateDedicatedCacheData::CreateDedicatedCacheData(RequestPath path, const FileRange& range) - : m_path(AZStd::move(path)) - , m_range(range) - {} - - FileRequest::DestroyDedicatedCacheData::DestroyDedicatedCacheData(RequestPath path, const FileRange& range) - : m_path(AZStd::move(path)) - , m_range(range) - {} + { + } - FileRequest::ReportData::ReportData(ReportType reportType) + Requests::ReportData::ReportData(ReportType reportType) : m_reportType(reportType) - {} + { + } - FileRequest::CustomData::CustomData(AZStd::any data, bool failWhenUnhandled) + CustomData::CustomData(AZStd::any data, bool failWhenUnhandled) : m_data(AZStd::move(data)) , m_failWhenUnhandled(failWhenUnhandled) - {} - + { + } +} // namespace AZ::IO::Requests +namespace AZ::IO +{ + using namespace Requests; // // FileRequest // @@ -263,7 +294,7 @@ namespace AZ::IO SetOptionalParent(parent); } - void FileRequest::CreateReport(ReportData::ReportType reportType) + void FileRequest::CreateReport(Requests::ReportType reportType) { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'Report', but another task was already assigned."); @@ -361,7 +392,7 @@ namespace AZ::IO "Request does not contain a valid command. It may have been reset already or was never assigned a command."); return true; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return args.m_failWhenUnhandled; } diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.h b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.h index dd4dad2387..7f5874c95d 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.h @@ -27,227 +27,261 @@ namespace AZ::IO class ExternalFileRequest; using FileRequestPtr = AZStd::intrusive_ptr; +} // namespace AZ::IO - class FileRequest final +namespace AZ::IO::Requests +{ + //! Request to read data. This is a translated request and holds an absolute path and has been + //! resolved to the archive file if needed. + struct ReadData { - public: - inline constexpr static AZStd::chrono::system_clock::time_point s_noDeadlineTime = AZStd::chrono::system_clock::time_point::max(); + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; - friend class StreamerContext; - friend class ExternalFileRequest; + ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead); - //! Stores a reference to the external request so it stays alive while the request is being processed. - //! This is needed because Streamer supports fire-and-forget requests since completion can be handled by - //! registering a callback. - struct ExternalRequestData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; + const RequestPath& m_path; //!< The path to the file that contains the requested data. + void* m_output; //!< Target output to write the read data to. + u64 m_outputSize; //!< Size of memory m_output points to. This needs to be at least as big as m_size, but can be bigger. + u64 m_offset; //!< The offset in bytes into the file. + u64 m_size; //!< The number of bytes to read from the file. + bool m_sharedRead; //!< True if other code will be reading from the file or the stack entry can exclusively lock. + }; - explicit ExternalRequestData(FileRequestPtr&& request); + //! Request to read data. This is an untranslated request and holds a relative path. The Scheduler + //! will translate this to the appropriate ReadData or CompressedReadData. + struct ReadRequestData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; + + ReadRequestData( + RequestPath path, + void* output, + u64 outputSize, + u64 offset, + u64 size, + AZStd::chrono::system_clock::time_point deadline, + IStreamerTypes::Priority priority); + ReadRequestData( + RequestPath path, + IStreamerTypes::RequestMemoryAllocator* allocator, + u64 offset, + u64 size, + AZStd::chrono::system_clock::time_point deadline, + IStreamerTypes::Priority priority); + ~ReadRequestData(); + + RequestPath m_path; //!< Relative path to the target file. + IStreamerTypes::RequestMemoryAllocator* m_allocator; //!< Allocator used to manage the memory for this request. + AZStd::chrono::system_clock::time_point m_deadline; //!< Time by which this request should have been completed. + void* m_output; //!< The memory address assigned (during processing) to store the read data to. + u64 m_outputSize; //!< The memory size of the addressed used to store the read data. + u64 m_offset; //!< The offset in bytes into the file. + u64 m_size; //!< The number of bytes to read from the file. + IStreamerTypes::Priority m_priority; //!< Priority used for ordering requests. This is used when requests have the same deadline. + IStreamerTypes::MemoryType m_memoryType; //!< The type of memory provided by the allocator if used. + }; - FileRequestPtr m_request; //!< The request that was send to Streamer. - }; + //! Creates a cache dedicated to a single file. This is best used for files where blocks are read from + //! periodically such as audio banks of video files. + struct CreateDedicatedCacheData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - //! Stores an instance of a RequestPath. To reduce copying instances of a RequestPath functions that - //! need a path take them by reference to the original request. In some cases a path originates from - //! within in the stack and temporary storage is needed. This struct allows for that temporary storage - //! so it can be safely referenced later. - struct RequestPathStoreData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; + CreateDedicatedCacheData(RequestPath path, const FileRange& range); - explicit RequestPathStoreData(RequestPath path); + RequestPath m_path; + FileRange m_range; + }; - RequestPath m_path; - }; + //! Destroys a cache dedicated to a single file that was previously created by CreateDedicatedCache + struct DestroyDedicatedCacheData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - //! Request to read data. This is an untranslated request and holds a relative path. The Scheduler - //! will translate this to the appropriate ReadData or CompressedReadData. - struct ReadRequestData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; - - ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size, - AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority); - ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size, - AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority); - ~ReadRequestData(); - - RequestPath m_path; //!< Relative path to the target file. - IStreamerTypes::RequestMemoryAllocator* m_allocator; //!< Allocator used to manage the memory for this request. - AZStd::chrono::system_clock::time_point m_deadline; //!< Time by which this request should have been completed. - void* m_output; //!< The memory address assigned (during processing) to store the read data to. - u64 m_outputSize; //!< The memory size of the addressed used to store the read data. - u64 m_offset; //!< The offset in bytes into the file. - u64 m_size; //!< The number of bytes to read from the file. - IStreamerTypes::Priority m_priority; //!< Priority used for ordering requests. This is used when requests have the same deadline. - IStreamerTypes::MemoryType m_memoryType; //!< The type of memory provided by the allocator if used. - }; + DestroyDedicatedCacheData(RequestPath path, const FileRange& range); - //! Request to read data. This is a translated request and holds an absolute path and has been - //! resolved to the archive file if needed. - struct ReadData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; + RequestPath m_path; + FileRange m_range; + }; - ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead); + enum class ReportType : int8_t + { + FileLocks + }; - const RequestPath& m_path; //!< The path to the file that contains the requested data. - void* m_output; //!< Target output to write the read data to. - u64 m_outputSize; //!< Size of memory m_output points to. This needs to be at least as big as m_size, but can be bigger. - u64 m_offset; //!< The offset in bytes into the file. - u64 m_size; //!< The number of bytes to read from the file. - bool m_sharedRead; //!< True if other code will be reading from the file or the stack entry can exclusively lock. - }; + struct ReportData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityLow; + inline constexpr static bool s_failWhenUnhandled = false; - //! Request to read and decompress data. - struct CompressedReadData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; + explicit ReportData(ReportType reportType); - CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize); + ReportType m_reportType; + }; - CompressionInfo m_compressionInfo; - void* m_output; //!< Target output to write the read data to. - u64 m_readOffset; //!< The offset into the decompressed to start copying from. - u64 m_readSize; //!< Number of bytes to read from the decompressed file. - }; + //! Stores a reference to the external request so it stays alive while the request is being processed. + //! This is needed because Streamer supports fire-and-forget requests since completion can be handled by + //! registering a callback. + struct ExternalRequestData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; - //! Holds the progress of an operation chain until this request is explicitly completed. - struct WaitData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; - }; + explicit ExternalRequestData(FileRequestPtr&& request); - //! Checks to see if any node in the stack can find a file at the provided path. - struct FileExistsCheckData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + FileRequestPtr m_request; //!< The request that was send to Streamer. + }; - explicit FileExistsCheckData(const RequestPath& path); + //! Stores an instance of a RequestPath. To reduce copying instances of a RequestPath functions that + //! need a path take them by reference to the original request. In some cases a path originates from + //! within in the stack and temporary storage is needed. This struct allows for that temporary storage + //! so it can be safely referenced later. + struct RequestPathStoreData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; - const RequestPath& m_path; - bool m_found{ false }; - }; + explicit RequestPathStoreData(RequestPath path); - //! Searches for a file in the stack and retrieves the meta data. This may be slower than a file exists - //! check. - struct FileMetaDataRetrievalData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + RequestPath m_path; + }; - explicit FileMetaDataRetrievalData(const RequestPath& path); + //! Request to read and decompress data. + struct CompressedReadData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; - const RequestPath& m_path; - u64 m_fileSize{ 0 }; - bool m_found{ false }; - }; + CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize); - //! Cancels a request in the stream stack, if possible. - struct CancelData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHighest; - inline constexpr static bool s_failWhenUnhandled = false; + CompressionInfo m_compressionInfo; + void* m_output; //!< Target output to write the read data to. + u64 m_readOffset; //!< The offset into the decompressed to start copying from. + u64 m_readSize; //!< Number of bytes to read from the decompressed file. + }; - explicit CancelData(FileRequestPtr target); + //! Holds the progress of an operation chain until this request is explicitly completed. + struct WaitData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; + }; - FileRequestPtr m_target; //!< The request that will be canceled. - }; + //! Checks to see if any node in the stack can find a file at the provided path. + struct FileExistsCheckData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - //! Updates the priority and deadline of a request that has not been queued yet. - struct RescheduleData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + explicit FileExistsCheckData(const RequestPath& path); - RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority); + const RequestPath& m_path; + bool m_found{ false }; + }; - FileRequestPtr m_target; //!< The request that will be rescheduled. - AZStd::chrono::system_clock::time_point m_newDeadline; //!< The new deadline for the request. - IStreamerTypes::Priority m_newPriority; //!< The new priority for the request. - }; + //! Searches for a file in the stack and retrieves the meta data. This may be slower than a file exists + //! check. + struct FileMetaDataRetrievalData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - //! Flushes all references to the provided file in the streaming stack. - struct FlushData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + explicit FileMetaDataRetrievalData(const RequestPath& path); - explicit FlushData(RequestPath path); + const RequestPath& m_path; + u64 m_fileSize{ 0 }; + bool m_found{ false }; + }; - RequestPath m_path; - }; + //! Cancels a request in the stream stack, if possible. + struct CancelData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHighest; + inline constexpr static bool s_failWhenUnhandled = false; - //! Flushes all caches in the streaming stack. - struct FlushAllData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; - }; + explicit CancelData(FileRequestPtr target); - //! Creates a cache dedicated to a single file. This is best used for files where blocks are read from - //! periodically such as audio banks of video files. - struct CreateDedicatedCacheData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + FileRequestPtr m_target; //!< The request that will be canceled. + }; - CreateDedicatedCacheData(RequestPath path, const FileRange& range); + //! Updates the priority and deadline of a request that has not been queued yet. + struct RescheduleData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - RequestPath m_path; - FileRange m_range; - }; + RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority); - //! Destroys a cache dedicated to a single file that was previously created by CreateDedicatedCache - struct DestroyDedicatedCacheData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + FileRequestPtr m_target; //!< The request that will be rescheduled. + AZStd::chrono::system_clock::time_point m_newDeadline; //!< The new deadline for the request. + IStreamerTypes::Priority m_newPriority; //!< The new priority for the request. + }; - DestroyDedicatedCacheData(RequestPath path, const FileRange& range); + //! Flushes all references to the provided file in the streaming stack. + struct FlushData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - RequestPath m_path; - FileRange m_range; - }; + explicit FlushData(RequestPath path); - struct ReportData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityLow; - inline constexpr static bool s_failWhenUnhandled = false; + RequestPath m_path; + }; - enum class ReportType - { - FileLocks - }; + //! Flushes all caches in the streaming stack. + struct FlushAllData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; + }; - explicit ReportData(ReportType reportType); + //! Data for a custom command. This can be used by nodes added extensions that need data that can't be stored + //! in the already provided data. + struct CustomData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - ReportType m_reportType; - }; + CustomData(AZStd::any data, bool failWhenUnhandled); - //! Data for a custom command. This can be used by nodes added extensions that need data that can't be stored - //! in the already provided data. - struct CustomData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + AZStd::any m_data; //!< The data for the custom request. + bool m_failWhenUnhandled; //!< Whether or not the request is marked as failed or success when no node process it. + }; + using CommandVariant = AZStd::variant< + AZStd::monostate, + ExternalRequestData, + RequestPathStoreData, + ReadRequestData, + ReadData, + CompressedReadData, + WaitData, + FileExistsCheckData, + FileMetaDataRetrievalData, + CancelData, + RescheduleData, + FlushData, + FlushAllData, + CreateDedicatedCacheData, + DestroyDedicatedCacheData, + ReportData, + CustomData>; + +} // namespace AZ::IO::Requests - CustomData(AZStd::any data, bool failWhenUnhandled); +namespace AZ::IO +{ + class FileRequest final + { + public: + inline constexpr static AZStd::chrono::system_clock::time_point s_noDeadlineTime = AZStd::chrono::system_clock::time_point::max(); - AZStd::any m_data; //!< The data for the custom request. - bool m_failWhenUnhandled; //!< Whether or not the request is marked as failed or success when no node process it. - }; + friend class StreamerContext; + friend class ExternalFileRequest; - using CommandVariant = AZStd::variant; + using CommandVariant = Requests::CommandVariant; using OnCompletionCallback = AZStd::function; AZ_CLASS_ALLOCATOR(FileRequest, SystemAllocator, 0); @@ -278,7 +312,7 @@ namespace AZ::IO void CreateFlushAll(); void CreateDedicatedCacheCreation(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr); void CreateDedicatedCacheDestruction(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr); - void CreateReport(ReportData::ReportType reportType); + void CreateReport(Requests::ReportType reportType); void CreateCustom(AZStd::any data, bool failWhenUnhandled = true, FileRequest* parent = nullptr); void SetCompletionCallback(OnCompletionCallback callback); @@ -325,17 +359,6 @@ namespace AZ::IO //! Command and parameters for the request. CommandVariant m_command; - //! Status of the request. - AZStd::atomic m_status{ IStreamerTypes::RequestStatus::Pending }; - - //! Called once the request has completed. This will always be called from the Streamer thread - //! and thread safety is the responsibility of called function. When assigning a lambda avoid - //! capturing a FileRequestPtr by value as this will cause a circular reference which causes - //! the FileRequestPtr to never be released and causes a memory leak. This call will - //! block the main Streamer thread until it returns so callbacks should be kept short. If - //! a longer running task is needed consider using a job to do the work. - OnCompletionCallback m_onCompletion; - //! Estimated time this request will complete. This is an estimation and depends on many //! factors which can cause it to change drastically from moment to moment. AZStd::chrono::system_clock::time_point m_estimatedCompletion; @@ -344,9 +367,21 @@ namespace AZ::IO //! other request depending on this one to complete. FileRequest* m_parent{ nullptr }; + //! Id assigned when the request is added to the pending queue. size_t m_pendingId{ 0 }; + //! Called once the request has completed. This will always be called from the Streamer thread + //! and thread safety is the responsibility of called function. When assigning a lambda avoid + //! capturing a FileRequestPtr by value as this will cause a circular reference which causes + //! the FileRequestPtr to never be released and causes a memory leak. This call will + //! block the main Streamer thread until it returns so callbacks should be kept short. If + //! a longer running task is needed consider using a job to do the work. + OnCompletionCallback m_onCompletion; + + //! Status of the request. + AZStd::atomic m_status{ IStreamerTypes::RequestStatus::Pending }; + //! The number of dependent file request that need to complete before this one is done. u16 m_dependencies{ 0 }; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp index 723a5d62c8..ac9344dd28 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp @@ -91,12 +91,12 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { PrepareReadRequest(request, args); } - else if constexpr (AZStd::is_same_v || - AZStd::is_same_v) + else if constexpr (AZStd::is_same_v || + AZStd::is_same_v) { PrepareDedicatedCache(request, args.m_path); } @@ -114,11 +114,11 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { m_pendingReads.push_back(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { m_pendingFileExistChecks.push_back(request); } @@ -203,7 +203,7 @@ namespace AZ::IO { FileRequest* compressedRequest = m_processingJobs[i].m_waitRequest->GetParent(); AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request."); - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(data, "Compressed request in the decompression queue in FullFileDecompressor didn't contain compression read data."); size_t bytesToDecompress = data->m_compressionInfo.m_compressedSize; @@ -255,7 +255,7 @@ namespace AZ::IO // Calculate the amount of time it will take to decompress the data. FileRequest* compressedRequest = m_readRequests[i]->GetParent(); - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); size_t bytesToDecompress = data->m_compressionInfo.m_compressedSize; auto decompressionDuration = AZStd::chrono::microseconds( @@ -290,7 +290,7 @@ namespace AZ::IO void FullFileDecompressor::EstimateCompressedReadRequest(FileRequest* request, AZStd::chrono::microseconds& cumulativeDelay, AZStd::chrono::microseconds decompressionDelay, double totalDecompressionDurationUs, double totalBytesDecompressed) const { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); if (data) { AZStd::chrono::microseconds processingTime = decompressionDelay; @@ -343,7 +343,7 @@ namespace AZ::IO m_numRunningJobs == 0; } - void FullFileDecompressor::PrepareReadRequest(FileRequest* request, FileRequest::ReadRequestData& data) + void FullFileDecompressor::PrepareReadRequest(FileRequest* request, Requests::ReadRequestData &data) { CompressionInfo info; if (CompressionUtils::FindCompressionInfo(info, data.m_path.GetRelativePath())) @@ -359,7 +359,7 @@ namespace AZ::IO { FileRequest* pathStorageRequest = m_context->GetNewInternalRequest(); pathStorageRequest->CreateRequestPathStore(request, AZStd::move(info.m_archiveFilename)); - auto& pathStorage = AZStd::get(pathStorageRequest->GetCommand()); + auto& pathStorage = AZStd::get(pathStorageRequest->GetCommand()); nextRequest->CreateRead(pathStorageRequest, data.m_output, data.m_outputSize, pathStorage.m_path, info.m_offset + data.m_offset, data.m_size, info.m_isSharedPak); @@ -370,13 +370,13 @@ namespace AZ::IO auto callback = [this, nextRequest](const FileRequest& checkRequest) { AZ_PROFILE_FUNCTION(AzCore); - auto check = AZStd::get_if(&checkRequest.GetCommand()); + auto check = AZStd::get_if(&checkRequest.GetCommand()); AZ_Assert(check, "Callback in FullFileDecompressor::PrepareReadRequest expected FileExistsCheck but got another command."); if (check->m_found) { FileRequest* originalRequest = m_context->RejectRequest(nextRequest); - if (AZStd::holds_alternative(originalRequest->GetCommand())) + if (AZStd::holds_alternative(originalRequest->GetCommand())) { originalRequest = m_context->RejectRequest(originalRequest); } @@ -412,12 +412,12 @@ namespace AZ::IO AZStd::visit([request, &info, nextRequest](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { nextRequest->CreateDedicatedCacheCreation(AZStd::move(info.m_archiveFilename), FileRange::CreateRange(info.m_offset, info.m_compressedSize), request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { nextRequest->CreateDedicatedCacheDestruction(AZStd::move(info.m_archiveFilename), FileRange::CreateRange(info.m_offset, info.m_compressedSize), request); @@ -429,7 +429,7 @@ namespace AZ::IO auto callback = [this, nextRequest](const FileRequest& checkRequest) { AZ_PROFILE_FUNCTION(AzCore); - auto check = AZStd::get_if(&checkRequest.GetCommand()); + auto check = AZStd::get_if(&checkRequest.GetCommand()); AZ_Assert(check, "Callback in FullFileDecompressor::PrepareDedicatedCache expected FileExistsCheck but got another command."); if (check->m_found) @@ -461,7 +461,7 @@ namespace AZ::IO void FullFileDecompressor::FileExistsCheck(FileRequest* checkRequest) { - auto& fileCheckRequest = AZStd::get(checkRequest->GetCommand()); + auto& fileCheckRequest = AZStd::get(checkRequest->GetCommand()); CompressionInfo info; if (CompressionUtils::FindCompressionInfo(info, fileCheckRequest.m_path.GetRelativePath())) { @@ -487,7 +487,7 @@ namespace AZ::IO { if (m_readBufferStatus[i] == ReadBufferStatus::Unused) { - auto data = AZStd::get_if(&compressedReadRequest->GetCommand()); + auto data = AZStd::get_if(&compressedReadRequest->GetCommand()); AZ_Assert(data, "Compressed request that's starting a read in FullFileDecompressor didn't contain compression read data."); AZ_Assert(data->m_compressionInfo.m_decompressor, "FileRequest for FullFileDecompressor is missing a decompression callback."); @@ -549,7 +549,7 @@ namespace AZ::IO } else { - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(data, "Compressed request in FullFileDecompressor that finished unsuccessfully didn't contain compression read data."); CompressionInfo& info = data->m_compressionInfo; size_t offsetAdjustment = info.m_offset - AZ_SIZE_ALIGN_DOWN(info.m_offset, aznumeric_cast(m_alignment)); @@ -591,7 +591,7 @@ namespace AZ::IO } FileRequest* waitRequest = m_readRequests[readSlot]; - AZ_Assert(AZStd::holds_alternative(waitRequest->GetCommand()), + AZ_Assert(AZStd::holds_alternative(waitRequest->GetCommand()), "File request waiting for decompression wasn't marked as being a wait operation."); FileRequest* compressedRequest = waitRequest->GetParent(); AZ_Assert(compressedRequest, "Read requests started by FullFileDecompressor is missing a parent request."); @@ -610,7 +610,7 @@ namespace AZ::IO m_readBuffers[readSlot] = nullptr; AZ::Job* decompressionJob; - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(data, "Compressed request in FullFileDecompressor that's starting decompression didn't contain compression read data."); AZ_Assert(data->m_compressionInfo.m_decompressor, "FullFileDecompressor is queuing a decompression job but couldn't find a decompressor."); @@ -664,7 +664,7 @@ namespace AZ::IO FileRequest* compressedRequest = jobInfo.m_waitRequest->GetParent(); AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request."); - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(data, "Compressed request in FullFileDecompressor that completed decompression didn't contain compression read data."); CompressionInfo& info = data->m_compressionInfo; size_t offsetAdjustment = info.m_offset - AZ_SIZE_ALIGN_DOWN(info.m_offset, aznumeric_cast(m_alignment)); @@ -694,7 +694,7 @@ namespace AZ::IO FileRequest* compressedRequest = info.m_waitRequest->GetParent(); AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request."); - auto request = AZStd::get_if(&compressedRequest->GetCommand()); + auto request = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(request, "Compressed request in FullFileDecompressor that's running full decompression didn't contain compression read data."); CompressionInfo& compressionInfo = request->m_compressionInfo; AZ_Assert(compressionInfo.m_decompressor, "Full decompressor job started, but there's no decompressor callback assigned."); @@ -719,7 +719,7 @@ namespace AZ::IO FileRequest* compressedRequest = info.m_waitRequest->GetParent(); AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request."); - auto request = AZStd::get_if(&compressedRequest->GetCommand()); + auto request = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(request, "Compressed request in FullFileDecompressor that's running partial decompression didn't contain compression read data."); CompressionInfo& compressionInfo = request->m_compressionInfo; AZ_Assert(compressionInfo.m_decompressor, "Partial decompressor job started, but there's no decompressor callback assigned."); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.h b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.h index d9bd68f1a1..cb4226fd7c 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.h @@ -21,6 +21,11 @@ namespace AZ::IO { + namespace Requests + { + struct ReadRequestData; + } + struct FullFileDecompressorConfig final : public IStreamerStackConfig { @@ -87,7 +92,7 @@ namespace AZ::IO bool IsIdle() const; - void PrepareReadRequest(FileRequest* request, FileRequest::ReadRequestData& data); + void PrepareReadRequest(FileRequest* request, Requests::ReadRequestData& data); void PrepareDedicatedCache(FileRequest* request, const RequestPath& path); void FileExistsCheck(FileRequest* checkRequest); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/ReadSplitter.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/ReadSplitter.cpp index a952e31a93..282a1e15c9 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/ReadSplitter.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/ReadSplitter.cpp @@ -118,7 +118,7 @@ namespace AZ::IO return; } - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); if (data == nullptr) { StreamStackEntry::QueueRequest(request); @@ -156,7 +156,7 @@ namespace AZ::IO void ReadSplitter::QueueAlignedRead(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command."); if (data->m_size <= m_maxReadSize) @@ -187,7 +187,7 @@ namespace AZ::IO bool ReadSplitter::QueueAlignedRead(PendingRead& pending) { - auto data = AZStd::get_if(&pending.m_request->GetCommand()); + auto data = AZStd::get_if(&pending.m_request->GetCommand()); AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command."); while (pending.m_readSize > 0) @@ -237,7 +237,7 @@ namespace AZ::IO void ReadSplitter::QueueBufferedRead(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command."); PendingRead pendingRead; @@ -262,7 +262,7 @@ namespace AZ::IO bool ReadSplitter::QueueBufferedRead(PendingRead& pending) { - auto data = AZStd::get_if(&pending.m_request->GetCommand()); + auto data = AZStd::get_if(&pending.m_request->GetCommand()); AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command."); while (pending.m_readSize > 0) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp index fe1d5a5eda..65f72946b0 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp @@ -6,9 +6,11 @@ * */ +#include + +#include #include #include -#include #include #include @@ -35,6 +37,10 @@ namespace AZ::IO m_threadData.m_streamStack = AZStd::move(streamStack); } + Scheduler::~Scheduler() + { + } + void Scheduler::Start(const AZStd::thread_desc& threadDesc) { if (!m_isRunning) @@ -222,10 +228,10 @@ namespace AZ::IO { using Command = AZStd::decay_t; if constexpr ( - AZStd::is_same_v || - AZStd::is_same_v) + AZStd::is_same_v || + AZStd::is_same_v) { - auto parentReadRequest = next->GetCommandFromChain(); + auto parentReadRequest = next->GetCommandFromChain(); AZ_Assert(parentReadRequest != nullptr, "The issued read request can't be found for the (compressed) read command."); size_t size = parentReadRequest->m_size; @@ -234,7 +240,7 @@ namespace AZ::IO AZ_Assert(parentReadRequest->m_allocator, "The read request was issued without a memory allocator or valid output address."); u64 recommendedSize = size; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { recommendedSize = m_recommendations.CalculateRecommendedMemorySize(size, parentReadRequest->m_offset); } @@ -249,12 +255,12 @@ namespace AZ::IO parentReadRequest->m_output = allocation.m_address; parentReadRequest->m_outputSize = allocation.m_size; parentReadRequest->m_memoryType = allocation.m_type; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { args.m_output = parentReadRequest->m_output; args.m_outputSize = allocation.m_size; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { args.m_output = parentReadRequest->m_output; } @@ -267,7 +273,7 @@ namespace AZ::IO } #endif - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { m_threadData.m_lastFilePath = args.m_path; m_threadData.m_lastFileOffset = args.m_offset + args.m_size; @@ -275,7 +281,7 @@ namespace AZ::IO m_processingSize += args.m_size; #endif } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { const CompressionInfo& info = args.m_compressionInfo; m_threadData.m_lastFilePath = info.m_archiveFilename; @@ -288,15 +294,15 @@ namespace AZ::IO "Streamer queued %zu: %s", next->GetCommand().index(), parentReadRequest->m_path.GetRelativePath()); m_threadData.m_streamStack->QueueRequest(next); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return Thread_ProcessCancelRequest(next, args); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return Thread_ProcessRescheduleRequest(next, args); } - else if constexpr (AZStd::is_same_v || AZStd::is_same_v) + else if constexpr (AZStd::is_same_v || AZStd::is_same_v) { AZ_PROFILE_INTERVAL_START_COLORED(AzCore, next, ProfilerColor, "Streamer queued %zu", next->GetCommand().index()); @@ -345,7 +351,7 @@ namespace AZ::IO #endif { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { if (args.m_output == nullptr && args.m_allocator != nullptr) { @@ -393,7 +399,7 @@ namespace AZ::IO } } - void Scheduler::Thread_ProcessCancelRequest(FileRequest* request, FileRequest::CancelData& data) + void Scheduler::Thread_ProcessCancelRequest(FileRequest* request, Requests::CancelData& data) { AZ_PROFILE_INTERVAL_START_COLORED(AzCore, request, ProfilerColor, "Streamer queued cancel"); auto& pending = m_context.GetPreparedRequests(); @@ -415,7 +421,7 @@ namespace AZ::IO m_threadData.m_streamStack->QueueRequest(request); } - void Scheduler::Thread_ProcessRescheduleRequest(FileRequest* request, FileRequest::RescheduleData& data) + void Scheduler::Thread_ProcessRescheduleRequest(FileRequest* request, Requests::RescheduleData& data) { AZ_PROFILE_INTERVAL_START_COLORED(AzCore, request, ProfilerColor, "Streamer queued reschedule"); auto& pendingRequests = m_context.GetPreparedRequests(); @@ -424,7 +430,7 @@ namespace AZ::IO if (pending->WorksOn(data.m_target)) { // Read requests are the only requests that use deadlines and dynamic priorities. - auto readRequest = pending->GetCommandFromChain(); + auto readRequest = pending->GetCommandFromChain(); if (readRequest) { readRequest->m_deadline = data.m_newDeadline; @@ -463,8 +469,8 @@ namespace AZ::IO // Order is the same for both requests, so prioritize the request that are at risk of missing // it's deadline. - const FileRequest::ReadRequestData* firstRead = first->GetCommandFromChain(); - const FileRequest::ReadRequestData* secondRead = second->GetCommandFromChain(); + const Requests::ReadRequestData* firstRead = first->GetCommandFromChain(); + const Requests::ReadRequestData* secondRead = second->GetCommandFromChain(); if (firstRead == nullptr || secondRead == nullptr) { @@ -496,11 +502,11 @@ namespace AZ::IO auto sameFile = [this](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { return m_threadData.m_lastFilePath == args.m_path; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return m_threadData.m_lastFilePath == args.m_compressionInfo.m_archiveFilename; } @@ -517,11 +523,11 @@ namespace AZ::IO auto offset = [](auto&& args) -> s64 { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { return aznumeric_caster(args.m_offset); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return aznumeric_caster(args.m_compressionInfo.m_offset); } diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.h b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.h index 053a57d332..502002fd27 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -24,11 +25,19 @@ namespace AZ::IO { class FileRequest; + namespace Requests + { + struct CancelData; + struct RescheduleData; + } // namespace Requests + class Scheduler final { public: explicit Scheduler(AZStd::shared_ptr streamStack, u64 memoryAlignment = AZCORE_GLOBAL_NEW_ALIGNMENT, u64 sizeAlignment = 1, u64 granularity = 1_mib); + ~Scheduler(); + void Start(const AZStd::thread_desc& threadDesc); void Stop(); @@ -61,14 +70,14 @@ namespace AZ::IO bool Thread_ExecuteRequests(); bool Thread_PrepareRequests(AZStd::vector& outstandingRequests); void Thread_ProcessTillIdle(); - void Thread_ProcessCancelRequest(FileRequest* request, FileRequest::CancelData& data); - void Thread_ProcessRescheduleRequest(FileRequest* request, FileRequest::RescheduleData& data); + void Thread_ProcessCancelRequest(FileRequest* request, Requests::CancelData& data); + void Thread_ProcessRescheduleRequest(FileRequest* request, Requests::RescheduleData& data); enum class Order { - FirstRequest, //< The first request is the most important to process next. - SecondRequest, //< The second request is the most important to process next. - Equal //< Both requests are equally important. + FirstRequest, //!< The first request is the most important to process next. + SecondRequest, //!< The second request is the most important to process next. + Equal //!< Both requests are equally important. }; //! Determine which of the two provided requests is more important to process next. Order Thread_PrioritizeRequests(const FileRequest* first, const FileRequest* second) const; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.cpp index cfd191b68f..701eb563ec 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.cpp @@ -60,9 +60,9 @@ namespace AZ::IO AZ_PROFILE_FUNCTION(AzCore); AZ_Assert(request, "PrepareRequest was provided a null request."); - if (AZStd::holds_alternative(request->GetCommand())) + if (AZStd::holds_alternative(request->GetCommand())) { - auto& readRequest = AZStd::get(request->GetCommand()); + auto& readRequest = AZStd::get(request->GetCommand()); FileRequest* read = m_context->GetNewInternalRequest(); read->CreateRead(request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path, @@ -79,29 +79,29 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v || - AZStd::is_same_v || - AZStd::is_same_v) + if constexpr (AZStd::is_same_v || + AZStd::is_same_v || + AZStd::is_same_v) { m_pendingRequests.push_back(request); return; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { CancelRequest(request, args.m_target); return; } else { - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { Report(args); } @@ -118,15 +118,15 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { ReadFile(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileExistsRequest(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileMetaDataRetrievalRequest(request); } @@ -199,25 +199,25 @@ namespace AZ::IO AZStd::visit([&](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { targetFile = &args.m_path; readSize = args.m_size; offset = args.m_offset; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { targetFile = &args.m_compressionInfo.m_archiveFilename; readSize = args.m_compressionInfo.m_compressedSize; offset = args.m_compressionInfo.m_offset; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds averageTime = m_getFileExistsTimeAverage.CalculateAverage(); startTime += averageTime; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds averageTime = m_getFileMetaDataTimeAverage.CalculateAverage(); @@ -254,7 +254,7 @@ namespace AZ::IO { AZ_PROFILE_FUNCTION(AzCore); - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data, "FileRequest queued on StorageDrive to be read didn't contain read data."); SystemFile* file = nullptr; @@ -342,7 +342,7 @@ namespace AZ::IO AZ_PROFILE_FUNCTION(AzCore); TIMED_AVERAGE_WINDOW_SCOPE(m_getFileExistsTimeAverage); - auto& fileExists = AZStd::get(request->GetCommand()); + auto& fileExists = AZStd::get(request->GetCommand()); size_t cacheIndex = FindFileInCache(fileExists.m_path); if (cacheIndex != s_fileNotFound) { @@ -360,7 +360,7 @@ namespace AZ::IO AZ_PROFILE_FUNCTION(AzCore); TIMED_AVERAGE_WINDOW_SCOPE(m_getFileMetaDataTimeAverage); - auto& command = AZStd::get(request->GetCommand()); + auto& command = AZStd::get(request->GetCommand()); // If the file is already open, use the file handle which usually is cheaper than asking for the file by name. size_t cacheIndex = FindFileInCache(command.m_path); if (cacheIndex != s_fileNotFound) @@ -446,11 +446,11 @@ namespace AZ::IO } } - void StorageDrive::Report(const FileRequest::ReportData& data) const + void StorageDrive::Report(const Requests::ReportData& data) const { switch (data.m_reportType) { - case FileRequest::ReportData::ReportType::FileLocks: + case Requests::ReportType::FileLocks: for (u32 i = 0; i < m_fileHandles.size(); ++i) { if (m_fileHandles[i] != nullptr) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.h b/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.h index d90b31eeec..25f3d7b353 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -16,6 +17,11 @@ #include #include +namespace AZ::IO::Requests +{ + struct ReportData; +} + namespace AZ::IO { struct StorageDriveConfig final : @@ -72,7 +78,7 @@ namespace AZ::IO void EstimateCompletionTimeForRequest(FileRequest* request, AZStd::chrono::system_clock::time_point& startTime, const RequestPath*& activeFile, u64& activeOffset) const; - void Report(const FileRequest::ReportData& data) const; + void Report(const Requests::ReportData& data) const; TimedAverageWindow m_fileOpenCloseTimeAverage; TimedAverageWindow m_getFileExistsTimeAverage; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.cpp index e4976f7d1c..dfc156bbee 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -210,7 +211,7 @@ namespace AZ::IO IStreamerTypes::ClaimMemory claimMemory) const { AZ_Assert(request.m_request, "The request handle provided to Streamer::GetReadRequestResult is invalid."); - auto readRequest = AZStd::get_if(&request.m_request->GetCommand()); + auto readRequest = AZStd::get_if(&request.m_request->GetCommand()); if (readRequest != nullptr) { buffer = readRequest->m_output; @@ -281,14 +282,14 @@ namespace AZ::IO } } - FileRequestPtr Streamer::Report(FileRequest::ReportData::ReportType reportType) + FileRequestPtr Streamer::Report(Requests::ReportType reportType) { FileRequestPtr result = CreateRequest(); Report(result, reportType); return result; } - FileRequestPtr& Streamer::Report(FileRequestPtr& request, FileRequest::ReportData::ReportType reportType) + FileRequestPtr& Streamer::Report(FileRequestPtr& request, Requests::ReportType reportType) { request->m_request.CreateReport(reportType); return request; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.h b/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.h index bb363a0c64..7e3dd1a742 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.h @@ -20,6 +20,10 @@ namespace AZStd struct thread_desc; } +namespace AZ::IO::Requests +{ + enum class ReportType : int8_t; +} namespace AZ::IO { @@ -185,9 +189,9 @@ namespace AZ::IO void RecordStatistics(); //! Tells AZ::IO::Streamer the report the information for the report to the output. - FileRequestPtr Report(FileRequest::ReportData::ReportType reportType); + FileRequestPtr Report(Requests::ReportType reportType); //! Tells AZ::IO::Streamer the report the information for the report to the output. - FileRequestPtr& Report(FileRequestPtr& request, FileRequest::ReportData::ReportType reportType); + FileRequestPtr& Report(FileRequestPtr& request, Requests::ReportType reportType); Streamer(const AZStd::thread_desc& threadDesc, AZStd::unique_ptr streamStack); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp index 9a465021c2..25fdb9bdfb 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -207,7 +208,7 @@ namespace AZ { if (m_streamer) { - m_streamer->QueueRequest(m_streamer->Report(AZ::IO::FileRequest::ReportData::ReportType::FileLocks)); + m_streamer->QueueRequest(m_streamer->Report(AZ::IO::Requests::ReportType::FileLocks)); } } diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.cpp index 5cd483bc55..fc5d6468d8 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.cpp @@ -22,6 +22,10 @@ namespace AZ static constexpr char LatePredictionName[] = "Early completions"; static constexpr char MissedDeadlinesName[] = "Missed deadlines"; #endif // AZ_STREAMER_ADD_EXTRA_PROFILING_INFO + + StreamerContext::StreamerContext() + { + } StreamerContext::~StreamerContext() { for (FileRequest* entry : m_internalRecycleBin) @@ -204,7 +208,7 @@ namespace AZ m_latePredictionsPercentageStat.GetMostRecentSample()); } } - auto readRequest = AZStd::get_if(&top->GetCommand()); + auto readRequest = AZStd::get_if(&top->GetCommand()); if (readRequest != nullptr) { m_missedDeadlinePercentageStat.PushSample(now < readRequest->m_deadline ? 0.0 : 1.0); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.h b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.h index 356eb7ddac..f4caaffc70 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.h @@ -7,23 +7,28 @@ */ #pragma once -#include -#include #include #include #include +#include +#include #include -#include #include -#include +#include namespace AZ::IO { + class FileRequest; + class ExternalFileRequest; + + using FileRequestPtr = AZStd::intrusive_ptr; + class StreamerContext { public: using PreparedQueue = AZStd::deque; + StreamerContext(); ~StreamerContext(); //! Gets a new file request, either by creating a new instance or diff --git a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.cpp b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.cpp index feb8bce111..708bacd7cd 100644 --- a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.cpp +++ b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.cpp @@ -172,9 +172,9 @@ namespace AZ::IO AZ_PROFILE_FUNCTION(AzCore); AZ_Assert(request, "PrepareRequest was provided a null request."); - if (AZStd::holds_alternative(request->GetCommand())) + if (AZStd::holds_alternative(request->GetCommand())) { - auto& readRequest = AZStd::get(request->GetCommand()); + auto& readRequest = AZStd::get(request->GetCommand()); if (IsServicedByThisDrive(readRequest.m_path.GetAbsolutePath())) { FileRequest* read = m_context->GetNewInternalRequest(); @@ -195,7 +195,7 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { if (IsServicedByThisDrive(args.m_path.GetAbsolutePath())) { @@ -203,8 +203,8 @@ namespace AZ::IO return; } } - else if constexpr (AZStd::is_same_v || - AZStd::is_same_v) + else if constexpr (AZStd::is_same_v || + AZStd::is_same_v) { if (IsServicedByThisDrive(args.m_path.GetAbsolutePath())) { @@ -212,7 +212,7 @@ namespace AZ::IO return; } } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { if (CancelRequest(request, args.m_target)) { @@ -221,15 +221,15 @@ namespace AZ::IO return; } } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { Report(args); } @@ -257,13 +257,13 @@ namespace AZ::IO hasWorked = AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FileExistsRequest(request); m_pendingRequests.pop_front(); return true; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileMetaDataRetrievalRequest(request); m_pendingRequests.pop_front(); @@ -308,7 +308,7 @@ namespace AZ::IO FileReadInformation& read = m_readSlots_readInfo[i]; u64 totalBytesRead = m_readSizeAverage.GetTotal(); double totalReadTimeUSec = aznumeric_caster(m_readTimeAverage.GetTotal().count()); - auto readCommand = AZStd::get_if(&read.m_request->GetCommand()); + auto readCommand = AZStd::get_if(&read.m_request->GetCommand()); AZ_Assert(readCommand, "Request currently reading doesn't contain a read command."); auto endTime = read.m_startTime + AZStd::chrono::microseconds(aznumeric_cast((readCommand->m_size * totalReadTimeUSec) / totalBytesRead)); earliestSlot = AZStd::min(earliestSlot, endTime); @@ -354,25 +354,25 @@ namespace AZ::IO AZStd::visit([&](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { targetFile = &args.m_path; readSize = args.m_size; offset = args.m_offset; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { targetFile = &args.m_compressionInfo.m_archiveFilename; readSize = args.m_compressionInfo.m_compressedSize; offset = args.m_compressionInfo.m_offset; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds getFileExistsTimeAverage = m_getFileExistsTimeAverage.CalculateAverage(); startTime += getFileExistsTimeAverage; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds getFileExistsTimeAverage = m_getFileMetaDataRetrievalTimeAverage.CalculateAverage(); @@ -411,15 +411,15 @@ namespace AZ::IO AZStd::visit([&, this](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v || - AZStd::is_same_v) + if constexpr (AZStd::is_same_v || + AZStd::is_same_v) { if (IsServicedByThisDrive(args.m_path.GetAbsolutePath())) { EstimateCompletionTimeForRequest(request, startTime, activeFile, activeOffset); } } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { if (IsServicedByThisDrive(args.m_compressionInfo.m_archiveFilename.GetAbsolutePath())) { @@ -435,7 +435,7 @@ namespace AZ::IO aznumeric_cast(m_pendingRequests.size()) - m_activeReads_Count; } - auto StorageDriveWin::OpenFile(HANDLE& fileHandle, size_t& cacheSlot, FileRequest* request, const FileRequest::ReadData& data) -> OpenFileResult + auto StorageDriveWin::OpenFile(HANDLE& fileHandle, size_t& cacheSlot, FileRequest* request, const Requests::ReadData& data) -> OpenFileResult { HANDLE file = INVALID_HANDLE_VALUE; @@ -553,7 +553,7 @@ namespace AZ::IO return false; } - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data, "Read request in StorageDriveWin doesn't contain read data."); HANDLE file = INVALID_HANDLE_VALUE; @@ -780,7 +780,7 @@ namespace AZ::IO void StorageDriveWin::FileExistsRequest(FileRequest* request) { - auto& fileExists = AZStd::get(request->GetCommand()); + auto& fileExists = AZStd::get(request->GetCommand()); AZ_PROFILE_SCOPE(AzCore, "StorageDriveWin::FileExistsRequest %s : %s", m_name.c_str(), fileExists.m_path.GetRelativePath()); @@ -836,7 +836,7 @@ namespace AZ::IO void StorageDriveWin::FileMetaDataRetrievalRequest(FileRequest* request) { - auto& command = AZStd::get(request->GetCommand()); + auto& command = AZStd::get(request->GetCommand()); AZ_PROFILE_SCOPE(AzCore, "StorageDriveWin::FileMetaDataRetrievalRequest %s : %s", m_name.c_str(), command.m_path.GetRelativePath()); @@ -1005,7 +1005,7 @@ namespace AZ::IO FileReadInformation& fileReadInfo = m_readSlots_readInfo[readSlot]; - auto readCommand = AZStd::get_if(&fileReadInfo.m_request->GetCommand()); + auto readCommand = AZStd::get_if(&fileReadInfo.m_request->GetCommand()); AZ_Assert(readCommand != nullptr, "Request stored with the overlapped I/O call did not contain a read request."); if (fileReadInfo.m_sectorAlignedOutput && !encounteredError) @@ -1147,11 +1147,11 @@ namespace AZ::IO StreamStackEntry::CollectStatistics(statistics); } - void StorageDriveWin::Report(const FileRequest::ReportData& data) const + void StorageDriveWin::Report(const Requests::ReportData& data) const { switch (data.m_reportType) { - case FileRequest::ReportData::ReportType::FileLocks: + case Requests::ReportType::FileLocks: if (m_cachesInitialized) { for (u32 i = 0; i < m_maxFileHandles; ++i) diff --git a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.h b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.h index c70eb7804d..5207d094ef 100644 --- a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.h +++ b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -19,6 +20,12 @@ #include #include +namespace AZ::IO::Requests +{ + struct ReadData; + struct ReportData; +} + namespace AZ::IO { class StorageDriveWin @@ -111,7 +118,7 @@ namespace AZ::IO CacheFull }; - OpenFileResult OpenFile(HANDLE& fileHandle, size_t& cacheSlot, FileRequest* request, const FileRequest::ReadData& data); + OpenFileResult OpenFile(HANDLE& fileHandle, size_t& cacheSlot, FileRequest* request, const Requests::ReadData& data); bool ReadRequest(FileRequest* request); bool ReadRequest(FileRequest* request, size_t readSlot); bool CancelRequest(FileRequest* cancelRequest, FileRequestPtr& target); @@ -137,7 +144,7 @@ namespace AZ::IO void FinalizeSingleRequest(FileReadStatus& status, size_t readSlot, DWORD numBytesTransferred, bool isCanceled, bool encounteredError); - void Report(const FileRequest::ReportData& data) const; + void Report(const Requests::ReportData& data) const; TimedAverageWindow m_fileOpenCloseTimeAverage; TimedAverageWindow m_getFileExistsTimeAverage; diff --git a/Code/Framework/AzCore/Tests/Asset/AssetDataStreamTests.cpp b/Code/Framework/AzCore/Tests/Asset/AssetDataStreamTests.cpp index 27903495f1..3846c6cf16 100644 --- a/Code/Framework/AzCore/Tests/Asset/AssetDataStreamTests.cpp +++ b/Code/Framework/AzCore/Tests/Asset/AssetDataStreamTests.cpp @@ -6,6 +6,7 @@ * */ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp b/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp index e312e2058d..95b0626d7f 100644 --- a/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp +++ b/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp @@ -406,7 +406,7 @@ namespace AZ::IO request->CreateFileMetaDataRetrieval(path); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_FALSE(fileMetaData.m_found); EXPECT_EQ(0, fileMetaData.m_fileSize); }); @@ -424,7 +424,7 @@ namespace AZ::IO request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_TRUE(fileMetaData.m_found); EXPECT_EQ(4_kib, fileMetaData.m_fileSize); }); @@ -442,7 +442,7 @@ namespace AZ::IO request->CreateFileMetaDataRetrieval(path); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_FALSE(fileMetaData.m_found); EXPECT_EQ(0, fileMetaData.m_fileSize); }); @@ -460,7 +460,7 @@ namespace AZ::IO request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_TRUE(fileMetaData.m_found); EXPECT_EQ(16_kib, fileMetaData.m_fileSize); }); @@ -484,7 +484,7 @@ namespace AZ::IO request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_TRUE(fileMetaData.m_found); EXPECT_EQ(4_kib, fileMetaData.m_fileSize); }); @@ -502,7 +502,7 @@ namespace AZ::IO request->CreateFileExistsCheck(invalidPath); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_FALSE(fileExistsCheck.m_found); }); @@ -519,7 +519,7 @@ namespace AZ::IO request->CreateFileExistsCheck(path); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_FALSE(fileExistsCheck.m_found); }); @@ -535,7 +535,7 @@ namespace AZ::IO request->CreateFileExistsCheck(m_dummyRequestPath); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_TRUE(fileExistsCheck.m_found); }); @@ -551,7 +551,7 @@ namespace AZ::IO request->CreateFileExistsCheck(m_dummyRequestPath); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_TRUE(fileExistsCheck.m_found); }); @@ -573,7 +573,7 @@ namespace AZ::IO request->CreateFileExistsCheck(m_dummyRequestPath); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_TRUE(fileExistsCheck.m_found); }); @@ -603,7 +603,7 @@ namespace AZ::IO AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); - auto& readRequest = AZStd::get(request.GetCommand()); + auto& readRequest = AZStd::get(request.GetCommand()); EXPECT_EQ(readRequest.m_size, fileSize); EXPECT_STREQ(readRequest.m_path.GetAbsolutePath(), m_dummyFilepath.c_str()); }; @@ -648,7 +648,7 @@ namespace AZ::IO AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); - auto& readRequest = AZStd::get(request.GetCommand()); + auto& readRequest = AZStd::get(request.GetCommand()); EXPECT_EQ(readRequest.m_size, unalignedSize); EXPECT_EQ(readRequest.m_offset, unalignedOffset); EXPECT_STREQ(readRequest.m_path.GetAbsolutePath(), m_dummyFilepath.c_str()); @@ -796,7 +796,7 @@ namespace AZ::IO AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); - auto& readRequest = AZStd::get(request.GetCommand()); + auto& readRequest = AZStd::get(request.GetCommand()); EXPECT_EQ(readRequest.m_size, chunkSize); EXPECT_EQ(readRequest.m_offset, i * chunkSize); }; diff --git a/Code/Framework/AzCore/Tests/Streamer/BlockCacheTests.cpp b/Code/Framework/AzCore/Tests/Streamer/BlockCacheTests.cpp index 3a107da61d..d9d3623fbd 100644 --- a/Code/Framework/AzCore/Tests/Streamer/BlockCacheTests.cpp +++ b/Code/Framework/AzCore/Tests/Streamer/BlockCacheTests.cpp @@ -100,7 +100,7 @@ namespace AZ::IO void QueueReadRequest(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); if (data) { if (m_fakeFileFound) @@ -122,15 +122,15 @@ namespace AZ::IO m_context->MarkRequestAsCompleted(request); } else if ( - AZStd::holds_alternative(request->GetCommand()) || - AZStd::holds_alternative(request->GetCommand())) + AZStd::holds_alternative(request->GetCommand()) || + AZStd::holds_alternative(request->GetCommand())) { request->SetStatus(IStreamerTypes::RequestStatus::Completed); m_context->MarkRequestAsCompleted(request); } - else if (AZStd::holds_alternative(request->GetCommand())) + else if (AZStd::holds_alternative(request->GetCommand())) { - auto& data2 = AZStd::get(request->GetCommand()); + auto& data2 = AZStd::get(request->GetCommand()); data2.m_found = m_fakeFileFound; data2.m_fileSize = m_fakeFileLength; request->SetStatus(m_fakeFileFound ? IStreamerTypes::RequestStatus::Completed : IStreamerTypes::RequestStatus::Failed); @@ -158,16 +158,16 @@ namespace AZ::IO void QueueCanceledReadRequest(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); if (data) { ReadFile(data->m_output, data->m_path, data->m_offset, data->m_size); request->SetStatus(IStreamerTypes::RequestStatus::Canceled); m_context->MarkRequestAsCompleted(request); } - else if (AZStd::holds_alternative(request->GetCommand())) + else if (AZStd::holds_alternative(request->GetCommand())) { - auto& data2 = AZStd::get(request->GetCommand()); + auto& data2 = AZStd::get(request->GetCommand()); data2.m_found = true; data2.m_fileSize = m_fakeFileLength; request->SetStatus(IStreamerTypes::RequestStatus::Completed); diff --git a/Code/Framework/AzCore/Tests/Streamer/FullDecompressorTests.cpp b/Code/Framework/AzCore/Tests/Streamer/FullDecompressorTests.cpp index 9f2f0dbd6b..f465a0dafb 100644 --- a/Code/Framework/AzCore/Tests/Streamer/FullDecompressorTests.cpp +++ b/Code/Framework/AzCore/Tests/Streamer/FullDecompressorTests.cpp @@ -145,7 +145,7 @@ namespace AZ::IO void PrepareReadRequest(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); ASSERT_NE(nullptr, data); u64 size = data->m_size >> 2; diff --git a/Code/Framework/AzCore/Tests/Streamer/IStreamerMock.h b/Code/Framework/AzCore/Tests/Streamer/IStreamerMock.h index 7b784caffd..0059a25b93 100644 --- a/Code/Framework/AzCore/Tests/Streamer/IStreamerMock.h +++ b/Code/Framework/AzCore/Tests/Streamer/IStreamerMock.h @@ -9,6 +9,7 @@ #include #include +#include using namespace AZ::IO; diff --git a/Code/Framework/AzCore/Tests/Streamer/ReadSplitterTests.cpp b/Code/Framework/AzCore/Tests/Streamer/ReadSplitterTests.cpp index a68ce091b8..01fe2f6a12 100644 --- a/Code/Framework/AzCore/Tests/Streamer/ReadSplitterTests.cpp +++ b/Code/Framework/AzCore/Tests/Streamer/ReadSplitterTests.cpp @@ -155,7 +155,7 @@ namespace AZ::IO { EXPECT_EQ(subRequests[i]->GetParent(), readRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); ASSERT_NE(nullptr, data); EXPECT_EQ(SplitSize, data->m_size); EXPECT_EQ(SplitSize * i, data->m_offset); @@ -210,7 +210,7 @@ namespace AZ::IO { EXPECT_EQ(subRequests[i]->GetParent(), readRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); ASSERT_NE(nullptr, data); EXPECT_EQ(SplitSize, data->m_size); EXPECT_EQ(SplitSize * i, data->m_offset); @@ -230,7 +230,7 @@ namespace AZ::IO { EXPECT_EQ(subRequests[i]->GetParent(), readRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); ASSERT_NE(nullptr, data); EXPECT_EQ(SplitSize, data->m_size); EXPECT_EQ(SplitSize * (batchSize + i), data->m_offset); @@ -265,7 +265,7 @@ namespace AZ::IO m_readSplitter->QueueRequest(readRequest); ASSERT_NE(nullptr, subRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequest->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequest->GetCommand()); EXPECT_NE(buffer, data->m_output); EXPECT_EQ(readSize, data->m_size); EXPECT_EQ(0, data->m_offset); @@ -311,7 +311,7 @@ namespace AZ::IO m_readSplitter->QueueRequest(readRequest); ASSERT_NE(nullptr, subRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequest->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequest->GetCommand()); EXPECT_NE(buffer, data->m_output); EXPECT_EQ(readSize + offsetAdjustment, data->m_size); EXPECT_EQ(0, data->m_offset); diff --git a/Code/Framework/AzCore/Tests/Streamer/SchedulerTests.cpp b/Code/Framework/AzCore/Tests/Streamer/SchedulerTests.cpp index 6c360b97de..d1484b7409 100644 --- a/Code/Framework/AzCore/Tests/Streamer/SchedulerTests.cpp +++ b/Code/Framework/AzCore/Tests/Streamer/SchedulerTests.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -86,7 +87,7 @@ namespace AZ::IO .WillOnce([this](FileRequest* request) { AZ_Assert(m_streamerContext, "AZ::IO::Streamer is not ready to process requests."); - auto readData = AZStd::get_if(&request->GetCommand()); + auto readData = AZStd::get_if(&request->GetCommand()); AZ_Assert(readData, "Test didn't pass in the correct request."); FileRequest* read = m_streamerContext->GetNewInternalRequest(); read->CreateRead(request, readData->m_output, readData->m_outputSize, readData->m_path, @@ -99,7 +100,7 @@ namespace AZ::IO .WillOnce([this](FileRequest* request) { AZ_Assert(m_streamerContext, "AZ::IO::Streamer is not ready to process requests."); - auto readData = AZStd::get_if(&request->GetCommand()); + auto readData = AZStd::get_if(&request->GetCommand()); AZ_Assert(readData, "Test didn't pass in the correct request."); auto output = reinterpret_cast(readData->m_output); AZ_Assert(output != nullptr, "Output buffer has not been set."); @@ -304,7 +305,7 @@ namespace AZ::IO EXPECT_CALL(*m_mock, QueueRequest(_)).Times(1) .WillOnce(Invoke([this](FileRequest* request) { - auto* read = request->GetCommandFromChain(); + auto* read = request->GetCommandFromChain(); ASSERT_NE(nullptr, read); EXPECT_LT(read->m_deadline, FileRequest::s_noDeadlineTime); EXPECT_EQ(read->m_priority, IStreamerTypes::s_priorityHighest); diff --git a/Code/Framework/AzCore/Tests/Streamer/StreamStackEntryConformityTests.h b/Code/Framework/AzCore/Tests/Streamer/StreamStackEntryConformityTests.h index 6cbec7ea8a..8a5805cccd 100644 --- a/Code/Framework/AzCore/Tests/Streamer/StreamStackEntryConformityTests.h +++ b/Code/Framework/AzCore/Tests/Streamer/StreamStackEntryConformityTests.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/StreamerTests.cpp b/Code/Framework/AzCore/Tests/StreamerTests.cpp index 78f9f9060f..937b6d29de 100644 --- a/Code/Framework/AzCore/Tests/StreamerTests.cpp +++ b/Code/Framework/AzCore/Tests/StreamerTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp index bb361e56de..35c23d119a 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.cpp b/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.cpp index 589c805871..23d275cb78 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.cpp +++ b/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.cpp @@ -83,9 +83,9 @@ namespace AzFramework AZ_PROFILE_FUNCTION(AzCore); AZ_Assert(request, "PrepareRequest was provided a null request."); - if (AZStd::holds_alternative(request->GetCommand())) + if (AZStd::holds_alternative(request->GetCommand())) { - auto& readRequest = AZStd::get(request->GetCommand()); + auto& readRequest = AZStd::get(request->GetCommand()); FileRequest* read = m_context->GetNewInternalRequest(); read->CreateRead(request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path, @@ -106,14 +106,14 @@ namespace AzFramework { using namespace AZ::IO; using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v || - AZStd::is_same_v || - AZStd::is_same_v) + if constexpr (AZStd::is_same_v || + AZStd::is_same_v || + AZStd::is_same_v) { m_pendingRequests.push_back(request); return; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { if (CancelRequest(request, args.m_target)) { @@ -124,15 +124,15 @@ namespace AzFramework } else { - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { Report(args); } @@ -152,15 +152,15 @@ namespace AzFramework { using namespace AZ::IO; using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { ReadFile(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileExistsRequest(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileMetaDataRetrievalRequest(request); } @@ -232,23 +232,23 @@ namespace AzFramework { using namespace AZ::IO; using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { targetFile = &args.m_path; readSize = args.m_size; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { targetFile = &args.m_compressionInfo.m_archiveFilename; readSize = args.m_compressionInfo.m_compressedSize; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds averageTime = m_getFileExistsTimeAverage.CalculateAverage(); startTime += averageTime; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds averageTime = m_getFileMetaDataTimeAverage.CalculateAverage(); @@ -280,7 +280,7 @@ namespace AzFramework AZ_PROFILE_FUNCTION(AzCore); - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data, "Request doing reading in the RemoteStorageDrive didn't contain read data."); HandleType file = InvalidHandle; @@ -397,7 +397,7 @@ namespace AzFramework TIMED_AVERAGE_WINDOW_SCOPE(m_getFileExistsTimeAverage); - auto& fileExists = AZStd::get(request->GetCommand()); + auto& fileExists = AZStd::get(request->GetCommand()); size_t cacheIndex = FindFileInCache(fileExists.m_path); if (cacheIndex != s_fileNotFound) { @@ -430,7 +430,7 @@ namespace AzFramework AZ::u64 fileSize = 0; bool found = false; - auto& command = AZStd::get(request->GetCommand()); + auto& command = AZStd::get(request->GetCommand()); // If the file is already open, use the file handle which usually is cheaper than asking for the file by name. size_t cacheIndex = FindFileInCache(command.m_path); if (cacheIndex != s_fileNotFound) @@ -526,13 +526,13 @@ namespace AzFramework StreamStackEntry::CollectStatistics(statistics); } - void RemoteStorageDrive::Report(const AZ::IO::FileRequest::ReportData& data) const + void RemoteStorageDrive::Report(const AZ::IO::Requests::ReportData& data) const { using namespace AZ::IO; switch (data.m_reportType) { - case FileRequest::ReportData::ReportType::FileLocks: + case Requests::ReportType::FileLocks: for (AZ::u32 i = 0; i < m_fileHandles.size(); ++i) { if (m_fileHandles[i] != InvalidHandle) diff --git a/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.h b/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.h index c3e28f2e55..de0b855dcc 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.h +++ b/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.h @@ -16,6 +16,15 @@ #include #include +namespace AZ::IO +{ + class RequestPath; + namespace Requests + { + struct ReportData; + } +} + namespace AzFramework { struct RemoteStorageDriveConfig final : @@ -63,7 +72,7 @@ namespace AzFramework const AZ::IO::RequestPath*& activeFile) const; void FlushCache(const AZ::IO::RequestPath& filePath); void FlushEntireCache(); - void Report(const AZ::IO::FileRequest::ReportData& data) const; + void Report(const AZ::IO::Requests::ReportData& data) const; AZ::IO::RemoteFileIO m_fileIO; AZ::IO::TimedAverageWindow m_fileOpenCloseTimeAverage; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.cpp index 168152f686..bb1270a387 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyAutomation.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyAutomation.cpp index e78ff49f2d..a86ef12216 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyAutomation.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyAutomation.cpp @@ -8,6 +8,7 @@ #include +#include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.cpp index b51298a797..005ce291f6 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp index a5ebdd04c6..1937c50555 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp @@ -9,6 +9,7 @@ #include #include +#include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp index 2ff4780c9e..a0bb2d9366 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp @@ -8,6 +8,7 @@ #include +#include #include namespace AzPhysics diff --git a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp index 79365f1ebe..b2c36bc1c8 100644 --- a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h index a68b82468e..56a3e7005c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h @@ -7,6 +7,7 @@ */ #pragma once +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/Model/AssetCompleterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/Model/AssetCompleterModel.h index 4596d70657..34b3a0a0b6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/Model/AssetCompleterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/Model/AssetCompleterModel.h @@ -10,12 +10,13 @@ #if !defined(Q_MOC_RUN) #include +#include #endif namespace AzToolsFramework { using namespace AzToolsFramework::AssetBrowser; - + //! Model storing all the files that can be suggested in the Asset Autocompleter for PropertyAssetCtrl class AssetCompleterModel : public QAbstractTableModel @@ -45,7 +46,7 @@ namespace AzToolsFramework void SetFetchEntryType(AssetBrowserEntry::AssetEntryType entryType); private: - struct AssetItem + struct AssetItem { AZStd::string m_displayName; AZStd::string m_path; diff --git a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp index 78244ab02c..78e71ab3f2 100644 --- a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h index 982de92739..838cdd2256 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h @@ -7,13 +7,15 @@ */ #pragma once -#include -#include #include #include #include #include +#include +#include +#include + namespace AZ { class ReflectContext; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h index ce5a4c8a7a..701b90ace9 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h @@ -10,6 +10,7 @@ #include #include +#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index bd196a2e2b..b97ffecccd 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -267,7 +267,7 @@ namespace AZ return m_visScene->GetEntryCount(); } - + struct WorklistData { CullingDebugContext* m_debugCtx = nullptr; @@ -296,13 +296,13 @@ namespace AZ #endif return worklistData; } - + constexpr size_t WorkListCapacity = 5; using WorkListType = AZStd::fixed_vector; #if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED static MaskedOcclusionCulling::CullingResult TestOcclusionCulling( - const AZStd::shared_ptr& worklistData, + const AZStd::shared_ptr& worklistData, AzFramework::VisibilityEntry* visibleEntry); #endif @@ -320,8 +320,8 @@ namespace AZ for (const AzFramework::IVisibilityScene::NodeData& nodeData : worklist) { //If a node is entirely contained within the frustum, then we can skip the fine grained culling. - bool nodeIsContainedInFrustum = - !worklistData->m_debugCtx->m_enableFrustumCulling || + bool nodeIsContainedInFrustum = + !worklistData->m_debugCtx->m_enableFrustumCulling || ShapeIntersection::Contains(worklistData->m_frustum, nodeData.m_bounds); #ifdef AZ_CULL_PROFILE_VERBOSE @@ -460,12 +460,14 @@ namespace AZ cullStats.m_numVisibleCullables += numVisibleCullables; ++cullStats.m_numJobs; } +#else + (void)numDrawPackets; // prevent unused variable warning->error #endif //AZ_CULL_DEBUG_ENABLED } #if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED static MaskedOcclusionCulling::CullingResult TestOcclusionCulling( - const AZStd::shared_ptr& worklistData, + const AZStd::shared_ptr& worklistData, AzFramework::VisibilityEntry* visibleEntry) { if (!worklistData->m_maskedOcclusionCulling) @@ -527,9 +529,9 @@ namespace AZ #endif void CullingScene::ProcessCullablesCommon( - const Scene& scene [[maybe_unused]], - View& view, - AZ::Frustum& frustum [[maybe_unused]], + const Scene& scene [[maybe_unused]], + View& view, + AZ::Frustum& frustum [[maybe_unused]], void*& maskedOcclusionCulling [[maybe_unused]]) { AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesCommon() - %s", view.GetName().GetCStr()); @@ -898,7 +900,7 @@ namespace AZ { const Matrix4x4& worldToClip = viewPtr->GetWorldToClipMatrix(); Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip, Frustum::ReverseDepth::True); - m_debugCtx.m_frozenFrustums.insert({ viewPtr.get(), frustum }); + m_debugCtx.m_frozenFrustums.insert({ viewPtr.get(), frustum }); } } } @@ -911,7 +913,7 @@ namespace AZ } void CullingScene::EndCulling() - { + { m_cullDataConcurrencyCheck.soft_unlock(); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp index e11624a921..460e671b4c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp @@ -11,13 +11,13 @@ #include +#include +#include #include #include #include - #include -#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.cpp b/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.cpp index fd19c6bcb8..83e8dbf085 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.cpp +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.cpp @@ -7,6 +7,8 @@ */ #include "AssetManagerTestFixture.h" + +#include #include #include diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h index d1027daa36..b974b4443d 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 58b3d8b56e..35ae1b1257 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -38,6 +38,8 @@ #include #include +#include + namespace AZ::Render { static constexpr uint32_t s_maxActiveWrinkleMasks = 16; diff --git a/Gems/AtomTressFX/Code/Rendering/SharedBuffer.cpp b/Gems/AtomTressFX/Code/Rendering/SharedBuffer.cpp index b31d9f8ae0..6f97613c89 100644 --- a/Gems/AtomTressFX/Code/Rendering/SharedBuffer.cpp +++ b/Gems/AtomTressFX/Code/Rendering/SharedBuffer.cpp @@ -16,6 +16,8 @@ #include #include +#include + namespace AZ::Render { //! Setting the constructor as private will create compile error to remind the developer to set diff --git a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp index 2d95ddf4dd..ce17803531 100644 --- a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp @@ -8,6 +8,7 @@ #include +#include namespace Audio { @@ -285,5 +286,22 @@ namespace Audio return sResult; } + CATLAudioFileEntry::CATLAudioFileEntry(const char * const filePath, IATLAudioFileEntryData * const implData) + : m_filePath(filePath) + , m_fileSize(0) + , m_useCount(0) + , m_memoryBlockAlignment(AUDIO_MEMORY_ALIGNMENT) + , m_flags(eAFF_NOTFOUND) + , m_dataScope(eADS_ALL) + , m_memoryBlock(nullptr) + , m_implData(implData) + { + } + + CATLAudioFileEntry::~CATLAudioFileEntry() + { + + } + #endif // !AUDIO_RELEASE } // namespace Audio diff --git a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.h b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.h index f1ff74c370..4dfceeebfc 100644 --- a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.h +++ b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.h @@ -379,19 +379,9 @@ namespace Audio class CATLAudioFileEntry { public: - explicit CATLAudioFileEntry(const char* const filePath = nullptr, IATLAudioFileEntryData* const implData = nullptr) - : m_filePath(filePath) - , m_fileSize(0) - , m_useCount(0) - , m_memoryBlockAlignment(AUDIO_MEMORY_ALIGNMENT) - , m_flags(eAFF_NOTFOUND) - , m_dataScope(eADS_ALL) - , m_memoryBlock(nullptr) - , m_implData(implData) - { - } + explicit CATLAudioFileEntry(const char* const filePath = nullptr, IATLAudioFileEntryData* const implData = nullptr); - ~CATLAudioFileEntry() = default; + ~CATLAudioFileEntry(); AZStd::string m_filePath; size_t m_fileSize; diff --git a/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp b/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp index ffa94ad956..d1f2187942 100644 --- a/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/AudioSystem/Code/Tests/AudioSystemTest.cpp b/Gems/AudioSystem/Code/Tests/AudioSystemTest.cpp index 14e9f74fd2..a197fee96b 100644 --- a/Gems/AudioSystem/Code/Tests/AudioSystemTest.cpp +++ b/Gems/AudioSystem/Code/Tests/AudioSystemTest.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/AudioSystem/Code/Tests/Mocks/FileCacheManagerMock.h b/Gems/AudioSystem/Code/Tests/Mocks/FileCacheManagerMock.h index 06646216ae..1fb48bb3fb 100644 --- a/Gems/AudioSystem/Code/Tests/Mocks/FileCacheManagerMock.h +++ b/Gems/AudioSystem/Code/Tests/Mocks/FileCacheManagerMock.h @@ -13,6 +13,11 @@ #include #include +namespace AZ::IO +{ + class FileRequestHandle; +} + namespace Audio { class FileCacheManagerMock diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp index c196f34716..bde1259c44 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp @@ -34,6 +34,8 @@ #include #include +#include + namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(ActorInstance, ActorInstanceAllocator, 0) diff --git a/Gems/EMotionFX/Code/Tests/TestAssetCode/SimpleActors.cpp b/Gems/EMotionFX/Code/Tests/TestAssetCode/SimpleActors.cpp index 3e1dac41c2..e6415817cc 100644 --- a/Gems/EMotionFX/Code/Tests/TestAssetCode/SimpleActors.cpp +++ b/Gems/EMotionFX/Code/Tests/TestAssetCode/SimpleActors.cpp @@ -15,6 +15,8 @@ #include #include +#include + namespace EMotionFX { SimpleJointChainActor::SimpleJointChainActor(size_t jointCount, const char* name) diff --git a/Gems/LmbrCentral/Code/Source/Asset/AssetSystemDebugComponent.cpp b/Gems/LmbrCentral/Code/Source/Asset/AssetSystemDebugComponent.cpp index acd1bc8dae..91ba63ddd1 100644 --- a/Gems/LmbrCentral/Code/Source/Asset/AssetSystemDebugComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Asset/AssetSystemDebugComponent.cpp @@ -9,7 +9,9 @@ #include "AssetSystemDebugComponent.h" #include "ISystem.h" #include "IRenderAuxGeom.h" + #include "AzCore/Asset/AssetManager.h" +#include #include #include #include diff --git a/Gems/LmbrCentral/Code/Source/Audio/AudioAreaEnvironmentComponent.cpp b/Gems/LmbrCentral/Code/Source/Audio/AudioAreaEnvironmentComponent.cpp index 827e20b02b..3b14822076 100644 --- a/Gems/LmbrCentral/Code/Source/Audio/AudioAreaEnvironmentComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Audio/AudioAreaEnvironmentComponent.cpp @@ -8,6 +8,7 @@ #include "AudioAreaEnvironmentComponent.h" +#include #include #include #include diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.h b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.h index a400f742de..baac5b3776 100644 --- a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.h +++ b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace NvCloth diff --git a/Gems/NvCloth/Code/Source/Utils/AssetHelper.h b/Gems/NvCloth/Code/Source/Utils/AssetHelper.h index e3e5a5e7ad..4a40fe26f0 100644 --- a/Gems/NvCloth/Code/Source/Utils/AssetHelper.h +++ b/Gems/NvCloth/Code/Source/Utils/AssetHelper.h @@ -10,6 +10,7 @@ #include #include +#include #include diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h index 3f6d001112..11215ef29b 100644 --- a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h index 843fffd4c8..c7a50c43f5 100644 --- a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/Gems/PhysX/Code/Source/ForceRegionComponent.cpp b/Gems/PhysX/Code/Source/ForceRegionComponent.cpp index f8f5f1991e..eba902049d 100644 --- a/Gems/PhysX/Code/Source/ForceRegionComponent.cpp +++ b/Gems/PhysX/Code/Source/ForceRegionComponent.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include diff --git a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp index 975fec2181..c02324ed18 100644 --- a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp +++ b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp @@ -6,18 +6,22 @@ * */ -#include -#include -#include -#include +#include #include #include #include #include -#include #include +#include +#include +#include +#include +#include + + + namespace PhysX::Utils { struct PxJointActorData diff --git a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h index da7b3dc4e2..d5aa082763 100644 --- a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h +++ b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h @@ -14,8 +14,15 @@ #include +#include + namespace PhysX { + struct D6JointLimitConfiguration; + struct FixedJointConfiguration; + struct BallJointConfiguration; + struct HingeJointConfiguration; + namespace JointConstants { // Setting joint limits to very small values can cause extreme stability problems, so clamp above a small diff --git a/Gems/PhysX/Code/Source/JointComponent.cpp b/Gems/PhysX/Code/Source/JointComponent.cpp index d5c04598a5..085fc34b12 100644 --- a/Gems/PhysX/Code/Source/JointComponent.cpp +++ b/Gems/PhysX/Code/Source/JointComponent.cpp @@ -5,17 +5,18 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ +#include +#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include #include -#include namespace PhysX { diff --git a/Gems/PhysX/Code/Source/Material.cpp b/Gems/PhysX/Code/Source/Material.cpp index be79728cc5..fa131b5159 100644 --- a/Gems/PhysX/Code/Source/Material.cpp +++ b/Gems/PhysX/Code/Source/Material.cpp @@ -7,9 +7,10 @@ */ #include "Material.h" +#include #include -#include #include +#include #include namespace PhysX diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp index f4cdd01889..fbadb8aa79 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp @@ -5,13 +5,10 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - #include + #include #include -#include -#include -#include #include #include #include @@ -20,6 +17,12 @@ #include #include +#include +#include +#include + +#include + namespace PhysX::Utils::Characters { AZ::Outcome GetNodeIndex(const Physics::RagdollConfiguration& configuration, const AZStd::string& nodeName) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp index 34fe3d0295..3e240f3611 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp @@ -5,16 +5,17 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - -#include -#include -#include #include + #include #include #include - #include +#include +#include +#include +#include + namespace PhysX { diff --git a/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.cpp b/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.cpp index 2d05612d74..ece6acbd6f 100644 --- a/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.cpp @@ -6,15 +6,16 @@ * */ +#include #include #include +#include #include #include #include #include #include -#include #include #include diff --git a/Gems/PhysX/Code/Source/Pipeline/StreamWrapper.h b/Gems/PhysX/Code/Source/Pipeline/StreamWrapper.h index 15f9bfb36e..d987b7194c 100644 --- a/Gems/PhysX/Code/Source/Pipeline/StreamWrapper.h +++ b/Gems/PhysX/Code/Source/Pipeline/StreamWrapper.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace PhysX diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index dfac43b703..6c02697e90 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -7,14 +7,6 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include @@ -31,6 +23,16 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + namespace PhysX { AZ_CLASS_ALLOCATOR_IMPL(PhysXScene, AZ::SystemAllocator, 0); diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp index 168adc8910..d7ce797b47 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp @@ -5,18 +5,20 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include -#include -#include +#include +#include +#include #include -#include #include #include -#include +#include +#include +#include -#include +#include +#include +#include // only enable physx timestep warning when not running debug or in Release #if !defined(DEBUG) && !defined(RELEASE) diff --git a/Gems/PhysX/Code/Tests/PhysXTestUtil.cpp b/Gems/PhysX/Code/Tests/PhysXTestUtil.cpp index 2366f65e07..f79b7c8d34 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestUtil.cpp +++ b/Gems/PhysX/Code/Tests/PhysXTestUtil.cpp @@ -9,6 +9,7 @@ #include "PhysXTestUtil.h" #include #include +#include namespace PhysX { diff --git a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp index 65115f2790..746809a7ca 100644 --- a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp +++ b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp @@ -8,10 +8,7 @@ #include "SystemComponent.h" -#include -#include -#include -#include +#include #include #include @@ -19,20 +16,24 @@ #include #include +#include +#include +#include +#include + +#include #include #include #include #include #include -#include - -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include namespace PhysXDebug { diff --git a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasMemoryAsset.cpp b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasMemoryAsset.cpp index 439f9a8383..d9ec1de757 100644 --- a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasMemoryAsset.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasMemoryAsset.cpp @@ -10,11 +10,14 @@ #include "ScriptCanvasMemoryAsset.h" #include "ScriptCanvasUndoHelper.h" -#include +#include +#include +#include #include +#include #include +#include #include -#include namespace ScriptCanvasEditor { diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp index 49a35eac8a..95b4fbb843 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Gems/ScriptEvents/Code/Source/ScriptEventsSystemComponent.h b/Gems/ScriptEvents/Code/Source/ScriptEventsSystemComponent.h index 64a0e49bca..922bfb1dbc 100644 --- a/Gems/ScriptEvents/Code/Source/ScriptEventsSystemComponent.h +++ b/Gems/ScriptEvents/Code/Source/ScriptEventsSystemComponent.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxVertexTranslationModifier.h b/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxVertexTranslationModifier.h index a9565aedbe..d9bae70a31 100644 --- a/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxVertexTranslationModifier.h +++ b/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxVertexTranslationModifier.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include From 90f710b8c2564547293487a53af0b474572a8481 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sun, 31 Oct 2021 09:48:06 -0700 Subject: [PATCH 02/61] feat: add cursor wrapped mode Signed-off-by: Michael Pollind --- Code/Editor/EditorViewportSettings.cpp | 11 ++ Code/Editor/EditorViewportSettings.h | 3 + .../test_ModularViewportCameraController.cpp | 4 +- .../Input/QtEventToAzInputMapper.cpp | 107 ++++++++++++++---- .../Input/QtEventToAzInputMapper.h | 19 +++- .../Source/Viewport/RenderViewportWidget.cpp | 4 +- 6 files changed, 121 insertions(+), 27 deletions(-) diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index e06b9696e1..5711d1c3d3 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -23,6 +23,7 @@ namespace SandboxEditor constexpr AZStd::string_view AngleSizeSetting = "/Amazon/Preferences/Editor/AngleSize"; constexpr AZStd::string_view ShowGridSetting = "/Amazon/Preferences/Editor/ShowGrid"; constexpr AZStd::string_view StickySelectSetting = "/Amazon/Preferences/Editor/StickySelect"; + constexpr AZStd::string_view ManipulatorMouseWrapSetting = "/Amazon/Preferences/Editor/Manipulator/MouseWrapping"; constexpr AZStd::string_view ManipulatorLineBoundWidthSetting = "/Amazon/Preferences/Editor/Manipulator/LineBoundWidth"; constexpr AZStd::string_view ManipulatorCircleBoundWidthSetting = "/Amazon/Preferences/Editor/Manipulator/CircleBoundWidth"; constexpr AZStd::string_view CameraTranslateSpeedSetting = "/Amazon/Preferences/Editor/Camera/TranslateSpeed"; @@ -186,6 +187,16 @@ namespace SandboxEditor AzToolsFramework::SetRegistry(ManipulatorLineBoundWidthSetting, lineBoundWidth); } + bool ManipulatorMouseWrap() + { + return aznumeric_cast(GetRegistry(ManipulatorMouseWrapSetting, false)); + } + + void SetManipulatorMouseWrap(bool wrapping) + { + SetRegistry(ManipulatorMouseWrapSetting, wrapping); + } + float ManipulatorCircleBoundWidth() { return aznumeric_cast(AzToolsFramework::GetRegistry(ManipulatorCircleBoundWidthSetting, 0.1)); diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index fe1253ed0c..975feff307 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -57,6 +57,9 @@ namespace SandboxEditor SANDBOX_API float ManipulatorLineBoundWidth(); SANDBOX_API void SetManipulatorLineBoundWidth(float lineBoundWidth); + SANDBOX_API bool ManipulatorMouseWrap(); + SANDBOX_API void SetManipulatorMouseWrap(bool wrapping); + SANDBOX_API float ManipulatorCircleBoundWidth(); SANDBOX_API void SetManipulatorCircleBoundWidth(float circleBoundWidth); diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index 67d89ad967..f2ee3c79aa 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -47,12 +47,12 @@ namespace UnitTest void ViewportMouseCursorRequestImpl::BeginCursorCapture() { - m_inputChannelMapper->SetCursorCaptureEnabled(true); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CURSOR_MODE_CAPTURED); } void ViewportMouseCursorRequestImpl::EndCursorCapture() { - m_inputChannelMapper->SetCursorCaptureEnabled(false); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CURSOR_MODE_NONE); } bool ViewportMouseCursorRequestImpl::IsMouseOver() const diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp index 066bdc1654..0b6df58c1a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp @@ -241,25 +241,42 @@ namespace AzToolsFramework } } - void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled) + + void QtEventToAzInputMapper::SetCursorMode(QtEventToAzInputMapper::CursorInputMode mode) { - if (m_capturingCursor != enabled) + if(mode != m_cursorMode) { - m_capturingCursor = enabled; - - if (m_capturingCursor) + m_cursorMode = mode; + switch(m_cursorMode) { - m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden); - qApp->setOverrideCursor(Qt::BlankCursor); - } - else - { - m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); - qApp->restoreOverrideCursor(); + case CURSOR_MODE_CAPTURED: + qApp->setOverrideCursor(Qt::BlankCursor); + m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden); + break; + case CURSOR_MODE_WRAPPED: + qApp->restoreOverrideCursor(); + m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); + break; + case CURSOR_MODE_NONE: + qApp->restoreOverrideCursor(); + m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); + break; } } } + void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled) + { + if (enabled) + { + SetCursorMode(CURSOR_MODE_CAPTURED); + } + else + { + SetCursorMode(CURSOR_MODE_NONE); + } + } + bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event) { // Abort if processing isn't enabled. @@ -436,25 +453,73 @@ namespace AzToolsFramework return QPoint{ denormalizedX, denormalizedY }; } + void wrapCursorX(const QRect& rect, QPoint& point) { + if (rect.left() < point.x()) + { + point.setX(rect.right() - 1); + } + else if (rect.right() > point.x()) + { + point.setX(rect.left() + 1); + } + } + + void wrapCursorY(const QRect& rect, QPoint& point) { + if (rect.top() < point.y()) + { + point.setY(rect.bottom() - 1); + } + else if (rect.bottom() > point.y()) + { + point.setY(rect.top() + 1); + } + } + + void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& globalCursorPosition) { const QPoint cursorDelta = globalCursorPosition - m_previousGlobalCursorPosition; + QScreen* screen = m_sourceWidget->screen(); + const QRect widgetRect(m_sourceWidget->mapToGlobal(QPoint(0,0)), m_sourceWidget->size()); m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = WidgetPositionToNormalizedPosition(m_sourceWidget->mapFromGlobal(globalCursorPosition)); m_mouseDevice->m_cursorPositionData2D->m_normalizedPositionDelta = WidgetPositionToNormalizedPosition(cursorDelta); - ProcessPendingMouseEvents(cursorDelta); - - if (m_capturingCursor) - { - // Reset our cursor position to the previous point - AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition); - } - else + switch(m_cursorMode) { - m_previousGlobalCursorPosition = globalCursorPosition; + case CURSOR_MODE_CAPTURED: + AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition); + break; + case CURSOR_MODE_WRAPPED_X: + QPoint screenPos(globalCursorPosition); + wrapCursorX(widgetRect, screenPos); + QCursor::setPos(screen, screenPos); + QPoint screenDelta = globalCursorPosition - screenPos; + m_previousGlobalCursorPosition = globalCursorPosition - screenDelta; + break; + case CURSOR_MODE_WRAPPED_Y: + QPoint screenPos(globalCursorPosition); + wrapCursorY(widgetRect, screenPos); + QCursor::setPos(screen, screenPos); + QPoint screenDelta = globalCursorPosition - screenPos; + m_previousGlobalCursorPosition = globalCursorPosition - screenDelta; + break; + case CURSOR_MODE_WRAPPED: + QPoint screenPos(globalCursorPosition); + wrapCursorX(widgetRect, screenPos); + wrapCursorY(widgetRect, screenPos); + QCursor::setPos(screen, screenPos); + QPoint screenDelta = globalCursorPosition - screenPos; + m_previousGlobalCursorPosition = globalCursorPosition - screenDelta; + break; + default: + m_previousGlobalCursorPosition = globalCursorPosition; + break; + + } + ProcessPendingMouseEvents(cursorDelta); } void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h index 373945d445..7ba4e21bd4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h @@ -41,6 +41,17 @@ namespace AzToolsFramework Q_OBJECT public: + enum CursorInputMode { + CURSOR_MODE_NONE, + CURSOR_MODE_CAPTURED, //< Sets whether or not the cursor should be constrained to the source widget and invisible. + //< Internally, this will reset the cursor position after each move event to ensure movement + //< events don't allow the cursor to escape. This can be used for typical camera controls + //< like a dolly or rotation, where mouse movement is important but cursor location is not. + CURSOR_MODE_WRAPPED, //< Flags whether the curser is going to wrap around the soruce widget. + CURSOR_MODE_WRAPPED_X, + CURSOR_MODE_WRAPPED_Y + }; + QtEventToAzInputMapper(QWidget* sourceWidget, int syntheticDeviceId = 0); ~QtEventToAzInputMapper() = default; @@ -56,8 +67,12 @@ namespace AzToolsFramework //! Internally, this will reset the cursor position after each move event to ensure movement //! events don't allow the cursor to escape. This can be used for typical camera controls //! like a dolly or rotation, where mouse movement is important but cursor location is not. + //! @deprecated Use #SetCursorMode() void SetCursorCaptureEnabled(bool enabled); + //! Set the cursor mode. + void SetCursorMode(QtEventToAzInputMapper::CursorInputMode mode); + void SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride); void ClearOverrideCursor(); @@ -176,8 +191,8 @@ namespace AzToolsFramework QWidget* m_sourceWidget; // Flags whether or not Qt events should currently be processed. bool m_enabled = true; - // Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement). - bool m_capturingCursor = false; + // Controls the cursor behavior. + QtEventToAzInputMapper::CursorInputMode m_cursorMode = CURSOR_MODE_NONE; // Flags whether the cursor has been overridden. bool m_overrideCursor = false; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 505ff70122..cbede827e2 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -331,12 +331,12 @@ namespace AtomToolsFramework void RenderViewportWidget::BeginCursorCapture() { - m_inputChannelMapper->SetCursorCaptureEnabled(true); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CURSOR_MODE_CAPTURED); } void RenderViewportWidget::EndCursorCapture() { - m_inputChannelMapper->SetCursorCaptureEnabled(false); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CURSOR_MODE_NONE); } void RenderViewportWidget::SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) From fa809a76ca3804e95377c0776a40cf4004da90c8 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Mon, 13 Dec 2021 07:28:28 -0800 Subject: [PATCH 03/61] chore: address changes - updated checkstyle - update enum CursorInputMode - minor refeactor to QtEventToAzInputMapper Signed-off-by: Michael Pollind --- Code/Editor/EditorViewportSettings.cpp | 10 +- Code/Editor/EditorViewportSettings.h | 4 +- .../test_ModularViewportCameraController.cpp | 4 +- .../Input/QtEventToAzInputMapper.cpp | 116 +++++++++--------- .../Input/QtEventToAzInputMapper.h | 14 +-- .../Source/Viewport/RenderViewportWidget.cpp | 4 +- 6 files changed, 74 insertions(+), 78 deletions(-) diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index 5711d1c3d3..883eb91863 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -23,7 +23,7 @@ namespace SandboxEditor constexpr AZStd::string_view AngleSizeSetting = "/Amazon/Preferences/Editor/AngleSize"; constexpr AZStd::string_view ShowGridSetting = "/Amazon/Preferences/Editor/ShowGrid"; constexpr AZStd::string_view StickySelectSetting = "/Amazon/Preferences/Editor/StickySelect"; - constexpr AZStd::string_view ManipulatorMouseWrapSetting = "/Amazon/Preferences/Editor/Manipulator/MouseWrapping"; + constexpr AZStd::string_view ViewportMouseWrapSetting = "/Amazon/Preferences/Editor/Manipulator/MouseWrapping"; constexpr AZStd::string_view ManipulatorLineBoundWidthSetting = "/Amazon/Preferences/Editor/Manipulator/LineBoundWidth"; constexpr AZStd::string_view ManipulatorCircleBoundWidthSetting = "/Amazon/Preferences/Editor/Manipulator/CircleBoundWidth"; constexpr AZStd::string_view CameraTranslateSpeedSetting = "/Amazon/Preferences/Editor/Camera/TranslateSpeed"; @@ -187,14 +187,14 @@ namespace SandboxEditor AzToolsFramework::SetRegistry(ManipulatorLineBoundWidthSetting, lineBoundWidth); } - bool ManipulatorMouseWrap() + bool ViewportMouseWrapSetting() { - return aznumeric_cast(GetRegistry(ManipulatorMouseWrapSetting, false)); + return aznumeric_cast(GetRegistry(ViewportMouseWrapSetting, false)); } - void SetManipulatorMouseWrap(bool wrapping) + void SetViewportMouseWrapSetting(bool wrapping) { - SetRegistry(ManipulatorMouseWrapSetting, wrapping); + SetRegistry(ViewportMouseWrapSetting, wrapping); } float ManipulatorCircleBoundWidth() diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index 975feff307..a27187bc55 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -57,8 +57,8 @@ namespace SandboxEditor SANDBOX_API float ManipulatorLineBoundWidth(); SANDBOX_API void SetManipulatorLineBoundWidth(float lineBoundWidth); - SANDBOX_API bool ManipulatorMouseWrap(); - SANDBOX_API void SetManipulatorMouseWrap(bool wrapping); + SANDBOX_API bool ViewportMouseWrapSetting(); + SANDBOX_API void SetViewportMouseWrapSetting(bool wrapping); SANDBOX_API float ManipulatorCircleBoundWidth(); SANDBOX_API void SetManipulatorCircleBoundWidth(float circleBoundWidth); diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index f2ee3c79aa..9bfd9d396e 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -47,12 +47,12 @@ namespace UnitTest void ViewportMouseCursorRequestImpl::BeginCursorCapture() { - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CURSOR_MODE_CAPTURED); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorModeCaptured); } void ViewportMouseCursorRequestImpl::EndCursorCapture() { - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CURSOR_MODE_NONE); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorModeNone); } bool ViewportMouseCursorRequestImpl::IsMouseOver() const diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp index 0b6df58c1a..77f297b98c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp @@ -241,40 +241,32 @@ namespace AzToolsFramework } } - - void QtEventToAzInputMapper::SetCursorMode(QtEventToAzInputMapper::CursorInputMode mode) + void QtEventToAzInputMapper::SetCursorMode(QtEventToAzInputMapper::CursorInputMode mode) { - if(mode != m_cursorMode) + if (mode != m_cursorMode) { m_cursorMode = mode; - switch(m_cursorMode) + switch (m_cursorMode) { - case CURSOR_MODE_CAPTURED: - qApp->setOverrideCursor(Qt::BlankCursor); - m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden); - break; - case CURSOR_MODE_WRAPPED: - qApp->restoreOverrideCursor(); - m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); - break; - case CURSOR_MODE_NONE: - qApp->restoreOverrideCursor(); - m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); - break; + case CursorInputMode::CursorModeCaptured: + qApp->setOverrideCursor(Qt::BlankCursor); + m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden); + break; + case CursorInputMode::CursorModeWrapped: + qApp->restoreOverrideCursor(); + m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); + break; + case CursorInputMode::CursorModeNone: + qApp->restoreOverrideCursor(); + m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); + break; } } } void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled) { - if (enabled) - { - SetCursorMode(CURSOR_MODE_CAPTURED); - } - else - { - SetCursorMode(CURSOR_MODE_NONE); - } + SetCursorMode(enabled ? CursorInputMode::CursorModeCaptured : CursorInputMode::CursorModeNone); } bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event) @@ -453,71 +445,75 @@ namespace AzToolsFramework return QPoint{ denormalizedX, denormalizedY }; } - void wrapCursorX(const QRect& rect, QPoint& point) { - if (rect.left() < point.x()) + void wrapCursorX(const QRect& rect, QPoint& point) + { + if (rect.left() < point.x()) { point.setX(rect.right() - 1); } - else if (rect.right() > point.x()) + else if (rect.right() > point.x()) { point.setX(rect.left() + 1); } } - void wrapCursorY(const QRect& rect, QPoint& point) { - if (rect.top() < point.y()) + void wrapCursorY(const QRect& rect, QPoint& point) + { + if (rect.top() < point.y()) { point.setY(rect.bottom() - 1); } - else if (rect.bottom() > point.y()) + else if (rect.bottom() > point.y()) { point.setY(rect.top() + 1); } } - void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& globalCursorPosition) { const QPoint cursorDelta = globalCursorPosition - m_previousGlobalCursorPosition; QScreen* screen = m_sourceWidget->screen(); - const QRect widgetRect(m_sourceWidget->mapToGlobal(QPoint(0,0)), m_sourceWidget->size()); + const QRect widgetRect(m_sourceWidget->mapToGlobal(QPoint(0, 0)), m_sourceWidget->size()); m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = WidgetPositionToNormalizedPosition(m_sourceWidget->mapFromGlobal(globalCursorPosition)); m_mouseDevice->m_cursorPositionData2D->m_normalizedPositionDelta = WidgetPositionToNormalizedPosition(cursorDelta); - switch(m_cursorMode) + switch (m_cursorMode) { - case CURSOR_MODE_CAPTURED: - AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition); - break; - case CURSOR_MODE_WRAPPED_X: - QPoint screenPos(globalCursorPosition); - wrapCursorX(widgetRect, screenPos); - QCursor::setPos(screen, screenPos); - QPoint screenDelta = globalCursorPosition - screenPos; - m_previousGlobalCursorPosition = globalCursorPosition - screenDelta; - break; - case CURSOR_MODE_WRAPPED_Y: - QPoint screenPos(globalCursorPosition); - wrapCursorY(widgetRect, screenPos); - QCursor::setPos(screen, screenPos); - QPoint screenDelta = globalCursorPosition - screenPos; - m_previousGlobalCursorPosition = globalCursorPosition - screenDelta; - break; - case CURSOR_MODE_WRAPPED: + case CursorInputMode::CursorModeCaptured: + AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition); + break; + case CursorInputMode::CursorModeWrappedX: + case CursorInputMode::CursorModeWrappedY: + case CursorInputMode::CursorModeWrapped: + { QPoint screenPos(globalCursorPosition); - wrapCursorX(widgetRect, screenPos); - wrapCursorY(widgetRect, screenPos); + switch (m_cursorMode) + { + case CursorInputMode::CursorModeWrappedX: + wrapCursorX(widgetRect, screenPos); + break; + case CursorInputMode::CursorModeWrappedY: + wrapCursorY(widgetRect, screenPos); + break; + case CursorInputMode::CursorModeWrapped: + wrapCursorX(widgetRect, screenPos); + wrapCursorY(widgetRect, screenPos); + break; + default: + // this should never happen + AZ_Assert(false, "Invalid Curosr Mode: %i.", m_cursorMode); + break; + } QCursor::setPos(screen, screenPos); - QPoint screenDelta = globalCursorPosition - screenPos; + const QPoint screenDelta = globalCursorPosition - screenPos; m_previousGlobalCursorPosition = globalCursorPosition - screenDelta; - break; - default: - m_previousGlobalCursorPosition = globalCursorPosition; - break; - - + } + break; + default: + AZ_Assert(false, "Invalid Curosr Mode: %i.", m_cursorMode); + break; } ProcessPendingMouseEvents(cursorDelta); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h index 7ba4e21bd4..a6d9fdd753 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h @@ -41,15 +41,15 @@ namespace AzToolsFramework Q_OBJECT public: - enum CursorInputMode { - CURSOR_MODE_NONE, - CURSOR_MODE_CAPTURED, //< Sets whether or not the cursor should be constrained to the source widget and invisible. + enum class CursorInputMode { + CursorModeNone, + CursorModeCaptured, //< Sets whether or not the cursor should be constrained to the source widget and invisible. //< Internally, this will reset the cursor position after each move event to ensure movement //< events don't allow the cursor to escape. This can be used for typical camera controls //< like a dolly or rotation, where mouse movement is important but cursor location is not. - CURSOR_MODE_WRAPPED, //< Flags whether the curser is going to wrap around the soruce widget. - CURSOR_MODE_WRAPPED_X, - CURSOR_MODE_WRAPPED_Y + CursorModeWrapped, //< Flags whether the curser is going to wrap around the soruce widget. + CursorModeWrappedX, + CursorModeWrappedY }; QtEventToAzInputMapper(QWidget* sourceWidget, int syntheticDeviceId = 0); @@ -192,7 +192,7 @@ namespace AzToolsFramework // Flags whether or not Qt events should currently be processed. bool m_enabled = true; // Controls the cursor behavior. - QtEventToAzInputMapper::CursorInputMode m_cursorMode = CURSOR_MODE_NONE; + QtEventToAzInputMapper::CursorInputMode m_cursorMode = CursorInputMode::CursorModeNone; // Flags whether the cursor has been overridden. bool m_overrideCursor = false; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index cbede827e2..ef23560d57 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -331,12 +331,12 @@ namespace AtomToolsFramework void RenderViewportWidget::BeginCursorCapture() { - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CURSOR_MODE_CAPTURED); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorModeCaptured); } void RenderViewportWidget::EndCursorCapture() { - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CURSOR_MODE_NONE); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorModeNone); } void RenderViewportWidget::SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) From 3a6f877a9f79157424d5a86d05f9f1b7a98d1585 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Tue, 14 Dec 2021 23:05:13 -0800 Subject: [PATCH 04/61] chore: add first test Signed-off-by: Michael Pollind --- .../Input/QtEventToAzInputMapper.cpp | 8 ++--- .../Input/QtEventToAzInputMapperTests.cpp | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp index 77f297b98c..7852a4a5d5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp @@ -447,11 +447,11 @@ namespace AzToolsFramework void wrapCursorX(const QRect& rect, QPoint& point) { - if (rect.left() < point.x()) + if (point.x() < rect.left()) { point.setX(rect.right() - 1); } - else if (rect.right() > point.x()) + else if (point.x() > rect.right()) { point.setX(rect.left() + 1); } @@ -459,11 +459,11 @@ namespace AzToolsFramework void wrapCursorY(const QRect& rect, QPoint& point) { - if (rect.top() < point.y()) + if (point.y() < rect.top()) { point.setY(rect.bottom() - 1); } - else if (rect.bottom() > point.y()) + else if (point.y() > rect.bottom()) { point.setY(rect.top() + 1); } diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index 4c813a4bdd..e645c365e2 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -44,6 +44,10 @@ namespace UnitTest QObject::connect(m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(), [this]([[maybe_unused]] const AzFramework::InputChannel* inputChannel, QEvent* event) { + if(event == nullptr) { + return; + } + const QEvent::Type eventType = event->type(); if (eventType == QEvent::Type::MouseButtonPress || @@ -512,4 +516,32 @@ namespace UnitTest return info.param.m_az.GetName(); } ); + + TEST_F(QtEventToAzInputMapperFixture, MouseWrapMouseViewportQtEventToAzInputMapperFixture) + { + AzFramework::InputChannelNotificationBus::Handler::BusConnect(); + m_captureAzEvents = false; + + + const auto startPos = QPoint(WidgetSize.width() - 2, WidgetSize.height() / 2); + MouseMove(m_rootWidget.get(), startPos, QPoint(0,0)); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorInputMode::CursorModeWrappedX); + + const auto deltaPos = QPoint(200.0f, 0); + const auto expectedPosition = m_rootWidget->mapToGlobal(QPoint(200, WidgetSize.height() / 2)); + const int iterations = 50; + + for(float i = 0; i < iterations; i++) { + MouseMove(m_rootWidget.get(), m_rootWidget->mapFromGlobal(QCursor::pos()), (deltaPos / iterations)); + } + + QPointF endPosition = QCursor::pos(); + EXPECT_NEAR(endPosition.x(), expectedPosition.x(), 5.0f); + EXPECT_NEAR(endPosition.y(), expectedPosition.y(), 5.0f); + + // cleanup + m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorInputMode::CursorModeNone); + AzFramework::InputChannelNotificationBus::Handler::BusDisconnect(); + } + } // namespace UnitTest From 9e91c1872670c73cd1f3de98de616675ce9da8a5 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Wed, 15 Dec 2021 13:32:24 -0800 Subject: [PATCH 05/61] chore: address changes add test cases Signed-off-by: Michael Pollind --- Code/Editor/EditorViewportSettings.cpp | 2 +- .../Input/QtEventToAzInputMapper.cpp | 14 +- .../Input/QtEventToAzInputMapper.h | 26 +-- .../Input/QtEventToAzInputMapperTests.cpp | 153 +++++++++++++++--- 4 files changed, 154 insertions(+), 41 deletions(-) diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index 883eb91863..7108beca5a 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -189,7 +189,7 @@ namespace SandboxEditor bool ViewportMouseWrapSetting() { - return aznumeric_cast(GetRegistry(ViewportMouseWrapSetting, false)); + return GetRegistry(ViewportMouseWrapSetting, false); } void SetViewportMouseWrapSetting(bool wrapping) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp index 7852a4a5d5..edd45a7200 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp @@ -241,7 +241,7 @@ namespace AzToolsFramework } } - void QtEventToAzInputMapper::SetCursorMode(QtEventToAzInputMapper::CursorInputMode mode) + void QtEventToAzInputMapper::SetCursorMode(AzToolsFramework::CursorInputMode mode) { if (mode != m_cursorMode) { @@ -445,7 +445,7 @@ namespace AzToolsFramework return QPoint{ denormalizedX, denormalizedY }; } - void wrapCursorX(const QRect& rect, QPoint& point) + void WrapCursorX(const QRect& rect, QPoint& point) { if (point.x() < rect.left()) { @@ -457,7 +457,7 @@ namespace AzToolsFramework } } - void wrapCursorY(const QRect& rect, QPoint& point) + void WrapCursorY(const QRect& rect, QPoint& point) { if (point.y() < rect.top()) { @@ -492,14 +492,14 @@ namespace AzToolsFramework switch (m_cursorMode) { case CursorInputMode::CursorModeWrappedX: - wrapCursorX(widgetRect, screenPos); + WrapCursorX(widgetRect, screenPos); break; case CursorInputMode::CursorModeWrappedY: - wrapCursorY(widgetRect, screenPos); + WrapCursorY(widgetRect, screenPos); break; case CursorInputMode::CursorModeWrapped: - wrapCursorX(widgetRect, screenPos); - wrapCursorY(widgetRect, screenPos); + WrapCursorX(widgetRect, screenPos); + WrapCursorY(widgetRect, screenPos); break; default: // this should never happen diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h index a6d9fdd753..b84ac99c74 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h @@ -32,6 +32,17 @@ class QWheelEvent; namespace AzToolsFramework { + enum class CursorInputMode { + CursorModeNone, + CursorModeCaptured, //!< Sets whether or not the cursor should be constrained to the source widget and invisible. + //!< Internally, this will reset the cursor position after each move event to ensure movement + //!< events don't allow the cursor to escape. This can be used for typical camera controls + //!< like a dolly or rotation, where mouse movement is important but cursor location is not. + CursorModeWrapped, //!< Flags whether the curser is going to wrap around the soruce widget. + CursorModeWrappedX, + CursorModeWrappedY + }; + //! Maps events from the Qt input system to synthetic InputChannels in AzFramework //! that can be used by AzFramework::ViewportControllers. class QtEventToAzInputMapper final @@ -41,17 +52,6 @@ namespace AzToolsFramework Q_OBJECT public: - enum class CursorInputMode { - CursorModeNone, - CursorModeCaptured, //< Sets whether or not the cursor should be constrained to the source widget and invisible. - //< Internally, this will reset the cursor position after each move event to ensure movement - //< events don't allow the cursor to escape. This can be used for typical camera controls - //< like a dolly or rotation, where mouse movement is important but cursor location is not. - CursorModeWrapped, //< Flags whether the curser is going to wrap around the soruce widget. - CursorModeWrappedX, - CursorModeWrappedY - }; - QtEventToAzInputMapper(QWidget* sourceWidget, int syntheticDeviceId = 0); ~QtEventToAzInputMapper() = default; @@ -71,7 +71,7 @@ namespace AzToolsFramework void SetCursorCaptureEnabled(bool enabled); //! Set the cursor mode. - void SetCursorMode(QtEventToAzInputMapper::CursorInputMode mode); + void SetCursorMode(AzToolsFramework::CursorInputMode mode); void SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride); void ClearOverrideCursor(); @@ -192,7 +192,7 @@ namespace AzToolsFramework // Flags whether or not Qt events should currently be processed. bool m_enabled = true; // Controls the cursor behavior. - QtEventToAzInputMapper::CursorInputMode m_cursorMode = CursorInputMode::CursorModeNone; + AzToolsFramework::CursorInputMode m_cursorMode = AzToolsFramework::CursorInputMode::CursorModeNone; // Flags whether the cursor has been overridden. bool m_overrideCursor = false; diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index e645c365e2..9af445850c 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -44,10 +44,10 @@ namespace UnitTest QObject::connect(m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(), [this]([[maybe_unused]] const AzFramework::InputChannel* inputChannel, QEvent* event) { - if(event == nullptr) { + if(event == nullptr) + { return; } - const QEvent::Type eventType = event->type(); if (eventType == QEvent::Type::MouseButtonPress || @@ -517,31 +517,144 @@ namespace UnitTest } ); - TEST_F(QtEventToAzInputMapperFixture, MouseWrapMouseViewportQtEventToAzInputMapperFixture) + struct MouseMoveParam + { + AzToolsFramework::CursorInputMode mode; + int iterations; + QPoint startPos; + QPoint deltaPos; + QPoint expectedPos; + const char* name; + }; + + class MoveMoveWrapParamQtEventToAzInputMapperFixture + : public QtEventToAzInputMapperFixture + , public ::testing::WithParamInterface { + }; + + TEST_P(MoveMoveWrapParamQtEventToAzInputMapperFixture, MouseMove_NoAzHandlers_VerifyMouseMovmentViewport) + { + // setup + const MouseMoveParam mouseMoveParam = GetParam(); + AzFramework::InputChannelNotificationBus::Handler::BusConnect(); m_captureAzEvents = false; + m_inputChannelMapper->SetCursorMode(mouseMoveParam.mode); + m_rootWidget->move(100, 100); - - const auto startPos = QPoint(WidgetSize.width() - 2, WidgetSize.height() / 2); - MouseMove(m_rootWidget.get(), startPos, QPoint(0,0)); - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorInputMode::CursorModeWrappedX); - - const auto deltaPos = QPoint(200.0f, 0); - const auto expectedPosition = m_rootWidget->mapToGlobal(QPoint(200, WidgetSize.height() / 2)); - const int iterations = 50; - - for(float i = 0; i < iterations; i++) { - MouseMove(m_rootWidget.get(), m_rootWidget->mapFromGlobal(QCursor::pos()), (deltaPos / iterations)); + + MouseMove(m_rootWidget.get(), mouseMoveParam.startPos, QPoint(0,0)); + for(float i = 0; i < mouseMoveParam.iterations; i++) { + MouseMove(m_rootWidget.get(), m_rootWidget->mapFromGlobal(QCursor::pos()), (mouseMoveParam.deltaPos / mouseMoveParam.iterations)); } - - QPointF endPosition = QCursor::pos(); - EXPECT_NEAR(endPosition.x(), expectedPosition.x(), 5.0f); - EXPECT_NEAR(endPosition.y(), expectedPosition.y(), 5.0f); - + + QPointF endPosition = m_rootWidget->mapFromGlobal(QCursor::pos()); + EXPECT_NEAR(endPosition.x(), mouseMoveParam.expectedPos.x(), 1.0f); + EXPECT_NEAR(endPosition.y(), mouseMoveParam.expectedPos.y(), 1.0f); + // cleanup - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorInputMode::CursorModeNone); + m_rootWidget->move(0, 0); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeNone); AzFramework::InputChannelNotificationBus::Handler::BusDisconnect(); } + INSTANTIATE_TEST_CASE_P(All, MoveMoveWrapParamQtEventToAzInputMapperFixture, + testing::Values( + // verify CursorModeWrappedX wrapping + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(40, 0), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + "CursorModeWrappedX_Test_Right" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, + 40, + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(-40, 0), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + "CursorModeWrappedX_Test_Left" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20.0f), + QPoint(0, -40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, -20.0f), + "CursorModeWrappedX_Test_Top" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(0, 40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() + 20), + "CursorModeWrappedX_Test_Bottom" + }, + + // verify CursorModeWrappedY wrapping + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(40, 0), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() + 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + "CursorModeWrappedY_Test_Right" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, + 40, + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(-40, 0), + QPoint(-20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + "CursorModeWrappedY_Test_Left" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20.0f), + QPoint(0, -40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20.0f), + "CursorModeWrappedY_Test_Top" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(0, 40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20), + "CursorModeWrappedY_Test_Bottom" + }, + + // verify CursorModeWrapped wrapping + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(40, 0), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + "CursorModeWrapped_Test_Right" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, + 40, + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(-40, 0), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + "CursorModeWrapped_Test_Left" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20.0f), + QPoint(0, -40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20.0f), + "CursorModeWrapped_Test_Top" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(0, 40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20), + "CursorModeWrapped_Test_Bottom" + } + ), + [](const ::testing::TestParamInfo& info) + { + return info.param.name; + } + ); + } // namespace UnitTest From 1654ff052041567778624fd7078164533bc37f42 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Thu, 16 Dec 2021 22:37:31 -0800 Subject: [PATCH 06/61] chore: correct test case MouseMove_NoAzHandlers_VerifyMouseMovementViewport Signed-off-by: Michael Pollind --- .../Input/QtEventToAzInputMapper.cpp | 3 ++ .../Input/QtEventToAzInputMapper.h | 4 +- .../Input/QtEventToAzInputMapperTests.cpp | 43 ++++++++++++++++--- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp index edd45a7200..b660e87b56 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp @@ -511,6 +511,9 @@ namespace AzToolsFramework m_previousGlobalCursorPosition = globalCursorPosition - screenDelta; } break; + case CursorInputMode::CursorModeNone: + m_previousGlobalCursorPosition = globalCursorPosition; + break; default: AZ_Assert(false, "Invalid Curosr Mode: %i.", m_cursorMode); break; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h index b84ac99c74..67ef195363 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h @@ -39,8 +39,8 @@ namespace AzToolsFramework //!< events don't allow the cursor to escape. This can be used for typical camera controls //!< like a dolly or rotation, where mouse movement is important but cursor location is not. CursorModeWrapped, //!< Flags whether the curser is going to wrap around the soruce widget. - CursorModeWrappedX, - CursorModeWrappedY + CursorModeWrappedX, //!< Flags whether the curser is going to wrap around the soruce widget only on the left and right side. + CursorModeWrappedY //!< Flags whether the curser is going to wrap around the soruce widget only on the top and bottom side. }; //! Maps events from the Qt input system to synthetic InputChannels in AzFramework diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index 9af445850c..0282812be7 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -533,23 +533,38 @@ namespace UnitTest { }; - TEST_P(MoveMoveWrapParamQtEventToAzInputMapperFixture, MouseMove_NoAzHandlers_VerifyMouseMovmentViewport) + TEST_P(MoveMoveWrapParamQtEventToAzInputMapperFixture, MouseMove_NoAzHandlers_VerifyMouseMovementViewport) { + //TODO: mouseMove is bugged mapToGlobal is called twice + auto mouseMoveFix = [](QWidget* wid, QPoint globalPos, QPoint deltaPos) + { + QPoint globalPosition = globalPos + deltaPos; + QPoint localPosition = wid->mapFromGlobal(globalPosition); + QTest::mouseMove(wid, localPosition); + QMouseEvent mouseMoveEvent(QEvent::MouseMove, localPosition, globalPosition, Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::sendEvent(wid, &mouseMoveEvent); + }; + // setup const MouseMoveParam mouseMoveParam = GetParam(); AzFramework::InputChannelNotificationBus::Handler::BusConnect(); m_captureAzEvents = false; - m_inputChannelMapper->SetCursorMode(mouseMoveParam.mode); + m_rootWidget->move(100, 100); + mouseMoveFix(m_rootWidget.get(), m_rootWidget->mapToGlobal(mouseMoveParam.startPos), QPoint(0, 0)); + // given + m_inputChannelMapper->SetCursorMode(mouseMoveParam.mode); - MouseMove(m_rootWidget.get(), mouseMoveParam.startPos, QPoint(0,0)); - for(float i = 0; i < mouseMoveParam.iterations; i++) { - MouseMove(m_rootWidget.get(), m_rootWidget->mapFromGlobal(QCursor::pos()), (mouseMoveParam.deltaPos / mouseMoveParam.iterations)); + mouseMoveFix(m_rootWidget.get(), m_rootWidget->mapToGlobal(mouseMoveParam.startPos), QPoint(0, 0)); + for (float i = 0; i < mouseMoveParam.iterations; i++) + { + mouseMoveFix(m_rootWidget.get(), QCursor::pos(), (mouseMoveParam.deltaPos / mouseMoveParam.iterations)); } - QPointF endPosition = m_rootWidget->mapFromGlobal(QCursor::pos()); + // validate + QPoint endPosition = m_rootWidget->mapFromGlobal(QCursor::pos()); EXPECT_NEAR(endPosition.x(), mouseMoveParam.expectedPos.x(), 1.0f); EXPECT_NEAR(endPosition.y(), mouseMoveParam.expectedPos.y(), 1.0f); @@ -649,6 +664,22 @@ namespace UnitTest QPoint(0, 40), QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20), "CursorModeWrapped_Test_Bottom" + }, + // verify CursorModeCaptured + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeCaptured, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() / 2.0f), + QPoint(0, 40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() / 2.0f), + "CursorModeCaptured" + }, + // verify CursorModeNone + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeNone, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() / 2.0f), + QPoint(40.0f, 0), + QPoint((QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f) + 40.0f, (QtEventToAzInputMapperFixture::WidgetSize.height() / 2.0f)), + "CursorModeNone" } ), [](const ::testing::TestParamInfo& info) From 95c0c83642b2496f96418745b6c245ee50cf87b9 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sat, 18 Dec 2021 18:29:39 -0800 Subject: [PATCH 07/61] chore: remove unused setting and fixed compiling errors Signed-off-by: Michael Pollind --- Code/Editor/EditorViewportSettings.cpp | 11 ----------- Code/Editor/EditorViewportSettings.h | 3 --- .../Tests/test_ModularViewportCameraController.cpp | 4 ++-- .../Code/Source/Viewport/RenderViewportWidget.cpp | 4 ++-- 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index 7108beca5a..e06b9696e1 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -23,7 +23,6 @@ namespace SandboxEditor constexpr AZStd::string_view AngleSizeSetting = "/Amazon/Preferences/Editor/AngleSize"; constexpr AZStd::string_view ShowGridSetting = "/Amazon/Preferences/Editor/ShowGrid"; constexpr AZStd::string_view StickySelectSetting = "/Amazon/Preferences/Editor/StickySelect"; - constexpr AZStd::string_view ViewportMouseWrapSetting = "/Amazon/Preferences/Editor/Manipulator/MouseWrapping"; constexpr AZStd::string_view ManipulatorLineBoundWidthSetting = "/Amazon/Preferences/Editor/Manipulator/LineBoundWidth"; constexpr AZStd::string_view ManipulatorCircleBoundWidthSetting = "/Amazon/Preferences/Editor/Manipulator/CircleBoundWidth"; constexpr AZStd::string_view CameraTranslateSpeedSetting = "/Amazon/Preferences/Editor/Camera/TranslateSpeed"; @@ -187,16 +186,6 @@ namespace SandboxEditor AzToolsFramework::SetRegistry(ManipulatorLineBoundWidthSetting, lineBoundWidth); } - bool ViewportMouseWrapSetting() - { - return GetRegistry(ViewportMouseWrapSetting, false); - } - - void SetViewportMouseWrapSetting(bool wrapping) - { - SetRegistry(ViewportMouseWrapSetting, wrapping); - } - float ManipulatorCircleBoundWidth() { return aznumeric_cast(AzToolsFramework::GetRegistry(ManipulatorCircleBoundWidthSetting, 0.1)); diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index a27187bc55..fe1253ed0c 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -57,9 +57,6 @@ namespace SandboxEditor SANDBOX_API float ManipulatorLineBoundWidth(); SANDBOX_API void SetManipulatorLineBoundWidth(float lineBoundWidth); - SANDBOX_API bool ViewportMouseWrapSetting(); - SANDBOX_API void SetViewportMouseWrapSetting(bool wrapping); - SANDBOX_API float ManipulatorCircleBoundWidth(); SANDBOX_API void SetManipulatorCircleBoundWidth(float circleBoundWidth); diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index 9bfd9d396e..5009c626e2 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -47,12 +47,12 @@ namespace UnitTest void ViewportMouseCursorRequestImpl::BeginCursorCapture() { - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorModeCaptured); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeCaptured); } void ViewportMouseCursorRequestImpl::EndCursorCapture() { - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorModeNone); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeNone); } bool ViewportMouseCursorRequestImpl::IsMouseOver() const diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index ef23560d57..d6871a8795 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -331,12 +331,12 @@ namespace AtomToolsFramework void RenderViewportWidget::BeginCursorCapture() { - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorModeCaptured); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeCaptured); } void RenderViewportWidget::EndCursorCapture() { - m_inputChannelMapper->SetCursorMode(AzToolsFramework::QtEventToAzInputMapper::CursorModeNone); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeNone); } void RenderViewportWidget::SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) From ed39c784c304c0cf9a374b297f3521f7f0787fc2 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sun, 19 Dec 2021 18:22:14 -0800 Subject: [PATCH 08/61] chore: add accumulated test for mouse position Signed-off-by: Michael Pollind --- .../Tests/Input/QtEventToAzInputMapperTests.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index 0282812be7..5047c19044 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -100,6 +100,11 @@ namespace UnitTest m_azChannelEvents.push_back(AzEventInfo(inputChannel)); hasBeenConsumed = m_captureAzEvents; } + else if (inputChannelId == AzFramework::InputDeviceMouse::SystemCursorPosition) + { + m_azCursorPositions.push_back(*inputChannel.GetCustomData()); + hasBeenConsumed = m_captureAzEvents; + } } else if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId)) { @@ -165,6 +170,7 @@ namespace UnitTest AZStd::vector m_signalEvents; AZStd::vector m_azChannelEvents; AZStd::vector m_azTextEvents; + AZStd::vector m_azCursorPositions; bool m_captureAzEvents{ false }; bool m_captureTextEvents{ false }; @@ -549,13 +555,14 @@ namespace UnitTest const MouseMoveParam mouseMoveParam = GetParam(); AzFramework::InputChannelNotificationBus::Handler::BusConnect(); - m_captureAzEvents = false; + m_captureAzEvents = true; m_rootWidget->move(100, 100); mouseMoveFix(m_rootWidget.get(), m_rootWidget->mapToGlobal(mouseMoveParam.startPos), QPoint(0, 0)); // given m_inputChannelMapper->SetCursorMode(mouseMoveParam.mode); + m_azCursorPositions.clear(); mouseMoveFix(m_rootWidget.get(), m_rootWidget->mapToGlobal(mouseMoveParam.startPos), QPoint(0, 0)); for (float i = 0; i < mouseMoveParam.iterations; i++) @@ -563,11 +570,19 @@ namespace UnitTest mouseMoveFix(m_rootWidget.get(), QCursor::pos(), (mouseMoveParam.deltaPos / mouseMoveParam.iterations)); } + AZ::Vector2 accumalatedPosition(0.0f,0.0f); + for(const auto& pos: m_azCursorPositions) { + accumalatedPosition += (pos.m_normalizedPositionDelta * AZ::Vector2(WidgetSize.width(), WidgetSize.height())); + } + // validate QPoint endPosition = m_rootWidget->mapFromGlobal(QCursor::pos()); EXPECT_NEAR(endPosition.x(), mouseMoveParam.expectedPos.x(), 1.0f); EXPECT_NEAR(endPosition.y(), mouseMoveParam.expectedPos.y(), 1.0f); + EXPECT_NEAR(accumalatedPosition.GetX(), mouseMoveParam.deltaPos.x(), 1.0f); + EXPECT_NEAR(accumalatedPosition.GetY(), mouseMoveParam.deltaPos.y(), 1.0f); + // cleanup m_rootWidget->move(0, 0); m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeNone); From 832c525f67a0ea24ad004066c0d556c8c449a9af Mon Sep 17 00:00:00 2001 From: nemerle <96597+nemerle@users.noreply.github.com> Date: Sat, 8 Jan 2022 23:22:04 +0100 Subject: [PATCH 09/61] Fix missing include file Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- .../Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h index 838cdd2256..60417adf36 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace AZ { From c65a903c7dc8ae5ce441df130f26e735534dd6bd Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sat, 8 Jan 2022 21:01:29 -0800 Subject: [PATCH 10/61] chore: fix unit test Signed-off-by: Michael Pollind --- .../Input/QtEventToAzInputMapperTests.cpp | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index 5047c19044..d7ab896462 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -541,15 +541,6 @@ namespace UnitTest TEST_P(MoveMoveWrapParamQtEventToAzInputMapperFixture, MouseMove_NoAzHandlers_VerifyMouseMovementViewport) { - //TODO: mouseMove is bugged mapToGlobal is called twice - auto mouseMoveFix = [](QWidget* wid, QPoint globalPos, QPoint deltaPos) - { - QPoint globalPosition = globalPos + deltaPos; - QPoint localPosition = wid->mapFromGlobal(globalPosition); - QTest::mouseMove(wid, localPosition); - QMouseEvent mouseMoveEvent(QEvent::MouseMove, localPosition, globalPosition, Qt::NoButton, Qt::NoButton, Qt::NoModifier); - QApplication::sendEvent(wid, &mouseMoveEvent); - }; // setup const MouseMoveParam mouseMoveParam = GetParam(); @@ -558,30 +549,29 @@ namespace UnitTest m_captureAzEvents = true; m_rootWidget->move(100, 100); - mouseMoveFix(m_rootWidget.get(), m_rootWidget->mapToGlobal(mouseMoveParam.startPos), QPoint(0, 0)); + QScreen* screen = m_rootWidget->screen(); + MouseMove(m_rootWidget.get(), mouseMoveParam.startPos, QPoint(0, 0)); // given m_inputChannelMapper->SetCursorMode(mouseMoveParam.mode); m_azCursorPositions.clear(); - - mouseMoveFix(m_rootWidget.get(), m_rootWidget->mapToGlobal(mouseMoveParam.startPos), QPoint(0, 0)); for (float i = 0; i < mouseMoveParam.iterations; i++) { - mouseMoveFix(m_rootWidget.get(), QCursor::pos(), (mouseMoveParam.deltaPos / mouseMoveParam.iterations)); + MouseMove(m_rootWidget.get(), m_rootWidget->mapFromGlobal(QCursor::pos(screen)), (mouseMoveParam.deltaPos / mouseMoveParam.iterations)); } - AZ::Vector2 accumalatedPosition(0.0f,0.0f); + AZ::Vector2 accumulatedPosition(0.0f,0.0f); for(const auto& pos: m_azCursorPositions) { - accumalatedPosition += (pos.m_normalizedPositionDelta * AZ::Vector2(WidgetSize.width(), WidgetSize.height())); + accumulatedPosition += (pos.m_normalizedPositionDelta * AZ::Vector2(WidgetSize.width(), WidgetSize.height())); } // validate - QPoint endPosition = m_rootWidget->mapFromGlobal(QCursor::pos()); + const QPoint endPosition = m_rootWidget->mapFromGlobal(QCursor::pos(screen)); EXPECT_NEAR(endPosition.x(), mouseMoveParam.expectedPos.x(), 1.0f); EXPECT_NEAR(endPosition.y(), mouseMoveParam.expectedPos.y(), 1.0f); - EXPECT_NEAR(accumalatedPosition.GetX(), mouseMoveParam.deltaPos.x(), 1.0f); - EXPECT_NEAR(accumalatedPosition.GetY(), mouseMoveParam.deltaPos.y(), 1.0f); + EXPECT_NEAR(accumulatedPosition.GetX(), mouseMoveParam.deltaPos.x(), 1.0f); + EXPECT_NEAR(accumulatedPosition.GetY(), mouseMoveParam.deltaPos.y(), 1.0f); // cleanup m_rootWidget->move(0, 0); From 3cac520280a586e38292f6906947b143ac9147e7 Mon Sep 17 00:00:00 2001 From: nemerle <96597+nemerle@users.noreply.github.com> Date: Tue, 11 Jan 2022 00:47:00 +0100 Subject: [PATCH 11/61] Fix non-unity windows build Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- Code/Framework/AzCore/Tests/TestCatalog.h | 1 + Gems/Blast/Code/Include/Blast/BlastSystemBus.h | 1 + Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp | 1 + .../Code/Source/Components/TerrainPhysicsColliderComponent.cpp | 2 ++ 4 files changed, 5 insertions(+) diff --git a/Code/Framework/AzCore/Tests/TestCatalog.h b/Code/Framework/AzCore/Tests/TestCatalog.h index 9a1fb9b5e6..bbcd952f78 100644 --- a/Code/Framework/AzCore/Tests/TestCatalog.h +++ b/Code/Framework/AzCore/Tests/TestCatalog.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace UnitTest { diff --git a/Gems/Blast/Code/Include/Blast/BlastSystemBus.h b/Gems/Blast/Code/Include/Blast/BlastSystemBus.h index 95ffd75057..bda684181a 100644 --- a/Gems/Blast/Code/Include/Blast/BlastSystemBus.h +++ b/Gems/Blast/Code/Include/Blast/BlastSystemBus.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include diff --git a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp index 56c785b310..70e7c28bac 100644 --- a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp +++ b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp index 8c2c0e80b6..fccbedd8fa 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp @@ -6,12 +6,14 @@ * */ + #include #include #include #include #include +#include #include #include #include From 7b6ce50fe87c8306bc6b3ab1b65e4ddbdaf463be Mon Sep 17 00:00:00 2001 From: nemerle <96597+nemerle@users.noreply.github.com> Date: Tue, 11 Jan 2022 00:52:21 +0100 Subject: [PATCH 12/61] Remove un-needed `Requests` namespace use Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp index 90014578ea..13c01b2efc 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp @@ -139,7 +139,7 @@ namespace AZ::IO::Requests { } - Requests::ReportData::ReportData(ReportType reportType) + ReportData::ReportData(ReportType reportType) : m_reportType(reportType) { } From 2e19b703ef00e4f1bc94a904901c83f2e6fa3e3e Mon Sep 17 00:00:00 2001 From: nemerle <96597+nemerle@users.noreply.github.com> Date: Wed, 12 Jan 2022 01:37:02 +0100 Subject: [PATCH 13/61] fix release build Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp index 45a95a71d7..f799e8e69f 100644 --- a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp @@ -286,7 +286,9 @@ namespace Audio return sResult; } - CATLAudioFileEntry::CATLAudioFileEntry(const char * const filePath, IATLAudioFileEntryData * const implData) + +#endif // !AUDIO_RELEASE + CATLAudioFileEntry::CATLAudioFileEntry(const char* const filePath, IATLAudioFileEntryData* const implData) : m_filePath(filePath) , m_fileSize(0) , m_useCount(0) @@ -299,6 +301,4 @@ namespace Audio } CATLAudioFileEntry::~CATLAudioFileEntry() = default; - -#endif // !AUDIO_RELEASE } // namespace Audio From fabb2b186c4d3f0b9cd2aa49a1233977fa796e12 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Wed, 12 Jan 2022 20:52:57 -0800 Subject: [PATCH 14/61] chore: disable mouse move test Signed-off-by: Michael Pollind --- .../Tests/Input/QtEventToAzInputMapperTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index d7ab896462..31193f6f05 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -539,7 +539,7 @@ namespace UnitTest { }; - TEST_P(MoveMoveWrapParamQtEventToAzInputMapperFixture, MouseMove_NoAzHandlers_VerifyMouseMovementViewport) + TEST_P(MoveMoveWrapParamQtEventToAzInputMapperFixture, DISABLED_MouseMove_NoAzHandlers_VerifyMouseMovementViewport) { // setup From c262696056811660aa3e55cb6a83136232fb12fb Mon Sep 17 00:00:00 2001 From: abrmich Date: Tue, 18 Jan 2022 16:13:28 -0800 Subject: [PATCH 15/61] Move remaining two LyShine headers to the gem Signed-off-by: abrmich --- Code/Legacy/CryCommon/crycommon_files.cmake | 2 -- Code/Legacy/CrySystem/System.cpp | 3 --- Code/Legacy/CrySystem/XConsole.cpp | 5 ----- .../FontBuilderWorker/FontBuilderWorker.cpp | 4 ++-- .../Code/Tests/Builders/CopyDependencyBuilderTest.cpp | 2 -- .../LyShine/Code/Include}/LyShine/Bus/UiCursorBus.h | 0 .../LyShine/Code/Include}/LyShine/UiAssetTypes.h | 0 Gems/LyShine/Code/lyshine_static_files.cmake | 2 ++ 8 files changed, 4 insertions(+), 14 deletions(-) rename {Code/Legacy/CryCommon => Gems/LyShine/Code/Include}/LyShine/Bus/UiCursorBus.h (100%) rename {Code/Legacy/CryCommon => Gems/LyShine/Code/Include}/LyShine/UiAssetTypes.h (100%) diff --git a/Code/Legacy/CryCommon/crycommon_files.cmake b/Code/Legacy/CryCommon/crycommon_files.cmake index a4d87c207a..479f0ec879 100644 --- a/Code/Legacy/CryCommon/crycommon_files.cmake +++ b/Code/Legacy/CryCommon/crycommon_files.cmake @@ -100,8 +100,6 @@ set(FILES platform_impl.cpp Win32specific.h Win64specific.h - LyShine/UiAssetTypes.h - LyShine/Bus/UiCursorBus.h Maestro/Bus/EditorSequenceAgentComponentBus.h Maestro/Bus/EditorSequenceBus.h Maestro/Bus/EditorSequenceComponentBus.h diff --git a/Code/Legacy/CrySystem/System.cpp b/Code/Legacy/CrySystem/System.cpp index 7cf066f72e..0439b69efc 100644 --- a/Code/Legacy/CrySystem/System.cpp +++ b/Code/Legacy/CrySystem/System.cpp @@ -134,7 +134,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) #include -#include #include #include @@ -1374,7 +1373,6 @@ bool CSystem::HandleMessage([[maybe_unused]] HWND hWnd, UINT uMsg, WPARAM wParam // Fall through intended case WM_ENTERMENULOOP: { - UiCursorBus::Broadcast(&UiCursorInterface::IncrementVisibleCounter); return true; } case WM_CAPTURECHANGED: @@ -1392,7 +1390,6 @@ bool CSystem::HandleMessage([[maybe_unused]] HWND hWnd, UINT uMsg, WPARAM wParam // Fall through intended case WM_EXITMENULOOP: { - UiCursorBus::Broadcast(&UiCursorInterface::DecrementVisibleCounter); return (uMsg != WM_CAPTURECHANGED); } case WM_SYSKEYUP: diff --git a/Code/Legacy/CrySystem/XConsole.cpp b/Code/Legacy/CrySystem/XConsole.cpp index df82e34abe..e74b1e7c56 100644 --- a/Code/Legacy/CrySystem/XConsole.cpp +++ b/Code/Legacy/CrySystem/XConsole.cpp @@ -29,7 +29,6 @@ #include #include -#include //#define DEFENCE_CVAR_HASH_LOGGING // s should point to a buffer at least 65 chars long @@ -749,8 +748,6 @@ void CXConsole::ShowConsole(bool show, const int iRequestScrollMax) if (show && !m_bConsoleActive) { - UiCursorBus::Broadcast(&UiCursorBus::Events::IncrementVisibleCounter); - AzFramework::InputSystemCursorRequestBus::EventResult(m_previousSystemCursorState, AzFramework::InputDeviceMouse::Id, &AzFramework::InputSystemCursorRequests::GetSystemCursorState); @@ -760,8 +757,6 @@ void CXConsole::ShowConsole(bool show, const int iRequestScrollMax) } else if (!show && m_bConsoleActive) { - UiCursorBus::Broadcast(&UiCursorBus::Events::DecrementVisibleCounter); - AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id, &AzFramework::InputSystemCursorRequests::SetSystemCursorState, m_previousSystemCursorState); diff --git a/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/FontBuilderWorker/FontBuilderWorker.cpp b/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/FontBuilderWorker/FontBuilderWorker.cpp index 3fe399d05d..ccaa1700f9 100644 --- a/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/FontBuilderWorker/FontBuilderWorker.cpp +++ b/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/FontBuilderWorker/FontBuilderWorker.cpp @@ -9,7 +9,6 @@ #include "FontBuilderWorker.h" #include -#include #include #include @@ -52,7 +51,8 @@ namespace CopyDependencyBuilder if (fileExtension == "font" || fileExtension == "fontfamily") { - return azrtti_typeid(); + static AZ::Data::AssetType fontAssetType("{57767D37-0EBE-43BE-8F60-AB36D2056EF8}"); // form UiAssetTypes.h in the LyShine gem + return fontAssetType; } return AZ::Data::AssetType::CreateNull(); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp index 8b697f35f2..7767e4f239 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp @@ -19,8 +19,6 @@ #include #include -#include - #include #include #include diff --git a/Code/Legacy/CryCommon/LyShine/Bus/UiCursorBus.h b/Gems/LyShine/Code/Include/LyShine/Bus/UiCursorBus.h similarity index 100% rename from Code/Legacy/CryCommon/LyShine/Bus/UiCursorBus.h rename to Gems/LyShine/Code/Include/LyShine/Bus/UiCursorBus.h diff --git a/Code/Legacy/CryCommon/LyShine/UiAssetTypes.h b/Gems/LyShine/Code/Include/LyShine/UiAssetTypes.h similarity index 100% rename from Code/Legacy/CryCommon/LyShine/UiAssetTypes.h rename to Gems/LyShine/Code/Include/LyShine/UiAssetTypes.h diff --git a/Gems/LyShine/Code/lyshine_static_files.cmake b/Gems/LyShine/Code/lyshine_static_files.cmake index 925658e756..c1515f5978 100644 --- a/Gems/LyShine/Code/lyshine_static_files.cmake +++ b/Gems/LyShine/Code/lyshine_static_files.cmake @@ -11,6 +11,7 @@ set(FILES Include/LyShine/IRenderGraph.h Include/LyShine/ISprite.h Include/LyShine/ILyShine.h + Include/LyShine/UiAssetTypes.h Include/LyShine/UiBase.h Include/LyShine/UiLayoutCellBase.h Include/LyShine/UiSerializeHelpers.h @@ -26,6 +27,7 @@ set(FILES Include/LyShine/Bus/UiCanvasManagerBus.h Include/LyShine/Bus/UiCanvasUpdateNotificationBus.h Include/LyShine/Bus/UiCheckboxBus.h + Include/LyShine/Bus/UiCursorBus.h Include/LyShine/Bus/UiDraggableBus.h Include/LyShine/Bus/UiDropdownBus.h Include/LyShine/Bus/UiDropdownOptionBus.h From c31e3c020898816b1a2eee355af202e7c952c56c Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Thu, 20 Jan 2022 19:36:33 +0000 Subject: [PATCH 16/61] LYN-7640 Terrain Physics Materials hooked up all the way down to PhysX Signed-off-by: Sergey Pereslavtsev --- .../EditorHeightfieldColliderComponent.cpp | 3 + .../Source/HeightfieldColliderComponent.cpp | 4 + Gems/PhysX/Code/Source/Utils.cpp | 132 ++++++----- Gems/PhysX/Code/Source/Utils.h | 7 + ...ditorHeightfieldColliderComponentTests.cpp | 208 ++++++++++++++++-- Gems/PhysX/Code/Tests/EditorTestUtilities.cpp | 18 ++ Gems/PhysX/Code/Tests/EditorTestUtilities.h | 3 + 7 files changed, 302 insertions(+), 73 deletions(-) diff --git a/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp index fb0a38fa18..c6def5f0ab 100644 --- a/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp @@ -189,6 +189,9 @@ namespace PhysX configuration.m_entityId = GetEntityId(); configuration.m_debugName = GetEntity()->GetName(); + // Update material selection from the mapping + Utils::SetMaterialsFromHeightfieldProvider(GetEntityId(), m_colliderConfig.m_materialSelection); + AzPhysics::ShapeColliderPairList colliderShapePairs; colliderShapePairs.emplace_back(AZStd::make_shared(m_colliderConfig), m_shapeConfig); configuration.m_colliderAndShapeData = colliderShapePairs; diff --git a/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp index c1fa09f21f..b219253b6f 100644 --- a/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp @@ -140,6 +140,10 @@ namespace PhysX Physics::HeightfieldShapeConfiguration& configuration = static_cast(*m_shapeConfig.second); configuration = Utils::CreateHeightfieldShapeConfiguration(GetEntityId()); + + // Update material selection from the mapping + Physics::ColliderConfiguration* colliderConfig = m_shapeConfig.first.get(); + Utils::SetMaterialsFromHeightfieldProvider(GetEntityId(), colliderConfig->m_materialSelection); } void HeightfieldColliderComponent::RefreshHeightfield() diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index fecea57d9c..a988f6791c 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -66,6 +66,67 @@ namespace PhysX } } + AZStd::pair GetPhysXMaterialIndicesFromHeightfieldSamples( + const AZStd::vector& samples, + const int32_t row, const int32_t col, + const int32_t numRows, const int32_t numCols) + { + + uint8_t materialIndex0 = 0; + uint8_t materialIndex1 = 0; + + const bool lastRowIndex = (row == (numRows - 1)); + const bool lastColumnIndex = (col == (numCols - 1)); + + // In PhysX, the material indices refer to the quad down and to the right of the sample. + // If we're in the last row or last column, there aren't any quads down or to the right, + // so just clear these out. + + if (!lastRowIndex && !lastColumnIndex) + { + auto GetIndex = [numCols](int32_t row, int32_t col) + { + return (row * numCols) + col; + }; + + // Our source data is providing one material index per vertex, but PhysX wants one material index + // per triangle. The heuristic that we'll go with for selecting the material index is to choose + // the material for the vertex that's not on the diagonal of each triangle. + // Ex: A *---* B + // | / | For this, we'll use A for index0 and D for index1. + // C *---* D + // + // Ex: A *---* B + // | \ | For this, we'll use C for index0 and B for index1. + // C *---* D + // + // This is a pretty arbitrary choice, so the heuristic might need to be revisited over time if this + // causes incorrect or unpredictable physics material mappings. + + const Physics::HeightMaterialPoint& currentSample = samples[GetIndex(row, col)]; + + switch (currentSample.m_quadMeshType) + { + case Physics::QuadMeshType::SubdivideUpperLeftToBottomRight: + materialIndex0 = samples[GetIndex(row + 1, col)].m_materialIndex; + materialIndex1 = samples[GetIndex(row, col + 1)].m_materialIndex; + break; + case Physics::QuadMeshType::SubdivideBottomLeftToUpperRight: + materialIndex0 = currentSample.m_materialIndex; + materialIndex1 = samples[GetIndex(row + 1, col + 1)].m_materialIndex; + break; + case Physics::QuadMeshType::Hole: + materialIndex0 = physx::PxHeightFieldMaterial::eHOLE; + materialIndex1 = physx::PxHeightFieldMaterial::eHOLE; + break; + default: + AZ_Assert(false, "Unhandled case in GetPhysXMaterialIndicesFromHeightfieldSamples"); + break; + } + } + return { materialIndex0, materialIndex1 }; + } + void CreatePxGeometryFromHeightfield( Physics::HeightfieldShapeConfiguration& heightfieldConfig, physx::PxGeometryHolder& pxGeometry) { @@ -116,12 +177,8 @@ namespace PhysX for (int32_t row = 0; row < numRows; row++) { - const bool lastRowIndex = (row == (numRows - 1)); - for (int32_t col = 0; col < numCols; col++) { - const bool lastColumnIndex = (col == (numCols - 1)); - auto GetIndex = [numCols](int32_t row, int32_t col) { return (row * numCols) + col; @@ -134,52 +191,15 @@ namespace PhysX AZ_Assert(currentSample.m_materialIndex < physxMaximumMaterialIndex, "MaterialIndex must be less than 128"); currentPhysxSample.height = azlossy_cast( AZ::GetClamp(currentSample.m_height, minHeightBounds, maxHeightBounds) * scaleFactor); - if (lastRowIndex || lastColumnIndex) - { - // In PhysX, the material indices refer to the quad down and to the right of the sample. - // If we're in the last row or last column, there aren't any quads down or to the right, - // so just clear these out. - currentPhysxSample.materialIndex0 = 0; - currentPhysxSample.materialIndex1 = 0; - } - else + + auto [materialIndex0, materialIndex1] = GetPhysXMaterialIndicesFromHeightfieldSamples(samples, row, col, numRows, numCols); + currentPhysxSample.materialIndex0 = materialIndex0; + currentPhysxSample.materialIndex1 = materialIndex1; + + if (currentSample.m_quadMeshType == Physics::QuadMeshType::SubdivideUpperLeftToBottomRight) { - // Our source data is providing one material index per vertex, but PhysX wants one material index - // per triangle. The heuristic that we'll go with for selecting the material index is to choose - // the material for the vertex that's not on the diagonal of each triangle. - // Ex: A *---* B - // | / | For this, we'll use A for index0 and D for index1. - // C *---* D - // - // Ex: A *---* B - // | \ | For this, we'll use C for index0 and B for index1. - // C *---* D - // - // This is a pretty arbitrary choice, so the heuristic might need to be revisited over time if this - // causes incorrect or unpredictable physics material mappings. - - switch (currentSample.m_quadMeshType) - { - case Physics::QuadMeshType::SubdivideUpperLeftToBottomRight: - currentPhysxSample.materialIndex0 = samples[GetIndex(row + 1, col)].m_materialIndex; - currentPhysxSample.materialIndex1 = samples[GetIndex(row, col + 1)].m_materialIndex; - // Set the tesselation flag to say that we need to go from UL to BR - currentPhysxSample.materialIndex0.setBit(); - break; - case Physics::QuadMeshType::SubdivideBottomLeftToUpperRight: - currentPhysxSample.materialIndex0 = currentSample.m_materialIndex; - currentPhysxSample.materialIndex1 = samples[GetIndex(row + 1, col + 1)].m_materialIndex; - break; - case Physics::QuadMeshType::Hole: - currentPhysxSample.materialIndex0 = physx::PxHeightFieldMaterial::eHOLE; - currentPhysxSample.materialIndex1 = physx::PxHeightFieldMaterial::eHOLE; - break; - default: - AZ_Warning("PhysX Heightfield", false, "Unhandled case in CreatePxGeometryFromConfig"); - currentPhysxSample.materialIndex0 = 0; - currentPhysxSample.materialIndex1 = 0; - break; - } + // Set the tesselation flag to say that we need to go from UL to BR + currentPhysxSample.setTessFlag(); } } } @@ -1562,6 +1582,20 @@ namespace PhysX return configuration; } + + void SetMaterialsFromHeightfieldProvider(const AZ::EntityId& heightfieldProviderId, Physics::MaterialSelection& materialSelection) + { + AZStd::vector materialList; + Physics::HeightfieldProviderRequestsBus::EventResult( + materialList, heightfieldProviderId, &Physics::HeightfieldProviderRequestsBus::Events::GetMaterialList); + + materialSelection.SetMaterialSlots(Physics::MaterialSelection::SlotsArray(materialList.size(), "")); + + for (int i = 0; i < materialList.size(); ++i) + { + materialSelection.SetMaterialId(materialList[i], i); + } + } } // namespace Utils namespace ReflectionUtils diff --git a/Gems/PhysX/Code/Source/Utils.h b/Gems/PhysX/Code/Source/Utils.h index 525db03315..5244926aa2 100644 --- a/Gems/PhysX/Code/Source/Utils.h +++ b/Gems/PhysX/Code/Source/Utils.h @@ -188,8 +188,15 @@ namespace PhysX //! Returns defaultValue if the input is infinite or NaN, otherwise returns the input unchanged. const AZ::Vector3& Sanitize(const AZ::Vector3& input, const AZ::Vector3& defaultValue = AZ::Vector3::CreateZero()); + AZStd::pair GetPhysXMaterialIndicesFromHeightfieldSamples( + const AZStd::vector& samples, + const int32_t row, const int32_t col, + const int32_t numRows, const int32_t numCols); + Physics::HeightfieldShapeConfiguration CreateHeightfieldShapeConfiguration(AZ::EntityId entityId); + void SetMaterialsFromHeightfieldProvider(const AZ::EntityId& heightfieldProviderId, Physics::MaterialSelection& materialSelection); + namespace Geometry { using PointList = AZStd::vector; diff --git a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp index ef112d8623..9bdc63e60d 100644 --- a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using ::testing::NiceMock; using ::testing::Return; @@ -27,18 +28,28 @@ namespace PhysXEditorTests { AZStd::vector GetSamples() { - AZStd::vector samples{ { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 2.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 1.5f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 0.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight } }; + AZStd::vector samples{ { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 0 }, + { 2.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 1 }, + { 1.5f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 2 }, + { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 0 }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 1 }, + { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 2 }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 0 }, + { 0.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 1 }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 2 } }; return samples; } + AZStd::vector GetMaterialList() + { + AZStd::vector materials{ + {Physics::MaterialId::FromUUID("{EC976D51-2C26-4C1E-BBF2-75BAAAFA162C}")}, + {Physics::MaterialId::FromUUID("{B9836F51-A235-4781-95E3-A6302BEE9EFF}")}, + {Physics::MaterialId::FromUUID("{7E060707-BB03-47EB-B046-4503C7145B6E}")} + }; + return materials; + } + EntityPtr SetupHeightfieldComponent() { // create an editor entity with a shape collider component and a box shape component @@ -78,6 +89,7 @@ namespace PhysXEditorTests x = -3.0f; y = 3.0f; }); + ON_CALL(mockShapeRequests, GetMaterialList).WillByDefault(Return(GetMaterialList())); } EntityPtr TestCreateActiveGameEntityFromEditorEntity(AZ::Entity* editorEntity) @@ -89,6 +101,89 @@ namespace PhysXEditorTests return gameEntity; } + class PhysXEditorHeightfieldFixture : public PhysXEditorFixture + { + public: + void SetUp() override + { + PhysXEditorFixture::SetUp(); + PopulateDefaultMaterialLibrary(); + + m_editorEntity = SetupHeightfieldComponent(); + m_editorMockShapeRequests = AZStd::make_unique>(m_editorEntity->GetId()); + SetupMockMethods(*m_editorMockShapeRequests.get()); + m_editorEntity->Activate(); + + m_gameEntity = TestCreateActiveGameEntityFromEditorEntity(m_editorEntity.get()); + m_gameMockShapeRequests = AZStd::make_unique>(m_gameEntity->GetId()); + SetupMockMethods(*m_gameMockShapeRequests.get()); + m_gameEntity->Activate(); + } + + void TearDown() override + { + CleanupHeightfieldComponent(); + + m_editorEntity = nullptr; + m_gameEntity = nullptr; + m_editorMockShapeRequests = nullptr; + m_gameMockShapeRequests = nullptr; + + PhysXEditorFixture::TearDown(); + } + + void PopulateDefaultMaterialLibrary() + { + AZ::Data::AssetId assetId = AZ::Data::AssetId(AZ::Uuid::Create()); + + // Create an asset out of our Script Event + Physics::MaterialLibraryAsset* matLibAsset = aznew Physics::MaterialLibraryAsset; + { + AZStd::vector matIds = GetMaterialList(); + + for (Physics::MaterialId matId : matIds) + { + Physics::MaterialFromAssetConfiguration matConfig; + matConfig.m_id = matId; + matConfig.m_configuration.m_surfaceType = matId.GetUuid().ToString(); + matLibAsset->AddMaterialData(matConfig); + } + } + + // Note: There is no interface to simply update material library asset. It has to go via updating the entire configuration which causes assets reloading. + // It makes sense as a safety mechanism in the Editor but makes it harder to write tests. + // Hence have to work around it via const_cast here to be able to simply set the generated asset into configuration. + AzPhysics::SystemConfiguration* sysConfig = const_cast(AZ::Interface::Get()->GetConfiguration()); + + AZ::Data::Asset assetData(assetId, matLibAsset, AZ::Data::AssetLoadBehavior::Default); + sysConfig->m_materialLibraryAsset = assetData; + } + + Physics::Material* GetMaterialFromRaycast(float x, float y) + { + AzPhysics::RayCastRequest request; + request.m_start = AZ::Vector3(x, y, 5.0f); + request.m_direction = AZ::Vector3(0.0f, 0.0f, -1.0f); + request.m_distance = 10.0f; + + //query the scene + auto* sceneInterface = AZ::Interface::Get(); + AzPhysics::SceneQueryHits result = sceneInterface->QueryScene(m_defaultSceneHandle, &request); + EXPECT_EQ(result.m_hits.size(), 1); + + if (result) + { + return result.m_hits[0].m_material; + } + + return nullptr; + }; + + EntityPtr m_editorEntity; + EntityPtr m_gameEntity; + AZStd::unique_ptr> m_editorMockShapeRequests; + AZStd::unique_ptr> m_gameMockShapeRequests; + }; TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentDependenciesSatisfiedEntityIsValid) { @@ -149,21 +244,13 @@ namespace PhysXEditorTests CleanupHeightfieldComponent(); } - TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithAABoxCorrectRuntimeGeometry) + TEST_F(PhysXEditorHeightfieldFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithAABoxCorrectRuntimeGeometry) { - EntityPtr editorEntity = SetupHeightfieldComponent(); - NiceMock mockShapeRequests(editorEntity->GetId()); - SetupMockMethods(mockShapeRequests); - editorEntity->Activate(); - - EntityPtr gameEntity = TestCreateActiveGameEntityFromEditorEntity(editorEntity.get()); - NiceMock mockShapeRequests2(gameEntity->GetId()); - SetupMockMethods(mockShapeRequests2); - gameEntity->Activate(); + AZ::EntityId gameEntityId = m_gameEntity->GetId(); AzPhysics::SimulatedBody* staticBody = nullptr; AzPhysics::SimulatedBodyComponentRequestsBus::EventResult( - staticBody, gameEntity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); + staticBody, gameEntityId, &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); @@ -183,7 +270,7 @@ namespace PhysXEditorTests int32_t numRows{ 0 }; int32_t numColumns{ 0 }; Physics::HeightfieldProviderRequestsBus::Event( - gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows); + gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows); EXPECT_EQ(numColumns, heightfield->getNbColumns()); EXPECT_EQ(numRows, heightfield->getNbRows()); @@ -194,12 +281,12 @@ namespace PhysXEditorTests float minHeightBounds{ 0.0f }; float maxHeightBounds{ 0.0f }; Physics::HeightfieldProviderRequestsBus::Event( - gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldHeightBounds, minHeightBounds, + gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldHeightBounds, minHeightBounds, maxHeightBounds); AZStd::vector samples; Physics::HeightfieldProviderRequestsBus::EventResult( - samples, gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); + samples, gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); const float halfBounds{ (maxHeightBounds - minHeightBounds) / 2.0f }; const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits::max() / halfBounds; @@ -208,7 +295,80 @@ namespace PhysXEditorTests EXPECT_EQ(samplePhysX.height, azlossy_cast(samplePhysics.m_height * scaleFactor)); } } - CleanupHeightfieldComponent(); + } + + TEST_F(PhysXEditorHeightfieldFixture, EditorHeightfieldColliderComponentHeightfieldColliderCorrectMaterials) + { + AZ::EntityId gameEntityId = m_gameEntity->GetId(); + + int32_t numRows{ 0 }; + int32_t numColumns{ 0 }; + Physics::HeightfieldProviderRequestsBus::Event( + gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows); + + EXPECT_EQ(numRows, 3); + EXPECT_EQ(numColumns, 3); + + AZStd::vector samples; + Physics::HeightfieldProviderRequestsBus::EventResult( + samples, gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); + + AzPhysics::SimulatedBody* staticBody = nullptr; + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult( + staticBody, gameEntityId, &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); + + const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); + PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); + + physx::PxShape* shape = nullptr; + pxRigidStatic->getShapes(&shape, 1, 0); + + physx::PxHeightFieldGeometry heightfieldGeometry; + shape->getHeightFieldGeometry(heightfieldGeometry); + + physx::PxHeightField* heightfield = heightfieldGeometry.heightField; + + AZStd::vector physicsSurfaceTypes; + for (Physics::MaterialId materialId : GetMaterialList()) + { + physicsSurfaceTypes.emplace_back(materialId.GetUuid().ToString()); + } + + // PhysX Heightfield cooking doesn't map 1-1 sample material indices to triangle material indices + // Hence hardcoding the expected material indices in the test + const int physicsMaterialsValidationDataIndex[] = {0, 2, 1, 1}; + + for (int sampleRow = 0; sampleRow < numRows; ++sampleRow) + { + for (int sampleColumn = 0; sampleColumn < numColumns; ++sampleColumn) + { + physx::PxHeightFieldSample samplePhysX = heightfield->getSample(sampleRow, sampleColumn); + Physics::HeightMaterialPoint samplePhysics = samples[sampleRow * numColumns + sampleColumn]; + + auto [materialIndex0, materialIndex1] = PhysX::Utils::GetPhysXMaterialIndicesFromHeightfieldSamples(samples, sampleRow, sampleColumn, numRows, numColumns); + EXPECT_EQ(samplePhysX.materialIndex0, materialIndex0); + EXPECT_EQ(samplePhysX.materialIndex1, materialIndex1); + + if (sampleRow != numRows - 1 && sampleColumn != numColumns - 1) + { + const float x_offset = -0.25f; + const float y_offset = 0.75f; + const float secondRayOffset = 0.5f; + + float rayX = x_offset + sampleColumn; + float rayY = y_offset + sampleRow; + + Physics::Material* mat1 = GetMaterialFromRaycast(rayX, rayY); + EXPECT_NE(mat1, nullptr); + + Physics::Material* mat2 = GetMaterialFromRaycast(rayX + secondRayOffset, rayY + secondRayOffset); + EXPECT_NE(mat2, nullptr); + + AZStd::string expectedMaterialName = physicsSurfaceTypes[physicsMaterialsValidationDataIndex[sampleRow * 2 + sampleColumn]]; + EXPECT_EQ(mat1->GetSurfaceTypeName(), expectedMaterialName); + } + } + } } } // namespace PhysXEditorTests diff --git a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp index fa794c58a5..272d9157b2 100644 --- a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp +++ b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp @@ -70,6 +70,24 @@ namespace PhysXEditorTests } } + void PhysXEditorFixture::ConnectToPVD() + { + auto* debug = AZ::Interface::Get(); + if (debug) + { + debug->ConnectToPvd(); + } + } + + void PhysXEditorFixture::DisconnectFromPVD() + { + auto* debug = AZ::Interface::Get(); + if (debug) + { + debug->DisconnectFromPvd(); + } + } + // DefaultWorldBus AzPhysics::SceneHandle PhysXEditorFixture::GetDefaultSceneHandle() const { diff --git a/Gems/PhysX/Code/Tests/EditorTestUtilities.h b/Gems/PhysX/Code/Tests/EditorTestUtilities.h index 9dfe40f366..26566f724a 100644 --- a/Gems/PhysX/Code/Tests/EditorTestUtilities.h +++ b/Gems/PhysX/Code/Tests/EditorTestUtilities.h @@ -48,6 +48,9 @@ namespace PhysXEditorTests void SetUp() override; void TearDown() override; + void ConnectToPVD(); + void DisconnectFromPVD(); + // DefaultWorldBus AzPhysics::SceneHandle GetDefaultSceneHandle() const override; From cc50a18fe997eeccc342d63f913942581d2fc9ae Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 Jan 2022 11:38:22 +0000 Subject: [PATCH 17/61] add support for quad shapes in shape collider component Signed-off-by: greerdv --- .../Source/EditorShapeColliderComponent.cpp | 133 +++++++++++++++++- .../Source/EditorShapeColliderComponent.h | 11 ++ .../Code/Source/ShapeColliderComponent.h | 1 + 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 59b659a0bf..f61b2fce74 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,17 @@ namespace PhysX return AZ::Edit::PropertyVisibility::Hide; } + AZ::Crc32 EditorShapeColliderComponent::SingleSidedVisibility() + { + if ((m_shapeType == ShapeType::QuadSingleSided || m_shapeType == ShapeType::QuadDoubleSided) + && GetEntity()->FindComponent() == nullptr) + { + return AZ::Edit::PropertyVisibility::Show; + } + + return AZ::Edit::PropertyVisibility::Hide; + } + void EditorShapeColliderComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -74,6 +86,7 @@ namespace PhysX ->Field("DebugDrawSettings", &EditorShapeColliderComponent::m_colliderDebugDraw) ->Field("ShapeConfigs", &EditorShapeColliderComponent::m_shapeConfigs) ->Field("SubdivisionCount", &EditorShapeColliderComponent::m_subdivisionCount) + ->Field("SingleSided", &EditorShapeColliderComponent::m_singleSided) ; if (auto editContext = serializeContext->GetEditContext()) @@ -100,6 +113,10 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Max, Utils::MaxFrustumSubdivisions) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSubdivisionCountChange) ->Attribute(AZ::Edit::Attributes::Visibility, &EditorShapeColliderComponent::SubdivisionCountVisibility) + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_singleSided, "Single sided", + "If enabled, planar shapes will only collide from one direction (not valid for shapes attached to dynamic rigid bodies)") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSingleSidedChange) + ->Attribute(AZ::Edit::Attributes::Visibility, &EditorShapeColliderComponent::SingleSidedVisibility) ; } } @@ -126,7 +143,6 @@ namespace PhysX incompatible.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); incompatible.push_back(AZ_CRC_CE("CompoundShapeService")); incompatible.push_back(AZ_CRC_CE("DiskShapeService")); - incompatible.push_back(AZ_CRC_CE("QuadShapeService")); incompatible.push_back(AZ_CRC_CE("TubeShapeService")); incompatible.push_back(AZ_CRC_CE("ReferenceShapeService")); } @@ -206,6 +222,12 @@ namespace PhysX break; } + case ShapeType::QuadSingleSided: + case ShapeType::QuadDoubleSided: + { + // a quad shape has no interior, just return an empty vector of sample points + break; + } default: AZ_WarningOnce("PhysX Shape Collider Component", false, "Unsupported shape type in UpdateCachedSamplePoints"); break; @@ -331,6 +353,11 @@ namespace PhysX UpdateCylinderConfig(uniformScale); } + else if (shapeCrc == ShapeConstants::Quad) + { + UpdateQuadConfig(overallScale); + } + else { m_shapeType = !shapeCrc ? ShapeType::None : ShapeType::Unsupported; @@ -354,6 +381,60 @@ namespace PhysX m_geometryCache.m_boxDimensions = scale * boxDimensions; } + void EditorShapeColliderComponent::UpdateQuadConfig(const AZ::Vector3& scale) + { + LmbrCentral::QuadShapeConfig quadShapeConfig; + LmbrCentral::QuadShapeComponentRequestBus::EventResult(quadShapeConfig, GetEntityId(), + &LmbrCentral::QuadShapeComponentRequests::GetQuadConfiguration); + + const float minDimension = 1e-3f; // used to prevent the dimensions being 0 in any direction + const float xDim = AZ::GetMax(minDimension, quadShapeConfig.m_width); + const float yDim = AZ::GetMax(minDimension, quadShapeConfig.m_height); + + if (m_singleSided) + { + AZStd::vector cookedData; + + constexpr AZ::u32 vertexCount = 4; + const AZ::Vector3 vertices[vertexCount] = + { + AZ::Vector3(-0.5f * xDim, -0.5f * yDim, 0.0f), + AZ::Vector3(-0.5f * xDim, 0.5f * yDim, 0.0f), + AZ::Vector3(0.5f * xDim, 0.5f * yDim, 0.0f), + AZ::Vector3(0.5f * xDim, -0.5f * yDim, 0.0f), + }; + + constexpr AZ::u32 indexCount = 6; + const AZ::u32 indices[indexCount] = + { + 0, 1, 2, + 0, 2, 3 + }; + + bool cookingResult = false; + Physics::SystemRequestBus::BroadcastResult(cookingResult, &Physics::SystemRequests::CookTriangleMeshToMemory, + vertices, vertexCount, indices, indexCount, cookedData); + + Physics::CookedMeshShapeConfiguration shapeConfig; + shapeConfig.SetCookedMeshData(cookedData.data(), cookedData.size(), + Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh); + shapeConfig.m_scale = scale; + + SetShapeConfig(ShapeType::QuadSingleSided, shapeConfig); + } + + else + { + // it's not possible to create a perfectly 2d convex in PhysX, so the best we can do is a very thin box + const float zDim = AZ::GetMax(minDimension, 1e-3f * AZ::GetMin(xDim, yDim)); + const AZ::Vector3 boxDimensions(xDim, yDim, zDim); + + SetShapeConfig(ShapeType::QuadDoubleSided, Physics::BoxShapeConfiguration(boxDimensions)); + + m_shapeConfigs.back()->m_scale = scale; + } + } + void EditorShapeColliderComponent::UpdateCapsuleConfig(const AZ::Vector3& scale) { LmbrCentral::CapsuleShapeConfig lmbrCentralCapsuleShapeConfig; @@ -425,6 +506,12 @@ namespace PhysX } } + void EditorShapeColliderComponent::OnSingleSidedChange() + { + UpdateShapeConfigs(); + CreateStaticEditorCollider(); + } + AZ::u32 EditorShapeColliderComponent::OnSubdivisionCountChange() { const AZ::Vector3 uniformScale = Utils::GetUniformScale(GetEntityId()); @@ -615,6 +702,8 @@ namespace PhysX m_editorSceneHandle = m_sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName); } + UpdateTriggerSettings(); + UpdateSingleSidedSettings(); UpdateShapeConfigs(); // Debug drawing @@ -767,6 +856,7 @@ namespace PhysX if (changeReason == LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged) { UpdateShapeConfigs(); + UpdateTriggerSettings(); CreateStaticEditorCollider(); Physics::ColliderComponentEventBus::Event(GetEntityId(), &Physics::ColliderComponentEvents::OnColliderChanged); @@ -849,4 +939,45 @@ namespace PhysX { return m_colliderConfig.m_isTrigger; } + + void EditorShapeColliderComponent::UpdateTriggerSettings() + { + if (m_shapeType == ShapeType::QuadSingleSided || m_shapeType == ShapeType::QuadDoubleSided) + { + if (!m_previousIsTrigger.has_value()) + { + m_previousIsTrigger = m_colliderConfig.m_isTrigger; + } + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::PropertyVisibility::IsTrigger, false); + } + else + { + if (m_previousIsTrigger.has_value()) + { + m_colliderConfig.m_isTrigger = m_previousIsTrigger.value(); + m_previousIsTrigger.reset(); + } + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::PropertyVisibility::IsTrigger, true); + } + } + + void EditorShapeColliderComponent::UpdateSingleSidedSettings() + { + if (GetEntity()->FindComponent()) + { + if (!m_previousSingleSided.has_value()) + { + m_previousSingleSided = m_singleSided; + } + m_singleSided = false; + } + else + { + if (m_previousSingleSided.has_value()) + { + m_singleSided = m_previousSingleSided.value(); + m_previousSingleSided.reset(); + } + } + } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 3422d4959f..0f5350b781 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -41,6 +41,8 @@ namespace PhysX Sphere, PolygonPrism, Cylinder, + QuadDoubleSided, + QuadSingleSided, Unsupported }; @@ -89,6 +91,7 @@ namespace PhysX AZ::u32 OnConfigurationChanged(); void UpdateShapeConfigs(); void UpdateBoxConfig(const AZ::Vector3& scale); + void UpdateQuadConfig(const AZ::Vector3& scale); void UpdateCapsuleConfig(const AZ::Vector3& scale); void UpdateSphereConfig(const AZ::Vector3& scale); void UpdateCylinderConfig(const AZ::Vector3& scale); @@ -103,6 +106,8 @@ namespace PhysX AZ::u32 OnSubdivisionCountChange(); AZ::Crc32 SubdivisionCountVisibility(); + void OnSingleSidedChange(); + AZ::Crc32 SingleSidedVisibility(); // AZ::Component void Activate() override; @@ -137,6 +142,9 @@ namespace PhysX AZ::Aabb GetColliderShapeAabb() override; bool IsTrigger() override; + void UpdateTriggerSettings(); + void UpdateSingleSidedSettings(); + Physics::ColliderConfiguration m_colliderConfig; //!< Stores collision layers, whether the collider is a trigger, etc. DebugDraw::Collider m_colliderDebugDraw; //!< Handles drawing the collider based on global and local AzPhysics::SceneInterface* m_sceneInterface = nullptr; @@ -151,6 +159,9 @@ namespace PhysX //! @note 16 is the number of subdivisions in the debug cylinder that is loaded as a mesh (not generated procedurally) AZ::u8 m_subdivisionCount = 16; mutable GeometryCache m_geometryCache; //!< Cached data for generating sample points inside the attached shape. + AZStd::optional m_previousIsTrigger; //!< Stores the previous trigger setting if the shape is changed to one which does not support triggers. + bool m_singleSided = false; //!< Used for 2d shapes like quad which may be treated as either single or doubled sided. + AZStd::optional m_previousSingleSided; //!< Stores the previous single sided setting when unable to support single-sided shapes (such as when used with a dynamic rigid body). AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler m_physXConfigChangedHandler; AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler m_onMaterialLibraryChangedEventHandler; diff --git a/Gems/PhysX/Code/Source/ShapeColliderComponent.h b/Gems/PhysX/Code/Source/ShapeColliderComponent.h index 2065f17dcf..cfc51f08ed 100644 --- a/Gems/PhysX/Code/Source/ShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/ShapeColliderComponent.h @@ -30,6 +30,7 @@ namespace PhysX static const AZ::Crc32 Sphere = AZ_CRC("Sphere", 0x55f96687); static const AZ::Crc32 PolygonPrism = AZ_CRC("PolygonPrism", 0xd6b50036); static const AZ::Crc32 Cylinder = AZ_CRC("Cylinder", 0x9b045bea); + static const AZ::Crc32 Quad = AZ_CRC("QuadShape", 0x40d75e14); } // namespace ShapeConstants /// Component that provides a collider based on geometry from a shape component. From a8eb5be2e6f3952ce1fdff147104dd1e1e049d7a Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 Jan 2022 13:24:01 +0000 Subject: [PATCH 18/61] fix order of triangle indices for single-sided quad collider Signed-off-by: greerdv --- Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index f61b2fce74..b935ff2536 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -407,8 +407,8 @@ namespace PhysX constexpr AZ::u32 indexCount = 6; const AZ::u32 indices[indexCount] = { - 0, 1, 2, - 0, 2, 3 + 0, 2, 1, + 0, 3, 2 }; bool cookingResult = false; From f61011f5ac8382c7db145538f873933a64e76b51 Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 Jan 2022 18:14:33 +0000 Subject: [PATCH 19/61] add editor side tests for quad colliders Signed-off-by: greerdv --- .../Source/EditorShapeColliderComponent.cpp | 3 +- .../Tests/ShapeColliderComponentTests.cpp | 150 +++++++++++++++++- 2 files changed, 148 insertions(+), 5 deletions(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index b935ff2536..bdf70fc227 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -702,9 +702,9 @@ namespace PhysX m_editorSceneHandle = m_sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName); } - UpdateTriggerSettings(); UpdateSingleSidedSettings(); UpdateShapeConfigs(); + UpdateTriggerSettings(); // Debug drawing m_colliderDebugDraw.Connect(GetEntityId()); @@ -947,6 +947,7 @@ namespace PhysX if (!m_previousIsTrigger.has_value()) { m_previousIsTrigger = m_colliderConfig.m_isTrigger; + m_colliderConfig.m_isTrigger = false; } m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::PropertyVisibility::IsTrigger, false); } diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index 06ba4f3e9c..21d381a259 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -452,21 +453,55 @@ namespace PhysXEditorTests EXPECT_TRUE(aabb.GetMin().IsClose(translation - 0.5f * scale * boxDimensions)); } - void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger) + void SetBoolValueOnComponent(AZ::Component* component, AZ::Crc32 name, bool value) { AZ::SerializeContext* serializeContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); AzToolsFramework::InstanceDataHierarchy instanceDataHierarchy; - instanceDataHierarchy.AddRootInstance(editorShapeColliderComponent); + instanceDataHierarchy.AddRootInstance(component); instanceDataHierarchy.Build(serializeContext, AZ::SerializeContext::ENUM_ACCESS_FOR_WRITE); AzToolsFramework::InstanceDataHierarchy::InstanceDataNode* instanceNode = - instanceDataHierarchy.FindNodeByPartialAddress({ AZ_CRC("Trigger", 0x1a6b0f5d) }); + instanceDataHierarchy.FindNodeByPartialAddress({ name }); if (instanceNode) { - instanceNode->Write(isTrigger); + instanceNode->Write(value); } } + void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger) + { + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC("Trigger", 0x1a6b0f5d), isTrigger); + } + + bool GetBoolValueFromComponent(AZ::Component* component, AZ::Crc32 name) + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); + AzToolsFramework::InstanceDataHierarchy instanceDataHierarchy; + instanceDataHierarchy.AddRootInstance(component); + instanceDataHierarchy.Build(serializeContext, AZ::SerializeContext::ENUM_ACCESS_FOR_READ); + AzToolsFramework::InstanceDataHierarchy::InstanceDataNode* instanceNode = + instanceDataHierarchy.FindNodeByPartialAddress({ name }); + bool value = false; + instanceNode->Read(value); + return value; + } + + bool IsTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) + { + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC("Trigger")); + } + + void SetSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool singleSided) + { + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC("SingleSided"), singleSided); + } + + bool IsSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) + { + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC("SingleSided")); + } + EntityPtr CreateRigidBox(const AZ::Vector3& boxDimensions, const AZ::Vector3& position) { EntityPtr rigidBodyEditorEntity = CreateInactiveEditorEntity("RigidBodyEditorEntity"); @@ -566,4 +601,111 @@ namespace PhysXEditorTests EXPECT_THAT(aabb.GetMin(), UnitTest::IsClose(-0.5f * boxDimensions * parentScale)); } + class PhysXEditorParamBoolFixture + : public ::testing::WithParamInterface + , public PhysXEditorFixture + { + }; + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_ShapeColliderWithQuadShapeNonUniformlyScalesCorrectly) + { + // test both single and double-sided quad colliders + bool singleSided = GetParam(); + + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, singleSided); + editorEntity->CreateComponent(); + const auto entityId = editorEntity->GetId(); + + editorEntity->Activate(); + + LmbrCentral::QuadShapeComponentRequestBus::Event(entityId, &LmbrCentral::QuadShapeComponentRequests::SetQuadWidth, 1.2f); + LmbrCentral::QuadShapeComponentRequestBus::Event(entityId, &LmbrCentral::QuadShapeComponentRequests::SetQuadHeight, 0.8f); + + // update the transform scale and non-uniform scale + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, 3.0f); + AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3(1.5f, 0.5f, 1.0f)); + + // make a game entity and check that its AABB is as expected + EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); + AZ::Aabb aabb = gameEntity->FindComponent()->GetAabb(); + + EXPECT_NEAR(aabb.GetMin().GetX(), -2.7f, 1e-3f); + EXPECT_NEAR(aabb.GetMin().GetY(), -0.6f, 1e-3f); + EXPECT_NEAR(aabb.GetMax().GetX(), 2.7f, 1e-3f); + EXPECT_NEAR(aabb.GetMax().GetY(), 0.6f, 1e-3f); + EXPECT_TRUE(true); + } + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_TriggerSettingIsRememberedWhenSwitchingToQuadAndBack) + { + bool initialTriggerSetting = GetParam(); + + // create an editor entity with a box component (which does support trigger) + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + auto* boxShapeComponent = editorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetTrigger(shapeColliderComponent, initialTriggerSetting); + editorEntity->Activate(); + + // the trigger setting should be what it was set to + EXPECT_EQ(IsTrigger(shapeColliderComponent), initialTriggerSetting); + + // deactivate the entity and swap the box for a quad (which does not support trigger) + editorEntity->Deactivate(); + editorEntity->RemoveComponent(boxShapeComponent); + auto* quadShapeComponent = editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + editorEntity->Activate(); + + // the trigger setting should now be false, because quad shape does not support triggers + EXPECT_FALSE(IsTrigger(shapeColliderComponent)); + + // swap back to a box shape + editorEntity->Deactivate(); + editorEntity->RemoveComponent(quadShapeComponent); + editorEntity->AddComponent(boxShapeComponent); + editorEntity->Activate(); + + // the original trigger setting should have been remembered + EXPECT_EQ(IsTrigger(shapeColliderComponent), initialTriggerSetting); + + // the quad shape component is no longer attached to the entity so won't be automatically cleared up + delete quadShapeComponent; + } + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_SingleSidedSettingIsRememberedWhenAddingAndRemovingRigidBody) + { + bool initialSingleSidedSetting = GetParam(); + + // create an editor entity without a rigid body (that means both single-sided and double-sided quads are valid) + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, initialSingleSidedSetting); + editorEntity->Activate(); + + // verify that the single sided setting matches the initial value + EXPECT_EQ(IsSingleSided(shapeColliderComponent), initialSingleSidedSetting); + + // add an editor rigid body component (this should mean single-sided quads are not supported) + editorEntity->Deactivate(); + auto rigidBodyComponent = editorEntity->CreateComponent(); + editorEntity->Activate(); + + EXPECT_FALSE(IsSingleSided(shapeColliderComponent)); + + // remove the editor rigid body component (the previous single-sided setting should be restored) + editorEntity->Deactivate(); + editorEntity->RemoveComponent(rigidBodyComponent); + editorEntity->Activate(); + + EXPECT_EQ(IsSingleSided(shapeColliderComponent), initialSingleSidedSetting); + + // the rigid body component is no longer attached to the entity so won't be automatically cleared up + delete rigidBodyComponent; + } + + INSTANTIATE_TEST_CASE_P(PhysXEditorTests, PhysXEditorParamBoolFixture, ::testing::Bool()); } // namespace PhysXEditorTests From 6a700e6b955d784b814cf7d39aa1ed65244236fa Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 Jan 2022 19:10:56 +0000 Subject: [PATCH 20/61] add test for runtime behaviour of single sided quad collider Signed-off-by: greerdv --- .../Tests/ShapeColliderComponentTests.cpp | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index 21d381a259..c164e5de63 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -707,5 +707,39 @@ namespace PhysXEditorTests delete rigidBodyComponent; } - INSTANTIATE_TEST_CASE_P(PhysXEditorTests, PhysXEditorParamBoolFixture, ::testing::Bool()); + INSTANTIATE_TEST_CASE_P(PhysXEditorTest, PhysXEditorParamBoolFixture, ::testing::Bool()); + + TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_SingleSidedQuadDoesNotCollideFromBelow) + { + // create an editor entity without a rigid body (that means both single-sided and double-sided quads are valid), positioned at the origin + EntityPtr editorQuadEntity = CreateInactiveEditorEntity("QuadEntity"); + editorQuadEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorQuadEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, true); + editorQuadEntity->Activate(); + LmbrCentral::QuadShapeComponentRequestBus::Event(editorQuadEntity->GetId(), &LmbrCentral::QuadShapeComponentRequests::SetQuadHeight, 10.0f); + LmbrCentral::QuadShapeComponentRequestBus::Event(editorQuadEntity->GetId(), &LmbrCentral::QuadShapeComponentRequests::SetQuadWidth, 10.0f); + + // add a second entity with a box collider and a rigid body, positioned below the quad + EntityPtr editorBoxEntity = CreateInactiveEditorEntity("BoxEntity"); + editorBoxEntity->CreateComponent(LmbrCentral::BoxShapeComponentTypeId); + editorBoxEntity->CreateComponent(); + editorBoxEntity->CreateComponent(); + editorBoxEntity->Activate(); + AZ::TransformBus::Event(editorBoxEntity->GetId(), &AZ::TransformBus::Events::SetWorldTranslation, -AZ::Vector3::CreateAxisZ()); + + EntityPtr gameQuadEntity = CreateActiveGameEntityFromEditorEntity(editorQuadEntity.get()); + EntityPtr gameBoxEntity = CreateActiveGameEntityFromEditorEntity(editorBoxEntity.get()); + + // give the box enough upward velocity to rise above the level of the quad and simulate + Physics::RigidBodyRequestBus::Event(gameBoxEntity->GetId(), &Physics::RigidBodyRequests::SetLinearVelocity, AZ::Vector3::CreateAxisZ(6.0f)); + PhysX::TestUtils::UpdateScene(m_defaultScene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 200); + + // the box should travel through the base of the quad because it has no collision from that direction + // and land on the top surface of the quad, which does have collision + float finalHeight = 0.0f; + AZ::TransformBus::EventResult(finalHeight, gameBoxEntity->GetId(), &AZ::TransformBus::Events::GetWorldZ); + + EXPECT_GT(finalHeight, 0.0f); + } } // namespace PhysXEditorTests From 3977edb21ec25cba410e86bc03535a8b9df768af Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 19 Jan 2022 15:48:57 -0600 Subject: [PATCH 21/61] Atom Tools: Removing unnecessary modules, components, and dead code from SMC Looking toward creating a bare bones template for a standalone application, simplifying and removing any boilerplate wherever possible. SMC followed patterns established by material editor, subdividing everything into multiple modules, which required manually adding static modules, implementing system components with little to no functionality, and a bunch of unnecessary files for such a simple application. This change deletes unnecessary boilerplate code, moving everything into a single module, making the application class responsible for reflecting classes and buses. Signed-off-by: Guthrie Adams --- .../Application/AtomToolsApplication.cpp | 1 + .../Code/CMakeLists.txt | 45 +--- .../ShaderManagementConsoleDocumentModule.h | 28 --- .../ShaderManagementConsoleWindowModule.h | 28 --- .../ShaderManagementConsoleDocument.h | 2 +- .../ShaderManagementConsoleDocumentModule.cpp | 30 --- ...haderManagementConsoleDocumentRequestBus.h | 0 ...nagementConsoleDocumentSystemComponent.cpp | 86 ------- ...ManagementConsoleDocumentSystemComponent.h | 41 ---- .../ShaderManagementConsole_Traits_Linux.h | 2 - .../Mac/ShaderManagementConsole_Traits_Mac.h | 2 - .../ShaderManagementConsole_Traits_Windows.h | 2 - .../ShaderManagementConsoleApplication.cpp | 219 ++++++++++++++++-- .../ShaderManagementConsoleApplication.h | 29 ++- .../ShaderManagementConsoleRequestBus.h | 0 .../ShaderManagementConsoleBrowserWidget.cpp | 201 ---------------- .../Window/ShaderManagementConsoleWindow.cpp | 17 +- .../Window/ShaderManagementConsoleWindow.h | 4 +- ...ShaderManagementConsoleWindowComponent.cpp | 206 ---------------- .../ShaderManagementConsoleWindowComponent.h | 76 ------ .../ShaderManagementConsoleWindowModule.cpp | 38 --- .../ShaderManagementConsoleToolBar.cpp | 32 --- .../ToolBar/ShaderManagementConsoleToolBar.h | 31 --- .../Code/shadermanagementconsole_files.cmake | 11 + ...hadermanagementconsoledocument_files.cmake | 17 -- .../shadermanagementconsolewindow_files.cmake | 22 -- 26 files changed, 259 insertions(+), 911 deletions(-) delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentModule.h delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Window/ShaderManagementConsoleWindowModule.h delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentModule.cpp rename Gems/Atom/Tools/ShaderManagementConsole/Code/{Include/Atom => Source}/Document/ShaderManagementConsoleDocumentRequestBus.h (100%) delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.h rename Gems/Atom/Tools/ShaderManagementConsole/Code/{Include/Atom/Core => Source}/ShaderManagementConsoleRequestBus.h (100%) delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleBrowserWidget.cpp delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.cpp delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.h delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowModule.cpp delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.h delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsoledocument_files.cmake delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsolewindow_files.cmake diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 7b6be61050..150b06cabd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -157,6 +157,7 @@ namespace AtomToolsFramework components.insert( components.end(), { + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt index c5ceab5360..3c5de1cc1b 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt @@ -19,48 +19,10 @@ if(NOT PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED) endif() ly_add_target( - NAME ShaderManagementConsole.Document STATIC - NAMESPACE Gem - FILES_CMAKE - shadermanagementconsoledocument_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - . - Source - PUBLIC - Include - BUILD_DEPENDENCIES - PUBLIC - Gem::AtomToolsFramework.Static - Gem::AtomToolsFramework.Editor - Gem::Atom_RPI.Edit - Gem::Atom_RPI.Public - Gem::Atom_RHI.Reflect -) - -ly_add_target( - NAME ShaderManagementConsole.Window STATIC + NAME ShaderManagementConsole EXECUTABLE NAMESPACE Gem AUTOMOC AUTORCC - FILES_CMAKE - shadermanagementconsolewindow_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - . - Source - PUBLIC - Include - BUILD_DEPENDENCIES - PUBLIC - Gem::AtomToolsFramework.Static - Gem::AtomToolsFramework.Editor - Gem::Atom_RPI.Public -) - -ly_add_target( - NAME ShaderManagementConsole EXECUTABLE - NAMESPACE Gem FILES_CMAKE shadermanagementconsole_files.cmake ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake @@ -77,8 +39,9 @@ ly_add_target( PRIVATE Gem::AtomToolsFramework.Static Gem::AtomToolsFramework.Editor - Gem::ShaderManagementConsole.Window - Gem::ShaderManagementConsole.Document + Gem::Atom_RPI.Edit + Gem::Atom_RPI.Public + Gem::Atom_RHI.Reflect RUNTIME_DEPENDENCIES Gem::AtomToolsFramework.Editor Gem::EditorPythonBindings.Editor diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentModule.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentModule.h deleted file mode 100644 index dd5cc01506..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentModule.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -namespace ShaderManagementConsole -{ - //! Entry point for Shader Management Console Document library. - class ShaderManagementConsoleDocumentModule - : public AZ::Module - { - public: - AZ_RTTI(ShaderManagementConsoleDocumentModule, "{81D7A170-9284-4DE9-8D92-B6B94E8A2BDF}", AZ::Module); - AZ_CLASS_ALLOCATOR(ShaderManagementConsoleDocumentModule, AZ::SystemAllocator, 0); - - ShaderManagementConsoleDocumentModule(); - - //! Add required SystemComponents to the SystemEntity. - AZ::ComponentTypeList GetRequiredSystemComponents() const override; - }; -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Window/ShaderManagementConsoleWindowModule.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Window/ShaderManagementConsoleWindowModule.h deleted file mode 100644 index ff365ee98c..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Window/ShaderManagementConsoleWindowModule.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -namespace ShaderManagementConsole -{ - //! Entry point for Shader Management Console Window library. - class ShaderManagementConsoleWindowModule - : public AZ::Module - { - public: - AZ_RTTI(ShaderManagementConsoleWindowModule, "{57D6239C-AE03-4ED8-9125-35C5B1625503}", AZ::Module); - AZ_CLASS_ALLOCATOR(ShaderManagementConsoleWindowModule, AZ::SystemAllocator, 0); - - ShaderManagementConsoleWindowModule(); - - //! Add required SystemComponents to the SystemEntity. - AZ::ComponentTypeList GetRequiredSystemComponents() const override; - }; -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h index eb7d6b87a0..bd51616f7a 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h @@ -7,12 +7,12 @@ */ #pragma once -#include #include #include #include #include #include +#include namespace ShaderManagementConsole { diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentModule.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentModule.cpp deleted file mode 100644 index a9415fd85b..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentModule.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace ShaderManagementConsole -{ - ShaderManagementConsoleDocumentModule::ShaderManagementConsoleDocumentModule() - { - // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. - m_descriptors.insert(m_descriptors.end(), { - ShaderManagementConsoleDocumentSystemComponent::CreateDescriptor(), - }); - } - - AZ::ComponentTypeList ShaderManagementConsoleDocumentModule::GetRequiredSystemComponents() const - { - return AZ::ComponentTypeList{ - azrtti_typeid(), - azrtti_typeid(), - }; - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentRequestBus.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentRequestBus.h similarity index 100% rename from Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentRequestBus.h rename to Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentRequestBus.h diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp deleted file mode 100644 index 55b800067a..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace ShaderManagementConsole -{ - void ShaderManagementConsoleDocumentSystemComponent::Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serialize = azrtti_cast(context)) - { - serialize->Class() - ->Version(0); - - if (AZ::EditContext* ec = serialize->GetEditContext()) - { - ec->Class("ShaderManagementConsoleDocumentSystemComponent", "Manages documents") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; - } - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("ShaderManagementConsoleDocumentRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") - ->Event("GetShaderOptionCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount) - ->Event("GetShaderOptionDescriptor", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor) - ->Event("GetShaderVariantCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount) - ->Event("GetShaderVariantInfo", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo) - ; - } - } - - void ShaderManagementConsoleDocumentSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) - { - required.push_back(AZ_CRC_CE("AtomToolsDocumentSystemService")); - required.push_back(AZ_CRC_CE("AssetProcessorToolsConnection")); - required.push_back(AZ_CRC_CE("AssetDatabaseService")); - required.push_back(AZ_CRC_CE("PropertyManagerService")); - required.push_back(AZ_CRC_CE("RPISystem")); - } - - void ShaderManagementConsoleDocumentSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC_CE("ShaderManagementConsoleDocumentSystemService")); - } - - void ShaderManagementConsoleDocumentSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) - { - incompatible.push_back(AZ_CRC_CE("ShaderManagementConsoleDocumentSystemService")); - } - - void ShaderManagementConsoleDocumentSystemComponent::Init() - { - } - - void ShaderManagementConsoleDocumentSystemComponent::Activate() - { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType, - []() - { - return aznew ShaderManagementConsoleDocument(); - }); - } - - void ShaderManagementConsoleDocumentSystemComponent::Deactivate() - { - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.h deleted file mode 100644 index 1ee825f6de..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -namespace ShaderManagementConsole -{ - //! ShaderManagementConsoleDocumentSystemComponent - class ShaderManagementConsoleDocumentSystemComponent - : public AZ::Component - { - public: - AZ_COMPONENT(ShaderManagementConsoleDocumentSystemComponent, "{1610159D-59DC-48B1-B2D1-FCE7AFD3B012}"); - - ShaderManagementConsoleDocumentSystemComponent() = default; - ~ShaderManagementConsoleDocumentSystemComponent() = default; - ShaderManagementConsoleDocumentSystemComponent(const ShaderManagementConsoleDocumentSystemComponent&) = delete; - ShaderManagementConsoleDocumentSystemComponent& operator =(const ShaderManagementConsoleDocumentSystemComponent&) = delete; - - static void Reflect(AZ::ReflectContext* context); - - static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - - private: - //////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - void Init() override; - void Activate() override; - void Deactivate() override; - //////////////////////////////////////////////////////////////////////// - }; -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h index 2897402f04..c4cb36c3c3 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h @@ -7,5 +7,3 @@ */ #pragma once -#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT "" - diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Traits_Mac.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Traits_Mac.h index 627cce90c9..c4cb36c3c3 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Traits_Mac.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Traits_Mac.h @@ -7,5 +7,3 @@ */ #pragma once -#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT ".app" - diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Traits_Windows.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Traits_Windows.h index 4bd905f929..c4cb36c3c3 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Traits_Windows.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Traits_Windows.h @@ -7,5 +7,3 @@ */ #pragma once -#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT ".exe" - diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp index a242716b52..ba8038319b 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp @@ -6,21 +6,90 @@ * */ -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include +#include #include +AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT +#include +#include +#include +AZ_POP_DISABLE_WARNING + +void InitShaderManagementConsoleResources() +{ + // Must register qt resources from other modules + Q_INIT_RESOURCE(ShaderManagementConsole); + Q_INIT_RESOURCE(InspectorWidget); + Q_INIT_RESOURCE(AtomToolsAssetBrowser); +} + namespace ShaderManagementConsole { - //! This function returns the build system target name of "ShaderManagementConsole" - AZStd::string ShaderManagementConsoleApplication::GetBuildTargetName() const + ShaderManagementConsoleApplication::ShaderManagementConsoleApplication(int* argc, char*** argv) + : Base(argc, argv) { -#if !defined(LY_CMAKE_TARGET) -#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target" -#endif - return AZStd::string_view{ LY_CMAKE_TARGET }; + InitShaderManagementConsoleResources(); + + QApplication::setApplicationName("O3DE Shader Management Console"); + + // The settings registry has been created at this point, so add the CMake target + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization( + *AZ::SettingsRegistry::Get(), GetBuildTargetName()); + + ShaderManagementConsoleRequestBus::Handler::BusConnect(); + AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect(); + AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusConnect(); + } + + ShaderManagementConsoleApplication::~ShaderManagementConsoleApplication() + { + ShaderManagementConsoleRequestBus::Handler::BusDisconnect(); + AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect(); + AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusDisconnect(); + m_window.reset(); + } + + void ShaderManagementConsoleApplication::Reflect(AZ::ReflectContext* context) + { + Base::Reflect(context); + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("ShaderManagementConsoleRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Category, "Editor") + ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") + ->Event("GetSourceAssetInfo", &ShaderManagementConsoleRequestBus::Events::GetSourceAssetInfo) + ->Event("FindMaterialAssetsUsingShader", &ShaderManagementConsoleRequestBus::Events::FindMaterialAssetsUsingShader ) + ->Event("GetMaterialInstanceShaderItems", &ShaderManagementConsoleRequestBus::Events::GetMaterialInstanceShaderItems) + ; + + behaviorContext->EBus("ShaderManagementConsoleDocumentRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Editor") + ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") + ->Event("GetShaderOptionCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount) + ->Event("GetShaderOptionDescriptor", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor) + ->Event("GetShaderVariantCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount) + ->Event("GetShaderVariantInfo", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo) + ; + } } const char* ShaderManagementConsoleApplication::GetCurrentConfigurationName() const @@ -34,25 +103,139 @@ namespace ShaderManagementConsole #endif } - ShaderManagementConsoleApplication::ShaderManagementConsoleApplication(int* argc, char*** argv) - : Base(argc, argv) + void ShaderManagementConsoleApplication::StartCommon(AZ::Entity* systemEntity) { - QApplication::setApplicationName("O3DE Shader Management Console"); + Base::StartCommon(systemEntity); - // The settings registry has been created at this point, so add the CMake target - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization( - *AZ::SettingsRegistry::Get(), GetBuildTargetName()); + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( + &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType, + []() { return aznew ShaderManagementConsoleDocument(); }); } - void ShaderManagementConsoleApplication::CreateStaticModules(AZStd::vector& outModules) + AZStd::string ShaderManagementConsoleApplication::GetBuildTargetName() const { - Base::CreateStaticModules(outModules); - outModules.push_back(aznew ShaderManagementConsoleDocumentModule); - outModules.push_back(aznew ShaderManagementConsoleWindowModule); +#if !defined(LY_CMAKE_TARGET) +#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target" +#endif + //! Returns the build system target name of "ShaderManagementConsole" + return AZStd::string_view{ LY_CMAKE_TARGET }; } AZStd::vector ShaderManagementConsoleApplication::GetCriticalAssetFilters() const { return AZStd::vector({ "passes/", "config/" }); } + + QWidget* ShaderManagementConsoleApplication::GetAppMainWindow() + { + return m_window.get(); + } + + void ShaderManagementConsoleApplication::CreateMainWindow() + { + m_assetBrowserInteractions.reset(aznew ShaderManagementConsoleBrowserInteractions); + m_window.reset(aznew ShaderManagementConsoleWindow); + m_window->show(); + } + + void ShaderManagementConsoleApplication::DestroyMainWindow() + { + m_window.reset(); + } + + AZ::Data::AssetInfo ShaderManagementConsoleApplication::GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) + { + bool result = false; + AZ::Data::AssetInfo assetInfo; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, sourceAssetFileName.c_str(), assetInfo, + watchFolder); + AZ_Error(nullptr, result, "Failed to get the asset info for the file: %s.", sourceAssetFileName.c_str()); + + return assetInfo; + } + + AZStd::vector ShaderManagementConsoleApplication::FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) + { + // Collect the material types referencing the shader + AZStd::vector materialTypeSources; + + AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection; + assetDatabaseConnection.OpenDatabase(); + + assetDatabaseConnection.QuerySourceDependencyByDependsOnSource( + shaderFilePath.c_str(), nullptr, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any, + [&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry) + { + AZStd::string assetExtension; + if (AzFramework::StringFunc::Path::GetExtension(sourceFileDependencyEntry.m_source.c_str(), assetExtension, false)) + { + if (assetExtension == "materialtype") + { + materialTypeSources.push_back(sourceFileDependencyEntry.m_source); + } + } + return true; + }); + + AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productDependencies; + for (const auto& materialTypeSource : materialTypeSources) + { + bool result = false; + AZ::Data::AssetInfo materialTypeSourceAssetInfo; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, materialTypeSource.c_str(), + materialTypeSourceAssetInfo, watchFolder); + + assetDatabaseConnection.QueryDirectReverseProductDependenciesBySourceGuidSubId( + materialTypeSourceAssetInfo.m_assetId.m_guid, materialTypeSourceAssetInfo.m_assetId.m_subId, + [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& entry) + { + AZStd::string assetExtension; + if (AzFramework::StringFunc::Path::GetExtension(entry.m_productName.c_str(), assetExtension, false)) + { + if (assetExtension == "azmaterial") + { + productDependencies.push_back(entry); + } + } + return true; + }); + } + + AZStd::vector results; + results.reserve(productDependencies.size()); + for (auto product : productDependencies) + { + assetDatabaseConnection.QueryCombinedByProductID( + product.m_productID, + [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined) + { + results.push_back({ combined.m_sourceGuid, combined.m_subID }); + return false; + }, + nullptr); + } + return results; + } + + AZStd::vector ShaderManagementConsoleApplication::GetMaterialInstanceShaderItems( + const AZ::Data::AssetId& assetId) + { + auto materialAsset = AZ::RPI::AssetUtils::LoadAssetById(assetId, AZ::RPI::AssetUtils::TraceLevel::Error); + + auto materialInstance = AZ::RPI::Material::Create(materialAsset); + AZ_Error( + nullptr, materialAsset, "Failed to get a material instance from product asset id: %s", + assetId.ToString().c_str()); + + if (materialInstance != nullptr) + { + return AZStd::vector( + materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end()); + } + return AZStd::vector(); + } } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h index 6b0365e6bd..3bb62d0756 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h @@ -8,12 +8,21 @@ #pragma once +#include #include +#include +#include +#include +#include +#include namespace ShaderManagementConsole { class ShaderManagementConsoleApplication : public AtomToolsFramework::AtomToolsDocumentApplication + , private ShaderManagementConsoleRequestBus::Handler + , private AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler + , private AzToolsFramework::EditorWindowRequestBus::Handler { public: AZ_TYPE_INFO(ShaderManagementConsole::ShaderManagementConsoleApplication, "{A31B1AEB-4DA3-49CD-884A-CC998FF7546F}"); @@ -21,13 +30,31 @@ namespace ShaderManagementConsole using Base = AtomToolsFramework::AtomToolsDocumentApplication; ShaderManagementConsoleApplication(int* argc, char*** argv); + ~ShaderManagementConsoleApplication(); // AzFramework::Application overrides... - void CreateStaticModules(AZStd::vector& outModules) override; + void Reflect(AZ::ReflectContext* context) override; const char* GetCurrentConfigurationName() const override; + void StartCommon(AZ::Entity* systemEntity) override; // AtomToolsFramework::AtomToolsApplication overrides... AZStd::string GetBuildTargetName() const override; AZStd::vector GetCriticalAssetFilters() const override; + + // AzToolsFramework::EditorWindowRequests::Bus::Handler + QWidget* GetAppMainWindow() override; + + // AtomToolsMainWindowFactoryRequestBus::Handler overrides... + void CreateMainWindow() override; + void DestroyMainWindow() override; + + // ShaderManagementConsoleRequestBus::Handler overrides... + AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) override; + AZStd::vector FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) override; + AZStd::vector GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) override; + + private: + AZStd::unique_ptr m_window; + AZStd::unique_ptr m_assetBrowserInteractions; }; } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Core/ShaderManagementConsoleRequestBus.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleRequestBus.h similarity index 100% rename from Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Core/ShaderManagementConsoleRequestBus.h rename to Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleRequestBus.h diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleBrowserWidget.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleBrowserWidget.cpp deleted file mode 100644 index eb863a195a..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleBrowserWidget.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include -#include -AZ_POP_DISABLE_WARNING - -namespace ShaderManagementConsole -{ - ShaderManagementConsoleBrowserWidget::ShaderManagementConsoleBrowserWidget(QWidget* parent) - : QWidget(parent) - , m_ui(new Ui::ShaderManagementConsoleBrowserWidget) - { - using namespace AzToolsFramework::AssetBrowser; - - m_ui->setupUi(this); - - m_ui->m_searchWidget->Setup(true, true); - m_ui->m_searchWidget->SetFilterState("", AZ::RPI::ShaderAsset::Group, true); - m_ui->m_searchWidget->setMinimumSize(QSize(150, 0)); - - // Get the asset browser model - AssetBrowserModel* assetBrowserModel = nullptr; - AssetBrowserComponentRequestBus::BroadcastResult(assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel); - AZ_Assert(assetBrowserModel, "Failed to get file browser model"); - - // Hook up the data set to the tree view - m_filterModel = aznew AssetBrowserFilterModel(this); - m_filterModel->setSourceModel(assetBrowserModel); - m_filterModel->SetFilter(CreateFilter()); - - m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel); - m_ui->m_assetBrowserTreeViewWidget->SetShowSourceControlIcons(true); - m_ui->m_assetBrowserTreeViewWidget->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection); - - // Maintains the tree expansion state between runs - m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); - - connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel, &AssetBrowserFilterModel::filterUpdatedSlot); - connect(m_filterModel, &AssetBrowserFilterModel::filterChanged, this, [this]() - { - const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); - constexpr bool selectFirstFilteredIndex = true; - m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); - }); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::activated, this, &ShaderManagementConsoleBrowserWidget::OpenSelectedEntries); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, [this]() { - const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); - if (!selectedAssets.empty()) - { - m_ui->m_previewerFrame->Display(selectedAssets.front()); - } - else - { - m_ui->m_previewerFrame->Clear(); - } - }); - - AssetBrowserModelNotificationBus::Handler::BusConnect(); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(); - } - - ShaderManagementConsoleBrowserWidget::~ShaderManagementConsoleBrowserWidget() - { - // Maintains the tree expansion state between runs - m_ui->m_assetBrowserTreeViewWidget->SaveState(); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect(); - AssetBrowserModelNotificationBus::Handler::BusDisconnect(); - } - - AzToolsFramework::AssetBrowser::FilterConstType ShaderManagementConsoleBrowserWidget::CreateFilter() const - { - using namespace AzToolsFramework::AssetBrowser; - - QSharedPointer sourceFilter(new EntryTypeFilter); - sourceFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Source); - - QSharedPointer folderFilter(new EntryTypeFilter); - folderFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Folder); - - QSharedPointer sourceOrFolderFilter(new CompositeFilter(CompositeFilter::LogicOperatorType::OR)); - sourceOrFolderFilter->AddFilter(sourceFilter); - sourceOrFolderFilter->AddFilter(folderFilter); - - QSharedPointer finalFilter(new CompositeFilter(CompositeFilter::LogicOperatorType::AND)); - finalFilter->AddFilter(sourceOrFolderFilter); - finalFilter->AddFilter(m_ui->m_searchWidget->GetFilter()); - - return finalFilter; - } - - void ShaderManagementConsoleBrowserWidget::OpenSelectedEntries() - { - const AZStd::vector entries = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); - - const int multiSelectPromptThreshold = 10; - if (entries.size() >= multiSelectPromptThreshold) - { - if (QMessageBox::question( - QApplication::activeWindow(), - QString("Attemptng to open %1 files").arg(entries.size()), - "Would you like to open anyway?", - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - { - return; - } - } - - for (const AssetBrowserEntry* entry : entries) - { - const SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry); - if (!sourceEntry) - { - const ProductAssetBrowserEntry* productEntry = azrtti_cast(entry); - if (productEntry) - { - sourceEntry = azrtti_cast(productEntry->GetParent()); - } - } - - if (sourceEntry) - { - if (AzFramework::StringFunc::Path::IsExtension(sourceEntry->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) - { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, sourceEntry->GetFullPath()); - } - else - { - QDesktopServices::openUrl(QUrl::fromLocalFile(sourceEntry->GetFullPath().c_str())); - } - } - } - } - - void ShaderManagementConsoleBrowserWidget::EntryAdded(const AssetBrowserEntry* entry) - { - if (m_pathToSelect.empty()) - { - return; - } - - const SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry); - if (!sourceEntry) - { - const ProductAssetBrowserEntry* productEntry = azrtti_cast(entry); - if (productEntry) - { - sourceEntry = azrtti_cast(productEntry->GetParent()); - } - } - - if (sourceEntry) - { - AZStd::string sourcePath = sourceEntry->GetFullPath(); - AzFramework::StringFunc::Path::Normalize(sourcePath); - if (m_pathToSelect == sourcePath) - { - m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPath(m_pathToSelect); - m_pathToSelect.clear(); - } - } - } - - void ShaderManagementConsoleBrowserWidget::OnDocumentOpened(const AZ::Uuid& documentId) - { - AZStd::string absolutePath; - AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath); - if (!absolutePath.empty()) - { - m_pathToSelect = absolutePath; - AzFramework::StringFunc::Path::Normalize(m_pathToSelect); - m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPath(m_pathToSelect); - } - } - -} // namespace ShaderManagementConsole - -#include diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp index 8e8e047e83..f4672d670e 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp @@ -6,15 +6,17 @@ * */ -#include #include #include #include #include +#include #include +#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include +#include #include #include #include @@ -39,10 +41,6 @@ namespace ShaderManagementConsole setObjectName("ShaderManagementConsoleWindow"); - m_toolBar = new ShaderManagementConsoleToolBar(this); - m_toolBar->setObjectName("ToolBar"); - addToolBar(m_toolBar); - m_assetBrowser->SetFilterState("", AZ::RPI::ShaderAsset::Group, true); m_assetBrowser->SetOpenHandler([](const AZStd::string& absolutePath) { if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) @@ -63,10 +61,19 @@ namespace ShaderManagementConsole m_actionNew->setEnabled(false); m_actionSaveAsChild->setVisible(false); m_actionSaveAsChild->setEnabled(false); + m_actionSaveAll->setVisible(false); + m_actionSaveAll->setEnabled(false); OnDocumentOpened(AZ::Uuid::CreateNull()); } + bool ShaderManagementConsoleWindow::GetOpenDocumentParams(AZStd::string& openPath) + { + openPath = QFileDialog::getOpenFileName( + this, tr("Open Document"), AZ::Utils::GetProjectPath().c_str(), tr("Files (*.%1)").arg(AZ::RPI::ShaderVariantListSourceData::Extension)).toUtf8().constData(); + return !openPath.empty(); + } + QWidget* ShaderManagementConsoleWindow::CreateDocumentTabView(const AZ::Uuid& documentId) { AZStd::unordered_set optionNames; diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h index 1c7479e526..d5dd04c434 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h @@ -14,7 +14,6 @@ #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include #include AZ_POP_DISABLE_WARNING #endif @@ -36,8 +35,7 @@ namespace ShaderManagementConsole ~ShaderManagementConsoleWindow() = default; protected: + bool GetOpenDocumentParams(AZStd::string& openPath) override; QWidget* CreateDocumentTabView(const AZ::Uuid& documentId) override; - - ShaderManagementConsoleToolBar* m_toolBar = {}; }; } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.cpp deleted file mode 100644 index a59712d572..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include -#include -AZ_POP_DISABLE_WARNING - -namespace ShaderManagementConsole -{ - void ShaderManagementConsoleWindowComponent::Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serialize = azrtti_cast(context)) - { - serialize->Class() - ->Version(0); - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("ShaderManagementConsoleRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") - ->Event("GetSourceAssetInfo", &ShaderManagementConsoleRequestBus::Events::GetSourceAssetInfo) - ->Event("FindMaterialAssetsUsingShader", &ShaderManagementConsoleRequestBus::Events::FindMaterialAssetsUsingShader ) - ->Event("GetMaterialInstanceShaderItems", &ShaderManagementConsoleRequestBus::Events::GetMaterialInstanceShaderItems) - ; - } - } - - void ShaderManagementConsoleWindowComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) - { - required.push_back(AZ_CRC_CE("AssetBrowserService")); - required.push_back(AZ_CRC_CE("PropertyManagerService")); - required.push_back(AZ_CRC_CE("SourceControlService")); - required.push_back(AZ_CRC_CE("AtomToolsMainWindowSystemService")); - } - - void ShaderManagementConsoleWindowComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC_CE("ShaderManagementConsoleWindowService")); - } - - void ShaderManagementConsoleWindowComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) - { - incompatible.push_back(AZ_CRC_CE("ShaderManagementConsoleWindowService")); - } - - void ShaderManagementConsoleWindowComponent::Init() - { - } - - void ShaderManagementConsoleWindowComponent::Activate() - { - AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect(); - AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusConnect(); - ShaderManagementConsoleRequestBus::Handler::BusConnect(); - AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(&AzToolsFramework::SourceControlConnectionRequests::EnableSourceControl, true); - } - - void ShaderManagementConsoleWindowComponent::Deactivate() - { - ShaderManagementConsoleRequestBus::Handler::BusDisconnect(); - AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusDisconnect(); - AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect(); - - m_window.reset(); - } - - QWidget* ShaderManagementConsoleWindowComponent::GetAppMainWindow() - { - return m_window.get(); - } - - void ShaderManagementConsoleWindowComponent::CreateMainWindow() - { - m_assetBrowserInteractions.reset(aznew ShaderManagementConsoleBrowserInteractions); - - m_window.reset(aznew ShaderManagementConsoleWindow); - m_window->show(); - } - - void ShaderManagementConsoleWindowComponent::DestroyMainWindow() - { - m_window.reset(); - } - - AZ::Data::AssetInfo ShaderManagementConsoleWindowComponent::GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) - { - bool result = false; - AZ::Data::AssetInfo assetInfo; - AZStd::string watchFolder; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, sourceAssetFileName.c_str(), assetInfo, - watchFolder); - AZ_Error(nullptr, result, "Failed to get the asset info for the file: %s.", sourceAssetFileName.c_str()); - - return assetInfo; - } - - AZStd::vector ShaderManagementConsoleWindowComponent::FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) - { - // Collect the material types referencing the shader - AZStd::vector materialTypeSources; - - AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection; - assetDatabaseConnection.OpenDatabase(); - - assetDatabaseConnection.QuerySourceDependencyByDependsOnSource( - shaderFilePath.c_str(), - nullptr, - AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any, - [&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry) { - AZStd::string assetExtension; - if (AzFramework::StringFunc::Path::GetExtension(sourceFileDependencyEntry.m_source.c_str(), assetExtension, false)) - { - if (assetExtension == "materialtype") - { - materialTypeSources.push_back(sourceFileDependencyEntry.m_source); - } - } - return true; - }); - - AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productDependencies; - for (const auto& materialTypeSource : materialTypeSources) - { - bool result = false; - AZ::Data::AssetInfo materialTypeSourceAssetInfo; - AZStd::string watchFolder; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - result, - &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, - materialTypeSource.c_str(), - materialTypeSourceAssetInfo, - watchFolder - ); - - assetDatabaseConnection.QueryDirectReverseProductDependenciesBySourceGuidSubId( - materialTypeSourceAssetInfo.m_assetId.m_guid, - materialTypeSourceAssetInfo.m_assetId.m_subId, - [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& entry) { - AZStd::string assetExtension; - if (AzFramework::StringFunc::Path::GetExtension(entry.m_productName.c_str(), assetExtension, false)) - { - if (assetExtension == "azmaterial") - { - productDependencies.push_back(entry); - } - } - return true; - }); - } - - AZStd::vector results; - results.reserve(productDependencies.size()); - for (auto product : productDependencies) - { - assetDatabaseConnection.QueryCombinedByProductID( - product.m_productID, - [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined) { - results.push_back({combined.m_sourceGuid, combined.m_subID}); - return false; - }, - nullptr - ); - } - return results; - } - - AZStd::vector ShaderManagementConsoleWindowComponent::GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) - { - auto materialAsset = AZ::RPI::AssetUtils::LoadAssetById(assetId, AZ::RPI::AssetUtils::TraceLevel::Error); - - auto materialInstance = AZ::RPI::Material::Create(materialAsset); - AZ_Error(nullptr, materialAsset, "Failed to get a material instance from product asset id: %s", assetId.ToString().c_str()); - - if (materialInstance != nullptr) - { - return AZStd::vector(materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end()); - } - return AZStd::vector(); - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.h deleted file mode 100644 index aab0f7ce37..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include - -#include -#include - -#include -#include - -namespace ShaderManagementConsole -{ - //! ShaderManagementConsoleWindowComponent is the entry point for the Shader Management Console gem user interface, and is mainly - //! used for initialization and registration of other classes, including ShaderManagementConsoleWindow. - class ShaderManagementConsoleWindowComponent - : public AZ::Component - , private AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler - , private ShaderManagementConsoleRequestBus::Handler - , private AzToolsFramework::EditorWindowRequestBus::Handler - { - public: - AZ_COMPONENT(ShaderManagementConsoleWindowComponent, "{03976F19-3C74-49FE-A15F-7D3CADBA616C}"); - - static void Reflect(AZ::ReflectContext* context); - - static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - - private: - // Temporary structure when generating shader variants. - struct ShaderVariantListInfo - { - AZStd::string m_materialFileName; - AZStd::vector m_shaderItems; - }; - - ////////////////////////////////////////////////////////////////////////// - // AzToolsFramework::EditorWindowRequests::Bus::Handler - QWidget* GetAppMainWindow() override; - ////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // AtomToolsMainWindowFactoryRequestBus::Handler overrides... - void CreateMainWindow() override; - void DestroyMainWindow() override; - //////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // ShaderManagementConsoleRequestBus::Handler overrides... - AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) override; - AZStd::vector FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) override; - AZStd::vector GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) override; - //////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - void Init() override; - void Activate() override; - void Deactivate() override; - //////////////////////////////////////////////////////////////////////// - - AZStd::unique_ptr m_window; - AZStd::unique_ptr m_assetBrowserInteractions; - }; -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowModule.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowModule.cpp deleted file mode 100644 index a13b873aee..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowModule.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include - -void InitShaderManagementConsoleResources() -{ - // Must register qt resources from other modules - Q_INIT_RESOURCE(ShaderManagementConsole); - Q_INIT_RESOURCE(InspectorWidget); - Q_INIT_RESOURCE(AtomToolsAssetBrowser); -} - -namespace ShaderManagementConsole -{ - ShaderManagementConsoleWindowModule::ShaderManagementConsoleWindowModule() - { - InitShaderManagementConsoleResources(); - - // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. - m_descriptors.insert(m_descriptors.end(), { - ShaderManagementConsoleWindowComponent::CreateDescriptor(), - }); - } - - AZ::ComponentTypeList ShaderManagementConsoleWindowModule::GetRequiredSystemComponents() const - { - return AZ::ComponentTypeList{ - azrtti_typeid(), - }; - } -} // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp deleted file mode 100644 index 5699713240..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include - -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include -#include -#include -AZ_POP_DISABLE_WARNING - -namespace ShaderManagementConsole -{ - ShaderManagementConsoleToolBar::ShaderManagementConsoleToolBar(QWidget* parent) - : QToolBar(parent) - { - AzQtComponents::ToolBar::addMainToolBarStyle(this); - } - - ShaderManagementConsoleToolBar::~ShaderManagementConsoleToolBar() - { - } -} // namespace ShaderManagementConsole - -#include diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.h deleted file mode 100644 index c4d300d4e0..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#if !defined(Q_MOC_RUN) -#include -#endif - -namespace ShaderManagementConsole -{ - class ModelComboBox; - class LightingPresetComboBox; - - class ShaderManagementConsoleToolBar - : public QToolBar - { - Q_OBJECT - public: - - ShaderManagementConsoleToolBar(QWidget* parent = 0); - ~ShaderManagementConsoleToolBar(); - - private: - }; -} // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake index e832ba2784..dac14b7f16 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake @@ -7,8 +7,19 @@ # set(FILES + Source/Document/ShaderManagementConsoleDocumentRequestBus.h + Source/Document/ShaderManagementConsoleDocument.cpp + Source/Document/ShaderManagementConsoleDocument.h + + Source/Window/ShaderManagementConsoleBrowserInteractions.h + Source/Window/ShaderManagementConsoleBrowserInteractions.cpp + Source/Window/ShaderManagementConsoleWindow.h + Source/Window/ShaderManagementConsoleWindow.cpp + Source/Window/ShaderManagementConsole.qrc + Source/main.cpp Source/ShaderManagementConsoleApplication.cpp Source/ShaderManagementConsoleApplication.h + Source/ShaderManagementConsoleRequestBus.h ../Scripts/GenerateShaderVariantListForMaterials.py ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsoledocument_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsoledocument_files.cmake deleted file mode 100644 index 220d2895ac..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsoledocument_files.cmake +++ /dev/null @@ -1,17 +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 -# -# - -set(FILES - Include/Atom/Document/ShaderManagementConsoleDocumentModule.h - Include/Atom/Document/ShaderManagementConsoleDocumentRequestBus.h - Source/Document/ShaderManagementConsoleDocument.cpp - Source/Document/ShaderManagementConsoleDocument.h - Source/Document/ShaderManagementConsoleDocumentModule.cpp - Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp - Source/Document/ShaderManagementConsoleDocumentSystemComponent.h -) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsolewindow_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsolewindow_files.cmake deleted file mode 100644 index fb5cc0dad6..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsolewindow_files.cmake +++ /dev/null @@ -1,22 +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 -# -# - -set(FILES - Include/Atom/Window/ShaderManagementConsoleWindowModule.h - Include/Atom/Core/ShaderManagementConsoleRequestBus.h - Source/Window/ShaderManagementConsoleBrowserInteractions.h - Source/Window/ShaderManagementConsoleBrowserInteractions.cpp - Source/Window/ShaderManagementConsoleWindow.h - Source/Window/ShaderManagementConsoleWindow.cpp - Source/Window/ShaderManagementConsoleWindowModule.cpp - Source/Window/ShaderManagementConsole.qrc - Source/Window/ShaderManagementConsoleWindowComponent.h - Source/Window/ShaderManagementConsoleWindowComponent.cpp - Source/Window/ToolBar/ShaderManagementConsoleToolBar.h - Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp -) From dac77605d923159ddde84d56ed331a14e10b532c Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 20 Jan 2022 16:29:11 -0600 Subject: [PATCH 22/61] Atom Tools: removing unused traits for ME executable extension Signed-off-by: Guthrie Adams --- .../Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h | 3 --- .../Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h | 3 --- .../Source/Platform/Windows/MaterialEditor_Traits_Windows.h | 3 --- 3 files changed, 9 deletions(-) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h index 368b681b4c..03320d1dd8 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h @@ -6,6 +6,3 @@ * */ #pragma once - -#define AZ_TRAIT_MATERIALEDITOR_EXT "" - diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h index 2f46a3a12f..03320d1dd8 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h @@ -6,6 +6,3 @@ * */ #pragma once - -#define AZ_TRAIT_MATERIALEDITOR_EXT ".app" - diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Traits_Windows.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Traits_Windows.h index 2f016a2488..03320d1dd8 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Traits_Windows.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Traits_Windows.h @@ -6,6 +6,3 @@ * */ #pragma once - -#define AZ_TRAIT_MATERIALEDITOR_EXT ".exe" - From b49a7a9975d000e51bc2ac2895752e3d4b1bef74 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 20 Jan 2022 16:58:48 -0600 Subject: [PATCH 23/61] =?UTF-8?q?Atom=20Tools:=20updating=20gem=20autoload?= =?UTF-8?q?=20settings=20registry=20for=20ME=20and=20SMC=20to=20disable=20?= =?UTF-8?q?script=20canvas=20developer=20gem=20Script=20canvas=20and=20rel?= =?UTF-8?q?ated=20gems=20were=20already=20being=20disabled=20for=20some=20?= =?UTF-8?q?atom=20tools.=20The=20script=20canvas=20developer=20gem=20was?= =?UTF-8?q?=20not=20added=20initially=20because=20it=E2=80=99s=20not=20use?= =?UTF-8?q?d=20in=20automated=20testing=20or=20other=20common=20projects.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guthrie Adams --- Registry/gem_autoload.materialeditor.setreg | 3 +++ Registry/gem_autoload.shadermanagementconsole.setreg | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Registry/gem_autoload.materialeditor.setreg b/Registry/gem_autoload.materialeditor.setreg index fd00ff05c7..653cc80056 100644 --- a/Registry/gem_autoload.materialeditor.setreg +++ b/Registry/gem_autoload.materialeditor.setreg @@ -28,6 +28,9 @@ "ScriptCanvas.Editor": { "AutoLoad": false }, + "ScriptCanvasDeveloper": { + "AutoLoad": false + }, "ScriptCanvasPhysics": { "AutoLoad": false }, diff --git a/Registry/gem_autoload.shadermanagementconsole.setreg b/Registry/gem_autoload.shadermanagementconsole.setreg index fd00ff05c7..653cc80056 100644 --- a/Registry/gem_autoload.shadermanagementconsole.setreg +++ b/Registry/gem_autoload.shadermanagementconsole.setreg @@ -28,6 +28,9 @@ "ScriptCanvas.Editor": { "AutoLoad": false }, + "ScriptCanvasDeveloper": { + "AutoLoad": false + }, "ScriptCanvasPhysics": { "AutoLoad": false }, From 858a92f39437d18ac1369aa269f791c54842554b Mon Sep 17 00:00:00 2001 From: Shirang Jia Date: Thu, 20 Jan 2022 15:47:53 -0800 Subject: [PATCH 24/61] Deduplicate Jenkins parameters (#7035) Signed-off-by: Shirang Jia --- scripts/build/Jenkins/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 51f89d7829..b55689b871 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -813,7 +813,7 @@ try { } } - pipelineProperties.add(parameters(pipelineParameters)) + pipelineProperties.add(parameters(pipelineParameters.unique())) properties(pipelineProperties) // Stash the INCREMENTAL_BUILD_SCRIPT_PATH since all nodes will use it From e2d157006c260e99bc3e122d7368de04f5a280af Mon Sep 17 00:00:00 2001 From: evanchia Date: Thu, 20 Jan 2022 15:57:09 -0800 Subject: [PATCH 25/61] Temporary fix for editor log stomping during python tests Signed-off-by: evanchia --- Code/Editor/CryEdit.cpp | 2 -- Tools/LyTestTools/ly_test_tools/o3de/editor_test.py | 8 +++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 1c4e22b99e..cf233dc285 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -2134,8 +2134,6 @@ bool CCryEditApp::FixDanglingSharedMemory(const QString& sharedMemName) const int CCryEditApp::ExitInstance(int exitCode) { - AZ_TracePrintf("Exit", "Called ExitInstance() with exit code: 0x%x", exitCode); - if (m_pEditor) { m_pEditor->OnBeginShutdownSequence(); diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py index 8bd4fbe29a..672259b076 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py @@ -315,7 +315,7 @@ class EditorTestSuite(): return 8 ## Internal ## - _TIMEOUT_CRASH_LOG = 20 # Maximum time (seconds) for waiting for a crash file, in secondss + _TIMEOUT_CRASH_LOG = 20 # Maximum time (seconds) for waiting for a crash file, in seconds _TEST_FAIL_RETCODE = 0xF # Return code for test failure @pytest.fixture(scope="class") @@ -772,7 +772,8 @@ class EditorTestSuite(): return_code = editor.get_returncode() editor_log_content = editor_utils.retrieve_editor_log_content(run_id, log_name, workspace) # Save the editor log - workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name)) + workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name), + f'({run_id}){log_name}') if return_code == 0: test_result = Result.Pass.create(test_spec, output, editor_log_content) else: @@ -847,7 +848,8 @@ class EditorTestSuite(): return_code = editor.get_returncode() editor_log_content = editor_utils.retrieve_editor_log_content(run_id, log_name, workspace) # Save the editor log - workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name)) + workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name), + f'({run_id}){log_name}') if return_code == 0: # No need to scrap the output, as all the tests have passed for test_spec in test_spec_list: From 32e2ba754bc67d66e6dad87916360569db6c4a72 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 20 Jan 2022 16:53:33 -0800 Subject: [PATCH 26/61] Add Dom::Path class for representing positions in a Dom (#7008) * Add Dom::Path class for representing positions in a Dom This also adds Value support for doing a path-based lookup. The serialized representation is presently compliant with the JSON-pointer spec but the implementation supports Node types and may be later expanded if we require additional functionality (e.g. XPath style conditional querying). Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomPath.cpp | 478 ++++++++++++++++++ Code/Framework/AzCore/AzCore/DOM/DomPath.h | 144 ++++++ Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 121 +++++ Code/Framework/AzCore/AzCore/DOM/DomValue.h | 13 + .../AzCore/AzCore/azcore_files.cmake | 2 + .../AzCore/Tests/DOM/DomPathBenchmarks.cpp | 99 ++++ .../AzCore/Tests/DOM/DomPathTests.cpp | 177 +++++++ .../AzCore/Tests/azcoretests_files.cmake | 2 + 8 files changed, 1036 insertions(+) create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomPath.cpp create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomPath.h create mode 100644 Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp create mode 100644 Code/Framework/AzCore/Tests/DOM/DomPathTests.cpp diff --git a/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp b/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp new file mode 100644 index 0000000000..bc50a8513c --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp @@ -0,0 +1,478 @@ +/* + * 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 AZ::Dom +{ + PathEntry::PathEntry(size_t value) + : m_value(value) + { + } + + PathEntry::PathEntry(AZ::Name value) + : m_value(AZStd::move(value)) + { + } + + PathEntry::PathEntry(AZStd::string_view value) + : m_value(AZ::Name(value)) + { + } + + PathEntry& PathEntry::operator=(size_t value) + { + m_value = value; + return *this; + } + + PathEntry& PathEntry::operator=(AZ::Name value) + { + m_value = AZStd::move(value); + return *this; + } + + PathEntry& PathEntry::operator=(AZStd::string_view value) + { + m_value = AZ::Name(value); + return *this; + } + + bool PathEntry::operator==(const PathEntry& other) const + { + return m_value == other.m_value; + } + + bool PathEntry::operator==(size_t value) const + { + return IsIndex() && GetIndex() == value; + } + + bool PathEntry::operator==(const AZ::Name& key) const + { + return IsKey() && GetKey() == key; + } + + bool PathEntry::operator==(AZStd::string_view key) const + { + return IsKey() && GetKey() == AZ::Name(key); + } + + bool PathEntry::operator!=(const PathEntry& other) const + { + return m_value != other.m_value; + } + + bool PathEntry::operator!=(size_t value) const + { + return !IsIndex() || GetIndex() != value; + } + + bool PathEntry::operator!=(const AZ::Name& key) const + { + return !IsKey() || GetKey() != key; + } + + bool PathEntry::operator!=(AZStd::string_view key) const + { + return !IsKey() || GetKey() != AZ::Name(key); + } + + void PathEntry::SetEndOfArray() + { + m_value = EndOfArrayIndex; + } + + bool PathEntry::IsEndOfArray() const + { + const size_t* result = AZStd::get_if(&m_value); + return result == nullptr ? false : ((*result) == EndOfArrayIndex); + } + + bool PathEntry::IsIndex() const + { + const size_t* result = AZStd::get_if(&m_value); + return result == nullptr ? false : ((*result) != EndOfArrayIndex); + } + + bool PathEntry::IsKey() const + { + return AZStd::holds_alternative(m_value); + } + + size_t PathEntry::GetIndex() const + { + AZ_Assert(IsIndex(), "GetIndex called on PathEntry that is not an index"); + return AZStd::get(m_value); + } + + const AZ::Name& PathEntry::GetKey() const + { + AZ_Assert(IsKey(), "Key called on PathEntry that is not a key"); + return AZStd::get(m_value); + } + + Path::Path(AZStd::initializer_list init) + : m_entries(init) + { + } + + Path::Path(AZStd::string_view pathString) + { + FromString(pathString); + } + + Path Path::operator/(const PathEntry& entry) const + { + Path newPath(*this); + newPath /= entry; + return newPath; + } + + Path Path::operator/(size_t index) const + { + return *this / PathEntry(index); + } + + Path Path::operator/(AZ::Name key) const + { + return *this / PathEntry(key); + } + + Path Path::operator/(AZStd::string_view key) const + { + return *this / PathEntry(key); + } + + Path Path::operator/(const Path& other) const + { + Path newPath(*this); + newPath /= other; + return newPath; + } + + Path& Path::operator/=(const PathEntry& entry) + { + Push(entry); + return *this; + } + + Path& Path::operator/=(size_t index) + { + return *this /= PathEntry(index); + } + + Path& Path::operator/=(AZ::Name key) + { + return *this /= PathEntry(key); + } + + Path& Path::operator/=(AZStd::string_view key) + { + return *this /= PathEntry(key); + } + + Path& Path::operator/=(const Path& other) + { + for (const PathEntry& entry : other) + { + Push(entry); + } + return *this; + } + + bool Path::operator==(const Path& other) const + { + return m_entries == other.m_entries; + } + + const Path::ContainerType& Path::GetEntries() const + { + return m_entries; + } + + void Path::Push(PathEntry entry) + { + m_entries.push_back(AZStd::move(entry)); + } + + void Path::Push(size_t entry) + { + Push(PathEntry(entry)); + } + + void Path::Push(AZ::Name entry) + { + Push(PathEntry(AZStd::move(entry))); + } + + void Path::Push(AZStd::string_view entry) + { + Push(AZ::Name(entry)); + } + + void Path::Pop() + { + m_entries.pop_back(); + } + + void Path::Clear() + { + m_entries.clear(); + } + + PathEntry Path::At(size_t index) const + { + if (index < m_entries.size()) + { + return m_entries[index]; + } + return {}; + } + + size_t Path::Size() const + { + return m_entries.size(); + } + + PathEntry& Path::operator[](size_t index) + { + return m_entries[index]; + } + + const PathEntry& Path::operator[](size_t index) const + { + return m_entries[index]; + } + + Path::ContainerType::iterator Path::begin() + { + return m_entries.begin(); + } + + Path::ContainerType::iterator Path::end() + { + return m_entries.end(); + } + + Path::ContainerType::const_iterator Path::begin() const + { + return m_entries.cbegin(); + } + + Path::ContainerType::const_iterator Path::end() const + { + return m_entries.cend(); + } + + Path::ContainerType::const_iterator Path::cbegin() const + { + return m_entries.cbegin(); + } + + Path::ContainerType::const_iterator Path::cend() const + { + return m_entries.cend(); + } + + size_t Path::size() const + { + return m_entries.size(); + } + + size_t Path::GetStringLength() const + { + size_t size = 0; + for (const PathEntry& entry : m_entries) + { + ++size; + if (entry.IsEndOfArray()) + { + size += 1; + } + else if (entry.IsIndex()) + { + const size_t index = entry.GetIndex(); + const double digitCount = index > 0 ? log10(aznumeric_cast(index + 1)) : 1.0; + size += aznumeric_cast(ceil(digitCount)); + } + else + { + const char* nameBuffer = entry.GetKey().GetCStr(); + for (size_t i = 0; nameBuffer[i]; ++i) + { + if (nameBuffer[i] == EscapeCharacter || nameBuffer[i] == PathSeparator) + { + ++size; + } + ++size; + } + } + } + return size; + } + + void Path::FormatString(char* stringBuffer, size_t bufferSize) const + { + size_t bufferIndex = 0; + + auto putChar = [&](char c) + { + if (bufferIndex == bufferSize) + { + return; + } + stringBuffer[bufferIndex++] = c; + }; + + auto writeToBuffer = [&](const char* key) + { + for (size_t keyIndex = 0; key[keyIndex]; ++keyIndex) + { + const char c = key[keyIndex]; + if (c == EscapeCharacter) + { + putChar(EscapeCharacter); + putChar(TildeSequence); + } + else if (c == PathSeparator) + { + putChar(EscapeCharacter); + putChar(ForwardSlashSequence); + } + else + { + putChar(c); + } + } + }; + + for (const PathEntry& entry : m_entries) + { + putChar(PathSeparator); + if (entry.IsEndOfArray()) + { + putChar(EndOfArrayCharacter); + } + else if (entry.IsIndex()) + { + bufferIndex += azsnprintf(&stringBuffer[bufferIndex], bufferSize - bufferIndex, "%zu", entry.GetIndex()); + } + else + { + writeToBuffer(entry.GetKey().GetCStr()); + } + } + + putChar('\0'); + } + + AZStd::string Path::ToString() const + { + AZStd::string formattedString; + const size_t size = GetStringLength(); + formattedString.resize_no_construct(size); + FormatString(formattedString.data(), size + 1); + return formattedString; + } + + void Path::AppendToString(AZStd::string& output) const + { + const size_t startIndex = output.length(); + const size_t stringLength = GetStringLength(); + output.resize_no_construct(startIndex + stringLength); + FormatString(output.data() + startIndex, stringLength + 1); + } + + void Path::FromString(AZStd::string_view pathString) + { + m_entries.clear(); + if (pathString.empty()) + { + return; + } + + size_t pathEntryCount = 0; + for (size_t i = 1; i <= pathString.size(); ++i) + { + if (pathString[i] == PathSeparator) + { + ++pathEntryCount; + } + } + m_entries.reserve(pathEntryCount); + + // Ignore a preceeding path separator and start processing after it + size_t pathIndex = pathString[0] == PathSeparator ? 1 : 0; + bool isNumber = true; + AZStd::string convertedSection; + for (size_t i = pathIndex; i <= pathString.size(); ++i) + { + if (i == pathString.size() || pathString[i] == PathSeparator) + { + AZStd::string_view section = pathString.substr(pathIndex, i - pathIndex); + if (section.size() == 1 && section[0] == EndOfArrayCharacter) + { + PathEntry entry; + entry.SetEndOfArray(); + m_entries.push_back(AZStd::move(entry)); + } + else if (isNumber && !section.empty()) + { + size_t index = 0; + ConsoleTypeHelpers::StringToValue(index, section); + m_entries.push_back(PathEntry{ index }); + } + else + { + convertedSection.clear(); + size_t lastPos = 0; + size_t posToEscape = section.find(EscapeCharacter); + while (posToEscape != AZStd::string_view::npos) + { + if (convertedSection.empty()) + { + convertedSection.reserve(section.size() - 1); + } + convertedSection += section.substr(lastPos, posToEscape - lastPos); + if (section[posToEscape + 1] == ForwardSlashSequence) + { + convertedSection += '/'; + } + else + { + convertedSection += '~'; + } + + lastPos = posToEscape + 2; + posToEscape = section.find(EscapeCharacter, posToEscape + 2); + } + + if (!convertedSection.empty()) + { + convertedSection += section.substr(lastPos); + m_entries.emplace_back(convertedSection); + } + else + { + m_entries.emplace_back(section); + } + } + pathIndex = i + 1; + isNumber = true; + continue; + } + + const char c = pathString[i]; + isNumber = isNumber && c >= '0' && c <= '9'; + } + } +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomPath.h b/Code/Framework/AzCore/AzCore/DOM/DomPath.h new file mode 100644 index 0000000000..39f7c7da98 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomPath.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace AZ::Dom +{ + //! Represents the path to a direct descendant of a Value. + //! PathEntry may be one of the following: + //! - Index, a numerical index for indexing within Arrays and Nodes + //! - Key, a name for indexing within Objects and Nodes + //! - EndOfArray, a special-case indicator for representing the end of an array + //! used by the patching system to represent push / pop back operations. + class PathEntry final + { + public: + static constexpr size_t EndOfArrayIndex = size_t(-1); + + PathEntry() = default; + PathEntry(const PathEntry&) = default; + PathEntry(PathEntry&&) = default; + explicit PathEntry(size_t value); + explicit PathEntry(AZ::Name value); + explicit PathEntry(AZStd::string_view value); + + PathEntry& operator=(const PathEntry&) = default; + PathEntry& operator=(PathEntry&&) = default; + PathEntry& operator=(size_t value); + PathEntry& operator=(AZ::Name value); + PathEntry& operator=(AZStd::string_view value); + + bool operator==(const PathEntry& other) const; + bool operator==(size_t index) const; + bool operator==(const AZ::Name& key) const; + bool operator==(AZStd::string_view key) const; + bool operator!=(const PathEntry& other) const; + bool operator!=(size_t index) const; + bool operator!=(const AZ::Name& key) const; + bool operator!=(AZStd::string_view key) const; + + void SetEndOfArray(); + + bool IsEndOfArray() const; + bool IsIndex() const; + bool IsKey() const; + + size_t GetIndex() const; + const AZ::Name& GetKey() const; + + private: + AZStd::variant m_value; + }; + + //! Represents a path, represented as a series of PathEntry values, to a position in a Value. + class Path final + { + public: + using ContainerType = AZStd::vector; + static constexpr char PathSeparator = '/'; + static constexpr char EscapeCharacter = '~'; + static constexpr char TildeSequence = '0'; + static constexpr char ForwardSlashSequence = '1'; + static constexpr char EndOfArrayCharacter = '-'; + + Path() = default; + Path(const Path&) = default; + Path(Path&&) = default; + explicit Path(AZStd::initializer_list init); + //! Creates a Path from a path string, a path string is formatted per the JSON pointer specification + //! and looks like "/path/to/value/0" + explicit Path(AZStd::string_view pathString); + + template + explicit Path(InputIterator first, InputIterator last) + : m_entries(first, last) + { + } + + Path& operator=(const Path&) = default; + Path& operator=(Path&&) = default; + + Path operator/(const PathEntry&) const; + Path operator/(size_t) const; + Path operator/(AZ::Name) const; + Path operator/(AZStd::string_view) const; + Path operator/(const Path&) const; + + Path& operator/=(const PathEntry&); + Path& operator/=(size_t); + Path& operator/=(AZ::Name); + Path& operator/=(AZStd::string_view); + Path& operator/=(const Path&); + + bool operator==(const Path&) const; + + const ContainerType& GetEntries() const; + void Push(PathEntry entry); + void Push(size_t entry); + void Push(AZ::Name entry); + void Push(AZStd::string_view key); + void Pop(); + void Clear(); + PathEntry At(size_t index) const; + size_t Size() const; + + PathEntry& operator[](size_t index); + const PathEntry& operator[](size_t index) const; + + ContainerType::iterator begin(); + ContainerType::iterator end(); + ContainerType::const_iterator begin() const; + ContainerType::const_iterator end() const; + ContainerType::const_iterator cbegin() const; + ContainerType::const_iterator cend() const; + size_t size() const; + + //! Gets the length this path would require, if string-formatted. + //! The length includes the contents of the string but not a null terminator. + size_t GetStringLength() const; + //! Formats a JSON-pointer style path string into the target buffer. + //! This operation will fail if bufferSize < GetStringLength() + 1 + void FormatString(char* stringBuffer, size_t bufferSize) const; + //! Returns a JSON-pointer style path string for this path. + AZStd::string ToString() const; + void AppendToString(AZStd::string& output) const; + //! Reads a JSON-pointer style path from pathString and replaces this path's contents. + //! Paths are accepted in the following forms: + //! "/path/to/foo/0" + //! "path/to/foo/0" + void FromString(AZStd::string_view pathString); + + private: + ContainerType m_entries; + }; +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 10c8e33715..793fadd092 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -1177,4 +1178,124 @@ namespace AZ::Dom { return m_value; } + + Value& Value::operator[](const PathEntry& entry) + { + if (entry.IsEndOfArray()) + { + Array::ContainerType& array = GetArrayInternal(); + array.push_back(); + return array[array.size() - 1]; + } + return entry.IsIndex() ? operator[](entry.GetIndex()) : operator[](entry.GetKey()); + } + + const Value& Value::operator[](const PathEntry& entry) const + { + return entry.IsIndex() ? operator[](entry.GetIndex()) : operator[](entry.GetKey()); + } + + Value& Value::operator[](const Path& path) + { + Value* value = this; + for (const PathEntry& entry : path) + { + value = &value->operator[](entry); + } + return *value; + } + + const Value& Value::operator[](const Path& path) const + { + const Value* value = this; + for (const PathEntry& entry : path) + { + value = &value->operator[](entry); + } + return *value; + } + + const Value* Value::FindChild(const PathEntry& entry) const + { + if (entry.IsEndOfArray()) + { + return nullptr; + } + else if (entry.IsIndex()) + { + const Array::ContainerType& array = GetArrayInternal(); + const size_t index = entry.GetIndex(); + if (index < array.size()) + { + return &array[index]; + } + } + else + { + const Object::ContainerType& obj = GetObjectInternal(); + auto memberIt = FindMember(entry.GetKey()); + if (memberIt != obj.end()) + { + return &memberIt->second; + } + } + return nullptr; + } + + Value* Value::FindMutableChild(const PathEntry& entry) + { + if (entry.IsEndOfArray()) + { + Array::ContainerType& array = GetArrayInternal(); + array.push_back(); + return &array[array.size() - 1]; + } + else if (entry.IsIndex()) + { + Array::ContainerType& array = GetArrayInternal(); + const size_t index = entry.GetIndex(); + if (index < array.size()) + { + return &array[index]; + } + } + else + { + Object::ContainerType& obj = GetObjectInternal(); + auto memberIt = FindMutableMember(entry.GetKey()); + if (memberIt != obj.end()) + { + return &memberIt->second; + } + } + return nullptr; + } + + const Value* Value::FindChild(const Path& path) const + { + const Value* value = this; + for (const PathEntry& entry : path) + { + value = value->FindChild(entry); + if (value == nullptr) + { + return nullptr; + } + } + return value; + } + + Value* Value::FindMutableChild(const Path& path) + { + Value* value = this; + for (const PathEntry& entry : path) + { + value = value->FindMutableChild(entry); + if (value == nullptr) + { + return nullptr; + } + } + return value; + } } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index d1d3c1745d..be5d855003 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -22,6 +22,8 @@ namespace AZ::Dom { + class PathEntry; + class Path; using KeyType = AZ::Name; //! The type of underlying value stored in a value. \see Value @@ -380,6 +382,17 @@ namespace AZ::Dom Visitor::Result Accept(Visitor& visitor, bool copyStrings) const; AZStd::unique_ptr GetWriteHandler(); + // Path API... + Value& operator[](const PathEntry& entry); + const Value& operator[](const PathEntry& entry) const; + Value& operator[](const Path& path); + const Value& operator[](const Path& path) const; + + const Value* FindChild(const PathEntry& entry) const; + Value* FindMutableChild(const PathEntry& entry); + const Value* FindChild(const Path& path) const; + Value* FindMutableChild(const Path& path); + //! Gets the internal value of this Value. Note that this value's types may not correspond one-to-one with the Type enumeration, //! as internally the same type might have different storage mechanisms. Where possible, prefer using the typed API. const ValueType& GetInternalValue() const; diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 8b8a3b377a..2c0c318648 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -116,6 +116,8 @@ set(FILES Debug/TraceReflection.h DOM/DomBackend.cpp DOM/DomBackend.h + DOM/DomPath.cpp + DOM/DomPath.h DOM/DomUtils.cpp DOM/DomUtils.h DOM/DomValue.cpp diff --git a/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp new file mode 100644 index 0000000000..624b24cd20 --- /dev/null +++ b/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +namespace AZ::Dom::Benchmark +{ + using DomPathBenchmark = Tests::DomBenchmarkFixture; + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPath_Concatenate_InPlace)(benchmark::State& state) + { + AZ::Name entry1("entry1"); + AZ::Name entry2("entry2"); + PathEntry end; + end.SetEndOfArray(); + + for (auto _ : state) + { + Path p; + p /= entry1; + p /= entry2; + p /= 0; + p /= end; + } + + state.SetItemsProcessed(4 * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPath_Concatenate_InPlace); + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPath_Concatenate_Copy)(benchmark::State& state) + { + AZ::Name entry1("entry1"); + AZ::Name entry2("entry2"); + PathEntry end; + end.SetEndOfArray(); + + for (auto _ : state) + { + Path p = Path() / entry1 / entry2 / 0 / end; + } + + state.SetItemsProcessed(4 * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPath_Concatenate_Copy); + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPath_ToString)(benchmark::State& state) + { + Path p("/path/with/multiple/0/different/components/65536/999/-"); + AZStd::string s; + s.resize_no_construct(p.GetStringLength()); + + for (auto _ : state) + { + p.GetStringLength(); + p.FormatString(s.data(), s.size()); + } + + state.SetBytesProcessed(s.size() * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPath_ToString); + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPath_FromString)(benchmark::State& state) + { + AZStd::string pathString = "/path/with/multiple/0/different/components/including-long-strings-like-this/65536/999/-"; + + for (auto _ : state) + { + Path p(pathString); + benchmark::DoNotOptimize(p); + } + + state.SetBytesProcessed(pathString.size() * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPath_FromString); + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPathEntry_IsEndOfArray)(benchmark::State& state) + { + PathEntry name("name"); + PathEntry index(0); + PathEntry endOfArray; + endOfArray.SetEndOfArray(); + + for (auto _ : state) + { + name.IsEndOfArray(); + index.IsEndOfArray(); + endOfArray.IsEndOfArray(); + } + + state.SetItemsProcessed(3 * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPathEntry_IsEndOfArray); +} diff --git a/Code/Framework/AzCore/Tests/DOM/DomPathTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomPathTests.cpp new file mode 100644 index 0000000000..54bff9f29b --- /dev/null +++ b/Code/Framework/AzCore/Tests/DOM/DomPathTests.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AZ::Dom::Tests +{ + class DomPathTests : public DomTestFixture + { + }; + + TEST_F(DomPathTests, EmptyPath_IsEmpty) + { + Path path(""); + EXPECT_EQ(path.GetEntries().size(), 0); + } + + TEST_F(DomPathTests, EmptyPath_IsEqualToDefault) + { + EXPECT_EQ(Path(""), Path()); + } + + TEST_F(DomPathTests, Index_IsNumeric) + { + EXPECT_EQ(Path("/0")[0], 0); + EXPECT_EQ(Path("/20")[0], 20); + EXPECT_EQ(Path("/9999")[0], 9999); + EXPECT_EQ(Path("/0/4/5/1")[3], 1); + } + + TEST_F(DomPathTests, Index_ConvertsToString) + { + Path p; + EXPECT_EQ(p.ToString(), ""); + + p.Push(0); + EXPECT_EQ(p.ToString(), "/0"); + + p.Push(1); + EXPECT_EQ(p.ToString(), "/0/1"); + + p.Push(10); + EXPECT_EQ(p.ToString(), "/0/1/10"); + + p.Push(9999); + EXPECT_EQ(p.ToString(), "/0/1/10/9999"); + + p.Pop(); + EXPECT_EQ(p.ToString(), "/0/1/10"); + } + + TEST_F(DomPathTests, Key_IsString) + { + EXPECT_EQ(Path("/foo")[0], "foo"); + EXPECT_EQ(Path("/bar")[0], "bar"); + EXPECT_EQ(Path("/foo/bar/baz12345")[0], "foo"); + EXPECT_EQ(Path("/foo/bar/baz12345")[2], "baz12345"); + EXPECT_EQ(Path("//foo")[0], ""); + } + + TEST_F(DomPathTests, Key_ConvertsToString) + { + Path p; + + p.Push("foo"); + EXPECT_EQ(p.ToString(), "/foo"); + + p.Push("bar"); + EXPECT_EQ(p.ToString(), "/foo/bar"); + + p.Push("another_key"); + EXPECT_EQ(p.ToString(), "/foo/bar/another_key"); + + p.Pop(); + EXPECT_EQ(p.ToString(), "/foo/bar"); + } + + TEST_F(DomPathTests, Key_ConvertsFromEscaped) + { + EXPECT_EQ(Path("/foo~0")[0], "foo~"); + EXPECT_EQ(Path("/foo~0bar~0~0")[0], "foo~bar~~"); + EXPECT_EQ(Path("/foo~1bar/baz")[0], "foo/bar"); + EXPECT_EQ(Path("/~1foo~1")[0], "/foo/"); + } + + TEST_F(DomPathTests, Key_ConvertsToEscaped) + { + Path p; + + p.Push("with~tilde"); + EXPECT_EQ(p.ToString(), "/with~0tilde"); + + p.Push("with/slash"); + EXPECT_EQ(p.ToString(), "/with~0tilde/with~1slash"); + + p.Clear(); + p.Push("/~with/mixed/characters~"); + EXPECT_EQ(p.ToString(), "/~1~0with~1mixed~1characters~0"); + } + + TEST_F(DomPathTests, MixedPath_Resolves) + { + EXPECT_EQ(Path("/foo/0")[0], "foo"); + EXPECT_EQ(Path("/foo/0")[1], 0); + EXPECT_EQ(Path("/42/foo/bar/0")[0], 42); + EXPECT_EQ(Path("/42/foo/bar/0")[1], "foo"); + EXPECT_EQ(Path("/42/foo/bar/0")[2], "bar"); + EXPECT_EQ(Path("/42/foo/bar/0")[3], 0); + } + + TEST_F(DomPathTests, MixedPath_ConvertsToString) + { + Path p; + + p.Push("foo"); + EXPECT_EQ(p.ToString(), "/foo"); + + p.Push(0); + EXPECT_EQ(p.ToString(), "/foo/0"); + + p.Push("another_key"); + EXPECT_EQ(p.ToString(), "/foo/0/another_key"); + + p.Push(100); + EXPECT_EQ(p.ToString(), "/foo/0/another_key/100"); + + p.Pop(); + EXPECT_EQ(p.ToString(), "/foo/0/another_key"); + } + + TEST_F(DomPathTests, OperatorOverloads_Append) + { + EXPECT_EQ(Path("/foo/bar"), Path("/foo") / Path("/bar")); + EXPECT_EQ(Path("/foo"), Path("/foo") / Path()); + EXPECT_EQ(Path("/foo/1/bar/0"), Path("/foo/1") / Path("/bar/0")); + EXPECT_EQ(Path("/foo") / 0, Path("/foo/0")); + EXPECT_EQ(Path() / "foo" / "bar", Path("/foo/bar")); + EXPECT_EQ(Path("/foo") / "bar" / "baz", Path("/foo/bar/baz")); + + Path p("/foo/bar"); + p /= "baz"; + EXPECT_EQ(p, Path("/foo/bar/baz")); + p /= Path("0/1"); + EXPECT_EQ(p, Path("/foo/bar/baz/0/1")); + } + + TEST_F(DomPathTests, EndOfArray_FromString) + { + EXPECT_FALSE(Path("/foo/-")[0].IsEndOfArray()); + EXPECT_TRUE(Path("/foo/-")[1].IsEndOfArray()); + EXPECT_TRUE(Path("/foo/-/-")[2].IsEndOfArray()); + } + + TEST_F(DomPathTests, EndOfArray_ToString) + { + EXPECT_EQ(Path("/-").ToString(), "/-"); + EXPECT_EQ(Path("/0/-").ToString(), "/0/-"); + } + + TEST_F(DomPathTests, MixedPath_AppendToString) + { + Path p("/foo/0"); + AZStd::string s; + + p.AppendToString(s); + EXPECT_EQ(s, "/foo/0"); + p.AppendToString(s); + EXPECT_EQ(s, "/foo/0/foo/0"); + } +} // namespace AZ::Dom::Tests diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index aee6828b76..24a6601cf0 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -219,6 +219,8 @@ set(FILES DOM/DomFixtures.h DOM/DomJsonTests.cpp DOM/DomJsonBenchmarks.cpp + DOM/DomPathTests.cpp + DOM/DomPathBenchmarks.cpp DOM/DomValueTests.cpp DOM/DomValueBenchmarks.cpp ) From 56c6f81627fcc957f30a46f5e818cad7558c79c8 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Fri, 21 Jan 2022 13:21:57 +0000 Subject: [PATCH 27/61] PR addressing. Removed RTTI from HeightMaterialPoint since it's just a struct of data with no intention being a part of class hierarchy Signed-off-by: Sergey Pereslavtsev --- .../Physics/HeightfieldProviderBus.h | 5 +- Gems/PhysX/Code/Source/Utils.cpp | 83 ++++++++++--------- ...ditorHeightfieldColliderComponentTests.cpp | 13 +-- 3 files changed, 52 insertions(+), 49 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h index da361f0a2b..585fb59ad6 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h @@ -35,16 +35,15 @@ namespace Physics { } - virtual ~HeightMaterialPoint() = default; + ~HeightMaterialPoint() = default; static void Reflect(AZ::ReflectContext* context); - AZ_RTTI(HeightMaterialPoint, "{DF167ED4-24E6-4F7B-8AB7-42622F7DBAD3}"); + AZ_TYPE_INFO(HeightMaterialPoint, "{DF167ED4-24E6-4F7B-8AB7-42622F7DBAD3}"); float m_height{ 0.0f }; //!< Holds the height of this point in the heightfield relative to the heightfield entity location. QuadMeshType m_quadMeshType{ QuadMeshType::SubdivideUpperLeftToBottomRight }; //!< By default, create two triangles like this |\|, where this point is in the upper left corner. uint8_t m_materialIndex{ 0 }; //!< The surface material index for the upper left corner of this quad. uint16_t m_padding{ 0 }; //!< available for future use. - }; //! An interface to provide heightfield values. diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index a988f6791c..950404820d 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -71,7 +71,6 @@ namespace PhysX const int32_t row, const int32_t col, const int32_t numRows, const int32_t numCols) { - uint8_t materialIndex0 = 0; uint8_t materialIndex1 = 0; @@ -81,49 +80,51 @@ namespace PhysX // In PhysX, the material indices refer to the quad down and to the right of the sample. // If we're in the last row or last column, there aren't any quads down or to the right, // so just clear these out. - - if (!lastRowIndex && !lastColumnIndex) + if (lastRowIndex || lastColumnIndex) { - auto GetIndex = [numCols](int32_t row, int32_t col) - { - return (row * numCols) + col; - }; + return { materialIndex0, materialIndex1 }; + } + + auto GetIndex = [numCols](int32_t row, int32_t col) + { + return (row * numCols) + col; + }; - // Our source data is providing one material index per vertex, but PhysX wants one material index - // per triangle. The heuristic that we'll go with for selecting the material index is to choose - // the material for the vertex that's not on the diagonal of each triangle. - // Ex: A *---* B - // | / | For this, we'll use A for index0 and D for index1. - // C *---* D - // - // Ex: A *---* B - // | \ | For this, we'll use C for index0 and B for index1. - // C *---* D - // - // This is a pretty arbitrary choice, so the heuristic might need to be revisited over time if this - // causes incorrect or unpredictable physics material mappings. - - const Physics::HeightMaterialPoint& currentSample = samples[GetIndex(row, col)]; - - switch (currentSample.m_quadMeshType) - { - case Physics::QuadMeshType::SubdivideUpperLeftToBottomRight: - materialIndex0 = samples[GetIndex(row + 1, col)].m_materialIndex; - materialIndex1 = samples[GetIndex(row, col + 1)].m_materialIndex; - break; - case Physics::QuadMeshType::SubdivideBottomLeftToUpperRight: - materialIndex0 = currentSample.m_materialIndex; - materialIndex1 = samples[GetIndex(row + 1, col + 1)].m_materialIndex; - break; - case Physics::QuadMeshType::Hole: - materialIndex0 = physx::PxHeightFieldMaterial::eHOLE; - materialIndex1 = physx::PxHeightFieldMaterial::eHOLE; - break; - default: - AZ_Assert(false, "Unhandled case in GetPhysXMaterialIndicesFromHeightfieldSamples"); - break; - } + // Our source data is providing one material index per vertex, but PhysX wants one material index + // per triangle. The heuristic that we'll go with for selecting the material index is to choose + // the material for the vertex that's not on the diagonal of each triangle. + // Ex: A *---* B + // | / | For this, we'll use A for index0 and D for index1. + // C *---* D + // + // Ex: A *---* B + // | \ | For this, we'll use C for index0 and B for index1. + // C *---* D + // + // This is a pretty arbitrary choice, so the heuristic might need to be revisited over time if this + // causes incorrect or unpredictable physics material mappings. + + const Physics::HeightMaterialPoint& currentSample = samples[GetIndex(row, col)]; + + switch (currentSample.m_quadMeshType) + { + case Physics::QuadMeshType::SubdivideUpperLeftToBottomRight: + materialIndex0 = samples[GetIndex(row + 1, col)].m_materialIndex; + materialIndex1 = samples[GetIndex(row, col + 1)].m_materialIndex; + break; + case Physics::QuadMeshType::SubdivideBottomLeftToUpperRight: + materialIndex0 = currentSample.m_materialIndex; + materialIndex1 = samples[GetIndex(row + 1, col + 1)].m_materialIndex; + break; + case Physics::QuadMeshType::Hole: + materialIndex0 = physx::PxHeightFieldMaterial::eHOLE; + materialIndex1 = physx::PxHeightFieldMaterial::eHOLE; + break; + default: + AZ_Assert(false, "Unhandled case in GetPhysXMaterialIndicesFromHeightfieldSamples"); + break; } + return { materialIndex0, materialIndex1 }; } diff --git a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp index 9bdc63e60d..9a216b7d80 100644 --- a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp @@ -139,9 +139,9 @@ namespace PhysXEditorTests // Create an asset out of our Script Event Physics::MaterialLibraryAsset* matLibAsset = aznew Physics::MaterialLibraryAsset; { - AZStd::vector matIds = GetMaterialList(); + const AZStd::vector matIds = GetMaterialList(); - for (Physics::MaterialId matId : matIds) + for (const Physics::MaterialId& matId : matIds) { Physics::MaterialFromAssetConfiguration matConfig; matConfig.m_id = matId; @@ -336,7 +336,7 @@ namespace PhysXEditorTests // PhysX Heightfield cooking doesn't map 1-1 sample material indices to triangle material indices // Hence hardcoding the expected material indices in the test - const int physicsMaterialsValidationDataIndex[] = {0, 2, 1, 1}; + const AZStd::array physicsMaterialsValidationDataIndex = {0, 2, 1, 1}; for (int sampleRow = 0; sampleRow < numRows; ++sampleRow) { @@ -364,8 +364,11 @@ namespace PhysXEditorTests Physics::Material* mat2 = GetMaterialFromRaycast(rayX + secondRayOffset, rayY + secondRayOffset); EXPECT_NE(mat2, nullptr); - AZStd::string expectedMaterialName = physicsSurfaceTypes[physicsMaterialsValidationDataIndex[sampleRow * 2 + sampleColumn]]; - EXPECT_EQ(mat1->GetSurfaceTypeName(), expectedMaterialName); + if (mat1) + { + AZStd::string expectedMaterialName = physicsSurfaceTypes[physicsMaterialsValidationDataIndex[sampleRow * 2 + sampleColumn]]; + EXPECT_EQ(mat1->GetSurfaceTypeName(), expectedMaterialName); + } } } } From 9328f053bd40c2012bf7c06f94d479c3813c05af Mon Sep 17 00:00:00 2001 From: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com> Date: Fri, 21 Jan 2022 08:00:45 -0800 Subject: [PATCH 28/61] Updates EXPAT to use the latest packages (#7053) * Update EXPAT to be latest package version Signed-off-by: lawsonamzn <70027408+lawsonamzn@users.noreply.github.com> --- Code/Legacy/CrySystem/XML/xml.cpp | 1 - cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake | 2 +- cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake | 2 +- cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake | 2 +- cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake | 2 +- cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake | 2 +- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Code/Legacy/CrySystem/XML/xml.cpp b/Code/Legacy/CrySystem/XML/xml.cpp index 95755d78fe..fb0714500c 100644 --- a/Code/Legacy/CrySystem/XML/xml.cpp +++ b/Code/Legacy/CrySystem/XML/xml.cpp @@ -11,7 +11,6 @@ #include -#define XML_STATIC // Alternative to defining this here would be setting it project-wide #include #include "xml.h" #include diff --git a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake index c6a262926c..d231954c49 100644 --- a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake +++ b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake @@ -11,7 +11,7 @@ ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023) ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-android TARGETS expat PACKAGE_HASH 8c626bd58c2f9bb82cb654c2483be60e67f3a601547dee43686638a5e662bf3e) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 8db3099a5c..ee91415a6d 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -13,7 +13,7 @@ ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev3-multiplatform TARGETS pybind11 PACKAGE_HASH dccb5546607b8b31cd207033aaf24ab044ce6e188a9f12411236a010f9e0c4ff) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-linux TARGETS expat PACKAGE_HASH 07621d684fd909e2768e696a2652cfb1e975093f738193cfdcb60a016a9a9d4e) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 95f7c89930..1bcad01c1d 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -13,7 +13,7 @@ ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev3-multiplatform TARGETS pybind11 PACKAGE_HASH dccb5546607b8b31cd207033aaf24ab044ce6e188a9f12411236a010f9e0c4ff) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-mac TARGETS expat PACKAGE_HASH 231ec2cb8ef9ddeef891e5bd7215ad91864e3410a22fad5ab8355e7bf53621fe) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 1f7cb8d28f..c872708a3a 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -13,7 +13,7 @@ ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev3-multiplatform TARGETS pybind11 PACKAGE_HASH dccb5546607b8b31cd207033aaf24ab044ce6e188a9f12411236a010f9e0c4ff) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-windows TARGETS expat PACKAGE_HASH 123d81dcd0e30306fdb0d062348b992e68358cc31f0a15a98c4c04fc68dc437e) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) diff --git a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake index 4b99efc9d0..3f1bb204f7 100644 --- a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake +++ b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake @@ -11,7 +11,7 @@ ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023) ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-ios TARGETS expat PACKAGE_HASH 2e4f1ecaa9d7e9eb2f6f3b4a32d1b1a12605accf0ab3975175553b90917eb2d5) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) From 145e3646a49fab1e3594d95c157e2f7ced824e24 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Fri, 21 Jan 2022 10:34:00 -0600 Subject: [PATCH 29/61] Asset catalog lock inversion fix. (#7045) * Fix lock inversion. This method was calling AssetCatalog->AssetManager at the same time that loading threads are calling AssetManager->AssetCatalog, causing a deadlock due to lock inversion. The fix is to make this method call AssetManager *outside* of the AssetCatalog call. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixing the root cause in EnumerateAssets. Also added a unit test that failed with the previous EnumerateAssets logic, and succeeds with the new logic. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Make sure not to hold the secondary mutex lock either. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Remove unused alias. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../AzFramework/Asset/AssetCatalog.cpp | 29 ++++- .../AzFramework/Tests/AssetCatalog.cpp | 105 ++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp index 0c6b195434..a0f6b0608c 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp @@ -489,6 +489,21 @@ namespace AzFramework //========================================================================= void AssetCatalog::EnumerateAssets(BeginAssetEnumerationCB beginCB, AssetEnumerationCB enumerateCB, EndAssetEnumerationCB endCB) { + using AssetCatalogRequestBusContext = typename AZ::Data::AssetCatalogRequestBus::Context; + + // Setting trackCallstack to true causes the context mutex to attempt to re-lock. + // That is being avoided here as the code only wants to unlock the Context mutex if it is in a dispatch. + constexpr bool trackCallstack = false; + AssetCatalogRequestBusContext* assetCatalogContext = AZ::Data::AssetCatalogRequestBus::GetContext(trackCallstack); + + bool hasAssetCatalogMutex = false; + if (assetCatalogContext != nullptr && AZ::Data::AssetCatalogRequestBus::IsInDispatchThisThread(assetCatalogContext)) + { + hasAssetCatalogMutex = true; + // Unlock the dispatch mutex for the AssetCatalogRequestBus + assetCatalogContext->m_contextMutex.unlock(); + } + if (beginCB) { beginCB(); @@ -496,9 +511,13 @@ namespace AzFramework if (enumerateCB) { - AZStd::lock_guard lock(m_registryMutex); + // Make sure we don't hold on to any locks during the enumerateCB, so copy the registry info to a local variable + // and unlock the registryMutex before calling the callback. + m_registryMutex.lock(); + auto assetIdToInfoCopy = m_registry->m_assetIdToInfo; + m_registryMutex.unlock(); - for (auto& it : m_registry->m_assetIdToInfo) + for (auto& it : assetIdToInfoCopy) { enumerateCB(it.first, it.second); } @@ -508,6 +527,12 @@ namespace AzFramework { endCB(); } + + if (hasAssetCatalogMutex) + { + // Relock the mutex if it was unlocked earlier + assetCatalogContext->m_contextMutex.lock(); + } } //========================================================================= diff --git a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp index 8a24d164cd..e731b633f0 100644 --- a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -931,6 +933,109 @@ namespace UnitTest EXPECT_FALSE(m_assetCatalog->DoesAssetIdMatchWildcardPattern(m_firstAssetId, "")); } + TEST_F(AssetCatalogAPITest, EnumerateAssetsListsCorrectAssets) + { + AZStd::vector foundAssets; + + AZ::Data::AssetCatalogRequestBus::Broadcast( + &AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, + [&foundAssets](const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetInfo& assetInfo) + { + foundAssets.emplace_back(assetId); + }, + nullptr); + + ASSERT_EQ(foundAssets.size(), 2); + EXPECT_EQ(foundAssets[0], m_firstAssetId); + EXPECT_EQ(foundAssets[1], m_secondAssetId); + } + + TEST_F(AssetCatalogAPITest, EnumerateAssetsDoesNotBlockMutex) + { + // This test simulates a previously-occurring deadlock bug caused by having the main thread call AssetCatalog::EnumerateAssets + // with a callback that calls the AssetManager, and a loading thread running underneath the AssetManager that calls the + // AssetCatalog. + // To reproduce this state, we will do the following: + // Main Thread Job Thread + // wait on mainToJobSync + // call EnumerateAssets + // unblock mainToJobSync + // wait on jobToMainSync call AssetCatalog::X + // unblock jobToMainSync + // + // These steps should ensure that we create a theoretical "lock inversion", if EnumerateAssets locked an AssetCatalog mutex. + // Even though we're using binary semaphores instead of mutexes, we're creating the same blocking situation. + // EnumerateAssets itself should no longer lock the AssetCatalog mutex, so this test should succeed since there won't be any + // actual lock inversion. + + // Set up job manager with one thread that we can use to set up the concurrent mutex access. + AZ::AllocatorInstance::Create(); + AZ::JobManagerDesc desc; + AZ::JobManagerThreadDesc threadDesc; + desc.m_workerThreads.push_back(threadDesc); + auto jobManager = aznew AZ::JobManager(desc); + auto jobContext = aznew AZ::JobContext(*jobManager); + AZ::JobContext::SetGlobalContext(jobContext); + + AZStd::binary_semaphore mainToJobSync; + AZStd::binary_semaphore jobToMainSync; + + // Create a job whose sole purpose is to wait for EnumerateAssets to get called, call AssetCatalog, then tell EnumerateAssets + // to finish. If EnumerateAssets has a mutex held, this will deadlock. + auto job = AZ::CreateJobFunction( + [&mainToJobSync, &jobToMainSync]() mutable + { + // wait for the main thread to be inside the EnumerateAssets callback. + mainToJobSync.acquire(); + + // call AssetCatalog::X. This will deadlock if EnumerateAssets is holding an AssetCatalog mutex. + [[maybe_unused]] AZStd::string assetPath; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetPath, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetPathById, AZ::Data::AssetId()); + + // signal the main thread to continue + jobToMainSync.release(); + }, + true); + job->Start(); + + bool testCompletedSuccessfully = false; + + // Call EnumerateAssets with a callback that also tries to lock the mutex. + AZ::Data::AssetCatalogRequestBus::Broadcast( + &AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, + [this, &mainToJobSync, &jobToMainSync, &testCompletedSuccessfully] + (const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetInfo& assetInfo) + { + // Only run this logic on the first assetId. + if (assetId == m_firstAssetId) + { + // Tell the job it can continue + mainToJobSync.release(); + + // Wait for the job to finish calling AssetCatalog::X. This will deadlock if EnumerateAssets is holding an + // AssetCatalog mutex. + if (jobToMainSync.try_acquire_for(AZStd::chrono::seconds(5))) + { + // Only mark the test as completed successfully if we ran this logic and didn't deadlock. + testCompletedSuccessfully = true; + } + + } + }, + nullptr); + + EXPECT_TRUE(testCompletedSuccessfully); + + // Clean up the job manager + AZ::JobContext::SetGlobalContext(nullptr); + delete jobContext; + delete jobManager; + AZ::AllocatorInstance::Destroy(); + } + + + class AssetType1 : public AssetData { From 526ee7644a86ad6c2c2c4cfbc3d00ef3e0b02946 Mon Sep 17 00:00:00 2001 From: Scott Romero <24445312+AMZN-ScottR@users.noreply.github.com> Date: Fri, 21 Jan 2022 09:04:33 -0800 Subject: [PATCH 30/61] [development] fixed configure error with monolithic builds (#7057) The AWSNativeSDKTestLibs target is now only defined when tests are enabled Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com --- Code/Tools/AWSNativeSDKInit/CMakeLists.txt | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Code/Tools/AWSNativeSDKInit/CMakeLists.txt b/Code/Tools/AWSNativeSDKInit/CMakeLists.txt index de3e0008c2..7f04262781 100644 --- a/Code/Tools/AWSNativeSDKInit/CMakeLists.txt +++ b/Code/Tools/AWSNativeSDKInit/CMakeLists.txt @@ -25,28 +25,28 @@ ly_add_target( AZ::AzCore ) -ly_add_target( - NAME AWSNativeSDKTestLibs STATIC - NAMESPACE AZ - FILES_CMAKE - aws_native_sdk_test_files.cmake - INCLUDE_DIRECTORIES - PUBLIC - include - tests/libs - PRIVATE - source - BUILD_DEPENDENCIES - PRIVATE - 3rdParty::AWSNativeSDK::Core - AZ::AzCore - AZ::AzTest -) - ################################################################################ # Tests ################################################################################ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME AWSNativeSDKTestLibs STATIC + NAMESPACE AZ + FILES_CMAKE + aws_native_sdk_test_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + include + tests/libs + PRIVATE + source + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::AWSNativeSDK::Core + AZ::AzCore + AZ::AzTest + ) + ly_add_target( NAME AWSNativeSDKInit.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE AZ From 09dd3a87299c20b7c27da52f8f866f7ea9b7098f Mon Sep 17 00:00:00 2001 From: Scott Romero <24445312+AMZN-ScottR@users.noreply.github.com> Date: Fri, 21 Jan 2022 09:06:14 -0800 Subject: [PATCH 31/61] [development] patch fix for launcher exit crash (#7050) The launcher will crash on exit due to an access violation when clearing the BudgetTracker after CrySystem is unloaded. This is from a recent change to how the budget pointers are tracked making the BudgetTracker responsible for clearing them so the system can be re-entrant. Unfortunately, an assumption was made that all modules that use Budgets are managed by the ComponentApplication so they can be cleared prior to being unloaded. This is a patch to manually reset the BudgetTracker prior to unloading CrySystem until the legacy system can be removed (or converted to an AZ-like module). Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com --- Code/LauncherUnified/Launcher.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index 9dfd6213c4..7b681c5208 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -10,14 +10,17 @@ #include #include +#include +#include #include +#include #include #include #include -#include #include #include #include + #include #include #include @@ -571,6 +574,17 @@ namespace O3DELauncher } #if !defined(AZ_MONOLITHIC_BUILD) + + #if !defined(_RELEASE) + // until CrySystem can be removed (or made to be managed by the component application), + // we need to manually clear the BudgetTracker before CrySystem is unloaded so the Budget + // pointer(s) it has references to are cleared properly + if (auto budgetTracker = AZ::Interface::Get(); budgetTracker) + { + budgetTracker->Reset(); + } + #endif // !defined(_RELEASE) + delete systemInitParams.pSystem; crySystemLibrary.reset(nullptr); #endif // !defined(AZ_MONOLITHIC_BUILD) From 807464c58d29fa3c6afde1a12551b0f4ed2b7750 Mon Sep 17 00:00:00 2001 From: Scott Romero <24445312+AMZN-ScottR@users.noreply.github.com> Date: Fri, 21 Jan 2022 09:08:24 -0800 Subject: [PATCH 32/61] [development] updated Android build node configuration (#7007) Added the NDK to the install list Removed Android Java APIs prior to 28 from package list as they are unnecessary Updated LY_NDK_DIR to point to the SDK manager version Updated some inconsistencies in how the min Android native API is handled Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com --- cmake/Platform/Android/Toolchain_android.cmake | 10 +++++++++- .../Tools/Platform/Android/generate_android_project.py | 2 +- scripts/build/Platform/Android/build_config.json | 10 +++++----- scripts/build/Platform/Android/pipeline.json | 4 ++-- .../build_node/Platform/Windows/install_android.ps1 | 6 +++--- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/cmake/Platform/Android/Toolchain_android.cmake b/cmake/Platform/Android/Toolchain_android.cmake index 974eb33681..23b51a4c70 100644 --- a/cmake/Platform/Android/Toolchain_android.cmake +++ b/cmake/Platform/Android/Toolchain_android.cmake @@ -36,9 +36,17 @@ endif() if(NOT ANDROID_ABI MATCHES "^arm64-") message(FATAL_ERROR "Only the 64-bit ANDROID_ABI's are supported. arm64-v8a can be used if not set") endif() + +set(MIN_NATIVE_API_LEVEL 24) + if(NOT ANDROID_NATIVE_API_LEVEL) - set(ANDROID_NATIVE_API_LEVEL 24) + set(ANDROID_NATIVE_API_LEVEL ${MIN_NATIVE_API_LEVEL}) endif() + +if(${ANDROID_NATIVE_API_LEVEL} VERSION_LESS ${MIN_NATIVE_API_LEVEL}) + message(FATAL_ERROR "Unsupported Android native API version ${ANDROID_NATIVE_API_LEVEL}. Must be ${MIN_NATIVE_API_LEVEL} or above") +endif() + set(ANDROID_PLATFORM android-${ANDROID_NATIVE_API_LEVEL}) # Make a backup of the CMAKE_FIND_ROOT_PATH since it will be altered by the NDK toolchain file and needs to be restored after the input diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index 5e414e7738..a12006bd53 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -181,7 +181,7 @@ def main(args): default=-1) parser.add_argument(ANDROID_NATIVE_API_LEVEL, - help=f'The android native API level to use for the APK. If not set, this will default to the android SDK platform. (Minimum {MIN_ANDROID_SDK_PLATFORM})', + help=f'The android native API level to use for the APK. If not set, this will default to the android SDK platform. (Minimum {MIN_NATIVE_API_LEVEL})', type=int, default=-1) diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index c505d8e940..da538b22ec 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -35,7 +35,7 @@ "PARAMETERS": { "CONFIGURATION":"debug", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -50,7 +50,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -66,7 +66,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_UNITY_BUILD=FALSE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_UNITY_BUILD=FALSE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -102,7 +102,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -118,7 +118,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\mono_android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_MONOLITHIC_GAME=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_MONOLITHIC_GAME=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" diff --git a/scripts/build/Platform/Android/pipeline.json b/scripts/build/Platform/Android/pipeline.json index f0292f4501..2e426c7891 100644 --- a/scripts/build/Platform/Android/pipeline.json +++ b/scripts/build/Platform/Android/pipeline.json @@ -1,9 +1,9 @@ { "ENV": { "GRADLE_HOME": "C:/Gradle/gradle-7.0", - "NODE_LABEL": "windows-b3c8994f1", + "NODE_LABEL": "windows-a4a28adb2", "LY_3RDPARTY_PATH": "D:/workspace/3rdParty", - "LY_NDK_DIR": "C:/ly/3rdParty/android-ndk/r21d", + "LY_NDK_DIR": "C:/Android/android-sdk/ndk/21.4.7075529", "TIMEOUT": 30, "WORKSPACE": "D:/workspace", "MOUNT_VOLUME": true diff --git a/scripts/build/build_node/Platform/Windows/install_android.ps1 b/scripts/build/build_node/Platform/Windows/install_android.ps1 index 0a725af573..497ec508cc 100644 --- a/scripts/build/build_node/Platform/Windows/install_android.ps1 +++ b/scripts/build/build_node/Platform/Windows/install_android.ps1 @@ -10,16 +10,16 @@ choco install -y android-sdk $Env:ANDROID_HOME = "C:\Android\android-sdk" setx ANDROID_HOME "C:\Android\android-sdk" -$apis_packages = '"add-ons;addon-google_apis-google-19" "add-ons;addon-google_apis-google-21" "add-ons;addon-google_apis-google-22" "add-ons;addon-google_apis-google-23" "add-ons;addon-google_apis-google-24"' -$android_packages = '"platforms;android-19" "platforms;android-21" "platforms;android-22" "platforms;android-23" "platforms;android-24" "platforms;android-25" "platforms;android-26" "platforms;android-27" "platforms;android-28" "platforms;android-29" "platforms;android-30"' +$android_packages = '"platforms;android-28" "platforms;android-29" "platforms;android-30"' $googleplay_packages = '"extras;google;market_apk_expansion" "extras;google;market_licensing"' $build_tools = '"build-tools;30.0.2" "tools"' +$ndk = '"ndk;21.4.7075529"' $sdkmanager = "C:\Android\android-sdk\tools\bin\sdkmanager.bat" -Start-Process -FilePath $sdkmanager -ArgumentList $apis_packages -NoNewWindow -Wait Start-Process -FilePath $sdkmanager -ArgumentList $android_packages -NoNewWindow -Wait Start-Process -FilePath $sdkmanager -ArgumentList $googleplay_packages -NoNewWindow -Wait Start-Process -FilePath $sdkmanager -ArgumentList $build_tools -NoNewWindow -Wait +Start-Process -FilePath $sdkmanager -ArgumentList $ndk -NoNewWindow -Wait Write-Host "Installing Gradle and Ninja" Import-Module C:\ProgramData\chocolatey\helpers\chocolateyInstaller.psm1 #Grade needs a custom installer due to being hardcoded to C:\Programdata in Chocolatey From edbebb5f477f14dfc86535800201c50ca7781400 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Fri, 21 Jan 2022 09:19:54 -0800 Subject: [PATCH 33/61] Minor class definition updates for AWSGem (#7056) * Move the queued-events constructors for AWSApiClientJob and ServiceRequestJob from public to protected, and add short description for them Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> --- .../Code/Include/Framework/AWSApiRequestJob.h | 37 ++++++++---------- .../Include/Framework/ServiceRequestJob.h | 38 ++++++++----------- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/Gems/AWSCore/Code/Include/Framework/AWSApiRequestJob.h b/Gems/AWSCore/Code/Include/Framework/AWSApiRequestJob.h index 33c7e1ad29..816e9bc4b4 100644 --- a/Gems/AWSCore/Code/Include/Framework/AWSApiRequestJob.h +++ b/Gems/AWSCore/Code/Include/Framework/AWSApiRequestJob.h @@ -188,28 +188,6 @@ namespace AWSCore { } - AwsApiRequestJob(bool queueOnSuccess, - OnSuccessFunction onSuccess, - bool queueOnFailure, - OnFailureFunction onFailure, - bool queueDelete, - IConfig* config = GetDefaultConfig() - ) : AwsApiClientJobType(false, config) - , m_queueOnSuccess{ queueOnSuccess } - , m_onSuccess{ onSuccess } - , m_queueOnFailure{ queueOnFailure } - , m_onFailure{ onFailure } - , m_queueDelete{ queueDelete } - { - } - - AwsApiRequestJob(OnSuccessFunction onSuccess, - OnFailureFunction onFailure, - IConfig* config = GetDefaultConfig() - ) : AwsApiRequestJob(true, onSuccess, true, onFailure, true, config) - { - } - RequestType request; ResultType result; ErrorType error; @@ -232,6 +210,21 @@ namespace AWSCore } protected: + + /// Constructor for creating AwsApiRequestJob Jobs that can handle queued responses + /// for OnSuccess, OnFailure, and DoCleanup + AwsApiRequestJob(OnSuccessFunction onSuccess, + OnFailureFunction onFailure, + IConfig* config = GetDefaultConfig() + ) : AwsApiClientJobType(false, config) + , m_queueOnSuccess{ true } + , m_onSuccess{ onSuccess } + , m_queueOnFailure{ true } + , m_onFailure{ onFailure } + , m_queueDelete{ true } + { + } + bool m_wasSuccess{ false }; // Flag and optional function call to queue for onSuccess events diff --git a/Gems/AWSCore/Code/Include/Framework/ServiceRequestJob.h b/Gems/AWSCore/Code/Include/Framework/ServiceRequestJob.h index 6d00122d99..d3eac4983a 100644 --- a/Gems/AWSCore/Code/Include/Framework/ServiceRequestJob.h +++ b/Gems/AWSCore/Code/Include/Framework/ServiceRequestJob.h @@ -178,29 +178,6 @@ namespace AWSCore } } - ServiceRequestJob(bool queueOnSuccess, - OnSuccessFunction onSuccess, - bool queueOnFailure, - OnFailureFunction onFailure, - bool queueDelete, - IConfig* config = GetDefaultConfig() - ) : ServiceClientJobType{ false, config } - , m_requestUrl{ config->GetRequestUrl() } - , m_queueOnSuccess{ queueOnSuccess } - , m_onSuccess{ onSuccess } - , m_queueOnFailure{ queueOnFailure } - , m_onFailure{ onFailure } - , m_queueDelete{ queueDelete } - { - } - - ServiceRequestJob(OnSuccessFunction onSuccess, - OnFailureFunction onFailure, - IConfig* config = GetDefaultConfig() - ) : ServiceRequestJob(true, onSuccess, true, onFailure, true, config) - { - } - bool HasCredentials(IConfig* config) { if (config == nullptr) @@ -241,6 +218,21 @@ namespace AWSCore /// for replacing these parts of the url. const Aws::String& m_requestUrl; + /// Constructor for creating ServiceRequestJob Jobs that can handle queued responses + /// for OnSuccess, OnFailure, and DoCleanup + ServiceRequestJob(OnSuccessFunction onSuccess, + OnFailureFunction onFailure, + IConfig* config = GetDefaultConfig() + ) : ServiceClientJobType{ false, config } + , m_requestUrl{ config->GetRequestUrl() } + , m_queueOnSuccess{ true } + , m_onSuccess{ onSuccess } + , m_queueOnFailure{ true } + , m_onFailure{ onFailure } + , m_queueDelete{ true } + { + } + // Flag and optional function call to queue for onSuccess events bool m_queueOnSuccess{ false }; OnSuccessFunction m_onSuccess{}; From 1d3b675f889a742ea577aa73edbdc694975a5d56 Mon Sep 17 00:00:00 2001 From: lsemp3d <58790905+lsemp3d@users.noreply.github.com> Date: Fri, 21 Jan 2022 09:35:42 -0800 Subject: [PATCH 34/61] Regenerated the Input handler .names file Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> --- .../Nodes/Input_InputHandler.names | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/Nodes/Input_InputHandler.names b/Gems/ScriptCanvas/Assets/TranslationAssets/Nodes/Input_InputHandler.names index 8ce3fd36df..120c8f8ef3 100644 --- a/Gems/ScriptCanvas/Assets/TranslationAssets/Nodes/Input_InputHandler.names +++ b/Gems/ScriptCanvas/Assets/TranslationAssets/Nodes/Input_InputHandler.names @@ -1,7 +1,7 @@ { "entries": [ { - "base": "{0B0AC61B-4BBA-42BF-BDCD-DAF2D3CA41A8}", + "base": "{0A2EB488-5A6A-E166-BB62-23FF81499E33}", "context": "ScriptCanvas::Node", "variant": "", "details": { @@ -10,6 +10,13 @@ "tooltip": "Handle processed input events found in input binding assets" }, "slots": [ + { + "base": "Input_Connect Event_0", + "details": { + "name": "Connect Event", + "tooltip": "Connect to input event name as defined in an input binding asset." + } + }, { "base": "DataInput_Event Name_0", "details": { @@ -17,27 +24,34 @@ } }, { - "base": "DataOutput_Value_0", + "base": "Output_On Connect Event_0", "details": { - "name": "Value" + "name": "On Connect Event", + "tooltip": "Connect to input event name as defined in an input binding asset." } }, { - "base": "Output_Pressed_0", + "base": "Output_Pressed_1", "details": { "name": "Pressed", "tooltip": "Signaled when the input event begins." } }, { - "base": "Output_Held_1", + "base": "DataOutput_value_0", + "details": { + "name": "value" + } + }, + { + "base": "Output_Held_2", "details": { "name": "Held", "tooltip": "Signaled while the input event is active." } }, { - "base": "Output_Released_2", + "base": "Output_Released_3", "details": { "name": "Released", "tooltip": "Signaled when the input event ends." From be2241bda956d286fbf0161600d84422635f1a15 Mon Sep 17 00:00:00 2001 From: lsemp3d <58790905+lsemp3d@users.noreply.github.com> Date: Fri, 21 Jan 2022 09:41:37 -0800 Subject: [PATCH 35/61] Navigate to file after generating translation (only 1) Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> --- .../ScriptCanvasNodePaletteDockWidget.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp index 60d903d748..4b622bc025 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp @@ -894,6 +894,26 @@ namespace ScriptCanvasEditor GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast(sourceIndex.internalPointer()); nodePaletteItem->GenerateTranslationData(); } + + if (indexList.size() == 1) + { + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + QModelIndex sourceIndex = filterModel->mapToSource(indexList[0]); + if (sourceIndex.isValid()) + { + GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast(sourceIndex.internalPointer()); + + AZ::IO::Path gemPath = GetGemPath("ScriptCanvas.Editor"); + gemPath = gemPath / AZ::IO::Path("TranslationAssets"); + gemPath = gemPath / nodePaletteItem->GetTranslationDataPath(); + gemPath.ReplaceExtension(".names"); + + if (fileIO->Exists(gemPath.c_str())) + { + AzQtComponents::ShowFileOnDesktop(gemPath.c_str()); + } + } + } } void NodePaletteDockWidget::OpenTranslationData() From f684fe14ff81d329e1ae90e434710a0418a5d8f7 Mon Sep 17 00:00:00 2001 From: lsemp3d <58790905+lsemp3d@users.noreply.github.com> Date: Fri, 21 Jan 2022 09:45:27 -0800 Subject: [PATCH 36/61] Categorized Material Data under Rendering Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> --- .../Assets/TranslationAssets/Classes/MaterialData.names | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/MaterialData.names b/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/MaterialData.names index 9f6af426d0..72e48aa400 100644 --- a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/MaterialData.names +++ b/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/MaterialData.names @@ -5,7 +5,8 @@ "context": "BehaviorClass", "variant": "", "details": { - "name": "Material Data" + "name": "Material Data", + "category": "Rendering" }, "methods": [ { From 238962be739fe2dda678b92342d528e390675e7b Mon Sep 17 00:00:00 2001 From: Mikhail Naumov <82239319+AMZN-mnaumov@users.noreply.github.com> Date: Fri, 21 Jan 2022 12:02:40 -0600 Subject: [PATCH 37/61] fixing crash when opening different level during simulation mode (#6762) Signed-off-by: Mikhail Naumov --- Code/Editor/CryEdit.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 3beaf4d438..8789b31fdc 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -3363,6 +3363,10 @@ CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostR return GetIEditor()->GetDocument(); } + bool usePrefabSystemForLevels = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult( + usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled); + // If we are loading and we're in simulate mode, then switch it off before we do anything else if (GetIEditor()->GetGameEngine() && GetIEditor()->GetGameEngine()->GetSimulationMode()) { @@ -3370,6 +3374,15 @@ CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostR bool bIsDocModified = GetIEditor()->GetDocument()->IsModified(); OnSwitchPhysics(); GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified); + + if (usePrefabSystemForLevels) + { + auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get(); + if (rootSpawnableInterface) + { + rootSpawnableInterface->ProcessSpawnableQueue(); + } + } } // We're about to start loading a level, so start recording errors to display at the end. From d5f7bc7c9beeaeceac4c01ed2e9c86c943978162 Mon Sep 17 00:00:00 2001 From: lsemp3d <58790905+lsemp3d@users.noreply.github.com> Date: Fri, 21 Jan 2022 10:03:02 -0800 Subject: [PATCH 38/61] Consolidated navigation to file code Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> --- .../ScriptCanvasNodePaletteDockWidget.cpp | 43 ++++++++----------- .../ScriptCanvasNodePaletteDockWidget.h | 1 + 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp index 4b622bc025..9387b58c71 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp @@ -881,6 +881,23 @@ namespace ScriptCanvasEditor return ""; } + void NodePaletteDockWidget::NavigateToTranslationFile(GraphCanvas::NodePaletteTreeItem* nodePaletteItem) + { + if (nodePaletteItem) + { + AZ::IO::Path gemPath = GetGemPath("ScriptCanvas.Editor"); + gemPath = gemPath / AZ::IO::Path("TranslationAssets"); + gemPath = gemPath / nodePaletteItem->GetTranslationDataPath(); + gemPath.ReplaceExtension(".names"); + + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + if (fileIO && fileIO->Exists(gemPath.c_str())) + { + AzQtComponents::ShowFileOnDesktop(gemPath.c_str()); + } + } + } + void NodePaletteDockWidget::GenerateTranslation() { QModelIndexList indexList = GetTreeView()->selectionModel()->selectedRows(); @@ -897,21 +914,11 @@ namespace ScriptCanvasEditor if (indexList.size() == 1) { - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); QModelIndex sourceIndex = filterModel->mapToSource(indexList[0]); if (sourceIndex.isValid()) { GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast(sourceIndex.internalPointer()); - - AZ::IO::Path gemPath = GetGemPath("ScriptCanvas.Editor"); - gemPath = gemPath / AZ::IO::Path("TranslationAssets"); - gemPath = gemPath / nodePaletteItem->GetTranslationDataPath(); - gemPath.ReplaceExtension(".names"); - - if (fileIO->Exists(gemPath.c_str())) - { - AzQtComponents::ShowFileOnDesktop(gemPath.c_str()); - } + NavigateToTranslationFile(nodePaletteItem); } } } @@ -929,19 +936,7 @@ namespace ScriptCanvasEditor QModelIndex sourceIndex = filterModel->mapToSource(index); GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast(sourceIndex.internalPointer()); - if (nodePaletteItem) - { - AZ::IO::Path gemPath = GetGemPath("ScriptCanvas.Editor"); - gemPath = gemPath / AZ::IO::Path("TranslationAssets"); - gemPath = gemPath / nodePaletteItem->GetTranslationDataPath(); - gemPath.ReplaceExtension(".names"); - - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - if (fileIO->Exists(gemPath.c_str())) - { - AzQtComponents::ShowFileOnDesktop(gemPath.c_str()); - } - } + NavigateToTranslationFile(nodePaletteItem); } } } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h index 9f5fa0511f..3ee26e3f71 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h @@ -209,6 +209,7 @@ namespace ScriptCanvasEditor void HandleTreeItemDoubleClicked(GraphCanvas::GraphCanvasTreeItem* treeItem); void OpenTranslationData(); void GenerateTranslation(); + void NavigateToTranslationFile(GraphCanvas::NodePaletteTreeItem*); void ConfigureHelper(); void ParseCycleTargets(GraphCanvas::GraphCanvasTreeItem* treeItem); From d918991e273c982ac9d3bb0405453b1471d64d3a Mon Sep 17 00:00:00 2001 From: Mikhail Naumov <82239319+AMZN-mnaumov@users.noreply.github.com> Date: Fri, 21 Jan 2022 12:04:47 -0600 Subject: [PATCH 39/61] Allowing instantiating of prefab without selecting an entity in outliner (#6763) * Allowing instantiating of prefab without selecting an entity in outliner Signed-off-by: Mikhail Naumov * PR feedback Signed-off-by: Mikhail Naumov --- .../AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index bc65b9088b..d61efe0b6d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -341,7 +341,8 @@ namespace AzToolsFramework } // Instantiate Prefab - if (selectedEntities.size() == 1 && !readOnlyEntityInSelection && !onlySelectedEntityIsClosedPrefabContainer) + if (selectedEntities.size() == 0 || + selectedEntities.size() == 1 && !readOnlyEntityInSelection && !onlySelectedEntityIsClosedPrefabContainer) { QAction* instantiateAction = menu->addAction(QObject::tr("Instantiate Prefab...")); instantiateAction->setToolTip(QObject::tr("Instantiates a prefab file in the scene.")); From 2c33240b72f22494c4361157b7ac6a1c39aeb323 Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Fri, 21 Jan 2022 13:55:19 -0600 Subject: [PATCH 40/61] Converting Dynamic Vegetation and Gradient Signal tests to utilize prefab system (#7034) * Adding on-disk prefabs for automated tests Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Resolving import conflict with cherrypick Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Updating DynVeg tests to use open_base_level function Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Converting initial set of DynVeg tests to utilize prefab system Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Finalizing DynVeg test conversion to use prefab system Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Removing old test runners Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Removing optimized suffix from optimized test runners Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Removing unoptimized test runner Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Removing optimized suffix from optimized test runner Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> * Adding wait_for_condition on level save for E2E tests Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- AutomatedTesting/Assets/Prefabs/Bush.prefab | 129 + .../Assets/Prefabs/BushFlowerBlender.prefab | 2249 +++++++++++++++++ .../Assets/Prefabs/PurpleFlower.prefab | 129 + .../flower_pink.vegdescriptorlist | 6 +- .../hydra_editor_utils.py | 2 +- .../PythonTests/largeworlds/CMakeLists.txt | 6 +- ...rides_InstancesPlantAtSpecifiedAltitude.py | 10 +- .../AltitudeFilter_FilterStageToggle.py | 9 +- ...ample_InstancesPlantAtSpecifiedAltitude.py | 7 +- ...binedDescriptorsExpressInConfiguredArea.py | 44 +- ...tSelector_InstancesExpressBasedOnWeight.py | 12 +- ...errides_InstancesPlantAtSpecifiedRadius.py | 11 +- ...nFilter_InstancesPlantAtSpecifiedRadius.py | 11 +- .../DynVegUtils_TempPrefabCreationWorks.py | 10 +- ...nstanceSpawner_DynamicSliceSpawnerWorks.py | 3 +- .../EmptyInstanceSpawner_EmptySpawnerWorks.py | 3 +- ...anceSpawnerPriority_LayerAndSubPriority.py | 10 +- .../EditorScripts/LayerBlender_E2E_Editor.py | 31 +- ...locker_InstancesBlockedInConfiguredArea.py | 11 +- .../LayerSpawner_FilterStageToggle.py | 9 +- .../LayerSpawner_InheritBehaviorFlag.py | 25 +- ...wner_InstancesPlantInAllSupportedShapes.py | 12 +- ...tancesRefreshUsingCorrectViewportCamera.py | 15 +- .../MeshBlocker_InstancesBlockedByMesh.py | 12 +- ...cker_InstancesBlockedByMeshHeightTuning.py | 12 +- ...faceTagEmitter_DependentOnMeshComponent.py | 3 +- ...mitter_SurfaceTagsAddRemoveSuccessfully.py | 3 +- ...ysXColliderSurfaceTagEmitter_E2E_Editor.py | 9 +- ...PositionModifier_AutoSnapToSurfaceWorks.py | 10 +- ...rrides_InstancesPlantAtSpecifiedOffsets.py | 9 +- ... => PrefabInstanceSpawner_Embedded_E2E.py} | 16 +- ... => PrefabInstanceSpawner_External_E2E.py} | 7 +- ...ierOverrides_InstancesRotateWithinRange.py | 9 +- ...tionModifier_InstancesRotateWithinRange.py | 9 +- ...odifierOverrides_InstancesProperlyScale.py | 9 +- .../ScaleModifier_InstancesProperlyScale.py | 10 +- ...apeIntersectionFilter_FilterStageToggle.py | 9 +- ...ionFilter_InstancesPlantInAssignedShape.py | 10 +- ...ifierOverrides_InstanceSurfaceAlignment.py | 9 +- ...gnmentModifier_InstanceSurfaceAlignment.py | 9 +- ...AndOverrides_InstancesPlantOnValidSlope.py | 9 +- ...PrefabCreationAndVisibilityToggleWorks.py} | 79 +- .../SurfaceDataRefreshes_RemainsStable.py | 5 +- ...tipleDescriptorOverridesPlantAsExpected.py | 10 +- ...rfaceMaskFilter_BasicSurfaceTagCreation.py | 6 +- .../SurfaceMaskFilter_ExclusionList.py | 12 +- .../SurfaceMaskFilter_InclusionList.py | 12 +- .../SystemSettings_SectorPointDensity.py | 9 +- .../SystemSettings_SectorSize.py | 9 +- ...getationInstances_DespawnWhenOutOfRange.py | 10 +- .../largeworlds/dyn_veg/TestSuite_Main.py | 166 +- .../dyn_veg/TestSuite_Main_Optimized.py | 192 -- .../largeworlds/dyn_veg/TestSuite_Periodic.py | 270 +- .../dyn_veg/TestSuite_Periodic_Optimized.py | 23 - .../GradientGenerators_Incompatibilities.py | 3 +- .../GradientModifiers_Incompatibilities.py | 3 +- ...ClearingPinnedEntitySetsPreviewToOrigin.py | 3 +- ...eviewSettings_DefaultPinnedEntityIsSelf.py | 3 +- ...GradientReferencesAddRemoveSuccessfully.py | 3 +- ...SurfaceTagEmitter_ComponentDependencies.py | 3 +- ...mitter_SurfaceTagsAddRemoveSuccessfully.py | 3 +- ...ponentIncompatibleWithExpectedGradients.py | 3 +- ...sform_ComponentIncompatibleWithSpawners.py | 3 +- ..._FrequencyZoomCanBeSetBeyondSliderRange.py | 3 +- .../GradientTransform_RequiresShape.py | 3 +- ...ient_ProcessedImageAssignedSuccessfully.py | 3 +- .../ImageGradient_RequiresShape.py | 3 +- .../gradient_signal/TestSuite_Periodic.py | 60 +- .../TestSuite_Periodic_Optimized.py | 57 - .../editor_dynveg_test_helper.py | 45 +- .../instance_counter.scriptcanvas | 39 +- 71 files changed, 3047 insertions(+), 914 deletions(-) create mode 100644 AutomatedTesting/Assets/Prefabs/Bush.prefab create mode 100644 AutomatedTesting/Assets/Prefabs/BushFlowerBlender.prefab create mode 100644 AutomatedTesting/Assets/Prefabs/PurpleFlower.prefab rename AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/{DynamicSliceInstanceSpawner_Embedded_E2E.py => PrefabInstanceSpawner_Embedded_E2E.py} (87%) rename AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/{DynamicSliceInstanceSpawner_External_E2E.py => PrefabInstanceSpawner_External_E2E.py} (96%) rename AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/{SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py => SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks.py} (52%) delete mode 100644 AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py delete mode 100644 AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py delete mode 100644 AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py diff --git a/AutomatedTesting/Assets/Prefabs/Bush.prefab b/AutomatedTesting/Assets/Prefabs/Bush.prefab new file mode 100644 index 0000000000..ef497bdcd0 --- /dev/null +++ b/AutomatedTesting/Assets/Prefabs/Bush.prefab @@ -0,0 +1,129 @@ +{ + "ContainerEntity": { + "Id": "ContainerEntity", + "Name": "Bush", + "Components": { + "Component_[1140272189295067758]": { + "$type": "EditorInspectorComponent", + "Id": 1140272189295067758 + }, + "Component_[13437832196484687256]": { + "$type": "EditorOnlyEntityComponent", + "Id": 13437832196484687256 + }, + "Component_[1553903646452669645]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 1553903646452669645 + }, + "Component_[15914009348632444632]": { + "$type": "EditorEntitySortComponent", + "Id": 15914009348632444632, + "Child Entity Order": [ + "Entity_[7511491868318]" + ] + }, + "Component_[18046340308818780248]": { + "$type": "EditorPrefabComponent", + "Id": 18046340308818780248 + }, + "Component_[1948833233489872938]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 1948833233489872938, + "Parent Entity": "" + }, + "Component_[2903632350157981339]": { + "$type": "SelectionComponent", + "Id": 2903632350157981339 + }, + "Component_[48827510535192710]": { + "$type": "EditorPendingCompositionComponent", + "Id": 48827510535192710 + }, + "Component_[5609536793322429681]": { + "$type": "EditorLockComponent", + "Id": 5609536793322429681 + }, + "Component_[5859168386298620990]": { + "$type": "EditorEntityIconComponent", + "Id": 5859168386298620990 + }, + "Component_[6604616929271524505]": { + "$type": "EditorVisibilityComponent", + "Id": 6604616929271524505 + } + } + }, + "Entities": { + "Entity_[7511491868318]": { + "Id": "Entity_[7511491868318]", + "Name": "Bush", + "Components": { + "Component_[10227459330338484901]": { + "$type": "EditorInspectorComponent", + "Id": 10227459330338484901, + "ComponentOrderEntryArray": [ + { + "ComponentId": 4998941225335869157 + }, + { + "ComponentId": 9922994635792843826, + "SortIndex": 1 + } + ] + }, + "Component_[10972351222359420947]": { + "$type": "EditorOnlyEntityComponent", + "Id": 10972351222359420947 + }, + "Component_[12101122374155214392]": { + "$type": "EditorPendingCompositionComponent", + "Id": 12101122374155214392 + }, + "Component_[1535264614652988260]": { + "$type": "SelectionComponent", + "Id": 1535264614652988260 + }, + "Component_[16367811417907891218]": { + "$type": "EditorVisibilityComponent", + "Id": 16367811417907891218 + }, + "Component_[17044216787716682880]": { + "$type": "EditorEntitySortComponent", + "Id": 17044216787716682880 + }, + "Component_[2129822594969629430]": { + "$type": "EditorEntityIconComponent", + "Id": 2129822594969629430 + }, + "Component_[2838015156782745450]": { + "$type": "EditorLockComponent", + "Id": 2838015156782745450 + }, + "Component_[4998941225335869157]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 4998941225335869157, + "Parent Entity": "ContainerEntity" + }, + "Component_[8773358049076362578]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 8773358049076362578 + }, + "Component_[9922994635792843826]": { + "$type": "AZ::Render::EditorMeshComponent", + "Id": 9922994635792843826, + "Controller": { + "Configuration": { + "ModelAsset": { + "assetId": { + "guid": "{1201406D-FB20-5B5F-B9B5-6A6E8DE00A14}", + "subId": 276506120 + }, + "assetHint": "assets/objects/foliage/bush_privet_01.azmodel" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Assets/Prefabs/BushFlowerBlender.prefab b/AutomatedTesting/Assets/Prefabs/BushFlowerBlender.prefab new file mode 100644 index 0000000000..b938701b6a --- /dev/null +++ b/AutomatedTesting/Assets/Prefabs/BushFlowerBlender.prefab @@ -0,0 +1,2249 @@ +{ + "ContainerEntity": { + "Id": "ContainerEntity", + "Name": "BushFlowerBlender", + "Components": { + "Component_[10351434005293588180]": { + "$type": "EditorLockComponent", + "Id": 10351434005293588180 + }, + "Component_[10967554916960846519]": { + "$type": "EditorOnlyEntityComponent", + "Id": 10967554916960846519 + }, + "Component_[14400678265986305456]": { + "$type": "EditorEntityIconComponent", + "Id": 14400678265986305456 + }, + "Component_[15365520792759708998]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 15365520792759708998 + }, + "Component_[16281192066639198814]": { + "$type": "EditorEntitySortComponent", + "Id": 16281192066639198814, + "Child Entity Order": [ + "Entity_[263598916892318]" + ] + }, + "Component_[16356049875968660722]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 16356049875968660722, + "Parent Entity": "" + }, + "Component_[16391695178646515457]": { + "$type": "SelectionComponent", + "Id": 16391695178646515457 + }, + "Component_[16407238476365087382]": { + "$type": "EditorVisibilityComponent", + "Id": 16407238476365087382 + }, + "Component_[3542701736829700396]": { + "$type": "EditorPendingCompositionComponent", + "Id": 3542701736829700396 + }, + "Component_[5160094530340900971]": { + "$type": "EditorInspectorComponent", + "Id": 5160094530340900971 + }, + "Component_[6432428663776737136]": { + "$type": "EditorPrefabComponent", + "Id": 6432428663776737136 + } + } + }, + "Entities": { + "Entity_[263573147088542]": { + "Id": "Entity_[263573147088542]", + "Name": "BushSpawner", + "Components": { + "Component_[11886309121583108836]": { + "$type": "EditorPositionModifierComponent", + "Id": 11886309121583108836, + "Configuration": { + "GradientX": { + "GradientId": "Entity_[263590326957726]" + }, + "GradientY": { + "GradientId": "Entity_[263590326957726]" + }, + "GradientZ": { + "GradientId": "" + } + } + }, + "Component_[1240444912193942742]": { + "$type": "{DD96FD51-A86B-48BC-A6AB-89183B538269} EditorSpawnerComponent", + "Id": 1240444912193942742, + "PreviewEntity": "Entity_[263573147088542]" + }, + "Component_[12813702285187817973]": { + "$type": "EditorInspectorComponent", + "Id": 12813702285187817973, + "ComponentOrderEntryArray": [ + { + "ComponentId": 18133156195370267728 + }, + { + "ComponentId": 13450250419263773680, + "SortIndex": 1 + }, + { + "ComponentId": 8841963751527151666, + "SortIndex": 2 + }, + { + "ComponentId": 1240444912193942742, + "SortIndex": 3 + }, + { + "ComponentId": 17991239182321715249, + "SortIndex": 4 + }, + { + "ComponentId": 14175761523159824993, + "SortIndex": 5 + }, + { + "ComponentId": 2884829698060689565, + "SortIndex": 6 + }, + { + "ComponentId": 11886309121583108836, + "SortIndex": 7 + } + ] + }, + "Component_[13179606361687972691]": { + "$type": "EditorEntityIconComponent", + "Id": 13179606361687972691 + }, + "Component_[13450250419263773680]": { + "$type": "EditorReferenceShapeComponent", + "Id": 13450250419263773680, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[13688014277574607552]": { + "$type": "EditorOnlyEntityComponent", + "Id": 13688014277574607552 + }, + "Component_[14175761523159824993]": { + "$type": "EditorDistributionFilterComponent", + "Id": 14175761523159824993, + "Configuration": { + "ThresholdMin": 0.289000004529953, + "ThresholdMax": 0.5690000057220459, + "Gradient": { + "GradientId": "Entity_[263603211859614]" + } + } + }, + "Component_[14232774271081092639]": { + "$type": "EditorLockComponent", + "Id": 14232774271081092639 + }, + "Component_[14811533745267383811]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 14811533745267383811 + }, + "Component_[17540078415915694273]": { + "$type": "EditorVisibilityComponent", + "Id": 17540078415915694273 + }, + "Component_[17991239182321715249]": { + "$type": "EditorRotationModifierComponent", + "Id": 17991239182321715249, + "Configuration": { + "GradientX": { + "GradientId": "" + }, + "GradientY": { + "GradientId": "" + }, + "GradientZ": { + "GradientId": "Entity_[263590326957726]" + } + } + }, + "Component_[18133156195370267728]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 18133156195370267728, + "Parent Entity": "Entity_[263607506826910]" + }, + "Component_[2884829698060689565]": { + "$type": "EditorScaleModifierComponent", + "Id": 2884829698060689565, + "Configuration": { + "RangeMin": 0.75, + "RangeMax": 1.25, + "Gradient": { + "GradientId": "Entity_[263590326957726]" + } + } + }, + "Component_[50628676751372416]": { + "$type": "EditorEntitySortComponent", + "Id": 50628676751372416, + "Child Entity Order": [ + "Entity_[263603211859614]", + "Entity_[263590326957726]" + ] + }, + "Component_[5689270312836823309]": { + "$type": "SelectionComponent", + "Id": 5689270312836823309 + }, + "Component_[7105803915999711758]": { + "$type": "EditorPendingCompositionComponent", + "Id": 7105803915999711758 + }, + "Component_[8841963751527151666]": { + "$type": "EditorDescriptorListComponent", + "Id": 8841963751527151666, + "Configuration": { + "Descriptors": [ + { + "SpawnerType": "{74BEEDB5-81CF-409F-B375-0D93D81EF2E3}", + "InstanceSpawner": { + "$type": "PrefabInstanceSpawner", + "SpawnableAsset": { + "assetId": { + "guid": "{EE51E73C-D753-54AC-A1C0-4F29844FB6C3}", + "subId": 2740536329 + }, + "assetHint": "assets/prefabs/bush.spawnable" + } + } + } + ] + } + } + } + }, + "Entity_[263577442055838]": { + "Id": "Entity_[263577442055838]", + "Name": "RandomNoise", + "Components": { + "Component_[13412023990029767074]": { + "$type": "EditorRandomGradientComponent", + "Id": 13412023990029767074, + "PreviewEntity": "Entity_[263577442055838]" + }, + "Component_[14048059127502350032]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 14048059127502350032, + "Parent Entity": "Entity_[263581737023134]" + }, + "Component_[15585968636846328006]": { + "$type": "EditorEntitySortComponent", + "Id": 15585968636846328006 + }, + "Component_[17356242264256499333]": { + "$type": "EditorOnlyEntityComponent", + "Id": 17356242264256499333 + }, + "Component_[2157320692130316849]": { + "$type": "EditorLockComponent", + "Id": 2157320692130316849 + }, + "Component_[2167582872687341084]": { + "$type": "EditorReferenceShapeComponent", + "Id": 2167582872687341084, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[2794166072748175035]": { + "$type": "SelectionComponent", + "Id": 2794166072748175035 + }, + "Component_[4784966656524567789]": { + "$type": "EditorVisibilityComponent", + "Id": 4784966656524567789 + }, + "Component_[6185400249317227261]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 6185400249317227261 + }, + "Component_[639949486729001685]": { + "$type": "EditorGradientTransformComponent", + "Id": 639949486729001685, + "Configuration": { + "ShapeReference": "", + "Bounds": [ + 50.0, + 50.0, + 1.0 + ] + } + }, + "Component_[7347819195697153472]": { + "$type": "EditorEntityIconComponent", + "Id": 7347819195697153472 + }, + "Component_[9225409585951117678]": { + "$type": "EditorInspectorComponent", + "Id": 9225409585951117678, + "ComponentOrderEntryArray": [ + { + "ComponentId": 14048059127502350032 + }, + { + "ComponentId": 2167582872687341084, + "SortIndex": 1 + }, + { + "ComponentId": 639949486729001685, + "SortIndex": 2 + }, + { + "ComponentId": 13412023990029767074, + "SortIndex": 3 + } + ] + }, + "Component_[9667056664436803603]": { + "$type": "EditorPendingCompositionComponent", + "Id": 9667056664436803603 + } + } + }, + "Entity_[263581737023134]": { + "Id": "Entity_[263581737023134]", + "Name": "FlowerSpawner", + "Components": { + "Component_[10064436441176637741]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 10064436441176637741 + }, + "Component_[10418076448308581948]": { + "$type": "EditorRotationModifierComponent", + "Id": 10418076448308581948, + "Configuration": { + "GradientX": { + "GradientId": "" + }, + "GradientY": { + "GradientId": "" + }, + "GradientZ": { + "GradientId": "Entity_[263577442055838]" + } + } + }, + "Component_[12484622175138429315]": { + "$type": "EditorOnlyEntityComponent", + "Id": 12484622175138429315 + }, + "Component_[12590590072231203407]": { + "$type": "EditorLockComponent", + "Id": 12590590072231203407 + }, + "Component_[13079591667184987458]": { + "$type": "EditorScaleModifierComponent", + "Id": 13079591667184987458, + "Configuration": { + "RangeMin": 0.75, + "RangeMax": 1.25, + "Gradient": { + "GradientId": "Entity_[263577442055838]" + } + } + }, + "Component_[13174773394274524631]": { + "$type": "EditorInspectorComponent", + "Id": 13174773394274524631, + "ComponentOrderEntryArray": [ + { + "ComponentId": 1915862229814997128 + }, + { + "ComponentId": 582800388831726963, + "SortIndex": 1 + }, + { + "ComponentId": 18006385801184712266, + "SortIndex": 2 + }, + { + "ComponentId": 6902499453501456890, + "SortIndex": 3 + }, + { + "ComponentId": 10418076448308581948, + "SortIndex": 4 + }, + { + "ComponentId": 13983302912005698250, + "SortIndex": 5 + }, + { + "ComponentId": 13079591667184987458, + "SortIndex": 6 + }, + { + "ComponentId": 7912787520673119502, + "SortIndex": 7 + } + ] + }, + "Component_[13200009872392504010]": { + "$type": "EditorEntitySortComponent", + "Id": 13200009872392504010, + "Child Entity Order": [ + "Entity_[263594621925022]", + "Entity_[263577442055838]" + ] + }, + "Component_[13983302912005698250]": { + "$type": "EditorDistributionFilterComponent", + "Id": 13983302912005698250, + "Configuration": { + "ThresholdMin": 0.2879999876022339, + "ThresholdMax": 0.5680000185966492, + "Gradient": { + "GradientId": "Entity_[263586031990430]" + } + } + }, + "Component_[14726174657215669218]": { + "$type": "EditorPendingCompositionComponent", + "Id": 14726174657215669218 + }, + "Component_[18006385801184712266]": { + "$type": "EditorDescriptorListComponent", + "Id": 18006385801184712266, + "Configuration": { + "Descriptors": [ + { + "SpawnerType": "{74BEEDB5-81CF-409F-B375-0D93D81EF2E3}", + "InstanceSpawner": { + "$type": "PrefabInstanceSpawner", + "SpawnableAsset": { + "assetId": { + "guid": "{80C0CF4E-9A5E-544B-B89E-BC980175A259}", + "subId": 3875079122 + }, + "assetHint": "assets/prefabs/pinkflower.spawnable" + } + } + }, + { + "SpawnerType": "{74BEEDB5-81CF-409F-B375-0D93D81EF2E3}", + "InstanceSpawner": { + "$type": "PrefabInstanceSpawner", + "SpawnableAsset": { + "assetId": { + "guid": "{20DD1202-A434-5482-9FB9-AED4C78F6CBF}", + "subId": 2669863854 + }, + "assetHint": "assets/prefabs/purpleflower.spawnable" + } + } + }, + { + "SpawnerType": "{23C40FD4-A55F-4BD3-BE5B-DC5423F217C2}", + "InstanceSpawner": { + "$type": "EmptyInstanceSpawner" + } + } + ] + } + }, + "Component_[1915862229814997128]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 1915862229814997128, + "Parent Entity": "Entity_[263607506826910]" + }, + "Component_[2123416208548010747]": { + "$type": "SelectionComponent", + "Id": 2123416208548010747 + }, + "Component_[4172073657954183661]": { + "$type": "EditorVisibilityComponent", + "Id": 4172073657954183661 + }, + "Component_[4297556504463195307]": { + "$type": "EditorEntityIconComponent", + "Id": 4297556504463195307 + }, + "Component_[582800388831726963]": { + "$type": "EditorReferenceShapeComponent", + "Id": 582800388831726963, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[6902499453501456890]": { + "$type": "{DD96FD51-A86B-48BC-A6AB-89183B538269} EditorSpawnerComponent", + "Id": 6902499453501456890, + "PreviewEntity": "Entity_[263581737023134]" + }, + "Component_[7912787520673119502]": { + "$type": "EditorPositionModifierComponent", + "Id": 7912787520673119502, + "Configuration": { + "GradientX": { + "GradientId": "Entity_[263577442055838]" + }, + "GradientY": { + "GradientId": "Entity_[263577442055838]" + }, + "GradientZ": { + "GradientId": "" + } + } + } + } + }, + "Entity_[263586031990430]": { + "Id": "Entity_[263586031990430]", + "Name": "Invert", + "Components": { + "Component_[11123278080744920525]": { + "$type": "EditorEntitySortComponent", + "Id": 11123278080744920525 + }, + "Component_[1199715693160400161]": { + "$type": "EditorInspectorComponent", + "Id": 1199715693160400161, + "ComponentOrderEntryArray": [ + { + "ComponentId": 7438979211266329827 + }, + { + "ComponentId": 6027557066164356793, + "SortIndex": 1 + }, + { + "ComponentId": 8903771095804438921, + "SortIndex": 2 + } + ] + }, + "Component_[12162816428693886169]": { + "$type": "EditorEntityIconComponent", + "Id": 12162816428693886169 + }, + "Component_[14493180089799969074]": { + "$type": "EditorVisibilityComponent", + "Id": 14493180089799969074 + }, + "Component_[16206759293595766479]": { + "$type": "EditorPendingCompositionComponent", + "Id": 16206759293595766479 + }, + "Component_[17566144032052847628]": { + "$type": "SelectionComponent", + "Id": 17566144032052847628 + }, + "Component_[17784057508127919200]": { + "$type": "EditorLockComponent", + "Id": 17784057508127919200 + }, + "Component_[18377773171420051348]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 18377773171420051348 + }, + "Component_[6027557066164356793]": { + "$type": "EditorReferenceShapeComponent", + "Id": 6027557066164356793, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[7438979211266329827]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 7438979211266329827, + "Parent Entity": "Entity_[263594621925022]" + }, + "Component_[8421650081106982664]": { + "$type": "EditorOnlyEntityComponent", + "Id": 8421650081106982664 + }, + "Component_[8903771095804438921]": { + "$type": "EditorInvertGradientComponent", + "Id": 8903771095804438921, + "Configuration": { + "Gradient": { + "GradientId": "Entity_[263594621925022]" + } + }, + "PreviewEntity": "Entity_[263586031990430]" + } + } + }, + "Entity_[263590326957726]": { + "Id": "Entity_[263590326957726]", + "Name": "RandomNoise", + "Components": { + "Component_[10420548607186596407]": { + "$type": "SelectionComponent", + "Id": 10420548607186596407 + }, + "Component_[11351507264577024072]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 11351507264577024072, + "Parent Entity": "Entity_[263573147088542]" + }, + "Component_[11576941520191560445]": { + "$type": "EditorEntityIconComponent", + "Id": 11576941520191560445 + }, + "Component_[11787535428542045683]": { + "$type": "EditorPendingCompositionComponent", + "Id": 11787535428542045683 + }, + "Component_[11946132649485953311]": { + "$type": "EditorEntitySortComponent", + "Id": 11946132649485953311 + }, + "Component_[11970417753234451570]": { + "$type": "EditorOnlyEntityComponent", + "Id": 11970417753234451570 + }, + "Component_[13743879269876214143]": { + "$type": "EditorVisibilityComponent", + "Id": 13743879269876214143 + }, + "Component_[15614946697365576378]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 15614946697365576378 + }, + "Component_[17045174842161759045]": { + "$type": "EditorLockComponent", + "Id": 17045174842161759045 + }, + "Component_[3107716031616536910]": { + "$type": "EditorInspectorComponent", + "Id": 3107716031616536910, + "ComponentOrderEntryArray": [ + { + "ComponentId": 11351507264577024072 + }, + { + "ComponentId": 6316453818295975755, + "SortIndex": 1 + }, + { + "ComponentId": 4846745402056100407, + "SortIndex": 2 + }, + { + "ComponentId": 8967153379180885152, + "SortIndex": 3 + } + ] + }, + "Component_[4846745402056100407]": { + "$type": "EditorGradientTransformComponent", + "Id": 4846745402056100407, + "Configuration": { + "ShapeReference": "", + "Bounds": [ + 50.0, + 50.0, + 1.0 + ] + } + }, + "Component_[6316453818295975755]": { + "$type": "EditorReferenceShapeComponent", + "Id": 6316453818295975755, + "Configuration": { + "ShapeEntityId": "Entity_[263573147088542]" + } + }, + "Component_[8967153379180885152]": { + "$type": "EditorRandomGradientComponent", + "Id": 8967153379180885152, + "PreviewEntity": "Entity_[263590326957726]" + } + } + }, + "Entity_[263594621925022]": { + "Id": "Entity_[263594621925022]", + "Name": "PerlinNoise", + "Components": { + "Component_[15798567753940523121]": { + "$type": "EditorPerlinGradientComponent", + "Id": 15798567753940523121, + "Configuration": { + "randomSeed": 33, + "frequency": 0.25 + }, + "PreviewEntity": "Entity_[263594621925022]" + }, + "Component_[16035848776632936419]": { + "$type": "EditorPendingCompositionComponent", + "Id": 16035848776632936419 + }, + "Component_[16434108759160050253]": { + "$type": "EditorVisibilityComponent", + "Id": 16434108759160050253 + }, + "Component_[16805003046507210760]": { + "$type": "EditorReferenceShapeComponent", + "Id": 16805003046507210760, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[17679463569742913027]": { + "$type": "EditorOnlyEntityComponent", + "Id": 17679463569742913027 + }, + "Component_[18285870860955961523]": { + "$type": "SelectionComponent", + "Id": 18285870860955961523 + }, + "Component_[2330427084045350198]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 2330427084045350198 + }, + "Component_[5921906930080711994]": { + "$type": "EditorEntitySortComponent", + "Id": 5921906930080711994, + "Child Entity Order": [ + "Entity_[263586031990430]" + ] + }, + "Component_[764117453152842781]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 764117453152842781, + "Parent Entity": "Entity_[263581737023134]" + }, + "Component_[7701931249281891300]": { + "$type": "EditorEntityIconComponent", + "Id": 7701931249281891300 + }, + "Component_[7967070680905543315]": { + "$type": "EditorInspectorComponent", + "Id": 7967070680905543315, + "ComponentOrderEntryArray": [ + { + "ComponentId": 764117453152842781 + }, + { + "ComponentId": 16805003046507210760, + "SortIndex": 1 + }, + { + "ComponentId": 8029154041194694200, + "SortIndex": 2 + }, + { + "ComponentId": 15798567753940523121, + "SortIndex": 3 + } + ] + }, + "Component_[8029154041194694200]": { + "$type": "EditorGradientTransformComponent", + "Id": 8029154041194694200, + "Configuration": { + "ShapeReference": "", + "Bounds": [ + 50.0, + 50.0, + 1.0 + ] + } + }, + "Component_[8660028910391135744]": { + "$type": "EditorLockComponent", + "Id": 8660028910391135744 + } + } + }, + "Entity_[263598916892318]": { + "Id": "Entity_[263598916892318]", + "Name": "LandscapeCanvas", + "Components": { + "Component_[12967746135199800648]": { + "$type": "EditorVisibilityComponent", + "Id": 12967746135199800648 + }, + "Component_[13081062505162225823]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 13081062505162225823 + }, + "Component_[16021195931295059075]": { + "$type": "EditorPendingCompositionComponent", + "Id": 16021195931295059075 + }, + "Component_[16134261272053586014]": { + "$type": "EditorEntityIconComponent", + "Id": 16134261272053586014 + }, + "Component_[17500735767968801510]": { + "$type": "EditorLandscapeCanvasComponent", + "Id": 17500735767968801510, + "Graph": { + "m_nodes": [ + { + "Key": 1, + "Value": { + "$type": "CylinderShapeNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "BushFlowerBlender" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263607506826910]", + "m_componentId": 6683290476113150719 + } + }, + { + "Key": 2, + "Value": { + "$type": "RandomNoiseGradientNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "RandomNoise" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263590326957726]", + "m_componentId": 8967153379180885152 + } + }, + { + "Key": 3, + "Value": { + "$type": "SpawnerAreaNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "BushSpawner" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PlacementBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 1240444912193942742 + } + }, + { + "Key": 4, + "Value": { + "$type": "PerlinNoiseGradientNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "PerlinNoise" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263603211859614]", + "m_componentId": 10160640699095155981 + } + }, + { + "Key": 5, + "Value": { + "$type": "RandomNoiseGradientNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "RandomNoise" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263577442055838]", + "m_componentId": 13412023990029767074 + } + }, + { + "Key": 6, + "Value": { + "$type": "SpawnerAreaNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "FlowerSpawner" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PlacementBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 6902499453501456890 + } + }, + { + "Key": 7, + "Value": { + "$type": "AreaBlenderNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "BushFlowerBlender" + } + } + } + ], + "m_extendableSlots": { + "InboundArea": [ + { + "m_value": { + "$type": "EntityId", + "Value": "" + } + }, + { + "m_value": { + "$type": "EntityId", + "Value": "" + }, + "m_subId": 1 + } + ] + }, + "m_vegetationEntityId": "Entity_[263607506826910]", + "m_componentId": 7229206794941734410 + } + }, + { + "Key": 8, + "Value": { + "$type": "PerlinNoiseGradientNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "PerlinNoise" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263594621925022]", + "m_componentId": 15798567753940523121 + } + }, + { + "Key": 9, + "Value": { + "$type": "InvertGradientModifierNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "Invert" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263586031990430]", + "m_componentId": 8903771095804438921 + } + }, + { + "Key": 10, + "Value": { + "$type": "RotationModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradientX" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientY" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientZ" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 17991239182321715249 + } + }, + { + "Key": 11, + "Value": { + "$type": "DistributionFilterNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 14175761523159824993 + } + }, + { + "Key": 12, + "Value": { + "$type": "ScaleModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 2884829698060689565 + } + }, + { + "Key": 13, + "Value": { + "$type": "PositionModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradientX" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientY" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientZ" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 11886309121583108836 + } + }, + { + "Key": 14, + "Value": { + "$type": "RotationModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradientX" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientY" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientZ" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 10418076448308581948 + } + }, + { + "Key": 15, + "Value": { + "$type": "DistributionFilterNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 13983302912005698250 + } + }, + { + "Key": 16, + "Value": { + "$type": "ScaleModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 13079591667184987458 + } + }, + { + "Key": 17, + "Value": { + "$type": "PositionModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradientX" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientY" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientZ" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 7912787520673119502 + } + } + ], + "m_connections": [ + { + "m_sourceEndpoint": [ + 8, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 9, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 3, + { + "m_name": "OutboundArea" + } + ], + "m_targetEndpoint": [ + 7, + { + "m_name": "InboundArea" + } + ] + }, + { + "m_sourceEndpoint": [ + 6, + { + "m_name": "OutboundArea" + } + ], + "m_targetEndpoint": [ + 7, + { + "m_name": "InboundArea", + "m_subId": 1 + } + ] + }, + { + "m_sourceEndpoint": [ + 1, + { + "m_name": "Bounds" + } + ], + "m_targetEndpoint": [ + 3, + { + "m_name": "PlacementBounds" + } + ] + }, + { + "m_sourceEndpoint": [ + 1, + { + "m_name": "Bounds" + } + ], + "m_targetEndpoint": [ + 6, + { + "m_name": "PlacementBounds" + } + ] + }, + { + "m_sourceEndpoint": [ + 2, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 10, + { + "m_name": "InboundGradientZ" + } + ] + }, + { + "m_sourceEndpoint": [ + 4, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 11, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 2, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 12, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 2, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 13, + { + "m_name": "InboundGradientX" + } + ] + }, + { + "m_sourceEndpoint": [ + 2, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 13, + { + "m_name": "InboundGradientY" + } + ] + }, + { + "m_sourceEndpoint": [ + 5, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 14, + { + "m_name": "InboundGradientZ" + } + ] + }, + { + "m_sourceEndpoint": [ + 9, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 15, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 5, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 16, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 5, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 17, + { + "m_name": "InboundGradientX" + } + ] + }, + { + "m_sourceEndpoint": [ + 5, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 17, + { + "m_name": "InboundGradientY" + } + ] + } + ], + "m_uiMetadata": { + "m_sceneMetadata": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData", + "ViewParams": { + "Scale": 0.3771495156249999, + "AnchorX": -498.4760437011719, + "AnchorY": 808.6978149414063 + } + } + } + }, + "m_nodeMetadata": [ + { + "Key": 2, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "GradientNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 100.0, + 580.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{2D583FA2-5659-491F-8914-6302420E3F0B}" + } + } + } + }, + { + "Key": 3, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 480.0, + 600.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{A26480C4-A80B-4B36-ADE1-918E4541EA2B}" + } + } + } + }, + { + "Key": 5, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "GradientNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 100.0, + 1540.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{6CE9EA8B-85E7-4E17-B1D6-CA97D9D65276}" + } + } + } + }, + { + "Key": 6, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 480.0, + 1560.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{8FABE49F-1D73-46FE-8616-A982EEE46DDD}" + } + } + } + }, + { + "Key": 7, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 840.0, + 1580.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{C5EEBC1D-B91B-4852-A41D-425C6FED3FB9}" + } + } + } + }, + { + "Key": 9, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "GradientModifierNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 480.0, + 2060.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{5EB76670-0CBE-4F2A-9AE7-F65709D0BE92}" + } + } + } + }, + { + "Key": 10, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 147.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{5CB69A73-F7BA-4F76-AB86-F343052C3D7D}" + } + } + } + }, + { + "Key": 11, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 367.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{235DEBEC-E3F8-43C1-BA7E-41475B834D70}" + } + } + } + }, + { + "Key": 12, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 288.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{9E56E7B6-2ECE-42F9-9A23-FF7D192209D2}" + } + } + } + }, + { + "Key": 13, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 6.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{61759FE6-E6F7-4F4B-819B-FBFE125D7389}" + } + } + } + }, + { + "Key": 14, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 147.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{0AC1C6C8-7CC1-4068-A87D-F4B92AE8FCAF}" + } + } + } + }, + { + "Key": 15, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 367.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{5D698B90-830F-46FE-ADD8-6164EA098C3E}" + } + } + } + }, + { + "Key": 16, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 288.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{9DE6DE4A-2E6F-44BA-B292-3CD2215C1D5B}" + } + } + } + }, + { + "Key": 17, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 6.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{57FA822D-629F-4949-A8D7-D675544EBC0A}" + } + } + } + } + ] + }, + "m_nodeWrappings": [ + { + "Key": 10, + "Value": [ + 3, + 1 + ] + }, + { + "Key": 11, + "Value": [ + 3, + 6 + ] + }, + { + "Key": 12, + "Value": [ + 3, + 2 + ] + }, + { + "Key": 13, + "Value": [ + 3, + {} + ] + }, + { + "Key": 14, + "Value": [ + 6, + 1 + ] + }, + { + "Key": 15, + "Value": [ + 6, + 6 + ] + }, + { + "Key": 16, + "Value": [ + 6, + 2 + ] + }, + { + "Key": 17, + "Value": [ + 6, + {} + ] + } + ] + } + }, + "Component_[17574651838531525263]": { + "$type": "EditorEntitySortComponent", + "Id": 17574651838531525263, + "Child Entity Order": [ + "Entity_[263607506826910]" + ] + }, + "Component_[3086205760978540160]": { + "$type": "EditorLockComponent", + "Id": 3086205760978540160 + }, + "Component_[3102230753208284750]": { + "$type": "EditorInspectorComponent", + "Id": 3102230753208284750, + "ComponentOrderEntryArray": [ + { + "ComponentId": 5796173638130327223 + }, + { + "ComponentId": 17500735767968801510, + "SortIndex": 1 + } + ] + }, + "Component_[4695341524212000585]": { + "$type": "EditorOnlyEntityComponent", + "Id": 4695341524212000585 + }, + "Component_[5796173638130327223]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 5796173638130327223, + "Parent Entity": "ContainerEntity" + }, + "Component_[8278362871021770878]": { + "$type": "SelectionComponent", + "Id": 8278362871021770878 + } + } + }, + "Entity_[263603211859614]": { + "Id": "Entity_[263603211859614]", + "Name": "PerlinNoise", + "Components": { + "Component_[10006546776757889020]": { + "$type": "EditorVisibilityComponent", + "Id": 10006546776757889020 + }, + "Component_[10160640699095155981]": { + "$type": "EditorPerlinGradientComponent", + "Id": 10160640699095155981, + "Configuration": { + "randomSeed": 33, + "frequency": 0.25 + }, + "PreviewEntity": "Entity_[263603211859614]" + }, + "Component_[11506183926478566822]": { + "$type": "EditorEntitySortComponent", + "Id": 11506183926478566822 + }, + "Component_[13160180411150366392]": { + "$type": "EditorLockComponent", + "Id": 13160180411150366392 + }, + "Component_[15083697380300161163]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 15083697380300161163 + }, + "Component_[15404456223257061245]": { + "$type": "EditorInspectorComponent", + "Id": 15404456223257061245, + "ComponentOrderEntryArray": [ + { + "ComponentId": 6484803030406851652 + }, + { + "ComponentId": 4912800286535654244, + "SortIndex": 1 + }, + { + "ComponentId": 2395166459126906105, + "SortIndex": 2 + }, + { + "ComponentId": 10160640699095155981, + "SortIndex": 3 + } + ] + }, + "Component_[2395166459126906105]": { + "$type": "EditorGradientTransformComponent", + "Id": 2395166459126906105, + "Configuration": { + "ShapeReference": "", + "Bounds": [ + 50.0, + 50.0, + 1.0 + ] + } + }, + "Component_[4417011400526470160]": { + "$type": "EditorEntityIconComponent", + "Id": 4417011400526470160 + }, + "Component_[4601061670472962302]": { + "$type": "EditorPendingCompositionComponent", + "Id": 4601061670472962302 + }, + "Component_[4912800286535654244]": { + "$type": "EditorReferenceShapeComponent", + "Id": 4912800286535654244, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[6233982345083723563]": { + "$type": "SelectionComponent", + "Id": 6233982345083723563 + }, + "Component_[6484803030406851652]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 6484803030406851652, + "Parent Entity": "Entity_[263573147088542]" + }, + "Component_[6813813525871080299]": { + "$type": "EditorOnlyEntityComponent", + "Id": 6813813525871080299 + } + } + }, + "Entity_[263607506826910]": { + "Id": "Entity_[263607506826910]", + "Name": "BushFlowerBlender", + "Components": { + "Component_[13725600808872754587]": { + "$type": "EditorEntityIconComponent", + "Id": 13725600808872754587 + }, + "Component_[15959807606564505710]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 15959807606564505710, + "Parent Entity": "Entity_[263598916892318]" + }, + "Component_[1596826146661515925]": { + "$type": "EditorPendingCompositionComponent", + "Id": 1596826146661515925 + }, + "Component_[4525591661239956058]": { + "$type": "EditorVisibilityComponent", + "Id": 4525591661239956058 + }, + "Component_[6683290476113150719]": { + "$type": "EditorCylinderShapeComponent", + "Id": 6683290476113150719, + "CylinderShape": { + "Configuration": { + "Radius": 25.0 + } + } + }, + "Component_[6879394931668195375]": { + "$type": "EditorEntitySortComponent", + "Id": 6879394931668195375, + "Child Entity Order": [ + "Entity_[263573147088542]", + "Entity_[263581737023134]" + ] + }, + "Component_[7229206794941734410]": { + "$type": "EditorAreaBlenderComponent", + "Id": 7229206794941734410, + "Configuration": { + "Operations": [ + "Entity_[263573147088542]", + "Entity_[263581737023134]" + ] + }, + "PreviewEntity": "Entity_[263607506826910]" + }, + "Component_[8772711204845372843]": { + "$type": "EditorLockComponent", + "Id": 8772711204845372843 + }, + "Component_[8959345022109725835]": { + "$type": "SelectionComponent", + "Id": 8959345022109725835 + }, + "Component_[9094488642973843583]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 9094488642973843583 + }, + "Component_[9112960617967513749]": { + "$type": "EditorInspectorComponent", + "Id": 9112960617967513749, + "ComponentOrderEntryArray": [ + { + "ComponentId": 15959807606564505710 + }, + { + "ComponentId": 7229206794941734410, + "SortIndex": 1 + }, + { + "ComponentId": 6683290476113150719, + "SortIndex": 2 + } + ] + }, + "Component_[9541665258597207652]": { + "$type": "EditorOnlyEntityComponent", + "Id": 9541665258597207652 + } + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Assets/Prefabs/PurpleFlower.prefab b/AutomatedTesting/Assets/Prefabs/PurpleFlower.prefab new file mode 100644 index 0000000000..a8716ec099 --- /dev/null +++ b/AutomatedTesting/Assets/Prefabs/PurpleFlower.prefab @@ -0,0 +1,129 @@ +{ + "ContainerEntity": { + "Id": "ContainerEntity", + "Name": "PurpleFlower", + "Components": { + "Component_[10519928302743666073]": { + "$type": "EditorPrefabComponent", + "Id": 10519928302743666073 + }, + "Component_[13894087802180240181]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 13894087802180240181, + "Parent Entity": "" + }, + "Component_[15788541052719571801]": { + "$type": "EditorEntityIconComponent", + "Id": 15788541052719571801 + }, + "Component_[15842981265136092481]": { + "$type": "SelectionComponent", + "Id": 15842981265136092481 + }, + "Component_[16360384897559021149]": { + "$type": "EditorInspectorComponent", + "Id": 16360384897559021149 + }, + "Component_[16713545675046303279]": { + "$type": "EditorVisibilityComponent", + "Id": 16713545675046303279 + }, + "Component_[1806734194268113785]": { + "$type": "EditorPendingCompositionComponent", + "Id": 1806734194268113785 + }, + "Component_[5392020700593853313]": { + "$type": "EditorEntitySortComponent", + "Id": 5392020700593853313, + "Child Entity Order": [ + "Entity_[14335611090324]" + ] + }, + "Component_[5995854518752659458]": { + "$type": "EditorLockComponent", + "Id": 5995854518752659458 + }, + "Component_[6963022284400845376]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 6963022284400845376 + }, + "Component_[8055275578170091546]": { + "$type": "EditorOnlyEntityComponent", + "Id": 8055275578170091546 + } + } + }, + "Entities": { + "Entity_[14335611090324]": { + "Id": "Entity_[14335611090324]", + "Name": "PurpleFlower", + "Components": { + "Component_[10887353073528055802]": { + "$type": "EditorPendingCompositionComponent", + "Id": 10887353073528055802 + }, + "Component_[12641127425852859189]": { + "$type": "AZ::Render::EditorMeshComponent", + "Id": 12641127425852859189, + "Controller": { + "Configuration": { + "ModelAsset": { + "assetId": { + "guid": "{D493A670-6D82-5AE9-A2C8-A2EB02684F71}", + "subId": 284799939 + }, + "assetHint": "assets/objects/foliage/grass_flower_purple.azmodel" + } + } + } + }, + "Component_[14406733303466080015]": { + "$type": "EditorInspectorComponent", + "Id": 14406733303466080015, + "ComponentOrderEntryArray": [ + { + "ComponentId": 9231452352781000222 + }, + { + "ComponentId": 12641127425852859189, + "SortIndex": 1 + } + ] + }, + "Component_[1452384341905923012]": { + "$type": "EditorLockComponent", + "Id": 1452384341905923012 + }, + "Component_[2215454016415585892]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 2215454016415585892 + }, + "Component_[4104108067383423623]": { + "$type": "EditorVisibilityComponent", + "Id": 4104108067383423623 + }, + "Component_[4197335450471807917]": { + "$type": "SelectionComponent", + "Id": 4197335450471807917 + }, + "Component_[6877680739064997650]": { + "$type": "EditorOnlyEntityComponent", + "Id": 6877680739064997650 + }, + "Component_[7372550507186490390]": { + "$type": "EditorEntityIconComponent", + "Id": 7372550507186490390 + }, + "Component_[7673532337364366244]": { + "$type": "EditorEntitySortComponent", + "Id": 7673532337364366244 + }, + "Component_[9231452352781000222]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 9231452352781000222, + "Parent Entity": "ContainerEntity" + } + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Assets/VegDescriptorLists/flower_pink.vegdescriptorlist b/AutomatedTesting/Assets/VegDescriptorLists/flower_pink.vegdescriptorlist index 372cafbdce..9120d992c7 100644 --- a/AutomatedTesting/Assets/VegDescriptorLists/flower_pink.vegdescriptorlist +++ b/AutomatedTesting/Assets/VegDescriptorLists/flower_pink.vegdescriptorlist @@ -2,11 +2,11 @@ - + - + - + diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py index 36fc6003f7..ac74c611a2 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py @@ -5,9 +5,9 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ +import collections.abc from typing import List from math import isclose -import collections.abc import azlmbr.bus as bus import azlmbr.editor as editor diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt index ba607ede32..2e3e72883c 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt @@ -14,7 +14,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ NAME AutomatedTesting::DynamicVegetationTests_Main_Optimized TEST_SERIAL TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Main_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Main.py RUNTIME_DEPENDENCIES AZ::AssetProcessor Legacy::Editor @@ -27,7 +27,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ NAME AutomatedTesting::DynamicVegetationTests_Periodic_Optimized TEST_SERIAL TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Periodic_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Periodic.py RUNTIME_DEPENDENCIES AZ::AssetProcessor Legacy::Editor @@ -58,7 +58,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ NAME AutomatedTesting::GradientSignalTests_Periodic_Optimized TEST_SERIAL TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/TestSuite_Periodic_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/TestSuite_Periodic.py RUNTIME_DEPENDENCIES AZ::AssetProcessor Legacy::Editor diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py index 66aa6d27ad..aa5e5e4f9c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py @@ -51,22 +51,18 @@ def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(): import os - import azlmbr.asset as asset import azlmbr.editor as editor import azlmbr.legacy.general as general import azlmbr.bus as bus import azlmbr.math as math - import azlmbr.prefab as prefab import editor_python_test_tools.hydra_editor_utils as hydra - from editor_python_test_tools.prefab_utils import Prefab from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -76,9 +72,9 @@ def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(): center_point = math.Vector3(512.0, 512.0, 32.0) flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") - flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0] + flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "AltFilter_PinkFlower")[0] - spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, flower_prefab) + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, flower_prefab) # Add a Vegetation Altitude Filter spawner_entity.add_component("Vegetation Altitude Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py index 06f6c2a7b4..be2c5e4d8c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py @@ -32,9 +32,7 @@ def AltitudeFilter_FilterStageToggle(): import os import azlmbr.legacy.general as general - import azlmbr.bus as bus import azlmbr.math as math - import azlmbr.prefab as prefab import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg @@ -45,17 +43,16 @@ def AltitudeFilter_FilterStageToggle(): POSTPROCESS_INSTANCE_COUNT = 34 # Open an existing simple level - helper.init_idle() - helper.open_level("", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") - flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0] + flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "AltFilter_PinkFlower2")[0] - vegetation = dynveg.create_prefab_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, flower_prefab) + vegetation = dynveg.create_temp_prefab_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, flower_prefab) # Add a Vegetation Altitude Filter to the vegetation area entity vegetation.add_component("Vegetation Altitude Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py index 544a58e242..1e9762d7d8 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py @@ -56,8 +56,7 @@ def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -67,9 +66,9 @@ def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(): center_point = math.Vector3(512.0, 512.0, 32.0) flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") - flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0] + flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "AltFilter_PinkFlower3")[0] - spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, flower_prefab) + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, flower_prefab) # Add a Vegetation Altitude Filter spawner_entity.add_component("Vegetation Altitude Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py index a856256929..7242b0b629 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py @@ -56,19 +56,21 @@ def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(): """ import os + from pathlib import Path import azlmbr.bus as bus import azlmbr.editor as editor import azlmbr.legacy.general as general import azlmbr.math as math import azlmbr.vegetation as vegetation + import azlmbr.prefab as prefab import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper - def create_asset_list_entity(name, center, dynamic_slice_asset_path): + def create_asset_list_entity(name, center, target_prefab): asset_list_entity = hydra.Entity(name) asset_list_entity.create_entity( center, @@ -77,18 +79,34 @@ def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(): if asset_list_entity.id.IsValid(): print(f"'{asset_list_entity.name}' created") - # Set the Asset List to a Dynamic Slice spawner with a specific slice asset selected - dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner() - dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path) + if target_prefab: + # Get the in-memory spawnable asset id if exists + spawnable_name = Path(target_prefab.file_path).stem + spawnable_asset_id = prefab.PrefabPublicRequestBus(bus.Broadcast, 'GetInMemorySpawnableAssetId', + spawnable_name) + + # Create the in-memory spawnable asset from given prefab if the spawnable does not exist + if not spawnable_asset_id.is_valid(): + create_spawnable_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreateInMemorySpawnableAsset', + target_prefab.file_path, + spawnable_name) + assert create_spawnable_result.IsSuccess(), \ + f"Prefab operation 'CreateInMemorySpawnableAssets' failed. Error: {create_spawnable_result.GetError()}" + spawnable_asset_id = create_spawnable_result.GetValue() + else: + spawnable_asset_id = None + + # Set the vegetation area to a prefab instance spawner with a specific prefab asset selected descriptor = hydra.get_component_property_value(asset_list_entity.components[0], - "Configuration|Embedded Assets|[0]") - descriptor.spawner = dynamic_slice_spawner + 'Configuration|Embedded Assets|[0]') + prefab_spawner = vegetation.PrefabInstanceSpawner() + prefab_spawner.SetPrefabAssetId(spawnable_asset_id) + descriptor.spawner = prefab_spawner asset_list_entity.get_set_test(0, "Configuration|Embedded Assets|[0]", descriptor) return asset_list_entity # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -96,11 +114,13 @@ def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(): # 2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - asset_path2 = os.path.join("Slices", "PurpleFlower.dynamicslice") - asset_list_entity = create_asset_list_entity("Asset List 1", center_point, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "AssetList_PinkFlower")[0] + purple_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + purple_flower_prefab = dynveg.create_temp_mesh_prefab(purple_flower_asset_path, "AssetList_PurpleFlower")[0] + asset_list_entity = create_asset_list_entity("Asset List 1", center_point, pink_flower_prefab) asset_list_entity2 = create_asset_list_entity("Asset List 2", center_point, None) - asset_list_entity3 = create_asset_list_entity("Asset List 3", center_point, asset_path2) + asset_list_entity3 = create_asset_list_entity("Asset List 3", center_point, purple_flower_prefab) # 3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn # on center instead of corner diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py index b8b50f8110..8ea0b6b23d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py @@ -56,8 +56,7 @@ def AssetWeightSelector_InstancesExpressBasedOnWeight(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -66,14 +65,15 @@ def AssetWeightSelector_InstancesExpressBasedOnWeight(): # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors, one set to a # valid slice entity, and one set to None spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "AssetWeight_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + pink_flower_prefab) desc_asset = hydra.get_component_property_value(spawner_entity.components[2], "Configuration|Embedded Assets")[0] desc_list = [desc_asset, desc_asset] spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[1]|Instance|Slice Asset", None) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[1]|Instance|Prefab Asset", None) # Add an Asset Weight Selector component to the spawner entity spawner_entity.add_component("Vegetation Asset Weight Selector") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py index ed5501d719..be0fe8dba7 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py @@ -64,16 +64,15 @@ def DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(): instance_query_point_c = math.Vector3(515.0, 512.5, 32.0) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") - + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create a new entity with required vegetation area components spawner_center_point = math.Vector3(520.0, 520.0, 32.0) - asset_path = os.path.join("Slices", "1m_cube.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + cube_asset_path = os.path.join("testdata", "multi-mat_fbx", "multi-mat_1m_cube.azmodel") + cube_prefab = dynveg.create_temp_mesh_prefab(cube_asset_path, "DistanceBetween_1m_cube2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + cube_prefab) # 3) Create a surface to plant on surface_center_point = math.Vector3(512.0, 512.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py index c24ec3c314..b2244fdfa7 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py @@ -62,16 +62,15 @@ def DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(): instance_query_point_c = math.Vector3(515.0, 512.5, 32.0) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") - + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create a new entity with required vegetation area components spawner_center_point = math.Vector3(520.0, 520.0, 32.0) - asset_path = os.path.join("Slices", "1m_cube.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + cube_asset_path = os.path.join("testdata", "multi-mat_fbx", "multi-mat_1m_cube.azmodel") + cube_prefab = dynveg.create_temp_mesh_prefab(cube_asset_path, "DistanceBetween_1m_cube")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, + 16.0, 16.0, 16.0, cube_prefab) # 3) Create a surface to plant on surface_center_point = math.Vector3(512.0, 512.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py index d016473d5e..c1ca3cbfc6 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py @@ -39,11 +39,11 @@ def DynVegUtils_TempPrefabCreationWorks(): with Tracer() as error_tracer: # Create dictionary for prefab filenames and paths to create using helper function mesh_prefabs = { - "PinkFlower": os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel"), - "PurpleFlower": os.path.join("assets", "objects", "foliage", "grass_flower_purple.azmodel"), - "1m_Cube": os.path.join("objects", "_primitives", "_box_1x1.azmodel"), - "CedarTree": os.path.join("assets", "objects", "foliage", "cedar.azmodel"), - "Bush": os.path.join("assets", "objects", "foliage", "bush_privet_01.azmodel"), + "UtilsTest_PinkFlower": os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel"), + "UtilsTest_PurpleFlower": os.path.join("assets", "objects", "foliage", "grass_flower_purple.azmodel"), + "UtilsTest_1m_Cube": os.path.join("objects", "_primitives", "_box_1x1.azmodel"), + "UtilsTest_CedarTree": os.path.join("assets", "objects", "foliage", "cedar.azmodel"), + "UtilsTest_Bush": os.path.join("assets", "objects", "foliage", "bush_privet_01.azmodel"), } # 1) Open an existing simple level diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py index fcbf85c59d..c8581ad275 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py @@ -71,8 +71,7 @@ def DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Grab the UUID that we need for creating an Dynamic Slice Instance Spawner diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py index d0a51809b4..91315a6971 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py @@ -57,8 +57,7 @@ def EmptyInstanceSpawner_EmptySpawnerWorks(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Grab the UUID that we need for creating an Empty Spawner diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py index 471c8862fd..f83807a62b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py @@ -60,8 +60,7 @@ def InstanceSpawnerPriority_LayerAndSubPriority(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -69,9 +68,10 @@ def InstanceSpawnerPriority_LayerAndSubPriority(): # 2) Create overlapping areas: 1 instance spawner area, and 1 blocker area spawner_center_point = math.Vector3(508.0, 508.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "Priority_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0, + pink_flower_prefab) blocker_center_point = math.Vector3(516.0, 516.0, 32.0) blocker_entity = dynveg.create_blocker_area("Instance Blocker", blocker_center_point, 16.0, 16.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py index 6b5a80ee8e..79f2e4169b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py @@ -85,18 +85,14 @@ def LayerBlender_E2E_Editor(): # 2) Create 2 vegetation areas with different meshes purple_position = math.Vector3(504.0, 512.0, 32.0) - purple_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity_1 = dynveg.create_dynamic_slice_vegetation_area("Purple Spawner", - purple_position, - 16.0, 16.0, 1.0, - purple_asset_path) + purple_flower_prefab_path = os.path.join("assets", "prefabs", "PurpleFlower.spawnable") + spawner_entity_1 = dynveg.create_prefab_vegetation_area("Purple Spawner", purple_position, 16.0, 16.0, 1.0, + purple_flower_prefab_path) pink_position = math.Vector3(520.0, 512.0, 32.0) - pink_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity_2 = dynveg.create_dynamic_slice_vegetation_area("Pink Spawner", - pink_position, - 16.0, 16.0, 1.0, - pink_asset_path) + pink_flower_prefab_path = os.path.join("assets", "prefabs", "PinkFlower.spawnable") + spawner_entity_2 = dynveg.create_prefab_vegetation_area("Pink Spawner", pink_position, 16.0, 16.0, 1.0, + pink_flower_prefab_path) base_position = math.Vector3(512.0, 512.0, 32.0) dynveg.create_surface_entity("Surface Entity", @@ -135,11 +131,11 @@ def LayerBlender_E2E_Editor(): pink_count = 0 purple_count = 0 for instance in instances: - purple_asset_path = purple_asset_path.replace("\\", "/").lower() - pink_asset_path = pink_asset_path.replace("\\", "/").lower() - if instance.descriptor.spawner.GetSliceAssetPath() == pink_asset_path: + purple_flower_prefab_path = purple_flower_prefab_path.replace("\\", "/").lower() + pink_flower_prefab_path = pink_flower_prefab_path.replace("\\", "/").lower() + if instance.descriptor.spawner.GetPrefabAssetPath() == pink_flower_prefab_path: pink_count += 1 - elif instance.descriptor.spawner.GetSliceAssetPath() == purple_asset_path: + elif instance.descriptor.spawner.GetPrefabAssetPath() == purple_flower_prefab_path: purple_count += 1 Report.result(Tests.instances_blended, pink_count == purple_count and (pink_count + purple_count == num_expected)) @@ -152,11 +148,10 @@ def LayerBlender_E2E_Editor(): components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", search_entity_ids[0], cam_rot_degrees_vector) - # 6) Save and export to engine + # 6) Save the created level general.save_level() - general.export_to_engine() - pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") - success = helper.wait_for_condition(lambda: os.path.exists(pak_path), 10.0) + level_prefab_path = os.path.join(paths.products, "levels", lvl_name, f"{lvl_name}.spawnable") + success = helper.wait_for_condition(lambda: os.path.exists(level_prefab_path), 5.0) Report.result(Tests.saved_and_exported, success) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py index b89edcdeb7..5c3083ee3d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py @@ -17,7 +17,6 @@ class Tests: ) - def LayerBlocker_InstancesBlockedInConfiguredArea(): """ Summary: @@ -58,8 +57,7 @@ def LayerBlocker_InstancesBlockedInConfiguredArea(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -67,9 +65,10 @@ def LayerBlocker_InstancesBlockedInConfiguredArea(): # 2) Create a new instance spawner entity spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "Blocker_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, + 16.0, 16.0, 16.0, pink_flower_prefab) # 3) Create surface for planting on dynveg.create_surface_entity("Surface Entity", spawner_center_point, 32.0, 32.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py index 6796f56b11..a277f14a41 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py @@ -42,16 +42,17 @@ def LayerSpawner_FilterStageToggle(): POSTPROCESS_INSTANCE_COUNT = 19 # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) # Create a vegetation area with all needed components position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation_entity = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerFilter_PinkFlower")[0] + vegetation_entity = dynveg.create_temp_prefab_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, + pink_flower_prefab) vegetation_entity.add_component("Vegetation Altitude Filter") vegetation_entity.add_component("Vegetation Position Modifier") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py index 1153ae2657..dc06c298d1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py @@ -42,19 +42,16 @@ def LayerSpawner_InheritBehaviorFlag(): SURFACE_TAG = "test_tag" - def set_dynamic_slice_asset(entity_obj, component_index, dynamic_slice_asset_path): - dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner() - dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path) - descriptor = hydra.get_component_property_value( - entity_obj.components[component_index], "Configuration|Embedded Assets|[0]" - ) - descriptor.spawner = dynamic_slice_spawner + def set_prefab_asset(entity_obj, component_index, spawnable_prefab): + descriptor = hydra.get_component_property_value(entity_obj.components[component_index], + "Configuration|Embedded Assets|[0]") + prefab_spawner = vegetation.PrefabInstanceSpawner() + prefab_spawner.SetPrefabAssetId(spawnable_prefab) + descriptor.spawner = prefab_spawner entity_obj.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor) # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") - + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create Emitter entity and add the required components @@ -80,7 +77,9 @@ def LayerSpawner_InheritBehaviorFlag(): veg_1.create_entity( position, ["Vegetation Layer Spawner", "Shape Reference", "Vegetation Asset List"] ) - set_dynamic_slice_asset(veg_1, 2, os.path.join("Slices", "PinkFlower.dynamicslice")) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerInheritBehavior_PinkFlower")[0] + set_prefab_asset(veg_1, 2, pink_flower_prefab) veg_1.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id) # Create second vegetation area and assign a valid asset @@ -88,7 +87,9 @@ def LayerSpawner_InheritBehaviorFlag(): veg_2.create_entity( position, ["Vegetation Layer Spawner", "Shape Reference", "Vegetation Asset List"] ) - set_dynamic_slice_asset(veg_2, 2, os.path.join("Slices", "PurpleFlower.dynamicslice")) + purple_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + purple_flower_prefab = dynveg.create_temp_mesh_prefab(purple_flower_asset_path, "temp_PurpleFlower")[0] + set_prefab_asset(veg_2, 2, purple_flower_prefab) veg_2.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id) # Assign the vegetation areas to the Blender entity diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py index beec54b4eb..662ac63714 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py @@ -56,16 +56,14 @@ def LayerSpawner_InstancesPlantInAllSupportedShapes(): Report.result(success, result) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create basic vegetation area entity and set the properties entity_position = math.Vector3(125.0, 136.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - vegetation = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerShapePlant_PinkFlower")[0] + vegetation = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) vegetation.remove_component("Box Shape") vegetation.add_component("Shape Reference") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py index fe8863c625..deb0376c9b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py @@ -43,13 +43,13 @@ def LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(): import azlmbr.legacy.general as general import azlmbr.math as math + import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set up a test environment to validate that switching viewports correctly changes which camera # the vegetation system uses. @@ -100,11 +100,12 @@ def LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(): surface_height) # Create the two vegetation areas - test_slice_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - first_veg_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size, - box_size, test_slice_asset_path) - second_veg_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size, - box_size, test_slice_asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerViewportRefresh_PinkFlower")[0] + first_veg_entity = dynveg.create_temp_prefab_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size, + box_size, pink_flower_prefab) + second_veg_entity = dynveg.create_temp_prefab_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size, + box_size, pink_flower_prefab) # When the first viewport is active, the first area should be full of instances, and the second should be empty general.set_active_viewport(0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py index 9777a52c82..7c365d0ee5 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py @@ -50,19 +50,17 @@ def MeshBlocker_InstancesBlockedByMesh(): from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) # Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "MeshBlocker_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) # Create surface entity to plant on dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py index 3c47edeb91..a54a8653f0 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py @@ -53,19 +53,17 @@ def MeshBlocker_InstancesBlockedByMeshHeightTuning(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "MeshBlocker_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) # 3) Create surface entity to plant on dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py index cee8ebe5b6..9af17b0cd5 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py @@ -58,8 +58,7 @@ def MeshSurfaceTagEmitter_DependentOnMeshComponent(): return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with component "Mesh Surface Tag Emitter" entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py index d7abc66106..cd9963e844 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py @@ -47,8 +47,7 @@ def MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh" entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py index 6232cd374d..cdb2fac80b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py @@ -55,8 +55,7 @@ def PhysXColliderSurfaceTagEmitter_E2E_Editor(): return behavior_context_test_success # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Verify all of the BehaviorContext API: behavior_context = ( @@ -90,8 +89,10 @@ def PhysXColliderSurfaceTagEmitter_E2E_Editor(): dynveg.create_surface_entity("Baseline Surface", entity_center_point, 32.0, 32.0, 1.0) # Create a new entity with required vegetation area components - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "PhysXCollider_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_center_point, test_box_size, + test_box_size, test_box_size, pink_flower_prefab) # Add a Vegetation Surface Mask Filter component to the spawner entity and set it to include the "test" tag spawner_entity.add_component("Vegetation Surface Mask Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py index be7e8ad754..85a1cddb95 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py @@ -64,8 +64,7 @@ def PositionModifier_AutoSnapToSurfaceWorks(): 'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max'] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -73,9 +72,10 @@ def PositionModifier_AutoSnapToSurfaceWorks(): # 2) Create a new entity with required vegetation area components and a Position Modifier spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "PosMod_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, + 16.0, pink_flower_prefab) # Add a Vegetation Position Modifier and set offset values to 0 spawner_entity.add_component("Vegetation Position Modifier") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py index 07d4fbd067..6be3dc31f7 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py @@ -102,16 +102,17 @@ def PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(): return offset_success and offset_success2 # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(16.0, -5.0, 32.0) # 2) Create a new entity with required vegetation area components spawner_center_point = math.Vector3(16.0, 16.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "PosMod_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, + 1.0, pink_flower_prefab) # Add a Vegetation Position Modifier and set offset values to 0 spawner_entity.add_component("Vegetation Position Modifier") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_Embedded_E2E.py similarity index 87% rename from AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py rename to AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_Embedded_E2E.py index b4650c782e..8ce865b5d2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_Embedded_E2E.py @@ -63,12 +63,14 @@ def DynamicSliceInstanceSpawner_Embedded_E2E(): import azlmbr.entity as entity import azlmbr.math as math import azlmbr.paths as paths + import azlmbr.vegetation as vegetation import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper + # 1) Create a new, temporary level lvl_name = "tmp_level" helper.init_idle() @@ -79,13 +81,14 @@ def DynamicSliceInstanceSpawner_Embedded_E2E(): # 2) Create a new entity with required vegetation area components and Script Canvas component for launcher test center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path) + pink_flower_prefab_path = os.path.join("assets", "prefabs", "PinkFlower.spawnable") + spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, + pink_flower_prefab_path) spawner_entity.add_component("Script Canvas") instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas") instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path, math.Uuid(), False) - spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script) + spawner_entity.get_set_test(3, "Properties", instance_counter_script) Report.result(Tests.spawner_entity_created, spawner_entity.id.IsValid() and hydra.has_components(spawner_entity.id, ["Script Canvas"])) @@ -106,11 +109,10 @@ def DynamicSliceInstanceSpawner_Embedded_E2E(): search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) - # 6) Save and export to engine + # 6) Save the created level general.save_level() - general.export_to_engine() - pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") - success = helper.wait_for_condition(lambda: os.path.exists(pak_path), 10.0) + level_prefab_path = os.path.join(paths.products, "levels", lvl_name, f"{lvl_name}.spawnable") + success = helper.wait_for_condition(lambda: os.path.exists(level_prefab_path), 5.0) Report.result(Tests.saved_and_exported, success) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_External_E2E.py similarity index 96% rename from AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py rename to AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_External_E2E.py index a5e7e90ce2..a3e60f6337 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_External_E2E.py @@ -128,11 +128,10 @@ def DynamicSliceInstanceSpawner_External_E2E(): search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) - # 6) Save and export to engine + # 6) Save the created level general.save_level() - general.export_to_engine() - pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") - success = helper.wait_for_condition(lambda: os.path.exists(pak_path), 10.0) + level_prefab_path = os.path.join(paths.products, "levels", lvl_name, f"{lvl_name}.spawnable") + success = helper.wait_for_condition(lambda: os.path.exists(level_prefab_path), 5.0) Report.result(Tests.saved_and_exported, success) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py index c1ea8e03d1..877a38c231 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py @@ -81,14 +81,15 @@ def RotationModifierOverrides_InstancesRotateWithinRange(): return result # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create vegetation entity and add components entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "RotMod_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 16.0, 16.0, 16.0, + pink_flower_prefab) spawner_entity.add_component("Vegetation Rotation Modifier") # Our default vegetation settings places 20 instances per 16 meters, so we expect 20 * 20 total instances. num_expected = 20 * 20 diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py index 518c11a0cf..b0999f3195 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py @@ -120,13 +120,14 @@ def RotationModifier_InstancesRotateWithinRange(): # Main Script # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Set up vegetation entities - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "RotMod_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", LEVEL_CENTER, 2.0, 2.0, 2.0, + pink_flower_prefab) additional_components = [ "Vegetation Rotation Modifier" diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py index b2ce6d8afa..42b042e26e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py @@ -91,16 +91,17 @@ def ScaleModifierOverrides_InstancesProperlyScale(): return False # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) # 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "ScaleMod_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 16.0, 16.0, 16.0, + pink_flower_prefab) # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py index fd0df1c83d..8008224e3e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py @@ -84,8 +84,7 @@ def ScaleModifier_InstancesProperlyScale(): return False # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) @@ -93,9 +92,10 @@ def ScaleModifier_InstancesProperlyScale(): # 2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and # Vegetation Scale Modifier entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "ScaleMod_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 16.0, 16.0, 16.0, + pink_flower_prefab) spawner_entity.add_component("Vegetation Scale Modifier") # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py index b5c00a53b7..5436855433 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py @@ -49,15 +49,16 @@ def ShapeIntersectionFilter_FilterStageToggle(): from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "ShapeIntersection_PinkFlower")[0] + vegetation = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", position, 16.0, 16.0, 16.0, + pink_flower_prefab) # Create Surface for instances to plant on dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py index 6678a620af..e9e4c722f1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py @@ -60,8 +60,7 @@ def ShapeIntersectionFilter_InstancesPlantInAssignedShape(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -69,9 +68,10 @@ def ShapeIntersectionFilter_InstancesPlantInAssignedShape(): # 2) Create a new entity with required vegetation area components and Vegetation Shape Intersection Filter center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "ShapeIntersection_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, + pink_flower_prefab) spawner_entity.add_component("Vegetation Shape Intersection Filter") # Create a planting surface diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py index c9233d15ea..07dbb3f977 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py @@ -58,15 +58,16 @@ def SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(): return False # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create a spawner entity setup with all needed components center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SlopeAlign_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, + pink_flower_prefab) # Create a sloped mesh surface for the instances to plant on center_point = math.Vector3(502.0, 512.0, 24.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py index d92babffa4..088728480b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py @@ -59,15 +59,16 @@ def SlopeAlignmentModifier_InstanceSurfaceAlignment(): return False # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create a spawner entity setup with all needed components center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SlopeAlign_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, + pink_flower_prefab) # Create a sloped mesh surface for the instances to plant on center_point = math.Vector3(502.0, 512.0, 24.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py index 0f95853156..cc04dc4cb1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py @@ -63,16 +63,17 @@ def SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 475.0, 38.0) # 2) Create a new entity with required vegetation area components center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "Slope_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, + pink_flower_prefab) # Add a Vegetation Slope Filter spawner_entity.add_component("Vegetation Slope Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks.py similarity index 52% rename from AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py rename to AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks.py index fc55080cec..182bd5510a 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks.py @@ -7,21 +7,21 @@ SPDX-License-Identifier: Apache-2.0 OR MIT class Tests: - spawner_slice_created = ( - "Spawner slice created successfully", - "Failed to create Spawner slice" + spawner_prefab_created = ( + "Spawner prefab created successfully", + "Failed to create Spawner prefab" ) instance_count_unhidden = ( "Initial instance counts are as expected", "Found an unexpected number of initial instances" ) instance_count_hidden = ( - "Instance counts upon hiding the Spawner slice are as expected", - "Unexpectedly found instances with the Spawner slice hidden" + "Instance counts upon hiding the Spawner prefab are as expected", + "Unexpectedly found instances with the Spawner prefab hidden" ) - blender_slice_created = ( - "Blender slice created successfully", - "Failed to create Blender slice" + blender_prefab_created = ( + "Blender prefab created successfully", + "Failed to create Blender prefab" ) @@ -43,81 +43,76 @@ def SpawnerSlices_SliceCreationAndVisibilityToggleWorks(): import azlmbr.math as math import azlmbr.legacy.general as general - import azlmbr.slice as slice import azlmbr.bus as bus import azlmbr.editor as editor - import azlmbr.asset as asset import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg + from editor_python_test_tools.editor_entity_utils import EditorEntity from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper - - def path_is_valid_asset(asset_path): - asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False) - return asset_id.invoke("IsValid") + from editor_python_test_tools.prefab_utils import Prefab # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) - # 2) C2627900 Verifies if a slice containing the Vegetation Layer Spawner component can be created. + # 2) Verifies if a prefab containing the Vegetation Layer Spawner component can be created. # 2.1) Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - veg_1 = dynveg.create_dynamic_slice_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerPrefab_PinkFlower")[0] + veg_1 = dynveg.create_temp_prefab_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, + pink_flower_prefab) - # 2.2) Create slice from the entity - slice_path = os.path.join("slices", "TestSlice_1.slice") - slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", veg_1.id, slice_path) + # 2.2) Create prefab from the entity + spawner_prefab_filename = "TestPrefab_1" + spawner_prefab, spawner_prefab_instance = Prefab.create_prefab([veg_1], spawner_prefab_filename) - # 2.3) Verify if the slice has been created successfully - spawner_slice_success = helper.wait_for_condition(lambda: path_is_valid_asset(slice_path), 10.0) - Report.result(Tests.spawner_slice_created, spawner_slice_success) + # Verify if prefab is created + Report.result(Tests.spawner_prefab_created, spawner_prefab.is_prefab_loaded(spawner_prefab_filename)) - # 3) C2627904: Hiding a slice containing the component clears any visuals from the Viewport + # 3) Hiding a prefab containing the component clears any visuals from the Viewport # 3.1) Create Surface for instances to plant on dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) - # 3.2) Initially verify instance count before hiding slice + # 3.2) Initially verify instance count before hiding prefab initial_count_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400), 5.0) Report.result(Tests.instance_count_unhidden, initial_count_success) - # 3.3) Hide the slice and verify instance count - editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, False) + # 3.3) Hide the prefab and verify instance count + editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", spawner_prefab_instance.container_entity.id, False) hidden_instance_count = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 0), 5.0) Report.result(Tests.instance_count_hidden, hidden_instance_count) # 3.4) Unhide the slice - editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, True) + editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", spawner_prefab_instance.container_entity.id, True) - # 4) C2627905 A slice containing the Vegetation Layer Blender component can be created. + # 4) A slice containing the Vegetation Layer Blender component can be created. # 4.1) Create another vegetation entity to add to blender component - veg_2 = dynveg.create_dynamic_slice_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "") + veg_2 = dynveg.create_empty_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0) # 4.2) Create entity with Vegetation Layer Blender components_to_add = ["Box Shape", "Vegetation Layer Blender"] - blender_entity = hydra.Entity("blender_entity") - blender_entity.create_entity(position, components_to_add) + blender_entity = EditorEntity.create_editor_entity("blender_entity") + blender_entity.add_components(components_to_add) # 4.3) Pin both the vegetation areas to the blender entity - pte = hydra.get_property_tree(blender_entity.components[1]) + pte = blender_entity.components[1].get_property_tree() path = "Configuration|Vegetation Areas" pte.update_container_item(path, 0, veg_1.id) pte.add_container_item(path, 1, veg_2.id) # 4.4) Drag the simple vegetation areas under the Vegetation Layer Blender entity to create an entity hierarchy. veg_1.set_test_parent_entity(blender_entity) - veg_2.set_test_parent_entity(blender_entity) + veg_2.set_parent_entity(blender_entity.id) - # 4.5) Create slice from blender entity - slice_path = os.path.join("slices", "TestSlice_2.slice") - slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", blender_entity.id, slice_path) + # 4.5) Create prefab from blender entity + blender_prefab_filename = "TestPrefab_2" + blender_prefab, blender_prefab_instance = Prefab.create_prefab([veg_2], blender_prefab_filename) - # 4.6) Verify if the slice has been created successfully - blender_slice_success = helper.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0) - Report.result(Tests.blender_slice_created, blender_slice_success) + # 4.6) Verify if the prefab has been created successfully + Report.result(Tests.blender_prefab_created, blender_prefab.is_prefab_loaded(blender_prefab_filename)) if __name__ == "__main__": diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py index 1ad4f305c3..3612e03bcc 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py @@ -34,13 +34,12 @@ def SurfaceDataRefreshes_RemainsStable(): import azlmbr.legacy.general as general import azlmbr.math as math + import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() world_center = math.Vector3(512.0, 512.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py index 3e76a03c53..b98b0f0546 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py @@ -67,8 +67,7 @@ def SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(): surface_data.SurfaceTag("test_tag3")] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -76,9 +75,10 @@ def SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(): # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SurfaceMaskOverrides_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, + 16.0, pink_flower_prefab) asset_list_component = spawner_entity.components[2] desc_asset = hydra.get_component_property_value(asset_list_component, "Configuration|Embedded Assets")[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py index fee62c04d1..fe17b03c6f 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py @@ -39,12 +39,12 @@ def SurfaceMaskFilter_BasicSurfaceTagCreation(): """ import azlmbr.surface_data as surface_data + + import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() tag1 = surface_data.SurfaceTag() tag2 = surface_data.SurfaceTag() diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py index 3f298e793e..f5174af3bb 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py @@ -91,18 +91,16 @@ def SurfaceMaskFilter_ExclusionList(): Report.info(f"Failed to add Generated surface tag of {surface_tag}") # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SurfaceMaskTagExclusion_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) # 3) Add a Vegetation Surface Mask Filter component to the entity. spawner_entity.add_component("Vegetation Surface Mask Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py index 39b72ff4a9..34d22d3d9b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py @@ -92,18 +92,16 @@ def SurfaceMaskFilter_InclusionList(): Report.info(f"Failed to add Generated surface tag of {surface_tag}") # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SurfaceMaskTagInclusion_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) # 3) Add a Vegetation Surface Mask Filter component to the entity. spawner_entity.add_component("Vegetation Surface Mask Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py index 167fb8901c..7516bfb31d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py @@ -47,15 +47,16 @@ def SystemSettings_SectorPointDensity(): INSTANCE_COUNT_AFTER_DENSITY_CHANGE = 100 # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SectorPoint_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", position, 16.0, 16.0, 1.0, + pink_flower_prefab) dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) # Count the number of vegetation instances in the vegetation area diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py index 7bb78ac3e0..ef7959a3e7 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py @@ -43,15 +43,16 @@ def SystemSettings_SectorSize(): VEGETATION_INSTANCE_COUNT = 400 # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SectorSize_PinkFlower")[0] + vegetation = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", position, 16.0, 16.0, 1.0, + pink_flower_prefab) dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) # Add the Vegetation Debugger component to the Level Inspector diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py index 8a6c4d9a17..b6069d1ef5 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py @@ -43,18 +43,20 @@ def VegetationInstances_DespawnWhenOutOfRange(): import azlmbr.legacy.general as general import azlmbr.math as math + import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Create vegetation layer spawner world_center = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "RangeDespawn_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", world_center, 16.0, 16.0, 16.0, + pink_flower_prefab) # Create a surface to spawn on dynveg.create_surface_entity("Spawner Entity", world_center, 16.0, 16.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py index 06c9c5f615..d508e390cb 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py @@ -7,21 +7,171 @@ SPDX-License-Identifier: Apache-2.0 OR MIT import os import pytest -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared') -from base import TestAutomationBase +import ly_test_tools.environment.file_system as file_system +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite @pytest.mark.SUITE_main @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): +class TestAutomation(EditorTestSuite): - def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, workspace, editor, launcher_platform): + # Helpers for test asset cleanup + def cleanup_test_level(self, workspace): + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], + True, True) + + class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorSharedTest): + from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module + + class test_AltitudeFilter_FilterStageToggle(EditorSharedTest): + from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module + + class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorSharedTest): + from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module + + class test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(EditorSharedTest): + from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module + + class test_AssetWeightSelector_InstancesExpressBasedOnWeight(EditorSharedTest): + from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") + class test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(EditorSharedTest): + from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") + class test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(EditorSharedTest): + from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module + + class test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(EditorSharedTest): from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, workspace, editor, launcher_platform): + class test_EmptyInstanceSpawner_EmptySpawnerWorks(EditorSharedTest): from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + + class test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(EditorSharedTest): + from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module + + class test_LayerBlender_E2E_Editor(EditorSingleTest): + from .EditorScripts import LayerBlender_E2E_Editor as test_module + + # Custom setup/teardown to remove test level created during test + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + class test_LayerBlocker_InstancesBlockedInConfiguredArea(EditorSharedTest): + from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module + + class test_LayerSpawner_FilterStageToggle(EditorSharedTest): + from .EditorScripts import LayerSpawner_FilterStageToggle as test_module + + class test_LayerSpawner_InheritBehaviorFlag(EditorSharedTest): + from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module + + class test_LayerSpawner_InstancesPlantInAllSupportedShapes(EditorSharedTest): + from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/6549") + class test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(EditorSharedTest): + from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module + + class test_MeshBlocker_InstancesBlockedByMesh(EditorSharedTest): + from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module + + class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorSharedTest): + from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module + + class test_MeshSurfaceTagEmitter_DependentOnMeshComponent(EditorSharedTest): + from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module + + class test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorSharedTest): + from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module + + class test_PhysXColliderSurfaceTagEmitter_E2E_Editor(EditorSharedTest): + from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module + + class test_PositionModifier_AutoSnapToSurfaceWorks(EditorSharedTest): + from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module + + class test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(EditorSharedTest): + from .EditorScripts import \ + PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module + + class test_PrefabInstanceSpawner_Embedded_E2E_Editor(EditorSingleTest): + from .EditorScripts import PrefabInstanceSpawner_Embedded_E2E as test_module + + # Custom setup/teardown to remove test level created during test + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + class test_PrefabInstanceSpawner_External_E2E_Editor(EditorSingleTest): + from .EditorScripts import PrefabInstanceSpawner_External_E2E as test_module + + # Custom setup/teardown to remove test level created during test + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + class test_RotationModifier_InstancesRotateWithinRange(EditorSharedTest): + from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module + + class test_RotationModifierOverrides_InstancesRotateWithinRange(EditorSharedTest): + from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module + + class test_ScaleModifier_InstancesProperlyScale(EditorSharedTest): + from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module + + class test_ScaleModifierOverrides_InstancesProperlyScale(EditorSharedTest): + from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module + + class test_ShapeIntersectionFilter_FilterStageToggle(EditorSharedTest): + from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module + + class test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(EditorSharedTest): + from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module + + class test_SlopeAlignmentModifier_InstanceSurfaceAlignment(EditorSharedTest): + from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module + + class test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(EditorSharedTest): + from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module + + class test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(EditorSharedTest): + from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module + + class test_SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks(EditorSharedTest): + from .EditorScripts import SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks as test_module + + class test_SurfaceDataRefreshes_RemainsStable(EditorSharedTest): + from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module + + class test_SurfaceMaskFilter_BasicSurfaceTagCreation(EditorSharedTest): + from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module + + class test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(EditorSharedTest): + from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module + + class test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(EditorSharedTest): + from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module + + class test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(EditorSharedTest): + from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module + + class test_SystemSettings_SectorPointDensity(EditorSharedTest): + from .EditorScripts import SystemSettings_SectorPointDensity as test_module + + class test_SystemSettings_SectorSize(EditorSharedTest): + from .EditorScripts import SystemSettings_SectorSize as test_module + + class test_VegetationInstances_DespawnWhenOutOfRange(EditorSharedTest): + from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py deleted file mode 100644 index 5b1e504442..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py +++ /dev/null @@ -1,192 +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 -""" - -import os -import pytest - -import ly_test_tools.environment.file_system as file_system -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite - - -@pytest.mark.SUITE_main -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(EditorTestSuite): - class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorParallelTest): - from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module - - class test_AltitudeFilter_FilterStageToggle(EditorParallelTest): - from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module - - class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorParallelTest): - from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module - - -@pytest.mark.SUITE_main -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation_PrefabNotEnabled(EditorTestSuite): - - enable_prefab_system = False - - # Helpers for test asset cleanup - def cleanup_test_level(self, workspace): - file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], - True, True) - - def cleanup_test_slices(self, workspace): - file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "slices", - "TestSlice_1.slice")], True, True) - file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "slices", - "TestSlice_2.slice")], True, True) - - class test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(EditorParallelTest): - from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module - - class test_EmptyInstanceSpawner_EmptySpawnerWorks(EditorParallelTest): - from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module - - class test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(EditorSingleTest): - # Custom teardown to remove slice asset created during test - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_slices(self, workspace) - from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module - - class test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(EditorParallelTest): - from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module - - class test_AssetWeightSelector_InstancesExpressBasedOnWeight(EditorParallelTest): - from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module - - @pytest.mark.skip(reason="https://github.com/o3de/o3de/issues/4155") - class test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(EditorParallelTest): - from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module - - @pytest.mark.skip(reason="https://github.com/o3de/o3de/issues/4155") - class test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(EditorParallelTest): - from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module - - class test_SurfaceDataRefreshes_RemainsStable(EditorParallelTest): - from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module - - class test_VegetationInstances_DespawnWhenOutOfRange(EditorParallelTest): - from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module - - class test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(EditorParallelTest): - from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module - - class test_LayerBlocker_InstancesBlockedInConfiguredArea(EditorParallelTest): - from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module - - class test_LayerSpawner_InheritBehaviorFlag(EditorParallelTest): - from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module - - class test_LayerSpawner_InstancesPlantInAllSupportedShapes(EditorParallelTest): - from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module - - class test_LayerSpawner_FilterStageToggle(EditorParallelTest): - from .EditorScripts import LayerSpawner_FilterStageToggle as test_module - - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038") - class test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(EditorParallelTest): - from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module - - class test_MeshBlocker_InstancesBlockedByMesh(EditorParallelTest): - from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module - - class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorParallelTest): - from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module - - class test_MeshSurfaceTagEmitter_DependentOnMeshComponent(EditorParallelTest): - from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module - - class test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorParallelTest): - from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module - - class test_PhysXColliderSurfaceTagEmitter_E2E_Editor(EditorParallelTest): - from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module - - class test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(EditorParallelTest): - from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module - - class test_PositionModifier_AutoSnapToSurfaceWorks(EditorParallelTest): - from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module - - class test_RotationModifier_InstancesRotateWithinRange(EditorParallelTest): - from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module - - class test_RotationModifierOverrides_InstancesRotateWithinRange(EditorParallelTest): - from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module - - class test_ScaleModifier_InstancesProperlyScale(EditorParallelTest): - from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module - - class test_ScaleModifierOverrides_InstancesProperlyScale(EditorParallelTest): - from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module - - class test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(EditorParallelTest): - from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module - - class test_ShapeIntersectionFilter_FilterStageToggle(EditorParallelTest): - from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module - - class test_SlopeAlignmentModifier_InstanceSurfaceAlignment(EditorParallelTest): - from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module - - class test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(EditorParallelTest): - from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module - - class test_SurfaceMaskFilter_BasicSurfaceTagCreation(EditorParallelTest): - from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module - - class test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(EditorParallelTest): - from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module - - class test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(EditorParallelTest): - from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module - - class test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(EditorParallelTest): - from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module - - class test_SystemSettings_SectorPointDensity(EditorParallelTest): - from .EditorScripts import SystemSettings_SectorPointDensity as test_module - - class test_SystemSettings_SectorSize(EditorParallelTest): - from .EditorScripts import SystemSettings_SectorSize as test_module - - class test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(EditorParallelTest): - from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module - - class test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(EditorSingleTest): - from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module - - # Custom setup/teardown to remove test level created during test - def setup(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - class test_DynamicSliceInstanceSpawner_External_E2E_Editor(EditorSingleTest): - from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module - - # Custom setup/teardown to remove test level created during test - def setup(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - class test_LayerBlender_E2E_Editor(EditorSingleTest): - from .EditorScripts import LayerBlender_E2E_Editor as test_module - - # Custom setup/teardown to remove test level created during test - def setup(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py index 64f6cfdf30..bf7e76f973 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py @@ -7,277 +7,17 @@ SPDX-License-Identifier: Apache-2.0 OR MIT import os import pytest -import sys -import ly_test_tools.environment.waiter as waiter import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra -from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole - -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared') -from base import TestAutomationBase - - -@pytest.fixture -def remove_test_slice(request, workspace, project): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, - True) - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, - True) - - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, - True) - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, - True) - request.addfinalizer(teardown) - - -@pytest.fixture -def remote_console_instance(request): - console = RemoteConsole() - - def teardown(): - if console.connected: - console.stop() - - request.addfinalizer(teardown) - return console +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): - - def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module - self._run_test(request, workspace, editor, test_module) - - def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module - self._run_test(request, workspace, editor, test_module) - - def test_AltitudeFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module - self._run_test(request, workspace, editor, test_module) - - def test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(self, request, workspace, editor, remove_test_slice, launcher_platform): - from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_AssetWeightSelector_InstancesExpressBasedOnWeight(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") - def test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform): - from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") - def test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform): - from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceDataRefreshes_RemainsStable(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_VegetationInstances_DespawnWhenOutOfRange(self, request, workspace, editor, launcher_platform): - from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(self, request, workspace, editor, launcher_platform): - from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_LayerBlocker_InstancesBlockedInConfiguredArea(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_LayerSpawner_InheritBehaviorFlag(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_LayerSpawner_InstancesPlantInAllSupportedShapes(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_LayerSpawner_FilterStageToggle(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerSpawner_FilterStageToggle as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038") - def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_MeshBlocker_InstancesBlockedByMesh(self, request, workspace, editor, launcher_platform): - from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_MeshBlocker_InstancesBlockedByMeshHeightTuning(self, request, workspace, editor, launcher_platform): - from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_MeshSurfaceTagEmitter_DependentOnMeshComponent(self, request, workspace, editor, launcher_platform): - from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform): - from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_PhysXColliderSurfaceTagEmitter_E2E_Editor(self, request, workspace, editor, launcher_platform): - from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(self, request, workspace, editor, launcher_platform): - from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_PositionModifier_AutoSnapToSurfaceWorks(self, request, workspace, editor, launcher_platform): - from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_RotationModifier_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform): - from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_RotationModifierOverrides_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform): - from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_ScaleModifier_InstancesProperlyScale(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_ScaleModifierOverrides_InstancesProperlyScale(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_ShapeIntersectionFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SlopeAlignmentModifier_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceMaskFilter_BasicSurfaceTagCreation(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SystemSettings_SectorPointDensity(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SystemSettings_SectorPointDensity as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SystemSettings_SectorSize(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SystemSettings_SectorSize as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - -@pytest.mark.SUITE_periodic -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -class TestAutomationE2E(TestAutomationBase): - - # The following tests must run in order, please do not move tests out of order - - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): - # Ensure our test level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.parametrize("launcher_platform", ['windows']) - def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level, - remote_console_instance, project, launcher_platform): - - expected_lines = [ - "Instances found in area = 400" - ] - - hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, launch_ap=False) - - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_DynamicSliceInstanceSpawner_External_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): - # Ensure our test level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.parametrize("launcher_platform", ['windows']) - def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level, - remote_console_instance, project, launcher_platform): - - expected_lines = [ - "Instances found in area = 400" - ] - - hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, launch_ap=False) - - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_LayerBlender_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): - # Ensure our test level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - from .EditorScripts import LayerBlender_E2E_Editor as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.parametrize("launcher_platform", ['windows']) - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4170") - def test_LayerBlender_E2E_Launcher(self, workspace, launcher, level, - remote_console_instance, project, launcher_platform): - - launcher.args.extend(["-rhi=Null"]) - launcher.start(launch_ap=False) - assert launcher.is_alive(), "Launcher failed to start" - - # Wait for test script to quit the launcher. If wait_for returns exc, test was not successful - waiter.wait_for(lambda: not launcher.is_alive(), timeout=300) +class TestAutomation(EditorTestSuite): - # Verify launcher quit successfully and did not crash - ret_code = launcher.get_returncode() - assert ret_code == 0, "Test failed. See Game.log for details" + global_extra_cmdline_args = ["-BatchMode", "-autotest_mode"] - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + class test_DynVegUtils_TempPrefabCreationWorks(EditorSharedTest): + from .EditorScripts import DynVegUtils_TempPrefabCreationWorks as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py deleted file mode 100644 index f87d6567ef..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py +++ /dev/null @@ -1,23 +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 -""" - -import os -import pytest - -import ly_test_tools.environment.file_system as file_system -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite - - -@pytest.mark.SUITE_main -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(EditorTestSuite): - - global_extra_cmdline_args = ["-BatchMode", "-autotest_mode"] - - class test_DynVegUtils_TempPrefabCreationWorks(EditorSharedTest): - from .EditorScripts import DynVegUtils_TempPrefabCreationWorks as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py index 09cd760647..6979ff1dee 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py @@ -65,8 +65,7 @@ def GradientGenerators_Incompatibilities(): } # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # For every gradient generator component, verify that they are incompatible # which each vegetation area component diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py index a4d9181744..962a0004f9 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py @@ -74,8 +74,7 @@ def GradientModifiers_Incompatibilities(): } # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # For every gradient modifier component, verify that they are incompatible # which each vegetation area and gradient generator/modifier component diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py index 3749f93bf5..87f9e1a9b1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py @@ -86,8 +86,7 @@ def GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(): return entity # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create entity with Random Noise gradient and verify gradient position after clearing pinned entity clear_entityid_check_position( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py index 3756452710..cd3fd7e1ee 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py @@ -90,8 +90,7 @@ def GradientPreviewSettings_DefaultPinnedEntityIsSelf(): ] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() for param in param_list: execute_test(param.required_components[param.accessed_component], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSampling_GradientReferencesAddRemoveSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSampling_GradientReferencesAddRemoveSuccessfully.py index 8e85345a6f..f2980d9634 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSampling_GradientReferencesAddRemoveSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSampling_GradientReferencesAddRemoveSuccessfully.py @@ -61,8 +61,7 @@ def GradientSampling_GradientReferencesAddRemoveSuccessfully(): Report.result(gradient_cleared_from_modifier, entity.Equal(EntityId.EntityId())) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with components "Random Noise Gradient", "Gradient Transform Modifier" and "Box Shape" entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py index f653515a39..0cc325efd1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py @@ -42,8 +42,7 @@ def GradientSurfaceTagEmitter_ComponentDependencies(): return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Create an entity with Gradient Surface Tag Emitter component position = math.Vector3(512.0, 512.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py index 90840a135d..cf48096a31 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py @@ -36,8 +36,7 @@ def GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create an entity with Gradient Surface Tag Emitter and Reference Gradient components. entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithExpectedGradients.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithExpectedGradients.py index 5e6df4d52f..27ef248d6c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithExpectedGradients.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithExpectedGradients.py @@ -46,8 +46,7 @@ def GradientTransform_ComponentIncompatibleWithExpectedGradients(): return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with components Gradient Transform Modifier and Box Shape entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithSpawners.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithSpawners.py index fd65996fdd..9c23cdea75 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithSpawners.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithSpawners.py @@ -44,8 +44,7 @@ def GradientTransform_ComponentIncompatibleWithSpawners(): return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with components Gradient Transform Modifier and Box Shape entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange.py index 530503db86..6d6911ff3a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange.py @@ -54,8 +54,7 @@ def GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create entity entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py index 09cce359d5..ec142f92d2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py @@ -47,8 +47,7 @@ def GradientTransform_RequiresShape(): from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Create a new Entity in the level entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId()) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py index 42458665e1..afe0893da9 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py @@ -56,8 +56,7 @@ def ImageGradient_ProcessedImageAssignedSuccessfully(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create an entity with Image Gradient and Gradient Transform Modifier components components_to_add = ["Image Gradient", "Gradient Transform Modifier", "Box Shape"] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py index 3b832d160a..85582d198a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py @@ -47,8 +47,7 @@ def ImageGradient_RequiresShape(): from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Create a new Entity in the level entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId()) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py index 3ad27eb973..d4199c31f6 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py @@ -5,67 +5,51 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os import pytest -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared') -from base import TestAutomationBase +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): +class TestAutomation(EditorTestSuite): - def test_GradientGenerators_Incompatibilities(self, request, workspace, editor, launcher_platform): + class test_GradientGenerators_Incompatibilities(EditorSharedTest): from .EditorScripts import GradientGenerators_Incompatibilities as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientModifiers_Incompatibilities(self, request, workspace, editor, launcher_platform): + class test_GradientModifiers_Incompatibilities(EditorSharedTest): from .EditorScripts import GradientModifiers_Incompatibilities as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(self, request, workspace, editor, launcher_platform): - from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(self, request, workspace, editor, launcher_platform): + class test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(EditorSharedTest): from .EditorScripts import GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientSampling_GradientReferencesAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform): + class test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(EditorSharedTest): + from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module + + class test_GradientSampling_GradientReferencesAddRemoveSuccessfully(EditorSharedTest): from .EditorScripts import GradientSampling_GradientReferencesAddRemoveSuccessfully as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientSurfaceTagEmitter_ComponentDependencies(self, request, workspace, editor, launcher_platform): + class test_GradientSurfaceTagEmitter_ComponentDependencies(EditorSharedTest): from .EditorScripts import GradientSurfaceTagEmitter_ComponentDependencies as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform): + class test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorSharedTest): from .EditorScripts import GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientTransform_RequiresShape(self, request, workspace, editor, launcher_platform): - from .EditorScripts import GradientTransform_RequiresShape as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(self, request, workspace, editor, launcher_platform): - from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + class test_GradientTransform_ComponentIncompatibleWithExpectedGradients(EditorSharedTest): + from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module - def test_GradientTransform_ComponentIncompatibleWithSpawners(self, request, workspace, editor, launcher_platform): + class test_GradientTransform_ComponentIncompatibleWithSpawners(EditorSharedTest): from .EditorScripts import GradientTransform_ComponentIncompatibleWithSpawners as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientTransform_ComponentIncompatibleWithExpectedGradients(self, request, workspace, editor, launcher_platform): - from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + class test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(EditorSharedTest): + from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module - def test_ImageGradient_RequiresShape(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ImageGradient_RequiresShape as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + class test_GradientTransform_RequiresShape(EditorSharedTest): + from .EditorScripts import GradientTransform_RequiresShape as test_module - def test_ImageGradient_ProcessedImageAssignedSuccessfully(self, request, workspace, editor, launcher_platform): + class test_ImageGradient_ProcessedImageAssignedSuccessfully(EditorSharedTest): from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + + class test_ImageGradient_RequiresShape(EditorSharedTest): + from .EditorScripts import ImageGradient_RequiresShape as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py deleted file mode 100644 index 6ac0658b4d..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py +++ /dev/null @@ -1,57 +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 -""" - -import pytest - -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite - - -@pytest.mark.SUITE_periodic -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(EditorTestSuite): - - enable_prefab_system = False - - class test_GradientGenerators_Incompatibilities(EditorSharedTest): - from .EditorScripts import GradientGenerators_Incompatibilities as test_module - - class test_GradientModifiers_Incompatibilities(EditorSharedTest): - from .EditorScripts import GradientModifiers_Incompatibilities as test_module - - class test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(EditorSharedTest): - from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module - - class test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(EditorSharedTest): - from .EditorScripts import GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin as test_module - - class test_GradientSampling_GradientReferencesAddRemoveSuccessfully(EditorSharedTest): - from .EditorScripts import GradientSampling_GradientReferencesAddRemoveSuccessfully as test_module - - class test_GradientSurfaceTagEmitter_ComponentDependencies(EditorSharedTest): - from .EditorScripts import GradientSurfaceTagEmitter_ComponentDependencies as test_module - - class test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorSharedTest): - from .EditorScripts import GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module - - class test_GradientTransform_RequiresShape(EditorSharedTest): - from .EditorScripts import GradientTransform_RequiresShape as test_module - - class test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(EditorSharedTest): - from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module - - class test_GradientTransform_ComponentIncompatibleWithSpawners(EditorSharedTest): - from .EditorScripts import GradientTransform_ComponentIncompatibleWithSpawners as test_module - - class test_GradientTransform_ComponentIncompatibleWithExpectedGradients(EditorSharedTest): - from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module - - class test_ImageGradient_RequiresShape(EditorSharedTest): - from .EditorScripts import ImageGradient_RequiresShape as test_module - - class test_ImageGradient_ProcessedImageAssignedSuccessfully(EditorSharedTest): - from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py b/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py index 37afeff8c0..fd6a907b2e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py @@ -114,7 +114,8 @@ def create_dynamic_slice_vegetation_area(name, center_point, box_size_x, box_siz return spawner_entity -def create_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, target_prefab): +def create_temp_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, target_prefab): + """Creates a vegetation area using in-memory prefabs. Use when test is solely contained to Editor""" # Create a vegetation area entity to use as our test vegetation spawner spawner_entity = hydra.Entity(name) spawner_entity.create_entity( @@ -148,6 +149,31 @@ def create_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, bo return spawner_entity +def create_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, prefab_path): + """Creates a vegetation area using on-disk spawnable prefabs. Use when test requires saving/loading in Launcher""" + # Create a vegetation area entity to use as our test vegetation spawner + prefab_asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", prefab_path, math.Uuid(), + False) + spawner_entity = hydra.Entity(name) + spawner_entity.create_entity( + center_point, + ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"] + ) + if spawner_entity.id.IsValid(): + print(f"'{spawner_entity.name}' created") + spawner_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(box_size_x, box_size_y, + box_size_z)) + + # Set the vegetation area to a prefab instance spawner with a specific prefab asset selected + descriptor = hydra.get_component_property_value(spawner_entity.components[2], + 'Configuration|Embedded Assets|[0]') + prefab_spawner = vegetation.PrefabInstanceSpawner() + prefab_spawner.SetPrefabAssetId(prefab_asset_id) + descriptor.spawner = prefab_spawner + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor) + return spawner_entity + + def create_blocker_area(name, center_point, box_size_x, box_size_y, box_size_z): # Create a Vegetation Layer Blocker area blocker_entity = hydra.Entity(name) @@ -162,6 +188,23 @@ def create_blocker_area(name, center_point, box_size_x, box_size_y, box_size_z): return blocker_entity +def create_empty_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z): + # Create a vegetation area entity to use as our test vegetation spawner + spawner_entity = EditorEntity.create_editor_entity_at(center_point, name=name) + spawner_entity.add_components(["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]) + if spawner_entity.id.IsValid(): + print(f"'{spawner_entity.get_name()}' created") + spawner_entity.components[1].set_component_property_value("Box Shape|Box Configuration|Dimensions", + math.Vector3(box_size_x, box_size_y, box_size_z)) + + # Set the vegetation area to an empty spawner + empty_spawner = vegetation.EmptyInstanceSpawner() + descriptor = spawner_entity.components[2].get_component_property_value("Configuration|Embedded Assets|[0]") + descriptor.spawner = empty_spawner + spawner_entity.components[2].set_component_property_value("Configuration|Embedded Assets|[0]", descriptor) + return spawner_entity + + def validate_instance_count(center, radius, num_expected): # Verify that we have the correct number of instances in the given area. This creates a bounding box based on a # sphere radius diff --git a/AutomatedTesting/ScriptCanvas/instance_counter.scriptcanvas b/AutomatedTesting/ScriptCanvas/instance_counter.scriptcanvas index 0e5524273c..4ef5f0a8d5 100644 --- a/AutomatedTesting/ScriptCanvas/instance_counter.scriptcanvas +++ b/AutomatedTesting/ScriptCanvas/instance_counter.scriptcanvas @@ -5,7 +5,7 @@ "ClassData": { "m_scriptCanvas": { "Id": { - "id": 3744110453276 + "id": 337666590525276931 }, "Name": "instance_counter", "Components": { @@ -87,7 +87,6 @@ ], "Datums": [ { - "isOverloadedStorage": false, "scriptCanvasType": { "m_type": 3 }, @@ -228,7 +227,6 @@ ], "Datums": [ { - "isOverloadedStorage": false, "scriptCanvasType": { "m_type": 1 }, @@ -237,7 +235,7 @@ "value": { "id": 2901262558 }, - "label": "Source" + "label": "EntityID: 0" } ], "methodType": 0, @@ -340,7 +338,6 @@ ], "Datums": [ { - "isOverloadedStorage": false, "scriptCanvasType": { "m_type": 3 }, @@ -587,26 +584,10 @@ "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}", "versionData": { "_grammarVersion": 1, - "_runtimeVersion": 1 + "_runtimeVersion": 1, + "_fileVersion": 1 }, "GraphCanvasData": [ - { - "Key": { - "id": 3744110453276 - }, - "Value": { - "ComponentData": { - "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { - "$type": "SceneComponentSaveData", - "ViewParams": { - "Scale": 0.9218543514249998, - "AnchorX": 50.98419189453125, - "AnchorY": -272.27728271484375 - } - } - } - } - }, { "Key": { "id": 3748405420572 @@ -757,6 +738,18 @@ } } } + }, + { + "Key": { + "id": 337666590525276931 + }, + "Value": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData" + } + } + } } ], "StatisticsHelper": { From b21878d04e78e02f430c660b4cc9c9c5bda361a8 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Fri, 21 Jan 2022 19:57:28 +0000 Subject: [PATCH 41/61] Removed unused variable Signed-off-by: Sergey Pereslavtsev --- .../PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp index 9a216b7d80..5f830acc8a 100644 --- a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp @@ -343,7 +343,6 @@ namespace PhysXEditorTests for (int sampleColumn = 0; sampleColumn < numColumns; ++sampleColumn) { physx::PxHeightFieldSample samplePhysX = heightfield->getSample(sampleRow, sampleColumn); - Physics::HeightMaterialPoint samplePhysics = samples[sampleRow * numColumns + sampleColumn]; auto [materialIndex0, materialIndex1] = PhysX::Utils::GetPhysXMaterialIndicesFromHeightfieldSamples(samples, sampleRow, sampleColumn, numRows, numColumns); EXPECT_EQ(samplePhysX.materialIndex0, materialIndex0); From a8d97cf8aa39f515c2f801e7959fdf163edcb866 Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Fri, 21 Jan 2022 12:02:20 -0800 Subject: [PATCH 42/61] Fix hi-res display issues in UI Editor (#6912) * Fix dpi scaling issues in UI Editor Signed-off-by: abrmich * Remove older method for getting dpi scaling factor Signed-off-by: abrmich * Remove direct call to devicePixelRatioF() Signed-off-by: abrmich * Use existing convenience function Signed-off-by: abrmich * Fix one more text size Signed-off-by: abrmich * Fix compile warning Signed-off-by: abrmich * Remove unnecessary typecast Signed-off-by: abrmich --- Gems/LyShine/Code/Editor/GuideHelpers.cpp | 5 +- Gems/LyShine/Code/Editor/QtHelpers.cpp | 14 ---- Gems/LyShine/Code/Editor/QtHelpers.h | 5 -- Gems/LyShine/Code/Editor/RulerWidget.cpp | 17 ++++- Gems/LyShine/Code/Editor/ViewportHelpers.cpp | 6 +- Gems/LyShine/Code/Editor/ViewportIcon.cpp | 2 +- .../Code/Editor/ViewportInteraction.cpp | 39 +++++----- .../LyShine/Code/Editor/ViewportInteraction.h | 2 +- Gems/LyShine/Code/Editor/ViewportWidget.cpp | 71 ++++++++++++------- Gems/LyShine/Code/Editor/ViewportWidget.h | 13 ++-- 10 files changed, 97 insertions(+), 77 deletions(-) diff --git a/Gems/LyShine/Code/Editor/GuideHelpers.cpp b/Gems/LyShine/Code/Editor/GuideHelpers.cpp index b6d7acc57a..0713782063 100644 --- a/Gems/LyShine/Code/Editor/GuideHelpers.cpp +++ b/Gems/LyShine/Code/Editor/GuideHelpers.cpp @@ -134,7 +134,7 @@ namespace GuideHelpers AZ::Matrix4x4 transform; EBUS_EVENT_ID_RESULT(transform, canvasEntityId, UiCanvasBus, GetCanvasToViewportMatrix); - AZ::Vector2 viewportSize(aznumeric_cast(viewport->size().width()), aznumeric_cast(viewport->size().height())); + AZ::Vector2 viewportSize = viewport->GetRenderViewportSize(); AZ::Color guideColor; EBUS_EVENT_ID_RESULT(guideColor, canvasEntityId, UiEditorCanvasBus, GetGuideColor); @@ -167,8 +167,7 @@ namespace GuideHelpers void DrawGhostGuideLine(Draw2dHelper& draw2d, AZ::EntityId canvasEntityId, bool guideIsVertical, ViewportWidget* viewport, const AZ::Vector2& canvasPoint) { AZ::Vector2 viewportPoint = CanvasHelpers::GetViewportPoint(canvasEntityId, canvasPoint); - - AZ::Vector2 viewportSize(aznumeric_cast(viewport->size().width()), aznumeric_cast(viewport->size().height())); + AZ::Vector2 viewportSize = viewport->GetRenderViewportSize(); // the line is drawn as the inverse of the background color AZ::Color guideColor(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/Gems/LyShine/Code/Editor/QtHelpers.cpp b/Gems/LyShine/Code/Editor/QtHelpers.cpp index d9147463ed..3b38894016 100644 --- a/Gems/LyShine/Code/Editor/QtHelpers.cpp +++ b/Gems/LyShine/Code/Editor/QtHelpers.cpp @@ -29,18 +29,4 @@ namespace QtHelpers bool inWidget = (localPos.x() >= 0 && localPos.x() < size.width() && localPos.y() >= 0 && localPos.y() < size.height()); return inWidget; } - - float GetHighDpiScaleFactor(const QWidget& widget) - { - return static_cast(QHighDpiScaling::factor(widget.windowHandle()->screen())); - } - - QSize GetDpiScaledViewportSize(const QWidget& widget) - { - float dpiScale = GetHighDpiScaleFactor(widget); - float width = ceilf(widget.size().width() * dpiScale); - float height = ceilf(widget.size().height() * dpiScale); - return QSize(static_cast(width), static_cast(height)); - } - } // namespace QtHelpers diff --git a/Gems/LyShine/Code/Editor/QtHelpers.h b/Gems/LyShine/Code/Editor/QtHelpers.h index 4a9e7091ff..1b5f561871 100644 --- a/Gems/LyShine/Code/Editor/QtHelpers.h +++ b/Gems/LyShine/Code/Editor/QtHelpers.h @@ -16,9 +16,4 @@ namespace QtHelpers AZ::Vector2 MapGlobalPosToLocalVector2(const QWidget* widget, const QPoint& pos); bool IsGlobalPosInWidget(const QWidget* widget, const QPoint& pos); - - float GetHighDpiScaleFactor(const QWidget& widget); - - QSize GetDpiScaledViewportSize(const QWidget& widget); - } // namespace QtHelpers diff --git a/Gems/LyShine/Code/Editor/RulerWidget.cpp b/Gems/LyShine/Code/Editor/RulerWidget.cpp index 0a9209ba38..c82a64ac14 100644 --- a/Gems/LyShine/Code/Editor/RulerWidget.cpp +++ b/Gems/LyShine/Code/Editor/RulerWidget.cpp @@ -61,6 +61,14 @@ void RulerWidget::paintEvent([[maybe_unused]] QPaintEvent* event) float scale = translationAndScale.scale; float translation = (m_orientation == Orientation::Horizontal) ? translationAndScale.translation.GetX() : translationAndScale.translation.GetY(); + // Convert back to qt widget coords for painting + float dpiScaleFactor = m_editorWindow->GetViewport()->WidgetToViewportFactor(); + if (dpiScaleFactor != 0.0f) + { + scale /= dpiScaleFactor; + translation /= dpiScaleFactor; + } + // If the viewport is really small then scale can be zero (or very close) which would cause a divide by zero in later math so we just don't paint anything const float epsilon = 0.00001f; if (scale < epsilon) @@ -87,7 +95,9 @@ void RulerWidget::paintEvent([[maybe_unused]] QPaintEvent* event) void RulerWidget::mousePressEvent(QMouseEvent* ev) { // start a drag interaction to create a guide - AZ::Vector2 viewportMousePos = QtHelpers::MapGlobalPosToLocalVector2(m_editorWindow->GetViewport(), ev->globalPos()); + AZ::Vector2 localMousePos = QtHelpers::MapGlobalPosToLocalVector2(m_editorWindow->GetViewport(), ev->globalPos()); + float dpiScaleFactor = m_editorWindow->GetViewport()->WidgetToViewportFactor(); + AZ::Vector2 viewportMousePos = localMousePos * dpiScaleFactor; bool isVertical = m_orientation == Orientation::Vertical; m_dragInteraction = new ViewportAddGuideInteraction(m_editorWindow, m_editorWindow->GetCanvas(), isVertical, viewportMousePos); } @@ -98,7 +108,10 @@ void RulerWidget::mouseMoveEvent(QMouseEvent* ev) // We only get the events if the mouse is pressed down. So we only get here when adding a ruler. if (m_dragInteraction) { - AZ::Vector2 viewportMousePos = QtHelpers::MapGlobalPosToLocalVector2(m_editorWindow->GetViewport(), ev->globalPos()); + AZ::Vector2 localMousePos = QtHelpers::MapGlobalPosToLocalVector2(m_editorWindow->GetViewport(), ev->globalPos()); + float dpiScaleFactor = m_editorWindow->GetViewport()->WidgetToViewportFactor(); + AZ::Vector2 viewportMousePos = localMousePos * dpiScaleFactor; + m_dragInteraction->Update(viewportMousePos); } diff --git a/Gems/LyShine/Code/Editor/ViewportHelpers.cpp b/Gems/LyShine/Code/Editor/ViewportHelpers.cpp index 7d81a12477..ee89b213ef 100644 --- a/Gems/LyShine/Code/Editor/ViewportHelpers.cpp +++ b/Gems/LyShine/Code/Editor/ViewportHelpers.cpp @@ -336,7 +336,7 @@ namespace ViewportHelpers draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); - draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, GetDpiScaledSize(16.0f), 1.0f); + draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, 8.0f, 1.0f); } } @@ -347,9 +347,11 @@ namespace ViewportHelpers const AZ::Vector2 textLabelOffset(10.0f, -10.0f); QPoint viewportCursorPos = viewport->mapFromGlobal(QCursor::pos()); AZ::Vector2 textPos = AZ::Vector2(aznumeric_cast(viewportCursorPos.x()), aznumeric_cast(viewportCursorPos.y())) + textLabelOffset; + float dpiScale = viewport->WidgetToViewportFactor(); + textPos *= dpiScale; draw2d.SetTextAlignment(IDraw2d::HAlign::Left, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); - draw2d.DrawText(textLabel.c_str(), textPos, GetDpiScaledSize(16.0f), 1.0f); + draw2d.DrawText(textLabel.c_str(), textPos, 8.0f, 1.0f); } } // namespace ViewportHelpers diff --git a/Gems/LyShine/Code/Editor/ViewportIcon.cpp b/Gems/LyShine/Code/Editor/ViewportIcon.cpp index 33f2f40ffe..bdbad6afb2 100644 --- a/Gems/LyShine/Code/Editor/ViewportIcon.cpp +++ b/Gems/LyShine/Code/Editor/ViewportIcon.cpp @@ -296,7 +296,7 @@ void ViewportIcon::DrawDistanceLine(Draw2dHelper& draw2d, AZ::Vector2 start, AZ: draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(rotation); - draw2d.DrawText(textBuf, textPos, 16.0f * ViewportIcon::GetDpiScaleFactor(), 1.0f); + draw2d.DrawText(textBuf, textPos, 8.0f, 1.0f); } void ViewportIcon::DrawAnchorLinesSplit(Draw2dHelper& draw2d, AZ::Vector2 anchorPos1, AZ::Vector2 anchorPos2, diff --git a/Gems/LyShine/Code/Editor/ViewportInteraction.cpp b/Gems/LyShine/Code/Editor/ViewportInteraction.cpp index a39a7b9cac..7bd55be4bc 100644 --- a/Gems/LyShine/Code/Editor/ViewportInteraction.cpp +++ b/Gems/LyShine/Code/Editor/ViewportInteraction.cpp @@ -674,13 +674,13 @@ void ViewportInteraction::MouseReleaseEvent(QMouseEvent* ev, { // test to see if the mouse position is inside the viewport on each axis const QPoint& pos = ev->pos(); - const QSize& size = m_editorWindow->GetViewport()->size(); + const AZ::Vector2 size = m_editorWindow->GetViewport()->GetRenderViewportSize(); ViewportDragInteraction::EndState inWidget; - if (pos.x() >= 0 && pos.x() < size.width()) - inWidget = pos.y() >= 0 && pos.y() < size.height() ? ViewportDragInteraction::EndState::Inside : ViewportDragInteraction::EndState::OutsideY; + if (pos.x() >= 0 && pos.x() < size.GetX()) + inWidget = pos.y() >= 0 && pos.y() < size.GetY() ? ViewportDragInteraction::EndState::Inside : ViewportDragInteraction::EndState::OutsideY; else - inWidget = pos.y() >= 0 && pos.y() < size.height() ? ViewportDragInteraction::EndState::OutsideX : ViewportDragInteraction::EndState::OutsideXY; + inWidget = pos.y() >= 0 && pos.y() < size.GetY() ? ViewportDragInteraction::EndState::OutsideX : ViewportDragInteraction::EndState::OutsideXY; // Some interactions end differently depending on whether the mouse was released inside or outside the viewport m_dragInteraction->EndInteraction(inWidget); @@ -709,12 +709,12 @@ void ViewportInteraction::MouseReleaseEvent(QMouseEvent* ev, UpdateCursor(); } -void ViewportInteraction::MouseWheelEvent(QWheelEvent* ev) +bool ViewportInteraction::MouseWheelEvent(QWheelEvent* ev) { if (m_leftButtonIsActive || m_middleButtonIsActive) { // Ignore event. - return; + return false; } const QPoint numDegrees(ev->angleDelta()); @@ -735,6 +735,8 @@ void ViewportInteraction::MouseWheelEvent(QWheelEvent* ev) SetCanvasToViewportScale(QuantizeZoomScale(newScale), &pivotPoint); } + + return true; } bool ViewportInteraction::KeyPressEvent(QKeyEvent* ev) @@ -921,27 +923,27 @@ void ViewportInteraction::GetScaleToFitTransformProps(const AZ::Vector2* newCanv EBUS_EVENT_ID_RESULT(canvasSize, m_editorWindow->GetCanvas(), UiCanvasBus, GetCanvasSize); } - QSize viewportSize = QtHelpers::GetDpiScaledViewportSize(*m_editorWindow->GetViewport()); - const int viewportWidth = viewportSize.width(); - const int viewportHeight = viewportSize.height(); + AZ::Vector2 viewportSize = m_editorWindow->GetViewport()->GetRenderViewportSize(); + const float viewportWidth = viewportSize.GetX(); + const float viewportHeight = viewportSize.GetY(); // We pad the edges of the viewport to allow the user to easily see the borders of // the canvas edges, which is especially helpful if there are anchors sitting on // the edges of the canvas. static const int canvasBorderPaddingInPixels = 32; AZ::Vector2 viewportPaddedSize( - aznumeric_cast(viewportWidth - canvasBorderPaddingInPixels), - aznumeric_cast(viewportHeight - canvasBorderPaddingInPixels)); + viewportWidth - canvasBorderPaddingInPixels, + viewportHeight - canvasBorderPaddingInPixels); // Guard against very small viewports if (viewportPaddedSize.GetX() <= 0.0f) { - viewportPaddedSize.SetX(aznumeric_cast(viewportWidth)); + viewportPaddedSize.SetX(viewportWidth); } if (viewportPaddedSize.GetY() <= 0.0f) { - viewportPaddedSize.SetY(aznumeric_cast(viewportHeight)); + viewportPaddedSize.SetY(viewportHeight); } // Use a "scale to fit" approach @@ -949,8 +951,8 @@ void ViewportInteraction::GetScaleToFitTransformProps(const AZ::Vector2* newCanv viewportPaddedSize.GetX() / canvasSize.GetX(), viewportPaddedSize.GetY() / canvasSize.GetY()); - const int scaledCanvasWidth = static_cast(canvasSize.GetX() * canvasToViewportScale); - const int scaledCanvasHeight = static_cast(canvasSize.GetY() * canvasToViewportScale); + const float scaledCanvasWidth = canvasSize.GetX() * canvasToViewportScale; + const float scaledCanvasHeight = canvasSize.GetY() * canvasToViewportScale; // Centers the canvas within the viewport propsOut.translation = AZ::Vector3( @@ -1008,9 +1010,12 @@ void ViewportInteraction::SetCanvasToViewportScale(float newScale, Vec2i* option } else { + AZ::Vector2 viewportSize = m_editorWindow->GetViewport()->GetRenderViewportSize(); + const float viewportWidth = viewportSize.GetX(); + const float viewportHeight = viewportSize.GetY(); pivotPoint = Vec2i( - static_cast(m_editorWindow->GetViewport()->size().width() * 0.5f), - static_cast(m_editorWindow->GetViewport()->size().height() * 0.5f)); + static_cast(viewportWidth * 0.5f), + static_cast(viewportHeight * 0.5f)); } // Get the distance between our pivot point and the upper-left corner of the diff --git a/Gems/LyShine/Code/Editor/ViewportInteraction.h b/Gems/LyShine/Code/Editor/ViewportInteraction.h index a104d87634..b6417977ab 100644 --- a/Gems/LyShine/Code/Editor/ViewportInteraction.h +++ b/Gems/LyShine/Code/Editor/ViewportInteraction.h @@ -77,7 +77,7 @@ public: // member functions const QTreeWidgetItemRawPtrQList& selectedItems); void MouseReleaseEvent(QMouseEvent* ev, const QTreeWidgetItemRawPtrQList& selectedItems); - void MouseWheelEvent(QWheelEvent* ev); + bool MouseWheelEvent(QWheelEvent* ev); bool KeyPressEvent(QKeyEvent* ev); bool KeyReleaseEvent(QKeyEvent* ev); diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.cpp b/Gems/LyShine/Code/Editor/ViewportWidget.cpp index b481e9cbf1..eb5625ac31 100644 --- a/Gems/LyShine/Code/Editor/ViewportWidget.cpp +++ b/Gems/LyShine/Code/Editor/ViewportWidget.cpp @@ -351,6 +351,17 @@ void ViewportWidget::SetRedrawEnabled(bool enabled) m_canvasRenderIsEnabled = enabled; } +AZ::Vector2 ViewportWidget::GetRenderViewportSize() const +{ + AZ::Vector2 widgetSize(aznumeric_cast(size().width()), aznumeric_cast(size().height())); + return widgetSize * WidgetToViewportFactor(); +} + +float ViewportWidget::WidgetToViewportFactor() const +{ + return GetViewportContext()->GetDpiScalingFactor(); +} + void ViewportWidget::PickItem(AZ::EntityId entityId) { AzToolsFramework::EditorPickModeRequestBus::Broadcast( @@ -496,7 +507,7 @@ void ViewportWidget::OnRenderTick() return; } - const float dpiScale = QtHelpers::GetHighDpiScaleFactor(*this); + const float dpiScale = WidgetToViewportFactor(); ViewportIcon::SetDpiScaleFactor(dpiScale); UiEditorMode editorMode = m_editorWindow->GetEditorMode(); @@ -510,6 +521,12 @@ void ViewportWidget::OnRenderTick() } } +//////////////////////////////////////////////////////////////////////////////////////////////////// +void ViewportWidget::OnViewportDpiScalingChanged(float dpiScale) +{ + ViewportIcon::SetDpiScaleFactor(dpiScale); +} + void ViewportWidget::RefreshTick() { #ifdef LYSHINE_EDITOR_TODO // still need this? @@ -640,27 +657,34 @@ void ViewportWidget::mouseReleaseEvent(QMouseEvent* ev) void ViewportWidget::wheelEvent(QWheelEvent* ev) { + bool handled = false; UiEditorMode editorMode = m_editorWindow->GetEditorMode(); - QWheelEvent scaledEvent( - WidgetToViewport(ev->position()), - ev->globalPosition(), - ev->pixelDelta(), - ev->angleDelta(), - ev->buttons(), - ev->modifiers(), - ev->phase(), - ev->inverted() - ); - if (editorMode == UiEditorMode::Edit) { + QWheelEvent scaledEvent( + WidgetToViewport(ev->position()), + ev->globalPosition(), + ev->pixelDelta(), + ev->angleDelta(), + ev->buttons(), + ev->modifiers(), + ev->phase(), + ev->inverted() + ); + // in Edit mode just send input to ViewportInteraction - m_viewportInteraction->MouseWheelEvent(&scaledEvent); + handled = m_viewportInteraction->MouseWheelEvent(&scaledEvent); } - RenderViewportWidget::wheelEvent(ev); - - Refresh(); + if (handled) + { + ev->accept(); + Refresh(); + } + else + { + RenderViewportWidget::wheelEvent(ev); + } } bool ViewportWidget::eventFilter([[maybe_unused]] QObject* watched, QEvent* event) @@ -975,8 +999,7 @@ void ViewportWidget::RenderEditMode() EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, false, canvasSize); // Render this canvas - QSize scaledViewportSize = QtHelpers::GetDpiScaledViewportSize(*this); - AZ::Vector2 viewportSize(static_cast(scaledViewportSize.width()), static_cast(scaledViewportSize.height())); + AZ::Vector2 viewportSize = GetRenderViewportSize(); EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, RenderCanvasInEditorViewport, false, viewportSize); m_draw2d->SetSortKey(topLayerKey); @@ -1085,15 +1108,12 @@ void ViewportWidget::UpdatePreviewMode(float deltaTime) if (canvasEntityId.IsValid()) { - QSize scaledViewportSize = QtHelpers::GetDpiScaledViewportSize(*this); - AZ::Vector2 viewportSize(static_cast(scaledViewportSize.width()), static_cast(scaledViewportSize.height())); - // Get the canvas size AZ::Vector2 canvasSize = m_editorWindow->GetPreviewCanvasSize(); if (canvasSize.GetX() == 0.0f && canvasSize.GetY() == 0.0f) { // special value of (0,0) means use the viewport size - canvasSize = viewportSize; + canvasSize = GetRenderViewportSize();; } // Set the target size of the canvas @@ -1127,8 +1147,7 @@ void ViewportWidget::RenderPreviewMode() if (canvasEntityId.IsValid()) { - QSize scaledViewportSize = QtHelpers::GetDpiScaledViewportSize(*this); - AZ::Vector2 viewportSize(static_cast(scaledViewportSize.width()), static_cast(scaledViewportSize.height())); + AZ::Vector2 viewportSize = GetRenderViewportSize(); // Get the canvas size AZ::Vector2 canvasSize = m_editorWindow->GetPreviewCanvasSize(); @@ -1208,13 +1227,13 @@ void ViewportWidget::RenderPreviewMode() void ViewportWidget::RenderViewportBackground() { - QSize viewportSize = QtHelpers::GetDpiScaledViewportSize(*this); + AZ::Vector2 viewportSize = GetRenderViewportSize(); AZ::Color backgroundColor = ViewportHelpers::backgroundColorDark; const AZ::Data::Instance& image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); Draw2dHelper draw2d(m_draw2d.get()); draw2d.SetImageColor(backgroundColor.GetAsVector3()); - draw2d.DrawImage(image, AZ::Vector2(0.0f, 0.0f), AZ::Vector2(static_cast(viewportSize.width()), static_cast(viewportSize.height()))); + draw2d.DrawImage(image, AZ::Vector2(0.0f, 0.0f), viewportSize); } void ViewportWidget::SetupShortcuts() diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.h b/Gems/LyShine/Code/Editor/ViewportWidget.h index 722335aa93..c285e0d969 100644 --- a/Gems/LyShine/Code/Editor/ViewportWidget.h +++ b/Gems/LyShine/Code/Editor/ViewportWidget.h @@ -72,6 +72,12 @@ public: // member functions //! Used by ViewportInteraction for drawing ViewportHighlight* GetViewportHighlight() { return m_viewportHighlight.get(); } + //! Get the size of the RPI render viewport + AZ::Vector2 GetRenderViewportSize() const; + + //! Get the widget to viewport scale factor + float WidgetToViewportFactor() const; + bool IsInObjectPickMode() { return m_inObjectPickMode; } void PickItem(AZ::EntityId entityId); @@ -156,6 +162,7 @@ private: // member functions // AZ::RPI::ViewportContextNotificationBus::Handler overrides... void OnRenderTick() override; + void OnViewportDpiScalingChanged(float dpiScale) override; //! Update UI canvases when in edit mode void UpdateEditMode(float deltaTime); @@ -187,12 +194,6 @@ private: // data bool AcceptsMimeData(const QMimeData* mimeData); - double WidgetToViewportFactor() const - { - // Needed for high DPI mode on windows - return devicePixelRatioF(); - } - QPointF WidgetToViewport(const QPointF &point) const; EditorWindow* m_editorWindow; From 7fc4e4bd105ab53ec2ea990fa055b581da97d666 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 21 Jan 2022 14:29:37 -0600 Subject: [PATCH 43/61] Refactored the styling of TableView header sections Signed-off-by: Chris Galvan --- .../AzQtComponents/Components/Style.cpp | 12 ------------ .../Components/Widgets/TableView.cpp | 18 ------------------ .../Components/Widgets/TableView.h | 2 -- .../Components/Widgets/TableView.qss | 2 +- .../AzQtComponents/Gallery/TableViewPage.cpp | 3 +++ 5 files changed, 4 insertions(+), 33 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp index ef29c05e61..849785ff01 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp @@ -411,18 +411,6 @@ namespace AzQtComponents } break; - case CE_HeaderSection: - { - if (qobject_cast(widget)) - { - if (TableView::drawHeaderSection(this, option, painter, widget, m_data->tableViewConfig)) - { - return; - } - } - } - break; - case CE_ComboBoxLabel: { if (qobject_cast(widget)) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.cpp index f5cb90ae1a..b4b49bdcec 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.cpp @@ -38,7 +38,6 @@ namespace AzQtComponents ConfigHelpers::read(settings, QStringLiteral("FocusBorderWidth"), config.focusBorderWidth); ConfigHelpers::read(settings, QStringLiteral("FocusBorderColor"), config.focusBorderColor); ConfigHelpers::read(settings, QStringLiteral("FocusFillColor"), config.focusFillColor); - ConfigHelpers::read(settings, QStringLiteral("HeaderFillColor"), config.headerFillColor); settings.endGroup(); return config; @@ -52,7 +51,6 @@ namespace AzQtComponents config.focusBorderWidth = 1; config.focusBorderColor = QStringLiteral("#94D2FF"); config.focusFillColor = QStringLiteral("#10ffffff"); - config.headerFillColor = QStringLiteral("#2d2d2d"); return config; } @@ -177,22 +175,6 @@ namespace AzQtComponents return true; } - bool TableView::drawHeaderSection(const Style* style, const QStyleOption* option, QPainter* painter, const QWidget* widget, const Config& config) - { - Q_UNUSED(widget); - Q_UNUSED(style); - - const auto headerViewOption = qstyleoption_cast(option); - if (!headerViewOption) - { - return false; - } - - painter->fillRect(headerViewOption->rect, config.headerFillColor); - - return true; - } - bool TableView::drawFrameFocusRect(const Style* style, const QStyleOption* option, QPainter* painter, const Config& config) { Q_UNUSED(style); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.h index 99fb77c3d8..6187f44dd5 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.h @@ -62,7 +62,6 @@ namespace AzQtComponents qreal focusBorderWidth; QColor focusBorderColor; QColor focusFillColor; - QColor headerFillColor; }; /*! @@ -93,7 +92,6 @@ namespace AzQtComponents private: static bool drawHeader(const Style* style, const QStyleOption* option, QPainter* painter, const QWidget* widget, const Config& config); - static bool drawHeaderSection(const Style* style, const QStyleOption* option, QPainter* painter, const QWidget* widget, const Config& config); static bool drawFrameFocusRect(const Style* style, const QStyleOption* option, QPainter* painter, const Config& config); static QRect itemViewItemRect(const Style* style, QStyle::SubElement element, const QStyleOptionViewItem* option, const QWidget* widget, const Config& config); static QSize sizeFromContents(const Style* style, QStyle::ContentsType type, const QStyleOption* option, const QSize& contentsSize, const QWidget* widget, const Config& config); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.qss b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.qss index 47281eda7e..531a3378fa 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.qss +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.qss @@ -66,7 +66,7 @@ QTreeView QHeaderView::section { height: 24px; - background: #444444; + background: #2d2d2d; border: none; font-size: 12px; diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Gallery/TableViewPage.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Gallery/TableViewPage.cpp index 788ba62366..24e65ac540 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Gallery/TableViewPage.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Gallery/TableViewPage.cpp @@ -134,6 +134,9 @@ TableViewPage::TableViewPage(QWidget* parent) auto logItemDelegate = new AzToolsFramework::Logging::LogTableItemDelegate(ui->logTableView); ui->logTableView->setItemDelegate(logItemDelegate); + // Example of changing the header section background color + ui->logTableView->setStyleSheet("QHeaderView::section { background: transparent; }"); + ui->qTableView->setModel(logModel); ui->qTableView->setAlternatingRowColors(true); ui->qTableView->setShowGrid(false); From 86a0be256135d047fe82f4041283e77a13c73dba Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Fri, 21 Jan 2022 12:35:29 -0800 Subject: [PATCH 44/61] Prevent read-only entities from being duplicated via prefab workflows (#7076) * Refactor the sanitation of the selection for prefab operations to prevent read-only entities from being deleted or duplicated. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Fix comment. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabPublicHandler.cpp | 45 ++++++++----------- .../Prefab/PrefabPublicHandler.h | 5 ++- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index bde5de2f64..0c1fb9bed2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -1063,7 +1063,7 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("No entities to duplicate.")); } - const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); + const EntityIdList entityIdsNoFocusContainer = SanitizeEntityIdList(entityIds); if (entityIdsNoFocusContainer.empty()) { return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the container entity of the focused instance.")); @@ -1202,7 +1202,7 @@ namespace AzToolsFramework PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants) { // Remove the container entity of the focused prefab from the list, if it is included. - const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); + const EntityIdList entityIdsNoFocusContainer = SanitizeEntityIdList(entityIds); if (entityIdsNoFocusContainer.empty()) { @@ -1236,27 +1236,6 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("Cannot delete entities belonging to an instance that is not being edited.")); } - // None of the specified entities can be marked as read only, otherwise this operation is invalid. - if (auto readOnlyEntityPublicInterface = AZ::Interface::Get()) - { - AZ::EntityId readOnlyEntityId; - for (const auto& entityId : entityIdsNoFocusContainer) - { - if (readOnlyEntityPublicInterface->IsReadOnly(entityId)) - { - readOnlyEntityId = entityId; - break; - } - } - - if (readOnlyEntityId.IsValid()) - { - return AZ::Failure(AZStd::string::format( - "Cannot delete entities because entity (id '%llu') is marked as read only.", - static_cast(readOnlyEntityId))); - } - } - // Retrieve entityList from entityIds EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoFocusContainer); @@ -1680,10 +1659,24 @@ namespace AzToolsFramework return AZ::Success(); } - EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutFocusedInstanceContainer( - const EntityIdList& entityIds) const + EntityIdList PrefabPublicHandler::SanitizeEntityIdList(const EntityIdList& entityIds) const { - EntityIdList outEntityIds(entityIds); + EntityIdList outEntityIds; + + if (auto readOnlyEntityPublicInterface = AZ::Interface::Get()) + { + std::copy_if( + entityIds.begin(), entityIds.end(), AZStd::back_inserter(outEntityIds), + [readOnlyEntityPublicInterface](const AZ::EntityId& entityId) + { + return !readOnlyEntityPublicInterface->IsReadOnly(entityId); + } + ); + } + else + { + outEntityIds = entityIds; + } AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); AZ::EntityId focusedInstanceContainerEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 41c9b4a292..a1a7ac9844 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -75,7 +75,10 @@ namespace AzToolsFramework Instance& commonRootEntityOwningInstance, EntityList& outEntities, AZStd::vector& outInstances) const; - EntityIdList GenerateEntityIdListWithoutFocusedInstanceContainer(const EntityIdList& entityIds) const; + + //! Sanitizes an EntityIdList to remove entities that should not be affected by prefab operations. + //! It will identify and exclude the container entity of the root prefab instance, and all read-only entities. + EntityIdList SanitizeEntityIdList(const EntityIdList& entityIds) const; InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; From 7fa6a82f550a4ca687b1a7742948859ad472af4f Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Fri, 21 Jan 2022 15:03:57 -0800 Subject: [PATCH 45/61] Make ILyShine an AZ::Interface (#6923) * Move ILyShine instance out of legacy code Signed-off-by: abrmich * Remove last remaining reference of gEnv->pLyShine Signed-off-by: abrmich * Make m_lyShine a unique pointer Signed-off-by: abrmich --- Code/Legacy/CryCommon/ISystem.h | 1 - .../Editor/CommandCanvasPropertiesChange.cpp | 2 +- Gems/LyShine/Code/Editor/EditorWindow.cpp | 10 ++++---- .../Editor/LyShineEditorSystemComponent.cpp | 4 +-- .../Code/Editor/SpriteBorderEditor.cpp | 3 ++- Gems/LyShine/Code/Editor/ViewportWidget.cpp | 10 ++++---- Gems/LyShine/Code/Include/LyShine/IDraw2d.h | 8 +++--- Gems/LyShine/Code/Include/LyShine/ILyShine.h | 5 +++- Gems/LyShine/Code/Source/LyShine.cpp | 14 +++++------ Gems/LyShine/Code/Source/LyShine.h | 2 +- Gems/LyShine/Code/Source/LyShineDebug.cpp | 8 +++--- .../LyShine/Code/Source/LyShineLoadScreen.cpp | 25 ++++++++++--------- .../Code/Source/LyShineSystemComponent.cpp | 20 ++++++++------- .../Code/Source/LyShineSystemComponent.h | 2 +- .../Code/Source/Script/UiCanvasLuaBus.cpp | 4 +-- .../LyShine/Code/Source/UiCanvasComponent.cpp | 8 +++--- Gems/LyShine/Code/Source/UiImageComponent.cpp | 12 ++++----- .../Code/Source/UiImageSequenceComponent.cpp | 4 +-- .../Code/Source/UiInteractableState.cpp | 8 +++--- .../Source/UiParticleEmitterComponent.cpp | 6 ++--- .../World/UiCanvasAssetRefComponent.cpp | 9 ++++--- .../Code/Tests/UiTooltipComponentTest.cpp | 5 ---- .../Code/Source/LyShineExamplesCppExample.cpp | 4 +-- .../Code/Source/UiCustomImageComponent.cpp | 6 ++--- .../Code/Source/LyShineMessagePopup.cpp | 12 ++++----- 25 files changed, 97 insertions(+), 95 deletions(-) diff --git a/Code/Legacy/CryCommon/ISystem.h b/Code/Legacy/CryCommon/ISystem.h index 6fdc8b52fe..14b78fd155 100644 --- a/Code/Legacy/CryCommon/ISystem.h +++ b/Code/Legacy/CryCommon/ISystem.h @@ -614,7 +614,6 @@ struct SSystemGlobalEnvironment ISystem* pSystem = nullptr; ILog* pLog; IMovieSystem* pMovieSystem; - ILyShine* pLyShine; SharedEnvironmentInstance* pSharedEnvironment; #if defined(AZ_RESTRICTED_PLATFORM) diff --git a/Gems/LyShine/Code/Editor/CommandCanvasPropertiesChange.cpp b/Gems/LyShine/Code/Editor/CommandCanvasPropertiesChange.cpp index 30a716b142..e90252e54a 100644 --- a/Gems/LyShine/Code/Editor/CommandCanvasPropertiesChange.cpp +++ b/Gems/LyShine/Code/Editor/CommandCanvasPropertiesChange.cpp @@ -64,7 +64,7 @@ void CommandCanvasPropertiesChange::Recreate(bool isUndo) // Create a new canvas from the XML and release the old canvas, use the new entity context for // the new canvas const AZStd::string& xml = isUndo ? m_undoXml : m_redoXml; - gEnv->pLyShine->ReloadCanvasFromXml(xml, newEntityContext); + AZ::Interface::Get()->ReloadCanvasFromXml(xml, newEntityContext); // Tell the editor window to use the new entity context m_editorWindow->ReplaceEntityContext(newEntityContext); diff --git a/Gems/LyShine/Code/Editor/EditorWindow.cpp b/Gems/LyShine/Code/Editor/EditorWindow.cpp index 7b60474ed5..8f1d574ae5 100644 --- a/Gems/LyShine/Code/Editor/EditorWindow.cpp +++ b/Gems/LyShine/Code/Editor/EditorWindow.cpp @@ -315,7 +315,7 @@ EditorWindow::~EditorWindow() // unload the preview mode canvas if it exists (e.g. if we close the editor window while in preview mode) if (m_previewModeCanvasEntityId.IsValid()) { - gEnv->pLyShine->ReleaseCanvas(m_previewModeCanvasEntityId, false); + AZ::Interface::Get()->ReleaseCanvas(m_previewModeCanvasEntityId, false); } delete m_sliceLibraryTree; @@ -564,7 +564,7 @@ bool EditorWindow::OnPreWarning(const char* /*window*/, const char* /*fileName*/ void EditorWindow::DestroyCanvas(const UiCanvasMetadata& canvasMetadata) { - gEnv->pLyShine->ReleaseCanvas(canvasMetadata.m_canvasEntityId, true); + AZ::Interface::Get()->ReleaseCanvas(canvasMetadata.m_canvasEntityId, true); } bool EditorWindow::IsCanvasTabMetadataValidForTabIndex(int index) @@ -950,14 +950,14 @@ bool EditorWindow::LoadCanvas(const QString& canvasFilename, bool autoLoad, bool bool errorsOnLoad = false; if (canvasFilename.isEmpty()) { - canvasEntityId = gEnv->pLyShine->CreateCanvasInEditor(entityContext); + canvasEntityId = AZ::Interface::Get()->CreateCanvasInEditor(entityContext); } else { // Collect errors and warnings during the canvas load AZ::Debug::TraceMessageBus::Handler::BusConnect(); - canvasEntityId = gEnv->pLyShine->LoadCanvasInEditor(assetIdPathname.c_str(), sourceAssetPathName.c_str(), entityContext); + canvasEntityId = AZ::Interface::Get()->LoadCanvasInEditor(assetIdPathname.c_str(), sourceAssetPathName.c_str(), entityContext); // Stop receiving error and warning events AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); @@ -1603,7 +1603,7 @@ void EditorWindow::ToggleEditorMode() EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, m_previewModeCanvasEntityId); if (entity) { - gEnv->pLyShine->ReleaseCanvas(m_previewModeCanvasEntityId, false); + AZ::Interface::Get()->ReleaseCanvas(m_previewModeCanvasEntityId, false); } m_previewModeCanvasEntityId.SetInvalid(); } diff --git a/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp b/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp index 229101d4c0..3ec6980cfb 100644 --- a/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp +++ b/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp @@ -211,9 +211,9 @@ namespace LyShineEditor void LyShineEditorSystemComponent::OnStopPlayInEditor() { // reset UI system - if (gEnv->pLyShine) + if (AZ::Interface::Get()) { - gEnv->pLyShine->Reset(); + AZ::Interface::Get()->Reset(); } } } diff --git a/Gems/LyShine/Code/Editor/SpriteBorderEditor.cpp b/Gems/LyShine/Code/Editor/SpriteBorderEditor.cpp index 93d77bd174..b9ca4cd32c 100644 --- a/Gems/LyShine/Code/Editor/SpriteBorderEditor.cpp +++ b/Gems/LyShine/Code/Editor/SpriteBorderEditor.cpp @@ -7,6 +7,7 @@ */ #include "SpriteBorderEditorCommon.h" +#include #include #include #include @@ -35,7 +36,7 @@ SpriteBorderEditor::SpriteBorderEditor(const char* path, QWidget* parent) setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint); // Make sure the sprite can load and be displayed before continuing - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePath.toLatin1().constData()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePath.toLatin1().constData()); QString fullPath = Path::GamePathToFullPath(m_sprite->GetTexturePathname().c_str()); const bool imageWontLoad = !m_sprite || QPixmap(fullPath).isNull(); if (imageWontLoad) diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.cpp b/Gems/LyShine/Code/Editor/ViewportWidget.cpp index eb5625ac31..32231058bf 100644 --- a/Gems/LyShine/Code/Editor/ViewportWidget.cpp +++ b/Gems/LyShine/Code/Editor/ViewportWidget.cpp @@ -181,7 +181,7 @@ namespace EBUS_EVENT_ID_RESULT(handled, canvasEntityId, UiCanvasBus, HandleInputEvent, inputSnapshot, viewportPos, activeModifierKeys); // Execute events that have been queued during the input event handler - gEnv->pLyShine->ExecuteQueuedEvents(); + AZ::Interface::Get()->ExecuteQueuedEvents(); return handled; } @@ -192,7 +192,7 @@ namespace EBUS_EVENT_ID_RESULT(handled, canvasEntityId, UiCanvasBus, HandleTextEvent, textUTF8); // Execute events that have been queued during the input event handler - gEnv->pLyShine->ExecuteQueuedEvents(); + AZ::Interface::Get()->ExecuteQueuedEvents(); return handled; } @@ -265,7 +265,7 @@ ViewportWidget::~ViewportWidget() // Notify LyShine that this is no longer a valid UiRenderer. // Only one viewport/renderer is currently supported in the UI Editor - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); lyShine->SetUiRendererForEditor(nullptr); } @@ -276,7 +276,7 @@ void ViewportWidget::InitUiRenderer() // Notify LyShine that this is the UiRenderer to be used for rendering // UI canvases that are loaded in the UI Editor. // Only one viewport/renderer is currently supported in the UI Editor - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); lyShine->SetUiRendererForEditor(m_uiRenderer); m_draw2d = AZStd::make_shared(GetViewportContext()); @@ -1123,7 +1123,7 @@ void ViewportWidget::UpdatePreviewMode(float deltaTime) EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, true); // Execute events that have been queued during the canvas update - gEnv->pLyShine->ExecuteQueuedEvents(); + AZ::Interface::Get()->ExecuteQueuedEvents(); } } diff --git a/Gems/LyShine/Code/Include/LyShine/IDraw2d.h b/Gems/LyShine/Code/Include/LyShine/IDraw2d.h index 77dc5ac601..e880ca54a7 100644 --- a/Gems/LyShine/Code/Include/LyShine/IDraw2d.h +++ b/Gems/LyShine/Code/Include/LyShine/IDraw2d.h @@ -473,9 +473,9 @@ public: // static member functions //! Helper to get the default IDraw2d interface static IDraw2d* GetDefaultDraw2d() { - if (gEnv && gEnv->pLyShine) // [LYSHINE_ATOM_TODO][GHI #3569] Remove LyShine global interface pointer from legacy global environment + if (AZ::Interface::Get()) { - IDraw2d* draw2d = gEnv->pLyShine->GetDraw2d(); + IDraw2d* draw2d = AZ::Interface::Get()->GetDraw2d(); return reinterpret_cast(draw2d); } @@ -485,9 +485,9 @@ public: // static member functions //! Helper to load a texture static AZ::Data::Instance LoadTexture(const AZStd::string& pathName) { - if (gEnv && gEnv->pLyShine) // [LYSHINE_ATOM_TODO][GHI #3569] Remove LyShine global interface pointer from legacy global environment + if (AZ::Interface::Get()) { - return gEnv->pLyShine->LoadTexture(pathName); + return AZ::Interface::Get()->LoadTexture(pathName); } return nullptr; diff --git a/Gems/LyShine/Code/Include/LyShine/ILyShine.h b/Gems/LyShine/Code/Include/LyShine/ILyShine.h index 71ca6ec074..d05e105c87 100644 --- a/Gems/LyShine/Code/Include/LyShine/ILyShine.h +++ b/Gems/LyShine/Code/Include/LyShine/ILyShine.h @@ -38,7 +38,10 @@ namespace AZ::RPI class ILyShine { public: - virtual ~ILyShine(){} + AZ_RTTI(ILyShine, "{652ED9D7-0782-44E8-BCE7-65DD38C90907}"); + + ILyShine() = default; + virtual ~ILyShine() = default; //! Delete this object virtual void Release() = 0; diff --git a/Gems/LyShine/Code/Source/LyShine.cpp b/Gems/LyShine/Code/Source/LyShine.cpp index b362ed2106..df26a905ad 100644 --- a/Gems/LyShine/Code/Source/LyShine.cpp +++ b/Gems/LyShine/Code/Source/LyShine.cpp @@ -124,7 +124,7 @@ AllocateConstIntCVar(CLyShine, CV_ui_RunUnitTestsOnStartup); #endif //////////////////////////////////////////////////////////////////////////////////////////////////// -CLyShine::CLyShine([[maybe_unused]] ISystem* system) +CLyShine::CLyShine() : AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityUI()) , AzFramework::InputTextEventListener(AzFramework::InputTextEventListener::GetPriorityUI()) , m_draw2d(new CDraw2d) @@ -386,8 +386,8 @@ void CLyShine::Update(float deltaTimeInSeconds) } // Tell the UI system the size of the viewport we are rendering to - this drives the - // canvas size for full screen UI canvases. It needs to be set before either pLyShine->Update or - // pLyShine->Render are called. It must match the viewport size that the input system is using. + // canvas size for full screen UI canvases. It needs to be set before either lyShine->Update or + // lyShine->Render are called. It must match the viewport size that the input system is using. SetViewportSize(m_uiRenderer->GetViewportSize()); // Guard against nested updates. This can occur if a canvas update below triggers the load screen component's @@ -708,10 +708,10 @@ void CLyShine::RenderUiCursor() //////////////////////////////////////////////////////////////////////////////////////////////////// void CLyShine::DebugReportDrawCalls(IConsoleCmdArgs* cmdArgs) { - if (gEnv->pLyShine) + if (AZ::Interface::Get()) { // We want to use an internal-only non-static function so downcast to CLyShine - CLyShine* pLyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); // There is an optional parameter which is a name to include in the output filename AZStd::string name; @@ -721,7 +721,7 @@ void CLyShine::DebugReportDrawCalls(IConsoleCmdArgs* cmdArgs) } // Use the canvas manager to access all the loaded canvases - pLyShine->m_uiCanvasManager->DebugReportDrawCalls(name); + lyShine->m_uiCanvasManager->DebugReportDrawCalls(name); } } #endif @@ -742,7 +742,7 @@ void CLyShine::RunUnitTests(IConsoleCmdArgs* cmdArgs) return; } - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); AZ_Assert(lyShine, "Attempting to run unit-tests prior to LyShine initialization!"); TextMarkup::UnitTest(cmdArgs); diff --git a/Gems/LyShine/Code/Source/LyShine.h b/Gems/LyShine/Code/Source/LyShine.h index eab52258c1..2fd0f80210 100644 --- a/Gems/LyShine/Code/Source/LyShine.h +++ b/Gems/LyShine/Code/Source/LyShine.h @@ -48,7 +48,7 @@ class CLyShine public: //! Create the LyShine object, the given system pointer is stored internally - CLyShine(ISystem* system); + CLyShine(); // ILyShine diff --git a/Gems/LyShine/Code/Source/LyShineDebug.cpp b/Gems/LyShine/Code/Source/LyShineDebug.cpp index 7e99652f45..3502699525 100644 --- a/Gems/LyShine/Code/Source/LyShineDebug.cpp +++ b/Gems/LyShine/Code/Source/LyShineDebug.cpp @@ -997,7 +997,7 @@ static AZ::Entity* CreateButton(const char* name, bool atRoot, AZ::EntityId pare EBUS_EVENT_ID(buttonId, UiInteractableStatesBus, SetStateAlpha, UiInteractableStatesInterface::StatePressed, buttonId, pressedColor.GetA()); AZStd::string pathname = "Textures/Basic/Button_Sliced_Normal.sprite"; - ISprite* sprite = gEnv->pLyShine->LoadSprite(pathname); + ISprite* sprite = AZ::Interface::Get()->LoadSprite(pathname); EBUS_EVENT_ID(buttonId, UiImageBus, SetSprite, sprite); EBUS_EVENT_ID(buttonId, UiImageBus, SetImageType, UiImageInterface::ImageType::Sliced); @@ -1096,7 +1096,7 @@ static AZ::Entity* CreateTextInput(const char* name, bool atRoot, AZ::EntityId p EBUS_EVENT_ID(textInputId, UiInteractableStatesBus, SetStateAlpha, UiInteractableStatesInterface::StatePressed, textInputId, pressedColor.GetA()); AZStd::string pathname = "Textures/Basic/Button_Sliced_Normal.sprite"; - ISprite* sprite = gEnv->pLyShine->LoadSprite(pathname); + ISprite* sprite = AZ::Interface::Get()->LoadSprite(pathname); EBUS_EVENT_ID(textInputId, UiImageBus, SetSprite, sprite); EBUS_EVENT_ID(textInputId, UiImageBus, SetImageType, UiImageInterface::ImageType::Sliced); @@ -1192,7 +1192,7 @@ static void DestroyTestCanvas() delete g_testActionListener2; g_testActionListener2 = nullptr; - gEnv->pLyShine->ReleaseCanvas(g_testCanvasId, false); + AZ::Interface::Get()->ReleaseCanvas(g_testCanvasId, false); g_testCanvasId.SetInvalid(); } } @@ -1216,7 +1216,7 @@ static void TestCanvasCreate ([[maybe_unused]] IConsoleCmdArgs* Cmd) DestroyTestCanvas(); // test creation of canvas and some simple elements - AZ::EntityId canvasEntityId = gEnv->pLyShine->CreateCanvas(); + AZ::EntityId canvasEntityId = AZ::Interface::Get()->CreateCanvas(); UiCanvasInterface* canvas = UiCanvasBus::FindFirstHandler(canvasEntityId); if (!canvas) { diff --git a/Gems/LyShine/Code/Source/LyShineLoadScreen.cpp b/Gems/LyShine/Code/Source/LyShineLoadScreen.cpp index c59cb5cb34..e0916d43b2 100644 --- a/Gems/LyShine/Code/Source/LyShineLoadScreen.cpp +++ b/Gems/LyShine/Code/Source/LyShineLoadScreen.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -63,7 +64,7 @@ namespace LyShine AZ_ErrorOnce(nullptr, false, "NotifyGameLoadStart needs to be removed/ported to use Atom"); return false; #if 0 - if (!gEnv || gEnv->pRenderer || !gEnv->pLyShine) + if (!gEnv || gEnv->pRenderer || !AZ::Interface::Get()) { return false; } @@ -103,7 +104,7 @@ namespace LyShine return false; //TODO: gEnv->pRenderer is always null, fix the logic below #if 0 - if (!gEnv || gEnv->pRenderer || !gEnv->pLyShine) + if (!gEnv || gEnv->pRenderer || !AZ::Interface::Get()) { return false; } @@ -140,22 +141,22 @@ namespace LyShine void LyShineLoadScreenComponent::UpdateAndRender([[maybe_unused]] float deltaTimeInSeconds) { AZ_Assert(m_isPlaying, "LyShineLoadScreenComponent should not be connected to LoadScreenUpdateNotificationBus while not playing"); - AZ_ErrorOnce(nullptr, m_isPlaying && gEnv && gEnv->pLyShine, "UpdateAndRender needs to be removed/ported to use Atom"); + AZ_ErrorOnce(nullptr, m_isPlaying && AZ::Interface::Get(), "UpdateAndRender needs to be removed/ported to use Atom"); //TODO: gEnv->pRenderer is always null, fix the logic below #if 0 - if (m_isPlaying && gEnv && gEnv->pLyShine && gEnv->pRenderer) + if (m_isPlaying && gEnv && AZ::Interface::Get() && gEnv->pRenderer) { AZ_Assert(GetCurrentThreadId() == gEnv->mMainThreadId, "UpdateAndRender should only be called from the main thread"); // update the animation system - gEnv->pLyShine->Update(deltaTimeInSeconds); + AZ::Interface::Get()->Update(deltaTimeInSeconds); // Render. gEnv->pRenderer->SetViewport(0, 0, gEnv->pRenderer->GetOverlayWidth(), gEnv->pRenderer->GetOverlayHeight()); gEnv->pRenderer->BeginFrame(); - gEnv->pLyShine->Render(); + AZ::Interface::Get()->Render(); gEnv->pRenderer->EndFrame(); } #endif @@ -177,7 +178,7 @@ namespace LyShine m_isPlaying = false; - if (gEnv && gEnv->pLyShine) + if (AZ::Interface::Get()) { AZ::Entity* canvasEntity = nullptr; @@ -187,8 +188,8 @@ namespace LyShine EBUS_EVENT_RESULT(canvasEntity, AZ::ComponentApplicationBus, FindEntity, m_gameCanvasEntityId); if (canvasEntity) { - gEnv->pLyShine->ReleaseCanvas(m_gameCanvasEntityId, false); - gEnv->pLyShine->OnLoadScreenUnloaded(); + AZ::Interface::Get()->ReleaseCanvas(m_gameCanvasEntityId, false); + AZ::Interface::Get()->OnLoadScreenUnloaded(); } } @@ -198,8 +199,8 @@ namespace LyShine EBUS_EVENT_RESULT(canvasEntity, AZ::ComponentApplicationBus, FindEntity, m_levelCanvasEntityId); if (canvasEntity) { - gEnv->pLyShine->ReleaseCanvas(m_levelCanvasEntityId, false); - gEnv->pLyShine->OnLoadScreenUnloaded(); + AZ::Interface::Get()->ReleaseCanvas(m_levelCanvasEntityId, false); + AZ::Interface::Get()->OnLoadScreenUnloaded(); } } } @@ -234,7 +235,7 @@ namespace LyShine return AZ::EntityId(); } - AZ::EntityId canvasId = gEnv->pLyShine->LoadCanvas(path); + AZ::EntityId canvasId = AZ::Interface::Get()->LoadCanvas(path); AZ_Warning("LoadScreenComponent", canvasId.IsValid(), "Can't load canvas: %s", path.c_str()); if (!canvasId.IsValid()) { diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp index 787797e462..7253935c2d 100644 --- a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp +++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp @@ -384,16 +384,16 @@ namespace LyShine // When module is linked statically, we'll share the application's gEnv pointer. gEnv = system.GetGlobalEnvironment(); #endif - m_pLyShine = new CLyShine(gEnv->pSystem); - gEnv->pLyShine = m_pLyShine; + m_lyShine = AZStd::make_unique(); + AZ::Interface::Register(m_lyShine.get()); system.GetILevelSystem()->AddListener(this); BroadcastCursorImagePathname(); - if (gEnv->pLyShine) + if (AZ::Interface::Get()) { - gEnv->pLyShine->PostInit(); + AZ::Interface::Get()->PostInit(); } } @@ -402,18 +402,20 @@ namespace LyShine { system.GetILevelSystem()->RemoveListener(this); - gEnv->pLyShine = nullptr; - delete m_pLyShine; - m_pLyShine = nullptr; + if (m_lyShine) + { + AZ::Interface::Unregister(m_lyShine.get()); + m_lyShine.reset(); + } } //////////////////////////////////////////////////////////////////////// void LyShineSystemComponent::OnUnloadComplete([[maybe_unused]] const char* levelName) { // Perform level unload procedures for the LyShine UI system - if (gEnv && gEnv->pLyShine) + if (AZ::Interface::Get()) { - gEnv->pLyShine->OnLevelUnload(); + AZ::Interface::Get()->OnLevelUnload(); } } diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.h b/Gems/LyShine/Code/Source/LyShineSystemComponent.h index d2cdcf6761..001ddd6fab 100644 --- a/Gems/LyShine/Code/Source/LyShineSystemComponent.h +++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.h @@ -107,7 +107,7 @@ namespace LyShine protected: // data - CLyShine* m_pLyShine = nullptr; + AZStd::unique_ptr m_lyShine; AzFramework::SimpleAssetReference m_cursorImagePathname; diff --git a/Gems/LyShine/Code/Source/Script/UiCanvasLuaBus.cpp b/Gems/LyShine/Code/Source/Script/UiCanvasLuaBus.cpp index 4b0ff56241..679f13b333 100644 --- a/Gems/LyShine/Code/Source/Script/UiCanvasLuaBus.cpp +++ b/Gems/LyShine/Code/Source/Script/UiCanvasLuaBus.cpp @@ -114,7 +114,7 @@ AZ::EntityId UiCanvasLuaProxy::LoadCanvas(const char* canvasFilename) CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "UiCanvasLuaProxy:LoadCanvas is deprecated. Please use UiCanvasManagerBus:LoadCanvas instead\n"); - return gEnv->pLyShine->LoadCanvas(canvasFilename); + return AZ::Interface::Get()->LoadCanvas(canvasFilename); } void UiCanvasLuaProxy::UnloadCanvas(AZ::EntityId canvasEntityId) @@ -130,7 +130,7 @@ void UiCanvasLuaProxy::UnloadCanvas(AZ::EntityId canvasEntityId) UiCanvasComponent* canvasComponent = canvasEntity->FindComponent(); if (canvasComponent) { - gEnv->pLyShine->ReleaseCanvas(canvasEntityId, false); + AZ::Interface::Get()->ReleaseCanvas(canvasEntityId, false); } else { diff --git a/Gems/LyShine/Code/Source/UiCanvasComponent.cpp b/Gems/LyShine/Code/Source/UiCanvasComponent.cpp index ea6e3681a3..0a7161ecb3 100644 --- a/Gems/LyShine/Code/Source/UiCanvasComponent.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasComponent.cpp @@ -254,9 +254,9 @@ namespace UiRenderer* GetUiRendererForGame() { - if (gEnv && gEnv->pLyShine) + if (AZ::Interface::Get()) { - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); return lyShine->GetUiRenderer(); } return nullptr; @@ -264,9 +264,9 @@ namespace UiRenderer* GetUiRendererForEditor() { - if (gEnv && gEnv->pLyShine) + if (AZ::Interface::Get()) { - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); return lyShine->GetUiRendererForEditor(); } return nullptr; diff --git a/Gems/LyShine/Code/Source/UiImageComponent.cpp b/Gems/LyShine/Code/Source/UiImageComponent.cpp index a8b4210d6b..d412695ac3 100644 --- a/Gems/LyShine/Code/Source/UiImageComponent.cpp +++ b/Gems/LyShine/Code/Source/UiImageComponent.cpp @@ -588,7 +588,7 @@ void UiImageComponent::SetSpritePathname(AZStd::string spritePath) //////////////////////////////////////////////////////////////////////////////////////////////////// bool UiImageComponent::SetSpritePathnameIfExists(AZStd::string spritePath) { - if (gEnv->pLyShine->DoesSpriteTextureAssetExist(spritePath)) + if (AZ::Interface::Get()->DoesSpriteTextureAssetExist(spritePath)) { SetSpritePathname(spritePath); return true; @@ -1179,7 +1179,7 @@ void UiImageComponent::Init() // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } @@ -1192,14 +1192,14 @@ void UiImageComponent::Init() { if (!m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } } else if (m_spriteType == UiImageInterface::SpriteType::RenderTarget) { if (!m_renderTargetName.empty()) { - m_sprite = gEnv->pLyShine->CreateSprite(m_renderTargetName.c_str()); + m_sprite = AZ::Interface::Get()->CreateSprite(m_renderTargetName.c_str()); } } else @@ -2527,7 +2527,7 @@ void UiImageComponent::OnSpritePathnameChange() if (!m_spritePathname.GetAssetPath().empty()) { // Load the new texture. - newSprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + newSprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } // if listening for notifications from a current sprite then disconnect @@ -2557,7 +2557,7 @@ void UiImageComponent::OnSpriteRenderTargetNameChange() if (!m_renderTargetName.empty()) { - newSprite = gEnv->pLyShine->CreateSprite(m_renderTargetName.c_str()); + newSprite = AZ::Interface::Get()->CreateSprite(m_renderTargetName.c_str()); } SAFE_RELEASE(m_sprite); diff --git a/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp b/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp index 3559dc6306..3981777f2d 100644 --- a/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp +++ b/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp @@ -52,7 +52,7 @@ namespace spriteList.reserve(imageList.size()); for (auto& textureAssetRef : imageList) { - ISprite* sprite = gEnv->pLyShine->LoadSprite(textureAssetRef.GetAssetPath().c_str()); + ISprite* sprite = AZ::Interface::Get()->LoadSprite(textureAssetRef.GetAssetPath().c_str()); if (sprite) { spriteList.push_back(sprite); @@ -383,7 +383,7 @@ void UiImageSequenceComponent::Init() // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } diff --git a/Gems/LyShine/Code/Source/UiInteractableState.cpp b/Gems/LyShine/Code/Source/UiInteractableState.cpp index d2c09201c6..18bacb3a9f 100644 --- a/Gems/LyShine/Code/Source/UiInteractableState.cpp +++ b/Gems/LyShine/Code/Source/UiInteractableState.cpp @@ -274,7 +274,7 @@ UiInteractableStateSprite::UiInteractableStateSprite(AZ::EntityId target, const if (!m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } } @@ -297,7 +297,7 @@ void UiInteractableStateSprite::Init(AZ::EntityId interactableEntityId) // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } @@ -306,7 +306,7 @@ void UiInteractableStateSprite::Init(AZ::EntityId interactableEntityId) // are not loaded then load them if (!m_sprite && !m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } if (!m_sprite) @@ -366,7 +366,7 @@ void UiInteractableStateSprite::OnSpritePathnameChange() if (!m_spritePathname.GetAssetPath().empty()) { // Load the new texture. - newSprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + newSprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } SAFE_RELEASE(m_sprite); diff --git a/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp b/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp index b999480940..b9f986c14a 100644 --- a/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp +++ b/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp @@ -1429,14 +1429,14 @@ void UiParticleEmitterComponent::Init() // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } if (!m_sprite && !m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } m_currentAspectRatio = m_particleSize.GetX() / m_particleSize.GetY(); @@ -1927,7 +1927,7 @@ void UiParticleEmitterComponent::OnSpritePathnameChange() if (!m_spritePathname.GetAssetPath().empty()) { // Load the new texture. - newSprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + newSprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } SAFE_RELEASE(m_sprite); diff --git a/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp b/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp index 933cca424a..e186e1158d 100644 --- a/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp +++ b/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp @@ -8,6 +8,7 @@ #include "UiCanvasAssetRefComponent.h" #include #include +#include #include #include #include @@ -106,11 +107,11 @@ AZ::EntityId UiCanvasAssetRefComponent::LoadCanvas() // Check if we already have a referenced UI canvas, if so release it if (m_canvasEntityId.IsValid()) { - gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId); + AZ::Interface::Get()->ReleaseCanvasDeferred(m_canvasEntityId); m_canvasEntityId.SetInvalid(); } - m_canvasEntityId = gEnv->pLyShine->LoadCanvas(canvasPath.c_str()); + m_canvasEntityId = AZ::Interface::Get()->LoadCanvas(canvasPath.c_str()); EBUS_EVENT_ID(GetEntityId(), UiCanvasAssetRefNotificationBus, OnCanvasLoadedIntoEntity, m_canvasEntityId); EBUS_EVENT_ID(GetEntityId(), UiCanvasRefNotificationBus, OnCanvasRefChanged, GetEntityId(), m_canvasEntityId); @@ -124,7 +125,7 @@ void UiCanvasAssetRefComponent::UnloadCanvas() { if (m_canvasEntityId.IsValid()) { - gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId); + AZ::Interface::Get()->ReleaseCanvasDeferred(m_canvasEntityId); m_canvasEntityId.SetInvalid(); EBUS_EVENT_ID(GetEntityId(), UiCanvasRefNotificationBus, OnCanvasRefChanged, GetEntityId(), m_canvasEntityId); @@ -243,7 +244,7 @@ void UiCanvasAssetRefComponent::Deactivate() { if (m_canvasEntityId.IsValid()) { - gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId); + AZ::Interface::Get()->ReleaseCanvasDeferred(m_canvasEntityId); m_canvasEntityId.SetInvalid(); } diff --git a/Gems/LyShine/Code/Tests/UiTooltipComponentTest.cpp b/Gems/LyShine/Code/Tests/UiTooltipComponentTest.cpp index 8a3d9f2af6..652cec3c0f 100644 --- a/Gems/LyShine/Code/Tests/UiTooltipComponentTest.cpp +++ b/Gems/LyShine/Code/Tests/UiTooltipComponentTest.cpp @@ -144,7 +144,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnHover); @@ -170,7 +169,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnHover); @@ -194,7 +192,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnPress); @@ -218,7 +215,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnPress); @@ -242,7 +238,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnClick); diff --git a/Gems/LyShineExamples/Code/Source/LyShineExamplesCppExample.cpp b/Gems/LyShineExamples/Code/Source/LyShineExamplesCppExample.cpp index f2da79fa99..7619764fbc 100644 --- a/Gems/LyShineExamples/Code/Source/LyShineExamplesCppExample.cpp +++ b/Gems/LyShineExamples/Code/Source/LyShineExamplesCppExample.cpp @@ -47,7 +47,7 @@ namespace LyShineExamples // Remove the existing example canvas if it exists DestroyCanvas(); - AZ::EntityId canvasEntityId = gEnv->pLyShine->CreateCanvas(); + AZ::EntityId canvasEntityId = AZ::Interface::Get()->CreateCanvas(); if (!canvasEntityId.IsValid()) { return; @@ -90,7 +90,7 @@ namespace LyShineExamples m_healthBar.SetInvalid(); - gEnv->pLyShine->ReleaseCanvas(m_canvasId, false); + AZ::Interface::Get()->ReleaseCanvas(m_canvasId, false); m_canvasId.SetInvalid(); } } diff --git a/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp b/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp index e8b0e4370a..c228176b4f 100644 --- a/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp +++ b/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp @@ -284,7 +284,7 @@ namespace LyShineExamples // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } @@ -294,7 +294,7 @@ namespace LyShineExamples { if (!m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } } @@ -399,7 +399,7 @@ namespace LyShineExamples if (!m_spritePathname.GetAssetPath().empty()) { // Load the new texture. - newSprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + newSprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } SAFE_RELEASE(m_sprite); diff --git a/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp b/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp index 40fed3448b..de54bef4eb 100644 --- a/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp +++ b/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp @@ -127,21 +127,21 @@ namespace MessagePopup switch (_buttons) { case EPopupButtons::EPopupButtons_NoButtons: - canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/defaultmessagepopup.uicanvas"); + canvasEntityId = AZ::Interface::Get()->LoadCanvas("@products@/ui/canvases/defaultmessagepopup.uicanvas"); break; case EPopupButtons::EPopupButtons_Confirm: - canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_confirm.uicanvas"); + canvasEntityId = AZ::Interface::Get()->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_confirm.uicanvas"); isNavigationSupported = true; break; case EPopupButtons::EPopupButtons_YesNo: - canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_yesno.uicanvas"); + canvasEntityId = AZ::Interface::Get()->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_yesno.uicanvas"); isNavigationSupported = true; break; } } else if (_kind == EPopupKind_Toaster) { - canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/toaster.uicanvas"); + canvasEntityId = AZ::Interface::Get()->LoadCanvas("@products@/ui/canvases/toaster.uicanvas"); } if (canvasEntityId.IsValid()) @@ -183,7 +183,7 @@ namespace MessagePopup { // get the canvas ID in the clientdata LyShine::CanvasId canvasId = *((LyShine::CanvasId*)&_popupInfo.m_clientData); - AZ::EntityId canvasEntityId = gEnv->pLyShine->FindCanvasById(canvasId); + AZ::EntityId canvasEntityId = AZ::Interface::Get()->FindCanvasById(canvasId); if (canvasEntityId.IsValid()) { // Hide the cursor if it was shown in LyShineMessagePopup::OnShowPopup @@ -197,7 +197,7 @@ namespace MessagePopup // Disable the popup EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetEnabled, false); - gEnv->pLyShine->ReleaseCanvas(canvasEntityId, false); + AZ::Interface::Get()->ReleaseCanvas(canvasEntityId, false); UiCanvasNotificationBus::MultiHandler::BusDisconnect(canvasEntityId); m_activePopupIdsByCanvasId.erase(canvasEntityId); From a145d3c82fa3c5a71dc0545708518d7bd7769d53 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 21 Jan 2022 15:07:27 -0800 Subject: [PATCH 46/61] Issues/install missing assets (#7081) * Fixes for missing assets in the install folder Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * delayed expansion to try to fix project_engineinstall_profile Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Improvements to build scripts to solve the project build from the install folder Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- CMakeLists.txt | 4 ++++ cmake/Platform/Common/Install_common.cmake | 8 +++++--- .../build/Platform/Windows/build_windows.cmd | 11 ++++++----- .../build/Platform/Windows/env_windows.cmd | 19 ++++++++++++------- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c81581fe0f..efcc866195 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,10 @@ endfunction() # Add the projects first so the Launcher can find them include(cmake/Projects.cmake) +if(LY_EXTERNAL_SUBDIRS) + set_property(GLOBAL APPEND PROPERTY LY_EXTERNAL_SUBDIRS ${LY_EXTERNAL_SUBDIRS}) +endif() + if(NOT INSTALLED_ENGINE) # Add external subdirectories listed in the engine.json. LY_EXTERNAL_SUBDIRS is a cache variable so the user can add extra # external subdirectories. This should go before adding the rest of the targets so the targets are availbe to the launcher. diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 46130f1345..e42664c107 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -445,9 +445,10 @@ function(ly_setup_cmake_install) ) endforeach() - # Transform the LY_EXTERNAL_SUBDIRS list into a json array + # Transform the LY_EXTERNAL_SUBDIRS global property list into a json array set(indent " ") - foreach(external_subdir ${LY_EXTERNAL_SUBDIRS}) + get_property(external_subdirs GLOBAL PROPERTY LY_EXTERNAL_SUBDIRS) + foreach(external_subdir ${external_subdirs}) cmake_path(RELATIVE_PATH external_subdir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE engine_rel_external_subdir) list(APPEND relative_external_subdirs "\"${engine_rel_external_subdir}\"") endforeach() @@ -639,7 +640,8 @@ function(ly_setup_assets) # the install layout from the root directory. Such as /Cache. # This is also done to avoid globbing thousands of files in subdirectories that shouldn't # be processed. - foreach(gem_candidate_dir IN LISTS LY_EXTERNAL_SUBDIRS LY_PROJECTS) + get_property(external_subdirs GLOBAL PROPERTY LY_EXTERNAL_SUBDIRS) + foreach(gem_candidate_dir IN LISTS external_subdirs LY_PROJECTS) file(REAL_PATH ${gem_candidate_dir} gem_candidate_dir BASE_DIRECTORY ${LY_ROOT_FOLDER}) # Don't recurse immediately in order to exclude transient source artifacts file(GLOB diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 798550370f..c77c262692 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -9,6 +9,7 @@ REM SETLOCAL EnableDelayedExpansion CALL "%~dp0env_windows.cmd" +IF NOT %ERRORLEVEL%==0 EXIT /b 1 IF NOT EXIST "%OUTPUT_DIRECTORY%" ( MKDIR %OUTPUT_DIRECTORY%. @@ -21,7 +22,7 @@ ECHO [ci_build] cmake --version cmake --version IF ERRORLEVEL 1 ( ECHO [ci_build] CMAKE not found! - exit /b 1 + EXIT /b 1 ) REM Compute half the amount of processors so some jobs can run @@ -47,14 +48,14 @@ IF NOT EXIST CMakeCache.txt ( ) ) IF DEFINED RUN_CONFIGURE ( - ECHO [ci_build] %CONFIGURE_CMD% - %CONFIGURE_CMD% + call ECHO [ci_build] %CONFIGURE_CMD% + call %CONFIGURE_CMD% IF NOT !ERRORLEVEL!==0 GOTO :error ECHO !CONFIGURE_CMD!> %LAST_CONFIGURE_CMD_FILE% ) -ECHO [ci_build] cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS% -cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS% +call ECHO [ci_build] cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS% +call cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS% IF NOT %ERRORLEVEL%==0 GOTO :error POPD diff --git a/scripts/build/Platform/Windows/env_windows.cmd b/scripts/build/Platform/Windows/env_windows.cmd index 78b5ca6c1a..c6f46ef575 100644 --- a/scripts/build/Platform/Windows/env_windows.cmd +++ b/scripts/build/Platform/Windows/env_windows.cmd @@ -13,17 +13,21 @@ SETLOCAL EnableExtensions EnableDelayedExpansion where /Q cmake IF NOT %ERRORLEVEL%==0 ( ECHO [ci_build] CMake not found - GOTO :error -) - -IF NOT "%COMMAND_CWD%"=="" ( - ECHO [ci_build] Changing CWD to %COMMAND_CWD% - CD %COMMAND_CWD% + GOTO :errorlocal ) REM Ending the local environment to be able to propagate the TMP/TEMP variables to the calling script ENDLOCAL +IF NOT "%COMMAND_CWD%"=="" ( + call ECHO [ci_build] Changing CWD to %COMMAND_CWD% + call CD %COMMAND_CWD% + IF ERRORLEVEL 1 ( + ECHO [ci_build] Failed to change directory to %COMMAND_CWD% + GOTO :error + ) +) + REM Jenkins does not defined TMP IF "%TMP%"=="" ( IF "%WORKSPACE%"=="" ( @@ -41,6 +45,7 @@ IF "%TMP%"=="" ( EXIT /b 0 -:error +:errorlocal ENDLOCAL +:error EXIT /b 1 \ No newline at end of file From dbd6ddbc1c62db227344407a1e51421380adde1c Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Fri, 21 Jan 2022 18:05:19 -0600 Subject: [PATCH 47/61] Optimized Gradient Previewer (#7074) * Faster method with older one commented out for profiling. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Removed commented-out code Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Addressed PR feedback, fixed subtle bug in interlace math. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed unrelated bug in terrain cmake file. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fix linux compile error. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Editor/EditorGradientPreviewRenderer.h | 204 +++++++++++------- Gems/Terrain/Code/CMakeLists.txt | 1 - 2 files changed, 127 insertions(+), 78 deletions(-) diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientPreviewRenderer.h b/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientPreviewRenderer.h index 2790a668a3..db39431ba1 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientPreviewRenderer.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientPreviewRenderer.h @@ -25,7 +25,6 @@ #include #include - namespace GradientSignal { //! EditorGradientPreviewUpdateJob offloads the creation of a gradient preview image to another thread. @@ -223,7 +222,7 @@ namespace GradientSignal // If you ever want to make this use a smaller number of passes, set this value to any even number. // A value of 0 is the same as "non-interlaced", 6 would exactly use the Adam7 algorithm, etc. // It's currently clamping to 30 as a somewhat aribtrary choice. - const uint64_t maxFinalInterlacingPass = 30; + const int64_t maxFinalInterlacingPass = 30; m_finalInterlacingPass = AZ::GetMin(m_finalInterlacingPass, maxFinalInterlacingPass); // Finally, lock our mutex, modify our status variables, and start the Job. @@ -238,6 +237,8 @@ namespace GradientSignal //! Process runs exactly once for each time Start() is called on a Job, and processes on a Job worker thread. void Process() override { + AZ_PROFILE_FUNCTION(Entity); + // Guard against the case that we're trying to cancel even before we've started to run. if (!m_shouldCancel) { @@ -247,88 +248,136 @@ namespace GradientSignal // This is the "striding value". When walking directly through our preview image bits() buffer, there might be // extra pad bytes for each line due to alignment. We use this to make sure we start writing each line at the right byte offset. - const uint64_t imageBytesPerLine = m_previewImage->bytesPerLine(); - - // The following are all used for calculating our interlaced pixel updates. - - // The current interlace pass that we're on. - int64_t curPass = 0; - // The index of the first pixel for this pass. This is used to calculate the relative pixel index per pass. - uint64_t firstPixelPerPass = 0; - // Total number of pixels that we'll process per pass. After the first two passes, the amount doubles per pass till we reach 100%. - uint64_t totalPixelsPerPass = (m_imageBoundsPowerOfTwo * m_imageBoundsPowerOfTwo) / (1LL << (m_finalInterlacingPass - curPass)); - // The general interlace formulas need a multiplier and an offset for x and y to apply to each relative pixel index. - // The pixel multipliers start high and reduce on each pass to increase the pixel density per pass. - // The pixel offsets alternate between 0 and a reducing number because we start on aligned grids, then fill in the midpoints - // of the grids on every other pass. - uint64_t xPixelMult = 1LL << (m_finalInterlacingPass / 2); - uint64_t xPixelOffset = 0; - uint64_t yPixelMult = 1LL << (m_finalInterlacingPass / 2); - uint64_t yPixelOffset = 0; - - // The heart of the processing - loop through each pixel using interlaced indexing, get the gradient value, and write it into - // the pixel buffer. On each pixel, we also check to see if the main thread requested a cancel so that we can early-out. - // The loop itself runs through the full square power-of-two bounds that encapsulates our image so that we can perform our - // interlaced indexing easily, but we skip processing any pixel that falls outside the actual image bounds. - for (uint64_t curPixel = 0; (!m_shouldCancel) && (curPixel < (m_imageBoundsPowerOfTwo * m_imageBoundsPowerOfTwo)); curPixel++) + const int64_t imageBytesPerLine = m_previewImage->bytesPerLine(); + + // Keep track of the total number of pixels that we intend to process. For easy interlacing calculations, we always use + // square power-of-two conceptual images, but we'll skip any pixels that fall outside of our actual image bounds. + const int64_t totalPixels = (m_imageBoundsPowerOfTwo * m_imageBoundsPowerOfTwo); + + // Preallocate buffers for our gradient lookup positions, our gradient output values, and the corresponding pixel buffer + // index to store the value into. These allow us to fetch gradient values in bulk, which is much faster than fetching them + // individually. The max size we'll need is for our last interacing pass which requests 50% of our total pixels (as + // described further below), so that's what we will preallocate. + AZStd::vector gradientLookupPositions(totalPixels / 2); + AZStd::vector gradientValues(totalPixels / 2); + AZStd::vector pixelBufferIndex(totalPixels / 2); + + // The following loop uses a variant of the Adam7 interlacing algorithm that's been generalized to work for N passes, + // instead of exactly 7 passes. The first two passes fill in 1 pixel each, and then each subsequent pass doubles the + // number of pixels it fills in, until the last pass fills in 50%. + // Note that m_finalInteracingPass contains the value of the final pass to process, not the total number of passes. + // On each pass, we'll also early-out if the main thread requested a cancellation. + for (int64_t curPass = 0; (!m_shouldCancel) && (curPass <= m_finalInterlacingPass); curPass++) { - // Check to see if we've finished the pixels for this pass and need to move on to the next pass. - if (curPixel >= (firstPixelPerPass + totalPixelsPerPass)) + gradientLookupPositions.clear(); + pixelBufferIndex.clear(); + gradientValues.clear(); + + // The general interlace formulas need a multiplier and an offset for x and y to apply to each relative pixel index. + // + // The first 3 passes are a little different than the others because they establish the base pattern: + // 1 . . . 2 . . . + // . . . . . . . . + // . . . . . . . . + // . . . . . . . . + // 3 . . . 3 . . . + // . . . . . . . . + // . . . . . . . . + // . . . . . . . . + // + // Every 2 passes from then on do the same thing, with shrinking grids. One pass fills in the grid X midpoints on the + // lines that were already processed, and the second pass fills in all the equivalent points on the Y grid midpoints + // x . 4 . x . 4 . + // . . . . . . . . + // 5 . 5 . 5 . 5 . + // . . . . . . . . + // x . 4 . x . 4 . + // . . . . . . . . + // 5 . 5 . 5 . 5 . + // . . . . . . . . + // + // x 6 x 6 x 6 x 6 + // 7 7 7 7 7 7 7 7 + // x 6 x 6 x 6 x 6 + // 7 7 7 7 7 7 7 7 + // x 6 x 6 x 6 x 6 + // 7 7 7 7 7 7 7 7 + // x 6 x 6 x 6 x 6 + // 7 7 7 7 7 7 7 7 + // + // The total number of pixels processed per pass starts at 1 pixel each for the first two passes, then doubles per + // pass till we reach 50% in the last pass, since all the other passes before it will have covered the other 50%. + // Ex: 7 passes will do N/64, N/64, N/32, N/16, N/8, N/4, N/2 pixels per pass. + + + // For X, we want our starting pixel offset to alternate between 0 and a decreasing power of 2 on every pass, and + // our stride to decrease by a power of 2 every two passes, ending with an offset of 0 and a stride of 1 on the last + // pass. + const int64_t xOffsetShifter = AZ::GetMin(m_finalInterlacingPass - curPass, m_finalInterlacingPass - 1); + const int64_t xPixelOffset = (curPass % 2) * (1LL << (xOffsetShifter / 2)); + const int64_t xPixelStride = 1LL << ((xOffsetShifter + 1) / 2); + + // For Y, we want our starting pixel offset and our stride to behave the same as X, except that we hold our starting + // offset and stride for one additional pass which is what causes the first 3 passes to behave differently than the + // rest. The pass offset between X and Y is also what causes the pattern to keep filling in pixels and lines that + // haven't already been processed. + const int64_t laggingPass = AZ::GetMax(curPass - 1, 0); + const int64_t yOffsetShifter = AZ::GetMin(m_finalInterlacingPass - laggingPass, m_finalInterlacingPass - 1); + const int64_t yPixelOffset = (laggingPass % 2) * (1LL << (yOffsetShifter / 2)); + const int64_t yPixelStride = 1LL << ((yOffsetShifter + 1) / 2); + + // First, we loop and fill in all the gradientLookupPositions and pixelBufferIndex values for any pixels that don't + // get culled out. We're using a power of two for calculating our interlacing offsets and strides, but we don't need + // to actually process any of those pixels that fall outside our image bounds, so we end our loops at the bounds. + for (int64_t y = yPixelOffset; y < m_imageBoundsY; y += yPixelStride) { - curPass++; - - // Adjust our interlacing formula adjustments on each pass. These will cause us to process an increasing - // number of pixels at a higher density on each pass, interleaving in a way that ensures each pixel is only - // processed once at the end. - yPixelMult = xPixelMult; - yPixelOffset = xPixelOffset; - xPixelMult = 1LL << ((m_finalInterlacingPass - curPass + 1) / 2); - xPixelOffset = (curPass % 2) * (1LL << ((m_finalInterlacingPass - curPass) / 2)); - - firstPixelPerPass += totalPixelsPerPass; - totalPixelsPerPass = (m_imageBoundsPowerOfTwo * m_imageBoundsPowerOfTwo) / (1LL << (m_finalInterlacingPass - curPass + 1)); + for (int64_t x = xPixelOffset; x < m_imageBoundsX; x += xPixelStride) + { + // Map the pixel coordinate back into world coordinates for the shape and gradient queries. Note that we + // invert world y to match the world axis. (We use "imageBoundsY- 1" to invert because our loop doesn't go all + // the way to imageBoundsY) + AZ::Vector3 uvw(static_cast(x), static_cast((m_imageBoundsY - 1) - y), 0.0f); + AZ::Vector3 position = m_previewBoundsStart + (uvw * m_pixelToBoundsScale) + m_scaledTexelOffset; + + // If our preview is only drawing what appears inside the given shape, check to see if the pixel should be + // drawn. + bool inBounds = true; + if (m_constrainToShape) + { + LmbrCentral::ShapeComponentRequestsBus::EventResult( + inBounds, m_previewEntityId, &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, position); + } + + // If we're drawing this pixel, push it into our buffer of lookup positions. + if (inBounds) + { + gradientLookupPositions.emplace_back(position); + pixelBufferIndex.emplace_back(((m_centeringOffsetY + y) * imageBytesPerLine) + (m_centeringOffsetX + x)); + } + } } - // Here's where interlacing happens. If this were non-interlaced, we'd simply have the following: - // x = curPixel % m_imageBoundsPowerOfTwo - // y = curPixel / m_imageBoundsPowerOfTwo - uint64_t adjustedPixel = curPixel - firstPixelPerPass; - uint64_t x = ((adjustedPixel * xPixelMult) + xPixelOffset) % m_imageBoundsPowerOfTwo; - uint64_t y = ((((adjustedPixel * xPixelMult) + xPixelOffset) / m_imageBoundsPowerOfTwo) * yPixelMult) + yPixelOffset; + // Resize our output buffer to match our input buffer and query for all the gradient values at once. + gradientValues.resize(gradientLookupPositions.size()); + m_sampler.GetValues(gradientLookupPositions, gradientValues); - // Since we're using a power of two for calculating our interlacing, it's possible to get pixel offsets beyond the bounds - // of our actual image. We just skip those and continue on to the next pixel. - if ((x >= m_imageBoundsX) || (y >= m_imageBoundsY)) + // For each output value, run it through a filter if we were given one, then store it in the pixel buffer. + for (size_t index = 0; index < gradientLookupPositions.size(); index++) { - continue; - } - - // Now that we've calculated the pixel position, update it with the gradient value. - { - // Invert world y to match axis. (We use "imageBoundsY- 1" to invert because our loop doesn't go all the way to imageBoundsY) - AZ::Vector3 uvw(static_cast(x), static_cast((m_imageBoundsY - 1) - y), 0.0f); - - GradientSampleParams sampleParams; - sampleParams.m_position = m_previewBoundsStart + (uvw * m_pixelToBoundsScale) + m_scaledTexelOffset; - - bool inBounds = true; - if (m_constrainToShape) - { - LmbrCentral::ShapeComponentRequestsBus::EventResult(inBounds, m_previewEntityId, &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, sampleParams.m_position); - } - - float sample = inBounds ? m_sampler.GetValue(sampleParams) : 0.0f; + float sample = gradientValues[index]; if (m_filterFunc) { + GradientSampleParams sampleParams; + sampleParams.m_position = gradientLookupPositions[index]; sample = m_filterFunc(sample, sampleParams); } - buffer[((m_centeringOffsetY + y) * imageBytesPerLine) + (m_centeringOffsetX + x)] = static_cast(sample * 255); - } + buffer[pixelBufferIndex[index]] = static_cast(sample * 255); - // Notify the main thread via atomic bool that the image has changed by at least one pixel. - m_refreshUI = true; + // Notify the main thread via atomic bool that the image has changed by at least one pixel. + m_refreshUI = true; + } } } @@ -356,15 +405,15 @@ namespace GradientSignal AZ::EntityId m_previewEntityId; // Values calculated during preview setup that we'll use during processing - uint64_t m_imageBoundsX = 0; - uint64_t m_imageBoundsY = 0; - uint64_t m_centeringOffsetX = 0; - uint64_t m_centeringOffsetY = 0; + int64_t m_imageBoundsX = 0; + int64_t m_imageBoundsY = 0; + int64_t m_centeringOffsetX = 0; + int64_t m_centeringOffsetY = 0; AZ::Vector3 m_previewBoundsStart; AZ::Vector3 m_pixelToBoundsScale; AZ::Vector3 m_scaledTexelOffset; - uint64_t m_imageBoundsPowerOfTwo = 1; - uint64_t m_finalInterlacingPass = 0; + int64_t m_imageBoundsPowerOfTwo = 1; + int64_t m_finalInterlacingPass = 0; // Communication / synchronization mechanisms between the different threads AZStd::mutex m_previewMutex; @@ -456,3 +505,4 @@ namespace GradientSignal EditorGradientPreviewUpdateJob* m_updateJob = nullptr; }; } //namespace GradientSignal + diff --git a/Gems/Terrain/Code/CMakeLists.txt b/Gems/Terrain/Code/CMakeLists.txt index 69a1bef3c0..44e33b7008 100644 --- a/Gems/Terrain/Code/CMakeLists.txt +++ b/Gems/Terrain/Code/CMakeLists.txt @@ -98,7 +98,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAME Terrain.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE Gem FILES_CMAKE - terrain_files.cmake terrain_tests_files.cmake INCLUDE_DIRECTORIES PRIVATE From dd7706f9feec5370a8d6b5e9b2565c13421016e9 Mon Sep 17 00:00:00 2001 From: chiyenteng <82238204+chiyenteng@users.noreply.github.com> Date: Fri, 21 Jan 2022 16:23:31 -0800 Subject: [PATCH 48/61] Fire OnEditorEntityCreated notification in SandboxIntegrationManager::CreateNewEntityAtPosition (#7079) * Ensure to fire OnEditorEntityCreated notification in SandboxIntegrationManager::CreateNewEntityAtPosition Signed-off-by: chiyenteng <82238204+chiyenteng@users.noreply.github.com> * Ensure to fire OnEditorEntityCreated notification in SandboxIntegrationManager::CreateNewEntityAtPosition Signed-off-by: chiyenteng <82238204+chiyenteng@users.noreply.github.com> * convert test AreaNodes_DependentComponentsAdded to use prefab system Signed-off-by: chiyenteng <82238204+chiyenteng@users.noreply.github.com> * Prevent SetupEditorEntity being called twice Signed-off-by: chiyenteng <82238204+chiyenteng@users.noreply.github.com> --- .../AreaNodes_DependentComponentsAdded.py | 2 +- .../TestSuite_Main_Optimized.py | 10 +++++--- .../Entity/EditorEntityContextBus.h | 5 +++- .../Entity/EditorEntityContextComponent.cpp | 23 +++++-------------- .../Entity/EditorEntityContextComponent.h | 5 +--- .../Prefab/PrefabPublicHandler.cpp | 1 + 6 files changed, 20 insertions(+), 26 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py index c69ce77041..8151299cea 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py @@ -69,7 +69,7 @@ def AreaNodes_DependentComponentsAdded(): # Open an existing simple level helper.init_idle() - helper.open_level("Physics", "Base") + helper.open_level("", "Base") # Open Landscape Canvas tool and verify general.open_pane('Landscape Canvas') diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py index 402df133cb..e501b3ad3b 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py @@ -12,6 +12,13 @@ import ly_test_tools.environment.file_system as file_system import ly_test_tools._internal.pytest_plugin as internal_plugin from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite +@pytest.mark.SUITE_periodic +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestAutomationWithPrefabSystemEnabled(EditorTestSuite): + + class test_LandscapeCanvas_AreaNodes_DependentComponentsAdded(EditorSharedTest): + from .EditorScripts import AreaNodes_DependentComponentsAdded as test_module @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @@ -26,9 +33,6 @@ class TestAutomation(EditorTestSuite): class test_LandscapeCanvas_GradientMixer_NodeConstruction(EditorSharedTest): from .EditorScripts import GradientMixer_NodeConstruction as test_module - class test_LandscapeCanvas_AreaNodes_DependentComponentsAdded(EditorSharedTest): - from .EditorScripts import AreaNodes_DependentComponentsAdded as test_module - class test_LandscapeCanvas_AreaNodes_EntityCreatedOnNodeAdd(EditorSharedTest): from .EditorScripts import AreaNodes_EntityCreatedOnNodeAdd as test_module diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h index 1a5ba60bdf..29d9b742e2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h @@ -63,9 +63,12 @@ namespace AzToolsFramework /// Registers an existing set of entities with the editor context. virtual void AddEditorEntities(const EntityList& entities) = 0; - /// Registers an existing set of entities with the editor context. + /// Triggers registered callbacks for an existing set of entities with the editor context. virtual void HandleEntitiesAdded(const EntityList& entities) = 0; + /// Creates an editor ready entity, and sends out notification for the creation. + virtual void FinalizeEditorEntity(AZ::Entity* entity) = 0; + /// Destroys an entity in the editor context. /// \return whether or not the entity was destroyed. A false return value signifies the entity did not belong to the game context. virtual bool DestroyEditorEntity(AZ::EntityId entityId) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp index 470ca8b9ea..95b3f2bf93 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp @@ -227,14 +227,8 @@ namespace AzToolsFramework { AZ::Entity* entity = CreateEntity(name); AZ_Assert(entity != nullptr, "Entity with name %s couldn't be created.", name); - if (m_isLegacySliceService) - { - FinalizeEditorEntity(entity); - } - else - { - EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnEditorEntityCreated, entity->GetId()); - } + FinalizeEditorEntity(entity); + return entity->GetId(); } @@ -263,14 +257,7 @@ namespace AzToolsFramework entity = aznew AZ::Entity(entityId, name); AZ_Assert(entity != nullptr, "Entity with name %s couldn't be created.", name); AddEntity(entity); - if (m_isLegacySliceService) - { - FinalizeEditorEntity(entity); - } - else - { - EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnEditorEntityCreated, entity->GetId()); - } + FinalizeEditorEntity(entity); return entity->GetId(); } @@ -284,10 +271,12 @@ namespace AzToolsFramework { return; } - SetupEditorEntity(entity); // Store creation undo command. + if (m_isLegacySliceService) { + SetupEditorEntity(entity); + ScopedUndoBatch undoBatch("Create Entity"); EntityCreateCommand* command = aznew EntityCreateCommand(static_cast(entity->GetId())); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h index 28cf67c0f2..4e04b81c67 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h @@ -81,6 +81,7 @@ namespace AzToolsFramework void AddEditorEntity(AZ::Entity* entity) override; void AddEditorEntities(const EntityList& entities) override; void HandleEntitiesAdded(const EntityList& entities) override; + void FinalizeEditorEntity(AZ::Entity* entity) override; bool CloneEditorEntities(const EntityIdList& sourceEntities, EntityList& resultEntities, AZ::SliceComponent::EntityIdToEntityIdMap& sourceToCloneEntityIdMap) override; @@ -142,13 +143,9 @@ namespace AzToolsFramework } protected: - void OnContextEntitiesAdded(const EntityList& entities) override; void OnContextEntityRemoved(const AZ::EntityId& id) override; - // Helper function for creating editor ready entities. - void FinalizeEditorEntity(AZ::Entity* entity); - void SetupEditorEntity(AZ::Entity* entity); void SetupEditorEntities(const EntityList& entities); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 0c1fb9bed2..62003cd846 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -661,6 +661,7 @@ namespace AzToolsFramework entityOwningInstance.AddEntity(*entity, entityAlias); EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequestBus::Events::HandleEntitiesAdded, EntityList{entity}); + EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequestBus::Events::FinalizeEditorEntity, entity); AZ::Transform transform = AZ::Transform::CreateIdentity(); transform.SetTranslation(position); From 1f7040c6b825361e54e6d502d8452e82543d56b7 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 21 Jan 2022 18:28:13 -0600 Subject: [PATCH 49/61] Updating mock calls in unit_test_manifest.py to fix test (#7090) The manifest.py `get_all_templates` method was modified to add a call to `get_all_gems` and `get_gem_templates`. Those calls were not mocked, so they tried to load an o3de_manifest.json file that was on user's machine, which failed on the CI node. Also added a mock call for `load_o3de_manifest`, that validates that it is not called to catch this issue during Automated Review. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- scripts/o3de/tests/unit_test_manifest.py | 39 +++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/scripts/o3de/tests/unit_test_manifest.py b/scripts/o3de/tests/unit_test_manifest.py index c7d7a7573b..c8eb916bf3 100644 --- a/scripts/o3de/tests/unit_test_manifest.py +++ b/scripts/o3de/tests/unit_test_manifest.py @@ -29,10 +29,17 @@ class TestGetTemplatesForCreation: def get_project_templates() -> list: return [] + @staticmethod + def get_gem_templates() -> list: + return [] + @staticmethod def get_engine_templates() -> list: return [pathlib.Path('D:/o3de/Templates/DefaultProject'), pathlib.Path('D:/o3de/Templates/DefaultGem')] + @staticmethod + def get_all_gems(project_path: pathlib.Path = None) -> list: + return [] @pytest.mark.parametrize("expected_template_paths", [ pytest.param([]) @@ -50,17 +57,25 @@ class TestGetTemplatesForCreation: as get_manifest_templates_patch, \ patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates)\ as get_project_templates_patch, \ + patch('o3de.manifest.get_gem_templates', side_effect=self.get_gem_templates) \ + as get_gem_templates_patch, \ patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates)\ as get_engine_templates_patch, \ + patch('o3de.manifest.get_all_gems', side_effect=self.get_all_gems) \ + as get_all_gems_patch, \ patch('o3de.validation.valid_o3de_template_json', return_value=True) \ as validate_template_json,\ patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) \ as validate_project_json,\ patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) \ - as validate_gem_json: + as validate_gem_json,\ + patch('o3de.manifest.load_o3de_manifest') as load_o3de_manifest_patch: templates = manifest.get_templates_for_generic_creation() assert templates == expected_template_paths + # make sure the o3de manifest isn't attempted to be loaded + load_o3de_manifest_patch.assert_not_called() + @pytest.mark.parametrize("expected_template_paths", [ pytest.param([pathlib.Path('D:/o3de/Templates/DefaultProject')]) @@ -78,17 +93,25 @@ class TestGetTemplatesForCreation: as get_manifest_templates_patch, \ patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \ as get_project_templates_patch, \ + patch('o3de.manifest.get_gem_templates', side_effect=self.get_gem_templates) \ + as get_gem_templates_patch, \ patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \ as get_engine_templates_patch, \ + patch('o3de.manifest.get_all_gems', side_effect=self.get_all_gems) \ + as get_all_gems_patch, \ patch('o3de.validation.valid_o3de_template_json', return_value=True) \ as validate_template_json, \ patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) \ as validate_project_json, \ patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) \ - as validate_gem_json: + as validate_gem_json,\ + patch('o3de.manifest.load_o3de_manifest') as load_o3de_manifest_patch: templates = manifest.get_templates_for_project_creation() assert templates == expected_template_paths + # make sure the o3de manifest isn't attempted to be loaded + load_o3de_manifest_patch.assert_not_called() + @pytest.mark.parametrize("expected_template_paths", [ pytest.param([pathlib.Path('D:/o3de/Templates/DefaultGem')]) @@ -106,13 +129,21 @@ class TestGetTemplatesForCreation: as get_manifest_templates_patch, \ patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \ as get_project_templates_patch, \ + patch('o3de.manifest.get_gem_templates', side_effect=self.get_gem_templates) \ + as get_gem_templates_patch, \ patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \ as get_engine_templates_patch, \ + patch('o3de.manifest.get_all_gems', side_effect=self.get_all_gems) \ + as get_all_gems_patch, \ patch('o3de.validation.valid_o3de_template_json', return_value=True) \ as validate_template_json, \ patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) \ as validate_project_json, \ patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) \ - as validate_gem_json: + as validate_gem_json, \ + patch('o3de.manifest.load_o3de_manifest') as load_o3de_manifest_patch: templates = manifest.get_templates_for_gem_creation() - assert templates == expected_template_paths \ No newline at end of file + assert templates == expected_template_paths + + # make sure the o3de manifest isn't attempted to be loaded + load_o3de_manifest_patch.assert_not_called() From 878ba24a3f617c1bed46dad8e0f7ee105f21af26 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 21 Jan 2022 18:28:26 -0600 Subject: [PATCH 50/61] Removed unused AWSNativeSDK dependency from Editor (#7091) * Removed unused AWSNativeSDK dependency from Editor Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Remove build dependencies on the aws-cpp-sdk library from the Editor Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Code/Editor/CMakeLists.txt | 3 --- Code/Editor/CryEdit.cpp | 10 ---------- Code/Editor/IEditorImpl.cpp | 10 ---------- Code/Editor/IEditorImpl.h | 1 - Code/Editor/MainWindow.cpp | 5 ----- 5 files changed, 29 deletions(-) diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index 9d74f8e3a2..3f340e37d8 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -104,13 +104,11 @@ ly_add_target( 3rdParty::Qt::Concurrent 3rdParty::TIFF 3rdParty::squish-ccr - 3rdParty::AWSNativeSDK::STS Legacy::CryCommon Legacy::EditorCommon AZ::AzCore AZ::AzToolsFramework Gem::LmbrCentral.Static - AZ::AWSNativeSDKInit AZ::AtomCore Gem::Atom_RPI.Edit Gem::Atom_RPI.Public @@ -119,7 +117,6 @@ ly_add_target( Gem::AtomViewportDisplayInfo ${additional_dependencies} PUBLIC - 3rdParty::AWSNativeSDK::Core 3rdParty::Qt::Network Legacy::EditorCore RUNTIME_DEPENDENCIES diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 8789b31fdc..cb64d47a57 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -36,14 +36,6 @@ AZ_POP_DISABLE_WARNING #include #include -// Aws Native SDK -#include -#include -#include -#include -#include -#include - // AzCore #include #include @@ -144,9 +136,7 @@ AZ_POP_DISABLE_WARNING #include "Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h" -// AWSNativeSDK #include -#include #if defined(AZ_PLATFORM_WINDOWS) diff --git a/Code/Editor/IEditorImpl.cpp b/Code/Editor/IEditorImpl.cpp index 38006c6fca..a2712b653d 100644 --- a/Code/Editor/IEditorImpl.cpp +++ b/Code/Editor/IEditorImpl.cpp @@ -16,12 +16,6 @@ // Qt #include -// AWS Native SDK -AZ_PUSH_DISABLE_WARNING(4251 4355 4996, "-Wunknown-warning-option") -#include -#include -AZ_POP_DISABLE_WARNING - // AzCore #include #include @@ -78,9 +72,6 @@ AZ_POP_DISABLE_WARNING // EditorCommon #include -// AWSNativeSDK -#include - #include "Core/QtEditorApplication.h" // for Editor::EditorQtApplication static CCryEditDoc * theDocument; @@ -132,7 +123,6 @@ CEditorImpl::CEditorImpl() , m_pSettingsManager(nullptr) , m_pLevelIndependentFileMan(nullptr) , m_pExportManager(nullptr) - , m_awsResourceManager(nullptr) , m_bMatEditMode(false) , m_bShowStatusText(true) , m_bInitialized(false) diff --git a/Code/Editor/IEditorImpl.h b/Code/Editor/IEditorImpl.h index 7867912941..354f631507 100644 --- a/Code/Editor/IEditorImpl.h +++ b/Code/Editor/IEditorImpl.h @@ -369,7 +369,6 @@ protected: QString m_selectFileBuffer; QString m_levelNameBuffer; - IAWSResourceManager* m_awsResourceManager; std::unique_ptr m_winWidgetManager; //! True if the editor is in material edit mode. Fast preview of materials. diff --git a/Code/Editor/MainWindow.cpp b/Code/Editor/MainWindow.cpp index bbebac96a3..63841299be 100644 --- a/Code/Editor/MainWindow.cpp +++ b/Code/Editor/MainWindow.cpp @@ -11,11 +11,6 @@ #include -// AWs Native SDK -AZ_PUSH_DISABLE_WARNING(4251 4355 4996, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING - // Qt #include #include From c4addbc0e0e693f63a8891625ea0d0b9719685fc Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Fri, 21 Jan 2022 22:08:31 -0800 Subject: [PATCH 51/61] Moved AutomatedTesting specific script translation assets from gems/canvas into AutomatedTesting Signed-off-by: Gene Walters --- ...orityToAutonomousNoParamsNotifyEvent.names | 0 .../AuthorityToAutonomousNotifyEvent.names | 0 ...ToAutonomous_PlayerNumberNotifyEvent.names | 56 ++++++ ...AuthorityToClientNoParamsNotifyEvent.names | 0 ...tyToClientNoParams_PlayFxNotifyEvent.names | 50 +++++ .../AuthorityToClientNotifyEvent.names | 0 ...nomousToAuthorityNoParamsNotifyEvent.names | 0 .../AutonomousToAuthorityNotifyEvent.names | 0 .../ServerToAuthorityNoParamNotifyEvent.names | 0 .../ServerToAuthorityNotifyEvent.names | 0 ...verToAuthority_DealDamageNotifyEvent.names | 56 ++++++ .../Classes/NetworkTestPlayerComponent.names | 172 +++++++++--------- ...tworkTestPlayerComponentNetworkInput.names | 0 13 files changed, 248 insertions(+), 86 deletions(-) rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names (100%) rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names (100%) create mode 100644 AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomous_PlayerNumberNotifyEvent.names rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names (100%) create mode 100644 AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParams_PlayFxNotifyEvent.names rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names (100%) rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names (100%) rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names (100%) rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names (100%) rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names (100%) create mode 100644 AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthority_DealDamageNotifyEvent.names rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/Classes/NetworkTestPlayerComponent.names (90%) rename {Gems/ScriptCanvas/Assets => AutomatedTesting/ScriptCanvas}/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names (100%) diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names diff --git a/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomous_PlayerNumberNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomous_PlayerNumberNotifyEvent.names new file mode 100644 index 0000000000..7255c61d23 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomous_PlayerNumberNotifyEvent.names @@ -0,0 +1,56 @@ +{ + "entries": [ + { + "base": "AuthorityToAutonomous_PlayerNumber Notify Event", + "context": "AZEventHandler", + "variant": "", + "details": { + "name": "Authority To Autonomous_ Player Number Notify Event" + }, + "slots": [ + { + "base": "player_number", + "details": { + "name": "player_number" + } + }, + { + "base": "AuthorityToAutonomous_PlayerNumber Notify Event", + "details": { + "name": "AuthorityToAutonomous_PlayerNumber Notify Event" + } + }, + { + "base": "Connect", + "details": { + "name": "Connect" + } + }, + { + "base": "Disconnect", + "details": { + "name": "Disconnect" + } + }, + { + "base": "On Connected", + "details": { + "name": "On Connected" + } + }, + { + "base": "On Disconnected", + "details": { + "name": "On Disconnected" + } + }, + { + "base": "OnEvent", + "details": { + "name": "OnEvent" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names diff --git a/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParams_PlayFxNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParams_PlayFxNotifyEvent.names new file mode 100644 index 0000000000..3e0207a705 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParams_PlayFxNotifyEvent.names @@ -0,0 +1,50 @@ +{ + "entries": [ + { + "base": "AuthorityToClientNoParams_PlayFx Notify Event", + "context": "AZEventHandler", + "variant": "", + "details": { + "name": "Authority To Client No Params_ Play Fx Notify Event" + }, + "slots": [ + { + "base": "AuthorityToClientNoParams_PlayFx Notify Event", + "details": { + "name": "AuthorityToClientNoParams_PlayFx Notify Event" + } + }, + { + "base": "Connect", + "details": { + "name": "Connect" + } + }, + { + "base": "Disconnect", + "details": { + "name": "Disconnect" + } + }, + { + "base": "On Connected", + "details": { + "name": "On Connected" + } + }, + { + "base": "On Disconnected", + "details": { + "name": "On Disconnected" + } + }, + { + "base": "OnEvent", + "details": { + "name": "OnEvent" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names diff --git a/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthority_DealDamageNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthority_DealDamageNotifyEvent.names new file mode 100644 index 0000000000..2505d9593a --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthority_DealDamageNotifyEvent.names @@ -0,0 +1,56 @@ +{ + "entries": [ + { + "base": "ServerToAuthority_DealDamage Notify Event", + "context": "AZEventHandler", + "variant": "", + "details": { + "name": "Server To Authority_ Deal Damage Notify Event" + }, + "slots": [ + { + "base": "damage", + "details": { + "name": "damage" + } + }, + { + "base": "ServerToAuthority_DealDamage Notify Event", + "details": { + "name": "ServerToAuthority_DealDamage Notify Event" + } + }, + { + "base": "Connect", + "details": { + "name": "Connect" + } + }, + { + "base": "Disconnect", + "details": { + "name": "Disconnect" + } + }, + { + "base": "On Connected", + "details": { + "name": "On Connected" + } + }, + { + "base": "On Disconnected", + "details": { + "name": "On Disconnected" + } + }, + { + "base": "OnEvent", + "details": { + "name": "OnEvent" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponent.names similarity index 90% rename from Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponent.names index 22636e0453..03e949f784 100644 --- a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponent.names +++ b/AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponent.names @@ -9,76 +9,72 @@ }, "methods": [ { - "base": "AutonomousToAuthority", + "base": "AutonomousToAuthorityByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Autonomous To Authority" + "tooltip": "When signaled, this will invoke Autonomous To Authority By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Autonomous To Authority is invoked" + "tooltip": "Signaled after Autonomous To Authority By Entity Id is invoked" }, "details": { - "name": "Autonomous To Authority" + "name": "Autonomous To Authority By Entity Id" }, "params": [ { - "typeid": "{CA5E5C37-98A6-04D2-E15C-1B4BFEE4C7DD}", + "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", "details": { - "name": "Network Test Player Component" + "name": "Source", + "tooltip": "The Source containing the NetworkTestPlayerComponentController" } }, { "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", "details": { - "name": "float" + "name": "some Float" } } ] }, { - "base": "ServerToAuthority", + "base": "AuthorityToClientNoParams_PlayFxByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Server To Authority" + "tooltip": "When signaled, this will invoke Authority To Client No Params_ Play Fx By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Server To Authority is invoked" + "tooltip": "Signaled after Authority To Client No Params_ Play Fx By Entity Id is invoked" }, "details": { - "name": "Server To Authority" + "name": "Authority To Client No Params_ Play Fx By Entity Id" }, "params": [ { - "typeid": "{CA5E5C37-98A6-04D2-E15C-1B4BFEE4C7DD}", - "details": { - "name": "Network Test Player Component" - } - }, - { - "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", + "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", "details": { - "name": "float" + "name": "Source", + "tooltip": "The Source containing the NetworkTestPlayerComponentController" } } ] }, { - "base": "AutonomousToAuthorityByEntityId", + "base": "AuthorityToAutonomous_PlayerNumberByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Autonomous To Authority By Entity Id" + "tooltip": "When signaled, this will invoke Authority To Autonomous_ Player Number By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Autonomous To Authority By Entity Id is invoked" + "tooltip": "Signaled after Authority To Autonomous_ Player Number By Entity Id is invoked" }, "details": { - "name": "Autonomous To Authority By Entity Id" + "name": "Authority To Autonomous_ Player Number By Entity Id" }, "params": [ { @@ -89,26 +85,26 @@ } }, { - "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", + "typeid": "{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}", "details": { - "name": "some Float" + "name": "player_number" } } ] }, { - "base": "ServerToAuthorityByEntityId", + "base": "ServerToAuthority_DealDamageByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Server To Authority By Entity Id" + "tooltip": "When signaled, this will invoke Server To Authority_ Deal Damage By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Server To Authority By Entity Id is invoked" + "tooltip": "Signaled after Server To Authority_ Deal Damage By Entity Id is invoked" }, "details": { - "name": "Server To Authority By Entity Id" + "name": "Server To Authority_ Deal Damage By Entity Id" }, "params": [ { @@ -121,24 +117,48 @@ { "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", "details": { - "name": "some Float" + "name": "damage" } } ] }, { - "base": "AutonomousToAuthorityNoParams", + "base": "AutonomousToAuthorityNoParamsByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Autonomous To Authority No Params" + "tooltip": "When signaled, this will invoke Autonomous To Authority No Params By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Autonomous To Authority No Params is invoked" + "tooltip": "Signaled after Autonomous To Authority No Params By Entity Id is invoked" }, "details": { - "name": "Autonomous To Authority No Params" + "name": "Autonomous To Authority No Params By Entity Id" + }, + "params": [ + { + "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", + "details": { + "name": "Source", + "tooltip": "The Source containing the NetworkTestPlayerComponentController" + } + } + ] + }, + { + "base": "ServerToAuthority_DealDamage", + "context": "NetworkTestPlayerComponent", + "entry": { + "name": "In", + "tooltip": "When signaled, this will invoke Server To Authority_ Deal Damage" + }, + "exit": { + "name": "Out", + "tooltip": "Signaled after Server To Authority_ Deal Damage is invoked" + }, + "details": { + "name": "Server To Authority_ Deal Damage" }, "params": [ { @@ -146,22 +166,28 @@ "details": { "name": "Network Test Player Component" } + }, + { + "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", + "details": { + "name": "float" + } } ] }, { - "base": "AuthorityToAutonomous", + "base": "AutonomousToAuthority", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Authority To Autonomous" + "tooltip": "When signaled, this will invoke Autonomous To Authority" }, "exit": { "name": "Out", - "tooltip": "Signaled after Authority To Autonomous is invoked" + "tooltip": "Signaled after Autonomous To Authority is invoked" }, "details": { - "name": "Authority To Autonomous" + "name": "Autonomous To Authority" }, "params": [ { @@ -179,18 +205,18 @@ ] }, { - "base": "AuthorityToClientNoParams", + "base": "AuthorityToAutonomous_PlayerNumber", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Authority To Client No Params" + "tooltip": "When signaled, this will invoke Authority To Autonomous_ Player Number" }, "exit": { "name": "Out", - "tooltip": "Signaled after Authority To Client No Params is invoked" + "tooltip": "Signaled after Authority To Autonomous_ Player Number is invoked" }, "details": { - "name": "Authority To Client No Params" + "name": "Authority To Autonomous_ Player Number" }, "params": [ { @@ -198,6 +224,12 @@ "details": { "name": "Network Test Player Component" } + }, + { + "typeid": "{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}", + "details": { + "name": "int" + } } ] }, @@ -308,79 +340,47 @@ ] }, { - "base": "AuthorityToClientNoParamsByEntityId", - "context": "NetworkTestPlayerComponent", - "entry": { - "name": "In", - "tooltip": "When signaled, this will invoke Authority To Client No Params By Entity Id" - }, - "exit": { - "name": "Out", - "tooltip": "Signaled after Authority To Client No Params By Entity Id is invoked" - }, - "details": { - "name": "Authority To Client No Params By Entity Id" - }, - "params": [ - { - "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", - "details": { - "name": "Source", - "tooltip": "The Source containing the NetworkTestPlayerComponentController" - } - } - ] - }, - { - "base": "AuthorityToAutonomousByEntityId", + "base": "AutonomousToAuthorityNoParams", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Authority To Autonomous By Entity Id" + "tooltip": "When signaled, this will invoke Autonomous To Authority No Params" }, "exit": { "name": "Out", - "tooltip": "Signaled after Authority To Autonomous By Entity Id is invoked" + "tooltip": "Signaled after Autonomous To Authority No Params is invoked" }, "details": { - "name": "Authority To Autonomous By Entity Id" + "name": "Autonomous To Authority No Params" }, "params": [ { - "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", - "details": { - "name": "Source", - "tooltip": "The Source containing the NetworkTestPlayerComponentController" - } - }, - { - "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", + "typeid": "{CA5E5C37-98A6-04D2-E15C-1B4BFEE4C7DD}", "details": { - "name": "some Float" + "name": "Network Test Player Component" } } ] }, { - "base": "AutonomousToAuthorityNoParamsByEntityId", + "base": "AuthorityToClientNoParams_PlayFx", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Autonomous To Authority No Params By Entity Id" + "tooltip": "When signaled, this will invoke Authority To Client No Params_ Play Fx" }, "exit": { "name": "Out", - "tooltip": "Signaled after Autonomous To Authority No Params By Entity Id is invoked" + "tooltip": "Signaled after Authority To Client No Params_ Play Fx is invoked" }, "details": { - "name": "Autonomous To Authority No Params By Entity Id" + "name": "Authority To Client No Params_ Play Fx" }, "params": [ { - "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", + "typeid": "{CA5E5C37-98A6-04D2-E15C-1B4BFEE4C7DD}", "details": { - "name": "Source", - "tooltip": "The Source containing the NetworkTestPlayerComponentController" + "name": "Network Test Player Component" } } ] diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names From 0a5f472f4331d5a310e55ac8b9d5dabdc491d079 Mon Sep 17 00:00:00 2001 From: Daniel Edwards Date: Sat, 22 Jan 2022 14:36:20 +0100 Subject: [PATCH 52/61] Clang 13: Fix build errors ... ... due to local variables only being written to (but never read). Signed-off-by: Daniel Edwards --- Code/Editor/EditorPreferencesPageAWS.cpp | 2 +- Code/Editor/Settings.cpp | 2 +- Code/Editor/ViewPane.cpp | 2 +- Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp | 6 +++--- Code/Framework/AzCore/AzCore/Math/Uuid.cpp | 2 +- Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp | 2 +- Code/Framework/AzCore/Tests/Name/NameTests.cpp | 2 +- .../AzFramework/Asset/Benchmark/BenchmarkCommands.cpp | 2 +- Code/Framework/AzFramework/Tests/CameraState.cpp | 2 +- .../AzQtComponents/Components/StylesheetPreprocessor.cpp | 2 +- .../AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp | 2 +- .../AzToolsFramework/Prefab/PrefabSystemComponent.cpp | 4 ++-- .../AzToolsFramework/Slice/SliceTransaction.cpp | 2 +- .../UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp | 2 +- Code/Framework/AzToolsFramework/Tests/Slices.cpp | 2 +- Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp | 4 ++-- .../native/utilities/ApplicationManagerBase.cpp | 2 +- .../AssetProcessor/native/utilities/BuilderManager.inl | 2 +- Code/Tools/ProjectManager/Source/Settings.cpp | 2 +- Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp | 2 +- .../Source/Editor/Attribution/AWSCoreAttributionManager.cpp | 2 +- .../AssetValidation/Code/Source/AssetSystemTestCommands.cpp | 6 +++--- .../DiffuseProbeGridFeatureProcessor.cpp | 2 +- .../Code/Source/RayTracing/RayTracingFeatureProcessor.cpp | 2 +- .../ReflectionProbe/ReflectionProbeFeatureProcessor.cpp | 2 +- Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp | 2 +- Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp | 2 +- .../RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp | 2 +- .../RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp | 2 +- Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp | 4 ++-- Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp | 2 +- Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 2 +- Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp | 2 +- Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp | 2 +- Gems/GraphModel/Code/Source/Model/Node.cpp | 2 +- Gems/GraphModel/Code/Source/Model/Slot.cpp | 2 +- .../LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp | 2 +- .../EntityReplication/EntityReplicationManager.cpp | 2 +- .../Components/ClothComponentMesh/ActorClothColliders.cpp | 4 ++-- .../Source/PhysXCharacters/Components/RagdollComponent.cpp | 2 +- .../NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp | 2 +- .../Code/Source/TerrainRenderer/TerrainMeshManager.cpp | 2 +- 42 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Code/Editor/EditorPreferencesPageAWS.cpp b/Code/Editor/EditorPreferencesPageAWS.cpp index 9279dce7bc..968969245d 100644 --- a/Code/Editor/EditorPreferencesPageAWS.cpp +++ b/Code/Editor/EditorPreferencesPageAWS.cpp @@ -101,7 +101,7 @@ void CEditorPreferencesPage_AWS::SaveSettingsRegistryFile() return; } - bool saved{}; + [[maybe_unused]] bool saved{}; constexpr auto configurationMode = AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPath.data(), configurationMode)) diff --git a/Code/Editor/Settings.cpp b/Code/Editor/Settings.cpp index 8561e6dba3..9514957795 100644 --- a/Code/Editor/Settings.cpp +++ b/Code/Editor/Settings.cpp @@ -1128,7 +1128,7 @@ void SEditorSettings::SaveSettingsRegistryFile() return; } - bool saved{}; + [[maybe_unused]] bool saved{}; constexpr auto configurationMode = AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; diff --git a/Code/Editor/ViewPane.cpp b/Code/Editor/ViewPane.cpp index e3494a4049..90b44d9c9d 100644 --- a/Code/Editor/ViewPane.cpp +++ b/Code/Editor/ViewPane.cpp @@ -745,7 +745,7 @@ namespace void PySetActiveViewport(unsigned int viewportIndex) { - bool success = false; + [[maybe_unused]] bool success = false; CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout(); if (layout) { diff --git a/Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp b/Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp index 78da9e76d2..23d0eea618 100644 --- a/Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp @@ -25,7 +25,7 @@ namespace AZ void Matrix3x4SetRowGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc) { - bool rowIsSet = false; + [[maybe_unused]] bool rowIsSet = false; if (dc.GetNumArguments() >= 5) { if (dc.IsNumber(0)) @@ -88,7 +88,7 @@ namespace AZ void Matrix3x4SetColumnGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc) { - bool columnIsSet = false; + [[maybe_unused]] bool columnIsSet = false; if (dc.GetNumArguments() >= 4) { if (dc.IsNumber(0)) @@ -133,7 +133,7 @@ namespace AZ void Matrix3x4SetTranslationGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc) { - bool translationIsSet = false; + [[maybe_unused]] bool translationIsSet = false; if (dc.GetNumArguments() == 3 && dc.IsNumber(0) && diff --git a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp index 410d235a37..b27f1a0943 100644 --- a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp @@ -77,7 +77,7 @@ namespace AZ // check open brace char c = *current++; - bool has_open_brace = false; + [[maybe_unused]] bool has_open_brace = false; if (c == '{') { c = *current++; diff --git a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp index 1b39eb81bd..2bb4faf1d5 100644 --- a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp +++ b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp @@ -85,7 +85,7 @@ namespace AZ NameDictionary::~NameDictionary() { - bool leaksDetected = false; + [[maybe_unused]] bool leaksDetected = false; for (const auto& keyValue : m_dictionary) { diff --git a/Code/Framework/AzCore/Tests/Name/NameTests.cpp b/Code/Framework/AzCore/Tests/Name/NameTests.cpp index eb0a048e2f..9e94c4bfdf 100644 --- a/Code/Framework/AzCore/Tests/Name/NameTests.cpp +++ b/Code/Framework/AzCore/Tests/Name/NameTests.cpp @@ -642,7 +642,7 @@ namespace UnitTest char buffer[RandomStringBufferSize]; AZStd::sys_time_t newNameTime; - AZStd::sys_time_t existingNameTime; + [[maybe_unused]] AZStd::sys_time_t existingNameTime; AZStd::sys_time_t stringTime; { diff --git a/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkCommands.cpp b/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkCommands.cpp index 30c1aa39c0..2c2571ec76 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkCommands.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkCommands.cpp @@ -195,7 +195,7 @@ namespace AzFramework::AssetBenchmark // Console command: Add the given list of assets to the list of assets to load with BenchmarkLoadAssetList void BenchmarkAddAssetsToList(const AZ::ConsoleCommandContainer& parameters) { - bool allAssetsAdded = true; + [[maybe_unused]] bool allAssetsAdded = true; for (auto& assetName : parameters) { diff --git a/Code/Framework/AzFramework/Tests/CameraState.cpp b/Code/Framework/AzFramework/Tests/CameraState.cpp index 1f35d5a06c..974b5dd742 100644 --- a/Code/Framework/AzFramework/Tests/CameraState.cpp +++ b/Code/Framework/AzFramework/Tests/CameraState.cpp @@ -81,7 +81,7 @@ namespace UnitTest TEST_P(Rotation, Permutation) { - int expectedErrors = -1; + [[maybe_unused]] int expectedErrors = -1; AZ_TEST_START_TRACE_SUPPRESSION; // Given an orientation derived from the look at points diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/StylesheetPreprocessor.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/StylesheetPreprocessor.cpp index c334bbb59a..2602ecf535 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/StylesheetPreprocessor.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/StylesheetPreprocessor.cpp @@ -134,7 +134,7 @@ namespace AzQtComponents QColor color; QString colorName(m_namedVariables.value(name)); - bool colorSet = false; + [[maybe_unused]] bool colorSet = false; if (QColor::isValidColor(colorName)) { color.setNamedColor(colorName); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index 5711ca2608..4aab7288fa 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -177,7 +177,7 @@ namespace AzToolsFramework auto data = index.data(AssetBrowserModel::Roles::EntryRole); if (data.canConvert()) { - bool isEnabled = (option.state & QStyle::State_Enabled) != 0; + [[maybe_unused]] bool isEnabled = (option.state & QStyle::State_Enabled) != 0; QStyle* style = option.widget ? option.widget->style() : QApplication::style(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 98e0144f22..9648a9af09 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -506,7 +506,7 @@ namespace AzToolsFramework //Remove all Links owned by the Template from TemplateToLinkIdsMap. Template& templateToDelete = findTemplateResult->get(); const Template::Links& linkIdsToDelete = templateToDelete.GetLinks(); - bool result; + [[maybe_unused]] bool result; for (auto linkId : linkIdsToDelete) { result = RemoveLinkIdFromTemplateToLinkIdsMap(linkId); @@ -774,7 +774,7 @@ namespace AzToolsFramework } Link& link = findLinkResult->get(); - bool result; + [[maybe_unused]] bool result; result = RemoveLinkIdFromTemplateToLinkIdsMap(linkId, link); AZ_Assert(result, "Prefab - PrefabSystemComponent::RemoveLink - " diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp index 9d2c58a717..91517bd784 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp @@ -926,7 +926,7 @@ namespace AzToolsFramework const bool entityIsFromIgnoredSliceInstance = ignoreSliceInstance && ignoreSliceInstance->IsValid() && ignoreSliceInstance->GetReference()->GetSliceAsset().GetId() == instanceAddr.GetReference()->GetSliceAsset().GetId(); if (!entityIsFromIgnoredSliceInstance) { - bool foundTargetAncestor = false; + [[maybe_unused]] bool foundTargetAncestor = false; const AZ::SliceComponent::EntityList& entitiesInInstance = instanceAddr.GetInstance()->GetInstantiated()->m_entities; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp index f49b555dfe..f37fd860ea 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp @@ -45,7 +45,7 @@ namespace AzToolsFramework void PropertyEnumComboBoxCtrl::setValue(AZ::s64 value) { m_pComboBox->blockSignals(true); - bool indexWasFound = false; + [[maybe_unused]] bool indexWasFound = false; for (size_t enumValIndex = 0; enumValIndex < m_enumValues.size(); enumValIndex++) { if (m_enumValues[enumValIndex].first == value) diff --git a/Code/Framework/AzToolsFramework/Tests/Slices.cpp b/Code/Framework/AzToolsFramework/Tests/Slices.cpp index 9b546357cd..bf59e1f8bd 100644 --- a/Code/Framework/AzToolsFramework/Tests/Slices.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Slices.cpp @@ -430,7 +430,7 @@ namespace UnitTest size_t nextIndex = 1; size_t slices = 0; size_t liveAllocs = 0; - size_t totalAllocs = 0; + [[maybe_unused]] size_t totalAllocs = 0; auto cb = [&liveAllocs](void*, const AZ::Debug::AllocationInfo&, unsigned char) { diff --git a/Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp b/Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp index 2724bee1a7..9d88f26278 100644 --- a/Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp +++ b/Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp @@ -1952,8 +1952,8 @@ CarrierThread::ProcessConnections() bool isHandshakeTimeOut = false; bool isConnectionTimeout = false; - bool isBadTrafficConditions = false; - bool isBadPackets = false; + [[maybe_unused]] bool isBadTrafficConditions = false; + [[maybe_unused]] bool isBadPackets = false; if (connection->m_isBadPackets) { isBadPackets = true; diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp index 36450c4e65..634fdbd3bb 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp @@ -544,7 +544,7 @@ void ApplicationManagerBase::InitConnectionManager() EBUS_EVENT(AssetProcessor::ConnectionBus, SendPerPlatform, 0, message, QString::fromUtf8(message.m_platform.c_str())); }; - bool result = QObject::connect(GetAssetCatalog(), &AssetProcessor::AssetCatalog::SendAssetMessage, connectionAndChangeMessagesThreadContext, forwardMessageFunction, Qt::QueuedConnection); + [[maybe_unused]] bool result = QObject::connect(GetAssetCatalog(), &AssetProcessor::AssetCatalog::SendAssetMessage, connectionAndChangeMessagesThreadContext, forwardMessageFunction, Qt::QueuedConnection); AZ_Assert(result, "Failed to connect to AssetCatalog signal"); //Application manager related stuff diff --git a/Code/Tools/AssetProcessor/native/utilities/BuilderManager.inl b/Code/Tools/AssetProcessor/native/utilities/BuilderManager.inl index 039680dfae..4d1983691a 100644 --- a/Code/Tools/AssetProcessor/native/utilities/BuilderManager.inl +++ b/Code/Tools/AssetProcessor/native/utilities/BuilderManager.inl @@ -19,7 +19,7 @@ namespace AssetProcessor TNetResponse netResponse; netRequest.m_request = request; - AZ::u32 type; + [[maybe_unused]] AZ::u32 type; QByteArray data; AZStd::binary_semaphore wait; diff --git a/Code/Tools/ProjectManager/Source/Settings.cpp b/Code/Tools/ProjectManager/Source/Settings.cpp index 0a8b600dd9..78100848b5 100644 --- a/Code/Tools/ProjectManager/Source/Settings.cpp +++ b/Code/Tools/ProjectManager/Source/Settings.cpp @@ -41,7 +41,7 @@ namespace O3DE::ProjectManager o3deUserPath /= AZ::SettingsRegistryInterface::RegistryFolder; o3deUserPath /= "ProjectManager.setreg"; - bool saved = false; + [[maybe_unused]] bool saved = false; constexpr auto configurationMode = AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; diff --git a/Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp b/Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp index 5c0d2503c0..2b7497951f 100644 --- a/Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp +++ b/Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp @@ -25,7 +25,7 @@ protected: sceneCoreModule = AZ::DynamicModuleHandle::Create("SceneCore"); AZ_Assert(sceneCoreModule, "SceneBuilder unit tests failed to create SceneCore module."); - bool loaded = sceneCoreModule->Load(false); + [[maybe_unused]] bool loaded = sceneCoreModule->Load(false); AZ_Assert(loaded, "SceneBuilder unit tests failed to load SceneCore module."); auto init = sceneCoreModule->GetFunction(AZ::InitializeDynamicModuleFunctionName); AZ_Assert(init, "SceneBuilder unit tests failed to find the initialization function the SceneCore module."); diff --git a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp index 3b9352d250..c52d3be088 100644 --- a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp +++ b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp @@ -223,7 +223,7 @@ namespace AWSCore return; } - bool saved {}; + [[maybe_unused]] bool saved {}; constexpr auto configurationMode = AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPathAWSPreference.c_str(), configurationMode)) diff --git a/Gems/AssetValidation/Code/Source/AssetSystemTestCommands.cpp b/Gems/AssetValidation/Code/Source/AssetSystemTestCommands.cpp index 18888c7166..2007ac01a9 100644 --- a/Gems/AssetValidation/Code/Source/AssetSystemTestCommands.cpp +++ b/Gems/AssetValidation/Code/Source/AssetSystemTestCommands.cpp @@ -120,9 +120,9 @@ namespace AssetValidation AZ::SimpleLcgRandom randomizer(seedValue); int lastTick = 0; AZStd::vector> heldAssets; - AZStd::size_t heldCount{ 0 }; - AZ::u64 changeCount{ 0 }; - AZ::u64 blockCount{ 0 }; + [[maybe_unused]] AZStd::size_t heldCount{ 0 }; + [[maybe_unused]] AZ::u64 changeCount{ 0 }; + [[maybe_unused]] AZ::u64 blockCount{ 0 }; AZ_TracePrintf("TestChangeAssets", "Beginning run with %zu assets\n", assetList.size()); while (!forceStop && runMs < runTime) { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index ea02a8e0a2..d690c57dea 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -537,7 +537,7 @@ namespace AZ request.m_buffer = m_boxIndexBuffer.get(); request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, m_boxIndices.size() * sizeof(uint16_t) }; request.m_initialData = m_boxIndices.data(); - AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request); + [[maybe_unused]] AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request); AZ_Error("DiffuseProbeGridFeatureProcessor", result == RHI::ResultCode::Success, "Failed to initialize box index buffer - error [%d]", result); // create index buffer view diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index 0a9782980f..f5c1be284e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -123,7 +123,7 @@ namespace AZ // create the BLAS buffers for each sub-mesh, or re-use existing BLAS objects if they were already created. // Note: all sub-meshes must either create new BLAS objects or re-use existing ones, otherwise it's an error (it's the same model in both cases) // Note: the buffer is just reserved here, the BLAS is built in the RayTracingAccelerationStructurePass - bool blasInstanceFound = false; + [[maybe_unused]] bool blasInstanceFound = false; for (uint32_t subMeshIndex = 0; subMeshIndex < mesh.m_subMeshes.size(); ++subMeshIndex) { SubMesh& subMesh = mesh.m_subMeshes[subMeshIndex]; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 0f9356428a..3ff574d977 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -397,7 +397,7 @@ namespace AZ request.m_buffer = m_boxIndexBuffer.get(); request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, m_boxIndices.size() * sizeof(uint16_t) }; request.m_initialData = m_boxIndices.data(); - AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request); + [[maybe_unused]] AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request); AZ_Error("ReflectionProbeFeatureProcessor", result == RHI::ResultCode::Success, "Failed to initialize box index buffer - error [%d]", result); // create index buffer view diff --git a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp index 79c05c37da..3279372052 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp @@ -364,7 +364,7 @@ namespace AZ AZ_Assert(success, "PipelineStateEntry already exists in the pending cache."); } - ResultCode resultCode = ResultCode::InvalidArgument; + [[maybe_unused]] ResultCode resultCode = ResultCode::InvalidArgument; // Increment the pending compile count on the global entry, which tracks how many pipeline states // are currently being compiled across all threads. diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp index a322380fb1..e4a90d0739 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp @@ -120,7 +120,7 @@ namespace AZ AZ::RHI::BufferInitRequest scratchBufferRequest; scratchBufferRequest.m_buffer = buffers.m_scratchBuffer.get(); scratchBufferRequest.m_descriptor = scratchBufferDescriptor; - RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest); + [[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest); AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create BLAS scratch buffer"); BufferMemoryView* scratchMemoryView = static_cast(buffers.m_scratchBuffer.get())->GetBufferMemoryView(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp index 65281d923f..bddfb0df5e 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp @@ -170,7 +170,7 @@ namespace AZ createInfo.basePipelineHandle = nullptr; createInfo.basePipelineIndex = 0; - VkResult result = vkCreateRayTracingPipelinesKHR(device.GetNativeDevice(), nullptr, nullptr, 1, &createInfo, nullptr, &m_pipeline); + [[maybe_unused]] VkResult result = vkCreateRayTracingPipelinesKHR(device.GetNativeDevice(), nullptr, nullptr, 1, &createInfo, nullptr, &m_pipeline); AZ_Assert(result == VK_SUCCESS, "vkCreateRayTracingPipelinesKHR failed"); // retrieve the shader handles diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp index d5ef121875..5e820a18ba 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp @@ -43,7 +43,7 @@ namespace AZ AZ::RHI::BufferInitRequest shaderTableBufferRequest; shaderTableBufferRequest.m_buffer = shaderTableBuffer.get(); shaderTableBufferRequest.m_descriptor = shaderTableBufferDescriptor; - RHI::ResultCode resultCode = bufferPools.GetShaderTableBufferPool()->InitBuffer(shaderTableBufferRequest); + [[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetShaderTableBufferPool()->InitBuffer(shaderTableBufferRequest); AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create shader table buffer"); BufferMemoryView* shaderTableMemoryView = static_cast(shaderTableBuffer.get())->GetBufferMemoryView(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp index 9ea9ceccce..08b933c96b 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp @@ -66,7 +66,7 @@ namespace AZ AZ::RHI::BufferInitRequest tlasInstancesBufferRequest; tlasInstancesBufferRequest.m_buffer = buffers.m_tlasInstancesBuffer.get(); tlasInstancesBufferRequest.m_descriptor = tlasInstancesBufferDescriptor; - RHI::ResultCode resultCode = bufferPools.GetTlasInstancesBufferPool()->InitBuffer(tlasInstancesBufferRequest); + [[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetTlasInstancesBufferPool()->InitBuffer(tlasInstancesBufferRequest); AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create TLAS instances buffer"); BufferMemoryView* tlasInstancesMemoryView = static_cast(buffers.m_tlasInstancesBuffer.get())->GetBufferMemoryView(); @@ -160,7 +160,7 @@ namespace AZ AZ::RHI::BufferInitRequest scratchBufferRequest; scratchBufferRequest.m_buffer = buffers.m_scratchBuffer.get(); scratchBufferRequest.m_descriptor = scratchBufferDescriptor; - RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest); + [[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest); AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create TLAS scratch buffer"); BufferMemoryView* scratchMemoryView = static_cast(buffers.m_scratchBuffer.get())->GetBufferMemoryView(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index bd196a2e2b..41cfb5ff12 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -312,7 +312,7 @@ namespace AZ const View::UsageFlags viewFlags = worklistData->m_view->GetUsageFlags(); const RHI::DrawListMask drawListMask = worklistData->m_view->GetDrawListMask(); - uint32_t numDrawPackets = 0; + [[maybe_unused]] uint32_t numDrawPackets = 0; uint32_t numVisibleCullables = 0; AZ_Assert(worklist.size() > 0, "Received empty worklist in ProcessWorklist"); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 1b5110e327..83ac77a390 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -322,7 +322,7 @@ namespace AZ void Scene::RemoveRenderPipeline(const RenderPipelineId& pipelineId) { - bool removed = false; + [[maybe_unused]] bool removed = false; for (auto it = m_pipelines.begin(); it != m_pipelines.end(); ++it) { if (pipelineId == (*it)->GetId()) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp index 9130636e2a..72d574508f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp @@ -123,7 +123,7 @@ namespace EMotionFX TargetType* targetBuffer = static_cast(targetVertexAttributeLayer->GetData()); // Fill the vertex attribute layer by iterating through the Atom meshes and copying over the vertex data for each. - size_t addedElements = 0; + [[maybe_unused]] size_t addedElements = 0; for (const AZ::RPI::ModelLodAsset::Mesh& atomMesh : sourceModelLod->GetMeshes()) { const uint32_t atomMeshVertexCount = atomMesh.GetVertexCount(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp index 54fb130ad3..116026e8c1 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp @@ -863,7 +863,7 @@ namespace EMotionFX } // process all objects for this frame - size_t totalBytesRead = 0; + [[maybe_unused]] size_t totalBytesRead = 0; const size_t numObjects = frameObjects.size(); for (size_t a = 0; a < numObjects; ++a) { diff --git a/Gems/GraphModel/Code/Source/Model/Node.cpp b/Gems/GraphModel/Code/Source/Model/Node.cpp index f51bd1f617..e55b7c05a4 100644 --- a/Gems/GraphModel/Code/Source/Model/Node.cpp +++ b/Gems/GraphModel/Code/Source/Model/Node.cpp @@ -68,7 +68,7 @@ namespace GraphModel CreateSlotData(m_inputEventSlots, m_inputEventSlotDefinitions); CreateSlotData(m_outputEventSlots, m_outputEventSlotDefinitions); - int numExtendableSlots = 0; + [[maybe_unused]] int numExtendableSlots = 0; for (auto it = m_extendableSlots.begin(); it != m_extendableSlots.end(); it++) { numExtendableSlots += aznumeric_cast(it->second.size()); diff --git a/Gems/GraphModel/Code/Source/Model/Slot.cpp b/Gems/GraphModel/Code/Source/Model/Slot.cpp index 08b52760cb..108067cc93 100644 --- a/Gems/GraphModel/Code/Source/Model/Slot.cpp +++ b/Gems/GraphModel/Code/Source/Model/Slot.cpp @@ -491,7 +491,7 @@ namespace GraphModel // multiple supported types, Slot::GetDataType() will call GetParentNode() // to try and resolve its type, which will be a nullptr at this point // because the parent won't be valid yet - bool valueTypeSupported = false; + [[maybe_unused]] bool valueTypeSupported = false; DataTypePtr valueDataType = GetGraphContext()->GetDataTypeForValue(m_value); for (DataTypePtr dataType : GetSupportedDataTypes()) { diff --git a/Gems/LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp b/Gems/LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp index 9bb98dd575..809803c3a7 100644 --- a/Gems/LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp +++ b/Gems/LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp @@ -156,7 +156,7 @@ PropertyEntityIdComboBoxCtrl::PropertyEntityIdComboBoxCtrl(QWidget* pParent) void PropertyEntityIdComboBoxCtrl::setValue(AZ::EntityId value) { m_pComboBox->blockSignals(true); - bool indexWasFound = false; + [[maybe_unused]] bool indexWasFound = false; for (size_t enumValIndex = 0; enumValIndex < m_enumValues.size(); enumValIndex++) { if (m_enumValues[enumValIndex].first == value) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index e095af4ae7..893799a296 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -1126,7 +1126,7 @@ namespace Multiplayer netBindComponent->NotifyServerMigration(GetRemoteHostId()); } - bool didSucceed = true; + [[maybe_unused]] bool didSucceed = true; EntityMigrationMessage message; message.m_netEntityId = replicator->GetEntityHandle().GetNetEntityId(); message.m_prefabEntityId = netBindComponent->GetPrefabEntityId(); diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.cpp b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.cpp index 1e57999b6e..cde8740694 100644 --- a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.cpp +++ b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.cpp @@ -72,8 +72,8 @@ namespace NvCloth // Maximum number of spheres and capsules is imposed by NvCloth library size_t sphereCount = 0; size_t capsuleCount = 0; - bool maxSphereCountReachedWarned = false; - bool maxCapsuleCountReachedWarned = false; + [[maybe_unused]] bool maxSphereCountReachedWarned = false; + [[maybe_unused]] bool maxCapsuleCountReachedWarned = false; AZStd::vector sphereColliders; AZStd::vector capsuleColliders; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 7615a175d1..2c00fd188d 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -422,7 +422,7 @@ namespace PhysX return d1.m_depth < d2.m_depth; }); - bool massesClamped = false; + [[maybe_unused]] bool massesClamped = false; for (const auto& nodeDepth : nodeDepths) { const size_t nodeIndex = nodeDepth.m_index; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp index da0e9c6045..86fdc0342b 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp @@ -145,7 +145,7 @@ namespace ScriptCanvasEditor ScriptEvents::ScriptEventsAsset* data = asset.GetAs(); if (data) { - const ScriptEvents::ScriptEvent* previousDefinition = nullptr; + [[maybe_unused]] const ScriptEvents::ScriptEvent* previousDefinition = nullptr; ScriptEvents::ScriptEventsAsset* previousData = m_asset.GetAs(); if (previousData) { diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp index d689d2635c..4a0d260a74 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp @@ -291,7 +291,7 @@ namespace Terrain modelAssetCreator.Begin(AZ::Uuid::CreateRandom()); uint16_t gridSize = GridSize; - float gridSpacing = GridSpacing; + [[maybe_unused]] float gridSpacing = GridSpacing; for (uint32_t i = 0; i < AZ::RPI::ModelLodAsset::LodCountMax && gridSize > 0; ++i) { From 1643c68fa7f133ff68eb7405e43e5a22f2901de5 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sun, 23 Jan 2022 18:26:31 -0800 Subject: [PATCH 53/61] chore: fix compiling errors Signed-off-by: Michael Pollind --- .../Input/QtEventToAzInputMapperTests.cpp | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index 31193f6f05..78a08af6cb 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -562,7 +562,7 @@ namespace UnitTest AZ::Vector2 accumulatedPosition(0.0f,0.0f); for(const auto& pos: m_azCursorPositions) { - accumulatedPosition += (pos.m_normalizedPositionDelta * AZ::Vector2(WidgetSize.width(), WidgetSize.height())); + accumulatedPosition += (pos.m_normalizedPositionDelta * AZ::Vector2((float)WidgetSize.width(), (float)WidgetSize.height())); } // validate @@ -584,106 +584,106 @@ namespace UnitTest // verify CursorModeWrappedX wrapping MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), QPoint(40, 0), - QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), "CursorModeWrappedX_Test_Right" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, 40, - QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), QPoint(-40, 0), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), "CursorModeWrappedX_Test_Left" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), QPoint(0, -40), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, -20.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, -20), "CursorModeWrappedX_Test_Top" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), QPoint(0, 40), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() + 20), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() + 20), "CursorModeWrappedX_Test_Bottom" }, // verify CursorModeWrappedY wrapping MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), QPoint(40, 0), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() + 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() + 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), "CursorModeWrappedY_Test_Right" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, 40, - QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), QPoint(-40, 0), - QPoint(-20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(-20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), "CursorModeWrappedY_Test_Left" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), QPoint(0, -40), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), "CursorModeWrappedY_Test_Top" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), QPoint(0, 40), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), "CursorModeWrappedY_Test_Bottom" }, // verify CursorModeWrapped wrapping MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), QPoint(40, 0), - QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), "CursorModeWrapped_Test_Right" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, 40, - QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), QPoint(-40, 0), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), "CursorModeWrapped_Test_Left" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), QPoint(0, -40), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), "CursorModeWrapped_Test_Top" }, MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), QPoint(0, 40), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, 20), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), "CursorModeWrapped_Test_Bottom" }, // verify CursorModeCaptured MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeCaptured, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() / 2.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), QPoint(0, 40), - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() / 2.0f), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), "CursorModeCaptured" }, // verify CursorModeNone MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeNone, 40, - QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f, QtEventToAzInputMapperFixture::WidgetSize.height() / 2.0f), - QPoint(40.0f, 0), - QPoint((QtEventToAzInputMapperFixture::WidgetSize.width() / 2.0f) + 40.0f, (QtEventToAzInputMapperFixture::WidgetSize.height() / 2.0f)), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + QPoint(40, 0), + QPoint((QtEventToAzInputMapperFixture::WidgetSize.width() / 2) + 40, (QtEventToAzInputMapperFixture::WidgetSize.height() / 2)), "CursorModeNone" } ), From 3113b8be03c02b12d9b94b1ae5d4d5eae471a948 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Sun, 23 Jan 2022 23:17:25 -0800 Subject: [PATCH 54/61] Fix for 'Cannot Find PThread' when using GCC (#7097) * Fix for 'Cannot Find PThread' when using GCC Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> * Fix error when COMPILATION_C and COMPILATION_CXX handling in ly_append_configurations_options Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> * Add missing 'PARENT_SCOPE' when appending to the CMAKE_C_FLAGS and CMAKE_CXX_FLAGS Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> * Fix appending of CMAKE_C_FLAGS and CMAKE_CXX_FLAGS Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> * Workaround for C/C++ compilation flag settings for gcc. COMPILATION_C/COMPILATION_CXX is wiping out COMPILATION, so define each one separately with no sharing Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> * Remove unnecessary gcc ignore warnings for C files Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> --- cmake/Configurations.cmake | 13 +++++----- .../Common/GCC/Configurations_gcc.cmake | 24 +++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/cmake/Configurations.cmake b/cmake/Configurations.cmake index 63af3d8810..20d94024bb 100644 --- a/cmake/Configurations.cmake +++ b/cmake/Configurations.cmake @@ -42,6 +42,8 @@ include(cmake/ConfigurationTypes.cmake) # \arg:LINK_SHARED # \arg:LINK_SHARED_${CONFIGURATION} # +# Note: COMPILATION_C/COMPILATION_CXX are mutually exclusive with COMPILATION. You can only specify COMPILATION for C/C++ flags or +# a combination of COMPILATION_C/COMPILATION_CXX for the separate c/c++ flags separately. function(ly_append_configurations_options) set(options) @@ -80,15 +82,14 @@ function(ly_append_configurations_options) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILATION_STR}" PARENT_SCOPE) endif() if(ly_append_configurations_options_COMPILATION_C) - string(REPLACE ";" " " COMPILATION_STR "${ly_append_configurations_options_COMPILATION}") - string(APPEND CMAKE_C_FLAGS " " ${COMPILATION_STR}) - set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} PARENT_SCOPE) + string(REPLACE ";" " " COMPILATION_STR "${ly_append_configurations_options_COMPILATION_C}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILATION_STR}" PARENT_SCOPE) endif() if(ly_append_configurations_options_COMPILATION_CXX) - string(REPLACE ";" " " COMPILATION_STR "${ly_append_configurations_options_COMPILATION}") - string(APPEND CMAKE_CXX_FLAGS " " ${COMPILATION_STR}) - set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} PARENT_SCOPE) + string(REPLACE ";" " " COMPILATION_STR "${ly_append_configurations_options_COMPILATION_CXX}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILATION_STR}" PARENT_SCOPE) endif() + if(ly_append_configurations_options_LINK) string(REPLACE ";" " " LINK_STR "${ly_append_configurations_options_LINK}") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} ${LINK_STR}" PARENT_SCOPE) diff --git a/cmake/Platform/Common/GCC/Configurations_gcc.cmake b/cmake/Platform/Common/GCC/Configurations_gcc.cmake index 17c2474fb8..3db96306ee 100644 --- a/cmake/Platform/Common/GCC/Configurations_gcc.cmake +++ b/cmake/Platform/Common/GCC/Configurations_gcc.cmake @@ -20,7 +20,17 @@ endif() ly_append_configurations_options( - COMPILATION + + COMPILATION_C + -fno-exceptions + -fvisibility=hidden + -Wall + -Werror + + ${LY_GCC_GCOV_FLAGS} + ${LY_GCC_GPROF_FLAGS} + + COMPILATION_CXX -fno-exceptions -fvisibility=hidden -Wall @@ -40,10 +50,8 @@ ly_append_configurations_options( -Wno-unused-value -Wno-unused-variable -Wno-format-truncation - -Wno-reorder -Wno-uninitialized -Wno-array-bounds - -Wno-class-memaccess -Wno-nonnull-compare -Wno-strict-aliasing -Wno-unused-result @@ -58,18 +66,14 @@ ly_append_configurations_options( -Wno-enum-compare -Wno-int-in-bool-context -Wno-sequence-point - -Wno-delete-non-virtual-dtor -Wno-comment - -Wno-reorder -Wno-restrict -Wno-format-overflow - - COMPILATION_C - -Wno-absolute-value - - COMPILATION_CXX -fvisibility-inlines-hidden -Wno-invalid-offsetof + -Wno-class-memaccess + -Wno-delete-non-virtual-dtor + -Wno-reorder COMPILATION_DEBUG -O0 # No optimization From 59409822446d896aed127c587eda277c1d47cde1 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Mon, 24 Jan 2022 10:41:32 +0000 Subject: [PATCH 55/61] Ensure render geometry is refreshed when the mesh component controller is moved (#6452) * ensure render geometry is refreshed when the mesh component controller is moved Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * cache bus pointer and send update notification when mesh changes Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * add integration test to detect MeshComponentController notification to IntersectionNotificationBus Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * remove optimize off Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * update build visibility for AtomLyIntegration editor static lib Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * move runtime dependencies to gem module Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> --- .../AzFramework/Render/Intersector.h | 26 ++-- .../CommonFeatures/Code/CMakeLists.txt | 50 +++++++- .../Source/Mesh/MeshComponentController.cpp | 36 ++++-- .../Source/Mesh/MeshComponentController.h | 3 +- .../CommonFeatures/Code/Tests/Main.cpp | 39 ++++++ .../Tests/MeshComponentControllerTests.cpp | 116 ++++++++++++++++++ ...egration_commonfeatures_editor_files.cmake | 1 - ...ion_commonfeatures_editor_test_files.cmake | 11 ++ 8 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Tests/Main.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Tests/MeshComponentControllerTests.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_test_files.cmake diff --git a/Code/Framework/AzFramework/AzFramework/Render/Intersector.h b/Code/Framework/AzFramework/AzFramework/Render/Intersector.h index acd6553c32..ba11996a65 100644 --- a/Code/Framework/AzFramework/AzFramework/Render/Intersector.h +++ b/Code/Framework/AzFramework/AzFramework/Render/Intersector.h @@ -5,25 +5,23 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include #include #include - #include +#include #include #include -#include - namespace AzFramework { - namespace RenderGeometry { - //! Implementation of IntersectorBus interface, this class contains a cached AABB list - //! of the connected entities to the intersection bus and calculates performant ray intersections against them + //! Implementation of IntersectorBus interface, this class contains a cached AABB list of the connected + //! entities to the intersection bus and calculates efficient ray intersections against them. class Intersector final : public IntersectorBus::Handler , protected IntersectionNotificationBus::Handler @@ -34,32 +32,32 @@ namespace AzFramework Intersector(AzFramework::EntityContextId contextId); ~Intersector(); - // IntersectorBus + // IntersectorBus overrides ... RayResult RayIntersect(const RayRequest& ray) override; - // IntersectionNotifications + // IntersectionNotificationBus overrides ... void OnEntityConnected(AZ::EntityId entityId) override; void OnEntityDisconnected(AZ::EntityId entityId) override; void OnGeometryChanged(AZ::EntityId entityId) override; private: - struct EntityData { EntityData(AZ::EntityId id, AZ::Aabb aabb) : m_id(id) , m_aabb(aabb) - , m_ref(1) {} + , m_ref(1) + { + } AZ::EntityId m_id; AZ::Aabb m_aabb; int m_ref; }; - + class EntityDataList { public: - int AddRef(AZ::EntityId entityId); int RemoveRef(AZ::EntityId entityId); void Update(const EntityData& newData); @@ -88,5 +86,5 @@ namespace AzFramework AZStd::vector> m_candidatesSortedByDist; AzFramework::EntityContextId m_contextId; }; - } -} + } // namespace RenderGeometry +} // namespace AzFramework diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt index 3234db2292..7c31751421 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt @@ -77,8 +77,7 @@ ly_create_alias(NAME AtomLyIntegration_CommonFeatures.Servers NAMESPACE Gem if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( - NAME AtomLyIntegration_CommonFeatures.Editor GEM_MODULE - + NAME AtomLyIntegration_CommonFeatures.Editor.Static STATIC NAMESPACE Gem AUTOUIC AUTOMOC @@ -98,7 +97,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) PRIVATE ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR BUILD_DEPENDENCIES - PRIVATE + PUBLIC Gem::AtomLyIntegration_CommonFeatures.Static Gem::Atom_RPI.Edit Gem::AtomToolsFramework.Static @@ -108,6 +107,24 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Legacy::Editor.Headers Legacy::EditorCommon Legacy::CryCommon + ) + + ly_add_target( + NAME AtomLyIntegration_CommonFeatures.Editor GEM_MODULE + NAMESPACE Gem + FILES_CMAKE + atomlyintegration_commonfeatures_shared_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + COMPILE_DEFINITIONS + PRIVATE + ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR + BUILD_DEPENDENCIES + PRIVATE + Gem::AtomLyIntegration_CommonFeatures.Editor.Static RUNTIME_DEPENDENCIES Gem::Atom_RPI.Editor Gem::Atom_Feature_Common.Editor @@ -128,6 +145,33 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AtomLyIntegration_CommonFeatures.Editor Gem::GradientSignal.Tools ) + + ################################################################################ + # Tests + ################################################################################ + if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME AtomLyIntegration_CommonFeatures.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + atomlyintegration_commonfeatures_editor_test_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + AZ::AzTestShared + AZ::AzToolsFramework + AZ::AzToolsFrameworkTestCommon + Gem::AtomLyIntegration_CommonFeatures.Static + Gem::AtomLyIntegration_CommonFeatures.Editor.Static + ) + ly_add_googletest( + NAME Gem::AtomLyIntegration_CommonFeatures.Editor.Tests + ) + endif() endif() # AtomLyIntegration_CommonFeatures gem targets are required as part of the Editor and AssetProcessor diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index a68ad24adf..e26d328e17 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -82,7 +82,6 @@ namespace AZ ->Field("MinimumScreenCoverage", &MeshComponentConfig::m_minimumScreenCoverage) ->Field("QualityDecayRate", &MeshComponentConfig::m_qualityDecayRate); } - } bool MeshComponentConfig::IsAssetSet() @@ -225,33 +224,42 @@ namespace AZ { } + static AzFramework::EntityContextId FindOwningContextId(const AZ::EntityId entityId) + { + AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult( + contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId); + return contextId; + } + void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair) { const AZ::EntityId entityId = entityComponentIdPair.GetEntityId(); m_entityComponentIdPair = entityComponentIdPair; m_transformInterface = TransformBus::FindFirstHandler(entityId); - AZ_Warning("MeshComponentController", m_transformInterface, "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin."); + AZ_Warning( + "MeshComponentController", m_transformInterface, + "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin."); m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId."); m_cachedNonUniformScale = AZ::Vector3::CreateOne(); AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, entityId, &AZ::NonUniformScaleRequests::GetScale); - AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, - m_nonUniformScaleChangedHandler); + AZ::NonUniformScaleRequestBus::Event( + entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler); + const auto entityContextId = FindOwningContextId(entityId); MeshComponentRequestBus::Handler::BusConnect(entityId); TransformNotificationBus::Handler::BusConnect(entityId); MaterialReceiverRequestBus::Handler::BusConnect(entityId); MaterialComponentNotificationBus::Handler::BusConnect(entityId); AzFramework::BoundsRequestBus::Handler::BusConnect(entityId); - AzFramework::EntityContextId contextId; - AzFramework::EntityIdContextQueryBus::EventResult( - contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId); - AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({entityId, contextId}); + AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({ entityId, entityContextId }); + AzFramework::RenderGeometry::IntersectionNotificationBus::Bind(m_intersectionNotificationBus, entityContextId); - //Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange + // Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange RegisterModel(); } @@ -291,6 +299,11 @@ namespace AZ { m_meshFeatureProcessor->SetTransform(m_meshHandle, world, m_cachedNonUniformScale); } + + // ensure the render geometry is kept in sync with any changes to the entity the mesh is on + AzFramework::RenderGeometry::IntersectionNotificationBus::Event( + m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged, + m_entityComponentIdPair.GetEntityId()); } void MeshComponentController::HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale) @@ -301,7 +314,7 @@ namespace AZ m_meshFeatureProcessor->SetTransform(m_meshHandle, m_transformInterface->GetWorldTM(), m_cachedNonUniformScale); } } - + RPI::ModelMaterialSlotMap MeshComponentController::GetModelMaterialSlots() const { Data::Asset modelAsset = GetModelAsset(); @@ -369,6 +382,9 @@ namespace AZ MeshComponentNotificationBus::Event(entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model); MaterialReceiverNotificationBus::Event(entityId, &MaterialReceiverNotificationBus::Events::OnMaterialAssignmentsChanged); AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(entityId); + AzFramework::RenderGeometry::IntersectionNotificationBus::Event( + m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged, + m_entityComponentIdPair.GetEntityId()); } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h index 76cf6a1b39..cecd744b80 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h @@ -162,6 +162,8 @@ namespace AZ bool m_isVisible = true; MeshComponentConfig m_configuration; AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne(); + //! Cached bus to use to notify RenderGeometry::Intersector the entity/component has changed. + AzFramework::RenderGeometry::IntersectionNotificationBus::BusPtr m_intersectionNotificationBus; MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_changeEventHandler { @@ -173,6 +175,5 @@ namespace AZ [&](const AZ::Vector3& nonUniformScale) { HandleNonUniformScaleChange(nonUniformScale); } }; }; - } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/Main.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/Main.cpp new file mode 100644 index 0000000000..2d75cf96f5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/Main.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include + +class AtomLyIntegrationHook : public AZ::Test::ITestEnvironment +{ +public: + void SetupEnvironment() override + { + AZ::AllocatorInstance::Create(); + } + + void TeardownEnvironment() override + { + AZ::AllocatorInstance::Destroy(); + } +}; + +// required to support running integration tests with Qt +AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv) +{ + ::testing::InitGoogleMock(&argc, argv); + QApplication app(argc, argv); + AZ::Test::printUnusedParametersWarning(argc, argv); + AZ::Test::addTestEnvironments({ DEFAULT_UNIT_TEST_ENV, new AtomLyIntegrationHook }); + int result = RUN_ALL_TESTS(); + return result; +} + +IMPLEMENT_TEST_EXECUTABLE_MAIN(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/MeshComponentControllerTests.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/MeshComponentControllerTests.cpp new file mode 100644 index 0000000000..bc1bf3a247 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/MeshComponentControllerTests.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include +#include + +namespace UnitTest +{ + static AzFramework::EntityContextId FindOwningContextId(const AZ::EntityId entityId) + { + AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult(contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId); + return contextId; + } + + class IntersectionNotificationDetector : public AzFramework::RenderGeometry::IntersectionNotificationBus::Handler + { + public: + void Connect(const AzFramework::EntityContextId& entityContextId); + void Disconnect(); + + // IntersectionNotificationBus overrides ... + void OnEntityConnected(AZ::EntityId entityId) override; + void OnEntityDisconnected(AZ::EntityId entityId) override; + void OnGeometryChanged(AZ::EntityId entityId) override; + + AZ::EntityId m_lastEntityIdChanged; + }; + + void IntersectionNotificationDetector::Connect(const AzFramework::EntityContextId& entityContextId) + { + AzFramework::RenderGeometry::IntersectionNotificationBus::Handler::BusConnect(entityContextId); + } + + void IntersectionNotificationDetector::Disconnect() + { + AzFramework::RenderGeometry::IntersectionNotificationBus::Handler::BusDisconnect(); + } + + void IntersectionNotificationDetector::OnEntityConnected([[maybe_unused]] AZ::EntityId entityId) + { + } + + void IntersectionNotificationDetector::OnEntityDisconnected([[maybe_unused]] AZ::EntityId entityId) + { + } + + void IntersectionNotificationDetector::OnGeometryChanged(AZ::EntityId entityId) + { + m_lastEntityIdChanged = entityId; + } + + class MeshComponentControllerFixture : public ToolsApplicationFixture + { + public: + void SetUpEditorFixtureImpl() override + { + m_meshComponentDescriptor = AZStd::unique_ptr(AZ::Render::MeshComponent::CreateDescriptor()); + m_meshComponentDescriptor->Reflect(GetApplication()->GetSerializeContext()); + + m_editorMeshComponentDescriptor = + AZStd::unique_ptr(AZ::Render::EditorMeshComponent::CreateDescriptor()); + m_editorMeshComponentDescriptor->Reflect(GetApplication()->GetSerializeContext()); + + m_entityId1 = CreateDefaultEditorEntity("Entity1"); + m_entityIds.push_back(m_entityId1); + + m_intersectionNotificationDetector.Connect(FindOwningContextId(m_entityId1)); + } + + void TearDownEditorFixtureImpl() override + { + bool entityDestroyed = false; + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + entityDestroyed, &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, m_entityId1); + + m_intersectionNotificationDetector.Disconnect(); + + m_meshComponentDescriptor.reset(); + m_editorMeshComponentDescriptor.reset(); + } + + AZ::EntityId m_entityId1; + AzToolsFramework::EntityIdList m_entityIds; + AZStd::unique_ptr m_meshComponentDescriptor; + AZStd::unique_ptr m_editorMeshComponentDescriptor; + IntersectionNotificationDetector m_intersectionNotificationDetector; + }; + + TEST_F(MeshComponentControllerFixture, IntersectionNotificationBusIsNotifiedWhenMeshComponentControllerTransformIsModified) + { + auto* entity1 = AzToolsFramework::GetEntityById(m_entityId1); + entity1->Deactivate(); + entity1->CreateComponent(); + // note: RPI::Scene::GetFeatureProcessorForEntity(...) returns nullptr + // and so m_meshFeatureProcessor is null + AZ_TEST_START_TRACE_SUPPRESSION; + entity1->Activate(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + AZ::TransformBus::Event( + m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f))); + + using ::testing::Eq; + EXPECT_THAT(m_entityId1, Eq(m_intersectionNotificationDetector.m_lastEntityIdChanged)); + } +} // namespace UnitTest diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index 0dea725d70..018795dcf2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -10,7 +10,6 @@ set(FILES Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h - Source/Module.cpp Source/Animation/EditorAttachmentComponent.h Source/Animation/EditorAttachmentComponent.cpp Source/EditorCommonFeaturesSystemComponent.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_test_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_test_files.cmake new file mode 100644 index 0000000000..5a58124e0a --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_test_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + Tests/Main.cpp + Tests/MeshComponentControllerTests.cpp) From d412f3cc502d598464422444ea991cd0cfb0ab84 Mon Sep 17 00:00:00 2001 From: greerdv Date: Mon, 24 Jan 2022 12:38:15 +0000 Subject: [PATCH 56/61] update with feedback from PR Signed-off-by: greerdv --- .../Source/EditorShapeColliderComponent.cpp | 17 ++++++++--------- .../Code/Source/EditorShapeColliderComponent.h | 4 ++-- .../Code/Tests/ShapeColliderComponentTests.cpp | 16 +++++++++------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index bdf70fc227..d99a76f306 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -55,7 +55,7 @@ namespace PhysX m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::Offset, false); } - AZ::Crc32 EditorShapeColliderComponent::SubdivisionCountVisibility() + AZ::Crc32 EditorShapeColliderComponent::SubdivisionCountVisibility() const { if (m_shapeType == ShapeType::Cylinder) { @@ -65,7 +65,7 @@ namespace PhysX return AZ::Edit::PropertyVisibility::Hide; } - AZ::Crc32 EditorShapeColliderComponent::SingleSidedVisibility() + AZ::Crc32 EditorShapeColliderComponent::SingleSidedVisibility() const { if ((m_shapeType == ShapeType::QuadSingleSided || m_shapeType == ShapeType::QuadDoubleSided) && GetEntity()->FindComponent() == nullptr) @@ -124,16 +124,16 @@ namespace PhysX void EditorShapeColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { - provided.push_back(AZ_CRC("PhysicsWorldBodyService", 0x944da0cc)); - provided.push_back(AZ_CRC("PhysXColliderService", 0x4ff43f7c)); - provided.push_back(AZ_CRC("PhysXTriggerService", 0x3a117d7b)); - provided.push_back(AZ_CRC("PhysXShapeColliderService", 0x98a7e779)); + provided.push_back(AZ_CRC_CE("PhysicsWorldBodyService")); + provided.push_back(AZ_CRC_CE("PhysXColliderService")); + provided.push_back(AZ_CRC_CE("PhysXTriggerService")); + provided.push_back(AZ_CRC_CE("PhysXShapeColliderService")); } void EditorShapeColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { - required.push_back(AZ_CRC("TransformService", 0x8ee22c50)); - required.push_back(AZ_CRC("ShapeService", 0xe86aa5fe)); + required.push_back(AZ_CRC_CE("TransformService")); + required.push_back(AZ_CRC_CE("ShapeService")); } void EditorShapeColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) @@ -422,7 +422,6 @@ namespace PhysX SetShapeConfig(ShapeType::QuadSingleSided, shapeConfig); } - else { // it's not possible to create a perfectly 2d convex in PhysX, so the best we can do is a very thin box diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 0f5350b781..09d5f803fc 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -105,9 +105,9 @@ namespace PhysX void RefreshUiProperties(); AZ::u32 OnSubdivisionCountChange(); - AZ::Crc32 SubdivisionCountVisibility(); + AZ::Crc32 SubdivisionCountVisibility() const; void OnSingleSidedChange(); - AZ::Crc32 SingleSidedVisibility(); + AZ::Crc32 SingleSidedVisibility() const; // AZ::Component void Activate() override; diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index c164e5de63..c9dcbe64d1 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -470,7 +470,7 @@ namespace PhysXEditorTests void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger) { - SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC("Trigger", 0x1a6b0f5d), isTrigger); + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC_CE("Trigger"), isTrigger); } bool GetBoolValueFromComponent(AZ::Component* component, AZ::Crc32 name) @@ -489,17 +489,17 @@ namespace PhysXEditorTests bool IsTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) { - return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC("Trigger")); + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC_CE("Trigger")); } void SetSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool singleSided) { - SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC("SingleSided"), singleSided); + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC_CE("SingleSided"), singleSided); } bool IsSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) { - return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC("SingleSided")); + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC_CE("SingleSided")); } EntityPtr CreateRigidBox(const AZ::Vector3& boxDimensions, const AZ::Vector3& position) @@ -636,7 +636,6 @@ namespace PhysXEditorTests EXPECT_NEAR(aabb.GetMin().GetY(), -0.6f, 1e-3f); EXPECT_NEAR(aabb.GetMax().GetX(), 2.7f, 1e-3f); EXPECT_NEAR(aabb.GetMax().GetY(), 0.6f, 1e-3f); - EXPECT_TRUE(true); } TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_TriggerSettingIsRememberedWhenSwitchingToQuadAndBack) @@ -731,9 +730,12 @@ namespace PhysXEditorTests EntityPtr gameQuadEntity = CreateActiveGameEntityFromEditorEntity(editorQuadEntity.get()); EntityPtr gameBoxEntity = CreateActiveGameEntityFromEditorEntity(editorBoxEntity.get()); - // give the box enough upward velocity to rise above the level of the quad and simulate + // give the box enough upward velocity to rise above the level of the quad + // simulate for enough time that the box would have reached the top of its trajectory and fallen back past the starting point if + // it hadn't collided with the top of the quad + const int numTimesteps = 100; Physics::RigidBodyRequestBus::Event(gameBoxEntity->GetId(), &Physics::RigidBodyRequests::SetLinearVelocity, AZ::Vector3::CreateAxisZ(6.0f)); - PhysX::TestUtils::UpdateScene(m_defaultScene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 200); + PhysX::TestUtils::UpdateScene(m_defaultScene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, numTimesteps); // the box should travel through the base of the quad because it has no collision from that direction // and land on the top surface of the quad, which does have collision From 7b649656bc870de433e0d6a8db6eb63b230a6cc5 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Mon, 24 Jan 2022 07:30:39 -0800 Subject: [PATCH 57/61] chore: use numeric cast for QtEventTOAzInputMapperTest Signed-off-by: Michael Pollind --- .../Tests/Input/QtEventToAzInputMapperTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index 78a08af6cb..8222a115f6 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -562,7 +562,7 @@ namespace UnitTest AZ::Vector2 accumulatedPosition(0.0f,0.0f); for(const auto& pos: m_azCursorPositions) { - accumulatedPosition += (pos.m_normalizedPositionDelta * AZ::Vector2((float)WidgetSize.width(), (float)WidgetSize.height())); + accumulatedPosition += (pos.m_normalizedPositionDelta * AZ::Vector2(aznumeric_cast(WidgetSize.width()), aznumeric_cast(WidgetSize.height()))); } // validate From 37c0b661f94ffbde91364ff8536dc916dcc48c6c Mon Sep 17 00:00:00 2001 From: moraaar Date: Mon, 24 Jan 2022 15:49:33 +0000 Subject: [PATCH 58/61] Added blast asset, whose fbx works with Atom, to help testing Blast Gem (#7110) Signed-off-by: moraaar --- .../Destruction/cinder_wall_complex.blast | Bin 0 -> 330848 bytes .../Assets/Destruction/cinder_wall_complex.fbx | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 AutomatedTesting/Assets/Destruction/cinder_wall_complex.blast create mode 100644 AutomatedTesting/Assets/Destruction/cinder_wall_complex.fbx diff --git a/AutomatedTesting/Assets/Destruction/cinder_wall_complex.blast b/AutomatedTesting/Assets/Destruction/cinder_wall_complex.blast new file mode 100644 index 0000000000000000000000000000000000000000..fcf67594c77c4fb51cc722d3d982a5b806a55fb4 GIT binary patch literal 330848 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⁡oo-r}+m3wDXFz&_Uv{b#sOK&*O#B9dC$e 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 literal 0 HcmV?d00001 diff --git a/AutomatedTesting/Assets/Destruction/cinder_wall_complex.fbx b/AutomatedTesting/Assets/Destruction/cinder_wall_complex.fbx new file mode 100644 index 0000000000..62c305c35e --- /dev/null +++ b/AutomatedTesting/Assets/Destruction/cinder_wall_complex.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f94f2634eacb4d7bee20dacc45edef96e4d268f1adb7960b8aa8f3b6e2906ed +size 6867609 From c7d1c4603f80174b57ae217dbb801a3875d77499 Mon Sep 17 00:00:00 2001 From: mrieggeramzn <61609885+mrieggeramzn@users.noreply.github.com> Date: Mon, 24 Jan 2022 09:24:20 -0800 Subject: [PATCH 59/61] Removing lux core (whats left of it) (#7089) * Removing lux core (whats left of it) Signed-off-by: mrieggeramzn * Removing files recommended by NLawson and EPapp Signed-off-by: mrieggeramzn --- Gems/Atom/Feature/Common/Code/CMakeLists.txt | 1 - .../Common/Clang/atom_feature_common_clang.cmake | 7 ------- .../Common/GCC/atom_feature_common_gcc.cmake | 13 ------------- .../Common/MSVC/atom_feature_common_msvc.cmake | 7 ------- 4 files changed, 28 deletions(-) delete mode 100644 Gems/Atom/Feature/Common/Code/Source/Platform/Common/Clang/atom_feature_common_clang.cmake delete mode 100644 Gems/Atom/Feature/Common/Code/Source/Platform/Common/GCC/atom_feature_common_gcc.cmake delete mode 100644 Gems/Atom/Feature/Common/Code/Source/Platform/Common/MSVC/atom_feature_common_msvc.cmake diff --git a/Gems/Atom/Feature/Common/Code/CMakeLists.txt b/Gems/Atom/Feature/Common/Code/CMakeLists.txt index a832c46539..0d779cc3ea 100644 --- a/Gems/Atom/Feature/Common/Code/CMakeLists.txt +++ b/Gems/Atom/Feature/Common/Code/CMakeLists.txt @@ -18,7 +18,6 @@ ly_add_target( ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake PLATFORM_INCLUDE_FILES ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake - ${common_source_dir}/${PAL_TRAIT_COMPILER_ID}/atom_feature_common_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/Clang/atom_feature_common_clang.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Common/Clang/atom_feature_common_clang.cmake deleted file mode 100644 index 7a325ca97e..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/Clang/atom_feature_common_clang.cmake +++ /dev/null @@ -1,7 +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 -# -# diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/GCC/atom_feature_common_gcc.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Common/GCC/atom_feature_common_gcc.cmake deleted file mode 100644 index b3f7867308..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/GCC/atom_feature_common_gcc.cmake +++ /dev/null @@ -1,13 +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 -# -# - -set_source_files_properties( - Source/LuxCore/LuxCoreRenderer.cpp - PROPERTIES - COMPILE_OPTIONS -fexceptions -) diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/MSVC/atom_feature_common_msvc.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Common/MSVC/atom_feature_common_msvc.cmake deleted file mode 100644 index 7a325ca97e..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/MSVC/atom_feature_common_msvc.cmake +++ /dev/null @@ -1,7 +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 -# -# From 0d2204917e1d6ac6a3e598df041890666bec5312 Mon Sep 17 00:00:00 2001 From: Scott Romero <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 24 Jan 2022 09:49:13 -0800 Subject: [PATCH 60/61] [development] enabled mouse wheel scrolling in the Profiler gem visualizer (#6930) Resolves #6698 Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com --- Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp index 27397f05f7..3e071570bd 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp @@ -536,8 +536,7 @@ namespace Profiler ImGui::Columns(1, "TimelineColumn", true); // Timeline - if (ImGui::BeginChild( - "Timeline", { 0, 0 }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) + if (ImGui::BeginChild("Timeline", { 0, 0 }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { // Find the next frame boundary after the viewport's right bound and draw until that tick auto nextFrameBoundaryItr = AZStd::lower_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), m_viewportEndTick); From 7bf7b03dd290af04e169ab6ea037e1f8b24d7962 Mon Sep 17 00:00:00 2001 From: SWMasterson Date: Mon, 24 Jan 2022 09:50:37 -0800 Subject: [PATCH 61/61] Added base level for tests, updated automation to use prefabs, and restored sandboxed tests (#6993) Signed-off-by: Sean Masterson --- .../Gem/PythonTests/Atom/TestSuite_Main.py | 10 +++- .../Gem/PythonTests/Atom/TestSuite_Sandbox.py | 10 ---- ...ntsLevel_DiffuseGlobalIlluminationAdded.py | 6 +-- ...ditorComponentsLevel_DisplayMapperAdded.py | 4 +- .../hydra_AtomEditorComponents_BloomAdded.py | 4 +- .../hydra_AtomEditorComponents_DecalAdded.py | 3 +- ...a_AtomEditorComponents_DeferredFogAdded.py | 4 +- ..._AtomEditorComponents_DepthOfFieldAdded.py | 4 +- ...mEditorComponents_DiffuseProbeGridAdded.py | 4 +- ...mEditorComponents_DirectionalLightAdded.py | 4 +- ...AtomEditorComponents_DisplayMapperAdded.py | 4 +- ...omEditorComponents_EntityReferenceAdded.py | 4 +- ...omEditorComponents_ExposureControlAdded.py | 4 +- ...EditorComponents_GlobalSkylightIBLAdded.py | 4 +- .../hydra_AtomEditorComponents_GridAdded.py | 4 +- ...omEditorComponents_HDRColorGradingAdded.py | 4 +- ...ra_AtomEditorComponents_HDRiSkyboxAdded.py | 4 +- .../hydra_AtomEditorComponents_LightAdded.py | 4 +- ...mEditorComponents_LookModificationAdded.py | 4 +- ...ydra_AtomEditorComponents_MaterialAdded.py | 4 +- .../hydra_AtomEditorComponents_MeshAdded.py | 4 +- ...orComponents_OcclusionCullingPlaneAdded.py | 4 +- ...a_AtomEditorComponents_PhysicalSkyAdded.py | 4 +- ...nents_PostFXGradientWeightModifierAdded.py | 4 +- ...a_AtomEditorComponents_PostFXLayerAdded.py | 4 +- ...ponents_PostFXRadiusWeightModifierAdded.py | 4 +- ...mponents_PostFxShapeWeightModifierAdded.py | 4 +- ...omEditorComponents_ReflectionProbeAdded.py | 4 +- .../hydra_AtomEditorComponents_SSAOAdded.py | 4 +- .../Graphics/base_empty/base_empty.prefab | 53 +++++++++++++++++++ .../Levels/Graphics/base_empty/tags.txt | 12 +++++ 31 files changed, 153 insertions(+), 41 deletions(-) create mode 100644 AutomatedTesting/Levels/Graphics/base_empty/base_empty.prefab create mode 100644 AutomatedTesting/Levels/Graphics/base_empty/tags.txt diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index f1e66ac492..cca20bbf8c 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -21,7 +21,7 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") @pytest.mark.parametrize("launcher_platform", ['windows_editor']) class TestAutomation(EditorTestSuite): - enable_prefab_system = False + enable_prefab_system = True @pytest.mark.test_case_id("C36525657") class AtomEditorComponents_BloomAdded(EditorSharedTest): @@ -120,6 +120,14 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_SSAOAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_SSAOAdded as test_module + @pytest.mark.test_case_id("C36529666") + class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module + + @pytest.mark.test_case_id("C36525660") + class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module + class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py index bd0477a1db..5299e55a2b 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py @@ -26,13 +26,3 @@ class TestAutomation(EditorTestSuite): @pytest.mark.test_case_id("C36525660") class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module - - # this test causes editor to crash when using slices. once automation transitions to prefabs it should pass - @pytest.mark.test_case_id("C36529666") - class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest): - from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module - - # this test causes editor to crash when using slices. once automation transitions to prefabs it should pass - @pytest.mark.test_case_id("C36525660") - class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest): - from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py index ddcd5c3c46..0c9b39e354 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py @@ -60,7 +60,7 @@ def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Add Diffuse Global Illumination level component to the level entity. @@ -86,10 +86,10 @@ def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity(): # 4. Set Quality Level property to Low diffuse_global_illumination_component.set_component_property_value( - AtomComponentProperties.diffuse_global_illumination('Quality Level', GLOBAL_ILLUMINATION_QUALITY['Low'])) + AtomComponentProperties.diffuse_global_illumination('Quality Level'), GLOBAL_ILLUMINATION_QUALITY['Low']) quality = diffuse_global_illumination_component.get_component_property_value( AtomComponentProperties.diffuse_global_illumination('Quality Level')) - Report.result(diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low']) + Report.result(Tests.diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low']) # 5. Enter/Exit game mode. TestHelper.enter_game_mode(Tests.enter_game_mode) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py index c050dd76d4..f9660957bb 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py @@ -67,7 +67,7 @@ def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Add Display Mapper level component to the level entity. @@ -102,7 +102,7 @@ def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity(): display_mapper_component.set_component_property_value( AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True) Report.result( - Test.enable_ldr_color_grading_lut, + Tests.enable_ldr_color_grading_lut, display_mapper_component.get_component_property_value( AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py index 56b22eb20d..ce29a1de88 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py @@ -97,7 +97,7 @@ def AtomEditorComponents_Bloom_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an Bloom entity with no components. @@ -170,10 +170,12 @@ def AtomEditorComponents_Bloom_AddedToEntity(): # 13. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, bloom_entity.exists()) # 14. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not bloom_entity.exists()) # 15. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py index e840cf3cf1..eb5e24e3a6 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py @@ -95,7 +95,7 @@ def AtomEditorComponents_Decal_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Decal entity with no components. @@ -162,6 +162,7 @@ def AtomEditorComponents_Decal_AddedToEntity(): # 11. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not decal_entity.exists()) # 12. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py index 71163ece94..f1578cbf8d 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py @@ -97,7 +97,7 @@ def AtomEditorComponents_DeferredFog_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an Deferred Fog entity with no components. @@ -174,10 +174,12 @@ def AtomEditorComponents_DeferredFog_AddedToEntity(): # 13. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, deferred_fog_entity.exists()) # 14. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not deferred_fog_entity.exists()) # 15. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py index e2c5f9c77d..3913420981 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py @@ -107,7 +107,7 @@ def AtomEditorComponents_DepthOfField_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a DepthOfField entity with no components. @@ -189,10 +189,12 @@ def AtomEditorComponents_DepthOfField_AddedToEntity(): # 15. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, depth_of_field_entity.exists()) # 16. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not depth_of_field_entity.exists()) # 17. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py index f42c091057..04b4f7876b 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py @@ -90,7 +90,7 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Diffuse Probe Grid entity with no components. @@ -168,10 +168,12 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, diffuse_probe_grid_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not diffuse_probe_grid_entity.exists()) # 14. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py index f3ffc0f366..19cfbedcd5 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py @@ -95,7 +95,7 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Directional Light entity with no components. @@ -168,10 +168,12 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, directional_light_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not directional_light_entity.exists()) # 14. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py index 558d69046e..d3ea669eab 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py @@ -91,7 +91,7 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Display Mapper entity with no components. @@ -166,10 +166,12 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity(): # 11. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, display_mapper_entity.exists()) # 12. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not display_mapper_entity.exists()) # 13. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_EntityReferenceAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_EntityReferenceAdded.py index dddcca64fa..ec402f0f5f 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_EntityReferenceAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_EntityReferenceAdded.py @@ -81,7 +81,7 @@ def AtomEditorComponents_EntityReference_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an Entity Reference entity with no components. @@ -139,10 +139,12 @@ def AtomEditorComponents_EntityReference_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, entity_reference_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not entity_reference_entity.exists()) # 11. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py index 6fa9660539..f60f0e18be 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py @@ -101,7 +101,7 @@ def AtomEditorComponents_ExposureControl_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Creation of Exposure Control entity with no components. @@ -169,10 +169,12 @@ def AtomEditorComponents_ExposureControl_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, exposure_control_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not exposure_control_entity.exists()) # 14. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py index 7f0c38e289..0e8e6fa43e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py @@ -99,7 +99,7 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Global Skylight (IBL) entity with no components. @@ -176,10 +176,12 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity(): # 11. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, global_skylight_entity.exists()) # 12. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not global_skylight_entity.exists()) # 13. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py index a77a1f50a4..3c6d261f22 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py @@ -82,7 +82,7 @@ def AtomEditorComponents_Grid_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Grid entity with no components. @@ -139,10 +139,12 @@ def AtomEditorComponents_Grid_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, grid_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not grid_entity.exists()) # 11. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py index 4972079fcd..5846206e50 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py @@ -96,7 +96,7 @@ def AtomEditorComponents_HDRColorGrading_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an HDR Color Grading entity with no components. @@ -173,10 +173,12 @@ def AtomEditorComponents_HDRColorGrading_AddedToEntity(): # 13. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, hdr_color_grading_entity.exists()) # 14. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not hdr_color_grading_entity.exists()) # 15. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py index 0f96bc5424..fc093b60db 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py @@ -87,7 +87,7 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an HDRi Skybox with no components. @@ -158,10 +158,12 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): # 10. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, hdri_skybox_entity.exists()) # 11. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not hdri_skybox_entity.exists()) # 12. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py index 249671556d..75a04612a3 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py @@ -89,7 +89,7 @@ def AtomEditorComponents_Light_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Light entity with no components. @@ -144,10 +144,12 @@ def AtomEditorComponents_Light_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, light_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not light_entity.exists()) # 11. Look for errors asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LookModificationAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LookModificationAdded.py index afb8033426..149af73151 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LookModificationAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LookModificationAdded.py @@ -104,7 +104,7 @@ def AtomEditorComponents_LookModification_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an Look Modification entity with no components. @@ -192,10 +192,12 @@ def AtomEditorComponents_LookModification_AddedToEntity(): # 14. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, look_modification_entity.exists()) # 15. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not look_modification_entity.exists()) # 16. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py index c613bb23e7..919a6753f4 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py @@ -102,7 +102,7 @@ def AtomEditorComponents_Material_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Material entity with no components. @@ -184,10 +184,12 @@ def AtomEditorComponents_Material_AddedToEntity(): # 16. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, material_entity.exists()) # 17. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not material_entity.exists()) # 18. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py index 9d56753961..a87ee75b53 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py @@ -87,7 +87,7 @@ def AtomEditorComponents_Mesh_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Mesh entity with no components. @@ -151,10 +151,12 @@ def AtomEditorComponents_Mesh_AddedToEntity(): # 10. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, mesh_entity.exists()) # 11. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not mesh_entity.exists()) # 12. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py index 4226ae3dfe..56bb5eb68c 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py @@ -80,7 +80,7 @@ def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a occlusion culling plane entity with no components. @@ -140,10 +140,12 @@ def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, occlusion_culling_plane_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not occlusion_culling_plane_entity.exists()) # 11. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py index fa8c626016..e0c558811e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py @@ -89,7 +89,7 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Physical Sky entity with no components. @@ -146,10 +146,12 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, physical_sky_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not physical_sky_entity.exists()) # 11. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py index 6e4ae2d9ac..b36267433f 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py @@ -92,7 +92,7 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a PostFX Gradient Weight Modifier entity with no components. @@ -162,10 +162,12 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, postfx_gradient_weight_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not postfx_gradient_weight_entity.exists()) # 14. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py index efef4c0a43..07fffb9bde 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py @@ -80,7 +80,7 @@ def AtomEditorComponents_postfx_layer_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a PostFX Layer entity with no components. @@ -137,10 +137,12 @@ def AtomEditorComponents_postfx_layer_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, postfx_layer_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not postfx_layer_entity.exists()) # 11. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py index 228ddfcdc5..616233d2d2 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py @@ -92,7 +92,7 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Post FX Radius Weight Modifier entity with no components. @@ -161,10 +161,12 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, postfx_radius_weight_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not postfx_radius_weight_entity.exists()) # 14. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py index 0a37ac6bf7..5f7d571a18 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py @@ -98,7 +98,7 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a PostFx Shape Weight Modifier entity with no components. @@ -188,10 +188,12 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): # 15. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, postfx_shape_weight_entity.exists()) # 16. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not postfx_shape_weight_entity.exists()) # 17. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py index 70adf9143a..85156b4db6 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py @@ -97,7 +97,7 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Reflection Probe entity with no components. @@ -183,10 +183,12 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity(): # 13. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, reflection_probe_entity.exists()) # 14. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not reflection_probe_entity.exists()) # 15. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_SSAOAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_SSAOAdded.py index 15f40f8b70..09f77c4a9f 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_SSAOAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_SSAOAdded.py @@ -94,7 +94,7 @@ def AtomEditorComponents_SSAO_AddedToEntity(): # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. TestHelper.init_idle() - TestHelper.open_level("", "Base") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a SSAO entity with no components. @@ -163,10 +163,12 @@ def AtomEditorComponents_SSAO_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, ssao_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not ssao_entity.exists()) # 14. Look for errors and asserts. diff --git a/AutomatedTesting/Levels/Graphics/base_empty/base_empty.prefab b/AutomatedTesting/Levels/Graphics/base_empty/base_empty.prefab new file mode 100644 index 0000000000..f7e42e7731 --- /dev/null +++ b/AutomatedTesting/Levels/Graphics/base_empty/base_empty.prefab @@ -0,0 +1,53 @@ +{ + "ContainerEntity": { + "Id": "Entity_[1146574390643]", + "Name": "Level", + "Components": { + "Component_[10641544592923449938]": { + "$type": "EditorInspectorComponent", + "Id": 10641544592923449938 + }, + "Component_[12039882709170782873]": { + "$type": "EditorOnlyEntityComponent", + "Id": 12039882709170782873 + }, + "Component_[12265484671603697631]": { + "$type": "EditorPendingCompositionComponent", + "Id": 12265484671603697631 + }, + "Component_[14126657869720434043]": { + "$type": "EditorEntitySortComponent", + "Id": 14126657869720434043 + }, + "Component_[15230859088967841193]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 15230859088967841193, + "Parent Entity": "" + }, + "Component_[16239496886950819870]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 16239496886950819870 + }, + "Component_[5688118765544765547]": { + "$type": "EditorEntityIconComponent", + "Id": 5688118765544765547 + }, + "Component_[6545738857812235305]": { + "$type": "SelectionComponent", + "Id": 6545738857812235305 + }, + "Component_[7247035804068349658]": { + "$type": "EditorPrefabComponent", + "Id": 7247035804068349658 + }, + "Component_[9307224322037797205]": { + "$type": "EditorLockComponent", + "Id": 9307224322037797205 + }, + "Component_[9562516168917670048]": { + "$type": "EditorVisibilityComponent", + "Id": 9562516168917670048 + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Levels/Graphics/base_empty/tags.txt b/AutomatedTesting/Levels/Graphics/base_empty/tags.txt new file mode 100644 index 0000000000..0d6c1880e7 --- /dev/null +++ b/AutomatedTesting/Levels/Graphics/base_empty/tags.txt @@ -0,0 +1,12 @@ +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0