From a1d836e36b2a0dd1be9bb82ca1f913b89ec827a4 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 2 Sep 2021 13:29:49 -0500 Subject: [PATCH 01/63] AtomTools: Removing unnecessary cmake files and setting up tool variant Signed-off-by: Guthrie Adams --- .../Application/AtomToolsApplication.cpp | 2 +- .../Tools/MaterialEditor/Code/CMakeLists.txt | 19 ++--- .../Code/materialeditor_files.cmake | 1 - .../Code/tool_dependencies.cmake | 22 ------ .../Tools/MaterialEditor/MaterialEditor.xml | 69 ------------------- .../Code/CMakeLists.txt | 10 +-- .../Code/tool_dependencies.cmake | 22 ------ 7 files changed, 10 insertions(+), 135 deletions(-) delete mode 100644 Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake delete mode 100644 Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 36e45bb7ca..bd04ba9395 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -468,7 +468,7 @@ namespace AtomToolsFramework void AtomToolsApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const { - appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game; + appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Tool; } void AtomToolsApplication::OnTraceMessage([[maybe_unused]] AZStd::string_view message) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt index 6230217ac2..0206cebf8b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt @@ -108,8 +108,14 @@ ly_add_target( Gem::MaterialEditor.Window Gem::MaterialEditor.Viewport Gem::MaterialEditor.Document + RUNTIME_DEPENDENCIES + Gem::AtomToolsFramework.Editor + Gem::EditorPythonBindings.Editor + Gem::ImageProcessingAtom.Editor ) +ly_set_gem_variant_to_load(TARGETS MaterialEditor VARIANTS Tools) + # Add a 'builders' alias to allow the MaterialEditor root gem path to be added to the generated # cmake_dependencies..assetprocessor.setreg to allow the asset scan folder for it to be added ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem) @@ -118,24 +124,11 @@ ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem) # Editor opens up the MaterialEditor ly_add_dependencies(Editor Gem::MaterialEditor) -ly_add_target_files( - TARGETS - MaterialEditor - FILES - ${CMAKE_CURRENT_LIST_DIR}/../MaterialEditor.xml - OUTPUT_SUBDIRECTORY - Gems/Atom/Tools/MaterialEditor -) - ly_add_target_dependencies( TARGETS MaterialEditor DEPENDENCIES_FILES - tool_dependencies.cmake Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake - # The Material Editor needs the LyShine "Tools" gem variant for the custom LyShine pass - DEPENDENT_TARGETS - Gem::LyShine.Tools ) # Inject the project path into the MaterialEditor VS debugger command arguments if the build system being invoked diff --git a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake index 2b554a852e..d4c4364ba7 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake @@ -10,5 +10,4 @@ set(FILES Source/main.cpp Source/MaterialEditorApplication.cpp Source/MaterialEditorApplication.h - tool_dependencies.cmake ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake b/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake deleted file mode 100644 index 8803be3852..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.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(GEM_DEPENDENCIES - Gem::Atom_RHI_Null.Private - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_Component_DebugCamera - Gem::Atom_RPI.Editor - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor - Gem::AtomToolsFramework.Editor - Gem::AtomLyIntegration_CommonFeatures.Editor - Gem::EditorPythonBindings.Editor - Gem::ImageProcessingAtom.Editor -) diff --git a/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml b/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml deleted file mode 100644 index 9f91001f66..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt index d7e415a279..5710a186d3 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt @@ -78,15 +78,12 @@ ly_add_target( Gem::ShaderManagementConsole.Window Gem::ShaderManagementConsole.Document RUNTIME_DEPENDENCIES - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_RPI.Private - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor + Gem::AtomToolsFramework.Editor Gem::EditorPythonBindings.Editor ) +ly_set_gem_variant_to_load(TARGETS ShaderManagementConsole VARIANTS Tools) + # Add build dependency to Editor for the ShaderManagementConsole application since # Editor opens up the ShaderManagementConsole ly_add_dependencies(Editor Gem::ShaderManagementConsole) @@ -95,7 +92,6 @@ ly_add_target_dependencies( TARGETS ShaderManagementConsole DEPENDENCIES_FILES - tool_dependencies.cmake Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake deleted file mode 100644 index 8803be3852..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.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(GEM_DEPENDENCIES - Gem::Atom_RHI_Null.Private - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_Component_DebugCamera - Gem::Atom_RPI.Editor - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor - Gem::AtomToolsFramework.Editor - Gem::AtomLyIntegration_CommonFeatures.Editor - Gem::EditorPythonBindings.Editor - Gem::ImageProcessingAtom.Editor -) From 76219f6c5f150f56ed38da85424f00189792d6a6 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 8 Sep 2021 17:25:02 -0500 Subject: [PATCH 02/63] Releasing style manager with window Signed-off-by: Guthrie Adams --- .../Code/Source/Application/AtomToolsApplication.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index bd04ba9395..bad4ad97de 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -87,6 +87,7 @@ namespace AtomToolsFramework AtomToolsApplication ::~AtomToolsApplication() { + m_styleManager.reset(); AtomToolsMainWindowNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); @@ -206,6 +207,7 @@ namespace AtomToolsFramework { // before modules are unloaded, destroy UI to free up any assets it cached AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); + m_styleManager.reset(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); @@ -461,6 +463,7 @@ namespace AtomToolsFramework void AtomToolsApplication::Stop() { AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); + m_styleManager.reset(); UnloadSettings(); Base::Stop(); From a0ada8fcd388d441f018fc1619845682a09f9c27 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 9 Sep 2021 16:44:35 +0100 Subject: [PATCH 03/63] Reworked net entities instantiation in order to fix entity references e.g. parent-child relationship. Note: Only entities within network spawnable keep the references until the ticket system refactoring is done Signed-off-by: pereslav --- .../Code/Source/MultiplayerGem.cpp | 2 - .../NetworkEntity/NetworkEntityManager.cpp | 68 ++++++++++- .../NetworkEntity/NetworkEntityManager.h | 7 ++ .../NetworkEntity/NetworkEntityTracker.h | 1 + .../Pipeline/NetBindMarkerComponent.cpp | 115 ------------------ .../Source/Pipeline/NetBindMarkerComponent.h | 47 ------- .../Pipeline/NetworkPrefabProcessor.cpp | 24 +--- .../NetworkSpawnableHolderComponent.cpp | 2 +- .../NetworkSpawnableHolderComponent.h | 2 +- Gems/Multiplayer/Code/Tests/MainTools.cpp | 2 - Gems/Multiplayer/Code/multiplayer_files.cmake | 2 - 11 files changed, 78 insertions(+), 194 deletions(-) delete mode 100644 Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp delete mode 100644 Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h diff --git a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp index 1ad7be1a0a..8028e0c5d1 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -23,7 +22,6 @@ namespace Multiplayer AzNetworking::NetworkingSystemComponent::CreateDescriptor(), MultiplayerSystemComponent::CreateDescriptor(), NetBindComponent::CreateDescriptor(), - NetBindMarkerComponent::CreateDescriptor(), NetworkSpawnableHolderComponent::CreateDescriptor(), }); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 69d2726c65..88f0b221ec 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -467,6 +467,54 @@ namespace Multiplayer return netEntityId; } + AzFramework::EntitySpawnTicket NetworkEntityManager::RequestNetSpawnableInstantiation(const AZ::Data::Asset& rootSpawnable) + { + const AzFramework::Spawnable::EntityList& entityList = rootSpawnable->GetEntities(); + if (entityList.size() == 0) + { + AZ_Assert(false, "RequestNetSpawnableInstantiation: No entities in the spawnable %s", rootSpawnable.GetHint().c_str()) + return {}; + } + + // The first entity in every spawnable is the root one + const AZ::Entity* rootEntity = (entityList.begin())->get(); + + const auto* holderComponent = rootEntity->FindComponent(); + if(!holderComponent) + { + // This spawnable doesn't have a corresponding network spawnable. + return {}; + } + + // Retrieve the corresponding network spawnable asset + AZ::Data::Asset netSpawnableAsset = holderComponent->GetNetworkSpawnableAsset(); + + // Prepare the parameters for the spawning process + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_priority = AzFramework::SpawnablePriority_High; + + // Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene + optionalArgs.m_preInsertionCallback = [spawnableAssetId = netSpawnableAsset.GetId()](AzFramework::EntitySpawnTicket::Id, + AzFramework::SpawnableEntityContainerView entities) + { + for (uint32_t netEntityIndex = 0; netEntityIndex < entities.size(); netEntityIndex++) + { + AZ::Entity* netEntity = *(entities.begin() + netEntityIndex); + AZ::Name spawnableName = AZ::Interface::Get()->GetSpawnableNameFromAssetId(spawnableAssetId); + + PrefabEntityId prefabEntityId; + prefabEntityId.m_prefabName = spawnableName; + prefabEntityId.m_entityOffset = netEntityIndex; + AZ::Interface::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority); + } + }; + + // Spawn with the newly created ticket. This allows the calling code to manage the lifetime of the constructed entities + AzFramework::EntitySpawnTicket ticket(netSpawnableAsset); + AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(ticket, AZStd::move(optionalArgs)); + return ticket; + } + void NetworkEntityManager::OnRootSpawnableAssigned( [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) { @@ -477,11 +525,15 @@ namespace Multiplayer { multiplayer->SendReadyForEntityUpdates(true); } + + if(ShouldSpawnNetEntities()) + { + m_rootNetSpawnableTicket = RequestNetSpawnableInstantiation(rootSpawnable); + } } void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) { - // TODO: Do we need to clear all entities here? auto* multiplayer = GetMultiplayer(); const auto agentType = multiplayer->GetAgentType(); @@ -489,6 +541,12 @@ namespace Multiplayer { multiplayer->SendReadyForEntityUpdates(false); } + + // Despawn any remaining net entities created locally + if (m_rootNetSpawnableTicket.IsValid()) + { + AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_rootNetSpawnableTicket); + } } void NetworkEntityManager::SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) @@ -506,4 +564,12 @@ namespace Multiplayer netEntity->GetName().c_str()); } } + + bool NetworkEntityManager::ShouldSpawnNetEntities() const + { + const auto agentType = GetMultiplayer()->GetAgentType(); + const bool shouldSpawnNetEntities = + (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); + return shouldSpawnNetEntities; + } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index 9ccc576447..a5956b448f 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,11 @@ namespace Multiplayer private: void RemoveEntities(); NetEntityId NextId(); + bool ShouldSpawnNetEntities() const; + + // Note: This is an async function. + // The instantiated entities are not available immediately but will be constructed by the spawnable system + AzFramework::EntitySpawnTicket RequestNetSpawnableInstantiation(const AZ::Data::Asset& rootSpawnable); NetworkEntityTracker m_networkEntityTracker; NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker; @@ -120,5 +126,6 @@ namespace Multiplayer DeferredRpcMessages m_localDeferredRpcMessages; NetworkSpawnableLibrary m_networkPrefabLibrary; + AzFramework::EntitySpawnTicket m_rootNetSpawnableTicket; }; } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h index 09238acaee..0e632dc7b4 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h @@ -37,6 +37,7 @@ namespace Multiplayer NetworkEntityHandle Get(NetEntityId netEntityId); ConstNetworkEntityHandle Get(NetEntityId netEntityId) const; + //! Returns Net Entity ID for a given AZ Entity ID. NetEntityId Get(const AZ::EntityId& entityId) const; //! Returns true if the netEntityId exists. diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp deleted file mode 100644 index c8abac25cb..0000000000 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp +++ /dev/null @@ -1,115 +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 Multiplayer -{ - void NetBindMarkerComponent::Reflect(AZ::ReflectContext* context) - { - AZ::SerializeContext* serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class() - ->Version(1) - ->Field("NetEntityIndex", &NetBindMarkerComponent::m_netEntityIndex) - ->Field("NetSpawnableAsset", &NetBindMarkerComponent::m_networkSpawnableAsset); - } - } - - AzFramework::Spawnable* GetSpawnableFromAsset(AZ::Data::Asset& asset) - { - AzFramework::Spawnable* spawnable = asset.GetAs(); - if (!spawnable) - { - asset = - AZ::Data::AssetManager::Instance().GetAsset(asset.GetId(), AZ::Data::AssetLoadBehavior::PreLoad); - AZ::Data::AssetManager::Instance().BlockUntilLoadComplete(asset); - - spawnable = asset.GetAs(); - } - - return spawnable; - } - - - void NetBindMarkerComponent::Activate() - { - const auto agentType = AZ::Interface::Get()->GetAgentType(); - const bool spawnImmediately = - (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); - - if (spawnImmediately && m_networkSpawnableAsset.GetId().IsValid()) - { - AZ::Transform worldTm = GetEntity()->FindComponent()->GetWorldTM(); - auto preInsertionCallback = - [worldTm = AZStd::move(worldTm), netEntityIndex = m_netEntityIndex, spawnableAssetId = m_networkSpawnableAsset.GetId()] - (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) - { - if (entities.size() == 1) - { - AZ::Entity* netEntity = *entities.begin(); - - auto* transformComponent = netEntity->FindComponent(); - transformComponent->SetWorldTM(worldTm); - - AZ::Name spawnableName = AZ::Interface::Get()->GetSpawnableNameFromAssetId(spawnableAssetId); - PrefabEntityId prefabEntityId; - prefabEntityId.m_prefabName = spawnableName; - prefabEntityId.m_entityOffset = static_cast(netEntityIndex); - AZ::Interface::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority); - } - else - { - AZ_Error("NetBindMarkerComponent", false, "Requested to spawn 1 entity, but received %d", entities.size()); - } - }; - - m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset); - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; - optionalArgs.m_preInsertionCallback = AZStd::move(preInsertionCallback); - AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities( - m_netSpawnTicket, { m_netEntityIndex }, AZStd::move(optionalArgs)); - } - } - - void NetBindMarkerComponent::Deactivate() - { - if(m_netSpawnTicket.IsValid()) - { - AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket); - } - } - - size_t NetBindMarkerComponent::GetNetEntityIndex() const - { - return m_netEntityIndex; - } - - void NetBindMarkerComponent::SetNetEntityIndex(size_t netEntityIndex) - { - m_netEntityIndex = netEntityIndex; - } - - void NetBindMarkerComponent::SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset) - { - m_networkSpawnableAsset = networkSpawnableAsset; - } - - AZ::Data::Asset NetBindMarkerComponent::GetNetworkSpawnableAsset() const - { - return m_networkSpawnableAsset; - } - -} diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h deleted file mode 100644 index dce3252200..0000000000 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include -#include - -namespace Multiplayer -{ - //! @class NetBindMarkerComponent - //! @brief Component for tracking net entities in the original non-networked spawnable. - class NetBindMarkerComponent final : public AZ::Component - { - public: - AZ_COMPONENT(NetBindMarkerComponent, "{40612C1B-427D-45C6-A2F0-04E16DF5B718}"); - - static void Reflect(AZ::ReflectContext* context); - - NetBindMarkerComponent() = default; - ~NetBindMarkerComponent() override = default; - - //! AZ::Component overrides. - //! @{ - void Activate() override; - void Deactivate() override; - //! @} - - size_t GetNetEntityIndex() const; - void SetNetEntityIndex(size_t val); - - void SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset); - AZ::Data::Asset GetNetworkSpawnableAsset() const; - - private: - AZ::Data::Asset m_networkSpawnableAsset{AZ::Data::AssetLoadBehavior::PreLoad}; - size_t m_netEntityIndex = 0; - AzFramework::EntitySpawnTicket m_netSpawnTicket; - }; -} // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index acfec5eb38..e0990aa785 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -46,7 +45,7 @@ namespace Multiplayer { if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) { - serializeContext->Class()->Version(1); + serializeContext->Class()->Version(2); } } @@ -137,8 +136,6 @@ namespace Multiplayer networkSpawnableAsset.Create(networkSpawnable->GetId()); networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad); - size_t netEntitiesIndexCounter = 0; - for (auto* prefabEntity : prefabNetEntities) { Instance* instance = netEntityToInstanceMap[prefabEntity]; @@ -148,30 +145,11 @@ namespace Multiplayer AZ_Assert(netEntity, "Unable to detach entity %s [%s] from the source prefab instance", prefabEntity->GetName().c_str(), entityId.ToString().c_str()); - // Net entity will need a new ID to avoid IDs collision - netEntity->SetId(AZ::Entity::MakeId()); netEntity->InvalidateDependencies(); netEntity->EvaluateDependencies(); // Insert the entity into the target net spawnable netSpawnableEntities.emplace_back(netEntity); - - // Use the old ID for the breadcrumb entity to keep parent-child relationship in the original spawnable - AZ::Entity* breadcrumbEntity = aznew AZ::Entity(entityId, netEntity->GetName()); - breadcrumbEntity->SetRuntimeActiveByDefault(netEntity->IsRuntimeActiveByDefault()); - - // Marker component is responsible to spawning entities based on the index. - NetBindMarkerComponent* netBindMarkerComponent = breadcrumbEntity->CreateComponent(); - netBindMarkerComponent->SetNetEntityIndex(netEntitiesIndexCounter); - netBindMarkerComponent->SetNetworkSpawnableAsset(networkSpawnableAsset); - - // Copy the transform component from the original entity to have the correct transform and parent-child relationship - AzFramework::TransformComponent* transformComponent = netEntity->FindComponent(); - breadcrumbEntity->CreateComponent(*transformComponent); - - instance->AddEntity(*breadcrumbEntity); - - netEntitiesIndexCounter++; } // Add net spawnable asset holder to the prefab root diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp index c40cb3677b..e83f6fea84 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp @@ -39,7 +39,7 @@ namespace Multiplayer m_networkSpawnableAsset = networkSpawnableAsset; } - AZ::Data::Asset NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset() + AZ::Data::Asset NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset() const { return m_networkSpawnableAsset; } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h index d369bbefd3..f32e8d31ab 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h @@ -33,7 +33,7 @@ namespace Multiplayer //! @} void SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset); - AZ::Data::Asset GetNetworkSpawnableAsset(); + AZ::Data::Asset GetNetworkSpawnableAsset() const; private: AZ::Data::Asset m_networkSpawnableAsset{ AZ::Data::AssetLoadBehavior::PreLoad }; diff --git a/Gems/Multiplayer/Code/Tests/MainTools.cpp b/Gems/Multiplayer/Code/Tests/MainTools.cpp index 89b2492bc6..53dfad62d1 100644 --- a/Gems/Multiplayer/Code/Tests/MainTools.cpp +++ b/Gems/Multiplayer/Code/Tests/MainTools.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -30,7 +29,6 @@ namespace Multiplayer { AZStd::vector descriptors({ NetBindComponent::CreateDescriptor(), - NetBindMarkerComponent::CreateDescriptor(), NetworkSpawnableHolderComponent::CreateDescriptor() }); diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index 0b2adb1530..bdc0129137 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -101,8 +101,6 @@ set(FILES Source/NetworkInput/NetworkInputMigrationVector.h Source/NetworkTime/NetworkTime.cpp Source/NetworkTime/NetworkTime.h - Source/Pipeline/NetBindMarkerComponent.cpp - Source/Pipeline/NetBindMarkerComponent.h Source/Pipeline/NetworkSpawnableHolderComponent.cpp Source/Pipeline/NetworkSpawnableHolderComponent.h Source/Physics/PhysicsUtils.cpp From c2b2cc060a788edb0d398578384de5939eb6c4f8 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 9 Sep 2021 10:52:12 -0500 Subject: [PATCH 04/63] WIP Signed-off-by: Guthrie Adams --- .../Application/AtomToolsApplication.h | 7 +++++ .../Application/AtomToolsApplication.cpp | 26 ++++++++++++------- .../Viewport/MaterialViewportRenderer.cpp | 7 ----- .../Viewport/MaterialViewportRenderer.h | 5 ---- .../Viewport/MaterialViewportWidget.cpp | 12 ++++++--- .../Code/Source/HttpRequestManager.cpp | 2 +- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h index d55755a242..f87aca641d 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -35,6 +36,7 @@ namespace AtomToolsFramework , public AzQtComponents::AzQtApplication , protected AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler , protected AzFramework::AssetSystemStatusBus::Handler + , protected AzFramework::AssetCatalogEventBus::Handler , protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler , protected AZ::UserSettingsOwnerRequestBus::Handler , protected AtomToolsMainWindowNotificationBus::Handler @@ -67,6 +69,11 @@ namespace AtomToolsFramework void OnMainWindowClosing() override; ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + // AzFramework::AssetCatalogEventBus::Handler overrides ... + void OnCatalogLoaded(const char* catalogFile) override; + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// // AssetDatabaseRequestsBus::Handler overrides... bool GetAssetDatabaseLocation(AZStd::string& result) override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index bad4ad97de..5751271312 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -91,6 +92,7 @@ namespace AtomToolsFramework AtomToolsMainWindowNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); } void AtomToolsApplication::CreateReflectionManager() @@ -173,16 +175,11 @@ namespace AtomToolsFramework AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast( &AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized); + AzFramework::AssetCatalogEventBus::Handler::BusConnect(); AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml"); - AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); - LoadSettings(); - AtomToolsMainWindowNotificationBus::Handler::BusConnect(); - - AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); - auto editorPythonEventsInterface = AZ::Interface::Get(); if (editorPythonEventsInterface) { @@ -192,9 +189,6 @@ namespace AtomToolsFramework editorPythonEventsInterface->StartPython(); } - // Delay execution of commands and scripts post initialization - QTimer::singleShot(0, [this]() { ProcessCommandLine(m_commandLine); }); - m_timer.start(); } @@ -203,12 +197,26 @@ namespace AtomToolsFramework ExitMainLoop(); } + void AtomToolsApplication::OnCatalogLoaded(const char* catalogFile) + { + AZ_UNUSED(catalogFile); + + // Delay execution of commands and scripts post initialization + AZ::TickBus::QueueFunction([this]() { + AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); + AtomToolsMainWindowNotificationBus::Handler::BusConnect(); + ProcessCommandLine(m_commandLine); + }); + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + } + void AtomToolsApplication::Destroy() { // before modules are unloaded, destroy UI to free up any assets it cached AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); m_styleManager.reset(); + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); AtomToolsMainWindowNotificationBus::Handler::BusDisconnect(); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index f98cdce91b..ceaed0e498 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -247,12 +247,10 @@ namespace MaterialEditor MaterialViewportNotificationBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect(); AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId()); - AzFramework::WindowSystemRequestBus::Handler::BusConnect(); } MaterialViewportRenderer::~MaterialViewportRenderer() { - AzFramework::WindowSystemRequestBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect(); @@ -303,11 +301,6 @@ namespace MaterialEditor return m_viewportController; } - AzFramework::NativeWindowHandle MaterialViewportRenderer::GetDefaultWindowHandle() - { - return (m_windowContext) ? m_windowContext->GetWindowHandle() : nullptr; - } - void MaterialViewportRenderer::OnDocumentOpened(const AZ::Uuid& documentId) { AZ::Data::Instance materialInstance; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h index 240d66fd43..df77b91356 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h @@ -16,7 +16,6 @@ #include #include #include -#include #include namespace AZ @@ -46,7 +45,6 @@ namespace MaterialEditor , public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler , public MaterialViewportNotificationBus::Handler , public AZ::TransformNotificationBus::MultiHandler - , public AzFramework::WindowSystemRequestBus::Handler { public: AZ_CLASS_ALLOCATOR(MaterialViewportRenderer, AZ::SystemAllocator, 0); @@ -81,9 +79,6 @@ namespace MaterialEditor // AZ::TransformNotificationBus::MultiHandler overrides... void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override; - // AzFramework::WindowSystemRequestBus::Handler overrides ... - AzFramework::NativeWindowHandle GetDefaultWindowHandle() override; - using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle; AZ::Data::Instance m_swapChainPass; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp index c78951ed45..d2fe78cb89 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp @@ -13,17 +13,18 @@ #include #include -#include -#include +#include +#include -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT +AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknow n-warning-option") // disable warnings spawned by QT #include #include -#include "Source/Viewport/ui_MaterialViewportWidget.h" +#include "Viewport/ui_MaterialViewportWidget.h" AZ_POP_DISABLE_WARNING #include #include +#include namespace Platform { @@ -45,6 +46,9 @@ namespace MaterialEditor dispatcher->installNativeEventFilter(this); } + AzFramework::WindowSystemNotificationBus::Broadcast( + &AzFramework::WindowSystemNotificationBus::Events::OnWindowCreated, GetViewportContext()->GetWindowHandle()); + // The viewport context created by AtomToolsFramework::RenderViewportWidget has no name. // Systems like frame capturing and post FX expect there to be a context with DefaultViewportContextName auto viewportContextManager = AZ::Interface::Get(); diff --git a/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp b/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp index 8ed40c1599..c4753d2e94 100644 --- a/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp +++ b/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp @@ -50,7 +50,7 @@ namespace HttpRequestor { m_thread.join(); } - + AWSNativeSDKInit::InitializationManager::Shutdown(); } void Manager::AddRequest(Parameters && httpRequestParameters) From 1f66e8a76054a7a136e13fcf1b2cd126c73c1d8a Mon Sep 17 00:00:00 2001 From: pereslav Date: Mon, 13 Sep 2021 13:35:35 +0100 Subject: [PATCH 05/63] PR feedback addressing Signed-off-by: pereslav --- .../NetworkEntity/NetworkEntityManager.cpp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 88f0b221ec..4e0818323e 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -470,14 +470,21 @@ namespace Multiplayer AzFramework::EntitySpawnTicket NetworkEntityManager::RequestNetSpawnableInstantiation(const AZ::Data::Asset& rootSpawnable) { const AzFramework::Spawnable::EntityList& entityList = rootSpawnable->GetEntities(); - if (entityList.size() == 0) + if (entityList.empty()) { - AZ_Assert(false, "RequestNetSpawnableInstantiation: No entities in the spawnable %s", rootSpawnable.GetHint().c_str()) + AZ_Error("NetworkEntityManager", false, + "RequestNetSpawnableInstantiation: No entities in the spawnable %s", rootSpawnable.GetHint().c_str()); return {}; } // The first entity in every spawnable is the root one const AZ::Entity* rootEntity = (entityList.begin())->get(); + if(!rootEntity) + { + AZ_Error("NetworkEntityManager", false, + "RequestNetSpawnableInstantiation: Root entity is null in the spawnable %s", rootSpawnable.GetHint().c_str()); + return {}; + } const auto* holderComponent = rootEntity->FindComponent(); if(!holderComponent) @@ -493,17 +500,19 @@ namespace Multiplayer AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_priority = AzFramework::SpawnablePriority_High; + AZ::Name netSpawnableName = AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnableAsset.GetId()); + // Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene - optionalArgs.m_preInsertionCallback = [spawnableAssetId = netSpawnableAsset.GetId()](AzFramework::EntitySpawnTicket::Id, + optionalArgs.m_preInsertionCallback = [netSpawnableName = netSpawnableName](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) { - for (uint32_t netEntityIndex = 0; netEntityIndex < entities.size(); netEntityIndex++) + for (uint32_t netEntityIndex = 0, entitiesSize = aznumeric_cast(entities.size()); + netEntityIndex < entitiesSize; netEntityIndex++) { AZ::Entity* netEntity = *(entities.begin() + netEntityIndex); - AZ::Name spawnableName = AZ::Interface::Get()->GetSpawnableNameFromAssetId(spawnableAssetId); PrefabEntityId prefabEntityId; - prefabEntityId.m_prefabName = spawnableName; + prefabEntityId.m_prefabName = netSpawnableName; prefabEntityId.m_entityOffset = netEntityIndex; AZ::Interface::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority); } From 9b0e3f26b14e6573ad0877a63ef9e9d66879cefb Mon Sep 17 00:00:00 2001 From: pereslav Date: Mon, 13 Sep 2021 16:30:42 +0100 Subject: [PATCH 06/63] PR feedback addressing 2 Signed-off-by: pereslav --- .../Code/Source/NetworkEntity/NetworkEntityManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 4e0818323e..793acc17ff 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -500,10 +500,11 @@ namespace Multiplayer AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_priority = AzFramework::SpawnablePriority_High; - AZ::Name netSpawnableName = AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnableAsset.GetId()); + const AZ::Name netSpawnableName = + AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnableAsset.GetId()); // Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene - optionalArgs.m_preInsertionCallback = [netSpawnableName = netSpawnableName](AzFramework::EntitySpawnTicket::Id, + optionalArgs.m_preInsertionCallback = [netSpawnableName](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) { for (uint32_t netEntityIndex = 0, entitiesSize = aznumeric_cast(entities.size()); From aa6c03888051eb90999428936efa7c5d28455ba0 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Mon, 13 Sep 2021 16:51:50 -0400 Subject: [PATCH 07/63] Hierarchical components, phase 1, unittests Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Multiplayer/Components/NetBindComponent.h | 2 + .../Components/NetworkHierarchyBus.h | 46 + .../NetworkHierarchyChildComponent.h | 66 + .../NetworkHierarchyRootComponent.h | 76 ++ .../Components/NetworkTransformComponent.h | 4 + ...kHierarchyChildComponent.AutoComponent.xml | 14 + ...rkHierarchyRootComponent.AutoComponent.xml | 14 + .../NetworkHierarchyChildComponent.cpp | 134 ++ .../NetworkHierarchyRootComponent.cpp | 277 ++++ .../Components/NetworkTransformComponent.cpp | 43 +- .../Code/Source/MultiplayerGem.cpp | 6 +- .../EntityReplicationManager.cpp | 26 +- .../EntityReplication/EntityReplicator.cpp | 96 +- .../EntityReplication/EntityReplicator.h | 4 +- .../Code/Tests/ClientHierarchyTests.cpp | 379 ++++++ .../Code/Tests/CommonHierarchySetup.h | 387 ++++++ Gems/Multiplayer/Code/Tests/MockInterfaces.h | 166 +++ .../Code/Tests/ServerHierarchyTests.cpp | 1145 +++++++++++++++++ Gems/Multiplayer/Code/multiplayer_files.cmake | 42 +- .../Code/multiplayer_tests_files.cmake | 4 + 20 files changed, 2886 insertions(+), 45 deletions(-) create mode 100644 Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h create mode 100644 Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h create mode 100644 Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h create mode 100644 Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml create mode 100644 Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml create mode 100644 Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp create mode 100644 Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp create mode 100644 Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp create mode 100644 Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h create mode 100644 Gems/Multiplayer/Code/Tests/MockInterfaces.h create mode 100644 Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index 65bac09726..855d109145 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -199,6 +199,8 @@ namespace Multiplayer friend class NetworkEntityManager; friend class EntityReplicationManager; + + friend class HierarchyTests; }; bool NetworkRoleHasController(NetEntityRole networkRole); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h new file mode 100644 index 0000000000..72dff65401 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace Multiplayer +{ + class NetworkHierarchyNotifications + : public AZ::ComponentBus + { + public: + //! Called when a hierarchy has been updated (a child added or removed, etc.) + virtual void OnNetworkHierarchyUpdated([[maybe_unused]] const AZ::EntityId& rootEntityId) {} + + //! Called when an entity has left a hierarchy + virtual void OnLeavingNetworkHierarchy() {} + }; + + typedef AZ::EBus NetworkHierarchyNotificationBus; + + class NetworkHierarchyRequests + : public AZ::ComponentBus + { + public: + //! @returns hierarchical entities, the first element is the top level root + virtual AZStd::vector GetHierarchicalEntities() const = 0; + + //! @returns the top level root of a hierarchy, or nullptr if this entity is not in a hierarchy + virtual AZ::Entity* GetHierarchicalRoot() const = 0; + + //! @return true if this entity is a child entity within a hierarchy + virtual bool IsHierarchicalChild() const = 0; + + //! @return true if this entity is the top level root of a hierarchy + virtual bool IsHierarchicalRoot() const = 0; + }; + + typedef AZ::EBus NetworkHierarchyRequestBus; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h new file mode 100644 index 0000000000..27f794ec21 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -0,0 +1,66 @@ +/* + * 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 Multiplayer +{ + class NetworkHierarchyRootComponent; + + //! @class NetworkHierarchyChildComponent + //! @brief Component that declares network dependency on the parent of this entity + /* + * The parent of this entity should have @NetworkHierarchyChildComponent (or @NetworkHierarchyRootComponent). + * A network hierarchy is a collection of entities with one @NetworkHierarchyRootComponent at the top parent + * and one or more @NetworkHierarchyChildComponent on its child entities. + */ + class NetworkHierarchyChildComponent final + : public NetworkHierarchyChildComponentBase + , public NetworkHierarchyRequestBus::Handler + { + friend class NetworkHierarchyRootComponent; + + public: + AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyChildComponent, s_networkHierarchyChildComponentConcreteUuid, Multiplayer::NetworkHierarchyChildComponentBase); + + 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); + + NetworkHierarchyChildComponent(); + + //! @{ + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + //! @} + + //! NetworkHierarchyRequestBus overrides + //! @{ + bool IsHierarchicalChild() const override; + bool IsHierarchicalRoot() const override { return false; } + AZ::Entity* GetHierarchicalRoot() const override; + AZStd::vector GetHierarchicalEntities() const override; + //! @} + + protected: + //! Used by @NetworkHierarchyRootComponent + void SetTopLevelHierarchyRootComponent(NetworkHierarchyRootComponent* hierarchyRoot); + + private: + const NetworkHierarchyRootComponent* m_hierarchyRootComponent = nullptr; + + AZ::Event::Handler m_hierarchyRootNetIdChanged; + void OnHierarchyRootNetIdChanged(NetEntityId rootNetId); + }; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h new file mode 100644 index 0000000000..48c003d9e7 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Multiplayer +{ + //! @class NetworkHierarchyRootComponent + //! @brief Component that declares the top level entity of a network hierarchy. + /* + * Call @GetHierarchyChildren to get the list of hierarchical entities. + * A network hierarchy is meant to be a small group of entities. You can control the maximum supported size of + * a network hierarchy by modifying CVar @bg_hierarchyEntityMaxLimit. + * + * A root component marks either a top most root of a hierarchy, or an inner root of an attach hierarchy. + */ + class NetworkHierarchyRootComponent final + : public NetworkHierarchyRootComponentBase + , public NetworkHierarchyRequestBus::Handler + , protected AZ::TransformNotificationBus::MultiHandler + { + public: + AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase); + + 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); + + //! NetworkHierarchyRootComponentBase overrides. + //! @{ + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + //! @} + + //! NetworkHierarchyRequestBus overrides. + //! @{ + bool IsHierarchicalRoot() const override; + bool IsHierarchicalChild() const override; + AZStd::vector GetHierarchicalEntities() const override; + AZ::Entity* GetHierarchicalRoot() const override; + //! @} + + protected: + //! AZ::TransformNotificationBus::Handler overrides. + //! @{ + void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId newParent) override; + void OnChildAdded(AZ::EntityId child) override; + void OnChildRemoved(AZ::EntityId childRemovedId) override; + //! @} + + void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); + + private: + AZ::Entity* m_higherRootEntity = nullptr; + AZStd::vector m_hierarchicalEntities; + + void RebuildHierarchy(); + + //! @returns false if the maximum supported hierarchy size has been reached + bool RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount); + //! @returns false if the maximum supported hierarchy size has been reached + bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount); + }; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index 914aeaadd3..969b59a672 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -36,6 +36,7 @@ namespace Multiplayer void OnTranslationChangedEvent(const AZ::Vector3& translation); void OnScaleChangedEvent(float scale); void OnResetCountChangedEvent(); + void OnParentIdChangedEvent(NetEntityId newParent); void UpdateTargetHostFrameId(); @@ -46,6 +47,7 @@ namespace Multiplayer AZ::Event::Handler m_translationEventHandler; AZ::Event::Handler m_scaleEventHandler; AZ::Event::Handler m_resetCountEventHandler; + AZ::Event::Handler m_parentIdChangedEventHandler; EntityPreRenderEvent::Handler m_entityPreRenderEventHandler; EntityCorrectionEvent::Handler m_entityCorrectionEventHandler; @@ -64,7 +66,9 @@ namespace Multiplayer private: void OnTransformChangedEvent(const AZ::Transform& worldTm); + void OnParentIdChangedEvent(AZ::EntityId oldParent, AZ::EntityId newParent); AZ::TransformChangedEvent::Handler m_transformChangedHandler; + AZ::ParentChangedEvent::Handler m_parentIdChangedHandler; }; } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml new file mode 100644 index 0000000000..46523d3724 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml new file mode 100644 index 0000000000..0f33e1f642 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp new file mode 100644 index 0000000000..c644b832a0 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + void NetworkHierarchyChildComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Network Hierarchy Child", "declares a network dependency on the root of this hierarchy") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ; + } + } + NetworkHierarchyChildComponentBase::Reflect(context); + } + + void NetworkHierarchyChildComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("NetworkTransformComponent")); + } + + void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + } + + void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + NetworkHierarchyChildComponent::NetworkHierarchyChildComponent() + : m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); }) + { + + } + + void NetworkHierarchyChildComponent::OnInit() + { + } + + void NetworkHierarchyChildComponent::OnActivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) + { + HierarchyRootAddEvent(m_hierarchyRootNetIdChanged); + NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); + } + + void NetworkHierarchyChildComponent::OnDeactivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) + { + NetworkHierarchyRequestBus::Handler::BusDisconnect(); + } + + bool NetworkHierarchyChildComponent::IsHierarchicalChild() const + { + if (GetHierarchyRoot() != InvalidNetEntityId) + { + return true; + } + + return false; + } + + AZ::Entity* NetworkHierarchyChildComponent::GetHierarchicalRoot() const + { + return m_hierarchyRootComponent ? m_hierarchyRootComponent->GetEntity() : nullptr; + } + + AZStd::vector NetworkHierarchyChildComponent::GetHierarchicalEntities() const + { + if (m_hierarchyRootComponent) + { + return m_hierarchyRootComponent->GetHierarchicalEntities(); + } + + return {}; + } + + void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootComponent(NetworkHierarchyRootComponent* hierarchyRoot) + { + m_hierarchyRootComponent = hierarchyRoot; + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + if (hierarchyRoot) + { + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetEntityId()); + controller->SetHierarchyRoot(netRootId); + + NetworkHierarchyNotificationBus::Event(GetEntityId(), &NetworkHierarchyNotificationBus::Events::OnNetworkHierarchyUpdated, hierarchyRoot->GetEntityId()); + } + else + { + controller->SetHierarchyRoot(InvalidNetEntityId); + NetworkHierarchyNotificationBus::Event(GetEntityId(), &NetworkHierarchyNotificationBus::Events::OnLeavingNetworkHierarchy); + } + } + } + + void NetworkHierarchyChildComponent::OnHierarchyRootNetIdChanged(NetEntityId rootNetId) + { + const ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(rootNetId); + if (rootHandle.Exists()) + { + m_hierarchyRootComponent = rootHandle.FindComponent(); + } + else + { + m_hierarchyRootComponent = nullptr; + } + } +} diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp new file mode 100644 index 0000000000..2fa960ed2a --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -0,0 +1,277 @@ +/* + * 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 + +AZ_CVAR(uint32_t, bg_hierarchyEntityMaxLimit, 16, nullptr, AZ::ConsoleFunctorFlags::Null, + "Maximum allowed size of network entity hierarchies, including top level entity."); + +namespace Multiplayer +{ + void NetworkHierarchyRootComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Network Hierarchy Root", "marks the entity as the root of an entity hierarchy") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ; + } + } + NetworkHierarchyRootComponentBase::Reflect(context); + } + + void NetworkHierarchyRootComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("NetworkTransformComponent")); + } + + void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + void NetworkHierarchyRootComponent::OnInit() + { + } + + void NetworkHierarchyRootComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + m_hierarchicalEntities.push_back(GetEntity()); + + NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); + AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); + } + + void NetworkHierarchyRootComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); + NetworkHierarchyRequestBus::Handler::BusDisconnect(); + + for (const AZ::Entity* childEntity : m_hierarchicalEntities) + { + auto* hierarchyChildComponent = childEntity->FindComponent(); + auto* hierarchyRootComponent = childEntity->FindComponent(); + + if (hierarchyChildComponent) + { + hierarchyChildComponent->SetTopLevelHierarchyRootComponent(nullptr); + } + if (hierarchyRootComponent) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr); + } + } + + m_hierarchicalEntities.clear(); + m_higherRootEntity = nullptr; + } + + bool NetworkHierarchyRootComponent::IsHierarchicalRoot() const + { + if (GetHierarchyRoot() != InvalidNetEntityId) + { + return false; + } + + return true; + } + + bool NetworkHierarchyRootComponent::IsHierarchicalChild() const + { + return !IsHierarchicalRoot(); + } + + AZStd::vector NetworkHierarchyRootComponent::GetHierarchicalEntities() const + { + return m_hierarchicalEntities; + } + + AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const + { + if (m_higherRootEntity) + { + return m_higherRootEntity; + } + + return GetEntity(); + } + + void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) + { + const AZ::EntityId entityBusId = *AZ::TransformNotificationBus::GetCurrentBusId(); + if (GetEntityId() != entityBusId) + { + return; // ignore parent changes of child entities + } + + if (AZ::Entity* parentEntity = AZ::Interface::Get()->FindEntity(newParent)) + { + if (parentEntity->FindComponent()) + { + m_higherRootEntity = parentEntity; + m_hierarchicalEntities.clear(); + AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); + + // Should still listen for its events, such as when this root detaches + AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); + } + } + else + { + // detached from parent + RebuildHierarchy(); + } + } + + void NetworkHierarchyRootComponent::OnChildAdded([[maybe_unused]] AZ::EntityId child) + { + // Parent-child notifications from TransformNotificationBus are not reliable enough to avoid duplicate notifications, + // so we will rebuild from scratch to avoid duplicate entries in @m_hierarchicalEntities. + RebuildHierarchy(); + } + + void NetworkHierarchyRootComponent::RebuildHierarchy() + { + m_hierarchicalEntities.clear(); + m_hierarchicalEntities.push_back(GetEntity()); // add the root itself + + AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); + AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); + + uint32_t currentEntityCount = aznumeric_cast(m_hierarchicalEntities.size()); + RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount); + } + + bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount) + { + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, underEntity, &AZ::TransformBus::Events::GetChildren); + + for (const AZ::EntityId newChildId : allChildren) + { + if (!RecursiveAttachHierarchicalChild(newChildId, currentEntityCount)) + { + return false; + } + } + + return true; + } + + bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount) + { + if (currentEntityCount >= bg_hierarchyEntityMaxLimit) + { + AZLOG_WARN("Entity %s is trying to build a network hierarchy that is too large. bg_hierarchyEntityMaxLimit is currently set to (%u)", + GetEntity()->GetName().c_str(), static_cast(bg_hierarchyEntityMaxLimit)); + return false; + } + + if (AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(entity)) + { + auto* hierarchyChildComponent = childEntity->FindComponent(); + auto* hierarchyRootComponent = childEntity->FindComponent(); + + if (hierarchyChildComponent || hierarchyRootComponent) + { + AZ::TransformNotificationBus::MultiHandler::BusConnect(entity); + m_hierarchicalEntities.push_back(childEntity); + ++currentEntityCount; + + if (hierarchyChildComponent) + { + hierarchyChildComponent->SetTopLevelHierarchyRootComponent(this); + } + else if (hierarchyRootComponent) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(GetEntity()); + } + + if (!RecursiveAttachHierarchicalEntities(entity, currentEntityCount)) + { + return false; + } + } + } + + return true; + } + + void NetworkHierarchyRootComponent::OnChildRemoved(AZ::EntityId childRemovedId) + { + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, childRemovedId, &AZ::TransformBus::Events::GetEntityAndAllDescendants); + + for (AZ::EntityId childId : allChildren) + { + AZ::TransformNotificationBus::MultiHandler::BusDisconnect(childId); + + const AZ::Entity* childEntity = nullptr; + + AZStd::erase_if(m_hierarchicalEntities, [childId, &childEntity](const AZ::Entity* entity) + { + if (entity->GetId() == childId) + { + childEntity = entity; + return true; + } + + return false; + }); + + if (childEntity) + { + if (NetworkHierarchyChildComponent* childComponent = childEntity->FindComponent()) + { + childComponent->SetTopLevelHierarchyRootComponent(nullptr); + } + } + } + } + + void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) + { + m_higherRootEntity = hierarchyRoot; + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + if (hierarchyRoot) + { + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetId()); + controller->SetHierarchyRoot(netRootId); + } + else + { + controller->SetHierarchyRoot(InvalidNetEntityId); + } + } + } +} diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index fa08794c4d..a37b6b52ed 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -30,6 +30,7 @@ namespace Multiplayer , m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); }) , m_scaleEventHandler([this](float scale) { OnScaleChangedEvent(scale); }) , m_resetCountEventHandler([this](const uint8_t&) { OnResetCountChangedEvent(); }) + , m_parentIdChangedEventHandler([this](NetEntityId newParent) { OnParentIdChangedEvent(newParent); }) , m_entityPreRenderEventHandler([this](float deltaTime, float blendFactor) { OnPreRender(deltaTime, blendFactor); }) , m_entityCorrectionEventHandler([this]() { OnCorrection(); }) { @@ -47,8 +48,12 @@ namespace Multiplayer TranslationAddEvent(m_translationEventHandler); ScaleAddEvent(m_scaleEventHandler); ResetCountAddEvent(m_resetCountEventHandler); - GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler); - GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler); + ParentEntityIdAddEvent(m_parentIdChangedEventHandler); + if (GetNetBindComponent()) + { + GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler); + GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler); + } // When coming into relevance, reset all blending factors so we don't interpolate to our start position OnResetCountChangedEvent(); @@ -82,15 +87,33 @@ namespace Multiplayer void NetworkTransformComponent::OnResetCountChangedEvent() { + OnParentIdChangedEvent(GetParentEntityId()); + m_targetTransform.SetRotation(GetRotation()); m_targetTransform.SetTranslation(GetTranslation()); m_targetTransform.SetUniformScale(GetScale()); m_previousTransform = m_targetTransform; } + void NetworkTransformComponent::OnParentIdChangedEvent([[maybe_unused]] NetEntityId newParent) + { + const ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(newParent); + if (rootHandle.Exists()) + { + const AZ::EntityId parentEntityId = rootHandle.GetEntity()->GetId(); + if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) + { + if (transformComponent->GetParentId() != parentEntityId) + { + transformComponent->SetParent(parentEntityId); + } + } + } + } + void NetworkTransformComponent::UpdateTargetHostFrameId() { - HostFrameId currentHostFrameId = Multiplayer::GetNetworkTime()->GetHostFrameId(); + const HostFrameId currentHostFrameId = Multiplayer::GetNetworkTime()->GetHostFrameId(); if (currentHostFrameId > m_targetHostFrameId) { m_targetHostFrameId = currentHostFrameId; @@ -137,6 +160,7 @@ namespace Multiplayer NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent) : NetworkTransformComponentControllerBase(parent) , m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformChangedEvent(worldTm); }) + , m_parentIdChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId newParent) { OnParentIdChangedEvent(oldParent, newParent); }) { ; } @@ -145,6 +169,9 @@ namespace Multiplayer { GetParent().GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler); OnTransformChangedEvent(GetParent().GetTransformComponent()->GetWorldTM()); + + GetParent().GetTransformComponent()->BindParentChangedEventHandler(m_parentIdChangedHandler); + OnParentIdChangedEvent(AZ::EntityId(), GetParent().GetTransformComponent()->GetParentId()); } void NetworkTransformComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) @@ -158,4 +185,14 @@ namespace Multiplayer SetTranslation(worldTm.GetTranslation()); SetScale(worldTm.GetUniformScale()); } + + void NetworkTransformComponentController::OnParentIdChangedEvent([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) + { + AZ::Entity* parentEntity = AZ::Interface::Get()->FindEntity(newParent); + if (parentEntity) + { + const ConstNetworkEntityHandle parentHandle(parentEntity, GetNetworkEntityTracker()); + SetParentEntityId(parentHandle.GetNetEntityId()); + } + } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp index 1ad7be1a0a..849dc15e10 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp @@ -6,13 +6,15 @@ * */ +#include +#include +#include +#include #include #include #include #include #include -#include -#include namespace Multiplayer { diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 482d3a1ee8..b45c05cda7 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -75,6 +75,8 @@ namespace Multiplayer void EntityReplicationManager::ActivatePendingEntities() { + AZStd::vector notReadyEntities; + const AZ::TimeMs endTimeMs = AZ::GetElapsedTimeMs() + m_entityActivationTimeSliceMs; while (!m_entitiesPendingActivation.empty()) { @@ -83,7 +85,14 @@ namespace Multiplayer EntityReplicator* entityReplicator = GetEntityReplicator(entityId); if (entityReplicator && !entityReplicator->IsMarkedForRemoval()) { - entityReplicator->ActivateNetworkEntity(); + if (entityReplicator->IsReadyToActivate()) + { + entityReplicator->ActivateNetworkEntity(); + } + else + { + notReadyEntities.push_back(entityId); + } } if (m_entityActivationTimeSliceMs > AZ::TimeMs{ 0 } && AZ::GetElapsedTimeMs() > endTimeMs) { @@ -91,6 +100,11 @@ namespace Multiplayer break; } } + + for (NetEntityId netEntityId : notReadyEntities) + { + m_entitiesPendingActivation.push_back(netEntityId); + } } void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs) @@ -249,15 +263,15 @@ namespace Multiplayer void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs) { EntityReplicatorList toSendList = GenerateEntityUpdateList(); - + AZLOG(NET_ReplicationInfo, "Sending %zd updates from %d to %d", toSendList.size(), (uint8_t)GetNetworkEntityManager()->GetHostId(), (uint8_t)GetRemoteHostId()); - + // prep a replication record for send, at this point, everything needs to be sent for (EntityReplicator* replicator : toSendList) { replicator->GetPropertyPublisher()->PrepareSerialization(); } - + // While our to send list is not empty, build up another packet to send do { @@ -524,7 +538,7 @@ namespace Multiplayer bool EntityReplicationManager::HandlePropertyChangeMessage ( - AzNetworking::IConnection* invokingConnection, + AzNetworking::IConnection* invokingConnection, EntityReplicator* entityReplicator, AzNetworking::PacketId packetId, NetEntityId netEntityId, @@ -1137,7 +1151,7 @@ namespace Multiplayer AzNetworking::TrackChangedSerializer outputSerializer(message.m_propertyUpdateData.GetBuffer(), static_cast(message.m_propertyUpdateData.GetSize())); if (!HandlePropertyChangeMessage ( - invokingConnection, + invokingConnection, replicator, AzNetworking::InvalidPacketId, message.m_entityId, diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index 14df1bb028..ad8c883f34 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -6,23 +6,25 @@ * */ -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include -#include #include #include @@ -30,6 +32,8 @@ #include +AZ_CVAR(bool, bg_debugHierarchyActivation, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Helpful messages when debugging network hierarchy behavior"); + namespace Multiplayer { EntityReplicator::EntityReplicator @@ -48,7 +52,7 @@ namespace Multiplayer , m_onForwardRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) , m_onSendAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) , m_onForwardAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) - , m_onEntityStopHandler([this](const ConstNetworkEntityHandle &) { OnEntityRemovedEvent(); }) + , m_onEntityStopHandler([this](const ConstNetworkEntityHandle&) { OnEntityRemovedEvent(); }) , m_proxyRemovalEvent([this] { OnProxyRemovalTimedEvent(); }, AZ::Name("ProxyRemovalTimedEvent")) { if (auto localEnt = m_entityHandle.GetEntity()) @@ -119,12 +123,12 @@ namespace Multiplayer { m_replicationManager.AddReplicatorToPendingSend(*this); m_propertyPublisher = AZStd::make_unique - ( - GetRemoteNetworkRole(), - !RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False, - m_netBindComponent, - *m_connection - ); + ( + GetRemoteNetworkRole(), + !RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False, + m_netBindComponent, + *m_connection + ); m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler); } else @@ -279,7 +283,7 @@ namespace Multiplayer AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent"); bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) - && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole()); + && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole()); bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client; bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous; if (isAuthority || isClient || isAutonomous) @@ -306,9 +310,9 @@ namespace Multiplayer bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const { bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server) - && (GetRemoteNetworkRole() == NetEntityRole::Authority); + && (GetRemoteNetworkRole() == NetEntityRole::Authority); bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client) - || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); + || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); return isServer || isClient; } @@ -405,6 +409,62 @@ namespace Multiplayer return m_replicationManager.GetResendTimeoutTimeMs(); } + bool EntityReplicator::IsReadyToActivate() const + { + const AZ::Entity* entity = m_entityHandle.GetEntity(); + AZ_Assert(entity, "Entity replicator entity unexpectedly missing"); + + const NetworkHierarchyChildComponent* hierarchyChildComponent = entity->FindComponent(); + const NetworkHierarchyRootComponent* hierarchyRootComponent = nullptr; + + if (hierarchyChildComponent == nullptr) + { + // child and root hierarchy components are mutually exclusive + hierarchyRootComponent = entity->FindComponent(); + } + + if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchicalChild()) + || (hierarchyRootComponent && hierarchyRootComponent->IsHierarchicalChild())) + { + // if hierarchy is enabled for the entity, check if the parent is available + if (const NetworkTransformComponent* networkTransform = entity->FindComponent()) + { + const NetEntityId parentId = networkTransform->GetParentEntityId(); + /* + * For root entities attached to a level, a network parent won't be set. + * In this case, this entity is the root entity of the hierarchy and it will be activated first. + */ + if (parentId != InvalidNetEntityId) + { + ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId); + + const AZ::Entity* parentEntity = parentHandle.GetEntity(); + if (parentEntity && parentEntity->GetState() == AZ::Entity::State::Active) + { + if (bg_debugHierarchyActivation) + { + AZLOG_DEBUG( + "Entity %s asking for activation - granted", + entity->GetName().c_str()); + } + return true; + } + + if (bg_debugHierarchyActivation) + { + AZLOG_DEBUG( + "Entity %s asking for activation - waiting on the parent %u", + entity->GetName().c_str(), + aznumeric_cast(parentId)); + } + return false; + } + } + } + + return true; + } + NetworkEntityUpdateMessage EntityReplicator::GenerateUpdatePacket() { if (IsMarkedForRemoval() && OwnsReplicatorLifetime()) // TODO: clean this up diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h index ec4bd8c4f5..e4dc62bc26 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h @@ -36,7 +36,7 @@ namespace Multiplayer { public: EntityReplicator(EntityReplicationManager& replicationManager, AzNetworking::IConnection* connection, NetEntityRole remoteNetworkRole, const ConstNetworkEntityHandle& entityHandle); - virtual ~EntityReplicator(); + ~EntityReplicator() override; NetEntityRole GetBoundLocalNetworkRole() const; NetEntityRole GetRemoteNetworkRole() const; @@ -62,6 +62,8 @@ namespace Multiplayer bool IsDeletionAcknowledged() const; bool WasMigrated() const; void SetWasMigrated(bool wasMigrated); + // If an entity is part of a network hierarchy then it is only ready to activate when its direct parent entity is active. + bool IsReadyToActivate() const; NetworkEntityUpdateMessage GenerateUpdatePacket(); diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp new file mode 100644 index 0000000000..d1b01a279c --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -0,0 +1,379 @@ +/* + * 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 + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + /* + * Test NetBindComponent activation. This must work before more complicated tests. + */ + TEST_F(HierarchyTests, On_Client_NetBindComponent_Activate) + { + AZ::Entity entity; + entity.CreateComponent(); + SetupEntity(entity, NetEntityId{ 1 }, NetEntityRole::Client); + entity.Activate(); + + StopEntity(entity); + + entity.Deactivate(); + } + + /* + * Hierarchy test - a child entity on a client delaying activation until its hierarchical parent has been activated + */ + TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_BeforeParent) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZ::Entity childEntity; + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(&childEntity, m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + entityReplicator.Initialize(childHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), false); + } + + TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_Inner_Root_Before_Top_Root) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZ::Entity innerRootEntity; + CreateEntityWithRootHierarchy(innerRootEntity); + SetupEntity(innerRootEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(innerRootEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(innerRootEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle innerRootHandle(&innerRootEntity, m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, innerRootHandle); + entityReplicator.Initialize(innerRootHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), false); + } + + TEST_F(HierarchyTests, On_Client_Not_In_Hierarchy_EntityReplicator_Ignores_Parent) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZ::Entity childEntity; + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, InvalidNetEntityId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(&childEntity, m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + entityReplicator.Initialize(childHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), true); + } + + /* + * Hierarchy test - a child entity on a client allowing activation when its hierarchical parent is active + */ + TEST_F(HierarchyTests, On_Client_EntityReplicator_Activates_AfterParent) + { + AZ::Entity childEntity; + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(&childEntity, m_networkEntityTracker.get()); + EntityReplicator childEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + childEntityReplicator.Initialize(childHandle); + + // Now let's create a parent entity and activate it + AZ::Entity parentEntity; + CreateEntityWithRootHierarchy(parentEntity); + SetupEntity(parentEntity, NetEntityId{ 1 }, NetEntityRole::Client); + + // Create an entity replicator for the parent entity + const NetworkEntityHandle parentHandle(&parentEntity, m_networkEntityTracker.get()); + ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Return(parentHandle)); + EntityReplicator parentEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, parentHandle); + parentEntityReplicator.Initialize(parentHandle); + + parentEntity.Activate(); + + // The child should be ready to be activated + EXPECT_EQ(childEntityReplicator.IsReadyToActivate(), true); + + StopEntity(parentEntity); + + parentEntity.Deactivate(); + } + + /* + * Parent -> Child + */ + class ClientSimpleHierarchyTests : public HierarchyTests + { + public: + static const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + static const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); + m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); + + m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), RootNetEntityId, EntityInfo::Role::Root); + m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), ChildNetEntityId, EntityInfo::Role::Child); + + CreateSimpleHierarchy(*m_rootEntityInfo, *m_childEntityInfo); + + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + // now the two entities are under one hierarchy + } + + void TearDown() override + { + m_childEntityInfo.reset(); + m_rootEntityInfo.reset(); + + StopAndDeleteEntity(m_childEntity); + StopAndDeleteEntity(m_rootEntity); + + HierarchyTests::TearDown(); + } + + void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child) + { + PopulateHierarchicalEntity(root); + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Client); + + PopulateHierarchicalEntity(child); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); + SetHierarchyRootFieldOnNetworkHierarchyChild(child.m_entity, root.m_netId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(&root.m_entity, m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity.Activate(); + child.m_entity.Activate(); + } + + void SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(const AZ::Entity& entity, NetEntityId value) + { + /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/; + + ReplicationRecord currentRecord(NetEntityRole::Client); + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(value), + "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + ReplicationRecord notifyRecord = currentRecord; + + entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + + entity.FindComponent()->NotifyStateDeltaChanges(notifyRecord); + } + + AZStd::unique_ptr m_rootEntity; + AZStd::unique_ptr m_childEntity; + + AZStd::unique_ptr m_rootEntityInfo; + AZStd::unique_ptr m_childEntityInfo; + }; + + TEST_F(ClientSimpleHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchicalRoot(), + m_rootEntity.get() + ); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 2) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], + m_rootEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], + m_childEntity.get() + ); + } + } + + TEST_F(ClientSimpleHierarchyTests, Client_Detaches_Child_When_Server_Detaches) + { + // simulate server detaching child entity + SetParentIdOnNetworkTransform(*m_childEntity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(*m_childEntity, InvalidNetEntityId); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + } + + /* + * Parent -> Child + */ + class ClientDeepHierarchyTests : public ClientSimpleHierarchyTests + { + public: + static const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + + void SetUp() override + { + ClientSimpleHierarchyTests::SetUp(); + + m_childOfChildEntity = AZStd::make_unique(AZ::EntityId(3), "child of child"); + m_childOfChildEntityInfo = AZStd::make_unique(*m_childOfChildEntity.get(), ChildOfChildNetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchyOnClient(*m_childOfChildEntityInfo); + + m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + } + + void TearDown() override + { + m_childOfChildEntityInfo.reset(); + StopAndDeleteEntity(m_childOfChildEntity); + + ClientSimpleHierarchyTests::TearDown(); + } + + void CreateDeepHierarchyOnClient(EntityInfo& childOfChild) + { + PopulateHierarchicalEntity(childOfChild); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childEntityInfo->m_netId); + SetHierarchyRootFieldOnNetworkHierarchyChild(childOfChild.m_entity, m_rootEntityInfo->m_netId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childOfChildHandle(&childOfChild.m_entity, m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + childOfChild.m_entity.Activate(); + } + + AZStd::unique_ptr m_childOfChildEntity; + AZStd::unique_ptr m_childOfChildEntityInfo; + }; + + TEST_F(ClientDeepHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + EXPECT_EQ( + m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchicalRoot(), + m_rootEntity.get() + ); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], + m_rootEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], + m_childEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChildEntity.get() + ); + } + } +} diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h new file mode 100644 index 0000000000..89d0d32428 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -0,0 +1,387 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + class HierarchyTests + : public AllocatorsFixture + { + public: + void SetUp() override + { + SetupAllocator(); + AZ::NameDictionary::Create(); + + m_mockComponentApplicationRequests = AZStd::make_unique>(); + AZ::Interface::Register(m_mockComponentApplicationRequests.get()); + + ON_CALL(*m_mockComponentApplicationRequests, AddEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::AddEntity)); + ON_CALL(*m_mockComponentApplicationRequests, FindEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::FindEntity)); + + // register components involved in testing + m_serializeContext = AZStd::make_unique(); + + m_transformDescriptor.reset(AzFramework::TransformComponent::CreateDescriptor()); + m_transformDescriptor->Reflect(m_serializeContext.get()); + + m_netBindDescriptor.reset(NetBindComponent::CreateDescriptor()); + m_netBindDescriptor->Reflect(m_serializeContext.get()); + + m_hierarchyRootDescriptor.reset(NetworkHierarchyRootComponent::CreateDescriptor()); + m_hierarchyRootDescriptor->Reflect(m_serializeContext.get()); + + m_hierarchyChildDescriptor.reset(NetworkHierarchyChildComponent::CreateDescriptor()); + m_hierarchyChildDescriptor->Reflect(m_serializeContext.get()); + + m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor()); + m_netTransformDescriptor->Reflect(m_serializeContext.get()); + + m_mockMultiplayer = AZStd::make_unique>(); + AZ::Interface::Register(m_mockMultiplayer.get()); + + EXPECT_NE(AZ::Interface::Get(), nullptr); + + // Create space for replication stats + // Without Multiplayer::RegisterMultiplayerComponents() the stats go to invalid id, which is fine for unit tests + GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0); + + m_mockNetworkEntityManager = AZStd::make_unique>(); + + ON_CALL(*m_mockNetworkEntityManager, AddEntityToEntityMap(_, _)).WillByDefault(Invoke(this, &HierarchyTests::AddEntityToEntityMap)); + ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::GetEntity)); + ON_CALL(*m_mockNetworkEntityManager, GetNetEntityIdById(_)).WillByDefault(Invoke(this, &HierarchyTests::GetNetEntityIdById)); + + m_mockTime = AZStd::make_unique>(); + AZ::Interface::Register(m_mockTime.get()); + + m_mockNetworkTime = AZStd::make_unique>(); + AZ::Interface::Register(m_mockNetworkTime.get()); + + ON_CALL(*m_mockMultiplayer, GetNetworkEntityManager()).WillByDefault(Return(m_mockNetworkEntityManager.get())); + EXPECT_NE(AZ::Interface::Get()->GetNetworkEntityManager(), nullptr); + + const IpAddress address("localhost", 1, ProtocolType::Udp); + m_mockConnection = AZStd::make_unique>(ConnectionId{ 1 }, address, ConnectionRole::Connector); + m_mockConnectionListener = AZStd::make_unique(); + + m_networkEntityTracker = AZStd::make_unique(); + ON_CALL(*m_mockNetworkEntityManager, GetNetworkEntityTracker()).WillByDefault(Return(m_networkEntityTracker.get())); + + m_networkEntityAuthorityTracker = AZStd::make_unique(*m_mockNetworkEntityManager); + ON_CALL(*m_mockNetworkEntityManager, GetNetworkEntityAuthorityTracker()).WillByDefault(Return(m_networkEntityAuthorityTracker.get())); + + m_entityReplicationManager = AZStd::make_unique(*m_mockConnection, *m_mockConnectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer); + + m_console.reset(aznew AZ::Console()); + AZ::Interface::Register(m_console.get()); + m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead()); + + m_multiplayerComponentRegistry = AZStd::make_unique(); + ON_CALL(*m_mockNetworkEntityManager, GetMultiplayerComponentRegistry()).WillByDefault(Return(m_multiplayerComponentRegistry.get())); + RegisterMultiplayerComponents(); + } + + void TearDown() override + { + m_multiplayerComponentRegistry.reset(); + + AZ::Interface::Unregister(m_console.get()); + m_console.reset(); + + m_networkEntityMap.clear(); + m_entities.clear(); + + m_entityReplicationManager.reset(); + + m_mockConnection.reset(); + m_mockConnectionListener.reset(); + m_networkEntityTracker.reset(); + m_networkEntityAuthorityTracker.reset(); + + AZ::Interface::Unregister(m_mockNetworkTime.get()); + AZ::Interface::Unregister(m_mockTime.get()); + AZ::Interface::Unregister(m_mockMultiplayer.get()); + AZ::Interface::Unregister(m_mockComponentApplicationRequests.get()); + + m_mockTime.reset(); + + m_mockNetworkEntityManager.reset(); + m_mockMultiplayer.reset(); + + m_transformDescriptor.reset(); + m_netTransformDescriptor.reset(); + m_hierarchyRootDescriptor.reset(); + m_hierarchyChildDescriptor.reset(); + m_netBindDescriptor.reset(); + m_serializeContext.reset(); + m_mockComponentApplicationRequests.reset(); + + AZ::NameDictionary::Destroy(); + TeardownAllocator(); + } + + AZStd::unique_ptr m_console; + + AZStd::unique_ptr> m_mockComponentApplicationRequests; + AZStd::unique_ptr m_serializeContext; + AZStd::unique_ptr m_transformDescriptor; + AZStd::unique_ptr m_netBindDescriptor; + AZStd::unique_ptr m_hierarchyRootDescriptor; + AZStd::unique_ptr m_hierarchyChildDescriptor; + AZStd::unique_ptr m_netTransformDescriptor; + + AZStd::unique_ptr> m_mockMultiplayer; + AZStd::unique_ptr m_mockNetworkEntityManager; + AZStd::unique_ptr> m_mockTime; + AZStd::unique_ptr> m_mockNetworkTime; + + AZStd::unique_ptr> m_mockConnection; + AZStd::unique_ptr m_mockConnectionListener; + AZStd::unique_ptr m_networkEntityTracker; + AZStd::unique_ptr m_networkEntityAuthorityTracker; + + AZStd::unique_ptr m_entityReplicationManager; + + AZStd::unique_ptr m_multiplayerComponentRegistry;; + + mutable AZStd::map m_networkEntityMap; + + NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) + { + m_networkEntityMap[netEntityId] = entity; + return NetworkEntityHandle(entity, netEntityId, m_networkEntityTracker.get()); + } + + ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const + { + AZ::Entity* entity = m_networkEntityMap[netEntityId]; + return ConstNetworkEntityHandle(entity, m_networkEntityTracker.get()); + } + + NetEntityId GetNetEntityIdById(const AZ::EntityId& entityId) const + { + for (const auto& pair : m_networkEntityMap) + { + if (pair.second->GetId() == entityId) + { + return pair.first; + } + } + + return InvalidNetEntityId; + } + + AZStd::map m_entities; + + bool AddEntity(AZ::Entity* entity) + { + m_entities[entity->GetId()] = entity; + return true; + } + + AZ::Entity* FindEntity(AZ::EntityId entityId) + { + const auto iterator = m_entities.find(entityId); + if (iterator != m_entities.end()) + { + return iterator->second; + } + + return nullptr; + } + + void SetupEntity(AZ::Entity& entity, NetEntityId netId, NetEntityRole role) + { + const auto netBindComponent = entity.FindComponent(); + EXPECT_NE(netBindComponent, nullptr); + netBindComponent->PreInit(&entity, PrefabEntityId{ AZ::Name("test"), 1 }, netId, role); + entity.Init(); + } + + void StopEntity(const AZ::Entity& entity) + { + const auto netBindComponent = entity.FindComponent(); + EXPECT_NE(netBindComponent, nullptr); + netBindComponent->StopEntity(); + } + + void StopAndDeleteEntity(AZStd::unique_ptr& entity) + { + if (entity) + { + StopEntity(*entity); + entity->Deactivate(); + entity.reset(); + } + } + + void CreateEntityWithRootHierarchy(AZ::Entity& rootEntity) + { + rootEntity.CreateComponent(); + rootEntity.CreateComponent(); + rootEntity.CreateComponent(); + rootEntity.CreateComponent(); + } + + void CreateEntityWithChildHierarchy(AZ::Entity& childEntity) + { + childEntity.CreateComponent(); + childEntity.CreateComponent(); + childEntity.CreateComponent(); + childEntity.CreateComponent(); + } + + void SetParentIdOnNetworkTransform(const AZ::Entity& entity, NetEntityId netParentId) + { + /* Derived from NetworkTransformComponent.AutoComponent.xml */ + constexpr int totalBits = 6 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int parentIdBit = 4 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::parentEntityId_DirtyFlag*/; + + ReplicationRecord currentRecord; + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(parentIdBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(netParentId), + "parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + // now the parent id is in the component + } + + template + void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZ::Entity& entity, NetEntityId value) + { + /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/; + + ReplicationRecord currentRecord; + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(value), + "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + // now the parent id is in the component + } + + struct EntityInfo + { + enum class Role + { + Root, + Child, + None + }; + + EntityInfo(AZ::Entity& entity, NetEntityId netId, Role role) + : m_entity(entity) + , m_netId(netId) + , m_role(role) + { + } + + AZ::Entity& m_entity; + NetEntityId m_netId; + AZStd::unique_ptr m_replicator; + Role m_role = Role::None; + }; + + void PopulateHierarchicalEntity(const EntityInfo& entityInfo) + { + entityInfo.m_entity.CreateComponent(); + entityInfo.m_entity.CreateComponent(); + entityInfo.m_entity.CreateComponent(); + switch (entityInfo.m_role) + { + case EntityInfo::Role::Root: + entityInfo.m_entity.CreateComponent(); + break; + case EntityInfo::Role::Child: + entityInfo.m_entity.CreateComponent(); + break; + case EntityInfo::Role::None: + break; + } + } + + void CreateDeepHierarchy(EntityInfo& root, EntityInfo& child, EntityInfo& childOfChild) + { + PopulateHierarchicalEntity(root); + PopulateHierarchicalEntity(child); + PopulateHierarchicalEntity(childOfChild); + + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Authority); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); + SetParentIdOnNetworkTransform(childOfChild.m_entity, child.m_netId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childOfChildHandle(&childOfChild.m_entity, m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(&root.m_entity, m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity.Activate(); + child.m_entity.Activate(); + childOfChild.m_entity.Activate(); + } + }; +} diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h new file mode 100644 index 0000000000..375fe2c5ba --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class MockMultiplayer : public Multiplayer::IMultiplayer + { + public: + MOCK_CONST_METHOD0(GetCurrentBlendFactor, float ()); + MOCK_CONST_METHOD0(GetAgentType, Multiplayer::MultiplayerAgentType()); + MOCK_METHOD1(InitializeMultiplayer, void(Multiplayer::MultiplayerAgentType)); + MOCK_METHOD2(StartHosting, bool(uint16_t, bool)); + MOCK_METHOD2(Connect, bool(AZStd::string, uint16_t)); + MOCK_METHOD1(Terminate, void(AzNetworking::DisconnectReason)); + MOCK_METHOD1(AddClientDisconnectedHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddConnectionAcquiredHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddSessionInitHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddSessionShutdownHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(SendReadyForEntityUpdates, void(bool)); + MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs()); + MOCK_METHOD0(GetNetworkTime, Multiplayer::INetworkTime* ()); + MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ()); + MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*)); + MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ()); + }; + + class MockNetworkEntityManager : public Multiplayer::INetworkEntityManager + { + public: + MOCK_METHOD4(CreateEntitiesImmediate, EntityList (const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate)); + MOCK_CONST_METHOD1(GetNetEntityIdById, Multiplayer::NetEntityId (const AZ::EntityId&)); + MOCK_METHOD0(GetNetworkEntityTracker, Multiplayer::NetworkEntityTracker* ()); + MOCK_METHOD0(GetNetworkEntityAuthorityTracker, Multiplayer::NetworkEntityAuthorityTracker* ()); + MOCK_METHOD0(GetMultiplayerComponentRegistry, Multiplayer::MultiplayerComponentRegistry* ()); + MOCK_CONST_METHOD0(GetHostId, Multiplayer::HostId()); + MOCK_METHOD3(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ:: + Transform&)); + MOCK_METHOD5(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityId, Multiplayer:: + NetEntityRole, Multiplayer::AutoActivate, const AZ::Transform&)); + MOCK_METHOD3(SetupNetEntity, void(AZ::Entity*, Multiplayer::PrefabEntityId, Multiplayer::NetEntityRole)); + MOCK_CONST_METHOD1(GetEntity, Multiplayer::ConstNetworkEntityHandle(Multiplayer::NetEntityId)); + MOCK_CONST_METHOD0(GetEntityCount, uint32_t()); + MOCK_METHOD2(AddEntityToEntityMap, Multiplayer::NetworkEntityHandle(Multiplayer::NetEntityId, AZ::Entity*)); + MOCK_METHOD1(MarkForRemoval, void(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_CONST_METHOD1(IsMarkedForRemoval, bool(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_METHOD1(ClearEntityFromRemovalList, void(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_METHOD0(ClearAllEntities, void()); + MOCK_METHOD1(AddEntityMarkedDirtyHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddEntityNotifyChangesHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddEntityExitDomainHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddControllersActivatedHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddControllersDeactivatedHandler, void(AZ::Event::Handler&)); + MOCK_METHOD0(NotifyEntitiesDirtied, void()); + MOCK_METHOD0(NotifyEntitiesChanged, void()); + MOCK_METHOD2(NotifyControllersActivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating)); + MOCK_METHOD2(NotifyControllersDeactivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating)); + MOCK_METHOD1(HandleLocalRpcMessage, void(Multiplayer::NetworkEntityRpcMessage&)); + }; + + class MockConnectionListener : public AzNetworking::IConnectionListener + { + public: + MOCK_METHOD3(ValidateConnect, ConnectResult(const IpAddress&, const IPacketHeader&, ISerializer&)); + MOCK_METHOD1(OnConnect, void(IConnection*)); + MOCK_METHOD3(OnPacketReceived, PacketDispatchResult (IConnection*, const IPacketHeader&, ISerializer&)); + MOCK_METHOD2(OnPacketLost, void(IConnection*, PacketId)); + MOCK_METHOD3(OnDisconnect, void(IConnection*, DisconnectReason, TerminationEndpoint)); + }; + + class MockTime : public AZ::ITime + { + public: + MOCK_CONST_METHOD0(GetElapsedTimeMs, AZ::TimeMs()); + }; + + class MockNetworkTime : public Multiplayer::INetworkTime + { + public: + MOCK_METHOD2(ForceSetTime, void (Multiplayer::HostFrameId, AZ::TimeMs)); + MOCK_CONST_METHOD0(GetHostBlendFactor, float ()); + MOCK_METHOD1(AlterBlendFactor, void (float)); + MOCK_CONST_METHOD0(IsTimeRewound, bool()); + MOCK_CONST_METHOD0(GetHostFrameId, Multiplayer::HostFrameId()); + MOCK_CONST_METHOD0(GetUnalteredHostFrameId, Multiplayer::HostFrameId()); + MOCK_METHOD0(IncrementHostFrameId, void()); + MOCK_CONST_METHOD0(GetHostTimeMs, AZ::TimeMs()); + MOCK_CONST_METHOD0(GetRewindingConnectionId, AzNetworking::ConnectionId()); + MOCK_CONST_METHOD1(GetHostFrameIdForRewindingConnection, Multiplayer::HostFrameId(AzNetworking::ConnectionId)); + MOCK_METHOD3(AlterTime, void(Multiplayer::HostFrameId, AZ::TimeMs, AzNetworking::ConnectionId)); + MOCK_METHOD1(SyncEntitiesToRewindState, void(const AZ::Aabb&)); + MOCK_METHOD0(ClearRewoundEntities, void()); + }; + + class MockComponentApplicationRequests : public AZ::ComponentApplicationRequests + { + public: + MOCK_METHOD1(RegisterComponentDescriptor, void(const AZ::ComponentDescriptor*)); + MOCK_METHOD1(UnregisterComponentDescriptor, void(const AZ::ComponentDescriptor*)); + MOCK_METHOD0(GetApplication, AZ::ComponentApplication* ()); + MOCK_METHOD1(RegisterEntityAddedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityRemovedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityActivatedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityDeactivatedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(SignalEntityActivated, void(AZ::Entity*)); + MOCK_METHOD1(SignalEntityDeactivated, void(AZ::Entity*)); + MOCK_METHOD1(AddEntity, bool(AZ::Entity*)); + MOCK_METHOD1(RemoveEntity, bool(AZ::Entity*)); + MOCK_METHOD1(DeleteEntity, bool(const AZ::EntityId&)); + MOCK_METHOD1(FindEntity, AZ::Entity* (const AZ::EntityId&)); + MOCK_METHOD1(GetEntityName, AZStd::string(const AZ::EntityId&)); + MOCK_METHOD1(EnumerateEntities, void(const EntityCallback&)); + MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); + MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); + MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); + MOCK_CONST_METHOD0(GetAppRoot, const char* ()); + MOCK_CONST_METHOD0(GetEngineRoot, const char* ()); + MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); + MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ()); + MOCK_METHOD1(ResolveModulePath, void(AZ::OSString&)); + MOCK_METHOD0(GetAzCommandLine, AZ::CommandLine* ()); + MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&)); + }; + + class MockSerializer : public ISerializer + { + public: + MOCK_CONST_METHOD0(IsValid, bool ()); + MOCK_CONST_METHOD0(GetSerializerMode, SerializerMode ()); + MOCK_METHOD2(Serialize, bool (bool&, const char*)); + MOCK_METHOD4(Serialize, bool (char&, const char*, char, char)); + MOCK_METHOD4(Serialize, bool (int8_t&, const char*, int8_t, int8_t)); + MOCK_METHOD4(Serialize, bool (int16_t&, const char*, int16_t, int16_t)); + MOCK_METHOD4(Serialize, bool (int32_t&, const char*, int32_t, int32_t)); + MOCK_METHOD4(Serialize, bool (int64_t&, const char*, int64_t, int64_t)); + MOCK_METHOD4(Serialize, bool (uint8_t&, const char*, uint8_t, uint8_t)); + MOCK_METHOD4(Serialize, bool (uint16_t&, const char*, uint16_t, uint16_t)); + MOCK_METHOD4(Serialize, bool (uint32_t&, const char*, uint32_t, uint32_t)); + MOCK_METHOD4(Serialize, bool (uint64_t&, const char*, uint64_t, uint64_t)); + MOCK_METHOD4(Serialize, bool (float&, const char*, float, float)); + MOCK_METHOD4(Serialize, bool (double&, const char*, double, double)); + MOCK_METHOD5(SerializeBytes, bool (uint8_t*, uint32_t, bool, uint32_t&, const char*)); + MOCK_METHOD2(BeginObject, bool (const char*, const char*)); + MOCK_METHOD2(EndObject, bool (const char*, const char*)); + MOCK_CONST_METHOD0(GetBuffer, const uint8_t* ()); + MOCK_CONST_METHOD0(GetCapacity, uint32_t ()); + MOCK_CONST_METHOD0(GetSize, uint32_t ()); + MOCK_METHOD0(ClearTrackedChangesFlag, void ()); + MOCK_CONST_METHOD0(GetTrackedChangesFlag, bool ()); + }; +} + diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp new file mode 100644 index 0000000000..ae2d8b162d --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -0,0 +1,1145 @@ +/* + * 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 + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + /* + * Parent -> Child + */ + class ServerSimpleHierarchyTests : public HierarchyTests + { + public: + void SetUp() override + { + HierarchyTests::SetUp(); + + m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); + m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); + + m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), NetEntityId{ 1 }, EntityInfo::Role::Root); + m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), NetEntityId{ 2 }, EntityInfo::Role::Child); + + CreateSimpleHierarchy(*m_rootEntityInfo, *m_childEntityInfo); + + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + // now the two entities are under one hierarchy + } + + void TearDown() override + { + m_childEntityInfo.reset(); + m_rootEntityInfo.reset(); + + StopAndDeleteEntity(m_childEntity); + StopAndDeleteEntity(m_rootEntity); + + HierarchyTests::TearDown(); + } + + void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child) + { + PopulateHierarchicalEntity(root); + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + + PopulateHierarchicalEntity(child); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + + //// we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + //SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(&root.m_entity, m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity.Activate(); + child.m_entity.Activate(); + } + + AZStd::unique_ptr m_rootEntity; + AZStd::unique_ptr m_childEntity; + + AZStd::unique_ptr m_rootEntityInfo; + AZStd::unique_ptr m_childEntityInfo; + }; + + TEST_F(ServerSimpleHierarchyTests, Server_Sets_Appropriate_Network_Fields_For_Clients) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + NetEntityId{ 1 } + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Is_Top_Level_Root) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->IsHierarchicalChild(), + false + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Set) + { + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + NetEntityId{ 1 } + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Cleared_On_Detach) + { + // now detach the child + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Has_Child_Reference) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Has_Child_References_Removed_On_Detach) + { + // now detach the child + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Deactivates_Child_Has_No_References_To_Root) + { + StopEntity(*m_rootEntity); + m_rootEntity->Deactivate(); + m_rootEntity.reset(); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Deactivates_Root_Has_No_References_To_Child) + { + StopEntity(*m_childEntity); + m_childEntity->Deactivate(); + m_childEntity.reset(); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + /* + * Parent -> Child -> ChildOfChild + */ + class ServerDeepHierarchyTests : public HierarchyTests + { + public: + static const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + static const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + static const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); + m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); + m_childOfChildEntity = AZStd::make_unique(AZ::EntityId(3), "child of child"); + + m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), RootNetEntityId, EntityInfo::Role::Root); + m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), ChildNetEntityId, EntityInfo::Role::Child); + m_childOfChildEntityInfo = AZStd::make_unique(*m_childOfChildEntity.get(), ChildOfChildNetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_rootEntityInfo, *m_childEntityInfo, *m_childOfChildEntityInfo); + + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChildEntityInfo.reset(); + m_childEntityInfo.reset(); + m_rootEntityInfo.reset(); + + StopAndDeleteEntity(m_childOfChildEntity); + StopAndDeleteEntity(m_childEntity); + StopAndDeleteEntity(m_rootEntity); + + HierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_rootEntity; + AZStd::unique_ptr m_childEntity; + AZStd::unique_ptr m_childOfChildEntity; + + AZStd::unique_ptr m_rootEntityInfo; + AZStd::unique_ptr m_childEntityInfo; + AZStd::unique_ptr m_childOfChildEntityInfo; + }; + + TEST_F(ServerDeepHierarchyTests, Root_Is_Top_Level_Root) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->IsHierarchicalChild(), + false + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_Child_References) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], + m_rootEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], + m_childEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChildEntity.get() + ); + } + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_Child_Of_Child_Reference_Removed_On_Detach) + { + m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_Removed_On_Detach_Of_Mid_Child) + { + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_If_Mid_Child_Added_With_Child) + { + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_If_Child_Of_Child_Added) + { + m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Points_To_Root_After_Attach) + { + m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + + EXPECT_EQ( + m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, All_New_Children_Point_To_Root_If_Mid_Child_Added_With_Child) + { + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + EXPECT_EQ( + m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Children_Clear_Reference_To_Root_After_Mid_Child_Detached) + { + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Clears_Reference_To_Root_After_Detached) + { + m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Deactivates_Children_Have_No_References_To_Root) + { + StopAndDeleteEntity(m_rootEntity); + + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Deactivates_Root_Removes_References_To_It) + { + StopAndDeleteEntity(m_childOfChildEntity); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerDeepHierarchyTests, Testing_Limiting_Hierarchy_Maximum_Size) + { + uint32_t currentMaxLimit = 0; + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); + + // remake the hierarchy + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_console->PerformCommand((AZStd::string("bg_hierarchyEntityMaxLimit ") + AZStd::to_string(currentMaxLimit)).c_str()); + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + } + + /* + * Parent -> Child -> Child Of Child + * -> Child2 -> Child Of Child2 + * -> Child2 Of Child2 + */ + class ServerBranchedHierarchyTests : public HierarchyTests + { + public: + static const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + static const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + static const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + static const NetEntityId Child2NetEntityId = NetEntityId{ 4 }; + static const NetEntityId ChildOfChild2NetEntityId = NetEntityId{ 5 }; + static const NetEntityId Child2OfChild2NetEntityId = NetEntityId{ 6 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); + m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); + m_childOfChildEntity = AZStd::make_unique(AZ::EntityId(3), "child of child"); + m_child2Entity = AZStd::make_unique(AZ::EntityId(4), "child2"); + m_childOfChild2Entity = AZStd::make_unique(AZ::EntityId(5), "child of child2"); + m_child2OfChild2Entity = AZStd::make_unique(AZ::EntityId(6), "child2 of child2"); + + m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), RootNetEntityId, EntityInfo::Role::Root); + m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), ChildNetEntityId, EntityInfo::Role::Child); + m_childOfChildEntityInfo = AZStd::make_unique(*m_childOfChildEntity.get(), ChildOfChildNetEntityId, EntityInfo::Role::Child); + m_child2EntityInfo = AZStd::make_unique(*m_child2Entity.get(), Child2NetEntityId, EntityInfo::Role::Child); + m_childOfChild2EntityInfo = AZStd::make_unique(*m_childOfChild2Entity.get(), ChildOfChild2NetEntityId, EntityInfo::Role::Child); + m_child2OfChild2EntityInfo = AZStd::make_unique(*m_child2OfChild2Entity.get(), Child2OfChild2NetEntityId, EntityInfo::Role::Child); + + CreateBranchedHierarchy(*m_rootEntityInfo, *m_childEntityInfo, *m_childOfChildEntityInfo, + *m_child2EntityInfo, *m_childOfChild2EntityInfo, *m_child2OfChild2EntityInfo); + + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_child2OfChild2EntityInfo.reset(); + m_childOfChild2EntityInfo.reset(); + m_child2EntityInfo.reset(); + m_childOfChildEntityInfo.reset(); + m_childEntityInfo.reset(); + m_rootEntityInfo.reset(); + + StopAndDeleteEntity(m_child2OfChild2Entity); + StopAndDeleteEntity(m_childOfChild2Entity); + StopAndDeleteEntity(m_child2Entity); + StopAndDeleteEntity(m_childOfChildEntity); + StopAndDeleteEntity(m_childEntity); + StopAndDeleteEntity(m_rootEntity); + + HierarchyTests::TearDown(); + } + + + void CreateBranchedHierarchy(EntityInfo& root, EntityInfo& child, EntityInfo& childOfChild, + EntityInfo& child2, EntityInfo& childOfChild2, EntityInfo& child2OfChild2) + { + PopulateHierarchicalEntity(root); + PopulateHierarchicalEntity(child); + PopulateHierarchicalEntity(childOfChild); + PopulateHierarchicalEntity(child2); + PopulateHierarchicalEntity(childOfChild2); + PopulateHierarchicalEntity(child2OfChild2); + + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Authority); + SetupEntity(child2.m_entity, child2.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild2.m_entity, childOfChild2.m_netId, NetEntityRole::Authority); + SetupEntity(child2OfChild2.m_entity, child2OfChild2.m_netId, NetEntityRole::Authority); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); + SetParentIdOnNetworkTransform(childOfChild.m_entity, child.m_netId); + SetParentIdOnNetworkTransform(child2.m_entity, root.m_netId); + SetParentIdOnNetworkTransform(childOfChild2.m_entity, child2.m_netId); + SetParentIdOnNetworkTransform(child2OfChild2.m_entity, child2.m_netId); + + // Create entity replicators + const NetworkEntityHandle childOfChild2Handle(&childOfChild2.m_entity, m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChild2Handle); + childOfChild.m_replicator->Initialize(childOfChild2Handle); + + const NetworkEntityHandle child2OfChild2Handle(&child2OfChild2.m_entity, m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, child2OfChild2Handle); + childOfChild.m_replicator->Initialize(child2OfChild2Handle); + + const NetworkEntityHandle child2Handle(&child2.m_entity, m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, child2Handle); + child.m_replicator->Initialize(child2Handle); + + const NetworkEntityHandle childOfChildHandle(&childOfChild.m_entity, m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + const NetworkEntityHandle rootHandle(&root.m_entity, m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity.Activate(); + child.m_entity.Activate(); + childOfChild.m_entity.Activate(); + child2.m_entity.Activate(); + childOfChild2.m_entity.Activate(); + child2OfChild2.m_entity.Activate(); + } + + AZStd::unique_ptr m_rootEntity; + AZStd::unique_ptr m_childEntity; + AZStd::unique_ptr m_childOfChildEntity; + AZStd::unique_ptr m_child2Entity; + AZStd::unique_ptr m_childOfChild2Entity; + AZStd::unique_ptr m_child2OfChild2Entity; + + AZStd::unique_ptr m_rootEntityInfo; + AZStd::unique_ptr m_childEntityInfo; + AZStd::unique_ptr m_childOfChildEntityInfo; + AZStd::unique_ptr m_child2EntityInfo; + AZStd::unique_ptr m_childOfChild2EntityInfo; + AZStd::unique_ptr m_child2OfChild2EntityInfo; + }; + + TEST_F(ServerBranchedHierarchyTests, Sanity_Check) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + + if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 6) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], + m_rootEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], + m_childEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChildEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[3], + m_child2Entity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[4], + m_child2OfChild2Entity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[5], + m_childOfChild2Entity.get() + ); + } + } + + TEST_F(ServerBranchedHierarchyTests, Detach_Child_While_Child2_Remains_Attached) + { + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + + if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 4) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], + m_rootEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], + m_child2Entity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], + m_child2OfChild2Entity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[3], + m_childOfChild2Entity.get() + ); + } + + EXPECT_EQ( + m_child2Entity->FindComponent()->GetHierarchicalRoot(), + m_rootEntity.get() + ); + EXPECT_EQ( + m_childEntity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + EXPECT_EQ( + m_childOfChildEntity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + } + + TEST_F(ServerBranchedHierarchyTests, Detach_Child_Then_Attach_To_Child2) + { + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childEntity->FindComponent()->SetParent(m_child2Entity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + /* + * Sets up 2 deep hierarchies. + */ + class ServerHierarchyOfHierarchyTests : public ServerDeepHierarchyTests + { + public: + static const NetEntityId Root2NetEntityId = NetEntityId{ 4 }; + static const NetEntityId Child2NetEntityId = NetEntityId{ 5 }; + static const NetEntityId ChildOfChild2NetEntityId = NetEntityId{ 6 }; + + void SetUp() override + { + ServerDeepHierarchyTests::SetUp(); + + m_rootEntity2 = AZStd::make_unique(AZ::EntityId(4), "root 2"); + m_childEntity2 = AZStd::make_unique(AZ::EntityId(5), "child 2"); + m_childOfChildEntity2 = AZStd::make_unique(AZ::EntityId(6), "child of child 2"); + + m_rootEntityInfo2 = AZStd::make_unique(*m_rootEntity2.get(), Root2NetEntityId, EntityInfo::Role::Root); + m_childEntityInfo2 = AZStd::make_unique(*m_childEntity2.get(), Child2NetEntityId, EntityInfo::Role::Child); + m_childOfChildEntityInfo2 = AZStd::make_unique(*m_childOfChildEntity2.get(), ChildOfChild2NetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_rootEntityInfo2, *m_childEntityInfo2, *m_childOfChildEntityInfo2); + + m_childEntity2->FindComponent()->SetParent(m_rootEntity2->GetId()); + m_childOfChildEntity2->FindComponent()->SetParent(m_childEntity2->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChildEntityInfo2.reset(); + m_childEntityInfo2.reset(); + m_rootEntityInfo2.reset(); + + StopAndDeleteEntity(m_childOfChildEntity2); + StopAndDeleteEntity(m_childEntity2); + StopAndDeleteEntity(m_rootEntity2); + + ServerDeepHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_rootEntity2; + AZStd::unique_ptr m_childEntity2; + AZStd::unique_ptr m_childOfChildEntity2; + + AZStd::unique_ptr m_rootEntityInfo2; + AZStd::unique_ptr m_childEntityInfo2; + AZStd::unique_ptr m_childOfChildEntityInfo2; + }; + + TEST_F(ServerHierarchyOfHierarchyTests, Hierarchies_Are_Not_Related) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], + m_rootEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], + m_childEntity.get() + ); + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChildEntity.get() + ); + } + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_rootEntity2->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities()[0], + m_rootEntity2.get() + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities()[1], + m_childEntity2.get() + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChildEntity2.get() + ); + } + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Is_Not_Top_Level_Root) + { + m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->IsHierarchicalChild(), + false + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->IsHierarchicalChild(), + true + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Root) + { + m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Child) + { + m_rootEntity2->FindComponent()->SetParent(m_childEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Child_Of_Child) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Root) + { + m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Child) + { + m_rootEntity2->FindComponent()->SetParent(m_childEntity->GetId()); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Child_Of_Child) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Doesnt_Keep_Child_References) + { + m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Top_Root) + { + m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + // detach + m_rootEntity2->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + if (m_rootEntity2->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities()[0], + m_rootEntity2.get() + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities()[1], + m_childEntity2.get() + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChildEntity2.get() + ); + } + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + // detach + m_rootEntity2->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Stress_Test_Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) + { + for (int i = 0; i < 100; ++i) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + // detach + m_rootEntity2->FindComponent()->SetParent(AZ::EntityId()); + } + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Detachment_Of_Child_Of_Child_In_Inner_Hierarchy) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + // detach + m_childOfChildEntity2->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 5 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Attachment_Of_Child_Of_Child_In_Inner_Hierarchy) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + // detach + m_childOfChildEntity2->FindComponent()->SetParent(AZ::EntityId()); + // re-connect + m_childOfChildEntity2->FindComponent()->SetParent(m_childEntity2->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Child_Of_Child_Changed_Hierarchies) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + // detach + m_childOfChildEntity2->FindComponent()->SetParent(AZ::EntityId()); + + // connect to a different hierarchy + m_childOfChildEntity2->FindComponent()->SetParent(m_childEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Detachment_Of_Child_In_Inner_Hierarchy) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + // detach + m_childEntity2->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Child_Changed_Hierarchies) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + // detach + m_childEntity2->FindComponent()->SetParent(AZ::EntityId()); + + // connect to a different hierarchy + m_childEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_No_Child_References_After_All_Children_Moved_To_Another_Hierarchy) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + + m_childEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + + // detach + m_rootEntity2->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Child_Deactivated_Top_Root_Has_No_Child_Reference_To_It) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + + StopAndDeleteEntity(m_childEntity2); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Testing_Limiting_Hierarchy_Maximum_Size) + { + uint32_t currentMaxLimit = 0; + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); + + // remake the top level hierarchy + m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_console->PerformCommand((AZStd::string("bg_hierarchyEntityMaxLimit ") + AZStd::to_string(currentMaxLimit)).c_str()); + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + } + + /* + * Parent -> Child -> ChildOfChild (not marked as in a hierarchy) + */ + class ServerMixedDeepHierarchyTests : public HierarchyTests + { + public: + void SetUp() override + { + HierarchyTests::SetUp(); + + m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); + m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); + m_childOfChildEntity = AZStd::make_unique(AZ::EntityId(3), "child of child"); + + m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), NetEntityId{ 1 }, EntityInfo::Role::Root); + m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), NetEntityId{ 2 }, EntityInfo::Role::Child); + m_childOfChildEntityInfo = AZStd::make_unique(*m_childOfChildEntity.get(), NetEntityId{ 3 }, EntityInfo::Role::None); + + CreateDeepHierarchy(*m_rootEntityInfo, *m_childEntityInfo, *m_childOfChildEntityInfo); + + m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChildEntityInfo.reset(); + m_childEntityInfo.reset(); + m_rootEntityInfo.reset(); + + StopAndDeleteEntity(m_childOfChildEntity); + StopAndDeleteEntity(m_childEntity); + StopAndDeleteEntity(m_rootEntity); + + HierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_rootEntity; + AZStd::unique_ptr m_childEntity; + AZStd::unique_ptr m_childOfChildEntity; + + AZStd::unique_ptr m_rootEntityInfo; + AZStd::unique_ptr m_childEntityInfo; + AZStd::unique_ptr m_childOfChildEntityInfo; + }; + + TEST_F(ServerMixedDeepHierarchyTests, Top_Root_Ignores_Non_Hierarchical_Entities) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerMixedDeepHierarchyTests, Detaching_Non_Hierarchical_Entity_Has_No_Effect_On_Top_Root) + { + m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerMixedDeepHierarchyTests, Attaching_Non_Hierarchical_Entity_Has_No_Effect_On_Top_Root) + { + m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChildEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + /* + * 1st hierarchy: Parent -> Child -> ChildOfChild (not marked as in a hierarchy) + * 2nd hierarchy: Parent2 -> Child2 (not marked as in a hierarchy) -> ChildOfChild2 + */ + class ServerMixedHierarchyOfHierarchyTests : public ServerMixedDeepHierarchyTests + { + public: + void SetUp() override + { + ServerMixedDeepHierarchyTests::SetUp(); + + m_rootEntity2 = AZStd::make_unique(AZ::EntityId(4), "root 2"); + m_childEntity2 = AZStd::make_unique(AZ::EntityId(5), "child 2"); + m_childOfChildEntity2 = AZStd::make_unique(AZ::EntityId(6), "child of child 2"); + + m_rootEntityInfo2 = AZStd::make_unique(*m_rootEntity2.get(), NetEntityId{ 4 }, EntityInfo::Role::Root); + m_childEntityInfo2 = AZStd::make_unique(*m_childEntity2.get(), NetEntityId{ 5 }, EntityInfo::Role::None); + m_childOfChildEntityInfo2 = AZStd::make_unique(*m_childOfChildEntity2.get(), NetEntityId{ 6 }, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_rootEntityInfo2, *m_childEntityInfo2, *m_childOfChildEntityInfo2); + + m_childEntity2->FindComponent()->SetParent(m_rootEntity2->GetId()); + m_childOfChildEntity2->FindComponent()->SetParent(m_childEntity2->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChildEntityInfo2.reset(); + m_childEntityInfo2.reset(); + m_rootEntityInfo2.reset(); + + + StopAndDeleteEntity(m_childOfChildEntity2); + StopAndDeleteEntity(m_childEntity2); + StopAndDeleteEntity(m_rootEntity2); + + ServerMixedDeepHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_rootEntity2; + AZStd::unique_ptr m_childEntity2; + AZStd::unique_ptr m_childOfChildEntity2; + + AZStd::unique_ptr m_rootEntityInfo2; + AZStd::unique_ptr m_childEntityInfo2; + AZStd::unique_ptr m_childOfChildEntityInfo2; + }; + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Sanity_Check_Ingore_Children_Without_Hierarchy_Components) + { + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Adding_Mixed_Hierarchy_Ingores_Children_Without_Hierarchy_Components) + { + m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + + EXPECT_EQ( + m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Attaching_Hierarchy_To_Non_Hierarchical_Entity_Does_Not_Merge_Hierarchies) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->IsHierarchicalChild(), + false + ); + } +} diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index 0b2adb1530..6475155f40 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -15,20 +15,28 @@ set(FILES Include/Multiplayer/MultiplayerTypes.h Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h Include/Multiplayer/Components/MultiplayerComponent.h - Include/Multiplayer/Components/MultiplayerController.h Include/Multiplayer/Components/MultiplayerComponentRegistry.h + Include/Multiplayer/Components/MultiplayerController.h Include/Multiplayer/Components/NetBindComponent.h + Include/Multiplayer/Components/NetworkHierarchyChildComponent.h + Include/Multiplayer/Components/NetworkHierarchyRootComponent.h + Include/Multiplayer/Components/NetworkHierarchyBus.h Include/Multiplayer/Components/NetworkTransformComponent.h Include/Multiplayer/ConnectionData/IConnectionData.h Include/Multiplayer/EntityDomains/IEntityDomain.h - Include/Multiplayer/NetworkEntity/INetworkEntityManager.h + Include/Multiplayer/IMultiplayer.h + Include/Multiplayer/IMultiplayerTools.h Include/Multiplayer/INetworkSpawnableLibrary.h + Include/Multiplayer/MultiplayerConstants.h + Include/Multiplayer/MultiplayerStats.h + Include/Multiplayer/MultiplayerTypes.h + Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h Include/Multiplayer/NetworkEntity/IFilterEntityManager.h - Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h - Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h + Include/Multiplayer/NetworkEntity/INetworkEntityManager.h Include/Multiplayer/NetworkEntity/NetworkEntityHandle.h Include/Multiplayer/NetworkEntity/NetworkEntityHandle.inl - Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h + Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h + Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h Include/Multiplayer/NetworkInput/NetworkInput.h Include/Multiplayer/NetworkTime/INetworkTime.h @@ -40,23 +48,24 @@ set(FILES Include/Multiplayer/NetworkTime/RewindableObject.inl Include/Multiplayer/Physics/PhysicsUtils.h Include/Multiplayer/ReplicationWindows/IReplicationWindow.h - Source/MultiplayerSystemComponent.cpp - Source/MultiplayerSystemComponent.h - Source/MultiplayerStats.cpp - Source/AutoGen/AutoComponent_Header.jinja - Source/AutoGen/AutoComponent_Source.jinja - Source/AutoGen/AutoComponent_Common.jinja Source/AutoGen/AutoComponentTypes_Header.jinja Source/AutoGen/AutoComponentTypes_Source.jinja + Source/AutoGen/AutoComponent_Common.jinja + Source/AutoGen/AutoComponent_Header.jinja + Source/AutoGen/AutoComponent_Source.jinja Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml Source/AutoGen/Multiplayer.AutoPackets.xml Source/AutoGen/MultiplayerEditor.AutoPackets.xml Source/AutoGen/NetworkTransformComponent.AutoComponent.xml + Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml + Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml Source/Components/LocalPredictionPlayerInputComponent.cpp Source/Components/MultiplayerComponent.cpp - Source/Components/MultiplayerController.cpp Source/Components/MultiplayerComponentRegistry.cpp + Source/Components/MultiplayerController.cpp Source/Components/NetBindComponent.cpp + Source/Components/NetworkHierarchyChildComponent.cpp + Source/Components/NetworkHierarchyRootComponent.cpp Source/Components/NetworkTransformComponent.cpp Source/ConnectionData/ClientToServerConnectionData.cpp Source/ConnectionData/ClientToServerConnectionData.h @@ -68,6 +77,9 @@ set(FILES Source/Editor/MultiplayerEditorConnection.h Source/EntityDomains/FullOwnershipEntityDomain.cpp Source/EntityDomains/FullOwnershipEntityDomain.h + Source/MultiplayerStats.cpp + Source/MultiplayerSystemComponent.cpp + Source/MultiplayerSystemComponent.h Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp Source/NetworkEntity/EntityReplication/EntityReplicationManager.h Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -83,13 +95,13 @@ set(FILES Source/NetworkEntity/NetworkEntityHandle.cpp Source/NetworkEntity/NetworkEntityManager.cpp Source/NetworkEntity/NetworkEntityManager.h - Source/NetworkEntity/NetworkSpawnableLibrary.cpp - Source/NetworkEntity/NetworkSpawnableLibrary.h Source/NetworkEntity/NetworkEntityRpcMessage.cpp Source/NetworkEntity/NetworkEntityTracker.cpp Source/NetworkEntity/NetworkEntityTracker.h Source/NetworkEntity/NetworkEntityTracker.inl Source/NetworkEntity/NetworkEntityUpdateMessage.cpp + Source/NetworkEntity/NetworkSpawnableLibrary.cpp + Source/NetworkEntity/NetworkSpawnableLibrary.h Source/NetworkInput/NetworkInput.cpp Source/NetworkInput/NetworkInputArray.cpp Source/NetworkInput/NetworkInputArray.h @@ -101,11 +113,11 @@ set(FILES Source/NetworkInput/NetworkInputMigrationVector.h Source/NetworkTime/NetworkTime.cpp Source/NetworkTime/NetworkTime.h + Source/Physics/PhysicsUtils.cpp Source/Pipeline/NetBindMarkerComponent.cpp Source/Pipeline/NetBindMarkerComponent.h Source/Pipeline/NetworkSpawnableHolderComponent.cpp Source/Pipeline/NetworkSpawnableHolderComponent.h - Source/Physics/PhysicsUtils.cpp Source/ReplicationWindows/NullReplicationWindow.cpp Source/ReplicationWindows/NullReplicationWindow.h Source/ReplicationWindows/ServerToClientReplicationWindow.cpp diff --git a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake index f385a21600..3f4fcc9efa 100644 --- a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake @@ -8,6 +8,10 @@ set(FILES Tests/Main.cpp + Tests/MockInterfaces.h + Tests/ClientHierarchyTests.cpp + Tests/ServerHierarchyTests.cpp + Tests/CommonHierarchySetup.h Tests/IMultiplayerConnectionMock.h Tests/MultiplayerSystemTests.cpp Tests/RewindableContainerTests.cpp From 2a10c400a7ac5b94c7eb86ce542e489f1b8a2609 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:28:52 -0400 Subject: [PATCH 08/63] Comments cleanup Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Multiplayer/Components/NetworkHierarchyRootComponent.h | 2 +- .../Code/Source/Components/NetworkHierarchyChildComponent.cpp | 2 +- .../Code/Source/Components/NetworkHierarchyRootComponent.cpp | 2 +- Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp | 4 ++-- Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp | 3 --- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 48c003d9e7..6192d41ad6 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -18,7 +18,7 @@ namespace Multiplayer //! @class NetworkHierarchyRootComponent //! @brief Component that declares the top level entity of a network hierarchy. /* - * Call @GetHierarchyChildren to get the list of hierarchical entities. + * Call @GetHierarchicalEntities to get the list of hierarchical entities. * A network hierarchy is meant to be a small group of entities. You can control the maximum supported size of * a network hierarchy by modifying CVar @bg_hierarchyEntityMaxLimit. * diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index c644b832a0..c04fc2cf3b 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -26,7 +26,7 @@ namespace Multiplayer if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Network Hierarchy Child", "declares a network dependency on the root of this hierarchy") + "Network Hierarchy Child", "Declares a network dependency on the root of this hierarchy.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 2fa960ed2a..d20aa8d898 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -32,7 +32,7 @@ namespace Multiplayer if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Network Hierarchy Root", "marks the entity as the root of an entity hierarchy") + "Network Hierarchy Root", "Marks the entity as the root of an entity hierarchy.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index d1b01a279c..23de4d2f04 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -287,8 +287,8 @@ namespace Multiplayer ); } - /* - * Parent -> Child + /* + * Parent -> Child -> ChildOfChild */ class ClientDeepHierarchyTests : public ClientSimpleHierarchyTests { diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp index ae2d8b162d..d119ade937 100644 --- a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -66,9 +66,6 @@ namespace Multiplayer PopulateHierarchicalEntity(child); SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); - //// we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) - //SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); - // Create an entity replicator for the child entity const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); From 853cffbf7bcfde4ebdb9cab4fd4bc1cd2fa6f2c1 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:37:05 -0400 Subject: [PATCH 09/63] Comment cleanup Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Code/Include/Multiplayer/Components/NetworkHierarchyBus.h | 2 +- .../Multiplayer/Components/NetworkHierarchyChildComponent.h | 3 ++- .../Code/Source/Components/NetworkHierarchyChildComponent.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h index 72dff65401..d644d6eb5a 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h @@ -20,7 +20,7 @@ namespace Multiplayer virtual void OnNetworkHierarchyUpdated([[maybe_unused]] const AZ::EntityId& rootEntityId) {} //! Called when an entity has left a hierarchy - virtual void OnLeavingNetworkHierarchy() {} + virtual void OnNetworkHierarchyLeave() {} }; typedef AZ::EBus NetworkHierarchyNotificationBus; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h index 27f794ec21..cfc3803d28 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -39,13 +39,14 @@ namespace Multiplayer NetworkHierarchyChildComponent(); + //! NetworkHierarchyChildComponentBase overrides. //! @{ void OnInit() override; void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; //! @} - //! NetworkHierarchyRequestBus overrides + //! NetworkHierarchyRequestBus overrides. //! @{ bool IsHierarchicalChild() const override; bool IsHierarchicalRoot() const override { return false; } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index c04fc2cf3b..cbdf214dfc 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -114,7 +114,7 @@ namespace Multiplayer else { controller->SetHierarchyRoot(InvalidNetEntityId); - NetworkHierarchyNotificationBus::Event(GetEntityId(), &NetworkHierarchyNotificationBus::Events::OnLeavingNetworkHierarchy); + NetworkHierarchyNotificationBus::Event(GetEntityId(), &NetworkHierarchyNotificationBus::Events::OnNetworkHierarchyLeave); } } } From 05e464b31bd8b99d7757874ef1e01c70089adeae Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:45:22 -0400 Subject: [PATCH 10/63] Improving comments Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Components/NetworkHierarchyRootComponent.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 6192d41ad6..4be8b24d8a 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -67,10 +67,19 @@ namespace Multiplayer AZStd::vector m_hierarchicalEntities; void RebuildHierarchy(); - - //! @returns false if the maximum supported hierarchy size has been reached + + //! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy + //! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method, + //! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities. + //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. + //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise bool RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount); - //! @returns false if the maximum supported hierarchy size has been reached + + //! @param entity Add the child entity and any of its relevant children to the hierarchy + //! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method, + //! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities. + //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. + //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount); }; } From 0d8b0fe496a3b62bba714acc32add2befcee3321 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:51:39 -0400 Subject: [PATCH 11/63] Fixes for clang builds Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Code/Source/Components/NetworkHierarchyRootComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index d20aa8d898..5f546298cb 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -175,7 +175,7 @@ namespace Multiplayer AZStd::vector allChildren; AZ::TransformBus::EventResult(allChildren, underEntity, &AZ::TransformBus::Events::GetChildren); - for (const AZ::EntityId newChildId : allChildren) + for (const AZ::EntityId& newChildId : allChildren) { if (!RecursiveAttachHierarchicalChild(newChildId, currentEntityCount)) { From 088511577b9a4ae6169d72411c36992a4a572cc0 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Mon, 13 Sep 2021 20:33:32 -0400 Subject: [PATCH 12/63] Fix for clan compiler Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp | 4 ++-- Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index 23de4d2f04..29d4f7f809 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -148,8 +148,8 @@ namespace Multiplayer class ClientSimpleHierarchyTests : public HierarchyTests { public: - static const NetEntityId RootNetEntityId = NetEntityId{ 1 }; - static const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; void SetUp() override { diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp index d119ade937..708a39d439 100644 --- a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -176,9 +176,9 @@ namespace Multiplayer class ServerDeepHierarchyTests : public HierarchyTests { public: - static const NetEntityId RootNetEntityId = NetEntityId{ 1 }; - static const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; - static const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; void SetUp() override { From fcd5b3f184af27c4514a4f6eab2c6d912ae1d1e9 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Tue, 14 Sep 2021 11:50:51 -0400 Subject: [PATCH 13/63] Converted to use AZ::Events for hiearchy notifications. Added unittests. Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Components/NetworkHierarchyBus.h | 22 +++++++------- .../NetworkHierarchyChildComponent.h | 5 ++++ .../NetworkHierarchyRootComponent.h | 5 ++++ .../NetworkHierarchyChildComponent.cpp | 17 +++++++++-- .../NetworkHierarchyRootComponent.cpp | 14 +++++++++ .../Components/NetworkTransformComponent.cpp | 23 +++++++++++---- .../EntityReplication/EntityReplicator.cpp | 28 +++++++++--------- .../Code/Tests/ClientHierarchyTests.cpp | 25 +++++++++++++++- .../Code/Tests/CommonHierarchySetup.h | 29 +++++++++++++++++-- 9 files changed, 131 insertions(+), 37 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h index d644d6eb5a..1401856afe 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h @@ -12,18 +12,8 @@ namespace Multiplayer { - class NetworkHierarchyNotifications - : public AZ::ComponentBus - { - public: - //! Called when a hierarchy has been updated (a child added or removed, etc.) - virtual void OnNetworkHierarchyUpdated([[maybe_unused]] const AZ::EntityId& rootEntityId) {} - - //! Called when an entity has left a hierarchy - virtual void OnNetworkHierarchyLeave() {} - }; - - typedef AZ::EBus NetworkHierarchyNotificationBus; + using NetworkHierarchyChangedEvent = AZ::Event; + using NetworkHierarchyLeaveEvent = AZ::Event<>; class NetworkHierarchyRequests : public AZ::ComponentBus @@ -40,6 +30,14 @@ namespace Multiplayer //! @return true if this entity is the top level root of a hierarchy virtual bool IsHierarchicalRoot() const = 0; + + //! Binds the provided NetworkHierarchyChangedEvent handler to a Network Hierarchy component. + //! @param handler the handler to invoke when the entity's network hierarchy has been modified. + virtual void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) = 0; + + //! Binds the provided NetworkHierarchyLeaveEvent handler to a Network Hierarchy component. + //! @param handler the handler to invoke when the entity left its network hierarchy. + virtual void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) = 0; }; typedef AZ::EBus NetworkHierarchyRequestBus; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h index cfc3803d28..93ce3e25d0 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -52,6 +52,8 @@ namespace Multiplayer bool IsHierarchicalRoot() const override { return false; } AZ::Entity* GetHierarchicalRoot() const override; AZStd::vector GetHierarchicalEntities() const override; + void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override; + void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} protected: @@ -63,5 +65,8 @@ namespace Multiplayer AZ::Event::Handler m_hierarchyRootNetIdChanged; void OnHierarchyRootNetIdChanged(NetEntityId rootNetId); + + NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; + NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; }; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 4be8b24d8a..228f953a32 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -50,6 +50,8 @@ namespace Multiplayer bool IsHierarchicalChild() const override; AZStd::vector GetHierarchicalEntities() const override; AZ::Entity* GetHierarchicalRoot() const override; + void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override; + void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} protected: @@ -63,6 +65,9 @@ namespace Multiplayer void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); private: + NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; + NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; + AZ::Entity* m_higherRootEntity = nullptr; AZStd::vector m_hierarchicalEntities; diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index cbdf214dfc..93574ec5b4 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -98,6 +98,16 @@ namespace Multiplayer return {}; } + void NetworkHierarchyChildComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyChangedEvent); + } + + void NetworkHierarchyChildComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyLeaveEvent); + } + void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootComponent(NetworkHierarchyRootComponent* hierarchyRoot) { m_hierarchyRootComponent = hierarchyRoot; @@ -109,12 +119,13 @@ namespace Multiplayer const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetEntityId()); controller->SetHierarchyRoot(netRootId); - NetworkHierarchyNotificationBus::Event(GetEntityId(), &NetworkHierarchyNotificationBus::Events::OnNetworkHierarchyUpdated, hierarchyRoot->GetEntityId()); + m_networkHierarchyChangedEvent.Signal(hierarchyRoot->GetEntityId()); } else { controller->SetHierarchyRoot(InvalidNetEntityId); - NetworkHierarchyNotificationBus::Event(GetEntityId(), &NetworkHierarchyNotificationBus::Events::OnNetworkHierarchyLeave); + + m_networkHierarchyLeaveEvent.Signal(); } } } @@ -125,10 +136,12 @@ namespace Multiplayer if (rootHandle.Exists()) { m_hierarchyRootComponent = rootHandle.FindComponent(); + m_networkHierarchyChangedEvent.Signal(m_hierarchyRootComponent->GetEntityId()); } else { m_hierarchyRootComponent = nullptr; + m_networkHierarchyLeaveEvent.Signal(); } } } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 5f546298cb..ced2c79bce 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -124,6 +124,16 @@ namespace Multiplayer return GetEntity(); } + void NetworkHierarchyRootComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyChangedEvent); + } + + void NetworkHierarchyRootComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyLeaveEvent); + } + void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) { const AZ::EntityId entityBusId = *AZ::TransformNotificationBus::GetCurrentBusId(); @@ -168,6 +178,8 @@ namespace Multiplayer uint32_t currentEntityCount = aznumeric_cast(m_hierarchicalEntities.size()); RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount); + + m_networkHierarchyChangedEvent.Signal(GetEntityId()); } bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount) @@ -255,6 +267,8 @@ namespace Multiplayer } } } + + m_networkHierarchyChangedEvent.Signal(GetEntityId()); } void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index a37b6b52ed..8461f950fd 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -97,15 +97,28 @@ namespace Multiplayer void NetworkTransformComponent::OnParentIdChangedEvent([[maybe_unused]] NetEntityId newParent) { - const ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(newParent); - if (rootHandle.Exists()) + if (newParent == InvalidNetEntityId) { - const AZ::EntityId parentEntityId = rootHandle.GetEntity()->GetId(); if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) { - if (transformComponent->GetParentId() != parentEntityId) + if (transformComponent->GetParentId() != AZ::EntityId()) { - transformComponent->SetParent(parentEntityId); + transformComponent->SetParent(AZ::EntityId()); + } + } + } + else + { + const ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(newParent); + if (rootHandle.Exists()) + { + const AZ::EntityId parentEntityId = rootHandle.GetEntity()->GetId(); + if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) + { + if (transformComponent->GetParentId() != parentEntityId) + { + transformComponent->SetParent(parentEntityId); + } } } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index ad8c883f34..bd98d15923 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -32,8 +32,6 @@ #include -AZ_CVAR(bool, bg_debugHierarchyActivation, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Helpful messages when debugging network hierarchy behavior"); - namespace Multiplayer { EntityReplicator::EntityReplicator @@ -441,22 +439,22 @@ namespace Multiplayer const AZ::Entity* parentEntity = parentHandle.GetEntity(); if (parentEntity && parentEntity->GetState() == AZ::Entity::State::Active) { - if (bg_debugHierarchyActivation) - { - AZLOG_DEBUG( - "Entity %s asking for activation - granted", - entity->GetName().c_str()); - } + AZLOG + ( + NET_HierarchyActivationInfo, + "Hierchical entity %s asking for activation - granted", + entity->GetName().c_str() + ); return true; } - if (bg_debugHierarchyActivation) - { - AZLOG_DEBUG( - "Entity %s asking for activation - waiting on the parent %u", - entity->GetName().c_str(), - aznumeric_cast(parentId)); - } + AZLOG + ( + NET_HierarchyActivationInfo, + "Hierchical entity %s asking for activation - waiting on the parent %u", + entity->GetName().c_str(), + aznumeric_cast(parentId) + ); return false; } } diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index 29d4f7f809..1483f32023 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -226,7 +226,6 @@ namespace Multiplayer ReplicationRecord notifyRecord = currentRecord; entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); - entity.FindComponent()->NotifyStateDeltaChanges(notifyRecord); } @@ -287,6 +286,30 @@ namespace Multiplayer ); } + TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Updated_Event_On_Child_Detached_On_Server) + { + MockNetworkHierarchyCallbackHandler mock; + EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_rootEntity->GetId())); + + m_rootEntity->FindComponent()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler); + + // simulate server detaching a child entity + SetParentIdOnNetworkTransform(*m_childEntity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(*m_childEntity, InvalidNetEntityId); + } + + TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Leave_Event_On_Child_Detached_On_Server) + { + MockNetworkHierarchyCallbackHandler mock; + EXPECT_CALL(mock, OnNetworkHierarchyLeave); + + m_childEntity->FindComponent()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler); + + // simulate server detaching a child entity + SetParentIdOnNetworkTransform(*m_childEntity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(*m_childEntity, InvalidNetEntityId); + } + /* * Parent -> Child -> ChildOfChild */ diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h index 89d0d32428..d4f9d218e2 100644 --- a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -35,6 +35,29 @@ namespace Multiplayer using namespace testing; using namespace ::UnitTest; + class NetworkHierarchyCallbacks + { + public: + virtual void OnNetworkHierarchyLeave() = 0; + virtual void OnNetworkHierarchyUpdated(const AZ::EntityId& hierarchyRootId) = 0; + }; + + class MockNetworkHierarchyCallbackHandler : public NetworkHierarchyCallbacks + { + public: + MockNetworkHierarchyCallbackHandler() + : m_leaveHandler([this]() { OnNetworkHierarchyLeave(); }) + , m_changedHandler([this](const AZ::EntityId& rootId) { OnNetworkHierarchyUpdated(rootId); }) + { + } + + NetworkHierarchyLeaveEvent::Handler m_leaveHandler; + NetworkHierarchyChangedEvent::Handler m_changedHandler; + + MOCK_METHOD0(OnNetworkHierarchyLeave, void()); + MOCK_METHOD1(OnNetworkHierarchyUpdated, void(const AZ::EntityId&)); + }; + class HierarchyTests : public AllocatorsFixture { @@ -282,8 +305,9 @@ namespace Multiplayer NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + ReplicationRecord notifyRecord = currentRecord; entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); - // now the parent id is in the component + entity.FindComponent()->NotifyStateDeltaChanges(notifyRecord); } template @@ -306,8 +330,9 @@ namespace Multiplayer NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + ReplicationRecord notifyRecord = currentRecord; entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); - // now the parent id is in the component + entity.FindComponent()->NotifyStateDeltaChanges(notifyRecord); } struct EntityInfo From 714dc0a0e8255a62b72e87029a542cbf41957cb9 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Wed, 15 Sep 2021 11:29:10 -0400 Subject: [PATCH 14/63] Refactoring away the use of AZ::TransformNotificationBus. Added unittests for deactivating parents of inner roots. Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Components/NetworkHierarchyBus.h | 4 + .../NetworkHierarchyChildComponent.h | 21 +- .../NetworkHierarchyRootComponent.h | 29 ++- .../NetworkHierarchyChildComponent.cpp | 108 +++++++-- .../NetworkHierarchyRootComponent.cpp | 210 +++++++++++------- .../Code/Tests/ServerHierarchyTests.cpp | 35 +++ 6 files changed, 296 insertions(+), 111 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h index 1401856afe..45119a89e3 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h @@ -19,6 +19,10 @@ namespace Multiplayer : public AZ::ComponentBus { public: + //! @returns true if the entity a hierarchical component attached should be considered for inclusion in a hierarchy + //! this should return false when an entity is deactivating + virtual bool IsHierarchyEnabled() const = 0; + //! @returns hierarchical entities, the first element is the top level root virtual AZStd::vector GetHierarchicalEntities() const = 0; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h index 93ce3e25d0..2effb18bb8 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -42,12 +43,13 @@ namespace Multiplayer //! NetworkHierarchyChildComponentBase overrides. //! @{ void OnInit() override; - void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; - void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnActivate(EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(EntityIsMigrating entityIsMigrating) override; //! @} //! NetworkHierarchyRequestBus overrides. //! @{ + bool IsHierarchyEnabled() const override; bool IsHierarchicalChild() const override; bool IsHierarchicalRoot() const override { return false; } AZ::Entity* GetHierarchicalRoot() const override; @@ -58,15 +60,26 @@ namespace Multiplayer protected: //! Used by @NetworkHierarchyRootComponent - void SetTopLevelHierarchyRootComponent(NetworkHierarchyRootComponent* hierarchyRoot); + void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); private: - const NetworkHierarchyRootComponent* m_hierarchyRootComponent = nullptr; + AZ::ChildChangedEvent::Handler m_childChangedHandler; + AZ::ParentChangedEvent::Handler m_parentChangedHandler; + + void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child); + void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent); + + //! Points to the top level root. + AZ::Entity* m_rootEntity = nullptr; AZ::Event::Handler m_hierarchyRootNetIdChanged; void OnHierarchyRootNetIdChanged(NetEntityId rootNetId); NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; + + bool m_isDeactivating = false; + + void NotifyChildrenHierarchyDisbanded(); }; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 228f953a32..35f0da5965 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -10,8 +10,8 @@ #include #include -#include #include +#include namespace Multiplayer { @@ -27,8 +27,8 @@ namespace Multiplayer class NetworkHierarchyRootComponent final : public NetworkHierarchyRootComponentBase , public NetworkHierarchyRequestBus::Handler - , protected AZ::TransformNotificationBus::MultiHandler { + friend class NetworkHierarchyChildComponent; public: AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase); @@ -37,6 +37,8 @@ namespace Multiplayer static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + NetworkHierarchyRootComponent(); + //! NetworkHierarchyRootComponentBase overrides. //! @{ void OnInit() override; @@ -46,6 +48,7 @@ namespace Multiplayer //! NetworkHierarchyRequestBus overrides. //! @{ + bool IsHierarchyEnabled() const override; bool IsHierarchicalRoot() const override; bool IsHierarchicalChild() const override; AZStd::vector GetHierarchicalEntities() const override; @@ -55,22 +58,24 @@ namespace Multiplayer //! @} protected: - //! AZ::TransformNotificationBus::Handler overrides. - //! @{ - void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId newParent) override; - void OnChildAdded(AZ::EntityId child) override; - void OnChildRemoved(AZ::EntityId childRemovedId) override; - //! @} - void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); private: + AZ::ChildChangedEvent::Handler m_childChangedHandler; + AZ::ParentChangedEvent::Handler m_parentChangedHandler; + + void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child); + void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent); + NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; - AZ::Entity* m_higherRootEntity = nullptr; + //! Points to the top level root, if this root is an inner root in this hierarchy. + AZ::Entity* m_rootEntity = nullptr; + AZStd::vector m_hierarchicalEntities; + //! Rebuilds hierarchy starting from this root component's entity. void RebuildHierarchy(); //! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy @@ -86,5 +91,9 @@ namespace Multiplayer //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount); + + void SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity); + + bool m_isDeactivating = false; }; } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index 93574ec5b4..eff051ed93 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -53,7 +54,9 @@ namespace Multiplayer } NetworkHierarchyChildComponent::NetworkHierarchyChildComponent() - : m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); }) + : m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); }) + , m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); }) + , m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); }) { } @@ -66,13 +69,36 @@ namespace Multiplayer { HierarchyRootAddEvent(m_hierarchyRootNetIdChanged); NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); + + if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) + { + transformComponent->BindChildChangedEventHandler(m_childChangedHandler); + transformComponent->BindParentChangedEventHandler(m_parentChangedHandler); + } } void NetworkHierarchyChildComponent::OnDeactivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) { + m_isDeactivating = true; + + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + + NotifyChildrenHierarchyDisbanded(); + NetworkHierarchyRequestBus::Handler::BusDisconnect(); } + bool NetworkHierarchyChildComponent::IsHierarchyEnabled() const + { + return !m_isDeactivating; + } + bool NetworkHierarchyChildComponent::IsHierarchicalChild() const { if (GetHierarchyRoot() != InvalidNetEntityId) @@ -85,14 +111,14 @@ namespace Multiplayer AZ::Entity* NetworkHierarchyChildComponent::GetHierarchicalRoot() const { - return m_hierarchyRootComponent ? m_hierarchyRootComponent->GetEntity() : nullptr; + return m_rootEntity; } AZStd::vector NetworkHierarchyChildComponent::GetHierarchicalEntities() const { - if (m_hierarchyRootComponent) + if (m_rootEntity) { - return m_hierarchyRootComponent->GetHierarchicalEntities(); + return m_rootEntity->FindComponent()->GetHierarchicalEntities(); } return {}; @@ -108,18 +134,18 @@ namespace Multiplayer handler.Connect(m_networkHierarchyLeaveEvent); } - void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootComponent(NetworkHierarchyRootComponent* hierarchyRoot) + void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) { - m_hierarchyRootComponent = hierarchyRoot; + m_rootEntity = hierarchyRoot; if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) { NetworkHierarchyChildComponentController* controller = static_cast(GetController()); - if (hierarchyRoot) + if (m_rootEntity) { - const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetEntityId()); + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); controller->SetHierarchyRoot(netRootId); - m_networkHierarchyChangedEvent.Signal(hierarchyRoot->GetEntityId()); + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); } else { @@ -128,20 +154,74 @@ namespace Multiplayer m_networkHierarchyLeaveEvent.Signal(); } } + + if (m_rootEntity == nullptr) + { + NotifyChildrenHierarchyDisbanded(); + } + } + + void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child) + { + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + } + + void NetworkHierarchyChildComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, [[maybe_unused]] AZ::EntityId parent) + { + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } } void NetworkHierarchyChildComponent::OnHierarchyRootNetIdChanged(NetEntityId rootNetId) { - const ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(rootNetId); + ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(rootNetId); if (rootHandle.Exists()) { - m_hierarchyRootComponent = rootHandle.FindComponent(); - m_networkHierarchyChangedEvent.Signal(m_hierarchyRootComponent->GetEntityId()); + AZ::Entity* newRoot = rootHandle.GetEntity(); + if (m_rootEntity != newRoot) + { + m_rootEntity = newRoot; + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); + } } else { - m_hierarchyRootComponent = nullptr; - m_networkHierarchyLeaveEvent.Signal(); + if (!m_rootEntity) + { + m_rootEntity = nullptr; + m_networkHierarchyLeaveEvent.Signal(); + } + } + } + + void NetworkHierarchyChildComponent::NotifyChildrenHierarchyDisbanded() + { + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren); + for (const AZ::EntityId& childEntityId : allChildren) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(childEntityId)) + { + if (auto* hierarchyChildComponent = childEntity->FindComponent()) + { + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr); + } + else if (auto* hierarchyRootComponent = childEntity->FindComponent()) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr); + } + } } } } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index ced2c79bce..d2b45a8971 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,12 @@ namespace Multiplayer incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); } + NetworkHierarchyRootComponent::NetworkHierarchyRootComponent() + : m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); }) + , m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); }) + { + } + void NetworkHierarchyRootComponent::OnInit() { } @@ -67,31 +74,54 @@ namespace Multiplayer m_hierarchicalEntities.push_back(GetEntity()); NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); - AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); + + if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) + { + transformComponent->BindChildChangedEventHandler(m_childChangedHandler); + transformComponent->BindParentChangedEventHandler(m_parentChangedHandler); + } } void NetworkHierarchyRootComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { - AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); - NetworkHierarchyRequestBus::Handler::BusDisconnect(); + m_isDeactivating = true; - for (const AZ::Entity* childEntity : m_hierarchicalEntities) + if (m_rootEntity) { - auto* hierarchyChildComponent = childEntity->FindComponent(); - auto* hierarchyRootComponent = childEntity->FindComponent(); - - if (hierarchyChildComponent) + // tell parent to re-build the hierarchy + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) { - hierarchyChildComponent->SetTopLevelHierarchyRootComponent(nullptr); + root->RebuildHierarchy(); } - if (hierarchyRootComponent) + } + else + { + // notify children to that a hierarchy is disbanded + + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren); + + for (const AZ::EntityId& childEntityId : allChildren) { - hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr); + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(childEntityId)) + { + SetRootForEntity(nullptr, childEntity); + } } } + m_childChangedHandler.Disconnect(); + m_parentChangedHandler.Disconnect(); + + NetworkHierarchyRequestBus::Handler::BusDisconnect(); + m_hierarchicalEntities.clear(); - m_higherRootEntity = nullptr; + m_rootEntity = nullptr; + } + + bool NetworkHierarchyRootComponent::IsHierarchyEnabled() const + { + return !m_isDeactivating; } bool NetworkHierarchyRootComponent::IsHierarchicalRoot() const @@ -116,9 +146,9 @@ namespace Multiplayer AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const { - if (m_higherRootEntity) + if (m_rootEntity) { - return m_higherRootEntity; + return m_rootEntity; } return GetEntity(); @@ -134,24 +164,36 @@ namespace Multiplayer handler.Connect(m_networkHierarchyLeaveEvent); } + void NetworkHierarchyRootComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child) + { + if (IsHierarchicalRoot()) + { + // Parent-child notifications are not reliable enough to avoid duplicate notifications, + // so we will rebuild from scratch to avoid duplicate entries in @m_hierarchicalEntities. + RebuildHierarchy(); + } + else if (NetworkHierarchyRootComponent* root = GetHierarchicalRoot()->FindComponent()) + { + root->RebuildHierarchy(); + } + } + void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) { - const AZ::EntityId entityBusId = *AZ::TransformNotificationBus::GetCurrentBusId(); - if (GetEntityId() != entityBusId) - { - return; // ignore parent changes of child entities - } + // If the parent is part of a hierarchy, it will detect this entity as a new child and rebuild hierarchy. + // Thus, we only need to take care of a case when the parent is not part of a hierarchy, + // in which case, this entity will be a new root of a new hierarchy. if (AZ::Entity* parentEntity = AZ::Interface::Get()->FindEntity(newParent)) { - if (parentEntity->FindComponent()) + if (parentEntity->FindComponent() == nullptr && + parentEntity->FindComponent() == nullptr) + { + RebuildHierarchy(); + } + else { - m_higherRootEntity = parentEntity; m_hierarchicalEntities.clear(); - AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); - - // Should still listen for its events, such as when this root detaches - AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); } } else @@ -161,25 +203,63 @@ namespace Multiplayer } } - void NetworkHierarchyRootComponent::OnChildAdded([[maybe_unused]] AZ::EntityId child) - { - // Parent-child notifications from TransformNotificationBus are not reliable enough to avoid duplicate notifications, - // so we will rebuild from scratch to avoid duplicate entries in @m_hierarchicalEntities. - RebuildHierarchy(); - } - void NetworkHierarchyRootComponent::RebuildHierarchy() { - m_hierarchicalEntities.clear(); - m_hierarchicalEntities.push_back(GetEntity()); // add the root itself + AZStd::vector previousEntities; + m_hierarchicalEntities.swap(previousEntities); - AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); - AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); + m_hierarchicalEntities.push_back(GetEntity()); // Add the root. uint32_t currentEntityCount = aznumeric_cast(m_hierarchicalEntities.size()); RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount); - m_networkHierarchyChangedEvent.Signal(GetEntityId()); + bool hierarchyChanged = false; + + // Send out join and leave events. + + for (AZ::Entity* currentEntity : m_hierarchicalEntities) + { + const auto prevEntityIterator = AZStd::find(previousEntities.begin(), previousEntities.end(), currentEntity); + if (prevEntityIterator != previousEntities.end()) + { + // This entity was here before the build of the hierarchy. + previousEntities.erase(prevEntityIterator); + } + else + { + // This is a newly added entity to the network hierarchy. + hierarchyChanged = true; + SetRootForEntity(GetEntity(), currentEntity); + } + } + + // These entities were removed since last rebuild. + for (const AZ::Entity* previousEntity : previousEntities) + { + SetRootForEntity(nullptr, previousEntity); + } + + if (!previousEntities.empty()) + { + hierarchyChanged = true; + } + + if (hierarchyChanged) + { + m_networkHierarchyChangedEvent.Signal(GetEntityId()); + } + } + + void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity) + { + if (auto* hierarchyChildComponent = childEntity->FindComponent()) + { + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(root); + } + else if (auto* hierarchyRootComponent = childEntity->FindComponent()) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(root); + } } bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount) @@ -212,21 +292,12 @@ namespace Multiplayer auto* hierarchyChildComponent = childEntity->FindComponent(); auto* hierarchyRootComponent = childEntity->FindComponent(); - if (hierarchyChildComponent || hierarchyRootComponent) + if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) || + (hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled())) { - AZ::TransformNotificationBus::MultiHandler::BusConnect(entity); m_hierarchicalEntities.push_back(childEntity); ++currentEntityCount; - if (hierarchyChildComponent) - { - hierarchyChildComponent->SetTopLevelHierarchyRootComponent(this); - } - else if (hierarchyRootComponent) - { - hierarchyRootComponent->SetTopLevelHierarchyRootEntity(GetEntity()); - } - if (!RecursiveAttachHierarchicalEntities(entity, currentEntityCount)) { return false; @@ -237,43 +308,10 @@ namespace Multiplayer return true; } - void NetworkHierarchyRootComponent::OnChildRemoved(AZ::EntityId childRemovedId) - { - AZStd::vector allChildren; - AZ::TransformBus::EventResult(allChildren, childRemovedId, &AZ::TransformBus::Events::GetEntityAndAllDescendants); - - for (AZ::EntityId childId : allChildren) - { - AZ::TransformNotificationBus::MultiHandler::BusDisconnect(childId); - - const AZ::Entity* childEntity = nullptr; - - AZStd::erase_if(m_hierarchicalEntities, [childId, &childEntity](const AZ::Entity* entity) - { - if (entity->GetId() == childId) - { - childEntity = entity; - return true; - } - - return false; - }); - - if (childEntity) - { - if (NetworkHierarchyChildComponent* childComponent = childEntity->FindComponent()) - { - childComponent->SetTopLevelHierarchyRootComponent(nullptr); - } - } - } - - m_networkHierarchyChangedEvent.Signal(GetEntityId()); - } - void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) { - m_higherRootEntity = hierarchyRoot; + m_rootEntity = hierarchyRoot; + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) { NetworkHierarchyChildComponentController* controller = static_cast(GetController()); @@ -287,5 +325,11 @@ namespace Multiplayer controller->SetHierarchyRoot(InvalidNetEntityId); } } + + if (m_rootEntity == nullptr) + { + // We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy. + RebuildHierarchy(); + } } } diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp index 708a39d439..0b76e276fd 100644 --- a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -847,6 +847,41 @@ namespace Multiplayer ); } + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Top_Root_Deactivates) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + + StopAndDeleteEntity(m_rootEntity); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Child_Of_Top_Root_Deactivates) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + + StopAndDeleteEntity(m_childEntity); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Child_Of_Child_Deactivates) + { + m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + StopAndDeleteEntity(m_childOfChildEntity); + + EXPECT_EQ( + m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + TEST_F(ServerHierarchyOfHierarchyTests, Stress_Test_Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) { for (int i = 0; i < 100; ++i) From c673988935b0602c8ed33b347c02bbc1605e178f Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Wed, 15 Sep 2021 17:58:06 -0400 Subject: [PATCH 15/63] Unittest refactoring Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Code/Tests/ClientHierarchyTests.cpp | 160 ++-- .../Code/Tests/CommonHierarchySetup.h | 82 +- .../Code/Tests/ServerHierarchyTests.cpp | 754 +++++++++--------- 3 files changed, 507 insertions(+), 489 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index 1483f32023..38ea451c85 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -27,14 +27,14 @@ namespace Multiplayer */ TEST_F(HierarchyTests, On_Client_NetBindComponent_Activate) { - AZ::Entity entity; - entity.CreateComponent(); + AZStd::unique_ptr entity = AZStd::make_unique(); + entity->CreateComponent(); SetupEntity(entity, NetEntityId{ 1 }, NetEntityRole::Client); - entity.Activate(); + entity->Activate(); StopEntity(entity); - entity.Deactivate(); + entity->Deactivate(); } /* @@ -43,7 +43,7 @@ namespace Multiplayer TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_BeforeParent) { // Create a child entity that will be tested for activation inside a hierarchy - AZ::Entity childEntity; + AZStd::unique_ptr childEntity = AZStd::make_unique(); CreateEntityWithChildHierarchy(childEntity); SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); // child entity is not activated on purpose here, we are about to test conditional activation check @@ -53,7 +53,7 @@ namespace Multiplayer SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, NetEntityId{ 1 }); // Create an entity replicator for the child entity - const NetworkEntityHandle childHandle(&childEntity, m_networkEntityTracker.get()); + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); entityReplicator.Initialize(childHandle); @@ -64,7 +64,7 @@ namespace Multiplayer TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_Inner_Root_Before_Top_Root) { // Create a child entity that will be tested for activation inside a hierarchy - AZ::Entity innerRootEntity; + AZStd::unique_ptr innerRootEntity = AZStd::make_unique(); CreateEntityWithRootHierarchy(innerRootEntity); SetupEntity(innerRootEntity, NetEntityId{ 2 }, NetEntityRole::Client); // child entity is not activated on purpose here, we are about to test conditional activation check @@ -74,7 +74,7 @@ namespace Multiplayer SetHierarchyRootFieldOnNetworkHierarchyChild(innerRootEntity, NetEntityId{ 1 }); // Create an entity replicator for the child entity - const NetworkEntityHandle innerRootHandle(&innerRootEntity, m_networkEntityTracker.get()); + const NetworkEntityHandle innerRootHandle(innerRootEntity.get(), m_networkEntityTracker.get()); EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, innerRootHandle); entityReplicator.Initialize(innerRootHandle); @@ -85,7 +85,7 @@ namespace Multiplayer TEST_F(HierarchyTests, On_Client_Not_In_Hierarchy_EntityReplicator_Ignores_Parent) { // Create a child entity that will be tested for activation inside a hierarchy - AZ::Entity childEntity; + AZStd::unique_ptr childEntity = AZStd::make_unique(); CreateEntityWithChildHierarchy(childEntity); SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); // child entity is not activated on purpose here, we are about to test conditional activation check @@ -95,7 +95,7 @@ namespace Multiplayer SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, InvalidNetEntityId); // Create an entity replicator for the child entity - const NetworkEntityHandle childHandle(&childEntity, m_networkEntityTracker.get()); + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); entityReplicator.Initialize(childHandle); @@ -108,7 +108,7 @@ namespace Multiplayer */ TEST_F(HierarchyTests, On_Client_EntityReplicator_Activates_AfterParent) { - AZ::Entity childEntity; + AZStd::unique_ptr childEntity = AZStd::make_unique(); CreateEntityWithChildHierarchy(childEntity); SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); @@ -117,29 +117,29 @@ namespace Multiplayer SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, NetEntityId{ 1 }); // Create an entity replicator for the child entity - const NetworkEntityHandle childHandle(&childEntity, m_networkEntityTracker.get()); + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); EntityReplicator childEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); childEntityReplicator.Initialize(childHandle); // Now let's create a parent entity and activate it - AZ::Entity parentEntity; + AZStd::unique_ptr parentEntity = AZStd::make_unique(); CreateEntityWithRootHierarchy(parentEntity); SetupEntity(parentEntity, NetEntityId{ 1 }, NetEntityRole::Client); // Create an entity replicator for the parent entity - const NetworkEntityHandle parentHandle(&parentEntity, m_networkEntityTracker.get()); + const NetworkEntityHandle parentHandle(parentEntity.get(), m_networkEntityTracker.get()); ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Return(parentHandle)); EntityReplicator parentEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, parentHandle); parentEntityReplicator.Initialize(parentHandle); - parentEntity.Activate(); + parentEntity->Activate(); // The child should be ready to be activated EXPECT_EQ(childEntityReplicator.IsReadyToActivate(), true); StopEntity(parentEntity); - parentEntity.Deactivate(); + parentEntity->Deactivate(); } /* @@ -155,25 +155,19 @@ namespace Multiplayer { HierarchyTests::SetUp(); - m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); - m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); + m_root = AZStd::make_unique(1, "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique(2, "child", ChildNetEntityId, EntityInfo::Role::Child); - m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), RootNetEntityId, EntityInfo::Role::Root); - m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), ChildNetEntityId, EntityInfo::Role::Child); + CreateSimpleHierarchy(*m_root, *m_child); - CreateSimpleHierarchy(*m_rootEntityInfo, *m_childEntityInfo); - - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); // now the two entities are under one hierarchy } void TearDown() override { - m_childEntityInfo.reset(); - m_rootEntityInfo.reset(); - - StopAndDeleteEntity(m_childEntity); - StopAndDeleteEntity(m_rootEntity); + m_child.reset(); + m_root.reset(); HierarchyTests::TearDown(); } @@ -191,20 +185,20 @@ namespace Multiplayer SetHierarchyRootFieldOnNetworkHierarchyChild(child.m_entity, root.m_netId); // Create an entity replicator for the child entity - const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); child.m_replicator->Initialize(childHandle); // Create an entity replicator for the root entity - const NetworkEntityHandle rootHandle(&root.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, rootHandle); root.m_replicator->Initialize(rootHandle); - root.m_entity.Activate(); - child.m_entity.Activate(); + root.m_entity->Activate(); + child.m_entity->Activate(); } - void SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(const AZ::Entity& entity, NetEntityId value) + void SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(const AZStd::unique_ptr& entity, NetEntityId value) { /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/; @@ -225,47 +219,44 @@ namespace Multiplayer ReplicationRecord notifyRecord = currentRecord; - entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); - entity.FindComponent()->NotifyStateDeltaChanges(notifyRecord); + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); } - AZStd::unique_ptr m_rootEntity; - AZStd::unique_ptr m_childEntity; - - AZStd::unique_ptr m_rootEntityInfo; - AZStd::unique_ptr m_childEntityInfo; + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; }; TEST_F(ClientSimpleHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchyRoot(), + m_root->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchicalRoot(), - m_rootEntity.get() + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); - if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 2) + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 2) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], - m_rootEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], - m_childEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() ); } } @@ -273,15 +264,15 @@ namespace Multiplayer TEST_F(ClientSimpleHierarchyTests, Client_Detaches_Child_When_Server_Detaches) { // simulate server detaching child entity - SetParentIdOnNetworkTransform(*m_childEntity, InvalidNetEntityId); - SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(*m_childEntity, InvalidNetEntityId); + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchicalRoot(), + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), nullptr ); } @@ -289,13 +280,13 @@ namespace Multiplayer TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Updated_Event_On_Child_Detached_On_Server) { MockNetworkHierarchyCallbackHandler mock; - EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_rootEntity->GetId())); + EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root->m_entity->GetId())); - m_rootEntity->FindComponent()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler); + m_root->m_entity->FindComponent()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler); // simulate server detaching a child entity - SetParentIdOnNetworkTransform(*m_childEntity, InvalidNetEntityId); - SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(*m_childEntity, InvalidNetEntityId); + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); } TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Leave_Event_On_Child_Detached_On_Server) @@ -303,11 +294,11 @@ namespace Multiplayer MockNetworkHierarchyCallbackHandler mock; EXPECT_CALL(mock, OnNetworkHierarchyLeave); - m_childEntity->FindComponent()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler); + m_child->m_entity->FindComponent()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler); // simulate server detaching a child entity - SetParentIdOnNetworkTransform(*m_childEntity, InvalidNetEntityId); - SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(*m_childEntity, InvalidNetEntityId); + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); } /* @@ -322,18 +313,16 @@ namespace Multiplayer { ClientSimpleHierarchyTests::SetUp(); - m_childOfChildEntity = AZStd::make_unique(AZ::EntityId(3), "child of child"); - m_childOfChildEntityInfo = AZStd::make_unique(*m_childOfChildEntity.get(), ChildOfChildNetEntityId, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); - CreateDeepHierarchyOnClient(*m_childOfChildEntityInfo); + CreateDeepHierarchyOnClient(*m_childOfChild); - m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); } void TearDown() override { - m_childOfChildEntityInfo.reset(); - StopAndDeleteEntity(m_childOfChildEntity); + m_childOfChild.reset(); ClientSimpleHierarchyTests::TearDown(); } @@ -344,58 +333,57 @@ namespace Multiplayer SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Client); // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) - SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childEntityInfo->m_netId); - SetHierarchyRootFieldOnNetworkHierarchyChild(childOfChild.m_entity, m_rootEntityInfo->m_netId); + SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childOfChild->m_netId); + SetHierarchyRootFieldOnNetworkHierarchyChild(childOfChild.m_entity, m_root->m_netId); // Create an entity replicator for the child entity - const NetworkEntityHandle childOfChildHandle(&childOfChild.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childOfChildHandle); childOfChild.m_replicator->Initialize(childOfChildHandle); - childOfChild.m_entity.Activate(); + childOfChild.m_entity->Activate(); } - AZStd::unique_ptr m_childOfChildEntity; - AZStd::unique_ptr m_childOfChildEntityInfo; + AZStd::unique_ptr m_childOfChild; }; TEST_F(ClientDeepHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchyRoot(), + m_root->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); EXPECT_EQ( - m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchicalRoot(), - m_rootEntity.get() + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); - if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 3) + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], - m_rootEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], - m_childEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], - m_childOfChildEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() ); } } diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h index d4f9d218e2..7fc1eef8b7 100644 --- a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -38,6 +38,7 @@ namespace Multiplayer class NetworkHierarchyCallbacks { public: + virtual ~NetworkHierarchyCallbacks() = default; virtual void OnNetworkHierarchyLeave() = 0; virtual void OnNetworkHierarchyUpdated(const AZ::EntityId& hierarchyRootId) = 0; }; @@ -245,48 +246,48 @@ namespace Multiplayer return nullptr; } - void SetupEntity(AZ::Entity& entity, NetEntityId netId, NetEntityRole role) + void SetupEntity(const AZStd::unique_ptr& entity, NetEntityId netId, NetEntityRole role) { - const auto netBindComponent = entity.FindComponent(); + const auto netBindComponent = entity->FindComponent(); EXPECT_NE(netBindComponent, nullptr); - netBindComponent->PreInit(&entity, PrefabEntityId{ AZ::Name("test"), 1 }, netId, role); - entity.Init(); + netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role); + entity->Init(); } - void StopEntity(const AZ::Entity& entity) + static void StopEntity(const AZStd::unique_ptr& entity) { - const auto netBindComponent = entity.FindComponent(); + const auto netBindComponent = entity->FindComponent(); EXPECT_NE(netBindComponent, nullptr); netBindComponent->StopEntity(); } - void StopAndDeleteEntity(AZStd::unique_ptr& entity) + static void StopAndDeactivateEntity(AZStd::unique_ptr& entity) { if (entity) { - StopEntity(*entity); + StopEntity(entity); entity->Deactivate(); entity.reset(); } } - void CreateEntityWithRootHierarchy(AZ::Entity& rootEntity) + void CreateEntityWithRootHierarchy(AZStd::unique_ptr& rootEntity) { - rootEntity.CreateComponent(); - rootEntity.CreateComponent(); - rootEntity.CreateComponent(); - rootEntity.CreateComponent(); + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); } - void CreateEntityWithChildHierarchy(AZ::Entity& childEntity) + void CreateEntityWithChildHierarchy(AZStd::unique_ptr& childEntity) { - childEntity.CreateComponent(); - childEntity.CreateComponent(); - childEntity.CreateComponent(); - childEntity.CreateComponent(); + childEntity->CreateComponent(); + childEntity->CreateComponent(); + childEntity->CreateComponent(); + childEntity->CreateComponent(); } - void SetParentIdOnNetworkTransform(const AZ::Entity& entity, NetEntityId netParentId) + void SetParentIdOnNetworkTransform(const AZStd::unique_ptr& entity, NetEntityId netParentId) { /* Derived from NetworkTransformComponent.AutoComponent.xml */ constexpr int totalBits = 6 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::Count*/; @@ -306,12 +307,12 @@ namespace Multiplayer NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); ReplicationRecord notifyRecord = currentRecord; - entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); - entity.FindComponent()->NotifyStateDeltaChanges(notifyRecord); + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); } template - void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZ::Entity& entity, NetEntityId value) + void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZStd::unique_ptr& entity, NetEntityId value) { /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/; @@ -331,8 +332,8 @@ namespace Multiplayer NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); ReplicationRecord notifyRecord = currentRecord; - entity.FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); - entity.FindComponent()->NotifyStateDeltaChanges(notifyRecord); + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); } struct EntityInfo @@ -344,14 +345,19 @@ namespace Multiplayer None }; - EntityInfo(AZ::Entity& entity, NetEntityId netId, Role role) - : m_entity(entity) + EntityInfo(AZ::u64 entityId, const char* entityName, NetEntityId netId, Role role) + : m_entity(AZStd::make_unique(AZ::EntityId(entityId), entityName)) , m_netId(netId) , m_role(role) { } - AZ::Entity& m_entity; + ~EntityInfo() + { + StopAndDeactivateEntity(m_entity); + } + + AZStd::unique_ptr m_entity; NetEntityId m_netId; AZStd::unique_ptr m_replicator; Role m_role = Role::None; @@ -359,16 +365,16 @@ namespace Multiplayer void PopulateHierarchicalEntity(const EntityInfo& entityInfo) { - entityInfo.m_entity.CreateComponent(); - entityInfo.m_entity.CreateComponent(); - entityInfo.m_entity.CreateComponent(); + entityInfo.m_entity->CreateComponent(); + entityInfo.m_entity->CreateComponent(); + entityInfo.m_entity->CreateComponent(); switch (entityInfo.m_role) { case EntityInfo::Role::Root: - entityInfo.m_entity.CreateComponent(); + entityInfo.m_entity->CreateComponent(); break; case EntityInfo::Role::Child: - entityInfo.m_entity.CreateComponent(); + entityInfo.m_entity->CreateComponent(); break; case EntityInfo::Role::None: break; @@ -390,23 +396,23 @@ namespace Multiplayer SetParentIdOnNetworkTransform(childOfChild.m_entity, child.m_netId); // Create an entity replicator for the child entity - const NetworkEntityHandle childOfChildHandle(&childOfChild.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); childOfChild.m_replicator->Initialize(childOfChildHandle); // Create an entity replicator for the child entity - const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); child.m_replicator->Initialize(childHandle); // Create an entity replicator for the root entity - const NetworkEntityHandle rootHandle(&root.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); root.m_replicator->Initialize(rootHandle); - root.m_entity.Activate(); - child.m_entity.Activate(); - childOfChild.m_entity.Activate(); + root.m_entity->Activate(); + child.m_entity->Activate(); + childOfChild.m_entity->Activate(); } }; } diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp index 0b76e276fd..05e36e466d 100644 --- a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -35,25 +35,19 @@ namespace Multiplayer { HierarchyTests::SetUp(); - m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); - m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); + m_root = AZStd::make_unique(1, "root", NetEntityId{ 1 }, EntityInfo::Role::Root); + m_child = AZStd::make_unique(2, "child", NetEntityId{ 2 }, EntityInfo::Role::Child); - m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), NetEntityId{ 1 }, EntityInfo::Role::Root); - m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), NetEntityId{ 2 }, EntityInfo::Role::Child); + CreateSimpleHierarchy(*m_root, *m_child); - CreateSimpleHierarchy(*m_rootEntityInfo, *m_childEntityInfo); - - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); // now the two entities are under one hierarchy } void TearDown() override { - m_childEntityInfo.reset(); - m_rootEntityInfo.reset(); - - StopAndDeleteEntity(m_childEntity); - StopAndDeleteEntity(m_rootEntity); + m_child.reset(); + m_root.reset(); HierarchyTests::TearDown(); } @@ -67,35 +61,32 @@ namespace Multiplayer SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); // Create an entity replicator for the child entity - const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); child.m_replicator->Initialize(childHandle); // Create an entity replicator for the root entity - const NetworkEntityHandle rootHandle(&root.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); root.m_replicator->Initialize(rootHandle); - root.m_entity.Activate(); - child.m_entity.Activate(); + root.m_entity->Activate(); + child.m_entity->Activate(); } - AZStd::unique_ptr m_rootEntity; - AZStd::unique_ptr m_childEntity; - - AZStd::unique_ptr m_rootEntityInfo; - AZStd::unique_ptr m_childEntityInfo; + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; }; TEST_F(ServerSimpleHierarchyTests, Server_Sets_Appropriate_Network_Fields_For_Clients) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchyRoot(), + m_root->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), NetEntityId{ 1 } ); } @@ -103,7 +94,7 @@ namespace Multiplayer TEST_F(ServerSimpleHierarchyTests, Root_Is_Top_Level_Root) { EXPECT_EQ( - m_rootEntity->FindComponent()->IsHierarchicalChild(), + m_root->m_entity->FindComponent()->IsHierarchicalChild(), false ); } @@ -111,18 +102,18 @@ namespace Multiplayer TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Set) { EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), NetEntityId{ 1 } ); } TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Cleared_On_Detach) { - // now detach the child - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + // now detach the child + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); } @@ -130,7 +121,7 @@ namespace Multiplayer TEST_F(ServerSimpleHierarchyTests, Root_Has_Child_Reference) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); } @@ -138,34 +129,32 @@ namespace Multiplayer TEST_F(ServerSimpleHierarchyTests, Root_Has_Child_References_Removed_On_Detach) { // now detach the child - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 1 ); } TEST_F(ServerSimpleHierarchyTests, Root_Deactivates_Child_Has_No_References_To_Root) { - StopEntity(*m_rootEntity); - m_rootEntity->Deactivate(); - m_rootEntity.reset(); + StopEntity(m_root->m_entity); + m_root->m_entity->Deactivate(); + m_root->m_entity.reset(); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); } TEST_F(ServerSimpleHierarchyTests, Child_Deactivates_Root_Has_No_References_To_Child) { - StopEntity(*m_childEntity); - m_childEntity->Deactivate(); - m_childEntity.reset(); + m_child.reset(); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 1 ); } @@ -184,47 +173,35 @@ namespace Multiplayer { HierarchyTests::SetUp(); - m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); - m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); - m_childOfChildEntity = AZStd::make_unique(AZ::EntityId(3), "child of child"); + m_root = AZStd::make_unique((1), "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", ChildNetEntityId, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); - m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), RootNetEntityId, EntityInfo::Role::Root); - m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), ChildNetEntityId, EntityInfo::Role::Child); - m_childOfChildEntityInfo = AZStd::make_unique(*m_childOfChildEntity.get(), ChildOfChildNetEntityId, EntityInfo::Role::Child); + CreateDeepHierarchy(*m_root, *m_child, *m_childOfChild); - CreateDeepHierarchy(*m_rootEntityInfo, *m_childEntityInfo, *m_childOfChildEntityInfo); - - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); - m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); // now the entities are under one hierarchy } void TearDown() override { - m_childOfChildEntityInfo.reset(); - m_childEntityInfo.reset(); - m_rootEntityInfo.reset(); - - StopAndDeleteEntity(m_childOfChildEntity); - StopAndDeleteEntity(m_childEntity); - StopAndDeleteEntity(m_rootEntity); + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); HierarchyTests::TearDown(); } - AZStd::unique_ptr m_rootEntity; - AZStd::unique_ptr m_childEntity; - AZStd::unique_ptr m_childOfChildEntity; - - AZStd::unique_ptr m_rootEntityInfo; - AZStd::unique_ptr m_childEntityInfo; - AZStd::unique_ptr m_childOfChildEntityInfo; + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; }; TEST_F(ServerDeepHierarchyTests, Root_Is_Top_Level_Root) { EXPECT_EQ( - m_rootEntity->FindComponent()->IsHierarchicalChild(), + m_root->m_entity->FindComponent()->IsHierarchicalChild(), false ); } @@ -232,144 +209,144 @@ namespace Multiplayer TEST_F(ServerDeepHierarchyTests, Root_Has_Child_References) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); - if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 3) + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], - m_rootEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], - m_childEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], - m_childOfChildEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() ); } } TEST_F(ServerDeepHierarchyTests, Root_Has_Child_Of_Child_Reference_Removed_On_Detach) { - m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); } TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_Removed_On_Detach_Of_Mid_Child) { - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 1 ); } TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_If_Mid_Child_Added_With_Child) { - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); // reconnect - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); } TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_If_Child_Of_Child_Added) { - m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); // reconnect - m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); } TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Points_To_Root_After_Attach) { - m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); // reconnect - m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); } TEST_F(ServerDeepHierarchyTests, All_New_Children_Point_To_Root_If_Mid_Child_Added_With_Child) { - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); // reconnect - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); EXPECT_EQ( - m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); } TEST_F(ServerDeepHierarchyTests, Children_Clear_Reference_To_Root_After_Mid_Child_Detached) { - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); EXPECT_EQ( - m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); } TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Clears_Reference_To_Root_After_Detached) { - m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); } TEST_F(ServerDeepHierarchyTests, Root_Deactivates_Children_Have_No_References_To_Root) { - StopAndDeleteEntity(m_rootEntity); + m_root.reset(); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchyRoot(), + m_child->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); EXPECT_EQ( - m_childOfChildEntity->FindComponent()->GetHierarchyRoot(), + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), InvalidNetEntityId ); } TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Deactivates_Root_Removes_References_To_It) { - StopAndDeleteEntity(m_childOfChildEntity); + m_childOfChild.reset(); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); } @@ -381,11 +358,11 @@ namespace Multiplayer m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); // remake the hierarchy - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); @@ -412,43 +389,29 @@ namespace Multiplayer { HierarchyTests::SetUp(); - m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); - m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); - m_childOfChildEntity = AZStd::make_unique(AZ::EntityId(3), "child of child"); - m_child2Entity = AZStd::make_unique(AZ::EntityId(4), "child2"); - m_childOfChild2Entity = AZStd::make_unique(AZ::EntityId(5), "child of child2"); - m_child2OfChild2Entity = AZStd::make_unique(AZ::EntityId(6), "child2 of child2"); + m_root = AZStd::make_unique((1), "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", ChildNetEntityId, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); + m_child2 = AZStd::make_unique((4), "child2", Child2NetEntityId, EntityInfo::Role::Child); + m_childOfChild2 = AZStd::make_unique((5), "child of child2", ChildOfChild2NetEntityId, EntityInfo::Role::Child); + m_child2OfChild2 = AZStd::make_unique((6), "child2 of child2", Child2OfChild2NetEntityId, EntityInfo::Role::Child); - m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), RootNetEntityId, EntityInfo::Role::Root); - m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), ChildNetEntityId, EntityInfo::Role::Child); - m_childOfChildEntityInfo = AZStd::make_unique(*m_childOfChildEntity.get(), ChildOfChildNetEntityId, EntityInfo::Role::Child); - m_child2EntityInfo = AZStd::make_unique(*m_child2Entity.get(), Child2NetEntityId, EntityInfo::Role::Child); - m_childOfChild2EntityInfo = AZStd::make_unique(*m_childOfChild2Entity.get(), ChildOfChild2NetEntityId, EntityInfo::Role::Child); - m_child2OfChild2EntityInfo = AZStd::make_unique(*m_child2OfChild2Entity.get(), Child2OfChild2NetEntityId, EntityInfo::Role::Child); + CreateBranchedHierarchy(*m_root, *m_child, *m_childOfChild, + *m_child2, *m_childOfChild2, *m_child2OfChild2); - CreateBranchedHierarchy(*m_rootEntityInfo, *m_childEntityInfo, *m_childOfChildEntityInfo, - *m_child2EntityInfo, *m_childOfChild2EntityInfo, *m_child2OfChild2EntityInfo); - - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); - m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); // now the entities are under one hierarchy } void TearDown() override { - m_child2OfChild2EntityInfo.reset(); - m_childOfChild2EntityInfo.reset(); - m_child2EntityInfo.reset(); - m_childOfChildEntityInfo.reset(); - m_childEntityInfo.reset(); - m_rootEntityInfo.reset(); - - StopAndDeleteEntity(m_child2OfChild2Entity); - StopAndDeleteEntity(m_childOfChild2Entity); - StopAndDeleteEntity(m_child2Entity); - StopAndDeleteEntity(m_childOfChildEntity); - StopAndDeleteEntity(m_childEntity); - StopAndDeleteEntity(m_rootEntity); + m_child2OfChild2.reset(); + m_childOfChild2.reset(); + m_child2.reset(); + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); HierarchyTests::TearDown(); } @@ -479,139 +442,132 @@ namespace Multiplayer SetParentIdOnNetworkTransform(child2OfChild2.m_entity, child2.m_netId); // Create entity replicators - const NetworkEntityHandle childOfChild2Handle(&childOfChild2.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle childOfChild2Handle(childOfChild2.m_entity.get(), m_networkEntityTracker.get()); childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChild2Handle); childOfChild.m_replicator->Initialize(childOfChild2Handle); - const NetworkEntityHandle child2OfChild2Handle(&child2OfChild2.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle child2OfChild2Handle(child2OfChild2.m_entity.get(), m_networkEntityTracker.get()); childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, child2OfChild2Handle); childOfChild.m_replicator->Initialize(child2OfChild2Handle); - const NetworkEntityHandle child2Handle(&child2.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle child2Handle(child2.m_entity.get(), m_networkEntityTracker.get()); child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, child2Handle); child.m_replicator->Initialize(child2Handle); - const NetworkEntityHandle childOfChildHandle(&childOfChild.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); childOfChild.m_replicator->Initialize(childOfChildHandle); - const NetworkEntityHandle childHandle(&child.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); child.m_replicator->Initialize(childHandle); - const NetworkEntityHandle rootHandle(&root.m_entity, m_networkEntityTracker.get()); + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); root.m_replicator->Initialize(rootHandle); - root.m_entity.Activate(); - child.m_entity.Activate(); - childOfChild.m_entity.Activate(); - child2.m_entity.Activate(); - childOfChild2.m_entity.Activate(); - child2OfChild2.m_entity.Activate(); + root.m_entity->Activate(); + child.m_entity->Activate(); + childOfChild.m_entity->Activate(); + child2.m_entity->Activate(); + childOfChild2.m_entity->Activate(); + child2OfChild2.m_entity->Activate(); } - AZStd::unique_ptr m_rootEntity; - AZStd::unique_ptr m_childEntity; - AZStd::unique_ptr m_childOfChildEntity; - AZStd::unique_ptr m_child2Entity; - AZStd::unique_ptr m_childOfChild2Entity; - AZStd::unique_ptr m_child2OfChild2Entity; - - AZStd::unique_ptr m_rootEntityInfo; - AZStd::unique_ptr m_childEntityInfo; - AZStd::unique_ptr m_childOfChildEntityInfo; - AZStd::unique_ptr m_child2EntityInfo; - AZStd::unique_ptr m_childOfChild2EntityInfo; - AZStd::unique_ptr m_child2OfChild2EntityInfo; + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; + AZStd::unique_ptr m_child2OfChild2; }; TEST_F(ServerBranchedHierarchyTests, Sanity_Check) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 6 ); - if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 6) + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 6) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], - m_rootEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], - m_childEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], - m_childOfChildEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[3], - m_child2Entity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[3], + m_child2->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[4], - m_child2OfChild2Entity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[4], + m_child2OfChild2->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[5], - m_childOfChild2Entity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[5], + m_childOfChild2->m_entity.get() ); } } TEST_F(ServerBranchedHierarchyTests, Detach_Child_While_Child2_Remains_Attached) { - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 4 ); - if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 4) + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 4) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], - m_rootEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], - m_child2Entity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], - m_child2OfChild2Entity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_child2OfChild2->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[3], - m_childOfChild2Entity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[3], + m_childOfChild2->m_entity.get() ); } EXPECT_EQ( - m_child2Entity->FindComponent()->GetHierarchicalRoot(), - m_rootEntity.get() + m_child2->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() ); EXPECT_EQ( - m_childEntity->FindComponent()->GetHierarchicalRoot(), + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), nullptr ); EXPECT_EQ( - m_childOfChildEntity->FindComponent()->GetHierarchicalRoot(), + m_childOfChild->m_entity->FindComponent()->GetHierarchicalRoot(), nullptr ); } TEST_F(ServerBranchedHierarchyTests, Detach_Child_Then_Attach_To_Child2) { - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); - m_childEntity->FindComponent()->SetParent(m_child2Entity->GetId()); + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 6 ); } @@ -630,254 +586,242 @@ namespace Multiplayer { ServerDeepHierarchyTests::SetUp(); - m_rootEntity2 = AZStd::make_unique(AZ::EntityId(4), "root 2"); - m_childEntity2 = AZStd::make_unique(AZ::EntityId(5), "child 2"); - m_childOfChildEntity2 = AZStd::make_unique(AZ::EntityId(6), "child of child 2"); + m_root2 = AZStd::make_unique((4), "root 2", Root2NetEntityId, EntityInfo::Role::Root); + m_child2 = AZStd::make_unique((5), "child 2", Child2NetEntityId, EntityInfo::Role::Child); + m_childOfChild2 = AZStd::make_unique((6), "child of child 2", ChildOfChild2NetEntityId, EntityInfo::Role::Child); - m_rootEntityInfo2 = AZStd::make_unique(*m_rootEntity2.get(), Root2NetEntityId, EntityInfo::Role::Root); - m_childEntityInfo2 = AZStd::make_unique(*m_childEntity2.get(), Child2NetEntityId, EntityInfo::Role::Child); - m_childOfChildEntityInfo2 = AZStd::make_unique(*m_childOfChildEntity2.get(), ChildOfChild2NetEntityId, EntityInfo::Role::Child); + CreateDeepHierarchy(*m_root2, *m_child2, *m_childOfChild2); - CreateDeepHierarchy(*m_rootEntityInfo2, *m_childEntityInfo2, *m_childOfChildEntityInfo2); - - m_childEntity2->FindComponent()->SetParent(m_rootEntity2->GetId()); - m_childOfChildEntity2->FindComponent()->SetParent(m_childEntity2->GetId()); + m_child2->m_entity->FindComponent()->SetParent(m_root2->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); // now the entities are under one hierarchy } void TearDown() override { - m_childOfChildEntityInfo2.reset(); - m_childEntityInfo2.reset(); - m_rootEntityInfo2.reset(); - - StopAndDeleteEntity(m_childOfChildEntity2); - StopAndDeleteEntity(m_childEntity2); - StopAndDeleteEntity(m_rootEntity2); + m_childOfChild2.reset(); + m_child2.reset(); + m_root2.reset(); ServerDeepHierarchyTests::TearDown(); } - AZStd::unique_ptr m_rootEntity2; - AZStd::unique_ptr m_childEntity2; - AZStd::unique_ptr m_childOfChildEntity2; - - AZStd::unique_ptr m_rootEntityInfo2; - AZStd::unique_ptr m_childEntityInfo2; - AZStd::unique_ptr m_childOfChildEntityInfo2; + AZStd::unique_ptr m_root2; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; }; TEST_F(ServerHierarchyOfHierarchyTests, Hierarchies_Are_Not_Related) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); - if (m_rootEntity->FindComponent()->GetHierarchicalEntities().size() == 3) + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[0], - m_rootEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[1], - m_childEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() ); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities()[2], - m_childOfChildEntity.get() + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() ); } EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); - if (m_rootEntity2->FindComponent()->GetHierarchicalEntities().size() == 3) + if (m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) { EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities()[0], - m_rootEntity2.get() + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root2->m_entity.get() ); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities()[1], - m_childEntity2.get() + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() ); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities()[2], - m_childOfChildEntity2.get() + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild2->m_entity.get() ); } } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Is_Not_Top_Level_Root) { - m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->IsHierarchicalChild(), + m_root->m_entity->FindComponent()->IsHierarchicalChild(), false ); EXPECT_EQ( - m_rootEntity2->FindComponent()->IsHierarchicalChild(), + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), true ); } TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Root) { - m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 6 ); } TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Child) { - m_rootEntity2->FindComponent()->SetParent(m_childEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 6 ); } TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Child_Of_Child) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 6 ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Root) { - m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity2->FindComponent()->IsHierarchicalChild(), + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), true ); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchyRoot(), + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Child) { - m_rootEntity2->FindComponent()->SetParent(m_childEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity2->FindComponent()->IsHierarchicalChild(), + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), true ); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchyRoot(), + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Child_Of_Child) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); EXPECT_EQ( - m_rootEntity2->FindComponent()->IsHierarchicalChild(), + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), true ); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchyRoot(), + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), RootNetEntityId ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Doesnt_Keep_Child_References) { - m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 0 ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Top_Root) { - m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); // detach - m_rootEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); - if (m_rootEntity2->FindComponent()->GetHierarchicalEntities().size() == 3) + if (m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) { EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities()[0], - m_rootEntity2.get() + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root2->m_entity.get() ); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities()[1], - m_childEntity2.get() + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() ); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities()[2], - m_childOfChildEntity2.get() + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild2->m_entity.get() ); } } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); // detach - m_rootEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Top_Root_Deactivates) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); - StopAndDeleteEntity(m_rootEntity); + m_root.reset(); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Child_Of_Top_Root_Deactivates) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); - StopAndDeleteEntity(m_childEntity); + m_child.reset(); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Child_Of_Child_Deactivates) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); - StopAndDeleteEntity(m_childOfChildEntity); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_childOfChild.reset(); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); } @@ -886,108 +830,108 @@ namespace Multiplayer { for (int i = 0; i < 100; ++i) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); // detach - m_rootEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); } EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); } TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Detachment_Of_Child_Of_Child_In_Inner_Hierarchy) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); // detach - m_childOfChildEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 5 ); } TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Attachment_Of_Child_Of_Child_In_Inner_Hierarchy) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); // detach - m_childOfChildEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); // re-connect - m_childOfChildEntity2->FindComponent()->SetParent(m_childEntity2->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 6 ); } TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Child_Of_Child_Changed_Hierarchies) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); // detach - m_childOfChildEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); // connect to a different hierarchy - m_childOfChildEntity2->FindComponent()->SetParent(m_childEntity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 6 ); } TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Detachment_Of_Child_In_Inner_Hierarchy) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); // detach - m_childEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_child2->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 4 ); } TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Child_Changed_Hierarchies) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); // detach - m_childEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_child2->m_entity->FindComponent()->SetParent(AZ::EntityId()); // connect to a different hierarchy - m_childEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 6 ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_No_Child_References_After_All_Children_Moved_To_Another_Hierarchy) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); - m_childEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); // detach - m_rootEntity2->FindComponent()->SetParent(AZ::EntityId()); + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 1 ); } TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Child_Deactivated_Top_Root_Has_No_Child_Reference_To_It) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); - StopAndDeleteEntity(m_childEntity2); + m_child2.reset(); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 4 ); } @@ -999,18 +943,18 @@ namespace Multiplayer m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); // remake the top level hierarchy - m_childEntity->FindComponent()->SetParent(AZ::EntityId()); - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); - m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); @@ -1028,68 +972,56 @@ namespace Multiplayer { HierarchyTests::SetUp(); - m_rootEntity = AZStd::make_unique(AZ::EntityId(1), "root"); - m_childEntity = AZStd::make_unique(AZ::EntityId(2), "child"); - m_childOfChildEntity = AZStd::make_unique(AZ::EntityId(3), "child of child"); + m_root = AZStd::make_unique((1), "root", NetEntityId{ 1 }, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", NetEntityId{ 2 }, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", NetEntityId{ 3 }, EntityInfo::Role::None); - m_rootEntityInfo = AZStd::make_unique(*m_rootEntity.get(), NetEntityId{ 1 }, EntityInfo::Role::Root); - m_childEntityInfo = AZStd::make_unique(*m_childEntity.get(), NetEntityId{ 2 }, EntityInfo::Role::Child); - m_childOfChildEntityInfo = AZStd::make_unique(*m_childOfChildEntity.get(), NetEntityId{ 3 }, EntityInfo::Role::None); + CreateDeepHierarchy(*m_root, *m_child, *m_childOfChild); - CreateDeepHierarchy(*m_rootEntityInfo, *m_childEntityInfo, *m_childOfChildEntityInfo); - - m_childEntity->FindComponent()->SetParent(m_rootEntity->GetId()); - m_childOfChildEntity->FindComponent()->SetParent(m_childEntity->GetId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); // now the entities are under one hierarchy } void TearDown() override { - m_childOfChildEntityInfo.reset(); - m_childEntityInfo.reset(); - m_rootEntityInfo.reset(); - - StopAndDeleteEntity(m_childOfChildEntity); - StopAndDeleteEntity(m_childEntity); - StopAndDeleteEntity(m_rootEntity); + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); HierarchyTests::TearDown(); } - AZStd::unique_ptr m_rootEntity; - AZStd::unique_ptr m_childEntity; - AZStd::unique_ptr m_childOfChildEntity; - - AZStd::unique_ptr m_rootEntityInfo; - AZStd::unique_ptr m_childEntityInfo; - AZStd::unique_ptr m_childOfChildEntityInfo; + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; }; TEST_F(ServerMixedDeepHierarchyTests, Top_Root_Ignores_Non_Hierarchical_Entities) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); } TEST_F(ServerMixedDeepHierarchyTests, Detaching_Non_Hierarchical_Entity_Has_No_Effect_On_Top_Root) { - m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); } TEST_F(ServerMixedDeepHierarchyTests, Attaching_Non_Hierarchical_Entity_Has_No_Effect_On_Top_Root) { - m_childOfChildEntity->FindComponent()->SetParent(AZ::EntityId()); - m_childOfChildEntity->FindComponent()->SetParent(m_rootEntity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); } @@ -1105,73 +1037,165 @@ namespace Multiplayer { ServerMixedDeepHierarchyTests::SetUp(); - m_rootEntity2 = AZStd::make_unique(AZ::EntityId(4), "root 2"); - m_childEntity2 = AZStd::make_unique(AZ::EntityId(5), "child 2"); - m_childOfChildEntity2 = AZStd::make_unique(AZ::EntityId(6), "child of child 2"); + m_root2 = AZStd::make_unique((4), "root 2", NetEntityId{ 4 }, EntityInfo::Role::Root); + m_child2 = AZStd::make_unique((5), "child 2", NetEntityId{ 5 }, EntityInfo::Role::None); + m_childOfChild2 = AZStd::make_unique((6), "child of child 2", NetEntityId{ 6 }, EntityInfo::Role::Child); - m_rootEntityInfo2 = AZStd::make_unique(*m_rootEntity2.get(), NetEntityId{ 4 }, EntityInfo::Role::Root); - m_childEntityInfo2 = AZStd::make_unique(*m_childEntity2.get(), NetEntityId{ 5 }, EntityInfo::Role::None); - m_childOfChildEntityInfo2 = AZStd::make_unique(*m_childOfChildEntity2.get(), NetEntityId{ 6 }, EntityInfo::Role::Child); + CreateDeepHierarchy(*m_root2, *m_child2, *m_childOfChild2); - CreateDeepHierarchy(*m_rootEntityInfo2, *m_childEntityInfo2, *m_childOfChildEntityInfo2); - - m_childEntity2->FindComponent()->SetParent(m_rootEntity2->GetId()); - m_childOfChildEntity2->FindComponent()->SetParent(m_childEntity2->GetId()); + m_child2->m_entity->FindComponent()->SetParent(m_root2->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); // now the entities are under one hierarchy } void TearDown() override { - m_childOfChildEntityInfo2.reset(); - m_childEntityInfo2.reset(); - m_rootEntityInfo2.reset(); - - - StopAndDeleteEntity(m_childOfChildEntity2); - StopAndDeleteEntity(m_childEntity2); - StopAndDeleteEntity(m_rootEntity2); + m_childOfChild2.reset(); + m_child2.reset(); + m_root2.reset(); ServerMixedDeepHierarchyTests::TearDown(); } - AZStd::unique_ptr m_rootEntity2; - AZStd::unique_ptr m_childEntity2; - AZStd::unique_ptr m_childOfChildEntity2; - - AZStd::unique_ptr m_rootEntityInfo2; - AZStd::unique_ptr m_childEntityInfo2; - AZStd::unique_ptr m_childOfChildEntityInfo2; + AZStd::unique_ptr m_root2; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; }; TEST_F(ServerMixedHierarchyOfHierarchyTests, Sanity_Check_Ingore_Children_Without_Hierarchy_Components) { EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 2 ); EXPECT_EQ( - m_rootEntity2->FindComponent()->GetHierarchicalEntities().size(), + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), 1 ); } TEST_F(ServerMixedHierarchyOfHierarchyTests, Adding_Mixed_Hierarchy_Ingores_Children_Without_Hierarchy_Components) { - m_rootEntity2->FindComponent()->SetParent(m_rootEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( - m_rootEntity->FindComponent()->GetHierarchicalEntities().size(), + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), 3 ); } TEST_F(ServerMixedHierarchyOfHierarchyTests, Attaching_Hierarchy_To_Non_Hierarchical_Entity_Does_Not_Merge_Hierarchies) { - m_rootEntity2->FindComponent()->SetParent(m_childOfChildEntity->GetId()); + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); EXPECT_EQ( - m_rootEntity2->FindComponent()->IsHierarchicalChild(), + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), false ); } + + /* + * Sets up a hierarchy with 3 roots, 2 of them being inner roots. + */ + class ServerHierarchyWithThreeRoots : public ServerHierarchyOfHierarchyTests + { + public: + static const NetEntityId Root3NetEntityId = NetEntityId{ 7 }; + static const NetEntityId Child3NetEntityId = NetEntityId{ 8 }; + static const NetEntityId ChildOfChild3NetEntityId = NetEntityId{ 9 }; + + void SetUp() override + { + ServerHierarchyOfHierarchyTests::SetUp(); + + m_root3 = AZStd::make_unique((7), "root 3", Root3NetEntityId, EntityInfo::Role::Root); + m_child3 = AZStd::make_unique((8), "child 3", Child3NetEntityId, EntityInfo::Role::Child); + m_childOfChild3 = AZStd::make_unique((9), "child of child 3", ChildOfChild3NetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root3, *m_child3, *m_childOfChild3); + + m_child3->m_entity->FindComponent()->SetParent(m_root3->m_entity->GetId()); + m_childOfChild3->m_entity->FindComponent()->SetParent(m_child3->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild3.reset(); + m_child3.reset(); + m_root3.reset(); + + ServerHierarchyOfHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root3; + AZStd::unique_ptr m_child3; + AZStd::unique_ptr m_childOfChild3; + }; + + TEST_F(ServerHierarchyWithThreeRoots, Top_Root_Active_Then_Inner_Roots_Have_No_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_root.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Child_Of_Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Child_Of_Child_Of_Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_childOfChild.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } } From 9e31c3ebf53b6dd9b26c5187322b84a1ab2ac212 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Wed, 15 Sep 2021 18:41:08 -0400 Subject: [PATCH 16/63] Refactoring m_isDeactivating into m_isHierarchyEnabled Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Components/NetworkHierarchyChildComponent.h | 3 ++- .../Components/NetworkHierarchyRootComponent.h | 5 +++-- .../Components/NetworkHierarchyChildComponent.cpp | 14 +++++++------- .../Components/NetworkHierarchyRootComponent.cpp | 5 +++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h index 2effb18bb8..544fb3d6cb 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -78,7 +78,8 @@ namespace Multiplayer NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; - bool m_isDeactivating = false; + //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. + bool m_isHierarchyEnabled = true; void NotifyChildrenHierarchyDisbanded(); }; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 35f0da5965..4c9f94c004 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -93,7 +93,8 @@ namespace Multiplayer bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount); void SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity); - - bool m_isDeactivating = false; + + //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. + bool m_isHierarchyEnabled = true; }; } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index eff051ed93..6ee407990e 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -67,6 +67,8 @@ namespace Multiplayer void NetworkHierarchyChildComponent::OnActivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) { + m_isHierarchyEnabled = true; + HierarchyRootAddEvent(m_hierarchyRootNetIdChanged); NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); @@ -79,7 +81,7 @@ namespace Multiplayer void NetworkHierarchyChildComponent::OnDeactivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) { - m_isDeactivating = true; + m_isHierarchyEnabled = false; if (m_rootEntity) { @@ -96,7 +98,7 @@ namespace Multiplayer bool NetworkHierarchyChildComponent::IsHierarchyEnabled() const { - return !m_isDeactivating; + return m_isHierarchyEnabled; } bool NetworkHierarchyChildComponent::IsHierarchicalChild() const @@ -197,11 +199,9 @@ namespace Multiplayer } else { - if (!m_rootEntity) - { - m_rootEntity = nullptr; - m_networkHierarchyLeaveEvent.Signal(); - } + m_isHierarchyEnabled = false; + m_rootEntity = nullptr; + m_networkHierarchyLeaveEvent.Signal(); } } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index d2b45a8971..f704242f34 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -71,6 +71,7 @@ namespace Multiplayer void NetworkHierarchyRootComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { + m_isHierarchyEnabled = true; m_hierarchicalEntities.push_back(GetEntity()); NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); @@ -84,7 +85,7 @@ namespace Multiplayer void NetworkHierarchyRootComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { - m_isDeactivating = true; + m_isHierarchyEnabled = false; if (m_rootEntity) { @@ -121,7 +122,7 @@ namespace Multiplayer bool NetworkHierarchyRootComponent::IsHierarchyEnabled() const { - return !m_isDeactivating; + return m_isHierarchyEnabled; } bool NetworkHierarchyRootComponent::IsHierarchicalRoot() const From 2637fa45b8010537a1752ff84438479930f5b718 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Wed, 15 Sep 2021 18:43:56 -0400 Subject: [PATCH 17/63] Minor refactor to address CR Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Source/Components/NetworkHierarchyChildComponent.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index 6ee407990e..3124eccaa8 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -103,12 +103,7 @@ namespace Multiplayer bool NetworkHierarchyChildComponent::IsHierarchicalChild() const { - if (GetHierarchyRoot() != InvalidNetEntityId) - { - return true; - } - - return false; + return GetHierarchyRoot() != InvalidNetEntityId; } AZ::Entity* NetworkHierarchyChildComponent::GetHierarchicalRoot() const From 2be5854beb1fbe2398608e224895b23663dd0b06 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Wed, 15 Sep 2021 18:49:24 -0400 Subject: [PATCH 18/63] Cleanup Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Components/NetworkHierarchyRootComponent.cpp | 15 ++++----------- .../EntityReplication/EntityReplicator.cpp | 4 ++-- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index f704242f34..4a5e9ce8d2 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -89,7 +89,7 @@ namespace Multiplayer if (m_rootEntity) { - // tell parent to re-build the hierarchy + // Tell parent to re-build the hierarchy if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) { root->RebuildHierarchy(); @@ -97,8 +97,7 @@ namespace Multiplayer } else { - // notify children to that a hierarchy is disbanded - + // Notify children that the hierarchy is disbanding AZStd::vector allChildren; AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren); @@ -127,12 +126,7 @@ namespace Multiplayer bool NetworkHierarchyRootComponent::IsHierarchicalRoot() const { - if (GetHierarchyRoot() != InvalidNetEntityId) - { - return false; - } - - return true; + return GetHierarchyRoot() == InvalidNetEntityId; } bool NetworkHierarchyRootComponent::IsHierarchicalChild() const @@ -199,7 +193,7 @@ namespace Multiplayer } else { - // detached from parent + // Detached from parent RebuildHierarchy(); } } @@ -217,7 +211,6 @@ namespace Multiplayer bool hierarchyChanged = false; // Send out join and leave events. - for (AZ::Entity* currentEntity : m_hierarchicalEntities) { const auto prevEntityIterator = AZStd::find(previousEntities.begin(), previousEntities.end(), currentEntity); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index bd98d15923..b684473ea3 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -417,14 +417,14 @@ namespace Multiplayer if (hierarchyChildComponent == nullptr) { - // child and root hierarchy components are mutually exclusive + // Child and root hierarchy components are mutually exclusive hierarchyRootComponent = entity->FindComponent(); } if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchicalChild()) || (hierarchyRootComponent && hierarchyRootComponent->IsHierarchicalChild())) { - // if hierarchy is enabled for the entity, check if the parent is available + // If hierarchy is enabled for the entity, check if the parent is available if (const NetworkTransformComponent* networkTransform = entity->FindComponent()) { const NetEntityId parentId = networkTransform->GetParentEntityId(); From 880f4d96cc7773e2378bd051b1dcfb1ee37b9989 Mon Sep 17 00:00:00 2001 From: dmcdiar Date: Wed, 15 Sep 2021 18:19:39 -0700 Subject: [PATCH 19/63] Changed the ReflectionProbe component to use an OBB Signed-off-by: dmcdiar --- .../Atom/Features/PBR/DefaultObjectSrg.azsli | 37 ++++++++++++-- .../Atom/Features/PBR/LightingUtils.azsli | 11 +++- .../Atom/Features/PBR/Lights/Ibl.azsli | 16 +++--- .../ReflectionProbeBlendWeight.azsl | 10 ++-- .../ReflectionProbeRenderCommon.azsli | 10 ++-- .../ReflectionProbeRenderInner.azsl | 2 +- .../ReflectionProbeRenderObjectSrg.azsli | 23 ++++++--- .../ReflectionProbeRenderOuter.azsl | 12 +++-- .../Code/Source/Mesh/MeshFeatureProcessor.cpp | 28 +++++------ .../ReflectionProbe/ReflectionProbe.cpp | 50 +++++++++---------- .../Source/ReflectionProbe/ReflectionProbe.h | 20 ++++---- .../ReflectionProbeFeatureProcessor.cpp | 40 +++++++-------- .../RPI/Assets/ShaderLib/Atom/RPI/Math.azsli | 16 ++++++ .../EditorReflectionProbeComponent.cpp | 7 +-- 14 files changed, 172 insertions(+), 110 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli index ccaf5cf5d0..7a2d2ee428 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli @@ -40,11 +40,10 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject //! Reflection Probe (smallest probe volume that overlaps the object position) struct ReflectionProbeData { - float3 m_aabbPos; - float3 m_outerAabbMin; - float3 m_outerAabbMax; - float3 m_innerAabbMin; - float3 m_innerAabbMax; + row_major float3x4 m_modelToWorld; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; float m_padding; bool m_useReflectionProbe; bool m_useParallaxCorrection; @@ -52,4 +51,32 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject ReflectionProbeData m_reflectionProbeData; TextureCube m_reflectionProbeCubeMap; + + float4x4 GetReflectionProbeWorldMatrix() + { + float4x4 modelToWorld = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0]; + modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1]; + modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2]; + return modelToWorld; + } + + float4x4 GetReflectionProbeWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0]; + modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1]; + modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2]; + return modelToWorldInverse; + } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli index 503095020c..3248fc83eb 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli @@ -50,10 +50,10 @@ float GetRoughnessMip(float roughness) return roughness * maxRoughnessMip; } -// compute parallax corrected reflection vector +// compute parallax corrected reflection vector, AABB version // we do this by finding the intersection with the volume and adjusting the reflection vector for the surface position // https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ -float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir) +float3 ApplyParallaxCorrectionAABB(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir) { float3 rcpReflectDir = 1.0f / reflectDir; float3 intersectA = (aabbMax - positionWS) * rcpReflectDir; @@ -63,3 +63,10 @@ float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, f float3 intersectPos = reflectDir * distance + positionWS; return (intersectPos - aabbPos); } + +// compute parallax corrected reflection vector, OBB version +float3 ApplyParallaxCorrectionOBB(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 positionWS, float3 reflectDir) +{ + float4 p = mul(obbTransformInverse, float4(positionWS, 1.0f)); + return ApplyParallaxCorrectionAABB(-obbHalfExtents, obbHalfExtents, float3(0.0f, 0.0f, 0.0f), p, reflectDir); +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli index 5a0e150dbb..b1cfb47be6 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli @@ -48,10 +48,9 @@ float3 GetIblSpecular( { if (ObjectSrg::m_reflectionProbeData.m_useParallaxCorrection) { - reflectDir = ApplyParallaxCorrection( - ObjectSrg::m_reflectionProbeData.m_outerAabbMin, - ObjectSrg::m_reflectionProbeData.m_outerAabbMax, - ObjectSrg::m_reflectionProbeData.m_aabbPos, + reflectDir = ApplyParallaxCorrectionOBB( + ObjectSrg::GetReflectionProbeWorldMatrixInverse(), + ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths, position, reflectDir); } @@ -60,11 +59,10 @@ float3 GetIblSpecular( probeSpecular *= (specularF0 * brdf.x + brdf.y); // compute blend amount based on world position in the reflection probe volume - float blendAmount = ComputeLerpBetweenInnerOuterAABBs( - ObjectSrg::m_reflectionProbeData.m_innerAabbMin, - ObjectSrg::m_reflectionProbeData.m_innerAabbMax, - ObjectSrg::m_reflectionProbeData.m_outerAabbMax, - ObjectSrg::m_reflectionProbeData.m_aabbPos, + float blendAmount = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::m_reflectionProbeData.m_modelToWorldInverse, + ObjectSrg::m_reflectionProbeData.m_innerObbHalfLengths, + ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths, position); outSpecular = lerp(outSpecular, probeSpecular, blendAmount); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl index 095576caaf..0fca4d03c6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl @@ -63,7 +63,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // make sure the pixel belongs to this probe volume // this is necessary since it could have the correct stencil value but actually reside // in another volume that's in between the camera and the volume we're rendering - if (!AabbContainsPoint(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, positionWS)) { discard; } @@ -71,11 +71,15 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // determine blend based on position with respect to the inner and outer AABBs // if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB float blendWeight = 1.0f; - if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS)) { // not inside the inner AABB, so it's in between the inner and outer AABBs // compute blend amount based on the distance to the outer AABB - blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS); + blendWeight = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_innerObbHalfLengths, + ObjectSrg::m_outerObbHalfLengths, + positionWS); } // write the blend weight (additive) at this position for the probe volume diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli index f172035177..ce4684b150 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli @@ -12,12 +12,12 @@ #include // compute final probe specular using the probe cubemap and the roughness, normals, and specularF0 for the surface -bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin, float3 aabbMax, uint sampleIndex, out float3 specular) +bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float4x4 obbTransformInverse, float3 outerObbHalfLengths, uint sampleIndex, out float3 specular) { // make sure the pixel belongs to this probe volume // this is necessary since it could have the correct stencil value but actually reside // in another volume that's in between the camera and the volume we're rendering - if (!AabbContainsPoint(aabbMin, aabbMax, positionWS)) + if (!ObbContainsPoint(obbTransformInverse, outerObbHalfLengths, positionWS)) { return false; } @@ -47,7 +47,11 @@ bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin float3 localReflectDir = reflectDir; if (ObjectSrg::m_useParallaxCorrection) { - localReflectDir = ApplyParallaxCorrection(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS, reflectDir); + localReflectDir = ApplyParallaxCorrectionOBB( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_outerObbHalfLengths, + positionWS, + reflectDir); } // sample reflection cubemap with the appropriate roughness mip diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl index 209605be03..a0734caf02 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl @@ -75,7 +75,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface float3 specular = float3(0.0f, 0.0f, 0.0f); - if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, sampleIndex, specular)) + if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, sampleIndex, specular)) { discard; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli index 92ecd82dee..8151ed2fd5 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli @@ -13,12 +13,9 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject { row_major float3x4 m_modelToWorld; - - float3 m_aabbPos; - float3 m_outerAabbMin; - float3 m_outerAabbMax; - float3 m_innerAabbMin; - float3 m_innerAabbMax; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; bool m_useParallaxCorrection; TextureCube m_reflectionCubeMap; @@ -35,4 +32,18 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject modelToWorld[2] = ObjectSrg::m_modelToWorld[2]; return modelToWorld; } + + float4x4 GetWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = ObjectSrg::m_modelToWorldInverse[0]; + modelToWorldInverse[1] = ObjectSrg::m_modelToWorldInverse[1]; + modelToWorldInverse[2] = ObjectSrg::m_modelToWorldInverse[2]; + return modelToWorldInverse; + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl index be94a53588..ac97172f1f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl @@ -77,7 +77,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface float3 specular = float3(0.0f, 0.0f, 0.0f); - if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, sampleIndex, specular)) + if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, sampleIndex, specular)) { discard; } @@ -85,13 +85,17 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // determine blend based on position with respect to the inner and outer AABBs // if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB float blendWeight = 1.0f; - if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS)) { // not inside the inner AABB, so it's in between the inner and outer AABBs // compute blend amount based on the distance to the outer AABB - blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS); + blendWeight = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_innerObbHalfLengths, + ObjectSrg::m_outerObbHalfLengths, + positionWS); } - + // retrieve the blend weight of all probes at this location float blendWeightAllProbes = PassSrg::m_blendWeight.Load(IN.m_position.xy, sampleIndex).r; diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 67702a8176..41ccec0fb5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -1119,20 +1119,17 @@ namespace AZ if (reflectionProbeFeatureProcessor && (m_descriptor.m_useForwardPassIblSpecular || m_hasForwardPassIblSpecularMaterial)) { // retrieve probe constant indices - AZ::RHI::ShaderInputConstantIndex posConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_aabbPos")); - AZ_Error("MeshDataInstance", posConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex modelToWorldConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorld")); + AZ_Error("MeshDataInstance", modelToWorldConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex outerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMin")); - AZ_Error("MeshDataInstance", outerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex modelToWorldInverseConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorldInverse")); + AZ_Error("MeshDataInstance", modelToWorldInverseConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex outerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMax")); - AZ_Error("MeshDataInstance", outerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex outerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerObbHalfLengths")); + AZ_Error("MeshDataInstance", outerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex innerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMin")); - AZ_Error("MeshDataInstance", innerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - - AZ::RHI::ShaderInputConstantIndex innerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMax")); - AZ_Error("MeshDataInstance", innerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex innerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerObbHalfLengths")); + AZ_Error("MeshDataInstance", innerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); AZ::RHI::ShaderInputConstantIndex useReflectionProbeConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useReflectionProbe")); AZ_Error("MeshDataInstance", useReflectionProbeConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); @@ -1154,11 +1151,10 @@ namespace AZ if (!reflectionProbes.empty() && reflectionProbes[0]) { - m_shaderResourceGroup->SetConstant(posConstantIndex, reflectionProbes[0]->GetPosition()); - m_shaderResourceGroup->SetConstant(outerAabbMinConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMin()); - m_shaderResourceGroup->SetConstant(outerAabbMaxConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMax()); - m_shaderResourceGroup->SetConstant(innerAabbMinConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMin()); - m_shaderResourceGroup->SetConstant(innerAabbMaxConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMax()); + m_shaderResourceGroup->SetConstant(modelToWorldConstantIndex, reflectionProbes[0]->GetTransform()); + m_shaderResourceGroup->SetConstant(modelToWorldInverseConstantIndex, reflectionProbes[0]->GetTransform().GetInverse()); + m_shaderResourceGroup->SetConstant(outerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetOuterObbWs().GetHalfLengths()); + m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths()); m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true); m_shaderResourceGroup->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection()); diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index 66c326e6e7..2be2ec72c8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -138,43 +138,40 @@ namespace AZ if (m_updateSrg) { // stencil Srg - // Note: the stencil pass uses a slightly reduced inner AABB to avoid seams + // Note: the stencil pass uses a slightly reduced inner OBB to avoid seams Vector3 innerExtentsReduced = m_innerExtents - Vector3(0.1f, 0.1f, 0.1f); - Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced); + Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced); m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldStencilConstantIndex, modelToWorldStencil); + m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseStencilConstantIndex, modelToWorldStencil.GetInverseFull()); m_stencilSrg->Compile(); + Matrix3x4 modelToWorldInverse = Matrix3x4::CreateFromTransform(m_transform).GetInverseFull(); + // blend weight Srg - Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents); + Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_blendWeightSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_blendWeightSrg->Compile(); // render outer Srg m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_renderOuterSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_renderOuterSrg->Compile(); // render inner Srg - Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents); + Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldInner); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_renderInnerSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_renderInnerSrg->Compile(); @@ -244,22 +241,22 @@ namespace AZ m_outerExtents *= m_transform.GetUniformScale(); m_innerExtents *= m_transform.GetUniformScale(); - m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); - m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); + m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f); + m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f); m_updateSrg = true; } void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents) { m_outerExtents = outerExtents * m_transform.GetUniformScale(); - m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); + m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f); m_updateSrg = true; } void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents) { m_innerExtents = innerExtents * m_transform.GetUniformScale(); - m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); + m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f); m_updateSrg = true; } @@ -394,13 +391,14 @@ namespace AZ lod.m_screenCoverageMax = 1.0f; // update cullable bounds + Aabb outerAabb = Aabb::CreateFromObb(m_outerObbWs); Vector3 center; float radius; - m_outerAabbWs.GetAsSphere(center, radius); + outerAabb.GetAsSphere(center, radius); m_cullable.m_cullData.m_boundingSphere = Sphere(center, radius); - m_cullable.m_cullData.m_boundingObb = m_outerAabbWs.GetTransformedObb(AZ::Transform::CreateIdentity()); - m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = m_outerAabbWs; + m_cullable.m_cullData.m_boundingObb = m_outerObbWs; + m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = outerAabb; m_cullable.m_cullData.m_visibilityEntry.m_userData = &m_cullable; m_cullable.m_cullData.m_visibilityEntry.m_typeFlags = AzFramework::VisibilityEntry::TYPE_RPI_Cullable; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h index bb30ce9470..169c4f1df2 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h @@ -56,12 +56,11 @@ namespace AZ RHI::DrawListTag m_renderInnerDrawListTag; RHI::ShaderInputConstantIndex m_modelToWorldStencilConstantIndex; + RHI::ShaderInputConstantIndex m_modelToWorldInverseStencilConstantIndex; RHI::ShaderInputConstantIndex m_modelToWorldRenderConstantIndex; - RHI::ShaderInputConstantIndex m_aabbPosRenderConstantIndex; - RHI::ShaderInputConstantIndex m_outerAabbMinRenderConstantIndex; - RHI::ShaderInputConstantIndex m_outerAabbMaxRenderConstantIndex; - RHI::ShaderInputConstantIndex m_innerAabbMinRenderConstantIndex; - RHI::ShaderInputConstantIndex m_innerAabbMaxRenderConstantIndex; + RHI::ShaderInputConstantIndex m_modelToWorldInverseRenderConstantIndex; + RHI::ShaderInputConstantIndex m_outerObbHalfLengthsRenderConstantIndex; + RHI::ShaderInputConstantIndex m_innerObbHalfLengthsRenderConstantIndex; RHI::ShaderInputConstantIndex m_useParallaxCorrectionRenderConstantIndex; RHI::ShaderInputImageIndex m_reflectionCubeMapRenderImageIndex; }; @@ -78,6 +77,7 @@ namespace AZ void Simulate(uint32_t probeIndex); const Vector3& GetPosition() const { return m_transform.GetTranslation(); } + const AZ::Transform& GetTransform() const { return m_transform; } void SetTransform(const AZ::Transform& transform); const AZ::Vector3& GetOuterExtents() const { return m_outerExtents; } @@ -86,8 +86,8 @@ namespace AZ const AZ::Vector3& GetInnerExtents() const { return m_innerExtents; } void SetInnerExtents(const AZ::Vector3& innerExtents); - const Aabb& GetOuterAabbWs() const { return m_outerAabbWs; } - const Aabb& GetInnerAabbWs() const { return m_innerAabbWs; } + const Obb& GetOuterObbWs() const { return m_outerObbWs; } + const Obb& GetInnerObbWs() const { return m_innerObbWs; } const Data::Instance& GetCubeMapImage() const { return m_cubeMapImage; } void SetCubeMapImage(const Data::Instance& cubeMapImage, const AZStd::string& relativePath); @@ -133,9 +133,9 @@ namespace AZ AZ::Vector3 m_outerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f); AZ::Vector3 m_innerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f); - // probe volume AABBs (world space), built from position and extents - Aabb m_outerAabbWs; - Aabb m_innerAabbWs; + // probe volume OBBs (world space), built from position and extents + Obb m_outerObbWs; + Obb m_innerObbWs; // cubemap Data::Instance m_cubeMapImage; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 341d1a0274..5c07fbae66 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -92,6 +92,10 @@ namespace AZ m_reflectionRenderData.m_modelToWorldStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldConstantName.GetCStr()); + Name modelToWorldInverseConstantName = Name("m_modelToWorldInverse"); + m_reflectionRenderData.m_modelToWorldInverseStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldInverseConstantName); + AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldInverseStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldInverseConstantName.GetCStr()); + // cache probe render shader indices // Note: the outer and inner render shaders use the same Srg Data::Instance renderReflectionSrg = RPI::ShaderResourceGroup::Create( @@ -104,25 +108,16 @@ namespace AZ m_reflectionRenderData.m_modelToWorldRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldConstantName.GetCStr()); - Name aabbPosConstantName = Name("m_aabbPos"); - m_reflectionRenderData.m_aabbPosRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(aabbPosConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_aabbPosRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", aabbPosConstantName.GetCStr()); + m_reflectionRenderData.m_modelToWorldInverseRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldInverseConstantName); + AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldInverseConstantName.GetCStr()); - Name outerAabbMinConstantName = Name("m_outerAabbMin"); - m_reflectionRenderData.m_outerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMinConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMinConstantName.GetCStr()); + Name outerObbHalfLengthsConstantName = Name("m_outerObbHalfLengths"); + m_reflectionRenderData.m_outerObbHalfLengthsRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerObbHalfLengthsConstantName); + AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerObbHalfLengthsRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerObbHalfLengthsConstantName.GetCStr()); - Name outerAabbMaxConstantName = Name("m_outerAabbMax"); - m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMaxConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMaxConstantName.GetCStr()); - - Name innerAabbMinConstantName = Name("m_innerAabbMin"); - m_reflectionRenderData.m_innerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMinConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMinConstantName.GetCStr()); - - Name innerAabbMaxConstantName = Name("m_innerAabbMax"); - m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMaxConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMaxConstantName.GetCStr()); + Name innerObbHalfLengthsConstantName = Name("m_innerObbHalfLengths"); + m_reflectionRenderData.m_innerObbHalfLengthsRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerObbHalfLengthsConstantName); + AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerObbHalfLengthsRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerObbHalfLengthsConstantName.GetCStr()); Name useParallaxCorrectionConstantName = Name("m_useParallaxCorrection"); m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(useParallaxCorrectionConstantName); @@ -199,10 +194,11 @@ namespace AZ // sort the probes by descending inner volume size, so the smallest volumes are rendered last auto sortFn = [](AZStd::shared_ptr const& probe1, AZStd::shared_ptr const& probe2) -> bool { - const Aabb& aabb1 = probe1->GetInnerAabbWs(); - const Aabb& aabb2 = probe2->GetInnerAabbWs(); - float size1 = aabb1.GetXExtent() * aabb1.GetZExtent() * aabb1.GetYExtent(); - float size2 = aabb2.GetXExtent() * aabb2.GetZExtent() * aabb2.GetYExtent(); + const Obb& obb1 = probe1->GetInnerObbWs(); + const Obb& obb2 = probe2->GetInnerObbWs(); + float size1 = obb1.GetHalfLengthX() * obb1.GetHalfLengthZ() * obb1.GetHalfLengthY(); + float size2 = obb2.GetHalfLengthX() * obb2.GetHalfLengthZ() * obb2.GetHalfLengthY(); + return (size1 > size2); }; @@ -347,7 +343,7 @@ namespace AZ // simple AABB check to find the reflection probes that contain the position for (auto& reflectionProbe : m_reflectionProbes) { - if (reflectionProbe->GetOuterAabbWs().Contains(position) + if (reflectionProbe->GetOuterObbWs().Contains(position) && reflectionProbe->GetCubeMapImage() && reflectionProbe->GetCubeMapImage()->IsInitialized()) { diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli index a94e01e11b..b004623c4e 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli @@ -116,6 +116,22 @@ float ComputeLerpBetweenInnerOuterAABBs(float3 innerAabbMin, float3 innerAabbMax return totalDistance > 0.0f ? saturate(shortestDistance / totalDistance) : 1.0f; } +// returns true if the Obb contains the specified point +bool ObbContainsPoint(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 testPoint) +{ + // get the position in Obb local space, force to positive quadrant with abs() + float4 p = abs(mul(obbTransformInverse, float4(testPoint, 1.0f))); + return AabbContainsPoint(-obbHalfExtents, obbHalfExtents, p); +} + +// computes [0..1] percentage of a point that's in between the inner and outer OBBs +float ComputeLerpBetweenInnerOuterOBBs(float3x4 obbTransformInverse, float3 innerObbHalfExtents, float3 outerObbHalfExtents, float3 position) +{ + // get the position in Obb local space, force to positive quadrant with abs() + float3 p = abs(mul(obbTransformInverse, float4(position, 1.0f))); + return ComputeLerpBetweenInnerOuterAABBs(-innerObbHalfExtents, innerObbHalfExtents, outerObbHalfExtents, float3(0.0f, 0.0f, 0.0f), p); +} + // ---------- Normal Encoding ----------- // Encode/Decode functions for Signed Octahedron normals diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index 4f4f21ab0f..aa68333108 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -212,6 +212,9 @@ namespace AZ AZ::Vector3 position = AZ::Vector3::CreateZero(); AZ::TransformBus::EventResult(position, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation); + AZ::Quaternion rotationQuaternion = AZ::Quaternion::CreateIdentity(); + AZ::TransformBus::EventResult(rotationQuaternion, GetEntityId(), &AZ::TransformBus::Events::GetWorldRotationQuaternion); + AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateFromQuaternion(rotationQuaternion); float scale = 1.0f; AZ::TransformBus::EventResult(scale, GetEntityId(), &AZ::TransformBus::Events::GetLocalUniformScale); @@ -224,9 +227,7 @@ namespace AZ AZ::Vector3 innerExtents(configuration.m_innerWidth, configuration.m_innerLength, configuration.m_innerHeight); innerExtents *= scale; - AZ::Vector3 innerMin(position.GetX() - innerExtents.GetX() / 2, position.GetY() - innerExtents.GetY() / 2, position.GetZ() - innerExtents.GetZ() / 2); - AZ::Vector3 innerMax(position.GetX() + innerExtents.GetX() / 2, position.GetY() + innerExtents.GetY() / 2, position.GetZ() + innerExtents.GetZ() / 2); - debugDisplay.DrawWireBox(innerMin, innerMax); + debugDisplay.DrawWireOBB(position, rotationMatrix.GetBasisX(), rotationMatrix.GetBasisY(), rotationMatrix.GetBasisZ(), innerExtents / 2.0f); } AZ::Aabb EditorReflectionProbeComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) From a3dfb8869691abf341dd27ac50dead1a890b6c52 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 16 Sep 2021 17:48:53 +0100 Subject: [PATCH 20/63] Added support for instantiating network spawnables for any spawnables, not just root spawnable Signed-off-by: pereslav --- .../Spawnable/SpawnableEntitiesInterface.h | 9 +++ .../Spawnable/SpawnableEntitiesManager.cpp | 20 +++++ .../Spawnable/SpawnableEntitiesManager.h | 6 ++ .../Mocks/MockSpawnableEntitiesInterface.h | 2 + .../NetworkEntity/NetworkEntityManager.cpp | 79 ++++++++++++++----- .../NetworkEntity/NetworkEntityManager.h | 12 ++- 6 files changed, 105 insertions(+), 23 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 4b09dcbc75..254df8f1b0 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -320,6 +320,15 @@ namespace AzFramework //! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs. virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0; + //! Register a handler for OnSpawned events. + //! @param handler The handler to receive the event. + virtual void AddOnSpawnedHandler(AZ::Event, + const AZStd::vector&, const void*>::Handler& handler) = 0; + + //! Register a handler for OnDespawned events. + //! @param handler The handler to receive the event. + virtual void AddOnDespawnedHandler(AZ::Event, const void*>::Handler& handler) = 0; + protected: [[nodiscard]] virtual AZStd::pair CreateTicket(AZ::Data::Asset&& spawnable) = 0; virtual void DestroyTicket(void* ticket) = 0; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 5fa072a451..f052f83224 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -146,6 +146,16 @@ namespace AzFramework QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } + void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event, const AZStd::vector&, const void*>::Handler& handler) + { + handler.Connect(m_onSpawnedEvent); + } + + void SpawnableEntitiesManager::AddOnDespawnedHandler(AZ::Event, const void*>::Handler& handler) + { + handler.Connect(m_onDespawnedEvent); + } + auto SpawnableEntitiesManager::ProcessQueue(CommandQueuePriority priority) -> CommandQueueStatus { CommandQueueStatus result = CommandQueueStatus::NoCommandsLeft; @@ -349,6 +359,8 @@ namespace AzFramework ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); } + m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket); + ticket.m_currentRequestId++; return true; } @@ -429,6 +441,8 @@ namespace AzFramework ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); } + m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket); + ticket.m_currentRequestId++; return true; } @@ -460,6 +474,8 @@ namespace AzFramework request.m_completionCallback(request.m_ticketId); } + m_onDespawnedEvent.Signal(ticket.m_spawnable, &ticket); + ticket.m_currentRequestId++; return true; } @@ -487,6 +503,8 @@ namespace AzFramework } } + m_onDespawnedEvent.Signal(ticket.m_spawnable, &ticket); + // Rebuild the list of entities. ticket.m_spawnedEntities.clear(); const Spawnable::EntityList& entities = request.m_spawnable->GetEntities(); @@ -544,6 +562,8 @@ namespace AzFramework ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end())); } + m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket); + ticket.m_currentRequestId++; return true; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index 69985f8aa9..c78b1778a4 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -69,6 +69,9 @@ namespace AzFramework void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) override; + void AddOnSpawnedHandler(AZ::Event, const AZStd::vector&, const void*>::Handler& handler) override; + void AddOnDespawnedHandler(AZ::Event, const void*>::Handler& handler) override; + // // The following function is thread safe but intended to be run from the main thread. // @@ -224,6 +227,9 @@ namespace AzFramework //! SpawnablePriority_Default which gives users a bit of room to fine tune the priorities as this value can be configured //! through the Settings Registry under the key "/O3DE/AzFramework/Spawnables/HighPriorityThreshold". SpawnablePriority m_highPriorityThreshold { 64 }; + + AZ::Event, const AZStd::vector&, const void*> m_onSpawnedEvent; + AZ::Event, const void*> m_onDespawnedEvent; }; AZ_DEFINE_ENUM_BITWISE_OPERATORS(AzFramework::SpawnableEntitiesManager::CommandQueuePriority); diff --git a/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h b/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h index a5df7af4c3..89e79ecf9b 100644 --- a/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h @@ -59,6 +59,8 @@ namespace AzFramework MOCK_METHOD1(CreateTicket, AZStd::pair(AZ::Data::Asset&& spawnable)); MOCK_METHOD1(DestroyTicket, void(void* ticket)); + MOCK_METHOD1(AddOnSpawnedHandler, void(AZ::Event, const AZStd::vector&, const void*>::Handler& handler)); + MOCK_METHOD1(AddOnDespawnedHandler, void(AZ::Event, const void*>::Handler& handler)); /** Installs some default result values for the above functions. * Note that you can always override these in scope of your test by adding additional ON_CALL / EXPECT_CALL diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 793acc17ff..62b57d9603 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -30,9 +30,14 @@ namespace Multiplayer : m_networkEntityAuthorityTracker(*this) , m_removeEntitiesEvent([this] { RemoveEntities(); }, AZ::Name("NetworkEntityManager remove entities event")) , m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event")) + , m_onSpawnedHandler([this](AZ::Data::Asset spawnable, const AZStd::vector& entities, const void* spawnTicket) { this->OnSpawned(spawnable, entities, spawnTicket); }) + , m_onDespawnedHandler([this](AZ::Data::Asset spawnable, const void* spawnTicket) { this->OnDespawned(spawnable, spawnTicket); }) { AZ::Interface::Register(this); AzFramework::RootSpawnableNotificationBus::Handler::BusConnect(); + + AzFramework::SpawnableEntitiesInterface::Get()->AddOnSpawnedHandler(m_onSpawnedHandler); + AzFramework::SpawnableEntitiesInterface::Get()->AddOnDespawnedHandler(m_onDespawnedHandler); } NetworkEntityManager::~NetworkEntityManager() @@ -467,30 +472,39 @@ namespace Multiplayer return netEntityId; } - AzFramework::EntitySpawnTicket NetworkEntityManager::RequestNetSpawnableInstantiation(const AZ::Data::Asset& rootSpawnable) + AZStd::unique_ptr NetworkEntityManager::RequestNetSpawnableInstantiation( + const AZ::Data::Asset& rootSpawnable, const AZStd::vector& entities) { - const AzFramework::Spawnable::EntityList& entityList = rootSpawnable->GetEntities(); - if (entityList.empty()) + if (entities.empty()) { AZ_Error("NetworkEntityManager", false, "RequestNetSpawnableInstantiation: No entities in the spawnable %s", rootSpawnable.GetHint().c_str()); - return {}; + return nullptr; } // The first entity in every spawnable is the root one - const AZ::Entity* rootEntity = (entityList.begin())->get(); + const AZ::Entity* rootEntity = *entities.begin(); if(!rootEntity) { AZ_Error("NetworkEntityManager", false, "RequestNetSpawnableInstantiation: Root entity is null in the spawnable %s", rootSpawnable.GetHint().c_str()); - return {}; + return nullptr; } const auto* holderComponent = rootEntity->FindComponent(); if(!holderComponent) { // This spawnable doesn't have a corresponding network spawnable. - return {}; + return nullptr; + } + + AzFramework::TransformComponent* rootEntityTransform = + rootEntity->FindComponent(); + if (!rootEntityTransform) + { + AZ_Error("NetworkEntityManager", false, + "RequestNetSpawnableInstantiation: Root entity has no transform in the spawnable %s", rootSpawnable.GetHint().c_str()); + return nullptr; } // Retrieve the corresponding network spawnable asset @@ -504,14 +518,26 @@ namespace Multiplayer AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnableAsset.GetId()); // Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene - optionalArgs.m_preInsertionCallback = [netSpawnableName](AzFramework::EntitySpawnTicket::Id, - AzFramework::SpawnableEntityContainerView entities) + optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = rootEntityTransform->GetWorldTM()] + (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) { + bool shouldUpdateTransform = (rootTransform.IsClose(AZ::Transform::Identity()) == false); + for (uint32_t netEntityIndex = 0, entitiesSize = aznumeric_cast(entities.size()); netEntityIndex < entitiesSize; netEntityIndex++) { AZ::Entity* netEntity = *(entities.begin() + netEntityIndex); + if(shouldUpdateTransform) + { + AzFramework::TransformComponent* netEntityTransform = + netEntity->FindComponent(); + + AZ::Transform worldTm = netEntityTransform->GetWorldTM(); + worldTm = rootTransform * worldTm; + netEntityTransform->SetWorldTM(worldTm); + } + PrefabEntityId prefabEntityId; prefabEntityId.m_prefabName = netSpawnableName; prefabEntityId.m_entityOffset = netEntityIndex; @@ -520,13 +546,13 @@ namespace Multiplayer }; // Spawn with the newly created ticket. This allows the calling code to manage the lifetime of the constructed entities - AzFramework::EntitySpawnTicket ticket(netSpawnableAsset); - AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(ticket, AZStd::move(optionalArgs)); + auto ticket = AZStd::make_unique(netSpawnableAsset); + AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*ticket, AZStd::move(optionalArgs)); return ticket; } - void NetworkEntityManager::OnRootSpawnableAssigned( - [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) + void NetworkEntityManager::OnRootSpawnableAssigned(AZ::Data::Asset rootSpawnable, + [[maybe_unused]] uint32_t generation) { auto* multiplayer = GetMultiplayer(); const auto agentType = multiplayer->GetAgentType(); @@ -535,11 +561,6 @@ namespace Multiplayer { multiplayer->SendReadyForEntityUpdates(true); } - - if(ShouldSpawnNetEntities()) - { - m_rootNetSpawnableTicket = RequestNetSpawnableInstantiation(rootSpawnable); - } } void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) @@ -552,13 +573,29 @@ namespace Multiplayer multiplayer->SendReadyForEntityUpdates(false); } - // Despawn any remaining net entities created locally - if (m_rootNetSpawnableTicket.IsValid()) + m_netSpawnableTickets.clear(); + } + + void NetworkEntityManager::OnSpawned(AZ::Data::Asset spawnable, + const AZStd::vector& entities, const void* spawnTicket) + { + if (ShouldSpawnNetEntities()) { - AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_rootNetSpawnableTicket); + AZStd::unique_ptr ticket = RequestNetSpawnableInstantiation(spawnable, entities); + if(ticket) + { + AZ::Data::AssetId spawnableAssetId = spawnable.GetId(); + m_netSpawnableTickets[spawnTicket] = AZStd::move(ticket); + } } } + void NetworkEntityManager::OnDespawned([[maybe_unused]] AZ::Data::Asset spawnable, + const void* spawnTicket) + { + m_netSpawnableTickets.erase(spawnTicket); + } + void NetworkEntityManager::SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) { auto* netBindComponent = netEntity->FindComponent(); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index a5956b448f..14efa4feeb 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -94,10 +94,14 @@ namespace Multiplayer void RemoveEntities(); NetEntityId NextId(); bool ShouldSpawnNetEntities() const; + void OnSpawned(AZ::Data::Asset spawnable, + const AZStd::vector& entities, const void* spawnTicket); + void OnDespawned(AZ::Data::Asset spawnable, const void* spawnTicket); // Note: This is an async function. // The instantiated entities are not available immediately but will be constructed by the spawnable system - AzFramework::EntitySpawnTicket RequestNetSpawnableInstantiation(const AZ::Data::Asset& rootSpawnable); + AZStd::unique_ptr RequestNetSpawnableInstantiation( + const AZ::Data::Asset& rootSpawnable, const AZStd::vector& entities); NetworkEntityTracker m_networkEntityTracker; NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker; @@ -126,6 +130,10 @@ namespace Multiplayer DeferredRpcMessages m_localDeferredRpcMessages; NetworkSpawnableLibrary m_networkPrefabLibrary; - AzFramework::EntitySpawnTicket m_rootNetSpawnableTicket; + + AZStd::unordered_map> m_netSpawnableTickets; + + AZ::Event, const AZStd::vector&, const void*>::Handler m_onSpawnedHandler; + AZ::Event, const void*>::Handler m_onDespawnedHandler; }; } From 0ed82e21d52e9c18da38d026ba9d75ee0cd3fbc6 Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 16 Sep 2021 16:39:59 -0700 Subject: [PATCH 21/63] Switch job dependency type to OrderOnce for material assets when property names are included. Signed-off-by: Robin --- .../Source/RPI.Builders/Material/MaterialBuilder.cpp | 7 ++++++- .../Model/MaterialAssetBuilderComponent.cpp | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index 5ddeb08ef8..099fdf33dd 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -9,6 +9,7 @@ #include "MaterialBuilder.h" #include #include +#include #include #include #include @@ -93,8 +94,12 @@ namespace AZ { AssetBuilderSDK::JobDependency jobDependency; jobDependency.m_jobKey = jobKey; - jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order; jobDependency.m_sourceFile.m_sourceFileDependencyPath = file; + + bool includeMaterialPropertyNames = true; + RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); + jobDependency.m_type = includeMaterialPropertyNames ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; + jobDependencies.push_back(jobDependency); } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp index cf7e53c596..b2c590a376 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp @@ -78,10 +78,7 @@ namespace AZ AZStd::string materialTypePath; RPI::MaterialConverterBus::BroadcastResult(materialTypePath, &RPI::MaterialConverterBus::Events::GetMaterialTypePath); - bool includeMaterialPropertyNames = true; - RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); - // TODO: Use includeMaterialPropertyNames to break materialtype dependency on fbx files. Materialasset's dependency on materialtypeasset will need to be decoupled first - if (conversionEnabled && !materialTypePath.empty() /*&& !includeMaterialPropertyNames*/) + if (conversionEnabled && !materialTypePath.empty()) { AssetBuilderSDK::SourceFileDependency materialTypeSource; materialTypeSource.m_sourceFileDependencyPath = materialTypePath; @@ -90,7 +87,10 @@ namespace AZ jobDependency.m_jobKey = "Atom Material Builder"; jobDependency.m_sourceFile = materialTypeSource; jobDependency.m_platformIdentifier = platformIdentifier; - jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order; + + bool includeMaterialPropertyNames = true; + RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); + jobDependency.m_type = includeMaterialPropertyNames ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; jobDependencyList.push_back(jobDependency); } From 454ae90afb9124d2f0b126ea51bb20cb305db78e Mon Sep 17 00:00:00 2001 From: pereslav Date: Fri, 17 Sep 2021 17:15:21 +0100 Subject: [PATCH 22/63] Added a few whitespaces Signed-off-by: pereslav --- .../Code/Source/NetworkEntity/NetworkEntityManager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 62b57d9603..61fd741dc6 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -484,7 +484,7 @@ namespace Multiplayer // The first entity in every spawnable is the root one const AZ::Entity* rootEntity = *entities.begin(); - if(!rootEntity) + if (!rootEntity) { AZ_Error("NetworkEntityManager", false, "RequestNetSpawnableInstantiation: Root entity is null in the spawnable %s", rootSpawnable.GetHint().c_str()); @@ -492,7 +492,7 @@ namespace Multiplayer } const auto* holderComponent = rootEntity->FindComponent(); - if(!holderComponent) + if (!holderComponent) { // This spawnable doesn't have a corresponding network spawnable. return nullptr; @@ -524,11 +524,11 @@ namespace Multiplayer bool shouldUpdateTransform = (rootTransform.IsClose(AZ::Transform::Identity()) == false); for (uint32_t netEntityIndex = 0, entitiesSize = aznumeric_cast(entities.size()); - netEntityIndex < entitiesSize; netEntityIndex++) + netEntityIndex < entitiesSize; ++netEntityIndex) { AZ::Entity* netEntity = *(entities.begin() + netEntityIndex); - if(shouldUpdateTransform) + if (shouldUpdateTransform) { AzFramework::TransformComponent* netEntityTransform = netEntity->FindComponent(); @@ -582,7 +582,7 @@ namespace Multiplayer if (ShouldSpawnNetEntities()) { AZStd::unique_ptr ticket = RequestNetSpawnableInstantiation(spawnable, entities); - if(ticket) + if (ticket) { AZ::Data::AssetId spawnableAssetId = spawnable.GetId(); m_netSpawnableTickets[spawnTicket] = AZStd::move(ticket); From 78148ead7c14921a51d290943f72a70242c61c90 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Mon, 20 Sep 2021 11:56:40 -0400 Subject: [PATCH 23/63] Unittests for IsHierarchyEnabled Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Code/Tests/ServerHierarchyTests.cpp | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp index 05e36e466d..c262782006 100644 --- a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -159,6 +159,42 @@ namespace Multiplayer ); } + TEST_F(ServerSimpleHierarchyTests, Root_Deactivates_IsHierarchyEnabled_Is_False) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchyEnabled(), + true + ); + + StopEntity(m_root->m_entity); + m_root->m_entity->Deactivate(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchyEnabled(), + false + ); + + m_root->m_entity.reset(); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Deactivates_IsHierarchyEnabled_Is_False) + { + EXPECT_EQ( + m_child->m_entity->FindComponent()->IsHierarchyEnabled(), + true + ); + + StopEntity(m_child->m_entity); + m_child->m_entity->Deactivate(); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->IsHierarchyEnabled(), + false + ); + + m_child->m_entity.reset(); + } + /* * Parent -> Child -> ChildOfChild */ From 2bd01f09fb0b8570089524bb2549080123abe277 Mon Sep 17 00:00:00 2001 From: dmcdiar Date: Mon, 20 Sep 2021 11:37:17 -0700 Subject: [PATCH 24/63] Removed modelToWorldInverse from the stencil Srg. Signed-off-by: dmcdiar --- .../Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp | 1 - .../Common/Code/Source/ReflectionProbe/ReflectionProbe.h | 1 - .../ReflectionProbe/ReflectionProbeFeatureProcessor.cpp | 5 +---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index 1c1766073b..e86d91d387 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -142,7 +142,6 @@ namespace AZ Vector3 innerExtentsReduced = m_innerExtents - Vector3(0.1f, 0.1f, 0.1f); Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced); m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldStencilConstantIndex, modelToWorldStencil); - m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseStencilConstantIndex, modelToWorldStencil.GetInverseFull()); m_stencilSrg->Compile(); Matrix3x4 modelToWorldInverse = Matrix3x4::CreateFromTransform(m_transform).GetInverseFull(); diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h index 169c4f1df2..4f6a3337be 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h @@ -56,7 +56,6 @@ namespace AZ RHI::DrawListTag m_renderInnerDrawListTag; RHI::ShaderInputConstantIndex m_modelToWorldStencilConstantIndex; - RHI::ShaderInputConstantIndex m_modelToWorldInverseStencilConstantIndex; RHI::ShaderInputConstantIndex m_modelToWorldRenderConstantIndex; RHI::ShaderInputConstantIndex m_modelToWorldInverseRenderConstantIndex; RHI::ShaderInputConstantIndex m_outerObbHalfLengthsRenderConstantIndex; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 5c07fbae66..09f75c07a6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -92,10 +92,6 @@ namespace AZ m_reflectionRenderData.m_modelToWorldStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldConstantName.GetCStr()); - Name modelToWorldInverseConstantName = Name("m_modelToWorldInverse"); - m_reflectionRenderData.m_modelToWorldInverseStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldInverseConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldInverseStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldInverseConstantName.GetCStr()); - // cache probe render shader indices // Note: the outer and inner render shaders use the same Srg Data::Instance renderReflectionSrg = RPI::ShaderResourceGroup::Create( @@ -108,6 +104,7 @@ namespace AZ m_reflectionRenderData.m_modelToWorldRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldConstantName.GetCStr()); + Name modelToWorldInverseConstantName = Name("m_modelToWorldInverse"); m_reflectionRenderData.m_modelToWorldInverseRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldInverseConstantName); AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldInverseConstantName.GetCStr()); From 594981f1304dc0fc4e6c06c55deb183e01b25d74 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Mon, 20 Sep 2021 11:58:31 -0700 Subject: [PATCH 25/63] Remove the PrefabEditManager. Introduce the FocusMode system on the Editor side, and a PrefabFocusHandler on the Prefab side to handle focus. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Application/ToolsApplication.cpp | 2 + .../AzToolsFrameworkModule.cpp | 2 + .../FocusMode/FocusModeInterface.h | 40 +++++++ .../FocusMode/FocusModeSystemComponent.cpp | 93 ++++++++++++++++ .../FocusMode/FocusModeSystemComponent.h | 49 +++++++++ .../Prefab/PrefabFocusHandler.cpp | 103 ++++++++++++++++++ .../Prefab/PrefabFocusHandler.h | 56 ++++++++++ .../Prefab/PrefabFocusInterface.h | 40 +++++++ .../Prefab/PrefabSystemComponent.cpp | 1 + .../Prefab/PrefabSystemComponent.h | 12 +- .../UI/Prefab/LevelRootUiHandler.cpp | 9 -- .../UI/Prefab/LevelRootUiHandler.h | 2 - .../UI/Prefab/PrefabEditInterface.h | 43 -------- .../UI/Prefab/PrefabEditManager.cpp | 46 -------- .../UI/Prefab/PrefabEditManager.h | 40 ------- .../UI/Prefab/PrefabIntegrationManager.cpp | 23 ++-- .../UI/Prefab/PrefabIntegrationManager.h | 12 +- .../UI/Prefab/PrefabUiHandler.cpp | 24 ++-- .../UI/Prefab/PrefabUiHandler.h | 5 +- .../aztoolsframework_files.cmake | 9 +- 20 files changed, 430 insertions(+), 181 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h delete mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditInterface.h delete mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.cpp delete mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.h diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index 75d31321c2..7238a22c51 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,7 @@ namespace AzToolsFramework components.insert(components.end(), { azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp index 97f52e253b..a557754437 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,7 @@ namespace AzToolsFramework Components::EditorSelectionAccentSystemComponent::CreateDescriptor(), EditorEntityContextComponent::CreateDescriptor(), EditorEntityFixupComponent::CreateDescriptor(), + FocusModeFramework::FocusModeSystemComponent::CreateDescriptor(), SliceMetadataEntityContextComponent::CreateDescriptor(), SliceRequestComponent::CreateDescriptor(), Prefab::PrefabSystemComponent::CreateDescriptor(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h new file mode 100644 index 0000000000..edd94fd6b0 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of + * this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + + +namespace AzToolsFramework::FocusModeFramework +{ + /*! + * FocusModeInterface + * Interface to handle the Editor Focus Mode. + */ + class FocusModeInterface + { + public: + AZ_RTTI(FocusModeInterface, "{437243B0-F86B-422F-B7B8-4A21CC000702}"); + + //! Sets the root entity the Editor should focus on. + //! The Editor will only allow the user to select entities belonging to the sub-tree that has root in the entityId provided. + //! @param entityId The entityId that will become the new focus root. + virtual void SetFocusRoot(AZ::EntityId entityId) = 0; + + //! Returns the entity id of the root of the current Editor focus. + //! @return The id of the entity that is the root of the Editor focus. + virtual AZ::EntityId GetFocusRoot() = 0; + + //! Returns whether the entity id provided is part of the focused sub-tree. + //! @return True if entityId belongs to the focused sub-tree, false otherwise. + virtual bool IsInFocusSubTree(AZ::EntityId entityId) = 0; + }; + +} // namespace AzToolsFramework::FocusModeFrameworkclassPrefabEditPublicInterface diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp new file mode 100644 index 0000000000..3ad2dbdfb8 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of + * this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +namespace AzToolsFramework::FocusModeFramework +{ + FocusModeSystemComponent::~FocusModeSystemComponent() + { + AZ::Interface::Unregister(this); + } + + void FocusModeSystemComponent::Init() + { + AZ::Interface::Register(this); + } + + void FocusModeSystemComponent::Activate() + { + } + + void FocusModeSystemComponent::Deactivate() + { + } + + void FocusModeSystemComponent::Reflect(AZ::ReflectContext* /*context*/) + { + } + + void FocusModeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("EditorFocusMode")); + } + + void FocusModeSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void FocusModeSystemComponent::GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + } + + void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId) + { + m_focusRoot = entityId; + + // TODO - If m_focusRoot != AZ::EntityId(), register focus mode + // Else, unregister focus mode + } + + AZ::EntityId FocusModeSystemComponent::GetFocusRoot() + { + return m_focusRoot; + } + + bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) + { + if (m_focusRoot == AZ::EntityId()) + { + return true; + } + + return IsInFocusSubTree_helper(entityId, m_focusRoot); + } + + bool FocusModeSystemComponent::IsInFocusSubTree_helper(AZ::EntityId entityId, AZ::EntityId focusRootId) + { + if (entityId == AZ::EntityId()) + { + return false; + } + + if (entityId == focusRootId) + { + return true; + } + + AZ::EntityId parentId; + AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformInterface::GetParentId); + + return IsInFocusSubTree_helper(parentId, focusRootId); + } + +} // namespace AzToolsFramework::FocusModeFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h new file mode 100644 index 0000000000..54312a1e92 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of + * this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +#include + +namespace AzToolsFramework::FocusModeFramework +{ + class FocusModeSystemComponent final + : public AZ::Component + , private FocusModeInterface + { + public: + AZ_COMPONENT(FocusModeSystemComponent, "{6CE522FE-2057-4794-BD05-61E04BD8EA30}"); + + FocusModeSystemComponent() = default; + virtual ~FocusModeSystemComponent(); + + // AZ::Component... + void Init() override; + void Activate() override; + void Deactivate() override; + + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + // FocusModeInterface... + void SetFocusRoot(AZ::EntityId entityId) override; + AZ::EntityId GetFocusRoot() override; + bool IsInFocusSubTree(AZ::EntityId entityId) override; + + private: + static bool IsInFocusSubTree_helper(AZ::EntityId entityId, AZ::EntityId focusRootId); + + AZ::EntityId m_focusRoot; + }; + +} // namespace AzToolsFramework::FocusModeFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp new file mode 100644 index 0000000000..a9c8c93eb0 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include + +namespace AzToolsFramework::Prefab +{ + FocusModeFramework::FocusModeInterface* PrefabFocusHandler::s_focusModeInterface = nullptr; + InstanceEntityMapperInterface* PrefabFocusHandler::s_instanceEntityMapperInterface = nullptr; + PrefabEditorEntityOwnershipInterface* PrefabFocusHandler::s_prefabEditorEntityOwnershipInterface = nullptr; + + PrefabFocusHandler::~PrefabFocusHandler() + { + AZ::Interface::Unregister(this); + } + + void PrefabFocusHandler::Initialize() + { + s_focusModeInterface = AZ::Interface::Get(); + AZ_Assert( + s_focusModeInterface, + "Prefab - PrefabFocusHandler - " + "Focus Mode Interface could not be found. " + "Check that it is being correctly initialized."); + + s_instanceEntityMapperInterface = AZ::Interface::Get(); + AZ_Assert( + s_instanceEntityMapperInterface, + "Prefab - PrefabFocusHandler - " + "Instance Entity Mapper Interface could not be found. " + "Check that it is being correctly initialized."); + + s_prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + AZ_Assert( + s_prefabEditorEntityOwnershipInterface, + "Prefab - PrefabFocusHandler - " + "Prefab Editor Entity Ownership Interface could not be found. " + "Check that it is being correctly initialized."); + + AZ::Interface::Register(this); + } + + void PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) + { + InstanceOptionalReference focusedInstance; + + if (entityId == AZ::EntityId()) + { + focusedInstance = s_prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); + } + else + { + focusedInstance = s_instanceEntityMapperInterface->FindOwningInstance(entityId); + } + + if (!focusedInstance.has_value()) + { + // TODO - ERROR + } + + m_focusedInstance = focusedInstance; + m_focusedTemplateId = focusedInstance->get().GetTemplateId(); + s_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + } + + TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() + { + return m_focusedTemplateId; + } + + InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance() + { + return m_focusedInstance; + } + + bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) + { + if (entityId == AZ::EntityId()) + { + // TODO - Warn? + return false; + } + + InstanceOptionalReference instance = s_instanceEntityMapperInterface->FindOwningInstance(entityId); + + if (!instance.has_value()) + { + // TODO - ERROR + } + + return (&instance->get() == &m_focusedInstance->get()); + } + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h new file mode 100644 index 0000000000..44e84dc345 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of + * this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include +#include + +namespace AzToolsFramework +{ + class PrefabEditorEntityOwnershipInterface; +} + +namespace AzToolsFramework::FocusModeFramework +{ + class FocusModeInterface; +} + +namespace AzToolsFramework::Prefab +{ + class InstanceEntityMapperInterface; + + class PrefabFocusHandler final + : private PrefabFocusInterface + { + public: + AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0); + + ~PrefabFocusHandler(); + + void Initialize(); + + // PrefabFocusInterface... + void FocusOnOwningPrefab(AZ::EntityId entityId) override; + TemplateId GetFocusedPrefabTemplateId() override; + InstanceOptionalReference GetFocusedPrefabInstance() override; + bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override; + + private: + InstanceOptionalReference m_focusedInstance; + TemplateId m_focusedTemplateId; + + static FocusModeFramework::FocusModeInterface* s_focusModeInterface; + static InstanceEntityMapperInterface* s_instanceEntityMapperInterface; + static PrefabEditorEntityOwnershipInterface* s_prefabEditorEntityOwnershipInterface; + }; + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h new file mode 100644 index 0000000000..d18a669f51 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of + * this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +#include +#include + +namespace AzToolsFramework::Prefab +{ + /*! + * PrefabFocusInterface + */ + class PrefabFocusInterface + { + public: + AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}"); + + // TODO - Add comment + virtual void FocusOnOwningPrefab(AZ::EntityId entityId) = 0; + + // TODO - Add comment + virtual TemplateId GetFocusedPrefabTemplateId() = 0; + + // TODO - Add comment + virtual InstanceOptionalReference GetFocusedPrefabInstance() = 0; + + // TODO - Add comment + virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0; + }; + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 0d2696f14b..7220cf3cce 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -34,6 +34,7 @@ namespace AzToolsFramework m_prefabLoader.RegisterPrefabLoaderInterface(); m_instanceUpdateExecutor.RegisterInstanceUpdateExecutorInterface(); m_instanceToTemplatePropagator.RegisterInstanceToTemplateInterface(); + m_prefabFocusHandler.Initialize(); m_prefabPublicHandler.RegisterPrefabPublicHandlerInterface(); m_prefabPublicRequestHandler.Connect(); AZ::SystemTickBus::Handler::BusConnect(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index d6908922c9..a9e16374e9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -369,7 +370,7 @@ namespace AzToolsFramework // A counter for generating unique Link Ids. AZStd::atomic m_linkIdCounter = 0u; - // Used for finding the owning instance of an arbitrary entity + // Used for finding the owning instance of an arbitrary entity. InstanceEntityMapper m_instanceEntityMapper; // Used for finding the Instances owned by an arbitrary Template. @@ -378,16 +379,19 @@ namespace AzToolsFramework // Used for loading/saving Prefab Template files. PrefabLoader m_prefabLoader; - // Handler the public Prefab API used by UI and scripting + // Handles the Prefab Focus API that determines what prefab is being edited. + PrefabFocusHandler m_prefabFocusHandler; + + // Handles the public Prefab API used by UI and scripting. PrefabPublicHandler m_prefabPublicHandler; // Used for updating Instances of Prefab Template. InstanceUpdateExecutor m_instanceUpdateExecutor; - // Used for updating Templates when Instances are modified + // Used for updating Templates when Instances are modified. InstanceToTemplatePropagator m_instanceToTemplatePropagator; - // Handler of the public Prefab requests + // Handler of the public Prefab requests. PrefabPublicRequestHandler m_prefabPublicRequestHandler; }; } // namespace Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp index c75f6aa86d..bb6bdf0ebd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp @@ -8,7 +8,6 @@ #include -#include #include #include @@ -24,14 +23,6 @@ namespace AzToolsFramework LevelRootUiHandler::LevelRootUiHandler() { - m_prefabEditInterface = AZ::Interface::Get(); - - if (m_prefabEditInterface == nullptr) - { - AZ_Assert(false, "LevelRootUiHandler - could not get PrefabEditInterface on LevelRootUiHandler construction."); - return; - } - m_prefabPublicInterface = AZ::Interface::Get(); if (m_prefabPublicInterface == nullptr) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h index 6eeccfe88d..1e485572b8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h @@ -14,7 +14,6 @@ namespace AzToolsFramework { namespace Prefab { - class PrefabEditInterface; class PrefabPublicInterface; }; @@ -36,7 +35,6 @@ namespace AzToolsFramework void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: - Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr; Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; static constexpr int m_levelRootBorderThickness = 1; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditInterface.h deleted file mode 100644 index 1bd4a164f8..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditInterface.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include - -namespace AzToolsFramework -{ - namespace Prefab - { - /*! - * PrefabEditInterface - * Interface to expose the API to Edit Prefabs in the Editor. - */ - class PrefabEditInterface - { - public: - AZ_RTTI(PrefabEditInterface, "{DABB1D43-3760-420E-9F1E-5104F0AFF167}"); - - /** - * Sets the prefab for the instance owning the entity provided as the prefab being edited. - * @param entityId The entity whose owning prefab should be edited. - */ - virtual void EditOwningPrefab(AZ::EntityId entityId) = 0; - - /** - * Queries the Edit Manager to know if the provided entity is part of the prefab currently being edited. - * @param entityId The entity whose prefab editing state we want to query. - * @return True if the prefab owning this entity is being edited, false otherwise. - */ - virtual bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) = 0; - }; - - } // namespace Prefab -} // namespace AzToolsFramework - diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.cpp deleted file mode 100644 index 27a4c3bcaf..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include - -#include - -namespace AzToolsFramework -{ - namespace Prefab - { - PrefabEditManager::PrefabEditManager() - { - m_prefabPublicInterface = AZ::Interface::Get(); - - if (m_prefabPublicInterface == nullptr) - { - AZ_Assert(false, "Prefab - could not get PrefabPublicInterface on PrefabEditManager construction."); - return; - } - - AZ::Interface::Register(this); - } - - PrefabEditManager::~PrefabEditManager() - { - AZ::Interface::Unregister(this); - } - - void PrefabEditManager::EditOwningPrefab(AZ::EntityId entityId) - { - m_instanceBeingEdited = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId); - } - - bool PrefabEditManager::IsOwningPrefabBeingEdited(AZ::EntityId entityId) - { - AZ::EntityId containerEntity = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId); - return m_instanceBeingEdited == containerEntity; - } - } -} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.h deleted file mode 100644 index d155322e3c..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include - -#include -#include - -namespace AzToolsFramework -{ - namespace Prefab - { - class PrefabEditManager final - : private PrefabEditInterface - { - public: - AZ_CLASS_ALLOCATOR(PrefabEditManager, AZ::SystemAllocator, 0); - - PrefabEditManager(); - ~PrefabEditManager(); - - private: - // PrefabEditInterface... - void EditOwningPrefab(AZ::EntityId entityId) override; - bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) override; - - AZ::EntityId m_instanceBeingEdited; - - PrefabPublicInterface* m_prefabPublicInterface; - }; - } -} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 0c61ccb501..9b7f2fff10 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -57,9 +58,9 @@ namespace AzToolsFramework { EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr; - PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; - PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr; + PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr; PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr; + PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr; const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab"; @@ -102,13 +103,6 @@ namespace AzToolsFramework return; } - s_prefabEditInterface = AZ::Interface::Get(); - if (s_prefabEditInterface == nullptr) - { - AZ_Assert(false, "Prefab - could not get PrefabEditInterface on PrefabIntegrationManager construction."); - return; - } - s_prefabLoaderInterface = AZ::Interface::Get(); if (s_prefabLoaderInterface == nullptr) { @@ -123,6 +117,13 @@ namespace AzToolsFramework return; } + s_prefabFocusInterface = AZ::Interface::Get(); + if (s_prefabFocusInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction."); + return; + } + EditorContextMenuBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); @@ -224,7 +225,7 @@ namespace AzToolsFramework // Edit Prefab if (prefabWipFeaturesEnabled) { - bool beingEdited = s_prefabEditInterface->IsOwningPrefabBeingEdited(selectedEntity); + bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity); if (!beingEdited) { @@ -428,7 +429,7 @@ namespace AzToolsFramework void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity) { - s_prefabEditInterface->EditOwningPrefab(containerEntity); + s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity); } void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 3e66350932..6f66b1f514 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -17,7 +17,7 @@ #include #include #include -#include + #include #include #include @@ -28,7 +28,7 @@ namespace AzToolsFramework { namespace Prefab { - + class PrefabFocusInterface; class PrefabLoaderInterface; //! Structure for saving/retrieving user settings related to prefab workflows. @@ -80,9 +80,6 @@ namespace AzToolsFramework void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override; private: - // Manages the Edit Mode UI for prefabs - PrefabEditManager m_prefabEditManager; - // Used to handle the UI for the level root LevelRootUiHandler m_levelRootUiHandler; @@ -135,13 +132,12 @@ namespace AzToolsFramework AZStd::unique_ptr ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference); void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog); - static const AZStd::string s_prefabFileExtension; static EditorEntityUiInterface* s_editorEntityUiInterface; - static PrefabPublicInterface* s_prefabPublicInterface; - static PrefabEditInterface* s_prefabEditInterface; + static PrefabFocusInterface* s_prefabFocusInterface; static PrefabLoaderInterface* s_prefabLoaderInterface; + static PrefabPublicInterface* s_prefabPublicInterface; static PrefabSystemComponentInterface* s_prefabSystemComponentInterface; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index fda56b79f1..c802850c4b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include @@ -26,21 +26,19 @@ namespace AzToolsFramework PrefabUiHandler::PrefabUiHandler() { - m_prefabEditInterface = AZ::Interface::Get(); - - if (m_prefabEditInterface == nullptr) - { - AZ_Assert(false, "PrefabUiHandler - could not get PrefabEditInterface on PrefabUiHandler construction."); - return; - } - m_prefabPublicInterface = AZ::Interface::Get(); - if (m_prefabPublicInterface == nullptr) { AZ_Assert(false, "PrefabUiHandler - could not get PrefabPublicInterface on PrefabUiHandler construction."); return; } + + m_prefabFocusInterface = AZ::Interface::Get(); + if (m_prefabFocusInterface == nullptr) + { + AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction."); + return; + } } QString PrefabUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const @@ -83,7 +81,7 @@ namespace AzToolsFramework QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const { - if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId)) + if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) { return QIcon(m_prefabEditIconPath); } @@ -105,7 +103,7 @@ namespace AzToolsFramework const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value() && index.model()->hasChildren(index); QColor backgroundColor = m_prefabCapsuleColor; - if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId)) + if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) { backgroundColor = m_prefabCapsuleEditColor; } @@ -191,7 +189,7 @@ namespace AzToolsFramework const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle; QColor borderColor = m_prefabCapsuleColor; - if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId)) + if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) { borderColor = m_prefabCapsuleEditColor; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h index d900fae427..547c100eb1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h @@ -12,9 +12,10 @@ namespace AzToolsFramework { + namespace Prefab { - class PrefabEditInterface; + class PrefabFocusInterface; class PrefabPublicInterface; }; @@ -37,7 +38,7 @@ namespace AzToolsFramework const QModelIndex& descendantIndex) const override; private: - Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr; + Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr; Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 2d9e75a115..400b87886b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -147,6 +147,9 @@ set(FILES Entity/SliceEditorEntityOwnershipServiceBus.h Fingerprinting/TypeFingerprinter.h Fingerprinting/TypeFingerprinter.cpp + FocusMode/FocusModeInterface.h + FocusMode/FocusModeSystemComponent.h + FocusMode/FocusModeSystemComponent.cpp Logger/TraceLogger.cpp Logger/TraceLogger.h Manipulators/AngularManipulator.cpp @@ -625,6 +628,9 @@ set(FILES Prefab/PrefabDomTypes.h Prefab/PrefabDomUtils.h Prefab/PrefabDomUtils.cpp + Prefab/PrefabFocusHandler.h + Prefab/PrefabFocusHandler.cpp + Prefab/PrefabFocusInterface.h Prefab/PrefabIdTypes.h Prefab/PrefabLoader.h Prefab/PrefabLoader.cpp @@ -717,9 +723,6 @@ set(FILES UI/Layer/LayerUiHandler.cpp UI/Prefab/LevelRootUiHandler.h UI/Prefab/LevelRootUiHandler.cpp - UI/Prefab/PrefabEditInterface.h - UI/Prefab/PrefabEditManager.h - UI/Prefab/PrefabEditManager.cpp UI/Prefab/PrefabIntegrationBus.h UI/Prefab/PrefabIntegrationManager.h UI/Prefab/PrefabIntegrationManager.cpp From 71bf61491271ec781669bc240d67d746ef8b2c7a Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Mon, 20 Sep 2021 12:18:23 -0700 Subject: [PATCH 26/63] Changed FocusOnOwningPrefab to return Outcome. Added comments and error checking. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../FocusMode/FocusModeSystemComponent.cpp | 4 ++-- .../Prefab/PrefabFocusHandler.cpp | 13 ++++++------- .../AzToolsFramework/Prefab/PrefabFocusHandler.h | 2 +- .../Prefab/PrefabFocusInterface.h | 15 ++++++++++----- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index 3ad2dbdfb8..d82ce9ffeb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -10,6 +10,7 @@ #include +#include #include namespace AzToolsFramework::FocusModeFramework @@ -53,8 +54,7 @@ namespace AzToolsFramework::FocusModeFramework { m_focusRoot = entityId; - // TODO - If m_focusRoot != AZ::EntityId(), register focus mode - // Else, unregister focus mode + // TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode } AZ::EntityId FocusModeSystemComponent::GetFocusRoot() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index a9c8c93eb0..ee663c0c3f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -49,7 +49,7 @@ namespace AzToolsFramework::Prefab AZ::Interface::Register(this); } - void PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) + PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) { InstanceOptionalReference focusedInstance; @@ -64,12 +64,15 @@ namespace AzToolsFramework::Prefab if (!focusedInstance.has_value()) { - // TODO - ERROR + return AZ::Failure(AZStd::string( + "Prefab Focus Handler: Couldn't find owning instance of entityId provided.")); } m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); s_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + + return AZ::Success(); } TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() @@ -86,16 +89,12 @@ namespace AzToolsFramework::Prefab { if (entityId == AZ::EntityId()) { - // TODO - Warn? return false; } InstanceOptionalReference instance = s_instanceEntityMapperInterface->FindOwningInstance(entityId); - if (!instance.has_value()) - { - // TODO - ERROR - } + AZ_Assert(instance.has_value(), "PrefabFocusHandler::IsOwningPrefabBeingFocused - Could not find owning Instance of queried entity."); return (&instance->get() == &m_focusedInstance->get()); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 44e84dc345..bf8b6994d1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -39,7 +39,7 @@ namespace AzToolsFramework::Prefab void Initialize(); // PrefabFocusInterface... - void FocusOnOwningPrefab(AZ::EntityId entityId) override; + PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; TemplateId GetFocusedPrefabTemplateId() override; InstanceOptionalReference GetFocusedPrefabInstance() override; bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index d18a669f51..7b867ab8d2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -16,6 +16,8 @@ namespace AzToolsFramework::Prefab { + typedef AZ::Outcome PrefabFocusOperationResult; + /*! * PrefabFocusInterface */ @@ -24,16 +26,19 @@ namespace AzToolsFramework::Prefab public: AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}"); - // TODO - Add comment - virtual void FocusOnOwningPrefab(AZ::EntityId entityId) = 0; + //! Set the focused prefab instance to the owning instance of the entityId provided + //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. + virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; - // TODO - Add comment + //! Returns the template id of the instance the prefab system is focusing on. virtual TemplateId GetFocusedPrefabTemplateId() = 0; - // TODO - Add comment + //! Returns a reference to the instance the prefab system is focusing on. virtual InstanceOptionalReference GetFocusedPrefabInstance() = 0; - // TODO - Add comment + //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. + //! @param entityId The entityId of the queried entity. + //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0; }; From 489a93cf91e30ff727c7e32c588c712f5efe436a Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Mon, 20 Sep 2021 13:44:25 -0700 Subject: [PATCH 27/63] Remove assert for edge case that can actually happen in normal circumstances, simply return false in those cases. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/Prefab/PrefabFocusHandler.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index ee663c0c3f..2f64234352 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -94,9 +94,7 @@ namespace AzToolsFramework::Prefab InstanceOptionalReference instance = s_instanceEntityMapperInterface->FindOwningInstance(entityId); - AZ_Assert(instance.has_value(), "PrefabFocusHandler::IsOwningPrefabBeingFocused - Could not find owning Instance of queried entity."); - - return (&instance->get() == &m_focusedInstance->get()); + return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); } } // namespace AzToolsFramework::Prefab From 88ee878025ab77e636ef157ef2a17809e220f983 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Mon, 20 Sep 2021 23:13:51 -0500 Subject: [PATCH 28/63] deleting platform specific code from ME and SMC fixed ME viewport Signed-off-by: Guthrie Adams --- .../Platform/Linux/MaterialEditor_Linux.cpp | 27 ----------- .../Platform/Mac/MaterialEditor_Mac.cpp | 27 ----------- .../Windows/MaterialEditor_Windows.cpp | 47 ------------------- .../Viewport/MaterialViewportWidget.cpp | 26 ---------- .../Source/Viewport/MaterialViewportWidget.h | 5 -- .../Mac/ShaderManagementConsole_Mac.cpp | 22 --------- .../ShaderManagementConsole_Windows.cpp | 45 ------------------ 7 files changed, 199 deletions(-) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp index 14b5fee1fe..d588e5a656 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp @@ -6,33 +6,6 @@ * */ -#include -#include -#include - namespace Platform { - void LoadPluginDependencies() - { - AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented"); - } - - void ProcessInput(void* message) - { - AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp index 14b5fee1fe..d588e5a656 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp @@ -6,33 +6,6 @@ * */ -#include -#include -#include - namespace Platform { - void LoadPluginDependencies() - { - AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented"); - } - - void ProcessInput(void* message) - { - AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp index 65c135315b..d588e5a656 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp @@ -6,53 +6,6 @@ * */ -#include -#include -#include -#include -#include - namespace Platform { - void ProcessInput(void* message) - { - MSG* msg = (MSG*)message; - - // Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system, - // but only while in game mode so we don't accumulate raw input events before we start actually - // ticking the input devices, otherwise the queued events will get sent when entering game mode. - if (msg->message == WM_INPUT) - { - UINT rawInputSize; - const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize); - - LPBYTE rawInputBytes = new BYTE[rawInputSize]; - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); - - RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; - - AzFramework::RawInputNotificationBusWindows::Broadcast( - &AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput); - } - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - return reinterpret_cast(winId); - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - RECT r; - if (GetWindowRect(reinterpret_cast(window), &r)) - { - return AzFramework::WindowSize{aznumeric_cast(r.right - r.left), aznumeric_cast(r.bottom - r.top)}; - } - else - { - AZ_Assert(false, "Failed to get dimensions for window"); - return AzFramework::WindowSize{}; - } - } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp index d2fe78cb89..e2c5ee0026 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp @@ -17,20 +17,11 @@ #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknow n-warning-option") // disable warnings spawned by QT -#include #include #include "Viewport/ui_MaterialViewportWidget.h" AZ_POP_DISABLE_WARNING -#include #include -#include - -namespace Platform -{ - void ProcessInput(void* message); -} - namespace MaterialEditor { @@ -41,14 +32,6 @@ namespace MaterialEditor { m_ui->setupUi(this); - if (auto dispatcher = QAbstractEventDispatcher::instance()) - { - dispatcher->installNativeEventFilter(this); - } - - AzFramework::WindowSystemNotificationBus::Broadcast( - &AzFramework::WindowSystemNotificationBus::Events::OnWindowCreated, GetViewportContext()->GetWindowHandle()); - // The viewport context created by AtomToolsFramework::RenderViewportWidget has no name. // Systems like frame capturing and post FX expect there to be a context with DefaultViewportContextName auto viewportContextManager = AZ::Interface::Get(); @@ -58,13 +41,4 @@ namespace MaterialEditor m_renderer = AZStd::make_unique(GetViewportContext()->GetWindowContext()); GetControllerList()->Add(m_renderer->GetController()); } - - // This is a temporary fix to get input working in Qt window, otherwise it wont receive input events - // This will later be handled on the QApplication subclass level - bool MaterialViewportWidget::nativeEventFilter(const QByteArray& /*eventType*/, void* message, long* /*result*/) - { - Platform::ProcessInput(message); - - return false; - } } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h index 438c7b9a9e..2a71dcc1df 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h @@ -8,12 +8,10 @@ #pragma once #if !defined(Q_MOC_RUN) -#include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include -#include AZ_POP_DISABLE_WARNING #endif @@ -38,14 +36,11 @@ namespace MaterialEditor class MaterialViewportWidget : public AtomToolsFramework::RenderViewportWidget - , public QAbstractNativeEventFilter { public: MaterialViewportWidget(QWidget* parent = nullptr); QScopedPointer m_ui; AZStd::unique_ptr m_renderer; - - bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; }; } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp index 6a05b895e0..d588e5a656 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp @@ -6,28 +6,6 @@ * */ -#include -#include -#include - namespace Platform { - void ProcessInput(void* message) - { - AZ_Warning("Shader Management Console", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Shader Management Console", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Shader Management Console", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } } diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp index ff94ef1196..d588e5a656 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp @@ -6,51 +6,6 @@ * */ -#include -#include -#include - namespace Platform { - void ProcessInput(void* message) - { - MSG* msg = (MSG*)message; - - // Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system, - // but only while in game mode so we don't accumulate raw input events before we start actually - // ticking the input devices, otherwise the queued events will get sent when entering game mode. - if (msg->message == WM_INPUT) - { - UINT rawInputSize; - const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize); - - LPBYTE rawInputBytes = new BYTE[rawInputSize]; - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); - - RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; - - AzFramework::RawInputNotificationBusWindows::Broadcast( - &AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput); - } - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - return reinterpret_cast(winId); - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - RECT r; - if (GetWindowRect(reinterpret_cast(window), &r)) - { - return AzFramework::WindowSize{aznumeric_cast(r.right - r.left), aznumeric_cast(r.bottom - r.top)}; - } - else - { - AZ_Assert(false, "Failed to get dimensions for window"); - return AzFramework::WindowSize{}; - } - } } From 895974db57cc6472baf94d7254f7acca23384fc4 Mon Sep 17 00:00:00 2001 From: pereslav Date: Tue, 21 Sep 2021 14:50:06 +0100 Subject: [PATCH 29/63] Removed spawn/despawn events from the spawnable system. Used NetworkSpawnableHolderComponent instead Signed-off-by: pereslav --- .../Spawnable/SpawnableEntitiesInterface.h | 9 --- .../Spawnable/SpawnableEntitiesManager.cpp | 20 ----- .../Spawnable/SpawnableEntitiesManager.h | 6 -- .../Mocks/MockSpawnableEntitiesInterface.h | 2 - .../NetworkEntity/INetworkEntityManager.h | 10 +++ .../NetworkEntity/NetworkEntityManager.cpp | 78 +------------------ .../NetworkEntity/NetworkEntityManager.h | 18 +---- .../NetworkSpawnableHolderComponent.cpp | 24 ++++++ .../NetworkSpawnableHolderComponent.h | 2 + 9 files changed, 43 insertions(+), 126 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 254df8f1b0..4b09dcbc75 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -320,15 +320,6 @@ namespace AzFramework //! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs. virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0; - //! Register a handler for OnSpawned events. - //! @param handler The handler to receive the event. - virtual void AddOnSpawnedHandler(AZ::Event, - const AZStd::vector&, const void*>::Handler& handler) = 0; - - //! Register a handler for OnDespawned events. - //! @param handler The handler to receive the event. - virtual void AddOnDespawnedHandler(AZ::Event, const void*>::Handler& handler) = 0; - protected: [[nodiscard]] virtual AZStd::pair CreateTicket(AZ::Data::Asset&& spawnable) = 0; virtual void DestroyTicket(void* ticket) = 0; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index f052f83224..5fa072a451 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -146,16 +146,6 @@ namespace AzFramework QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event, const AZStd::vector&, const void*>::Handler& handler) - { - handler.Connect(m_onSpawnedEvent); - } - - void SpawnableEntitiesManager::AddOnDespawnedHandler(AZ::Event, const void*>::Handler& handler) - { - handler.Connect(m_onDespawnedEvent); - } - auto SpawnableEntitiesManager::ProcessQueue(CommandQueuePriority priority) -> CommandQueueStatus { CommandQueueStatus result = CommandQueueStatus::NoCommandsLeft; @@ -359,8 +349,6 @@ namespace AzFramework ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); } - m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket); - ticket.m_currentRequestId++; return true; } @@ -441,8 +429,6 @@ namespace AzFramework ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); } - m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket); - ticket.m_currentRequestId++; return true; } @@ -474,8 +460,6 @@ namespace AzFramework request.m_completionCallback(request.m_ticketId); } - m_onDespawnedEvent.Signal(ticket.m_spawnable, &ticket); - ticket.m_currentRequestId++; return true; } @@ -503,8 +487,6 @@ namespace AzFramework } } - m_onDespawnedEvent.Signal(ticket.m_spawnable, &ticket); - // Rebuild the list of entities. ticket.m_spawnedEntities.clear(); const Spawnable::EntityList& entities = request.m_spawnable->GetEntities(); @@ -562,8 +544,6 @@ namespace AzFramework ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end())); } - m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket); - ticket.m_currentRequestId++; return true; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index c78b1778a4..69985f8aa9 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -69,9 +69,6 @@ namespace AzFramework void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) override; - void AddOnSpawnedHandler(AZ::Event, const AZStd::vector&, const void*>::Handler& handler) override; - void AddOnDespawnedHandler(AZ::Event, const void*>::Handler& handler) override; - // // The following function is thread safe but intended to be run from the main thread. // @@ -227,9 +224,6 @@ namespace AzFramework //! SpawnablePriority_Default which gives users a bit of room to fine tune the priorities as this value can be configured //! through the Settings Registry under the key "/O3DE/AzFramework/Spawnables/HighPriorityThreshold". SpawnablePriority m_highPriorityThreshold { 64 }; - - AZ::Event, const AZStd::vector&, const void*> m_onSpawnedEvent; - AZ::Event, const void*> m_onDespawnedEvent; }; AZ_DEFINE_ENUM_BITWISE_OPERATORS(AzFramework::SpawnableEntitiesManager::CommandQueuePriority); diff --git a/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h b/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h index 89e79ecf9b..a5df7af4c3 100644 --- a/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h @@ -59,8 +59,6 @@ namespace AzFramework MOCK_METHOD1(CreateTicket, AZStd::pair(AZ::Data::Asset&& spawnable)); MOCK_METHOD1(DestroyTicket, void(void* ticket)); - MOCK_METHOD1(AddOnSpawnedHandler, void(AZ::Event, const AZStd::vector&, const void*>::Handler& handler)); - MOCK_METHOD1(AddOnDespawnedHandler, void(AZ::Event, const void*>::Handler& handler)); /** Installs some default result values for the above functions. * Note that you can always override these in scope of your test by adding additional ON_CALL / EXPECT_CALL diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h index 8a5fec869c..5bcf038eff 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace Multiplayer { @@ -75,6 +76,15 @@ namespace Multiplayer const AZ::Transform& transform ) = 0; + //! Requests a network spawnable to instantiate at a given transform + //! This is an async function. The instantiated entities are not available immediately but will be constructed by the spawnable system + //! The spawnable ticket has to be kept for the whole lifetime of the entities + //! @param netSpawnable the network spawnable to spawn + //! @param transform the transform where the spawnable should be spawned + //! @return the ticket for managing the spawned entities + [[nodiscard]] virtual AZStd::unique_ptr RequestNetSpawnableInstantiation( + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) = 0; + //! Configures new networked entity //! @param netEntity the entity to setup //! @param prefabEntryId the name of the spawnable the entity originated from diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 61fd741dc6..f4f744494b 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -30,14 +30,9 @@ namespace Multiplayer : m_networkEntityAuthorityTracker(*this) , m_removeEntitiesEvent([this] { RemoveEntities(); }, AZ::Name("NetworkEntityManager remove entities event")) , m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event")) - , m_onSpawnedHandler([this](AZ::Data::Asset spawnable, const AZStd::vector& entities, const void* spawnTicket) { this->OnSpawned(spawnable, entities, spawnTicket); }) - , m_onDespawnedHandler([this](AZ::Data::Asset spawnable, const void* spawnTicket) { this->OnDespawned(spawnable, spawnTicket); }) { AZ::Interface::Register(this); AzFramework::RootSpawnableNotificationBus::Handler::BusConnect(); - - AzFramework::SpawnableEntitiesInterface::Get()->AddOnSpawnedHandler(m_onSpawnedHandler); - AzFramework::SpawnableEntitiesInterface::Get()->AddOnDespawnedHandler(m_onDespawnedHandler); } NetworkEntityManager::~NetworkEntityManager() @@ -473,52 +468,17 @@ namespace Multiplayer } AZStd::unique_ptr NetworkEntityManager::RequestNetSpawnableInstantiation( - const AZ::Data::Asset& rootSpawnable, const AZStd::vector& entities) + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) { - if (entities.empty()) - { - AZ_Error("NetworkEntityManager", false, - "RequestNetSpawnableInstantiation: No entities in the spawnable %s", rootSpawnable.GetHint().c_str()); - return nullptr; - } - - // The first entity in every spawnable is the root one - const AZ::Entity* rootEntity = *entities.begin(); - if (!rootEntity) - { - AZ_Error("NetworkEntityManager", false, - "RequestNetSpawnableInstantiation: Root entity is null in the spawnable %s", rootSpawnable.GetHint().c_str()); - return nullptr; - } - - const auto* holderComponent = rootEntity->FindComponent(); - if (!holderComponent) - { - // This spawnable doesn't have a corresponding network spawnable. - return nullptr; - } - - AzFramework::TransformComponent* rootEntityTransform = - rootEntity->FindComponent(); - if (!rootEntityTransform) - { - AZ_Error("NetworkEntityManager", false, - "RequestNetSpawnableInstantiation: Root entity has no transform in the spawnable %s", rootSpawnable.GetHint().c_str()); - return nullptr; - } - - // Retrieve the corresponding network spawnable asset - AZ::Data::Asset netSpawnableAsset = holderComponent->GetNetworkSpawnableAsset(); - // Prepare the parameters for the spawning process AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_priority = AzFramework::SpawnablePriority_High; const AZ::Name netSpawnableName = - AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnableAsset.GetId()); + AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnable.GetId()); // Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene - optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = rootEntityTransform->GetWorldTM()] + optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = transform] (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) { bool shouldUpdateTransform = (rootTransform.IsClose(AZ::Transform::Identity()) == false); @@ -546,7 +506,7 @@ namespace Multiplayer }; // Spawn with the newly created ticket. This allows the calling code to manage the lifetime of the constructed entities - auto ticket = AZStd::make_unique(netSpawnableAsset); + auto ticket = AZStd::make_unique(netSpawnable); AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*ticket, AZStd::move(optionalArgs)); return ticket; } @@ -572,28 +532,6 @@ namespace Multiplayer { multiplayer->SendReadyForEntityUpdates(false); } - - m_netSpawnableTickets.clear(); - } - - void NetworkEntityManager::OnSpawned(AZ::Data::Asset spawnable, - const AZStd::vector& entities, const void* spawnTicket) - { - if (ShouldSpawnNetEntities()) - { - AZStd::unique_ptr ticket = RequestNetSpawnableInstantiation(spawnable, entities); - if (ticket) - { - AZ::Data::AssetId spawnableAssetId = spawnable.GetId(); - m_netSpawnableTickets[spawnTicket] = AZStd::move(ticket); - } - } - } - - void NetworkEntityManager::OnDespawned([[maybe_unused]] AZ::Data::Asset spawnable, - const void* spawnTicket) - { - m_netSpawnableTickets.erase(spawnTicket); } void NetworkEntityManager::SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) @@ -611,12 +549,4 @@ namespace Multiplayer netEntity->GetName().c_str()); } } - - bool NetworkEntityManager::ShouldSpawnNetEntities() const - { - const auto agentType = GetMultiplayer()->GetAgentType(); - const bool shouldSpawnNetEntities = - (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); - return shouldSpawnNetEntities; - } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index 14efa4feeb..50e6beedad 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -61,6 +60,9 @@ namespace Multiplayer const AZ::Transform& transform ) override; + AZStd::unique_ptr RequestNetSpawnableInstantiation( + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) override; + void SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) override; uint32_t GetEntityCount() const override; @@ -93,15 +95,6 @@ namespace Multiplayer private: void RemoveEntities(); NetEntityId NextId(); - bool ShouldSpawnNetEntities() const; - void OnSpawned(AZ::Data::Asset spawnable, - const AZStd::vector& entities, const void* spawnTicket); - void OnDespawned(AZ::Data::Asset spawnable, const void* spawnTicket); - - // Note: This is an async function. - // The instantiated entities are not available immediately but will be constructed by the spawnable system - AZStd::unique_ptr RequestNetSpawnableInstantiation( - const AZ::Data::Asset& rootSpawnable, const AZStd::vector& entities); NetworkEntityTracker m_networkEntityTracker; NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker; @@ -130,10 +123,5 @@ namespace Multiplayer DeferredRpcMessages m_localDeferredRpcMessages; NetworkSpawnableLibrary m_networkPrefabLibrary; - - AZStd::unordered_map> m_netSpawnableTickets; - - AZ::Event, const AZStd::vector&, const void*>::Handler m_onSpawnedHandler; - AZ::Event, const void*>::Handler m_onDespawnedHandler; }; } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp index e83f6fea84..d33c3274c2 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include namespace Multiplayer { @@ -28,10 +30,32 @@ namespace Multiplayer void NetworkSpawnableHolderComponent::Activate() { + const auto agentType = GetMultiplayer()->GetAgentType(); + const bool shouldSpawnNetEntities = + (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); + + if(shouldSpawnNetEntities) + { + AZ::Transform rootEntityTransform = AZ::Transform::CreateIdentity(); + + AzFramework::TransformComponent* rootEntityTransformComponent = + GetEntity()->FindComponent(); + if (rootEntityTransformComponent) + { + rootEntityTransform = rootEntityTransformComponent->GetWorldTM(); + } + + INetworkEntityManager* networkEntityManager = GetNetworkEntityManager(); + AZ_Assert(networkEntityManager != nullptr, + "Network Entity Manager must be initialized before NetworkSpawnableHolderComponent is activated"); + + m_netSpawnableTicket = networkEntityManager->RequestNetSpawnableInstantiation(m_networkSpawnableAsset, rootEntityTransform); + } } void NetworkSpawnableHolderComponent::Deactivate() { + m_netSpawnableTicket.reset(); } void NetworkSpawnableHolderComponent::SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset) diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h index f32e8d31ab..3836212c48 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace Multiplayer { @@ -37,5 +38,6 @@ namespace Multiplayer private: AZ::Data::Asset m_networkSpawnableAsset{ AZ::Data::AssetLoadBehavior::PreLoad }; + AZStd::unique_ptr m_netSpawnableTicket; }; } // namespace Multiplayer From dc0bb00c10ad370d9c80db157c334a47f5d7f25e Mon Sep 17 00:00:00 2001 From: pereslav Date: Tue, 21 Sep 2021 14:58:33 +0100 Subject: [PATCH 30/63] Added an error check in case of a requested spawnable is not networked Signed-off-by: pereslav --- .../Code/Source/NetworkEntity/NetworkEntityManager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index f4f744494b..a66b750266 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -477,6 +477,14 @@ namespace Multiplayer const AZ::Name netSpawnableName = AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnable.GetId()); + if (netSpawnableName.IsEmpty()) + { + AZ_Error("NetworkEntityManager", false, + "RequestNetSpawnableInstantiation: Requested spawnable %s doesn't exist in the NetworkSpawnableLibrary. Please make sure it is a network spawnable", + netSpawnable.GetHint().c_str()); + return nullptr; + } + // Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = transform] (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) From b8c78caa2ffc6370424ed56ce13f465e6e6efa12 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 09:48:09 -0700 Subject: [PATCH 31/63] Remove nested FocusModeFramework namespace Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/Application/ToolsApplication.cpp | 2 +- .../AzToolsFramework/AzToolsFrameworkModule.cpp | 2 +- .../AzToolsFramework/FocusMode/FocusModeInterface.h | 5 ++--- .../FocusMode/FocusModeSystemComponent.cpp | 4 ++-- .../AzToolsFramework/FocusMode/FocusModeSystemComponent.h | 4 ++-- .../AzToolsFramework/Prefab/PrefabFocusHandler.cpp | 4 ++-- .../AzToolsFramework/Prefab/PrefabFocusHandler.h | 8 ++------ 7 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index 7238a22c51..91a916ae70 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -249,7 +249,7 @@ namespace AzToolsFramework components.insert(components.end(), { azrtti_typeid(), azrtti_typeid(), - azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp index a557754437..b68d086892 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp @@ -70,7 +70,7 @@ namespace AzToolsFramework Components::EditorSelectionAccentSystemComponent::CreateDescriptor(), EditorEntityContextComponent::CreateDescriptor(), EditorEntityFixupComponent::CreateDescriptor(), - FocusModeFramework::FocusModeSystemComponent::CreateDescriptor(), + FocusModeSystemComponent::CreateDescriptor(), SliceMetadataEntityContextComponent::CreateDescriptor(), SliceRequestComponent::CreateDescriptor(), Prefab::PrefabSystemComponent::CreateDescriptor(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index edd94fd6b0..70bb320a96 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -11,8 +11,7 @@ #include #include - -namespace AzToolsFramework::FocusModeFramework +namespace AzToolsFramework { /*! * FocusModeInterface @@ -37,4 +36,4 @@ namespace AzToolsFramework::FocusModeFramework virtual bool IsInFocusSubTree(AZ::EntityId entityId) = 0; }; -} // namespace AzToolsFramework::FocusModeFrameworkclassPrefabEditPublicInterface +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index d82ce9ffeb..fcca452bf5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -13,7 +13,7 @@ #include #include -namespace AzToolsFramework::FocusModeFramework +namespace AzToolsFramework { FocusModeSystemComponent::~FocusModeSystemComponent() { @@ -90,4 +90,4 @@ namespace AzToolsFramework::FocusModeFramework return IsInFocusSubTree_helper(parentId, focusRootId); } -} // namespace AzToolsFramework::FocusModeFramework +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index 54312a1e92..284515eeb7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -13,7 +13,7 @@ #include -namespace AzToolsFramework::FocusModeFramework +namespace AzToolsFramework { class FocusModeSystemComponent final : public AZ::Component @@ -46,4 +46,4 @@ namespace AzToolsFramework::FocusModeFramework AZ::EntityId m_focusRoot; }; -} // namespace AzToolsFramework::FocusModeFramework +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 2f64234352..63921cff21 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -14,7 +14,7 @@ namespace AzToolsFramework::Prefab { - FocusModeFramework::FocusModeInterface* PrefabFocusHandler::s_focusModeInterface = nullptr; + FocusModeInterface* PrefabFocusHandler::s_focusModeInterface = nullptr; InstanceEntityMapperInterface* PrefabFocusHandler::s_instanceEntityMapperInterface = nullptr; PrefabEditorEntityOwnershipInterface* PrefabFocusHandler::s_prefabEditorEntityOwnershipInterface = nullptr; @@ -25,7 +25,7 @@ namespace AzToolsFramework::Prefab void PrefabFocusHandler::Initialize() { - s_focusModeInterface = AZ::Interface::Get(); + s_focusModeInterface = AZ::Interface::Get(); AZ_Assert( s_focusModeInterface, "Prefab - PrefabFocusHandler - " diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index bf8b6994d1..6a2ff2f62e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -15,13 +15,9 @@ #include namespace AzToolsFramework -{ - class PrefabEditorEntityOwnershipInterface; -} - -namespace AzToolsFramework::FocusModeFramework { class FocusModeInterface; + class PrefabEditorEntityOwnershipInterface; } namespace AzToolsFramework::Prefab @@ -48,7 +44,7 @@ namespace AzToolsFramework::Prefab InstanceOptionalReference m_focusedInstance; TemplateId m_focusedTemplateId; - static FocusModeFramework::FocusModeInterface* s_focusModeInterface; + static FocusModeInterface* s_focusModeInterface; static InstanceEntityMapperInterface* s_instanceEntityMapperInterface; static PrefabEditorEntityOwnershipInterface* s_prefabEditorEntityOwnershipInterface; }; From 5a4476a0e426fbb45260cf0fab8c90f497642929 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 10:26:41 -0700 Subject: [PATCH 32/63] Minor fixes to variable names and comments. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../FocusMode/FocusModeInterface.h | 14 +++---- .../FocusMode/FocusModeSystemComponent.cpp | 40 +++++++++---------- .../FocusMode/FocusModeSystemComponent.h | 9 +++-- .../Prefab/PrefabFocusHandler.cpp | 33 +++++---------- .../Prefab/PrefabFocusHandler.h | 9 +++-- .../Prefab/PrefabFocusInterface.h | 8 ++-- 6 files changed, 51 insertions(+), 62 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index 70bb320a96..b2d008ca36 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -13,26 +13,26 @@ namespace AzToolsFramework { - /*! - * FocusModeInterface - * Interface to handle the Editor Focus Mode. - */ + //! FocusModeInterface + //! Interface to handle the Editor Focus Mode. class FocusModeInterface { public: AZ_RTTI(FocusModeInterface, "{437243B0-F86B-422F-B7B8-4A21CC000702}"); //! Sets the root entity the Editor should focus on. - //! The Editor will only allow the user to select entities belonging to the sub-tree that has root in the entityId provided. + //! The Editor will only allow the user to select entities that are descendants of the EntityId provided. //! @param entityId The entityId that will become the new focus root. virtual void SetFocusRoot(AZ::EntityId entityId) = 0; + //! Clears the Editor focus, allowing the user to select the whole level again. + virtual void ClearFocusRoot() = 0; + //! Returns the entity id of the root of the current Editor focus. - //! @return The id of the entity that is the root of the Editor focus. + //! @return The entity id of the root of the Editor focus, or an invalid entity id if no focus is set. virtual AZ::EntityId GetFocusRoot() = 0; //! Returns whether the entity id provided is part of the focused sub-tree. - //! @return True if entityId belongs to the focused sub-tree, false otherwise. virtual bool IsInFocusSubTree(AZ::EntityId entityId) = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index fcca452bf5..e609aa2f48 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -15,6 +15,24 @@ namespace AzToolsFramework { + bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId) + { + if (entityId == AZ::EntityId()) + { + return false; + } + + if (entityId == focusRootId) + { + return true; + } + + AZ::EntityId parentId; + AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformInterface::GetParentId); + + return IsInFocusSubTree(parentId, focusRootId); + } + FocusModeSystemComponent::~FocusModeSystemComponent() { AZ::Interface::Unregister(this); @@ -33,7 +51,7 @@ namespace AzToolsFramework { } - void FocusModeSystemComponent::Reflect(AZ::ReflectContext* /*context*/) + void FocusModeSystemComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context) { } @@ -69,25 +87,7 @@ namespace AzToolsFramework return true; } - return IsInFocusSubTree_helper(entityId, m_focusRoot); - } - - bool FocusModeSystemComponent::IsInFocusSubTree_helper(AZ::EntityId entityId, AZ::EntityId focusRootId) - { - if (entityId == AZ::EntityId()) - { - return false; - } - - if (entityId == focusRootId) - { - return true; - } - - AZ::EntityId parentId; - AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformInterface::GetParentId); - - return IsInFocusSubTree_helper(parentId, focusRootId); + return AzToolsFramework::IsInFocusSubTree(entityId, m_focusRoot); } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index 284515eeb7..2a2b1500ac 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -15,6 +15,9 @@ namespace AzToolsFramework { + bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId); + + //! System Component to handle the Editor Focus Mode system class FocusModeSystemComponent final : public AZ::Component , private FocusModeInterface @@ -25,7 +28,7 @@ namespace AzToolsFramework FocusModeSystemComponent() = default; virtual ~FocusModeSystemComponent(); - // AZ::Component... + // AZ::Component overrides ... void Init() override; void Activate() override; void Deactivate() override; @@ -35,14 +38,12 @@ namespace AzToolsFramework static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - // FocusModeInterface... + // FocusModeInterface overrides ... void SetFocusRoot(AZ::EntityId entityId) override; AZ::EntityId GetFocusRoot() override; bool IsInFocusSubTree(AZ::EntityId entityId) override; private: - static bool IsInFocusSubTree_helper(AZ::EntityId entityId, AZ::EntityId focusRootId); - AZ::EntityId m_focusRoot; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 63921cff21..bdd1b63afd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -14,10 +14,6 @@ namespace AzToolsFramework::Prefab { - FocusModeInterface* PrefabFocusHandler::s_focusModeInterface = nullptr; - InstanceEntityMapperInterface* PrefabFocusHandler::s_instanceEntityMapperInterface = nullptr; - PrefabEditorEntityOwnershipInterface* PrefabFocusHandler::s_prefabEditorEntityOwnershipInterface = nullptr; - PrefabFocusHandler::~PrefabFocusHandler() { AZ::Interface::Unregister(this); @@ -25,23 +21,23 @@ namespace AzToolsFramework::Prefab void PrefabFocusHandler::Initialize() { - s_focusModeInterface = AZ::Interface::Get(); + m_focusModeInterface = AZ::Interface::Get(); AZ_Assert( - s_focusModeInterface, + m_focusModeInterface, "Prefab - PrefabFocusHandler - " "Focus Mode Interface could not be found. " "Check that it is being correctly initialized."); - s_instanceEntityMapperInterface = AZ::Interface::Get(); + m_instanceEntityMapperInterface = AZ::Interface::Get(); AZ_Assert( - s_instanceEntityMapperInterface, + m_instanceEntityMapperInterface, "Prefab - PrefabFocusHandler - " "Instance Entity Mapper Interface could not be found. " "Check that it is being correctly initialized."); - s_prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + m_prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); AZ_Assert( - s_prefabEditorEntityOwnershipInterface, + m_prefabEditorEntityOwnershipInterface, "Prefab - PrefabFocusHandler - " "Prefab Editor Entity Ownership Interface could not be found. " "Check that it is being correctly initialized."); @@ -51,16 +47,9 @@ namespace AzToolsFramework::Prefab PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) { - InstanceOptionalReference focusedInstance; - - if (entityId == AZ::EntityId()) - { - focusedInstance = s_prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); - } - else - { - focusedInstance = s_instanceEntityMapperInterface->FindOwningInstance(entityId); - } + InstanceOptionalReference focusedInstance = (entityId == AZ::EntityId()) + ? m_prefabEditorEntityOwnershipInterface->GetRootPrefabInstance() + : m_instanceEntityMapperInterface->FindOwningInstance(entityId); if (!focusedInstance.has_value()) { @@ -70,7 +59,7 @@ namespace AzToolsFramework::Prefab m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); - s_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + m_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); return AZ::Success(); } @@ -92,7 +81,7 @@ namespace AzToolsFramework::Prefab return false; } - InstanceOptionalReference instance = s_instanceEntityMapperInterface->FindOwningInstance(entityId); + InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 6a2ff2f62e..60038d9bb8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -24,6 +24,7 @@ namespace AzToolsFramework::Prefab { class InstanceEntityMapperInterface; + //! Handles Prefab Focus mode, determining which prefab file entity changes will target. class PrefabFocusHandler final : private PrefabFocusInterface { @@ -34,7 +35,7 @@ namespace AzToolsFramework::Prefab void Initialize(); - // PrefabFocusInterface... + // PrefabFocusInterface override ... PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; TemplateId GetFocusedPrefabTemplateId() override; InstanceOptionalReference GetFocusedPrefabInstance() override; @@ -44,9 +45,9 @@ namespace AzToolsFramework::Prefab InstanceOptionalReference m_focusedInstance; TemplateId m_focusedTemplateId; - static FocusModeInterface* s_focusModeInterface; - static InstanceEntityMapperInterface* s_instanceEntityMapperInterface; - static PrefabEditorEntityOwnershipInterface* s_prefabEditorEntityOwnershipInterface; + FocusModeInterface* m_focusModeInterface; + InstanceEntityMapperInterface* m_instanceEntityMapperInterface; + PrefabEditorEntityOwnershipInterface* m_prefabEditorEntityOwnershipInterface; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 7b867ab8d2..c7f58f5de2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -16,17 +16,15 @@ namespace AzToolsFramework::Prefab { - typedef AZ::Outcome PrefabFocusOperationResult; + using PrefabFocusOperationResult = AZ::Outcome; - /*! - * PrefabFocusInterface - */ + //! Interface to handle operations related to the Prefab Focus system. class PrefabFocusInterface { public: AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}"); - //! Set the focused prefab instance to the owning instance of the entityId provided + //! Set the focused prefab instance to the owning instance of the entityId provided. //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; From df10b56f2ab4ae0d6289aefb160c7315d9f1aee8 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 10:47:02 -0700 Subject: [PATCH 33/63] ClearFocusRoot implementation Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp | 5 +++++ .../AzToolsFramework/FocusMode/FocusModeSystemComponent.h | 1 + 2 files changed, 6 insertions(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index e609aa2f48..6d5e672df9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -75,6 +75,11 @@ namespace AzToolsFramework // TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode } + void FocusModeSystemComponent::ClearFocusRoot() + { + SetFocusRoot(AZ::EntityId()); + } + AZ::EntityId FocusModeSystemComponent::GetFocusRoot() { return m_focusRoot; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index 2a2b1500ac..32386abf67 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -40,6 +40,7 @@ namespace AzToolsFramework // FocusModeInterface overrides ... void SetFocusRoot(AZ::EntityId entityId) override; + void ClearFocusRoot() override; AZ::EntityId GetFocusRoot() override; bool IsInFocusSubTree(AZ::EntityId entityId) override; From b78e41f5a8332399af22a69f8311b6f895eed331 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 21 Sep 2021 12:17:33 -0700 Subject: [PATCH 34/63] Refactor use of OrderOnce for materials to limit to materialtype dependencies only. Signed-off-by: Robin --- .../RPI.Builders/Material/MaterialBuilder.cpp | 53 +++++++++++-------- .../Model/MaterialAssetBuilderComponent.cpp | 4 +- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index 099fdf33dd..e1fed6a877 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -9,7 +9,6 @@ #include "MaterialBuilder.h" #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #include #include #include +#include namespace AZ { @@ -47,7 +47,7 @@ namespace AZ { AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor; materialBuilderDescriptor.m_name = JobKey; - materialBuilderDescriptor.m_version = 107; // ATOM-14918 + materialBuilderDescriptor.m_version = 108; // ATOM-14918 materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_busId = azrtti_typeid(); @@ -67,21 +67,19 @@ namespace AZ //! Adds all relevant dependencies for a referenced source file, considering that the path might be relative to the original file location or a full asset path. //! This will usually include multiple source dependencies and a single job dependency, but will include only source dependencies if the file is not found. //! Note the AssetBuilderSDK::JobDependency::m_platformIdentifier will not be set by this function. The calling code must set this value before passing back - //! to the AssetBuilderSDK::CreateJobsResponse. - void AddPossibleDependencies( - AZStd::string_view currentFilePath, AZStd::string_view referencedParentPath, - AZStd::vector& sourceFileDependencies, - const char* jobKey, AZStd::vector& jobDependencies) + //! to the AssetBuilderSDK::CreateJobsResponse. If isOrderedOnceForMaterialTypes is true and the dependency is a materialtype file, the job dependency type + //! will be set to JobDependencyType::OrderOnce. + void AddPossibleDependencies(AZStd::string_view currentFilePath, + AZStd::string_view referencedParentPath, + const char* jobKey, + AZStd::vector& jobDependencies, + bool isOrderedOnceForMaterialTypes = false) { bool dependencyFileFound = false; AZStd::vector possibleDependencies = RPI::AssetUtils::GetPossibleDepenencyPaths(currentFilePath, referencedParentPath); for (auto& file : possibleDependencies) { - AssetBuilderSDK::SourceFileDependency sourceFileDependency; - sourceFileDependency.m_sourceFileDependencyPath = file; - sourceFileDependencies.push_back(sourceFileDependency); - // The first path found is the highest priority, and will have a job dependency, as this is the one // the builder will actually use if (!dependencyFileFound) @@ -96,9 +94,8 @@ namespace AZ jobDependency.m_jobKey = jobKey; jobDependency.m_sourceFile.m_sourceFileDependencyPath = file; - bool includeMaterialPropertyNames = true; - RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); - jobDependency.m_type = includeMaterialPropertyNames ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; + const bool isMaterialTypeFile = AzFramework::StringFunc::Path::IsExtension(file.c_str(), MaterialTypeSourceData::Extension); + jobDependency.m_type = (isMaterialTypeFile && isOrderedOnceForMaterialTypes) ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; jobDependencies.push_back(jobDependency); } @@ -178,8 +175,9 @@ namespace AZ for (auto& shader : materialTypeSourceData.GetValue().m_shaderCollection) { - AddPossibleDependencies(request.m_sourceFile, shader.m_shaderFilePath, - response.m_sourceFileDependencyList, "Shader Asset", + AddPossibleDependencies(request.m_sourceFile, + shader.m_shaderFilePath, + "Shader Asset", outputJobDescriptor.m_jobDependencyList); } @@ -189,9 +187,10 @@ namespace AZ for (const MaterialFunctorSourceData::AssetDependency& dependency : dependencies) { - AddPossibleDependencies(request.m_sourceFile, dependency.m_sourceFilePath, - response.m_sourceFileDependencyList, - dependency.m_jobKey.c_str(), outputJobDescriptor.m_jobDependencyList); + AddPossibleDependencies(request.m_sourceFile, + dependency.m_sourceFilePath, + dependency.m_jobKey.c_str(), + outputJobDescriptor.m_jobDependencyList); } } } @@ -224,11 +223,21 @@ namespace AZ parentMaterialPath = materialTypePath; } + // If includeMaterialPropertyNames is true, then we need materials to depend on materialtype only once in order to get + // the property names. After the initial processing, materials will no longer be dependent on materialtype files. + bool includeMaterialPropertyNames = true; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(includeMaterialPropertyNames, "/O3DE/Atom/RPI/MaterialBuilder/IncludeMaterialPropertyNames"); + } + // Register dependency on the parent material source file so we can load it and use it's data to build this variant material. // Note, we don't need a direct dependency on the material type because the parent material will depend on it. - AddPossibleDependencies(request.m_sourceFile, parentMaterialPath, - response.m_sourceFileDependencyList, - JobKey, outputJobDescriptor.m_jobDependencyList); + AddPossibleDependencies(request.m_sourceFile, + parentMaterialPath, + JobKey, + outputJobDescriptor.m_jobDependencyList, + includeMaterialPropertyNames); } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp index b2c590a376..93994c567c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp @@ -44,7 +44,7 @@ namespace AZ if (auto* serialize = azrtti_cast(context)) { serialize->Class() - ->Version(4) + ->Version(5) ->Attribute(Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })); } } @@ -88,6 +88,8 @@ namespace AZ jobDependency.m_sourceFile = materialTypeSource; jobDependency.m_platformIdentifier = platformIdentifier; + // If includeMaterialPropertyNames is true, then we need materials to depend on materialtype only once in order to get + // the property names. After the initial processing, materials will no longer be dependent on materialtype files. bool includeMaterialPropertyNames = true; RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); jobDependency.m_type = includeMaterialPropertyNames ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; From 0c3dc9a0cf55931b44651b654998611396262b75 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 21 Sep 2021 12:25:05 -0700 Subject: [PATCH 35/63] Remove JIRA number. Signed-off-by: Robin --- .../RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index e1fed6a877..3e4147a949 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -47,7 +47,7 @@ namespace AZ { AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor; materialBuilderDescriptor.m_name = JobKey; - materialBuilderDescriptor.m_version = 108; // ATOM-14918 + materialBuilderDescriptor.m_version = 108; materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_busId = azrtti_typeid(); From 22e14ea90d159bb2f967c997fffac0704fece52a Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Tue, 21 Sep 2021 14:29:24 -0500 Subject: [PATCH 36/63] added m_drawContextRegistered flag to stop error calling UnregisterDynamicDrawContext in AtomViewportDisplayIconsSystemComponent Signed-off-by: Guthrie Adams --- .../Source/AtomViewportDisplayIconsSystemComponent.cpp | 7 ++++++- .../Code/Source/AtomViewportDisplayIconsSystemComponent.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp index 6ea6a8e85f..c0ca2fe993 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp @@ -82,6 +82,8 @@ namespace AZ::Render void AtomViewportDisplayIconsSystemComponent::Activate() { + m_drawContextRegistered = false; + AzToolsFramework::EditorViewportIconDisplay::Register(this); Bootstrap::NotificationBus::Handler::BusConnect(); @@ -97,9 +99,10 @@ namespace AZ::Render { return; } - if (perViewportDynamicDrawInterface) + if (perViewportDynamicDrawInterface && m_drawContextRegistered) { perViewportDynamicDrawInterface->UnregisterDynamicDrawContext(m_drawContextName); + m_drawContextRegistered = false; } AzToolsFramework::EditorViewportIconDisplay::Unregister(this); @@ -367,6 +370,8 @@ namespace AZ::Render drawContext->EndInit(); }); + m_drawContextRegistered = true; + Data::AssetBus::Handler::BusDisconnect(); } } // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h index 25cc7eb8f6..a0914e7e2d 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h @@ -77,6 +77,8 @@ namespace AZ }; AZStd::unordered_map m_iconData; IconId m_currentId = 0; + + bool m_drawContextRegistered = false; }; } // namespace Render } // namespace AZ From dd8761dd8bbf7527fcd81905d5bfe25495b2087b Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 21 Sep 2021 13:08:23 -0700 Subject: [PATCH 37/63] Edit comment and add marker for version update. Signed-off-by: Robin --- .../Source/RPI.Builders/Material/MaterialBuilder.cpp | 9 ++++++--- .../RPI.Builders/Model/MaterialAssetBuilderComponent.cpp | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index 3e4147a949..ef0678046c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -47,7 +47,7 @@ namespace AZ { AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor; materialBuilderDescriptor.m_name = JobKey; - materialBuilderDescriptor.m_version = 108; + materialBuilderDescriptor.m_version = 108; // Set materialtype dependency to OrderOnce materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_busId = azrtti_typeid(); @@ -223,8 +223,11 @@ namespace AZ parentMaterialPath = materialTypePath; } - // If includeMaterialPropertyNames is true, then we need materials to depend on materialtype only once in order to get - // the property names. After the initial processing, materials will no longer be dependent on materialtype files. + // If includeMaterialPropertyNames is false, then a job dependency is needed so the material builder can validate MaterialAsset properties + // against the MaterialTypeAsset at asset build time. + // If includeMaterialPropertyNames is true, the material properties will be validated at runtime when the material is loaded, so the job dependency + // is needed only for first-time processing to set up the initial MaterialAsset. This speeds up AP processing time when a materialtype file + // is edited (e.g. 10s when editing StandardPBR.materialtype on AtomTest project from 45s). bool includeMaterialPropertyNames = true; if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp index 93994c567c..c83761cf8e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp @@ -44,7 +44,7 @@ namespace AZ if (auto* serialize = azrtti_cast(context)) { serialize->Class() - ->Version(5) + ->Version(5) // Set materialtype dependency to OrderOnce ->Attribute(Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })); } } @@ -88,8 +88,11 @@ namespace AZ jobDependency.m_sourceFile = materialTypeSource; jobDependency.m_platformIdentifier = platformIdentifier; - // If includeMaterialPropertyNames is true, then we need materials to depend on materialtype only once in order to get - // the property names. After the initial processing, materials will no longer be dependent on materialtype files. + // If includeMaterialPropertyNames is false, then a job dependency is needed so the material builder can validate + // MaterialAsset properties against the MaterialTypeAsset at asset build time. If includeMaterialPropertyNames is true, the + // material properties will be validated at runtime when the material is loaded, so the job dependency is needed only for + // first-time processing to set up the initial MaterialAsset. This speeds up AP processing time when a materialtype file is + // edited (e.g. 10s when editing StandardPBR.materialtype on AtomTest project from 45s). bool includeMaterialPropertyNames = true; RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); jobDependency.m_type = includeMaterialPropertyNames ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; From 107732d057c0932879882b76331120a8298107fc Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 13:45:39 -0700 Subject: [PATCH 38/63] Remove pragma once from cpp file Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index 6d5e672df9..197119d017 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -6,8 +6,6 @@ * */ -#pragma once - #include #include From 0bcf275ed2c70d5b12175d59405000f356b0a41f Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Tue, 21 Sep 2021 16:39:04 -0500 Subject: [PATCH 39/63] updated/removed platform specific files fixed warning message Signed-off-by: Guthrie Adams --- .../Source/Platform/Linux/MaterialEditor_Linux.cpp | 11 ----------- .../Source/Platform/Linux/platform_linux_files.cmake | 1 - .../Code/Source/Platform/Mac/MaterialEditor_Mac.cpp | 11 ----------- .../Code/Source/Platform/Mac/platform_mac_files.cmake | 1 - .../Platform/Windows/MaterialEditor_Windows.cpp | 11 ----------- .../Platform/Windows/platform_windows_files.cmake | 1 - .../Code/Source/Viewport/MaterialViewportWidget.cpp | 2 +- .../Code/Source/Platform/Linux/PAL_linux.cmake | 2 +- .../Platform/Mac/ShaderManagementConsole_Mac.cpp | 11 ----------- .../Code/Source/Platform/Mac/platform_mac_files.cmake | 1 - .../Windows/ShaderManagementConsole_Windows.cpp | 11 ----------- .../Platform/Windows/platform_windows_files.cmake | 1 - 12 files changed, 2 insertions(+), 62 deletions(-) delete mode 100644 Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp delete mode 100644 Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp delete mode 100644 Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp deleted file mode 100644 index d588e5a656..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp +++ /dev/null @@ -1,11 +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 - * - */ - -namespace Platform -{ -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake index c09abb0dd2..2417ad1b55 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -9,5 +9,4 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Linux.h - MaterialEditor_Linux.cpp ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp deleted file mode 100644 index d588e5a656..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp +++ /dev/null @@ -1,11 +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 - * - */ - -namespace Platform -{ -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake index ec56cc1c4e..7275f82047 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -9,5 +9,4 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Mac.h - MaterialEditor_Mac.cpp ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp deleted file mode 100644 index d588e5a656..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp +++ /dev/null @@ -1,11 +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 - * - */ - -namespace Platform -{ -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake index c104a2b8be..608cb36a28 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,5 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Windows.h - MaterialEditor_Windows.cpp MaterialEditor.rc ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp index e2c5ee0026..0322fa194e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp @@ -16,7 +16,7 @@ #include #include -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknow n-warning-option") // disable warnings spawned by QT +AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include #include "Viewport/ui_MaterialViewportWidget.h" AZ_POP_DISABLE_WARNING diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake index 762ac16004..b5332e2e15 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake @@ -6,4 +6,4 @@ # # -set(PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED FALSE) +set(PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED TRUE) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp deleted file mode 100644 index d588e5a656..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp +++ /dev/null @@ -1,11 +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 - * - */ - -namespace Platform -{ -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake index e48e836b73..6fe2859a89 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -9,5 +9,4 @@ set(FILES ShaderManagementConsole_Traits_Platform.h ShaderManagementConsole_Traits_Mac.h - ShaderManagementConsole_Mac.cpp ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp deleted file mode 100644 index d588e5a656..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp +++ /dev/null @@ -1,11 +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 - * - */ - -namespace Platform -{ -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake index 4ec319f402..b978cedeb4 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,5 @@ set(FILES ShaderManagementConsole_Traits_Platform.h ShaderManagementConsole_Traits_Windows.h - ShaderManagementConsole_Windows.cpp ShaderManagementConsole.rc ) From e5cc029c76cb76f0f07facc5ae3bff840cda3a07 Mon Sep 17 00:00:00 2001 From: dmcdiar Date: Tue, 21 Sep 2021 14:40:26 -0700 Subject: [PATCH 40/63] Converted ReflectionProbe Srg indices to ShaderInputNameIndex Signed-off-by: dmcdiar --- .../Source/ReflectionProbe/ReflectionProbe.h | 14 +++--- .../ReflectionProbeFeatureProcessor.cpp | 47 ------------------- 2 files changed, 7 insertions(+), 54 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h index 4f6a3337be..5b56b70ec8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h @@ -55,13 +55,13 @@ namespace AZ RHI::DrawListTag m_renderOuterDrawListTag; RHI::DrawListTag m_renderInnerDrawListTag; - RHI::ShaderInputConstantIndex m_modelToWorldStencilConstantIndex; - RHI::ShaderInputConstantIndex m_modelToWorldRenderConstantIndex; - RHI::ShaderInputConstantIndex m_modelToWorldInverseRenderConstantIndex; - RHI::ShaderInputConstantIndex m_outerObbHalfLengthsRenderConstantIndex; - RHI::ShaderInputConstantIndex m_innerObbHalfLengthsRenderConstantIndex; - RHI::ShaderInputConstantIndex m_useParallaxCorrectionRenderConstantIndex; - RHI::ShaderInputImageIndex m_reflectionCubeMapRenderImageIndex; + RHI::ShaderInputNameIndex m_modelToWorldStencilConstantIndex = "m_modelToWorld"; + RHI::ShaderInputNameIndex m_modelToWorldRenderConstantIndex = "m_modelToWorld"; + RHI::ShaderInputNameIndex m_modelToWorldInverseRenderConstantIndex = "m_modelToWorldInverse"; + RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths"; + RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths"; + RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection"; + RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap"; }; // ReflectionProbe manages all aspects of a single probe, including rendering, visualization, and cubemap generation diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index e47a132481..9fecbc19fc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -77,53 +77,6 @@ namespace AZ m_reflectionRenderData.m_renderInnerSrgLayout, m_reflectionRenderData.m_renderInnerDrawListTag); - // create ShaderResourceGroups here so we can get the layout and cache the indices - // Note: the SRGs are not needed beyond this method since each probe creates its own SRGs, we are just interested in the indices - - // cache probe stencil shader indices - Data::Instance stencilSrg = RPI::ShaderResourceGroup::Create( - m_reflectionRenderData.m_stencilShader->GetAsset(), - m_reflectionRenderData.m_stencilShader->GetSupervariantIndex(), - m_reflectionRenderData.m_stencilSrgLayout->GetName()); - AZ_Error("ReflectionProbeFeatureProcessor", stencilSrg.get(), "Failed to create stencil back face shader resource group"); - - const RHI::ShaderResourceGroupLayout* stencilSrgLayout = stencilSrg->GetLayout(); - Name modelToWorldConstantName = Name("m_modelToWorld"); - m_reflectionRenderData.m_modelToWorldStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldConstantName.GetCStr()); - - // cache probe render shader indices - // Note: the outer and inner render shaders use the same Srg - Data::Instance renderReflectionSrg = RPI::ShaderResourceGroup::Create( - m_reflectionRenderData.m_renderOuterShader->GetAsset(), - m_reflectionRenderData.m_renderOuterShader->GetSupervariantIndex(), - m_reflectionRenderData.m_renderOuterSrgLayout->GetName()); - AZ_Error("ReflectionProbeFeatureProcessor", renderReflectionSrg.get(), "Failed to create render reflection shader resource group"); - - const RHI::ShaderResourceGroupLayout* renderReflectionSrgLayout = renderReflectionSrg->GetLayout(); - m_reflectionRenderData.m_modelToWorldRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldConstantName.GetCStr()); - - Name modelToWorldInverseConstantName = Name("m_modelToWorldInverse"); - m_reflectionRenderData.m_modelToWorldInverseRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldInverseConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldInverseConstantName.GetCStr()); - - Name outerObbHalfLengthsConstantName = Name("m_outerObbHalfLengths"); - m_reflectionRenderData.m_outerObbHalfLengthsRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerObbHalfLengthsConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerObbHalfLengthsRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerObbHalfLengthsConstantName.GetCStr()); - - Name innerObbHalfLengthsConstantName = Name("m_innerObbHalfLengths"); - m_reflectionRenderData.m_innerObbHalfLengthsRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerObbHalfLengthsConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerObbHalfLengthsRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerObbHalfLengthsConstantName.GetCStr()); - - Name useParallaxCorrectionConstantName = Name("m_useParallaxCorrection"); - m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(useParallaxCorrectionConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", useParallaxCorrectionConstantName.GetCStr()); - - Name reflectionCubeMapImageName = Name("m_reflectionCubeMap"); - m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex = renderReflectionSrgLayout->FindShaderInputImageIndex(reflectionCubeMapImageName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex.IsValid(), "Failed to find render shader input image [%s]", reflectionCubeMapImageName.GetCStr()); - EnableSceneNotification(); } From f14e8d6eac8d1d75a86cdc532df375f6da504dd9 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 15:06:29 -0700 Subject: [PATCH 41/63] Fix header formatting to pass validation Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/FocusMode/FocusModeInterface.h | 4 ++-- .../AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp | 4 ++-- .../AzToolsFramework/FocusMode/FocusModeSystemComponent.h | 4 ++-- .../AzToolsFramework/Prefab/PrefabFocusHandler.h | 4 ++-- .../AzToolsFramework/Prefab/PrefabFocusInterface.h | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index b2d008ca36..6d3a7599de 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -1,6 +1,6 @@ /* - * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of - * this distribution. + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index 197119d017..450e19da3e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of - * this distribution. + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index 32386abf67..9943d72951 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -1,6 +1,6 @@ /* - * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of - * this distribution. + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 60038d9bb8..b465f97908 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -1,6 +1,6 @@ /* - * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of - * this distribution. + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index c7f58f5de2..833a5ef7c8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -1,6 +1,6 @@ /* - * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of - * this distribution. + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * From d44cc844d14c7f27598df8b903cc14257f0fd495 Mon Sep 17 00:00:00 2001 From: brianherrera Date: Tue, 21 Sep 2021 15:06:31 -0700 Subject: [PATCH 42/63] Retry pipeline stages when a node goes offline This change is required to support spot instances in the AR pipeline. - When a node goes offline the job is retried on a new node - Valid script errors are handled properly and the status is updated in Jenkins Signed-off-by: brianherrera --- scripts/build/Jenkins/Jenkinsfile | 108 ++++++++++++++++-------------- 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 5bc4b919fb..a49cf1d548 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -9,6 +9,7 @@ import groovy.json.JsonOutput PIPELINE_CONFIG_FILE = 'scripts/build/Jenkins/lumberyard.json' INCREMENTAL_BUILD_SCRIPT_PATH = 'scripts/build/bootstrap/incremental_build_util.py' +PIPELINE_RETRY_ATTEMPTS = 3 EMPTY_JSON = readJSON text: '{}' @@ -502,64 +503,73 @@ def CreateTeardownStage(Map environmentVars) { def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVars, String branchName, String pipelineName, String repositoryName, String projectName, boolean onlyMountEBSVolume = false) { def nodeLabel = envVars['NODE_LABEL'] return { - node("${nodeLabel}") { - if(isUnix()) { // Has to happen inside a node - envVars['IS_UNIX'] = 1 - } - withEnv(GetEnvStringList(envVars)) { - def build_job_name = build_job.key - try { - CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call() + def currentResult = '' + def currentException = '' + retry(PIPELINE_RETRY_ATTEMPTS) { + node("${nodeLabel}") { + if(isUnix()) { // Has to happen inside a node + envVars['IS_UNIX'] = 1 + } + withEnv(GetEnvStringList(envVars)) { + def build_job_name = build_job.key + try { + CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call() - if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages - build_job.value.steps.each { build_step -> - build_job_name = build_step - envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) - try { - CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() + if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages + build_job.value.steps.each { build_step -> + build_job_name = build_step + envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) + try { + CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() + } + catch (Exception e) { + if (envVars['NONBLOCKING_STEP']?.toBoolean()) { + unstable(message: "Build step ${build_step} failed but it's a non-blocking step in build job ${build_job.key}") + } + } } - catch (Exception e) { - if (envVars['NONBLOCKING_STEP']?.toBoolean()) { - unstable(message: "Build step ${build_step} failed but it's a non-blocking step in build job ${build_job.key}") - } - else { - error "FAILURE: ${e}" - } + } else { + CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call() + } + } + catch(Exception e) { + if (e instanceof org.jenkinsci.plugins.workflow.steps.FlowInterruptedException) { + def causes = e.getCauses().toString() + if (causes.contains('RemovedNodeCause')) { + error "Node disconnected during build: ${e}" // Error raised to retry stage on a new node } } - } else { - CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call() + // All other errors will be raised outside the retry block + currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' + currentException = e.toString() } - } - catch(Exception e) { - // https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Result.java - // {SUCCESS,UNSTABLE,FAILURE,NOT_BUILT,ABORTED} - def currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' - if (currentResult == 'FAILURE') { - currentBuild.result = 'FAILURE' - error "FAILURE: ${e}" - } else if (currentResult == 'UNSTABLE') { - currentBuild.result = 'UNSTABLE' - unstable(message: "UNSTABLE: ${e}") + finally { + def params = platform.value.build_types[build_job_name].PARAMETERS + if (env.MARS_REPO && params && params.containsKey('TEST_METRICS') && params.TEST_METRICS == 'True') { + def output_directory = params.OUTPUT_DIRECTORY + def configuration = params.CONFIGURATION + CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call() + } + if (params && params.containsKey('TEST_RESULTS') && params.TEST_RESULTS == 'True') { + CreateExportTestResultsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() + } + if (params && params.containsKey('TEST_SCREENSHOTS') && params.TEST_SCREENSHOTS == 'True' && currentResult == 'FAILURE') { + CreateExportTestScreenshotsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() + } + CreateTeardownStage(envVars).call() } } - finally { - def params = platform.value.build_types[build_job_name].PARAMETERS - if (env.MARS_REPO && params && params.containsKey('TEST_METRICS') && params.TEST_METRICS == 'True') { - def output_directory = params.OUTPUT_DIRECTORY - def configuration = params.CONFIGURATION - CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call() - } - if (params && params.containsKey('TEST_RESULTS') && params.TEST_RESULTS == 'True') { - CreateExportTestResultsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() - } - if (params && params.containsKey('TEST_SCREENSHOTS') && params.TEST_SCREENSHOTS == 'True' && currentResult == 'FAILURE') { - CreateExportTestScreenshotsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() - } - CreateTeardownStage(envVars).call() - } } } + // https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Result.java + // {SUCCESS,UNSTABLE,FAILURE,NOT_BUILT,ABORTED} + if (currentResult == 'FAILURE') { + currentBuild.result = 'FAILURE' + error "FAILURE: ${currentException}" + } else if (currentResult == 'UNSTABLE') { + currentBuild.result = 'UNSTABLE' + unstable(message: "UNSTABLE: ${currentException}") + } } } From eaddd4eee77beb4eb79b71bc01adb7a7d7ae8ff9 Mon Sep 17 00:00:00 2001 From: brianherrera Date: Tue, 21 Sep 2021 16:05:25 -0700 Subject: [PATCH 43/63] Throw exception for build stage This is required so Jenkins will properly mark the build failed on valid script errors. Signed-off-by: brianherrera --- scripts/build/Jenkins/Jenkinsfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index a49cf1d548..5fe19ddf46 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -525,6 +525,8 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar catch (Exception e) { if (envVars['NONBLOCKING_STEP']?.toBoolean()) { unstable(message: "Build step ${build_step} failed but it's a non-blocking step in build job ${build_job.key}") + } else { + throw e } } } From df42377fc6933a04dca38db3160af7e78e8ebb61 Mon Sep 17 00:00:00 2001 From: dmcdiar Date: Tue, 21 Sep 2021 17:10:32 -0700 Subject: [PATCH 44/63] Minor changes to forward pass IBL specular Signed-off-by: dmcdiar --- .../Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli | 2 +- .../Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli index b1cfb47be6..d33a307dfe 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli @@ -60,7 +60,7 @@ float3 GetIblSpecular( // compute blend amount based on world position in the reflection probe volume float blendAmount = ComputeLerpBetweenInnerOuterOBBs( - ObjectSrg::m_reflectionProbeData.m_modelToWorldInverse, + ObjectSrg::GetReflectionProbeWorldMatrixInverse(), ObjectSrg::m_reflectionProbeData.m_innerObbHalfLengths, ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths, position); diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index ba3bdc0bb4..bd0d12106b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -1144,7 +1144,7 @@ namespace AZ if (!reflectionProbes.empty() && reflectionProbes[0]) { m_shaderResourceGroup->SetConstant(modelToWorldConstantIndex, reflectionProbes[0]->GetTransform()); - m_shaderResourceGroup->SetConstant(modelToWorldInverseConstantIndex, reflectionProbes[0]->GetTransform().GetInverse()); + m_shaderResourceGroup->SetConstant(modelToWorldInverseConstantIndex, Matrix3x4::CreateFromTransform(reflectionProbes[0]->GetTransform()).GetInverseFull()); m_shaderResourceGroup->SetConstant(outerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetOuterObbWs().GetHalfLengths()); m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths()); m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true); From ceed8f692b9cfb7f8bd8c50300c1505c7c75066d Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 17:16:27 -0700 Subject: [PATCH 45/63] Do not assert if m_focusModeInterface can't be initialized Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/Prefab/PrefabFocusHandler.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index bdd1b63afd..f74029aa73 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -22,11 +22,6 @@ namespace AzToolsFramework::Prefab void PrefabFocusHandler::Initialize() { m_focusModeInterface = AZ::Interface::Get(); - AZ_Assert( - m_focusModeInterface, - "Prefab - PrefabFocusHandler - " - "Focus Mode Interface could not be found. " - "Check that it is being correctly initialized."); m_instanceEntityMapperInterface = AZ::Interface::Get(); AZ_Assert( @@ -59,7 +54,10 @@ namespace AzToolsFramework::Prefab m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); - m_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + if (m_focusModeInterface) + { + m_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + } return AZ::Success(); } From 5f73061227aacfce39c8bd37486dd2f7e6b873ec Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Tue, 21 Sep 2021 22:19:21 -0400 Subject: [PATCH 46/63] Adding client side on-parent-changed logic for Network Transform Component Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Components/NetworkTransformComponent.h | 4 +++- .../Components/NetworkTransformComponent.cpp | 17 ++++++++++++++++ .../Code/Tests/CommonHierarchySetup.h | 4 ---- Gems/Multiplayer/Code/Tests/MockInterfaces.h | 2 +- .../Code/Tests/ServerHierarchyTests.cpp | 20 ++++++++----------- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index d90e7cddfe..35bf6e9f50 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -31,9 +31,11 @@ namespace Multiplayer private: void OnPreRender(float deltaTime); void OnCorrection(); - + void OnParentChanged(NetEntityId parentId); + EntityPreRenderEvent::Handler m_entityPreRenderEventHandler; EntityCorrectionEvent::Handler m_entityCorrectionEventHandler; + AZ::Event::Handler m_parentChangedEventHandler; Multiplayer::HostFrameId m_targetHostFrameId = HostFrameId(0); }; diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index 9ef585a0a0..d284f100ff 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -28,6 +28,7 @@ namespace Multiplayer NetworkTransformComponent::NetworkTransformComponent() : m_entityPreRenderEventHandler([this](float deltaTime) { OnPreRender(deltaTime); }) , m_entityCorrectionEventHandler([this]() { OnCorrection(); }) + , m_parentChangedEventHandler([this](NetEntityId parentId) { OnParentChanged(parentId); }) { ; } @@ -41,6 +42,7 @@ namespace Multiplayer { GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler); GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler); + ParentEntityIdAddEvent(m_parentChangedEventHandler); } void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) @@ -97,6 +99,21 @@ namespace Multiplayer } } + void NetworkTransformComponent::OnParentChanged(NetEntityId parentId) + { + const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId); + if (parentEntityHandle.Exists()) + { + if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity()) + { + GetEntity()->GetTransform()->SetParent(parentEntity->GetId()); + } + } + else + { + GetEntity()->GetTransform()->SetParent(AZ::EntityId()); + } + } NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent) : NetworkTransformComponentControllerBase(parent) diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h index 7fc1eef8b7..2deac1aa27 100644 --- a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -391,10 +391,6 @@ namespace Multiplayer SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Authority); - // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) - SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); - SetParentIdOnNetworkTransform(childOfChild.m_entity, child.m_netId); - // Create an entity replicator for the child entity const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h index 375fe2c5ba..6466fbb555 100644 --- a/Gems/Multiplayer/Code/Tests/MockInterfaces.h +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -101,7 +101,7 @@ namespace UnitTest MOCK_CONST_METHOD0(GetHostTimeMs, AZ::TimeMs()); MOCK_CONST_METHOD0(GetRewindingConnectionId, AzNetworking::ConnectionId()); MOCK_CONST_METHOD1(GetHostFrameIdForRewindingConnection, Multiplayer::HostFrameId(AzNetworking::ConnectionId)); - MOCK_METHOD3(AlterTime, void(Multiplayer::HostFrameId, AZ::TimeMs, AzNetworking::ConnectionId)); + MOCK_METHOD4(AlterTime, void (Multiplayer::HostFrameId, AZ::TimeMs, float, AzNetworking::ConnectionId)); MOCK_METHOD1(SyncEntitiesToRewindState, void(const AZ::Aabb&)); MOCK_METHOD0(ClearRewoundEntities, void()); }; diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp index c262782006..70859909b3 100644 --- a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -109,7 +109,7 @@ namespace Multiplayer TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Cleared_On_Detach) { - // now detach the child + // now detach the child m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); EXPECT_EQ( @@ -165,7 +165,7 @@ namespace Multiplayer m_root->m_entity->FindComponent()->IsHierarchyEnabled(), true ); - + StopEntity(m_root->m_entity); m_root->m_entity->Deactivate(); @@ -183,7 +183,7 @@ namespace Multiplayer m_child->m_entity->FindComponent()->IsHierarchyEnabled(), true ); - + StopEntity(m_child->m_entity); m_child->m_entity->Deactivate(); @@ -435,6 +435,9 @@ namespace Multiplayer CreateBranchedHierarchy(*m_root, *m_child, *m_childOfChild, *m_child2, *m_childOfChild2, *m_child2OfChild2); + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + m_child2OfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); // now the entities are under one hierarchy @@ -470,13 +473,6 @@ namespace Multiplayer SetupEntity(childOfChild2.m_entity, childOfChild2.m_netId, NetEntityRole::Authority); SetupEntity(child2OfChild2.m_entity, child2OfChild2.m_netId, NetEntityRole::Authority); - // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) - SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); - SetParentIdOnNetworkTransform(childOfChild.m_entity, child.m_netId); - SetParentIdOnNetworkTransform(child2.m_entity, root.m_netId); - SetParentIdOnNetworkTransform(childOfChild2.m_entity, child2.m_netId); - SetParentIdOnNetworkTransform(child2OfChild2.m_entity, child2.m_netId); - // Create entity replicators const NetworkEntityHandle childOfChild2Handle(childOfChild2.m_entity.get(), m_networkEntityTracker.get()); childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChild2Handle); @@ -1014,8 +1010,8 @@ namespace Multiplayer CreateDeepHierarchy(*m_root, *m_child, *m_childOfChild); - m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); - m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); // now the entities are under one hierarchy } From 9bd23dc5d8b8f6ca52991f6693ebbfd4cddcac50 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 22 Sep 2021 00:04:55 -0500 Subject: [PATCH 47/63] reverting call to AWSNativeSDKInit::InitializationManager::Shutdown() Signed-off-by: Guthrie Adams --- Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp b/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp index 2dad7cc3cc..d33f37a2d7 100644 --- a/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp +++ b/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp @@ -49,7 +49,6 @@ namespace HttpRequestor { m_thread.join(); } - AWSNativeSDKInit::InitializationManager::Shutdown(); } void Manager::AddRequest(Parameters && httpRequestParameters) From 7db46da755e4422ca1523e4319ebe1a5e41770c2 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 22 Sep 2021 00:35:19 -0500 Subject: [PATCH 48/63] updating SMC Linux config Signed-off-by: Guthrie Adams --- .../Platform/Android/tool_dependencies_android.cmake | 10 ---------- .../Linux/ShaderManagementConsole_Traits_Linux.h | 11 +++++++++++ .../Linux/ShaderManagementConsole_Traits_Platform.h | 10 ++++++++++ .../Source/Platform/Linux/platform_linux_files.cmake | 2 ++ .../Source/Platform/iOS/tool_dependencies_ios.cmake | 10 ---------- 5 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake create mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h create mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h delete mode 100644 Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake deleted file mode 100644 index 5bf4d7cb7e..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake +++ /dev/null @@ -1,10 +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(GEM_DEPENDENCIES -) 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 new file mode 100644 index 0000000000..355b135f50 --- /dev/null +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#define #define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT "" + diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h new file mode 100644 index 0000000000..d381cc79d0 --- /dev/null +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake index c2c5a11c4c..11babcdf15 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -7,4 +7,6 @@ # set(FILES + ShaderManagementConsole_Traits_Platform.h + ShaderManagementConsole_Traits_Linux.h ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake deleted file mode 100644 index 5bf4d7cb7e..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake +++ /dev/null @@ -1,10 +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(GEM_DEPENDENCIES -) From 7910803e820ff75b32e21157f175e63d6d05b250 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 22 Sep 2021 01:38:12 -0500 Subject: [PATCH 49/63] removing ly_add_target_dependencies Signed-off-by: Guthrie Adams --- .../Tools/MaterialEditor/Code/CMakeLists.txt | 13 +++------ .../Linux/tool_dependencies_linux.cmake | 2 +- .../Platform/Mac/tool_dependencies_mac.cmake | 2 +- .../Windows/tool_dependencies_windows.cmake | 2 +- .../Code/CMakeLists.txt | 27 ++++++++++--------- .../Linux/tool_dependencies_linux.cmake | 2 +- .../Platform/Mac/tool_dependencies_mac.cmake | 2 +- .../Windows/tool_dependencies_windows.cmake | 2 +- 8 files changed, 24 insertions(+), 28 deletions(-) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt index 0206cebf8b..d585e81162 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt @@ -93,12 +93,14 @@ ly_add_target( AUTOMOC FILES_CMAKE materialeditor_files.cmake - Source/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + PLATFORM_INCLUDE_FILES + ${pal_source_dir}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . Source - Source/Platform/${PAL_PLATFORM_NAME} + ${pal_source_dir} PUBLIC Include BUILD_DEPENDENCIES @@ -124,13 +126,6 @@ ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem) # Editor opens up the MaterialEditor ly_add_dependencies(Editor Gem::MaterialEditor) -ly_add_target_dependencies( - TARGETS - MaterialEditor - DEPENDENCIES_FILES - Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake -) - # Inject the project path into the MaterialEditor VS debugger command arguments if the build system being invoked # in a project centric view if(NOT PROJECT_NAME STREQUAL "O3DE") diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake index 374438983f..e1e811ff67 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake @@ -6,6 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES Gem::QtForPython.Editor ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt index 5710a186d3..d384378b09 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt @@ -43,6 +43,7 @@ ly_add_target( NAMESPACE Gem AUTOMOC AUTOUIC + AUTORCC FILES_CMAKE shadermanagementconsolewindow_files.cmake INCLUDE_DIRECTORIES @@ -64,6 +65,8 @@ ly_add_target( FILES_CMAKE shadermanagementconsole_files.cmake ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + PLATFORM_INCLUDE_FILES + ${pal_source_dir}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . @@ -88,13 +91,6 @@ ly_set_gem_variant_to_load(TARGETS ShaderManagementConsole VARIANTS Tools) # Editor opens up the ShaderManagementConsole ly_add_dependencies(Editor Gem::ShaderManagementConsole) -ly_add_target_dependencies( - TARGETS - ShaderManagementConsole - DEPENDENCIES_FILES - Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake -) - # Inject the project path into the ShaderManagementConsole VS debugger command arguments if the build system being invoked # in a project centric view if(NOT PROJECT_NAME STREQUAL "O3DE") @@ -104,9 +100,14 @@ endif() # Adds the ShaderManagementConsole target as a C preprocessor define so that it can be used as a Settings Registry # specialization in order to look up the generated .setreg which contains the dependencies # specified for the target. -set_source_files_properties( - Source/ShaderManagementConsoleApplication.cpp - PROPERTIES - COMPILE_DEFINITIONS - LY_CMAKE_TARGET="ShaderManagementConsole" -) +if(TARGET ShaderManagementConsole) + set_source_files_properties( + Source/ShaderManagementConsoleApplication.cpp + PROPERTIES + COMPILE_DEFINITIONS + LY_CMAKE_TARGET="ShaderManagementConsole" + ) +else() + message(FATAL_ERROR "Cannot set LY_CMAKE_TARGET define to ShaderManagementConsole as the target doesn't exist anymore." + " Perhaps it has been renamed") +endif() diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake index 374438983f..e1e811ff67 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake @@ -6,6 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES Gem::QtForPython.Editor ) From b865d2aec1ead951a5ab303a591699409f59195c Mon Sep 17 00:00:00 2001 From: hultonha <82228511+hultonha@users.noreply.github.com> Date: Wed, 22 Sep 2021 09:08:24 +0100 Subject: [PATCH 50/63] Add support for camera reference frames to support camera yaw in certain situations (#4088) * proof of concept change for fixing camera yaw rotation being ignored Signed-off-by: hultonha * updates to fully support camera roll Signed-off-by: hultonha * small fixes for PR Signed-off-by: hultonha * undo changes in EditorCameraComponent Signed-off-by: hultonha * add test to verify interaction between editor viewport view entity change and modular camera controller Signed-off-by: hultonha * add additional tests for updated camera behaviors Signed-off-by: hultonha * revert change to Camera Gem Output Name Signed-off-by: hultonha * move location of new files Signed-off-by: hultonha * fix tab/spaces issue Signed-off-by: hultonha * remove static from potentially unused functions Signed-off-by: hultonha * set camera lib name in CMakeLists.txt file Signed-off-by: hultonha * cosmetic CMakeLists.txt change Signed-off-by: hultonha --- Code/Editor/CMakeLists.txt | 31 +++ .../EditorModularViewportCameraComposer.cpp | 21 ++ .../EditorModularViewportCameraComposer.h | 8 +- .../Camera/editor_lib_camera_test_files.cmake | 11 + .../Lib/Tests/Camera/test_EditorCamera.cpp | 225 ++++++++++++++++++ .../test_ModularViewportCameraController.cpp | 24 +- .../EditorTransformComponentSelection.cpp | 8 +- .../EditorTransformComponentSelection.h | 2 +- .../ModularViewportCameraController.h | 23 ++ ...odularViewportCameraControllerRequestBus.h | 10 + .../ModularViewportCameraController.cpp | 90 ++++++- Gems/Camera/Code/CMakeLists.txt | 1 + 12 files changed, 418 insertions(+), 36 deletions(-) create mode 100644 Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake create mode 100644 Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index 5158ebc6d5..5884795413 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -254,4 +254,35 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_googletest( NAME Legacy::EditorLib.Tests ) + + ly_add_target( + NAME EditorLib.Camera.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Legacy + FILES_CMAKE + Lib/Tests/Camera/editor_lib_camera_test_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + . + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzTest + AZ::AzToolsFramework + AZ::AzTestShared + Legacy::EditorLib + Gem::Camera.Editor + Gem::AtomToolsFramework.Static + RUNTIME_DEPENDENCIES + Legacy::EditorLib + ) + + ly_add_source_properties( + SOURCES Lib/Tests/Camera/test_EditorCamera.cpp + PROPERTY COMPILE_DEFINITIONS + VALUES CAMERA_EDITOR_MODULE="$" + ) + + ly_add_googletest( + NAME Legacy::EditorLib.Camera.Tests + ) endif() diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index 498d6f3353..e375e98fb6 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -34,10 +35,12 @@ namespace SandboxEditor : m_viewportId(viewportId) { EditorModularViewportCameraComposerNotificationBus::Handler::BusConnect(viewportId); + Camera::EditorCameraNotificationBus::Handler::BusConnect(); } EditorModularViewportCameraComposer::~EditorModularViewportCameraComposer() { + Camera::EditorCameraNotificationBus::Handler::BusDisconnect(); EditorModularViewportCameraComposerNotificationBus::Handler::BusDisconnect(); } @@ -283,4 +286,22 @@ namespace SandboxEditor m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId()); m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId()); } + + void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) + { + if (viewEntityId.IsValid()) + { + AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM); + + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, + worldFromLocal); + } + else + { + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); + } + } } // namespace SandboxEditor diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index cb223d39e6..e6e71c976c 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -10,13 +10,16 @@ #include #include +#include #include #include namespace SandboxEditor { //! Type responsible for building the editor's modular viewport camera controller. - class EditorModularViewportCameraComposer : private EditorModularViewportCameraComposerNotificationBus::Handler + class EditorModularViewportCameraComposer + : private EditorModularViewportCameraComposerNotificationBus::Handler + , private Camera::EditorCameraNotificationBus::Handler { public: SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId); @@ -32,6 +35,9 @@ namespace SandboxEditor // EditorModularViewportCameraComposerNotificationBus overrides ... void OnEditorModularViewportCameraComposerSettingsChanged() override; + // EditorCameraNotificationBus overrides ... + void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override; + AZStd::shared_ptr m_firstPersonRotateCamera; AZStd::shared_ptr m_firstPersonPanCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; diff --git a/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake b/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake new file mode 100644 index 0000000000..69d3e37f2d --- /dev/null +++ b/Code/Editor/Lib/Tests/Camera/editor_lib_camera_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 + test_EditorCamera.cpp +) diff --git a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp new file mode 100644 index 0000000000..affe5794cc --- /dev/null +++ b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class EditorCameraTestEnvironment : public AZ::Test::GemTestEnvironment + { + // AZ::Test::GemTestEnvironment overrides ... + void AddGemsAndComponents() override; + }; + + void EditorCameraTestEnvironment::AddGemsAndComponents() + { + AddDynamicModulePaths({ CAMERA_EDITOR_MODULE }); + AddComponentDescriptors({ AzToolsFramework::Components::TransformComponent::CreateDescriptor() }); + } + + class EditorCameraFixture : public ::testing::Test + { + public: + AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr; + AZStd::unique_ptr m_editorModularViewportCameraComposer; + AZStd::unique_ptr m_editorLibHandle; + AzFramework::ViewportControllerListPtr m_controllerList; + AZStd::unique_ptr m_entity; + + static const AzFramework::ViewportId TestViewportId; + + void SetUp() override + { + m_editorLibHandle = AZ::DynamicModuleHandle::Create("EditorLib"); + [[maybe_unused]] const bool loaded = m_editorLibHandle->Load(true); + AZ_Assert(loaded, "EditorLib could not be loaded"); + + m_controllerList = AZStd::make_shared(); + m_controllerList->RegisterViewportContext(TestViewportId); + + m_entity = AZStd::make_unique(); + m_entity->Init(); + m_entity->CreateComponent(); + m_entity->Activate(); + + m_editorModularViewportCameraComposer = AZStd::make_unique(TestViewportId); + + auto controller = m_editorModularViewportCameraComposer->CreateModularViewportCameraController(); + // set some overrides for the test + controller->SetCameraViewportContextBuilderCallback( + [this](AZStd::unique_ptr& cameraViewportContext) mutable + { + cameraViewportContext = AZStd::make_unique(); + m_cameraViewportContextView = cameraViewportContext.get(); + }); + + m_controllerList->Add(controller); + } + + void TearDown() override + { + m_editorModularViewportCameraComposer.reset(); + m_cameraViewportContextView = nullptr; + m_entity.reset(); + m_editorLibHandle = {}; + } + }; + + const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337); + + TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged) + { + // Given + const auto entityTransform = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(10.0f, 5.0f, -2.0f)); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, entityTransform); + + // When + // imitate viewport entity changing + Camera::EditorCameraNotificationBus::Broadcast( + &Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId()); + + // ensure the viewport updates after the viewport view entity change + const float deltaTime = 1.0f / 60.0f; + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() }); + + // retrieve updated camera transform + const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + // camera transform matches that of the entity + EXPECT_THAT(cameraTransform, IsClose(entityTransform)); + } + + TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet) + { + // Given + m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f))); + + // When + AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + // reference frame is still the identity + EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } + + TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)); + m_cameraViewportContextView->SetCameraTransform(nextTransform); + + // When + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform)); + } + + TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + // When + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); + + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } + + TEST_F(EditorCameraFixture, InterpolateToTransform) + { + // When + AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, + transformToInterpolateTo, 0.0f); + + // simulate interpolation + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + + const auto finalTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo)); + } + + TEST_F(EditorCameraFixture, InterpolateToTransformWithReferenceSpaceSet) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f)); + + // When + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, + transformToInterpolateTo, 0.0f); + + // simulate interpolation + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + const auto finalTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo)); + EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } +} // namespace UnitTest + +// required to support running integration tests with the Camera Gem +AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv) +{ + ::testing::InitGoogleMock(&argc, argv); + AZ::Test::printUnusedParametersWarning(argc, argv); + AZ::Test::addTestEnvironments({ new UnitTest::EditorCameraTestEnvironment() }); + int result = RUN_ALL_TESTS(); + return result; +} + +IMPLEMENT_TEST_EXECUTABLE_MAIN(); diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index ce5564c7f6..157291cfc2 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -58,28 +58,6 @@ namespace UnitTest return true; } - class TestModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext - { - public: - AZ::Transform GetCameraTransform() const override - { - return m_cameraTransform; - } - - void SetCameraTransform(const AZ::Transform& transform) override - { - m_cameraTransform = transform; - } - - void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler&) override - { - // noop - } - - private: - AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity(); - }; - class ModularViewportCameraControllerFixture : public AllocatorsTestFixture { public: @@ -146,7 +124,7 @@ namespace UnitTest controller->SetCameraViewportContextBuilderCallback( [this](AZStd::unique_ptr& cameraViewportContext) { - cameraViewportContext = AZStd::make_unique(); + cameraViewportContext = AZStd::make_unique(); m_cameraViewportContextView = cameraViewportContext.get(); }); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 27a77f9ce7..8157d369d2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -3642,7 +3642,7 @@ namespace AzToolsFramework } } - void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& newViewId) + void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -3650,12 +3650,12 @@ namespace AzToolsFramework // match the editor camera translation/orientation), record the entity id if we have // a manipulator tracking it (entity id exists in m_entityIdManipulator lookups) // and remove it when recreating manipulators (see InitializeManipulators) - if (newViewId.IsValid()) + if (viewEntityId.IsValid()) { - const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(newViewId); + const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(viewEntityId); if (entityIdLookupIt != m_entityIdManipulators.m_lookups.end()) { - m_editorCameraComponentEntityId = newViewId; + m_editorCameraComponentEntityId = viewEntityId; RegenerateManipulators(); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 935e80951a..6cc89b2d10 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -270,7 +270,7 @@ namespace AzToolsFramework void OnTransformChanged(const AZ::Transform& localTM, const AZ::Transform& worldTM) override; // Camera::EditorCameraNotificationBus overrides ... - void OnViewportViewEntityChanged(const AZ::EntityId& newViewId) override; + void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override; // EditorContextVisibilityNotificationBus overrides ... void OnEntityVisibilityChanged(bool visibility) override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h index 42fe9a01c9..4956fbc1dc 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h @@ -116,11 +116,18 @@ namespace AtomToolsFramework // ModularViewportCameraControllerRequestBus overrides ... void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) override; AZStd::optional LookAtAfterInterpolation() const override; + AZ::Transform GetReferenceFrame() const override; + void SetReferenceFrame(const AZ::Transform& worldFromLocal) override; + void ClearReferenceFrame() override; private: // AzFramework::ViewportDebugDisplayEventBus overrides ... void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + //! Update the reference frame after a change has been made to the camera + //! view without updating the internal camera via user input. + void RefreshReferenceFrame(); + //! The current mode the camera controller is in. enum class CameraMode { @@ -139,6 +146,8 @@ namespace AtomToolsFramework AzFramework::Camera m_camera; //!< The current camera state (pitch/yaw/position/look-distance). AzFramework::Camera m_targetCamera; //!< The target (next) camera state that m_camera is catching up to. + AzFramework::Camera m_previousCamera; //!< The state of the camera from the previous frame. + AZStd::optional m_storedCamera; //!< A potentially stored camera for when a custom reference frame is set. AzFramework::CameraSystem m_cameraSystem; //!< The camera system responsible for managing all CameraInputs. AzFramework::CameraProps m_cameraProps; //!< Camera properties to control rotate and translate smoothness. CameraControllerPriorityFn m_priorityFn; //!< Controls at what priority the camera controller should respond to events. @@ -147,6 +156,7 @@ namespace AtomToolsFramework CameraMode m_cameraMode = CameraMode::Control; //!< The current mode the camera is operating in. AZStd::optional m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished. //!< Will be cleared when the view changes (camera looks away). + AZ::Transform m_referenceFrameOverride = AZ::Transform::CreateIdentity(); //!< //! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally). bool m_updatingTransformInternally = false; //! Listen for camera view changes outside of the camera controller. @@ -154,4 +164,17 @@ namespace AtomToolsFramework //! The current instance of the modular camera viewport context. AZStd::unique_ptr m_modularCameraViewportContext; }; + + //! Placeholder implementation for ModularCameraViewportContext (useful for verifying the interface). + class PlaceholderModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext + { + public: + AZ::Transform GetCameraTransform() const override; + void SetCameraTransform(const AZ::Transform& transform) override; + void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler) override; + + private: + AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity(); + AZ::RPI::ViewportContext::MatrixChangedEvent m_viewMatrixChangedEvent; + }; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h index ae4dc5dd25..388d24164a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h @@ -35,6 +35,16 @@ namespace AtomToolsFramework //! Look at point after an interpolation has finished and no translation has occurred. virtual AZStd::optional LookAtAfterInterpolation() const = 0; + //! Return the current reference frame. + //! @note If a reference frame has not been set or a frame has been cleared, this is just the identity. + virtual AZ::Transform GetReferenceFrame() const = 0; + + //! Set a new reference frame other than the identity for the camera controller. + virtual void SetReferenceFrame(const AZ::Transform& worldFromLocal) = 0; + + //! Clear the current reference frame to restore the identity. + virtual void ClearReferenceFrame() = 0; + protected: ~ModularViewportCameraControllerRequests() = default; }; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index 4419e0c49f..bcc9a08f77 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -30,6 +30,18 @@ namespace AtomToolsFramework ""); AZ_CVAR(float, ed_cameraSystemOrbitPointSize, 0.1f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); + AZ::Transform TransformFromMatrix4x4(const AZ::Matrix4x4& matrix) + { + const auto rotation = AZ::Matrix3x3::CreateFromMatrix4x4(matrix); + const auto translation = matrix.GetTranslation(); + return AZ::Transform::CreateFromMatrix3x3AndTranslation(rotation, translation); + } + + AZ::Matrix4x4 Matrix4x4FromTransform(const AZ::Transform& transform) + { + return AZ::Matrix4x4::CreateFromQuaternionAndTranslation(transform.GetRotation(), transform.GetTranslation()); + } + // debug void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength) { @@ -167,11 +179,19 @@ namespace AtomToolsFramework controller->SetupCameraControllerPriority(m_priorityFn); controller->SetupCameraControllerViewportContext(m_modularCameraViewportContext); - auto handleCameraChange = [this](const AZ::Matrix4x4&) + auto handleCameraChange = [this]([[maybe_unused]] const AZ::Matrix4x4& cameraView) { // ignore these updates if the camera is being updated internally if (!m_updatingTransformInternally) { + if (m_storedCamera.has_value()) + { + // if an external change occurs ensure we update the stored reference frame if one is set + RefreshReferenceFrame(); + return; + } + + m_previousCamera = m_targetCamera; UpdateCameraFromTransform(m_targetCamera, m_modularCameraViewportContext->GetCameraTransform()); m_camera = m_targetCamera; } @@ -231,7 +251,7 @@ namespace AtomToolsFramework } } - m_modularCameraViewportContext->SetCameraTransform(m_camera.Transform()); + m_modularCameraViewportContext->SetCameraTransform(m_referenceFrameOverride * m_camera.Transform()); } else if (m_cameraMode == CameraMode::Animation) { @@ -240,6 +260,8 @@ namespace AtomToolsFramework return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); }; + m_cameraAnimation.m_time = AZ::GetClamp(m_cameraAnimation.m_time + event.m_deltaTime.count(), 0.0f, 1.0f); + const auto& [transformStart, transformEnd, animationTime] = m_cameraAnimation; const float transitionTime = smootherStepFn(animationTime); @@ -253,14 +275,13 @@ namespace AtomToolsFramework m_camera.m_lookAt = current.GetTranslation(); m_targetCamera = m_camera; + m_modularCameraViewportContext->SetCameraTransform(current); + if (animationTime >= 1.0f) { m_cameraMode = CameraMode::Control; + RefreshReferenceFrame(); } - - m_cameraAnimation.m_time = AZ::GetClamp(animationTime + event.m_deltaTime.count(), 0.0f, 1.0f); - - m_modularCameraViewportContext->SetCameraTransform(current); } m_updatingTransformInternally = false; @@ -280,7 +301,7 @@ namespace AtomToolsFramework void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance) { m_cameraMode = CameraMode::Animation; - m_cameraAnimation = CameraAnimation{ m_camera.Transform(), worldFromLocal, 0.0f }; + m_cameraAnimation = CameraAnimation{ m_referenceFrameOverride * m_camera.Transform(), worldFromLocal, 0.0f }; m_lookAtAfterInterpolation = worldFromLocal.GetTranslation() + worldFromLocal.GetBasisY() * lookAtDistance; } @@ -288,4 +309,59 @@ namespace AtomToolsFramework { return m_lookAtAfterInterpolation; } + + AZ::Transform ModularViewportCameraControllerInstance::GetReferenceFrame() const + { + return m_referenceFrameOverride; + } + + void ModularViewportCameraControllerInstance::SetReferenceFrame(const AZ::Transform& worldFromLocal) + { + if (!m_storedCamera.has_value()) + { + m_storedCamera = m_previousCamera; + } + + m_referenceFrameOverride = worldFromLocal; + m_targetCamera.m_pitch = 0.0f; + m_targetCamera.m_yaw = 0.0f; + m_targetCamera.m_lookAt = AZ::Vector3::CreateZero(); + m_targetCamera.m_lookDist = 0.0f; + m_camera = m_targetCamera; + } + + void ModularViewportCameraControllerInstance::ClearReferenceFrame() + { + m_referenceFrameOverride = AZ::Transform::CreateIdentity(); + + if (m_storedCamera.has_value()) + { + m_targetCamera = m_storedCamera.value(); + m_camera = m_targetCamera; + } + + m_storedCamera.reset(); + } + + void ModularViewportCameraControllerInstance::RefreshReferenceFrame() + { + m_referenceFrameOverride = m_modularCameraViewportContext->GetCameraTransform() * m_camera.Transform().GetInverse(); + } + + AZ::Transform PlaceholderModularCameraViewportContextImpl::GetCameraTransform() const + { + return m_cameraTransform; + } + + void PlaceholderModularCameraViewportContextImpl::SetCameraTransform(const AZ::Transform& transform) + { + m_cameraTransform = transform; + m_viewMatrixChangedEvent.Signal(AzFramework::CameraViewFromCameraTransform(Matrix4x4FromTransform(transform))); + } + + void PlaceholderModularCameraViewportContextImpl::ConnectViewMatrixChangedHandler( + AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler) + { + handler.Connect(m_viewMatrixChangedEvent); + } } // namespace AtomToolsFramework diff --git a/Gems/Camera/Code/CMakeLists.txt b/Gems/Camera/Code/CMakeLists.txt index f34ac8a698..c9f0bf768f 100644 --- a/Gems/Camera/Code/CMakeLists.txt +++ b/Gems/Camera/Code/CMakeLists.txt @@ -57,6 +57,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) PRIVATE AZ::AzToolsFramework Gem::Camera.Static + Gem::AtomToolsFramework.Static RUNTIME_DEPENDENCIES Legacy::EditorCommon ) From 8b7439556096337988d476a026abb8d42f268270 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Wed, 22 Sep 2021 10:15:20 +0200 Subject: [PATCH 51/63] EMotionFX: Basic profile instrumentation for anim graph (#4240) Signed-off-by: Benjamin Jillich --- Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp | 4 ++++ .../Code/EMotionFX/Source/AnimGraphInstance.cpp | 10 ++++++++++ .../Code/EMotionFX/Source/AnimGraphMotionNode.cpp | 4 ++++ .../Code/EMotionFX/Source/AnimGraphStateMachine.cpp | 8 ++++++++ .../Code/EMotionFX/Source/BlendSpace1DNode.cpp | 4 ++++ .../Code/EMotionFX/Source/BlendSpace2DNode.cpp | 4 ++++ Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp | 9 ++++++++- .../Code/EMotionFX/Source/BlendTreeBlend2Node.cpp | 5 ++++- Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp | 2 ++ 9 files changed, 48 insertions(+), 2 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp index 5a4ca95670..b547b92306 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp @@ -372,6 +372,8 @@ namespace EMotionFX // updates the skinning matrices of all nodes void ActorInstance::UpdateSkinningMatrices() { + AZ_PROFILE_SCOPE(Animation, "ActorInstance::UpdateSkinningMatrices"); + AZ::Matrix3x4* skinningMatrices = m_transformData->GetSkinningMatrices(); const Pose* pose = m_transformData->GetCurrentPose(); @@ -596,6 +598,8 @@ namespace EMotionFX // update the bounding volume void ActorInstance::UpdateBounds(size_t geomLODLevel, EBoundsType boundsType, uint32 itemFrequency) { + AZ_PROFILE_SCOPE(Animation, "ActorInstance::UpdateBounds"); + // depending on the bounding volume update type switch (boundsType) { diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp index d0c77721d1..7f9e8ba6fc 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp @@ -218,6 +218,8 @@ namespace EMotionFX // output the results into the internal pose object void AnimGraphInstance::Output(Pose* outputPose) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::Output"); + // reset max used const uint32 threadIndex = m_actorInstance->GetThreadIndex(); AnimGraphPosePool& posePool = GetEMotionFX().GetThreadData(threadIndex)->GetPosePool(); @@ -854,6 +856,8 @@ namespace EMotionFX // synchronize all nodes, based on sync tracks etc void AnimGraphInstance::Update(float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::Update"); + // pass 0: (Optional, networking only) When this instance is shared between network, restore the instance using an animgraph snapshot. if (m_snapshot) { @@ -940,6 +944,8 @@ namespace EMotionFX // reset all node pose ref counts void AnimGraphInstance::ResetPoseRefCountsForAllNodes() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetPoseRefCountsForAllNodes"); + const size_t numNodes = m_animGraph->GetNumNodes(); for (size_t i = 0; i < numNodes; ++i) { @@ -951,6 +957,8 @@ namespace EMotionFX // reset all node pose ref counts void AnimGraphInstance::ResetRefDataRefCountsForAllNodes() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetRefDataRefCountsForAllNodes"); + const size_t numNodes = m_animGraph->GetNumNodes(); for (size_t i = 0; i < numNodes; ++i) { @@ -962,6 +970,8 @@ namespace EMotionFX // reset all node flags void AnimGraphInstance::ResetFlagsForAllObjects() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetFlagsForAllObjects"); + MCore::MemSet(m_objectFlags.data(), 0, sizeof(uint32) * m_objectFlags.size()); for (AnimGraphInstance* childInstance : m_childAnimGraphInstances) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp index 6bcc69360b..c5e50ed9dd 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp @@ -394,6 +394,8 @@ namespace EMotionFX // the main process method of the final node void AnimGraphMotionNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphMotionNode::Output"); + // if this motion is disabled, output the bind pose if (m_disabled) { @@ -537,6 +539,8 @@ namespace EMotionFX void AnimGraphMotionNode::UniqueData::Update() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphMotionNode::Update"); + AnimGraphMotionNode* motionNode = azdynamic_cast(m_object); AZ_Assert(motionNode, "Unique data linked to incorrect node type."); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp index 9b4c2d07ac..0024d97a45 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp @@ -92,6 +92,8 @@ namespace EMotionFX void AnimGraphStateMachine::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::Update"); + ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); AnimGraphPose* outputPose = nullptr; @@ -476,6 +478,8 @@ namespace EMotionFX void AnimGraphStateMachine::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::Update"); + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); // Defer switch to entry state. @@ -622,6 +626,8 @@ namespace EMotionFX void AnimGraphStateMachine::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::PostUpdate"); + RequestRefDatas(animGraphInstance); UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); AnimGraphRefCountedData* data = uniqueData->GetRefCountedData(); @@ -1344,6 +1350,8 @@ namespace EMotionFX void AnimGraphStateMachine::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::TopDownUpdate"); + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); if (!IsTransitioning(uniqueData)) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp index 0e823075a4..deeb593c96 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp @@ -166,6 +166,8 @@ namespace EMotionFX void BlendSpace1DNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace1DNode::Output"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; @@ -276,6 +278,8 @@ namespace EMotionFX void BlendSpace1DNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace1DNode::Update"); + if (!m_disabled) { EMotionFX::BlendTreeConnection* paramConnection = GetInputPort(INPUTPORT_VALUE).m_connection; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp index 03a7552410..66366087f8 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp @@ -282,6 +282,8 @@ namespace EMotionFX void BlendSpace2DNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace2DNode::Output"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; @@ -402,6 +404,8 @@ namespace EMotionFX void BlendSpace2DNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace2DNode::Update"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp index f742696275..a4109a5a60 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp @@ -116,10 +116,11 @@ namespace EMotionFX return nullptr; } - // process the blend tree and calculate its output void BlendTree::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::Output"); + AZ_Assert(m_finalNode, "There should always be a final node. Something seems to be wrong with the blend tree creation."); // get the output pose @@ -164,6 +165,8 @@ namespace EMotionFX // post sync update void BlendTree::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::PostUpdate"); + // if this node is disabled, exit if (m_disabled) { @@ -212,6 +215,8 @@ namespace EMotionFX // update all nodes void BlendTree::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::Update"); + // if this node is disabled, output the bind pose if (m_disabled) { @@ -256,6 +261,8 @@ namespace EMotionFX // top down update void BlendTree::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::TopDownUpdate"); + // get the final node AnimGraphNode* finalNode = GetRealFinalNode(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp index 06223d4b14..b14f72c7c3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp @@ -45,6 +45,8 @@ namespace EMotionFX void BlendTreeBlend2Node::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTreeBlend2Node::Update"); + if (m_disabled) { AnimGraphNodeData* uniqueData = FindOrCreateUniqueNodeData(animGraphInstance); @@ -88,9 +90,10 @@ namespace EMotionFX } } - void BlendTreeBlend2Node::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendTreeBlend2Node::Output"); + if (m_disabled) { RequestPoses(animGraphInstance); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp index a6ddb776c2..237389312f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp @@ -147,6 +147,8 @@ namespace EMotionFX // update motion queue and instances void MotionSystem::Update(float timePassed, bool updateNodes) { + AZ_PROFILE_SCOPE(Animation, "MotionSystem::Update"); + MCORE_UNUSED(updateNodes); // update the motion queue From ce1614bd575b1b877b50dfba88c3c49c933eb4a9 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Wed, 22 Sep 2021 08:35:35 -0400 Subject: [PATCH 52/63] Merged with dev Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- Gems/Multiplayer/Code/Tests/MockInterfaces.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h index 6466fbb555..42e034d234 100644 --- a/Gems/Multiplayer/Code/Tests/MockInterfaces.h +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -40,7 +40,10 @@ namespace UnitTest class MockNetworkEntityManager : public Multiplayer::INetworkEntityManager { public: - MOCK_METHOD4(CreateEntitiesImmediate, EntityList (const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate)); + MOCK_METHOD2(RequestNetSpawnableInstantiation, AZStd::unique_ptr (const AZ::Data::Asset&, const AZ::Transform&)); + MOCK_METHOD4( + CreateEntitiesImmediate, + EntityList (const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate)); MOCK_CONST_METHOD1(GetNetEntityIdById, Multiplayer::NetEntityId (const AZ::EntityId&)); MOCK_METHOD0(GetNetworkEntityTracker, Multiplayer::NetworkEntityTracker* ()); MOCK_METHOD0(GetNetworkEntityAuthorityTracker, Multiplayer::NetworkEntityAuthorityTracker* ()); From 6a1b685ea7028185554c8cbf1f670fd0fc896d8b Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 22 Sep 2021 09:03:24 -0500 Subject: [PATCH 53/63] fixing duplicate define Signed-off-by: Guthrie Adams --- .../Platform/Linux/ShaderManagementConsole_Traits_Linux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 355b135f50..2897402f04 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,5 @@ */ #pragma once -#define #define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT "" +#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT "" From a93c1758f4b8d94d1539f598ccb317006c8fb37d Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 22 Sep 2021 11:03:46 -0500 Subject: [PATCH 54/63] restoring immediate rpi initialization and window creation to fix broken tests Signed-off-by: Guthrie Adams --- .../Code/Source/BootstrapSystemComponent.cpp | 5 +++- .../Application/AtomToolsApplication.h | 7 ----- .../Application/AtomToolsApplication.cpp | 27 ++++++++----------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index e3749175bc..8a6b7db1ca 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -176,7 +176,10 @@ namespace AZ m_isAssetCatalogLoaded = true; - RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + if (!RPI::RPISystemInterface::Get()->IsInitialized()) + { + RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + } if (!RPI::RPISystemInterface::Get()->IsInitialized()) { diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h index f87aca641d..d55755a242 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -36,7 +35,6 @@ namespace AtomToolsFramework , public AzQtComponents::AzQtApplication , protected AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler , protected AzFramework::AssetSystemStatusBus::Handler - , protected AzFramework::AssetCatalogEventBus::Handler , protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler , protected AZ::UserSettingsOwnerRequestBus::Handler , protected AtomToolsMainWindowNotificationBus::Handler @@ -69,11 +67,6 @@ namespace AtomToolsFramework void OnMainWindowClosing() override; ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - // AzFramework::AssetCatalogEventBus::Handler overrides ... - void OnCatalogLoaded(const char* catalogFile) override; - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// // AssetDatabaseRequestsBus::Handler overrides... bool GetAssetDatabaseLocation(AZStd::string& result) override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 5751271312..a949852b5e 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -92,7 +92,6 @@ namespace AtomToolsFramework AtomToolsMainWindowNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); } void AtomToolsApplication::CreateReflectionManager() @@ -175,11 +174,18 @@ namespace AtomToolsFramework AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast( &AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized); - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml"); + if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized()) + { + AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + } + LoadSettings(); + AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); + AtomToolsMainWindowNotificationBus::Handler::BusConnect(); + auto editorPythonEventsInterface = AZ::Interface::Get(); if (editorPythonEventsInterface) { @@ -189,6 +195,9 @@ namespace AtomToolsFramework editorPythonEventsInterface->StartPython(); } + QTimer::singleShot(0, this, [this]() { + ProcessCommandLine(m_commandLine); + }); m_timer.start(); } @@ -197,26 +206,12 @@ namespace AtomToolsFramework ExitMainLoop(); } - void AtomToolsApplication::OnCatalogLoaded(const char* catalogFile) - { - AZ_UNUSED(catalogFile); - - // Delay execution of commands and scripts post initialization - AZ::TickBus::QueueFunction([this]() { - AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); - AtomToolsMainWindowNotificationBus::Handler::BusConnect(); - ProcessCommandLine(m_commandLine); - }); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); - } - void AtomToolsApplication::Destroy() { // before modules are unloaded, destroy UI to free up any assets it cached AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); m_styleManager.reset(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); AtomToolsMainWindowNotificationBus::Handler::BusDisconnect(); From 9912e68c5966fcff4fa885625239d08448e03bc7 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 22 Sep 2021 11:19:13 -0500 Subject: [PATCH 55/63] reverting unneeded changes Signed-off-by: Guthrie Adams --- .../Code/Source/Application/AtomToolsApplication.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index a949852b5e..13b9a2ba27 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -183,8 +182,8 @@ namespace AtomToolsFramework LoadSettings(); - AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); AtomToolsMainWindowNotificationBus::Handler::BusConnect(); + AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); auto editorPythonEventsInterface = AZ::Interface::Get(); if (editorPythonEventsInterface) @@ -195,9 +194,9 @@ namespace AtomToolsFramework editorPythonEventsInterface->StartPython(); } - QTimer::singleShot(0, this, [this]() { - ProcessCommandLine(m_commandLine); - }); + // Delay execution of commands and scripts post initialization + QTimer::singleShot(0, [this]() { ProcessCommandLine(m_commandLine); }); + m_timer.start(); } From d00d9a9515363ed5474462c6460693ec08532925 Mon Sep 17 00:00:00 2001 From: Roman <69218254+amzn-rhhong@users.noreply.github.com> Date: Wed, 22 Sep 2021 09:41:45 -0700 Subject: [PATCH 56/63] Fix the crash when switching between sim obj and physix layout in animation editor. (#4185) * Update the simulated object layout Signed-off-by: rhhong * Fixed the bug. Do not store uncessory cached selected model indices which would become stall. Signed-off-by: rhhong * update the simulted object layout. Signed-off-by: rhhong * Get rid of locally cached selectedIndices variable. Signed-off-by: rhhong * Remove debug code Signed-off-by: rhhong --- .../Editor/Layouts/SimulatedObjects.layout | Bin 6901 -> 7805 bytes .../Editor/Plugins/Cloth/ClothJointWidget.cpp | 10 +-- .../HitDetection/HitDetectionJointWidget.cpp | 10 +-- .../Plugins/Ragdoll/RagdollNodeWidget.cpp | 16 +++-- .../SimulatedObjectColliderWidget.cpp | 20 +++--- .../SkeletonOutlinerPlugin.cpp | 8 +-- .../Editor/SkeletonModelJointWidget.cpp | 61 +++++++++--------- .../Source/Editor/SkeletonModelJointWidget.h | 4 +- 8 files changed, 67 insertions(+), 62 deletions(-) diff --git a/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout b/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout index a7879584a9601f005f1c5aeba8d04f3320c8c66c..f52df642e84ae9f7b9b8b2f3685b3cb684345746 100644 GIT binary patch literal 7805 zcmc&&TWl0n82-20ZZQa=KumnlAzlK;;LgnM&Q871f>29J+fXZDjO~_AV9R!QZMR-X z)I_3*5ie1y#3ziJ7!!jJCM3cGi9RU45CSIfps3M^#v4yw3ibQW%yf6#Y29Avp3R&y zGkebW-@fyoGiT$L&eo)X-%N#eLhGe=;P}vG3x#v1de< zEN}RbEncbOKcD<->=a+{eJReek5=Ed7caFVznvnE-~W7r`^+&%S$QQ2eF#FF(n5TW z*N)tJ<}S473u_dvk2kTu|IGdz*LdLEX|~@+T>U$4xbzgCSxj7h|7hm(ca$7)@%{c& z`9lGejGy@I%kQcALgD&&nujmq`q!7w@%0|ahTOnFD%+>6PYp`G*R|uz7598V|ND4F zy?n_cIo$gF_pN!qv9v#X(LP_EKih`>oZEn@JI@vyFP}`Qy_T?Uz1^0s)wRBsz60xLZ*mgDi(7DISIE7qRYacFT(phIv zs^JISh{jFbu;PYc#w}YGo)24cg>)`EB-QnUVHt5tPwG)yPuNMbYJSi0j1pL!ZXy9YhAlAkV7{7kVVfnQW?(Sa68dDst$ghywGT$Ew}H7clq zT(=L1VJz5Kbwd~CimU@Qj$e#AFN4k_E^_$X+>xUagVu2%*Ix(ZCAfN_`z( zUZyu<%Fjz4g}#VqEyMiQ>~z){GvHb9MHKjmC3Ifg0+KunecuG~6S%NxslUu(N+?x<&NRm)1aHVmnLf9K2xeM)k zfW54w)zGrlWw565hpRie7xRSJViOc4)b6$-9Q|+w%q~a@A=;^U&-*m^@5taJ#*>dU z#8y%f6Hg+YA^pl3FyS$%@Gf2)D>vh|GBe@;Nx(ZcykUcggpK*0!QjwARI1Fc5yi=6 zLMA2qZYxHV67Q8JI3bOc&oG~mBBxU3JZVxJuT%OjEG;K)5d zz+4r1o)t?_AZb6@06st$5F1ZP>IAoj&tT9aYN$85oLz=uRvfjiMymvb@iv}Jjwg}E zeF%1iKSgK`!?ByjNgKc}3t=E#sT5~~sg$FeEXEoP4QXf}BnC|?SBhB$Ghbm)!MMUP zIa;|n)C3*QV%UhsmB=svU%-pnqEeg*$Qw1UqebGz1fyi7Yrk}=kPIUbDdcAd&Tj{E z`*F7u&)e{C9|Gjy-VIPU@;HMy)rc0A>GR(B9Kz@Y5Hjm-hFmD1o3r-bHtbFQqfB~GC11j3gR>jEGaPst&7#I2-j^)bUMzEomXHp&*+B->@}3?|3_^_^~7gf2n{b;d(IX!YF{d2y?n zWSnBmm}TKw2awG~V*%Z34xoh?l(PAMOW6_O>@AQ2>U3nkuPkyDyI25<1zUM?dUMYeGV0gF4d&|(i<+0$4NdNM*yP^ zr)!}*akJh>-CUthcU2U#x(nd(N*^CBipq05-@-mq7O`b+XAUxF*_(#Z1Eg6z!Z3K0^#JQT1@v z@X0Gd*7{%Zkxwv)teUyC#qW&qk9gn&F*r1UseiU;ZDkCea&PCE72-C#wDjO1$X5Sc zA@V2w{{@}qV(a*^0&s#DOk2Yzinh+3!Qm4SRZ`S48Dyt_%2EF`?95`s-0buYIe>E< z*CYn_l-s$$yEJITGSN5OY6o(_Kc}f5!k`~H64(4~{K<+52M6BDHF?RK=6C}-vCN-d y0UsnKdQ~T8^J$%*gT_xfJnIxxah&qERbiEh%i%+CYN}h26lGcDJ^>k?^32 zL?Z^GQ9<+teK5Xgf>G2&A9ye^A;cIHCHSJe7=L0!eL@iYzS%o_?=A~&Y3a^pX71cO z^PO|PIcI)$_1eL{?&0*%qZ=Q0TBV%2OWP8rk+2NQlnY3F`!>FxeMZDc$1*2kVSDrF zzFxMwkePx?)lr;R7FLfx*dp?s?u>CSy5;ZTtKq{0dEm?{B~O7z3a!uS{$onM(edS1 zQ9O~}!)$B3{*U(bQ%~Iw;)V9;?~DH~%h7G!Uu0O}e9%+YPxGPi`**)^>L1|fq{r8` zHJ-Ld+ioOb}GAW`t1Ev{e43{z3VK~cMRKfZQHXPNA@iGkoa9eJ#pMU z66OD`Wp5~XKkp6sp}eD|Wyhoa4(>lt#ouBnd*Rn74!y^IS}*FGp2+5V`Vs@#d?q`Y zSf9&h3Qx(L^I#jE>6>2CNqLsJF;Wh zVj-VMmx^P#e0EZru|AlV;o7F$qbq*(WmZ1RgH1^)E8_{YpB=R@+$=56LFU%D5DyC^?jQnZi>^GO9I& zdIx$W>i#V8#SoN0Yuhank7HXcApbH`WD}?^eh!e^7emecW#stxGeBO?5j}xJ&w_PllvNdSxA;sW!n)z?*C0DE3ZYawh`6|AlJDC_P4%n5|};u91pmM+&ghT`F|2K=um zBbcJC$~*a-gO_Gyvc@mzGi1z0NvW8VKqASIQAGxbyA5XCf)}4xn{h{#87@GQV8>UW zu7Y(mGZ-8@h(e{=H6jc&8!~-_fTk^+QA)f|xp)osRIyJ1-zE_51rWId2#8gY=UFfjvvwkBKfM9|0A1kNXr-tVXpMi10Z(uw6GvBj zm!&+*MQN(h2F7u`kCx5xLl{!w5Z=}LDPC^C%QY3Jt>axDW|!_tr5F*mQjRovoNIV! zY!dZ7#JE@~dI%z4VNgL=;n*ClXpX%K9p1(GuE1A1mjLiLyl4%TVkBVQXhB~j1{ftP zT}Ne5O-Tt8F@jYz3-Z(O+)+GjMSDHIW-vinJeMbE6INSJ;-yCPs`Ypx7+=6N1``OG zZMQ>iiApfh+S!Yx&5%sab5LlGwBKv4-# z*@$tiF9G$7h^RVKp%%5-w3)6-zoL&Y^qs(`LKDeyW5)Hv0~na&YPPvrg)`T};Mp88 zF7^kA<;xm}TG>#PQ`DFeQ_W%fBH(?ZP*Geh2jPON)+&(Iz7TdyL?z~BwXBtgWrM7k zMsWOJ-^+zX>@Vo>O;m?_8)9)l1sOw(D`wSn-4C=i8WC>a@){ZtgH#p&%d55!N3AqG zYj7_zRSQchXo|0Zio6D2;<5Gg%@2d@hDUTz~}%GoLe z0gETJ*!%!Is8r1G9}S#Yq?0w*3kDQ79@f>{4UZw*w+-b?T%&Q`K{Pt$C?wJ9D2sv7 z)P~0NkFa$PZNPaMxpG{lhDfd00c4jtL&X{o{>$yV3U-$&!-8#+#Z0m~NfwemM_&6J z2t0rn)3>$GF%md?k!x*@@5jpxp{+AmeE_5qH}q&4K%<_W`c%8{2iSQAou+SRxMfwY zCBUgSr?GK3;K|U=#uoHT?pA?)v6I1rt~TSLo~`<()cz;F^KdZ}TPqhO;MAK_+8RF= z+B)+&jUR)kNK%1c&rW@B>v$S=<}qSsb_Ulr;2cG+^<8eKQ9I{&-+@LsQ)VT@;>*vJ_?`1A*(1~gG^h#inuQ_5x^lHwW&8BsF4XWJT0_!-t Y4c;%&vwCCXjrClearInstances(false); @@ -142,7 +143,7 @@ namespace EMotionFX m_collidersWidget->Reset(); } - m_jointLimitWidget->Update(m_selectedModelIndices[0]); + m_jointLimitWidget->Update(selectedModelIndices[0]); m_ragdollNodeCard->setExpanded(true); m_ragdollNodeCard->show(); m_jointLimitWidget->show(); @@ -169,31 +170,32 @@ namespace EMotionFX void RagdollNodeWidget::OnAddRemoveRagdollNode() { + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); if (GetRagdollNodeConfig()) { // The node is present in the ragdoll, remove it. - RagdollNodeInspectorPlugin::RemoveFromRagdoll(m_selectedModelIndices); + RagdollNodeInspectorPlugin::RemoveFromRagdoll(selectedModelIndices); } else { // The node is not part of the ragdoll, add it. - RagdollNodeInspectorPlugin::AddToRagdoll(m_selectedModelIndices); + RagdollNodeInspectorPlugin::AddToRagdoll(selectedModelIndices); } } void RagdollNodeWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::Ragdoll, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::Ragdoll, colliderType); } void RagdollNodeWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Ragdoll); } void RagdollNodeWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll, replace); + ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Ragdoll, replace); } void RagdollNodeWidget::OnRemoveCollider(size_t colliderIndex) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp index 8c6c72934d..b56cafc9be 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp @@ -131,7 +131,8 @@ namespace EMotionFX void SimulatedObjectColliderWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); + if (selectedModelIndices.size() == 1) { Physics::CharacterColliderNodeConfiguration* nodeConfig = GetNodeConfig(); if (nodeConfig) @@ -172,12 +173,13 @@ namespace EMotionFX } AZStd::string labelText; + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); const AZStd::vector& simObjs = actor->GetSimulatedObjectSetup()->GetSimulatedObjects(); for (const SimulatedObject* obj : simObjs) { - for (int i = 0; i < m_selectedModelIndices.size(); ++i) + for (int i = 0; i < selectedModelIndices.size(); ++i) { - Node* node = m_selectedModelIndices[i].data(SkeletonModel::ROLE_POINTER).value(); + Node* node = selectedModelIndices[i].data(SkeletonModel::ROLE_POINTER).value(); if (obj->FindSimulatedJointBySkeletonJointIndex(node->GetNodeIndex())) { if (!labelText.empty()) @@ -208,8 +210,9 @@ namespace EMotionFX return; } + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); // Only show the notification when it is single selection. - if (m_selectedModelIndices.size() != 1) + if (selectedModelIndices.size() != 1) { return; } @@ -250,17 +253,18 @@ namespace EMotionFX void SimulatedObjectColliderWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::SimulatedObjectCollider, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::SimulatedObjectCollider, colliderType); } void SimulatedObjectColliderWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider); } void SimulatedObjectColliderWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider, replace); + ColliderHelpers::PasteColliderFromClipboard( + GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider, replace); } void SimulatedObjectColliderWidget::OnRemoveCollider(size_t colliderIndex) @@ -270,7 +274,7 @@ namespace EMotionFX Physics::CharacterColliderNodeConfiguration* SimulatedObjectColliderWidget::GetNodeConfig() const { - AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted"); + AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted"); Actor* actor = GetActor(); Node* joint = GetNode(); if (!actor || !joint) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp index f8ca291d55..ec8d1147f2 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp @@ -211,15 +211,15 @@ namespace EMotionFX AZ::Outcome SkeletonOutlinerPlugin::GetSelectedRowIndices() { - return AZ::Success(m_selectedRows); + return AZ::Success(m_treeView->selectionModel()->selectedRows()); } void SkeletonOutlinerPlugin::OnSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) { - m_selectedRows = m_treeView->selectionModel()->selectedRows(); - if (m_selectedRows.size() == 1) + QModelIndexList selectedRows = m_treeView->selectionModel()->selectedRows(); + if (selectedRows.size() == 1) { - const QModelIndex& modelIndex = m_selectedRows[0]; + const QModelIndex& modelIndex = selectedRows[0]; Node* selectedNode = modelIndex.data(SkeletonModel::ROLE_POINTER).value(); Actor* selectedActor = modelIndex.data(SkeletonModel::ROLE_ACTOR_POINTER).value(); SkeletonOutlinerNotificationBus::Broadcast(&SkeletonOutlinerNotifications::SingleNodeSelectionChanged, selectedActor, selectedNode); diff --git a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp index e9eeb7530d..9cfc763fc6 100644 --- a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp @@ -72,14 +72,7 @@ namespace EMotionFX setLayout(mainLayout); - AZ::Outcome selectedRowIndicesOutcome; - QModelIndexList selectedModelIndices; - SkeletonOutlinerRequestBus::BroadcastResult(selectedRowIndicesOutcome, &SkeletonOutlinerRequests::GetSelectedRowIndices); - if (selectedRowIndicesOutcome.IsSuccess()) - { - selectedModelIndices = selectedRowIndicesOutcome.GetValue(); - } - Reinit(selectedModelIndices); + Reinit(); // Connect to the model. SkeletonModel* skeletonModel = nullptr; @@ -92,9 +85,9 @@ namespace EMotionFX } } - void SkeletonModelJointWidget::Reinit(const QModelIndexList& selectedModelIndices) + void SkeletonModelJointWidget::Reinit() { - m_selectedModelIndices = selectedModelIndices; + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); if (!EMStudio::GetManager()->GetIgnoreVisibility() && !isVisible()) { @@ -103,15 +96,15 @@ namespace EMotionFX if (GetActor()) { - if (!m_selectedModelIndices.isEmpty()) + if (!selectedModelIndices.isEmpty()) { - if (m_selectedModelIndices.size() == 1) + if (selectedModelIndices.size() == 1) { m_jointNameLabel->setText(GetNode()->GetName()); } else { - m_jointNameLabel->setText(QString("%1 joints selected").arg(m_selectedModelIndices.size())); + m_jointNameLabel->setText(QString("%1 joints selected").arg(selectedModelIndices.size())); } m_noSelectionWidget->hide(); @@ -136,7 +129,7 @@ namespace EMotionFX void SkeletonModelJointWidget::showEvent(QShowEvent* event) { QWidget::showEvent(event); - Reinit(m_selectedModelIndices); + Reinit(); } void SkeletonModelJointWidget::OnSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) @@ -146,36 +139,28 @@ namespace EMotionFX if (skeletonModel) { const QModelIndexList selectedRows = skeletonModel->GetSelectionModel().selectedRows(); - Reinit(selectedRows); } + Reinit(); } void SkeletonModelJointWidget::OnDataChanged([[maybe_unused]] const QModelIndex& topLeft, [[maybe_unused]] const QModelIndex& bottomRight, [[maybe_unused]] const QVector& roles) { - Reinit(m_selectedModelIndices); + Reinit(); } void SkeletonModelJointWidget::OnModelReset() { - Reinit(QModelIndexList()); + Reinit(); } Actor* SkeletonModelJointWidget::GetActor() const { Actor* actor = nullptr; - if (!m_selectedModelIndices.empty()) + SkeletonModel* skeletonModel = nullptr; + SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); + if (skeletonModel) { - actor = m_selectedModelIndices[0].data(SkeletonModel::ROLE_ACTOR_POINTER).value(); - } - - if (!actor) - { - SkeletonModel* skeletonModel = nullptr; - SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); - if (skeletonModel) - { - actor = skeletonModel->GetActor(); - } + actor = skeletonModel->GetActor(); } return actor; } @@ -183,10 +168,24 @@ namespace EMotionFX Node* SkeletonModelJointWidget::GetNode() const { Node* node = nullptr; - if (!m_selectedModelIndices.empty()) + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); + if (!selectedModelIndices.empty()) { - node = m_selectedModelIndices[0].data(SkeletonModel::ROLE_POINTER).value(); + node = selectedModelIndices[0].data(SkeletonModel::ROLE_POINTER).value(); } return node; } + + QModelIndexList SkeletonModelJointWidget::GetSelectedModelIndices() const + { + QModelIndexList selectedModelIndices; + SkeletonModel* skeletonModel = nullptr; + SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); + if (skeletonModel) + { + selectedModelIndices = skeletonModel->GetSelectionModel().selectedRows(); + } + + return selectedModelIndices; + } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h index 52178b4b55..399615c42a 100644 --- a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h +++ b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h @@ -33,13 +33,14 @@ namespace EMotionFX virtual void CreateGUI(); - void Reinit(const QModelIndexList& selectedModelIndices); + void Reinit(); void showEvent(QShowEvent* event) override; protected: Actor* GetActor() const; Node* GetNode() const; + QModelIndexList GetSelectedModelIndices() const; virtual QWidget* CreateContentWidget(QWidget* parent) = 0; virtual QWidget* CreateNoSelectionWidget(QWidget* parent) = 0; virtual void InternalReinit() = 0; @@ -50,7 +51,6 @@ namespace EMotionFX void OnModelReset(); protected: - QModelIndexList m_selectedModelIndices; QLabel* m_jointNameLabel; static int s_jointLabelSpacing; static int s_jointNameSpacing; From 20849655ea498d0b3a43b73db17417b70f36c8e6 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Wed, 22 Sep 2021 11:56:53 -0700 Subject: [PATCH 57/63] Initial gem dependencies (#4229) These are immediate dependencies, and include many circular dependencies, largely amongst the Atom gems, as indicated by cmake Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- Gems/AWSClientAuth/gem.json | 16 +++++++++--- Gems/AWSCore/gem.json | 13 +++++++--- Gems/AWSGameLift/gem.json | 15 ++++++++--- Gems/AWSMetrics/gem.json | 15 ++++++++--- Gems/Achievements/gem.json | 12 ++++++--- Gems/AssetMemoryAnalyzer/gem.json | 15 ++++++++--- Gems/AssetValidation/gem.json | 13 +++++++--- Gems/Atom/Asset/ImageProcessingAtom/gem.json | 10 +++++--- Gems/Atom/Asset/Shader/gem.json | 9 ++++--- Gems/Atom/Bootstrap/gem.json | 8 +++--- Gems/Atom/Component/DebugCamera/gem.json | 8 +++--- Gems/Atom/Feature/Common/gem.json | 11 +++++--- Gems/Atom/RHI/DX12/gem.json | 8 +++--- Gems/Atom/RHI/Metal/gem.json | 8 +++--- Gems/Atom/RHI/Null/gem.json | 8 +++--- Gems/Atom/RHI/Vulkan/gem.json | 8 +++--- Gems/Atom/RHI/gem.json | 12 ++++++--- Gems/Atom/RPI/gem.json | 8 +++--- Gems/Atom/Tools/AtomToolsFramework/gem.json | 10 +++++--- Gems/Atom/Tools/MaterialEditor/gem.json | 15 ++++++++--- Gems/Atom/gem.json | 22 +++++++++++++--- Gems/AtomContent/ReferenceMaterials/gem.json | 3 ++- Gems/AtomContent/Sponza/gem.json | 11 +++++--- Gems/AtomContent/gem.json | 16 +++++++++--- Gems/AtomLyIntegration/AtomBridge/gem.json | 25 ++++++++++++++++--- Gems/AtomLyIntegration/AtomFont/gem.json | 10 +++++--- .../AtomLyIntegration/AtomImGuiTools/gem.json | 6 ++++- .../AtomViewportDisplayIcons/gem.json | 11 +++++--- .../AtomViewportDisplayInfo/gem.json | 9 ++++--- .../AtomLyIntegration/CommonFeatures/gem.json | 14 ++++++++--- Gems/AtomLyIntegration/EMotionFXAtom/gem.json | 13 +++++++--- Gems/AtomLyIntegration/ImguiAtom/gem.json | 9 ++++--- .../DccScriptingInterface/gem.json | 9 ++++--- Gems/AtomLyIntegration/gem.json | 22 +++++++++++++--- Gems/AudioEngineWwise/gem.json | 15 ++++++++--- Gems/AudioSystem/gem.json | 15 ++++++++--- Gems/BarrierInput/gem.json | 15 ++++++++--- Gems/Blast/gem.json | 19 +++++++++++--- Gems/Camera/gem.json | 14 ++++++++--- Gems/CameraFramework/gem.json | 13 +++++++--- Gems/CertificateManager/gem.json | 12 ++++++--- Gems/CrashReporting/gem.json | 12 ++++++--- Gems/CustomAssetExample/gem.json | 12 ++++++--- Gems/DebugDraw/gem.json | 16 +++++++++--- Gems/DevTextures/gem.json | 13 +++++++--- Gems/EMotionFX/gem.json | 16 +++++++++--- Gems/EditorPythonBindings/gem.json | 12 ++++++--- Gems/ExpressionEvaluation/gem.json | 12 ++++++--- Gems/FastNoise/gem.json | 17 ++++++++++--- Gems/GameState/gem.json | 13 +++++++--- Gems/GameStateSamples/gem.json | 22 +++++++++++++--- Gems/Gestures/gem.json | 15 ++++++++--- Gems/GradientSignal/gem.json | 17 ++++++++++--- Gems/GraphCanvas/gem.json | 13 +++++++--- Gems/GraphModel/gem.json | 15 ++++++++--- Gems/HttpRequestor/gem.json | 12 ++++++--- Gems/ImGui/gem.json | 15 ++++++++--- Gems/InAppPurchases/gem.json | 12 ++++++--- Gems/LandscapeCanvas/gem.json | 20 ++++++++++++--- Gems/LmbrCentral/gem.json | 14 ++++++++--- Gems/LocalUser/gem.json | 13 +++++++--- Gems/LyShine/gem.json | 21 +++++++++++++--- Gems/LyShineExamples/gem.json | 16 +++++++++--- Gems/Maestro/gem.json | 16 +++++++++--- Gems/MessagePopup/gem.json | 12 ++++++--- Gems/Metastream/gem.json | 13 +++++++--- Gems/Microphone/gem.json | 14 ++++++++--- Gems/Multiplayer/gem.json | 17 ++++++++++--- Gems/MultiplayerCompression/gem.json | 13 +++++++--- Gems/NvCloth/gem.json | 16 +++++++++--- Gems/PBSreferenceMaterials/gem.json | 13 +++++++--- Gems/PhysX/gem.json | 16 +++++++++--- Gems/PhysXDebug/gem.json | 16 +++++++++--- Gems/PhysXSamples/gem.json | 13 +++++++--- Gems/Prefab/PrefabBuilder/gem.json | 13 +++++++--- Gems/Presence/gem.json | 13 +++++++--- Gems/PrimitiveAssets/gem.json | 13 +++++++--- Gems/PythonAssetBuilder/gem.json | 15 ++++++++--- Gems/QtForPython/gem.json | 15 ++++++++--- Gems/SaveData/gem.json | 12 ++++++--- Gems/SceneLoggingExample/gem.json | 12 ++++++--- Gems/SceneProcessing/gem.json | 13 +++++++--- Gems/ScriptCanvas/gem.json | 17 ++++++++++--- Gems/ScriptCanvasDeveloper/gem.json | 16 +++++++++--- Gems/ScriptCanvasPhysics/gem.json | 15 ++++++++--- Gems/ScriptCanvasTesting/gem.json | 17 ++++++++++--- Gems/ScriptEvents/gem.json | 13 +++++++--- Gems/ScriptedEntityTweener/gem.json | 13 +++++++--- Gems/SliceFavorites/gem.json | 8 ++++-- Gems/StartingPointCamera/gem.json | 16 +++++++++--- Gems/StartingPointInput/gem.json | 15 ++++++++--- Gems/StartingPointMovement/gem.json | 13 +++++++--- Gems/SurfaceData/gem.json | 17 ++++++++++--- Gems/Terrain/gem.json | 20 ++++++++++++--- Gems/TestAssetBuilder/gem.json | 13 +++++++--- Gems/TextureAtlas/gem.json | 16 +++++++++--- Gems/TickBusOrderViewer/gem.json | 13 +++++++--- Gems/Twitch/gem.json | 15 ++++++++--- Gems/UiBasics/gem.json | 13 +++++++--- Gems/Vegetation/gem.json | 18 ++++++++++--- Gems/VideoPlaybackFramework/gem.json | 12 ++++++--- Gems/VirtualGamepad/gem.json | 12 ++++++--- Gems/WhiteBox/gem.json | 17 ++++++++++--- 103 files changed, 1100 insertions(+), 307 deletions(-) diff --git a/Gems/AWSClientAuth/gem.json b/Gems/AWSClientAuth/gem.json index 42996e7f4f..75c07d025b 100644 --- a/Gems/AWSClientAuth/gem.json +++ b/Gems/AWSClientAuth/gem.json @@ -5,9 +5,19 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "AWS Client Auth provides client authentication and AWS authorization solution.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/", + "dependencies": [ + "AWSCore", + "HttpRequestor" + ] } diff --git a/Gems/AWSCore/gem.json b/Gems/AWSCore/gem.json index 4c7889ace4..1bb9da9192 100644 --- a/Gems/AWSCore/gem.json +++ b/Gems/AWSCore/gem.json @@ -5,9 +5,16 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Core Gem provides basic shared AWS functionality such as AWS SDK initialization and client configuration, and is automatically added when selecting any AWS feature Gem.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/", + "dependencies": [] } diff --git a/Gems/AWSGameLift/gem.json b/Gems/AWSGameLift/gem.json index 7711495ee7..a0d7f62cd1 100644 --- a/Gems/AWSGameLift/gem.json +++ b/Gems/AWSGameLift/gem.json @@ -5,9 +5,18 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS GameLift Gem provides a framework to extend O3DE networking layer to work with GameLift resources via GameLift server and client SDK.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Framework", "Network"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Framework", + "Network" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/", + "dependencies": [ + "AWSCore" + ] } diff --git a/Gems/AWSMetrics/gem.json b/Gems/AWSMetrics/gem.json index 5faeb66787..df16890012 100644 --- a/Gems/AWSMetrics/gem.json +++ b/Gems/AWSMetrics/gem.json @@ -5,9 +5,18 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Metrics Gem provides a solution for AWS metrics submission and analytics.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/", + "dependencies": [ + "AWSCore" + ] } diff --git a/Gems/Achievements/gem.json b/Gems/Achievements/gem.json index ca2bc7f3e9..bd643f471a 100644 --- a/Gems/Achievements/gem.json +++ b/Gems/Achievements/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Achievements Gem provides a target platform agnostic interface for retrieving achievement details and unlocking achievements.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Achievements"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Achievements" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/", + "dependencies": [] } diff --git a/Gems/AssetMemoryAnalyzer/gem.json b/Gems/AssetMemoryAnalyzer/gem.json index 544e0f7948..902102fa27 100644 --- a/Gems/AssetMemoryAnalyzer/gem.json +++ b/Gems/AssetMemoryAnalyzer/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Memory Analyzer Gem provides tools to profile asset memory usage in Open 3D Engine through ImGUI (Immediate Mode Graphical User Interface).", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/", + "dependencies": [ + "ImGui" + ] } diff --git a/Gems/AssetValidation/gem.json b/Gems/AssetValidation/gem.json index 83e37ddaf5..1e57f60dc4 100644 --- a/Gems/AssetValidation/gem.json +++ b/Gems/AssetValidation/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Validation Gem provides seed-related commands to ensure assets have valid seeds for asset bundling.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Utility", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Utility", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/", + "dependencies": [] } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/gem.json b/Gems/Atom/Asset/ImageProcessingAtom/gem.json index 45d96f7168..1424841256 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/gem.json +++ b/Gems/Atom/Asset/ImageProcessingAtom/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_RHI", + "Atom" + ] } diff --git a/Gems/Atom/Asset/Shader/gem.json b/Gems/Atom/Asset/Shader/gem.json index eadf34acff..6d5e9f4dbe 100644 --- a/Gems/Atom/Asset/Shader/gem.json +++ b/Gems/Atom/Asset/Shader/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI" + ] } diff --git a/Gems/Atom/Bootstrap/gem.json b/Gems/Atom/Bootstrap/gem.json index d5f287eb7f..5e98a1887d 100644 --- a/Gems/Atom/Bootstrap/gem.json +++ b/Gems/Atom/Bootstrap/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/Atom/Component/DebugCamera/gem.json b/Gems/Atom/Component/DebugCamera/gem.json index cb2c597b07..586cb37058 100644 --- a/Gems/Atom/Component/DebugCamera/gem.json +++ b/Gems/Atom/Component/DebugCamera/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/Atom/Feature/Common/gem.json b/Gems/Atom/Feature/Common/gem.json index cbaa56e0d7..36a61fbbbb 100644 --- a/Gems/Atom/Feature/Common/gem.json +++ b/Gems/Atom/Feature/Common/gem.json @@ -8,7 +8,12 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom", + "ImGui", + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/DX12/gem.json b/Gems/Atom/RHI/DX12/gem.json index df0ae12cc4..eb876a1b9f 100644 --- a/Gems/Atom/RHI/DX12/gem.json +++ b/Gems/Atom/RHI/DX12/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Metal/gem.json b/Gems/Atom/RHI/Metal/gem.json index 497e0149b6..8da6bfabec 100644 --- a/Gems/Atom/RHI/Metal/gem.json +++ b/Gems/Atom/RHI/Metal/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Null/gem.json b/Gems/Atom/RHI/Null/gem.json index aa2e08501a..0870efaae7 100644 --- a/Gems/Atom/RHI/Null/gem.json +++ b/Gems/Atom/RHI/Null/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Vulkan/gem.json b/Gems/Atom/RHI/Vulkan/gem.json index 2b43de4ab1..81e8dd22ea 100644 --- a/Gems/Atom/RHI/Vulkan/gem.json +++ b/Gems/Atom/RHI/Vulkan/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/gem.json b/Gems/Atom/RHI/gem.json index 015ee64994..5f916e5224 100644 --- a/Gems/Atom/RHI/gem.json +++ b/Gems/Atom/RHI/gem.json @@ -8,7 +8,13 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI_DX12", + "Atom_RHI_Metal", + "Atom_RHI_Vulkan", + "Atom_RHI_Null", + "Atom_Feature_Common" + ] } diff --git a/Gems/Atom/RPI/gem.json b/Gems/Atom/RPI/gem.json index f30a1c3201..b5a6fd5a1a 100644 --- a/Gems/Atom/RPI/gem.json +++ b/Gems/Atom/RPI/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/Tools/AtomToolsFramework/gem.json b/Gems/Atom/Tools/AtomToolsFramework/gem.json index 5cea71c5cc..2b0380bdae 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/gem.json +++ b/Gems/Atom/Tools/AtomToolsFramework/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_RHI", + "Atom_Bootstrap" + ] } diff --git a/Gems/Atom/Tools/MaterialEditor/gem.json b/Gems/Atom/Tools/MaterialEditor/gem.json index 83d9909a84..85ff434eab 100644 --- a/Gems/Atom/Tools/MaterialEditor/gem.json +++ b/Gems/Atom/Tools/MaterialEditor/gem.json @@ -8,7 +8,16 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "AtomToolsFramework", + "Atom_RPI", + "Atom_RHI", + "Atom_Feature_Common", + "ImageProcessingAtom", + "Atom_Component_DebugCamera", + "CommonFeaturesAtom", + "LyShine" + ] } diff --git a/Gems/Atom/gem.json b/Gems/Atom/gem.json index 99ca26025a..1f5e4a37f3 100644 --- a/Gems/Atom/gem.json +++ b/Gems/Atom/gem.json @@ -5,8 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom Renderer Gem provides Atom Renderer and its associated tools (such as Material Editor), utilites, libraries, and interfaces.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Core" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom/", + "dependencies": [ + "Atom_Feature_Common", + "AtomShader", + "Atom_Bootstrap", + "Atom_Component_DebugCamera", + "Atom_RHI", + "Atom_RPI", + "AtomToolsFramework", + "MaterialEditor", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomContent/ReferenceMaterials/gem.json b/Gems/AtomContent/ReferenceMaterials/gem.json index 9e145c20cc..f697d50bc4 100644 --- a/Gems/AtomContent/ReferenceMaterials/gem.json +++ b/Gems/AtomContent/ReferenceMaterials/gem.json @@ -7,5 +7,6 @@ "summary": "Atom Asset Gem with a library of reference materials for StandardPBR (and others in the future)", "canonical_tags": ["Gem"], "user_tags": ["Assets", "PBR", "Materials"], - "icon_path": "preview.png" + "icon_path": "preview.png", + "dependencies": [] } diff --git a/Gems/AtomContent/Sponza/gem.json b/Gems/AtomContent/Sponza/gem.json index 3fc76927e1..64de0e5da0 100644 --- a/Gems/AtomContent/Sponza/gem.json +++ b/Gems/AtomContent/Sponza/gem.json @@ -5,7 +5,12 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "A standard test scene for Global Illumination (forked from crytek sponza scene)", - "canonical_tags": ["Gem"], - "user_tags": ["Assets"], - "requirements": "" + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets" + ], + "requirements": "", + "dependencies": [] } diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index 6fcb8903e4..1bffe7d989 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -5,8 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Atom Content Gem provides assets for Atom Renderer and a modified version of the Pixar Look Development Studio.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Assets", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Assets", + "Tools" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-content/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-content/", + "dependencies": [ + "ReferenceMaterials", + "Sponza" + ] } diff --git a/Gems/AtomLyIntegration/AtomBridge/gem.json b/Gems/AtomLyIntegration/AtomBridge/gem.json index 49a56d2201..1d48d8be61 100644 --- a/Gems/AtomLyIntegration/AtomBridge/gem.json +++ b/Gems/AtomLyIntegration/AtomBridge/gem.json @@ -8,7 +8,26 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_Bootstrap", + "Atom_RHI", + "Atom_RHI_Null", + "Atom_Feature_Common", + "Atom_Component_DebugCamera", + "AtomImGuiTools", + "CommonFeaturesAtom", + "EMotionFX_Atom", + "ImguiAtom", + "AtomFont", + "AtomViewportDisplayInfo", + "Atom", + "AtomShader", + "ImageProcessingAtom", + "AtomToolsFramework", + "AtomViewportDisplayIcons", + "MaterialEditor" + ] } diff --git a/Gems/AtomLyIntegration/AtomFont/gem.json b/Gems/AtomLyIntegration/AtomFont/gem.json index 7e1a71db86..ed5b488de7 100644 --- a/Gems/AtomLyIntegration/AtomFont/gem.json +++ b/Gems/AtomLyIntegration/AtomFont/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json index ff20d5a19f..564eeedee2 100644 --- a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json +++ b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json @@ -12,5 +12,9 @@ "Debug", "Rendering" ], - "requirements": "" + "requirements": "", + "dependencies": [ + "ImguiAtom", + "Atom" + ] } diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json index 52dbb219d3..a2a7ba4b77 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json @@ -8,7 +8,12 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI", + "Atom_Bootstrap", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json index f277d6cf82..be5cc96f95 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI" + ] } diff --git a/Gems/AtomLyIntegration/CommonFeatures/gem.json b/Gems/AtomLyIntegration/CommonFeatures/gem.json index 8eceb50932..30738ad14c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/gem.json +++ b/Gems/AtomLyIntegration/CommonFeatures/gem.json @@ -8,7 +8,15 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_Feature_Common", + "LmbrCentral", + "GradientSignal", + "SurfaceData", + "Atom_Bootstrap", + "Atom_RPI", + "AtomToolsFramework" + ] } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json index a7c3e96d2b..f921990360 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json +++ b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json @@ -8,7 +8,14 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "EMotionFX", + "Atom", + "Atom_Feature_Common", + "Atom_RPI", + "Atom_RHI", + "CommonFeaturesAtom" + ] } diff --git a/Gems/AtomLyIntegration/ImguiAtom/gem.json b/Gems/AtomLyIntegration/ImguiAtom/gem.json index 918414f053..6b032275b9 100644 --- a/Gems/AtomLyIntegration/ImguiAtom/gem.json +++ b/Gems/AtomLyIntegration/ImguiAtom/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "ImGui", + "Atom_Feature_Common" + ] } diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json index f978d777cf..1cee5c8298 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json @@ -10,9 +10,10 @@ ], "user_tags": [ "DCC", - "Digital", - "Content", - "Creation" + "Digital", + "Content", + "Creation" ], - "requirements": "" + "requirements": "", + "dependencies": [] } diff --git a/Gems/AtomLyIntegration/gem.json b/Gems/AtomLyIntegration/gem.json index 5af133c8e8..00b3a25f74 100644 --- a/Gems/AtomLyIntegration/gem.json +++ b/Gems/AtomLyIntegration/gem.json @@ -5,8 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom O3DE Integration Gem provides components, libraries, and functionality to support and integrate Atom Renderer in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Core", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Core", + "Utility" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-ly-integration/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-ly-integration/", + "dependencies": [ + "Atom_AtomBridge", + "AtomFont", + "AtomImGuiTools", + "AtomViewportDisplayIcons", + "AtomViewportDisplayInfo", + "CommonFeaturesAtom", + "EMotionFX_Atom", + "ImguiAtom" + ] } diff --git a/Gems/AudioEngineWwise/gem.json b/Gems/AudioEngineWwise/gem.json index 084c924977..0588af1908 100644 --- a/Gems/AudioEngineWwise/gem.json +++ b/Gems/AudioEngineWwise/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Wwise Audio Engine Gem provides support for Audiokinetic Wave Works Interactive Sound Engine (Wwise).", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "Users will need to download Wwise from the Audiokinetic Web Site.", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/wwise/audio-engine-wwise/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/wwise/audio-engine-wwise/", + "dependencies": [ + "AudioSystem" + ] } diff --git a/Gems/AudioSystem/gem.json b/Gems/AudioSystem/gem.json index f618103803..64d4d9af5a 100644 --- a/Gems/AudioSystem/gem.json +++ b/Gems/AudioSystem/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Audio System Gem provides the Audio Translation Layer (ATL) and Audio Controls Editor, which add support for audio in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/audio-system/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/audio-system/", + "dependencies": [ + "LmbrCentral" + ] } diff --git a/Gems/BarrierInput/gem.json b/Gems/BarrierInput/gem.json index 738d644d84..7fdc58e8b3 100644 --- a/Gems/BarrierInput/gem.json +++ b/Gems/BarrierInput/gem.json @@ -5,8 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Barrier Input Gem allows the Open 3D Engine to function as a Barrier client so that it can receive input from a remote Barrier server.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Barrier", "Synergy"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Barrier", + "Synergy" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/Blast/gem.json b/Gems/Blast/gem.json index 3b1205d6fe..d6af47f482 100644 --- a/Gems/Blast/gem.json +++ b/Gems/Blast/gem.json @@ -5,9 +5,22 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The NVIDIA Blast Gem provides tools to author fractured mesh assets in Houdini, and functionality to create realistic destruction simulations in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Animation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Animation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-blast/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-blast/", + "dependencies": [ + "Atom_Feature_Common", + "CommonFeaturesAtom", + "PhysX", + "Atom_RPI", + "PythonAssetBuilder" + ] } diff --git a/Gems/Camera/gem.json b/Gems/Camera/gem.json index 2fc2ea8355..9f8ea22412 100644 --- a/Gems/Camera/gem.json +++ b/Gems/Camera/gem.json @@ -5,9 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Gem provides a basic camera component that defines a frustum for runtime rendering.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera/", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/CameraFramework/gem.json b/Gems/CameraFramework/gem.json index 2ab25a84b4..c5520fd5b4 100644 --- a/Gems/CameraFramework/gem.json +++ b/Gems/CameraFramework/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Framework Gem provides a base for implementing more complex camera systems.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Framework", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Framework", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera-framework/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera-framework/", + "dependencies": [] } diff --git a/Gems/CertificateManager/gem.json b/Gems/CertificateManager/gem.json index cb49abc6b6..968da2788a 100644 --- a/Gems/CertificateManager/gem.json +++ b/Gems/CertificateManager/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Certificate Manager Gem provides access to authentication files for secure game connections from Amazon S3, files on disk, and other 3rd party sources.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/certificate-manager/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/certificate-manager/", + "dependencies": [] } diff --git a/Gems/CrashReporting/gem.json b/Gems/CrashReporting/gem.json index bff54dcf36..9b8d3a9e0a 100644 --- a/Gems/CrashReporting/gem.json +++ b/Gems/CrashReporting/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Crash Reporting Gem provides support for external crash reporting for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/crash-reporting/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/crash-reporting/", + "dependencies": [] } diff --git a/Gems/CustomAssetExample/gem.json b/Gems/CustomAssetExample/gem.json index ac7c2788d7..ab5ed7002d 100644 --- a/Gems/CustomAssetExample/gem.json +++ b/Gems/CustomAssetExample/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Custom Asset Example Gem provides example code for creating a custom asset for Open 3D Engine's asset pipeline.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/custom-asset-example/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/custom-asset-example/", + "dependencies": [] } diff --git a/Gems/DebugDraw/gem.json b/Gems/DebugDraw/gem.json index a7939f4077..7e0da07103 100644 --- a/Gems/DebugDraw/gem.json +++ b/Gems/DebugDraw/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Debug Draw Gem provides Editor and runtime debug visualization features for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/debug-draw/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/debug-draw/", + "dependencies": [ + "Atom_RPI", + "Atom_Bootstrap" + ] } diff --git a/Gems/DevTextures/gem.json b/Gems/DevTextures/gem.json index 81d14e78cb..8b40badbf1 100644 --- a/Gems/DevTextures/gem.json +++ b/Gems/DevTextures/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Dev Textures Gem provides a collection of general purpose texture assets useful for prototypes and preproduction.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Debug", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Debug", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/dev-textures/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/dev-textures/", + "dependencies": [] } diff --git a/Gems/EMotionFX/gem.json b/Gems/EMotionFX/gem.json index f00803ec2c..f1734d854d 100644 --- a/Gems/EMotionFX/gem.json +++ b/Gems/EMotionFX/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The EMotion FX Animation Gem provides Open 3D Engine's animation system for rigged actors and includes Animation Editor, a tool for creating animated behaviors, simulated objects, and colliders for rigged actors.", - "canonical_tags": ["Gem"], - "user_tags": ["Animation", "Tools", "Simulation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Animation", + "Tools", + "Simulation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/emotionfx/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/emotionfx/", + "dependencies": [ + "Atom_RPI", + "LmbrCentral" + ] } diff --git a/Gems/EditorPythonBindings/gem.json b/Gems/EditorPythonBindings/gem.json index fae091a63f..13c5800dd7 100644 --- a/Gems/EditorPythonBindings/gem.json +++ b/Gems/EditorPythonBindings/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Editor Python Bindings Gem provides Python commands for Open 3D Engine Editor functions.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/editor-python-bindings/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/editor-python-bindings/", + "dependencies": [] } diff --git a/Gems/ExpressionEvaluation/gem.json b/Gems/ExpressionEvaluation/gem.json index 2f555916a5..6bcc666a4d 100644 --- a/Gems/ExpressionEvaluation/gem.json +++ b/Gems/ExpressionEvaluation/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Expression Evaluation Gem provides a method for parsing and executing string expressions in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/expression-evaluation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/expression-evaluation/", + "dependencies": [] } diff --git a/Gems/FastNoise/gem.json b/Gems/FastNoise/gem.json index 5ee5bbaf89..ac59fe804e 100644 --- a/Gems/FastNoise/gem.json +++ b/Gems/FastNoise/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The FastNoise Gradient Gem uses the third-party, open source FastNoise library to provide a variety of high-performance noise generation algorithms.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/fast-noise/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/fast-noise/", + "dependencies": [ + "GradientSignal", + "LmbrCentral", + "SurfaceData" + ] } diff --git a/Gems/GameState/gem.json b/Gems/GameState/gem.json index a1f272c290..7bb3cf4214 100644 --- a/Gems/GameState/gem.json +++ b/Gems/GameState/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Gem provides a generic framework to determine and manage game states and game state transitions in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Framework", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Framework", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state/", + "dependencies": [] } diff --git a/Gems/GameStateSamples/gem.json b/Gems/GameStateSamples/gem.json index f0dd82c5fa..be982b9c5b 100644 --- a/Gems/GameStateSamples/gem.json +++ b/Gems/GameStateSamples/gem.json @@ -5,9 +5,25 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Samples Gem provides a set of sample game states (built on top of the Game State Gem), including primary user selection, main menu, level loading, level running, and level paused.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state-samples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state-samples/", + "dependencies": [ + "GameState", + "LocalUser", + "LyShine", + "SaveData", + "MessagePopup", + "LmbrCentral", + "UiBasics", + "LyShineExamples" + ] } diff --git a/Gems/Gestures/gem.json b/Gems/Gestures/gem.json index 4412e58c6d..fcc56b4704 100644 --- a/Gems/Gestures/gem.json +++ b/Gems/Gestures/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gestures Gem provides detection for common gesture-based input actions on iOS and Android devices.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/gestures/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/gestures/", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/GradientSignal/gem.json b/Gems/GradientSignal/gem.json index 67afb1f819..e87ccfe13a 100644 --- a/Gems/GradientSignal/gem.json +++ b/Gems/GradientSignal/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gradient Signal Gem provides a number of components for generating, modifying, and mixing gradient signals.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/gradient-signal/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/gradient-signal/", + "dependencies": [ + "SurfaceData", + "ImageProcessingAtom", + "LmbrCentral" + ] } diff --git a/Gems/GraphCanvas/gem.json b/Gems/GraphCanvas/gem.json index ab1e7ac77a..760bd157df 100644 --- a/Gems/GraphCanvas/gem.json +++ b/Gems/GraphCanvas/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Graph Canvas Gem provides a C++ framework for creating custom graphical node based editors for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Framework", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Framework", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-canvas/", + "dependencies": [] } diff --git a/Gems/GraphModel/gem.json b/Gems/GraphModel/gem.json index 62f1effb7c..256de75f6c 100644 --- a/Gems/GraphModel/gem.json +++ b/Gems/GraphModel/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Graph Model Gem provides a generic node graph data model framework for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Framework", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Framework", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-model/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-model/", + "dependencies": [ + "GraphCanvas" + ] } diff --git a/Gems/HttpRequestor/gem.json b/Gems/HttpRequestor/gem.json index e5eb8d6f44..eb1a112b0e 100644 --- a/Gems/HttpRequestor/gem.json +++ b/Gems/HttpRequestor/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The HTTP Requestor Gem provides functionality to make asynchronous HTTP/HTTPS requests and return data through a user-provided call back function.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/http-requestor/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/http-requestor/", + "dependencies": [] } diff --git a/Gems/ImGui/gem.json b/Gems/ImGui/gem.json index deaac9925a..c1d89d1728 100644 --- a/Gems/ImGui/gem.json +++ b/Gems/ImGui/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Immediate Mode GUI Gem provides the 3rdParty library IMGUI which can be used to create run time immediate mode overlays for debugging and profiling information in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Rendering", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Rendering", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/imgui/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/imgui/", + "dependencies": [ + "LmbrCentral" + ] } diff --git a/Gems/InAppPurchases/gem.json b/Gems/InAppPurchases/gem.json index ab43075896..1f1debd5fb 100644 --- a/Gems/InAppPurchases/gem.json +++ b/Gems/InAppPurchases/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The In-App Purchases Gem provides functionality for in app purchases for iOS and Android.", - "canonical_tags": ["Gem"], - "user_tags": ["SDK", "Network"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "SDK", + "Network" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/sdk/in-app-purchases/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/sdk/in-app-purchases/", + "dependencies": [] } diff --git a/Gems/LandscapeCanvas/gem.json b/Gems/LandscapeCanvas/gem.json index 63a80ec57e..ce0c64b75d 100644 --- a/Gems/LandscapeCanvas/gem.json +++ b/Gems/LandscapeCanvas/gem.json @@ -5,9 +5,23 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Landscape Canvas Gem provides the Landscape Canvas editor, a node-based graph tool for authoring workflows to populate landscape with dynamic vegetation.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Design", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Design", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/landscape-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/landscape-canvas/", + "dependencies": [ + "GraphModel", + "GradientSignal", + "SurfaceData", + "Vegetation", + "LmbrCentral", + "GraphCanvas" + ] } diff --git a/Gems/LmbrCentral/gem.json b/Gems/LmbrCentral/gem.json index b6442bfd06..36de4c4ed3 100644 --- a/Gems/LmbrCentral/gem.json +++ b/Gems/LmbrCentral/gem.json @@ -5,10 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The O3DE Core (LmbrCentral) Gem provides required code and assets for running Open 3D Engine Editor.", - "canonical_tags": ["Gem"], - "user_tags": ["Core", "Framework", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Core", + "Framework", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/core/lmbr-central/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/core/lmbr-central/", + "dependencies": [] } - diff --git a/Gems/LocalUser/gem.json b/Gems/LocalUser/gem.json index dcf4d44abb..f86e6e1bf6 100644 --- a/Gems/LocalUser/gem.json +++ b/Gems/LocalUser/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Local User Gem provides functionality for mapping local user ids to local player slots and managing local user profiles.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/local-user/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/local-user/", + "dependencies": [] } diff --git a/Gems/LyShine/gem.json b/Gems/LyShine/gem.json index e0fdb860e5..e2da8afa4d 100644 --- a/Gems/LyShine/gem.json +++ b/Gems/LyShine/gem.json @@ -5,9 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The LyShine Gem provides the runtime UI system and creation tools for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Tools", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Tools", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine/", + "dependencies": [ + "LmbrCentral", + "Atom_RPI", + "Atom", + "Atom_Bootstrap", + "AtomFont", + "TextureAtlas", + "AtomToolsFramework" + ] } diff --git a/Gems/LyShineExamples/gem.json b/Gems/LyShineExamples/gem.json index a411bfb363..122273f6d9 100644 --- a/Gems/LyShineExamples/gem.json +++ b/Gems/LyShineExamples/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The LyShine Examples Gem provides example code and assets for LyShine, the runtime UI system and editor for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine-examples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine-examples/", + "dependencies": [ + "LmbrCentral", + "LyShine" + ] } diff --git a/Gems/Maestro/gem.json b/Gems/Maestro/gem.json index 6ee989aa55..5149df7c14 100644 --- a/Gems/Maestro/gem.json +++ b/Gems/Maestro/gem.json @@ -5,10 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Maestro Cinematics Gem provides Track View, Open 3D Engine's animated sequence and cinematics editor.", - "canonical_tags": ["Gem"], - "user_tags": ["Animation", "Tools", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Animation", + "Tools", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/maestro/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/maestro/", + "dependencies": [ + "LmbrCentral" + ] } - diff --git a/Gems/MessagePopup/gem.json b/Gems/MessagePopup/gem.json index 2560b8869a..05d36bc2df 100644 --- a/Gems/MessagePopup/gem.json +++ b/Gems/MessagePopup/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Message Popup Gem provides an example implementation of popup messages using LyShine in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/message-popup/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/message-popup/", + "dependencies": [] } diff --git a/Gems/Metastream/gem.json b/Gems/Metastream/gem.json index 429d174cf5..862b17dd1d 100644 --- a/Gems/Metastream/gem.json +++ b/Gems/Metastream/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Metastream Gem provides functionality for an HTTP server that allows broadcasters to customize game streams with overlays of statistics and event data from a game session.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Network", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/metastream/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/metastream/", + "dependencies": [] } diff --git a/Gems/Microphone/gem.json b/Gems/Microphone/gem.json index cc47cbcd7e..68492ea786 100644 --- a/Gems/Microphone/gem.json +++ b/Gems/Microphone/gem.json @@ -5,9 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Microphone Gem provides support for audio input through microphones.", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Input"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Input" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/microphone/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/microphone/", + "dependencies": [ + "AudioSystem" + ] } diff --git a/Gems/Multiplayer/gem.json b/Gems/Multiplayer/gem.json index 8abbddea5f..47dbddfcbd 100644 --- a/Gems/Multiplayer/gem.json +++ b/Gems/Multiplayer/gem.json @@ -5,8 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Gem provides a public API for multiplayer functionality such as connecting and hosting.", - "canonical_tags": ["Gem"], - "user_tags": ["Multiplayer", "Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Multiplayer", + "Network", + "Framework" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [ + "CertificateManager", + "Atom_Feature_Common", + "ImGui" + ] } diff --git a/Gems/MultiplayerCompression/gem.json b/Gems/MultiplayerCompression/gem.json index 178cca0ffe..7dd31476e3 100644 --- a/Gems/MultiplayerCompression/gem.json +++ b/Gems/MultiplayerCompression/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Compression Gem provides an open source Compressor for use with AzNetworking's transport layer.", - "canonical_tags": ["Gem"], - "user_tags": ["Multiplayer", "Network", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Multiplayer", + "Network", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/multiplayer/multiplayer-compression/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/multiplayer/multiplayer-compression/", + "dependencies": [] } diff --git a/Gems/NvCloth/gem.json b/Gems/NvCloth/gem.json index 49ccbf32f1..019ce23742 100644 --- a/Gems/NvCloth/gem.json +++ b/Gems/NvCloth/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The NVIDIA Cloth Gem provides functionality to create fast, realistic cloth simulation with the NVIDIA Cloth library.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-cloth/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-cloth/", + "dependencies": [ + "CommonFeaturesAtom", + "EMotionFX" + ] } diff --git a/Gems/PBSreferenceMaterials/gem.json b/Gems/PBSreferenceMaterials/gem.json index 563dd31a38..feddc1e465 100644 --- a/Gems/PBSreferenceMaterials/gem.json +++ b/Gems/PBSreferenceMaterials/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The PBS Reference Materials Gem provides physically based reference materials for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/pbs-reference-materials/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/pbs-reference-materials/", + "dependencies": [] } diff --git a/Gems/PhysX/gem.json b/Gems/PhysX/gem.json index 0fbd3e44f9..bacbcf2dee 100644 --- a/Gems/PhysX/gem.json +++ b/Gems/PhysX/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The PhysX Gem provides physics simulation with NVIDIA PhysX including static and dynamic rigid body simulation, force regions, ragdolls, and dynamic PhysX joints.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx/", + "dependencies": [ + "LmbrCentral", + "CommonFeaturesAtom" + ] } diff --git a/Gems/PhysXDebug/gem.json b/Gems/PhysXDebug/gem.json index f7877cabdd..ece0774210 100644 --- a/Gems/PhysXDebug/gem.json +++ b/Gems/PhysXDebug/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The PhysX Debug Gem provides debugging functionality and visualizations for NVIDIA PhysX in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-debug/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-debug/", + "dependencies": [ + "PhysX", + "ImGui" + ] } diff --git a/Gems/PhysXSamples/gem.json b/Gems/PhysXSamples/gem.json index e48dc6991b..0c84a29f47 100644 --- a/Gems/PhysXSamples/gem.json +++ b/Gems/PhysXSamples/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The PhysX Samples Gem provides sample assets and scripts that demonstrate PhysX Gem features in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-samples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-samples/", + "dependencies": [] } diff --git a/Gems/Prefab/PrefabBuilder/gem.json b/Gems/Prefab/PrefabBuilder/gem.json index ad6062ae3b..ba78f96358 100644 --- a/Gems/Prefab/PrefabBuilder/gem.json +++ b/Gems/Prefab/PrefabBuilder/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Prefab Builder Gem provides an Asset Processor module for prefabs, which are complex assets built by combining smaller entities.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Utility", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Utility", + "Core" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/prefab/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/prefab/", + "dependencies": [] } diff --git a/Gems/Presence/gem.json b/Gems/Presence/gem.json index 49d937946d..a70953ae4e 100644 --- a/Gems/Presence/gem.json +++ b/Gems/Presence/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Presence Gem provides a target platform agnostic interface for Presence services.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Gameplay", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Gameplay", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/presence/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/presence/", + "dependencies": [] } diff --git a/Gems/PrimitiveAssets/gem.json b/Gems/PrimitiveAssets/gem.json index d1789d46fe..4ad3cb62ad 100644 --- a/Gems/PrimitiveAssets/gem.json +++ b/Gems/PrimitiveAssets/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Primitive Assets Gem provides primitive shape mesh assets with physics enabled.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Sample", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Sample", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/primitive-assets/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/primitive-assets/", + "dependencies": [] } diff --git a/Gems/PythonAssetBuilder/gem.json b/Gems/PythonAssetBuilder/gem.json index 1a76d7d2c2..ce30ba9e82 100644 --- a/Gems/PythonAssetBuilder/gem.json +++ b/Gems/PythonAssetBuilder/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Python Asset Builder Gem provides functionality to implement custom asset builders in Python for Asset Processor.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/python-asset-builder/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/python-asset-builder/", + "dependencies": [ + "EditorPythonBindings" + ] } diff --git a/Gems/QtForPython/gem.json b/Gems/QtForPython/gem.json index 1519d46c52..f83be43342 100644 --- a/Gems/QtForPython/gem.json +++ b/Gems/QtForPython/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Qt for Python Gem provides the PySide2 Python libraries to manage Qt widgets.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "UI", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "UI", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/qt-for-python/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/qt-for-python/", + "dependencies": [ + "EditorPythonBindings" + ] } diff --git a/Gems/SaveData/gem.json b/Gems/SaveData/gem.json index 3892cc80c2..333b4682d2 100644 --- a/Gems/SaveData/gem.json +++ b/Gems/SaveData/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Save Data Gem provides a platform independent API to save and load persistent user data in Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/save-data/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/save-data/", + "dependencies": [] } diff --git a/Gems/SceneLoggingExample/gem.json b/Gems/SceneLoggingExample/gem.json index ff7def1b32..16961b9c5b 100644 --- a/Gems/SceneLoggingExample/gem.json +++ b/Gems/SceneLoggingExample/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Scene Logging Example Gem demonstrates the basics of extending the Open 3D Engine Scene API by adding additional logging to the pipeline.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/scene-logging-example/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/scene-logging-example/", + "dependencies": [] } diff --git a/Gems/SceneProcessing/gem.json b/Gems/SceneProcessing/gem.json index a6c2cefbd6..de1dfeb23f 100644 --- a/Gems/SceneProcessing/gem.json +++ b/Gems/SceneProcessing/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scene Processing Gem provides Scene Settings, a tool you can use to specify the default settings for processing asset files for actors, meshes, motions, and PhysX.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Tools", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Tools", + "Core" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/scene-processing/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/scene-processing/", + "dependencies": [] } diff --git a/Gems/ScriptCanvas/gem.json b/Gems/ScriptCanvas/gem.json index b6b5522d7d..621ea42961 100644 --- a/Gems/ScriptCanvas/gem.json +++ b/Gems/ScriptCanvas/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas/", + "dependencies": [ + "ScriptEvents", + "ExpressionEvaluation", + "GraphCanvas" + ] } diff --git a/Gems/ScriptCanvasDeveloper/gem.json b/Gems/ScriptCanvasDeveloper/gem.json index d7b0aefad3..ee1bfcec84 100644 --- a/Gems/ScriptCanvasDeveloper/gem.json +++ b/Gems/ScriptCanvasDeveloper/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Developer Gem provides a suite of utility features for the development and debugging of Script Canvas systems.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-developer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-developer/", + "dependencies": [ + "ScriptCanvas", + "GraphCanvas" + ] } diff --git a/Gems/ScriptCanvasPhysics/gem.json b/Gems/ScriptCanvasPhysics/gem.json index 46e2ef1194..417fa6893a 100644 --- a/Gems/ScriptCanvasPhysics/gem.json +++ b/Gems/ScriptCanvasPhysics/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Canvas Physics Gem provides Script Canvas nodes for physics scene queries such as raycasts.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Physics", "Simulation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Physics", + "Simulation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-physics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-physics/", + "dependencies": [ + "ScriptCanvas" + ] } diff --git a/Gems/ScriptCanvasTesting/gem.json b/Gems/ScriptCanvasTesting/gem.json index 303eab8c7e..c45d2ac165 100644 --- a/Gems/ScriptCanvasTesting/gem.json +++ b/Gems/ScriptCanvasTesting/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Testing Gem provides a framework for testing for and with Script Canvas.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Debug", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Debug", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-testing/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-testing/", + "dependencies": [ + "ScriptCanvas", + "GraphCanvas", + "ScriptEvents" + ] } diff --git a/Gems/ScriptEvents/gem.json b/Gems/ScriptEvents/gem.json index 2b7d74ef3f..386d9b5614 100644 --- a/Gems/ScriptEvents/gem.json +++ b/Gems/ScriptEvents/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Events Gem provides a framework for creating event assets usable from any scripting solution in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Framework", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Framework", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-events/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-events/", + "dependencies": [] } diff --git a/Gems/ScriptedEntityTweener/gem.json b/Gems/ScriptedEntityTweener/gem.json index a0f558356b..c51f05df9a 100644 --- a/Gems/ScriptedEntityTweener/gem.json +++ b/Gems/ScriptedEntityTweener/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scripted Entity Tweener Gem provides a script driven animation system for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "UI", "Animation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "UI", + "Animation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/scripted-entity-tweener/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/scripted-entity-tweener/", + "dependencies": [] } diff --git a/Gems/SliceFavorites/gem.json b/Gems/SliceFavorites/gem.json index 3ae37e2145..84f42fc1a2 100644 --- a/Gems/SliceFavorites/gem.json +++ b/Gems/SliceFavorites/gem.json @@ -5,7 +5,11 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "Add the ability to favorite a slice to allow easy access and instantiation", - "user_tags": ["Editor", "Slices"], + "user_tags": [ + "Editor", + "Slices" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [] } diff --git a/Gems/StartingPointCamera/gem.json b/Gems/StartingPointCamera/gem.json index 5a9aa0df9b..613eb76267 100644 --- a/Gems/StartingPointCamera/gem.json +++ b/Gems/StartingPointCamera/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Camera Gem provides the behaviors used with the Camera Framework Gem to define a camera rig.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/starting-point-camera/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/starting-point-camera/", + "dependencies": [ + "CameraFramework", + "LmbrCentral" + ] } diff --git a/Gems/StartingPointInput/gem.json b/Gems/StartingPointInput/gem.json index e65f271fe5..d2641ea27b 100644 --- a/Gems/StartingPointInput/gem.json +++ b/Gems/StartingPointInput/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Input Gem provides functionality to map low-level input events to high-level actions.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-input/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-input/", + "dependencies": [ + "ScriptCanvas" + ] } diff --git a/Gems/StartingPointMovement/gem.json b/Gems/StartingPointMovement/gem.json index 70e1d105c1..7def6da768 100644 --- a/Gems/StartingPointMovement/gem.json +++ b/Gems/StartingPointMovement/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Movement Gem provides a series of Lua scripts that listen and respond to input events and trigger transform operations such as translation and rotation.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-movement/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-movement/", + "dependencies": [] } diff --git a/Gems/SurfaceData/gem.json b/Gems/SurfaceData/gem.json index 57e4d8fb70..51a134d5df 100644 --- a/Gems/SurfaceData/gem.json +++ b/Gems/SurfaceData/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Surface Data Gem provides functionality to emit signals or tags from surfaces such as meshes and terrain.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Utility", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Utility", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/surface-data/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/surface-data/", + "dependencies": [ + "LmbrCentral", + "Atom_RPI", + "Atom_Feature_Common" + ] } diff --git a/Gems/Terrain/gem.json b/Gems/Terrain/gem.json index c01ea7e534..cd72a91708 100644 --- a/Gems/Terrain/gem.json +++ b/Gems/Terrain/gem.json @@ -4,7 +4,21 @@ "license": "Apache-2.0 Or MIT", "origin": "Open 3D Engine - o3de.org", "summary": "The Terrain Gem is an experimental terrain system. The terrain system maps height, color, and surface data to regions of the world, provides gradient-based and shape-based authoring tools and workflows, includes specialized rendering for efficient display, and integrates with physics for physical simulation.", - "canonical_tags": [ "Gem" ], - "user_tags": [ "Environment", "Tools", "Design", "Terrain" ], - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/terrain/" + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Tools", + "Design", + "Terrain" + ], + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/terrain/", + "dependencies": [ + "Atom_RPI", + "Atom", + "GradientSignal", + "SurfaceData", + "LmbrCentral" + ] } diff --git a/Gems/TestAssetBuilder/gem.json b/Gems/TestAssetBuilder/gem.json index 1e0b84894a..ba7568005f 100644 --- a/Gems/TestAssetBuilder/gem.json +++ b/Gems/TestAssetBuilder/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Test Asset Builder Gem is used to feature test Asset Processor.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Debug", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Debug", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/test-asset-builder/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/test-asset-builder/", + "dependencies": [] } diff --git a/Gems/TextureAtlas/gem.json b/Gems/TextureAtlas/gem.json index 832dee0e62..345e085359 100644 --- a/Gems/TextureAtlas/gem.json +++ b/Gems/TextureAtlas/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Texture Atlas Gem provides the formatting for texture atlases from 2D textures for LyShine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/texture-atlas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/texture-atlas/", + "dependencies": [ + "Atom_RPI", + "ImageProcessingAtom" + ] } diff --git a/Gems/TickBusOrderViewer/gem.json b/Gems/TickBusOrderViewer/gem.json index 5ea936960a..dc5cb6f66c 100644 --- a/Gems/TickBusOrderViewer/gem.json +++ b/Gems/TickBusOrderViewer/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Tick Bus Order Viewer Gem provides a console variable that displays the order of runtime tick events.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Simulation", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Simulation", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/tick-bus-order-viewer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/tick-bus-order-viewer/", + "dependencies": [] } diff --git a/Gems/Twitch/gem.json b/Gems/Twitch/gem.json index 6f875e9171..f45433bc3f 100644 --- a/Gems/Twitch/gem.json +++ b/Gems/Twitch/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Twitch Gem provides access to the Twitch API v5 SDK including social functions, channels, and other APIs.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "SDK", "Multiplayer"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "SDK", + "Multiplayer" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/twitch/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/twitch/", + "dependencies": [ + "HttpRequestor" + ] } diff --git a/Gems/UiBasics/gem.json b/Gems/UiBasics/gem.json index d1e06eb15a..9a1e16a462 100644 --- a/Gems/UiBasics/gem.json +++ b/Gems/UiBasics/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The UI Basics Gem provides a collection of basic UI prefabs such as image, text, and button, that can be used with LyShine, the Open 3D Engine runtime User Interface system and editor.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/ui-basics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/ui-basics/", + "dependencies": [] } diff --git a/Gems/Vegetation/gem.json b/Gems/Vegetation/gem.json index 3119d67560..9dbcc3450b 100644 --- a/Gems/Vegetation/gem.json +++ b/Gems/Vegetation/gem.json @@ -5,9 +5,21 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Vegetation Gem provides tools to place natural-looking vegetation in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/vegetation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/vegetation/", + "dependencies": [ + "LmbrCentral", + "SurfaceData", + "CommonFeaturesAtom", + "GradientSignal" + ] } diff --git a/Gems/VideoPlaybackFramework/gem.json b/Gems/VideoPlaybackFramework/gem.json index 15b200c8db..9f491f47cb 100644 --- a/Gems/VideoPlaybackFramework/gem.json +++ b/Gems/VideoPlaybackFramework/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Video Playback Framework Gem provides the interface to play back video.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/video-playback-framework/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/video-playback-framework/", + "dependencies": [] } diff --git a/Gems/VirtualGamepad/gem.json b/Gems/VirtualGamepad/gem.json index 472e8b93fe..c6305f1754 100644 --- a/Gems/VirtualGamepad/gem.json +++ b/Gems/VirtualGamepad/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Virtual Gamepad Gem provides controls that emulate a gamepad on touch screen devices.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/virtual-gamepad/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/virtual-gamepad/", + "dependencies": [] } diff --git a/Gems/WhiteBox/gem.json b/Gems/WhiteBox/gem.json index 0e44e2fe5e..81e0ad5f88 100644 --- a/Gems/WhiteBox/gem.json +++ b/Gems/WhiteBox/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The White Box Gem provides White Box rapid design components for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Design", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Design", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/design/white-box/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/design/white-box/", + "dependencies": [ + "Atom_RPI", + "Atom_Feature_Common", + "CommonFeaturesAtom" + ] } From 4ae95719dee570926fd29696f66f04fde2f66d5f Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 22 Sep 2021 12:44:18 -0700 Subject: [PATCH 58/63] Initialization changes to pass unit tests. Some interfaces are now retrieved on demand and there's clearer failure paths that don't involve asserts to better handle test/headless initializations. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../FocusMode/FocusModeSystemComponent.cpp | 8 +-- .../FocusMode/FocusModeSystemComponent.h | 2 +- .../Prefab/PrefabFocusHandler.cpp | 49 +++++++++++-------- .../Prefab/PrefabFocusHandler.h | 11 +---- .../Prefab/PrefabPublicHandler.h | 4 ++ .../Prefab/PrefabSystemComponent.cpp | 1 - .../Prefab/PrefabSystemComponent.h | 4 -- 7 files changed, 37 insertions(+), 42 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index 450e19da3e..e08191032d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -31,22 +31,18 @@ namespace AzToolsFramework return IsInFocusSubTree(parentId, focusRootId); } - FocusModeSystemComponent::~FocusModeSystemComponent() - { - AZ::Interface::Unregister(this); - } - void FocusModeSystemComponent::Init() { - AZ::Interface::Register(this); } void FocusModeSystemComponent::Activate() { + AZ::Interface::Register(this); } void FocusModeSystemComponent::Deactivate() { + AZ::Interface::Unregister(this); } void FocusModeSystemComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index 9943d72951..697a4ff998 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -26,7 +26,7 @@ namespace AzToolsFramework AZ_COMPONENT(FocusModeSystemComponent, "{6CE522FE-2057-4794-BD05-61E04BD8EA30}"); FocusModeSystemComponent() = default; - virtual ~FocusModeSystemComponent(); + virtual ~FocusModeSystemComponent() = default; // AZ::Component overrides ... void Init() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index f74029aa73..2338c8892b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -14,15 +14,8 @@ namespace AzToolsFramework::Prefab { - PrefabFocusHandler::~PrefabFocusHandler() + PrefabFocusHandler::PrefabFocusHandler() { - AZ::Interface::Unregister(this); - } - - void PrefabFocusHandler::Initialize() - { - m_focusModeInterface = AZ::Interface::Get(); - m_instanceEntityMapperInterface = AZ::Interface::Get(); AZ_Assert( m_instanceEntityMapperInterface, @@ -30,21 +23,35 @@ namespace AzToolsFramework::Prefab "Instance Entity Mapper Interface could not be found. " "Check that it is being correctly initialized."); - m_prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); - AZ_Assert( - m_prefabEditorEntityOwnershipInterface, - "Prefab - PrefabFocusHandler - " - "Prefab Editor Entity Ownership Interface could not be found. " - "Check that it is being correctly initialized."); - AZ::Interface::Register(this); } + PrefabFocusHandler::~PrefabFocusHandler() + { + AZ::Interface::Unregister(this); + } + PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) { - InstanceOptionalReference focusedInstance = (entityId == AZ::EntityId()) - ? m_prefabEditorEntityOwnershipInterface->GetRootPrefabInstance() - : m_instanceEntityMapperInterface->FindOwningInstance(entityId); + InstanceOptionalReference focusedInstance; + + if (entityId == AZ::EntityId()) + { + PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = + AZ::Interface::Get(); + + if(!prefabEditorEntityOwnershipInterface) + { + return AZ::Failure(AZStd::string("Could not focus on root prefab instance - internal error " + "(PrefabEditorEntityOwnershipInterface unavailable).")); + } + + focusedInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); + } + else + { + focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); + } if (!focusedInstance.has_value()) { @@ -54,9 +61,11 @@ namespace AzToolsFramework::Prefab m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); - if (m_focusModeInterface) + + FocusModeInterface* focusModeInterface = AZ::Interface::Get(); + if (focusModeInterface) { - m_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); } return AZ::Success(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index b465f97908..fa7727894d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -14,12 +14,6 @@ #include #include -namespace AzToolsFramework -{ - class FocusModeInterface; - class PrefabEditorEntityOwnershipInterface; -} - namespace AzToolsFramework::Prefab { class InstanceEntityMapperInterface; @@ -31,10 +25,9 @@ namespace AzToolsFramework::Prefab public: AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0); + PrefabFocusHandler(); ~PrefabFocusHandler(); - void Initialize(); - // PrefabFocusInterface override ... PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; TemplateId GetFocusedPrefabTemplateId() override; @@ -45,9 +38,7 @@ namespace AzToolsFramework::Prefab InstanceOptionalReference m_focusedInstance; TemplateId m_focusedTemplateId; - FocusModeInterface* m_focusModeInterface; InstanceEntityMapperInterface* m_instanceEntityMapperInterface; - PrefabEditorEntityOwnershipInterface* m_prefabEditorEntityOwnershipInterface; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 42b6bc1666..f3c0e67d46 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -189,6 +190,9 @@ namespace AzToolsFramework PrefabLoaderInterface* m_prefabLoaderInterface = nullptr; PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; + // Handles the Prefab Focus API that determines what prefab is being edited. + PrefabFocusHandler m_prefabFocusHandler; + // Caches entity states for undo/redo purposes PrefabUndoCache m_prefabUndoCache; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 7220cf3cce..0d2696f14b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -34,7 +34,6 @@ namespace AzToolsFramework m_prefabLoader.RegisterPrefabLoaderInterface(); m_instanceUpdateExecutor.RegisterInstanceUpdateExecutorInterface(); m_instanceToTemplatePropagator.RegisterInstanceToTemplateInterface(); - m_prefabFocusHandler.Initialize(); m_prefabPublicHandler.RegisterPrefabPublicHandlerInterface(); m_prefabPublicRequestHandler.Connect(); AZ::SystemTickBus::Handler::BusConnect(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index a9e16374e9..746c306c2b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -379,9 +378,6 @@ namespace AzToolsFramework // Used for loading/saving Prefab Template files. PrefabLoader m_prefabLoader; - // Handles the Prefab Focus API that determines what prefab is being edited. - PrefabFocusHandler m_prefabFocusHandler; - // Handles the public Prefab API used by UI and scripting. PrefabPublicHandler m_prefabPublicHandler; From cfc9ec2530dbb389a4e123db7d0448c8f22ccff9 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Wed, 22 Sep 2021 13:44:21 -0700 Subject: [PATCH 59/63] Implement Project Manager 'build' button for Mac and Linux(#4248) Signed-off-by: Steve Pham --- .../Linux/ProjectBuilderWorker_linux.cpp | 65 +++++- .../Platform/Linux/ProjectUtils_linux.cpp | 39 +++- .../Platform/Mac/ProjectBuilderWorker_mac.cpp | 76 ++++++- .../Platform/Mac/ProjectUtils_mac.cpp | 52 ++++- .../Windows/ProjectBuilderWorker_windows.cpp | 194 ++---------------- .../Platform/Windows/ProjectUtils_windows.cpp | 78 +++++-- .../Source/ProjectBuilderWorker.cpp | 179 ++++++++++++++++ .../Source/ProjectBuilderWorker.h | 7 + .../Source/ProjectManagerDefs.h | 5 + .../ProjectManager/Source/ProjectUtils.cpp | 30 +++ .../ProjectManager/Source/ProjectUtils.h | 14 +- .../ProjectManager/Source/PythonBindings.cpp | 2 +- 12 files changed, 532 insertions(+), 209 deletions(-) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp index cffac78365..2987fc4dc2 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp @@ -7,14 +7,69 @@ */ #include +#include +#include + +#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const { - QString error = tr("Automatic building on Linux not currently supported!"); - QStringToAZTracePrint(error); - return AZ::Failure(error); + // Attempt to use the Ninja build system if it is installed (described in the o3de documentation) if possible, + // otherwise default to the the default for Linux (Unix Makefiles) + auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment()); + QString cmakeGenerator = (whichNinjaResult.IsSuccess()) ? "Ninja Multi-Config" : "Unix Makefiles"; + bool compileProfileOnBuild = (whichNinjaResult.IsSuccess()); + + // On Linux the default compiler is gcc. For O3DE, it is clang, so we need to specify the version of clang that is detected + // in order to get the compiler option. + auto compilerOptionResult = ProjectUtils::FindSupportedCompilerForPlatform(); + if (!compilerOptionResult.IsSuccess()) + { + return AZ::Failure(compilerOptionResult.GetError()); + } + auto clangCompilers = compilerOptionResult.GetValue().split('|'); + AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification"); + + QString clangCompilerOption = clangCompilers[0]; + QString clangPPCompilerOption = clangCompilers[1]; + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QStringList generateProjectArgs = QStringList{ProjectCMakeCommand, + "-B", ProjectBuildPathPostfix, + "-S", ".", + QString("-G%1").arg(cmakeGenerator), + QString("-DCMAKE_C_COMPILER=").append(clangCompilerOption), + QString("-DCMAKE_CXX_COMPILER=").append(clangPPCompilerOption), + QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath)}; + if (!compileProfileOnBuild) + { + generateProjectArgs.append("-DCMAKE_BUILD_TYPE=profile"); + } + return AZ::Success(generateProjectArgs); } - + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment()); + bool compileProfileOnBuild = (whichNinjaResult.IsSuccess()); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; + + QStringList buildProjectArgs = QStringList{ProjectCMakeCommand, + "--build", ProjectBuildPathPostfix, + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor}; + if (compileProfileOnBuild) + { + buildProjectArgs.append(QStringList{"--config","profile"}); + } + return AZ::Success(buildProjectArgs); + } + + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList{"kill", "-9", pidToKill}); + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp index 64d18ec605..56feb9f70a 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp @@ -6,15 +6,48 @@ */ #include +#include +#include namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + // The list of clang C/C++ compiler command lines to validate on the host Linux system + const QStringList SupportedClangCommands = {"clang-12|clang++-12"}; + + AZ::Outcome GetCommandLineProcessEnvironment() { - // Compiler detection not supported on platform - return AZ::Success(); + return AZ::Success(QProcessEnvironment(QProcessEnvironment::systemEnvironment())); + } + + AZ::Outcome FindSupportedCompilerForPlatform() + { + // Validate that cmake is installed and is in the command line + auto whichCMakeResult = ProjectUtils::ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, QProcessEnvironment::systemEnvironment()); + if (!whichCMakeResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("CMake not found. \n\n" + "Make sure that the minimum version of CMake is installed and available from the command prompt. " + "Refer to the O3DE requirements page for more information.")); + } + + // Look for the first compatible version of clang. The list below will contain the known clang compilers that have been tested for O3DE. + for (const QString& supportClangCommand : SupportedClangCommands) + { + auto clangCompilers = supportClangCommand.split('|'); + AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification"); + + auto whichClangResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[0]}, QProcessEnvironment::systemEnvironment()); + auto whichClangPPResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[1]}, QProcessEnvironment::systemEnvironment()); + if (whichClangResult.IsSuccess() && whichClangPPResult.IsSuccess()) + { + return AZ::Success(supportClangCommand); + } + } + return AZ::Failure(QObject::tr("Clang not found. \n\n" + "Make sure that the clang is installed and available from the command prompt. " + "Refer to the O3DE requirements page for more information.")); } } // namespace ProjectUtils diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp index 7f4c5eb5d0..ab412d84d8 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp @@ -7,14 +7,82 @@ */ #include +#include +#include + +#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + namespace Internal { - QString error = tr("Automatic building on MacOS not currently supported!"); - QStringToAZTracePrint(error); - return AZ::Failure(error); + AZ::Outcome QueryInstalledCmakeFullPath() + { + auto environmentRequest = ProjectUtils::GetCommandLineProcessEnvironment(); + if (!environmentRequest.IsSuccess()) + { + return AZ::Failure(environmentRequest.GetError()); + } + auto currentEnvironment = environmentRequest.GetValue(); + + auto queryCmakeInstalled = ProjectUtils::ExecuteCommandResult("which", + QStringList{ProjectCMakeCommand}, + currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect CMake on this host.")); + } + QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0]; + return AZ::Success(cmakeInstalledPath); + } + } + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const + { + // For Mac, we need to resolve the full path of cmake and use that in the process request. For + // some reason, 'which' will resolve the full path, but when you just specify cmake with the same + // environment, it is unable to resolve. To work around this, we will use 'which' to resolve the + // full path and then use it as the command argument + auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath(); + if (!cmakeInstalledPathQuery.IsSuccess()) + { + return AZ::Failure(cmakeInstalledPathQuery.GetError()); + } + QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue(); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + + return AZ::Success(QStringList{cmakeInstalledPath, + "-B", targetBuildPath, + "-S", m_projectInfo.m_path, + "-GXcode"}); + } + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + // For Mac, we need to resolve the full path of cmake and use that in the process request. For + // some reason, 'which' will resolve the full path, but when you just specify cmake with the same + // environment, it is unable to resolve. To work around this, we will use 'which' to resolve the + // full path and then use it as the command argument + auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath(); + if (!cmakeInstalledPathQuery.IsSuccess()) + { + return AZ::Failure(cmakeInstalledPathQuery.GetError()); + } + + QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue(); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; + + return AZ::Success(QStringList{cmakeInstalledPath, + "--build", targetBuildPath, + "--config", "profile", + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor}); + } + + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList{"kill", "-9", pidToKill}); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp index 64d18ec605..0d150abc3b 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp @@ -7,15 +7,59 @@ #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + AZ::Outcome GetCommandLineProcessEnvironment() { - // Compiler detection not supported on platform - return AZ::Success(); + // For CMake on Mac, if its installed through home-brew, then it will be installed + // under /usr/local/bin, which may not be in the system PATH environment. + // Add that path for the command line process so that it will be able to locate + // a home-brew installed version of CMake + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ":/usr/local/bin"; + currentEnvironment.insert("PATH", pathValue); + return AZ::Success(currentEnvironment); } - + + AZ::Outcome FindSupportedCompilerForPlatform() + { + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ":/usr/local/bin"; + currentEnvironment.insert("PATH", pathValue); + + // Validate that we have cmake installed first + auto queryCmakeInstalled = ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect CMake on this host.")); + } + QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0]; + + // Query the version of the installed cmake + auto queryCmakeVersionQuery = ExecuteCommandResult(cmakeInstalledPath, QStringList{"-version"}, currentEnvironment); + if (!queryCmakeVersionQuery.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to determine the version of CMake on this host.")); + } + AZ_TracePrintf("Project Manager", "Cmake version %s detected.", queryCmakeVersionQuery.GetValue().split("\n")[0].toUtf8().constData()); + + // Query for the version of xcodebuild (if installed) + auto queryXcodeBuildVersion = ExecuteCommandResult("xcodebuild", QStringList{"-version"}, currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect XCodeBuilder on this host.")); + } + QString xcodeBuilderVersionNumber = queryXcodeBuildVersion.GetValue().split("\n")[0]; + AZ_TracePrintf("Project Manager", "XcodeBuilder version %s detected.", xcodeBuilderVersionNumber.toUtf8().constData()); + + + return AZ::Success(xcodeBuilderVersionNumber); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp index 8856e2312a..075c6de774 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp @@ -8,189 +8,37 @@ #include #include -#include #include -#include -#include -#include -#include -#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const { - // Check if we are trying to cancel task - if (QThread::currentThread()->isInterruptionRequested()) - { - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); - QFile logFile(GetLogFilePath()); - if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) - { - QString error = tr("Failed to open log file."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } + return AZ::Success(QStringList{ ProjectCMakeCommand, + "-B", targetBuildPath, + "-S", m_projectInfo.m_path, + QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath), + "-DLY_UNITY_BUILD=ON" } ); + } - EngineInfo engineInfo; + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; - AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); - if (engineInfoResult.IsSuccess()) - { - engineInfo = engineInfoResult.GetValue(); - } - else - { - QString error = tr("Failed to get engine info."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } + return AZ::Success(QStringList{ ProjectCMakeCommand, + "--build", targetBuildPath, + "--config", "profile", + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor }); + } - QTextStream logStream(&logFile); - if (QThread::currentThread()->isInterruptionRequested()) - { - logFile.close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - - // Show some kind of progress with very approximate estimates - UpdateProgress(++m_progressEstimate); - - QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); - // Append cmake path to PATH incase it is missing - QDir cmakePath(engineInfo.m_path); - cmakePath.cd("cmake/runtime/bin"); - QString pathValue = currentEnvironment.value("PATH"); - pathValue += ";" + cmakePath.path(); - currentEnvironment.insert("PATH", pathValue); - - m_configProjectProcess = new QProcess(this); - m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels); - m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path); - m_configProjectProcess->setProcessEnvironment(currentEnvironment); - - m_configProjectProcess->start( - "cmake", - QStringList - { - "-B", - QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), - "-S", - m_projectInfo.m_path, - "-G", - "Visual Studio 16", - "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath, - "-DLY_UNITY_BUILD=1" - }); - - if (!m_configProjectProcess->waitForStarted()) - { - QString error = tr("Configuring project failed to start."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - bool containsGeneratingDone = false; - while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) - { - QString configOutput = m_configProjectProcess->readAllStandardOutput(); - - if (configOutput.contains("Generating done")) - { - containsGeneratingDone = true; - } - - logStream << configOutput; - logStream.flush(); - - UpdateProgress(qMin(++m_progressEstimate, 19)); - - if (QThread::currentThread()->isInterruptionRequested()) - { - logFile.close(); - m_configProjectProcess->close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - } - - if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit - || m_configProjectProcess->exitCode() != 0 - || !containsGeneratingDone) - { - QString error = tr("Configuring project failed. See log for details."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - UpdateProgress(++m_progressEstimate); - - m_buildProjectProcess = new QProcess(this); - m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels); - m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path); - m_buildProjectProcess->setProcessEnvironment(currentEnvironment); - - m_buildProjectProcess->start( - "cmake", - QStringList - { - "--build", - QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), - "--target", - m_projectInfo.m_projectName + ".GameLauncher", - "Editor", - "--config", - "profile" - }); - - if (!m_buildProjectProcess->waitForStarted()) - { - QString error = tr("Building project failed to start."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - // There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining - m_progressEstimate = 200; - while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) - { - logStream << m_buildProjectProcess->readAllStandardOutput(); - logStream.flush(); - - // Show 1% progress for every 10 steps completed - UpdateProgress(qMin(++m_progressEstimate / 10, 99)); - - if (QThread::currentThread()->isInterruptionRequested()) - { - // QProcess is unable to kill its child processes so we need to ask the operating system to do that for us - QProcess killBuildProcess; - killBuildProcess.setProcessChannelMode(QProcess::MergedChannels); - killBuildProcess.start( - "cmd.exe", QStringList{ "/C", "taskkill", "/pid", QString::number(m_buildProjectProcess->processId()), "/f", "/t" }); - killBuildProcess.waitForFinished(); - - logStream << "Killing Project Build."; - logStream << killBuildProcess.readAllStandardOutput(); - m_buildProjectProcess->kill(); - logFile.close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - } - - if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit - || m_configProjectProcess->exitCode() != 0) - { - QString error = tr("Building project failed. See log for details."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - return AZ::Success(); + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList { "cmd.exe", "/C", "taskkill", "/pid", pidToKill, "/f", "/t" } ); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp index 7ca51fb9c5..d012ca8921 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp @@ -7,6 +7,8 @@ #include +#include + #include #include #include @@ -16,8 +18,44 @@ namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + AZ::Outcome GetCommandLineProcessEnvironment() { + // Use the engine path to insert a path for cmake + auto engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (!engineInfoResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("Failed to get engine info")); + } + auto engineInfo = engineInfoResult.GetValue(); + + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + + // Append cmake path to PATH incase it is missing + QDir cmakePath(engineInfo.m_path); + cmakePath.cd("cmake/runtime/bin"); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ";" + cmakePath.path(); + currentEnvironment.insert("PATH", pathValue); + return AZ::Success(currentEnvironment); + } + + AZ::Outcome FindSupportedCompilerForPlatform() + { + // Validate that cmake is installed + auto cmakeProcessEnvResult = GetCommandLineProcessEnvironment(); + if (!cmakeProcessEnvResult.IsSuccess()) + { + return AZ::Failure(cmakeProcessEnvResult.GetError()); + } + auto cmakeVersionQueryResult = ExecuteCommandResult("cmake", QStringList{"--version"}, cmakeProcessEnvResult.GetValue()); + if (!cmakeVersionQueryResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("CMake not found. \n\n" + "Make sure that the minimum version of CMake is installed and available from the command prompt. " + "Refer to the O3DE requirements for more information.")); + } + + // Validate that the minimal version of visual studio is installed QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); QString programFilesPath = environment.value("ProgramFiles(x86)"); QString vsWherePath = QDir(programFilesPath).filePath("Microsoft Visual Studio/Installer/vswhere.exe"); @@ -25,27 +63,31 @@ namespace O3DE::ProjectManager QFileInfo vsWhereFile(vsWherePath); if (vsWhereFile.exists() && vsWhereFile.isFile()) { - QProcess vsWhereProcess; - vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels); + QStringList vsWhereBaseArguments = QStringList{"-version", + "16.9.2", + "-latest", + "-requires", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"}; - vsWhereProcess.start( - vsWherePath, - QStringList{ - "-version", - "16.9.2", - "-latest", - "-requires", - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", - "isComplete" - }); + QProcess vsWhereIsCompleteProcess; + vsWhereIsCompleteProcess.setProcessChannelMode(QProcess::MergedChannels); - if (vsWhereProcess.waitForStarted() && vsWhereProcess.waitForFinished()) + vsWhereIsCompleteProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{ "-property", "isComplete" }); + + if (vsWhereIsCompleteProcess.waitForStarted() && vsWhereIsCompleteProcess.waitForFinished()) { - QString vsWhereOutput(vsWhereProcess.readAllStandardOutput()); - if (vsWhereOutput.startsWith("1")) + QString vsWhereIsCompleteOutput(vsWhereIsCompleteProcess.readAllStandardOutput()); + if (vsWhereIsCompleteOutput.startsWith("1")) { - return AZ::Success(); + QProcess vsWhereCompilerVersionProcess; + vsWhereCompilerVersionProcess.setProcessChannelMode(QProcess::MergedChannels); + vsWhereCompilerVersionProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{"-property", "catalog_productDisplayVersion"}); + + if (vsWhereCompilerVersionProcess.waitForStarted() && vsWhereCompilerVersionProcess.waitForFinished()) + { + QString vsWhereCompilerVersionOutput(vsWhereCompilerVersionProcess.readAllStandardOutput()); + return AZ::Success(vsWhereCompilerVersionOutput); + } } } } diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp index 2fe1c6db15..c6a6b20a1d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp @@ -8,8 +8,15 @@ #include #include +#include +#include #include +#include +#include +#include +#include +#include //#define MOCK_BUILD_PROJECT true @@ -67,4 +74,176 @@ namespace O3DE::ProjectManager { AZ_TracePrintf("Project Manager", error.toStdString().c_str()); } + + AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + { + // Check if we are trying to cancel task + if (QThread::currentThread()->isInterruptionRequested()) + { + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + + QFile logFile(GetLogFilePath()); + if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + { + QString error = tr("Failed to open log file."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + EngineInfo engineInfo; + + AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (engineInfoResult.IsSuccess()) + { + engineInfo = engineInfoResult.GetValue(); + } + else + { + QString error = tr("Failed to get engine info."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + QTextStream logStream(&logFile); + if (QThread::currentThread()->isInterruptionRequested()) + { + logFile.close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + + // Show some kind of progress with very approximate estimates + UpdateProgress(++m_progressEstimate); + + auto currentEnvironmentRequest = ProjectUtils::GetCommandLineProcessEnvironment(); + if (!currentEnvironmentRequest.IsSuccess()) + { + QStringToAZTracePrint(currentEnvironmentRequest.GetError()); + return AZ::Failure(currentEnvironmentRequest.GetError()); + } + QProcessEnvironment currentEnvironment = currentEnvironmentRequest.GetValue(); + + m_configProjectProcess = new QProcess(this); + m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels); + m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path); + m_configProjectProcess->setProcessEnvironment(currentEnvironment); + + auto cmakeGenerateArgumentsResult = ConstructCmakeGenerateProjectArguments(engineInfo.m_thirdPartyPath); + if (!cmakeGenerateArgumentsResult.IsSuccess()) + { + QStringToAZTracePrint(cmakeGenerateArgumentsResult.GetError()); + return AZ::Failure(cmakeGenerateArgumentsResult.GetError()); + } + auto cmakeGenerateArguments = cmakeGenerateArgumentsResult.GetValue(); + m_configProjectProcess->start(cmakeGenerateArguments.front(), cmakeGenerateArguments.mid(1)); + if (!m_configProjectProcess->waitForStarted()) + { + QString error = tr("Configuring project failed to start."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + bool containsGeneratingDone = false; + while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) + { + QString configOutput = m_configProjectProcess->readAllStandardOutput(); + + if (configOutput.contains("Generating done")) + { + containsGeneratingDone = true; + } + + logStream << configOutput; + logStream.flush(); + + UpdateProgress(qMin(++m_progressEstimate, 19)); + + if (QThread::currentThread()->isInterruptionRequested()) + { + logFile.close(); + m_configProjectProcess->close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + } + + if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit || m_configProjectProcess->exitCode() != 0 || + !containsGeneratingDone) + { + QString error = tr("Configuring project failed. See log for details."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + UpdateProgress(++m_progressEstimate); + + m_buildProjectProcess = new QProcess(this); + m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels); + m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path); + m_buildProjectProcess->setProcessEnvironment(currentEnvironment); + + auto cmakeBuildArgumentsResult = ConstructCmakeBuildCommandArguments(); + if (!cmakeBuildArgumentsResult.IsSuccess()) + { + QStringToAZTracePrint(cmakeBuildArgumentsResult.GetError()); + return AZ::Failure(cmakeBuildArgumentsResult.GetError()); + } + auto cmakeBuildArguments = cmakeBuildArgumentsResult.GetValue(); + + m_buildProjectProcess->start(cmakeBuildArguments.front(), cmakeBuildArguments.mid(1)); + if (!m_buildProjectProcess->waitForStarted()) + { + QString error = tr("Building project failed to start."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + // There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining + m_progressEstimate = 200; + while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) + { + logStream << m_buildProjectProcess->readAllStandardOutput(); + logStream.flush(); + + // Show 1% progress for every 10 steps completed + UpdateProgress(qMin(++m_progressEstimate / 10, 99)); + + if (QThread::currentThread()->isInterruptionRequested()) + { + // QProcess is unable to kill its child processes so we need to ask the operating system to do that for us + auto killProcessArgumentsResult = ConstructKillProcessCommandArguments(QString::number(m_buildProjectProcess->processId())); + if (!killProcessArgumentsResult.IsSuccess()) + { + return AZ::Failure(killProcessArgumentsResult.GetError()); + } + auto killProcessArguments = killProcessArgumentsResult.GetValue(); + + + QProcess killBuildProcess; + + + killBuildProcess.setProcessChannelMode(QProcess::MergedChannels); + killBuildProcess.start(killProcessArguments.front(), killProcessArguments.mid(1)); + killBuildProcess.waitForFinished(); + + logStream << "Killing Project Build."; + logStream << killBuildProcess.readAllStandardOutput(); + m_buildProjectProcess->kill(); + logFile.close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + } + + if (m_buildProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit || m_buildProjectProcess->exitCode() != 0) + { + QString error = tr("Building project failed. See log for details."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + return AZ::Success(); + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h index f42c87f47b..94c4a6bd52 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h @@ -12,6 +12,7 @@ #include #include +#include #endif QT_FORWARD_DECLARE_CLASS(QProcess) @@ -44,6 +45,12 @@ namespace O3DE::ProjectManager AZ::Outcome BuildProjectForPlatform(); void QStringToAZTracePrint(const QString& error); + // Command line argument builders + AZ::Outcome ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const; + AZ::Outcome ConstructCmakeBuildCommandArguments() const; + AZ::Outcome ConstructKillProcessCommandArguments(const QString& pidToKill) const; + + QProcess* m_configProjectProcess = nullptr; QProcess* m_buildProjectProcess = nullptr; ProjectInfo m_projectInfo; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h index 8ab5128741..264515652f 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h @@ -14,6 +14,7 @@ namespace O3DE::ProjectManager inline constexpr static int ProjectPreviewImageWidth = 210; inline constexpr static int ProjectPreviewImageHeight = 280; inline constexpr static int ProjectTemplateImageWidth = 92; + inline constexpr static int ProjectCommandLineTimeoutSeconds = 30; static const QString ProjectBuildDirectoryName = "build"; extern const QString ProjectBuildPathPostfix; @@ -21,4 +22,8 @@ namespace O3DE::ProjectManager static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log"; static const QString ProjectCacheDirectoryName = "Cache"; static const QString ProjectPreviewImagePath = "preview.png"; + + static const QString ProjectCMakeCommand = "cmake"; + static const QString ProjectCMakeBuildTargetEditor = "Editor"; + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index 84d731832e..81eb70391d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -22,6 +22,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -507,5 +509,33 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::Invalid; } + + AZ::Outcome ExecuteCommandResult( + const QString& cmd, + const QStringList& arguments, + const QProcessEnvironment& processEnv, + int commandTimeoutSeconds /*= ProjectCommandLineTimeoutSeconds*/) + { + QProcess execProcess; + execProcess.setProcessEnvironment(processEnv); + execProcess.setProcessChannelMode(QProcess::MergedChannels); + execProcess.start(cmd, arguments); + if (!execProcess.waitForStarted()) + { + return AZ::Failure(QObject::tr("Unable to start process for command '%1'").arg(cmd)); + } + + if (!execProcess.waitForFinished(commandTimeoutSeconds * 1000 /* Milliseconds per second */)) + { + return AZ::Failure(QObject::tr("Process for command '%1' timed out at %2 seconds").arg(cmd).arg(commandTimeoutSeconds)); + } + int resultCode = execProcess.exitCode(); + if (resultCode != 0) + { + return AZ::Failure(QObject::tr("Process for command '%1' failed (result code %2").arg(cmd).arg(resultCode)); + } + QString resultOutput = execProcess.readAllStandardOutput(); + return AZ::Success(resultOutput); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index f1050531d4..0866b57923 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -9,8 +9,11 @@ #include #include +#include #include +#include + #include namespace O3DE::ProjectManager @@ -28,8 +31,17 @@ namespace O3DE::ProjectManager bool ReplaceProjectFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true); bool FindSupportedCompiler(QWidget* parent = nullptr); - AZ::Outcome FindSupportedCompilerForPlatform(); + AZ::Outcome FindSupportedCompilerForPlatform(); ProjectManagerScreen GetProjectManagerScreen(const QString& screen); + + AZ::Outcome ExecuteCommandResult( + const QString& cmd, + const QStringList& arguments, + const QProcessEnvironment& processEnv, + int commandTimeoutSeconds = ProjectCommandLineTimeoutSeconds); + + AZ::Outcome GetCommandLineProcessEnvironment(); + } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 6f8ff9abce..c92ad53cd4 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -339,7 +339,7 @@ namespace O3DE::ProjectManager { for (auto engine : allEngines) { - AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"])); + AZ::IO::FixedMaxPath enginePath(Py_To_String(engine)); if (enginePath.Compare(m_enginePath) == 0) { return; From 62f7fbb6654b7b28b92f33db17f7704f06720df9 Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Wed, 22 Sep 2021 14:43:57 -0700 Subject: [PATCH 60/63] LYN-6793 [iOS] [asset_profile] 4 assets fail to process for iOS (#4268) * LYN-6793 [iOS] [asset_profile] 4 assets fail to process for iOS The issue was because the compression of ETC formats took too long. Replaced all ETC and PVRTC formats with ASTC formats. Update all pixel operation for formats with single R channel to align the change with R8. Signed-off-by: Qing Tao --- .../Code/Source/Converters/PixelOperation.cpp | 16 ++++++++-------- .../ImageProcessingAtom/Config/Albedo.preset | 2 +- .../Config/AlbedoWithCoverage.preset | 2 +- .../Config/AlbedoWithGenericAlpha.preset | 2 +- .../Config/AlbedoWithOpacity.preset | 2 +- .../Config/AmbientOcclusion.preset | 4 ++-- .../Config/CloudShadows.preset | 4 ++-- .../Config/Decal_AlbedoWithOpacity.preset | 4 ++-- .../Detail_MergedAlbedoNormalsSmoothness.preset | 2 +- .../Config/Displacement.preset | 4 ++-- .../ImageProcessingAtom/Config/Emissive.preset | 2 +- .../ImageProcessingAtom/Config/Greyscale.preset | 4 ++-- .../ImageProcessingAtom/Config/LensOptics.preset | 2 +- .../Config/LightProjector.preset | 4 ++-- .../ImageProcessingAtom/Config/Minimap.preset | 2 +- .../Config/MuzzleFlash.preset | 2 +- .../ImageProcessingAtom/Config/Opacity.preset | 4 ++-- .../Config/Reflectance.preset | 2 +- .../ReflectanceWithSmoothness_Legacy.preset | 2 +- .../Config/Reflectance_Linear.preset | 2 +- .../ImageProcessingAtom/Config/SF_Image.preset | 4 ++-- .../Config/SF_Image_nonpower2.preset | 4 ++-- .../Config/Terrain_Albedo.preset | 2 +- .../Config/Terrain_Albedo_HighPassed.preset | 2 +- .../Config/UserInterface_Compressed.preset | 2 +- 25 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp index 3e897078fa..fa4baeefc0 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp @@ -248,8 +248,8 @@ namespace ImageProcessingAtom { const uint8* data = buf; r = U8ToF32(data[0]); - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -333,8 +333,8 @@ namespace ImageProcessingAtom { const uint16* data = (uint16*)(buf); r = U16ToF32(data[0]); - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -418,8 +418,8 @@ namespace ImageProcessingAtom { const float* data = (float*)(buf); r = data[0]; - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -485,8 +485,8 @@ namespace ImageProcessingAtom { const SHalf* data = (SHalf*)(buf); r = data[0]; - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset index 3a5122f18e..e0d043cf20 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset @@ -39,7 +39,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "DiscardAlpha": true, "IsPowerOf2": true, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset index 692ef99b1c..fda5a9cc52 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a1", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset index 4ebe773f0e..8c196530e9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset index 6049ef5bd4..346f0f8cac 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset index 56dec20f3e..638e31f838 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset @@ -28,7 +28,7 @@ "_amb", "_ambientocclusion" ], - "PixelFormat": "EAC_R11" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", @@ -41,7 +41,7 @@ "_amb", "_ambientocclusion" ], - "PixelFormat": "EAC_R11" + "PixelFormat": "ASTC_4x4" }, "mac": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset index 2280a06302..8d1105cdfc 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset @@ -15,14 +15,14 @@ "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "ios": { "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "mac": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset index f1e43e74b1..628cd673e9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset @@ -24,13 +24,13 @@ "FileMasks": [ "_decal" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" }, // Decal Texture Arrays need all mips available immediately for packing. - "NumberResidentMips": 255 + "NumberResidentMips": 255 }, "ios": { "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset index 991692c5cc..5bfea9a376 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset @@ -26,7 +26,7 @@ "FileMasks": [ "_detail" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset index 520e4ae193..8f21ad005b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset @@ -45,7 +45,7 @@ "_ht", "_h" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "DiscardAlpha": true, "IsPowerOf2": true, "SizeReduceLevel": 3, @@ -70,7 +70,7 @@ "_ht", "_h" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "DiscardAlpha": true, "IsPowerOf2": true, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset index ffb16482fd..8f6c846e82 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset @@ -29,7 +29,7 @@ "_em", "_emit" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "DiscardAlpha": true }, "ios": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset index f06682be42..c71ada1269 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset @@ -26,7 +26,7 @@ "FileMasks": [ "_mask" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -40,7 +40,7 @@ "FileMasks": [ "_mask" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset index 9f4b5bf68d..d277785151 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset @@ -12,7 +12,7 @@ "android": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", "Name": "LensOptics", - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset index ede264a78e..4de95427d6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset @@ -18,7 +18,7 @@ "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", - "PixelFormat": "EAC_RG11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -28,7 +28,7 @@ "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", - "PixelFormat": "EAC_RG11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset index 9370de063d..79dda1977e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset @@ -19,7 +19,7 @@ "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", "Name": "Minimap", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "SizeReduceLevel": 1, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset index 459cd5b1fb..b02227b454 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset @@ -18,7 +18,7 @@ "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", "Name": "MuzzleFlash", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset index bbd7fd5db9..8b66b30f28 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset @@ -44,7 +44,7 @@ "_msk", "_blend" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -67,7 +67,7 @@ "_msk", "_blend" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset index 1844e0186e..e9ea34060b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset @@ -57,7 +57,7 @@ "_roughness", "_rough" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset index e51cc7122b..e868a44096 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset @@ -22,7 +22,7 @@ "FileMasks": [ "_spec" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset index 07cc39c955..e52b616f48 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset @@ -26,7 +26,7 @@ "_spec", "_refl" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset index 46a32ce5d4..191425bb92 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset @@ -19,7 +19,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "ios": { @@ -28,7 +28,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "PVRTC4", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "mac": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset index 0ba70d2ca3..9b1a9c7c45 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset @@ -18,7 +18,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", @@ -26,7 +26,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "PVRTC4" + "PixelFormat": "ASTC_4x4" }, "mac": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset index 84a70935f1..8b12a08465 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset @@ -21,7 +21,7 @@ "Name": "Terrain_Albedo", "SourceColor": "Linear", "DestColor": "Linear", - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "HighPassMip": 5, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset index 1d83737ef9..d24531858f 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset @@ -20,7 +20,7 @@ "Name": "Terrain_Albedo_HighPassed", "SourceColor": "Linear", "DestColor": "Linear", - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset index 6f70e8f14f..13334de700 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset @@ -17,7 +17,7 @@ "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", "Name": "UserInterface_Compressed", "SuppressEngineReduce": true, - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_6x6" }, "ios": { "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", From 982bef8766c0008d4deade605ddb21a66503088a Mon Sep 17 00:00:00 2001 From: Tommy Walton <82672795+amzn-tommy@users.noreply.github.com> Date: Wed, 22 Sep 2021 15:48:43 -0700 Subject: [PATCH 61/63] Use resize_no_construct when creating a buffer asset, since the initial data is going to be memcopy'd anyways. (#4249) Signed-off-by: amzn-tommy --- .../RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp index bb80a1b8d2..feeeff36cf 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp @@ -57,7 +57,7 @@ namespace AZ // Only allocate buffer if initial data is not empty if (initialData != nullptr && initialDataSize > 0) { - bufferAsset->m_buffer.resize(descriptor.m_byteCount); + bufferAsset->m_buffer.resize_no_construct(descriptor.m_byteCount); memcpy(bufferAsset->m_buffer.data(), initialData, initialDataSize); } From 92c4b224450aff852bc64b52dcccc842c7784088 Mon Sep 17 00:00:00 2001 From: Tommy Walton <82672795+amzn-tommy@users.noreply.github.com> Date: Wed, 22 Sep 2021 15:49:01 -0700 Subject: [PATCH 62/63] Fix a minor spelling mistake (#4247) Signed-off-by: amzn-tommy --- Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp index 38b29bcdf2..f9f9580c1a 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp @@ -195,7 +195,7 @@ namespace EditorPythonBindings if (!m_handler) { - AZ_Error("python", false, "No EBus connection deteced; missing call or failed call to connect()?"); + AZ_Error("python", false, "No EBus connection detected; missing call or failed call to connect()?"); return false; } From 5ae35810ae4d98f64b543fdd43cad6a092e2b04f Mon Sep 17 00:00:00 2001 From: SJ Date: Wed, 22 Sep 2021 19:11:20 -0700 Subject: [PATCH 63/63] 1. Update iOS deployment target to 14 (#4266) 2. Set MacOS default deployment target to 11. Signed-off-by: amzn-sj --- cmake/Platform/Mac/PAL_mac.cmake | 4 ++++ cmake/Platform/iOS/Toolchain_ios.cmake | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/Platform/Mac/PAL_mac.cmake b/cmake/Platform/Mac/PAL_mac.cmake index 065b35d69c..561ce86570 100644 --- a/cmake/Platform/Mac/PAL_mac.cmake +++ b/cmake/Platform/Mac/PAL_mac.cmake @@ -37,6 +37,10 @@ endif() # Set the default asset type for deployment set(LY_ASSET_DEPLOY_ASSET_TYPE "mac" CACHE STRING "Set the asset type for deployment.") +# Set the deployment target for MacOS +set(LY_MAC_DEPLOYMENT_TARGET "11.0" CACHE STRING "Mac Deployment Target") +set(CMAKE_OSX_DEPLOYMENT_TARGET ${LY_MAC_DEPLOYMENT_TARGET}) + # Set the python cmd tool ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh) diff --git a/cmake/Platform/iOS/Toolchain_ios.cmake b/cmake/Platform/iOS/Toolchain_ios.cmake index 49f0b23461..719c492ebb 100644 --- a/cmake/Platform/iOS/Toolchain_ios.cmake +++ b/cmake/Platform/iOS/Toolchain_ios.cmake @@ -13,7 +13,7 @@ set(CMAKE_OSX_ARCHITECTURES arm64) set(LY_IOS_CODE_SIGNING_IDENTITY "iPhone Developer" CACHE STRING "iPhone Developer") -set(LY_IOS_DEPLOYMENT_TARGET "13.0" CACHE STRING "iOS Deployment Target") +set(LY_IOS_DEPLOYMENT_TARGET "14.0" CACHE STRING "iOS Deployment Target") set(LY_IOS_DEVELOPMENT_TEAM "CF9TGN983S" CACHE STRING "The development team ID")