diff --git a/.gitignore b/.gitignore index 24af068c53..eb24d72701 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ _savebackup/ #Output folder for test results when running Automated Tests TestResults/** *.swatches +/imgui.ini diff --git a/AutomatedTesting/Assets/ImageGradients/image_grad_test_gsi.png b/AutomatedTesting/Assets/ImageGradients/image_grad_test_gsi.png new file mode 100644 index 0000000000..228aa877ce --- /dev/null +++ b/AutomatedTesting/Assets/ImageGradients/image_grad_test_gsi.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:171f38d536d7b805cc644513d22dae5552a4eef2bffb88e97e089898cf769530 +size 2126 diff --git a/AutomatedTesting/Assets/ImageGradients/lumberyard_gsi.png b/AutomatedTesting/Assets/ImageGradients/lumberyard_gsi.png deleted file mode 100644 index eab76f1f78..0000000000 --- a/AutomatedTesting/Assets/ImageGradients/lumberyard_gsi.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6388466c97009fd3993e5d3b59a2b0961f623c6becbd0a12a0a5eb7bd8da5d4e -size 12302 diff --git a/AutomatedTesting/Gem/Code/Platform/Windows/runtime_dependencies.cmake b/AutomatedTesting/Gem/Code/Platform/Windows/runtime_dependencies.cmake index 617816dcd0..0a1541bcfc 100644 --- a/AutomatedTesting/Gem/Code/Platform/Windows/runtime_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/Platform/Windows/runtime_dependencies.cmake @@ -12,4 +12,5 @@ set(GEM_DEPENDENCIES Gem::Atom_RHI_Vulkan.Private Gem::Atom_RHI_DX12.Private + Gem::Atom_RHI_Null.Private ) \ No newline at end of file diff --git a/AutomatedTesting/Gem/Code/Platform/Windows/tool_dependencies.cmake b/AutomatedTesting/Gem/Code/Platform/Windows/tool_dependencies.cmake index d13d0fb180..ddd3bfa6a7 100644 --- a/AutomatedTesting/Gem/Code/Platform/Windows/tool_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/Platform/Windows/tool_dependencies.cmake @@ -15,5 +15,7 @@ set(GEM_DEPENDENCIES Gem::Atom_RHI_Vulkan.Builders Gem::Atom_RHI_DX12.Private Gem::Atom_RHI_DX12.Builders + Gem::Atom_RHI_Null.Private + Gem::Atom_RHI_Null.Builders Gem::Atom_RHI_Metal.Builders ) \ No newline at end of file diff --git a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake index d281f64954..c8e66740e4 100644 --- a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake @@ -42,7 +42,6 @@ set(GEM_DEPENDENCIES Gem::SurfaceData Gem::GradientSignal Gem::Vegetation - Gem::Atom_RHI.Private Gem::Atom_RPI.Private Gem::Atom_Feature_Common @@ -54,4 +53,5 @@ set(GEM_DEPENDENCIES Gem::ImguiAtom Gem::Atom_AtomBridge Gem::AtomFont + Gem::Blast ) diff --git a/AutomatedTesting/Gem/Code/tool_dependencies.cmake b/AutomatedTesting/Gem/Code/tool_dependencies.cmake index 22132da686..8c5da63f42 100644 --- a/AutomatedTesting/Gem/Code/tool_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/tool_dependencies.cmake @@ -68,4 +68,5 @@ set(GEM_DEPENDENCIES Gem::ImguiAtom Gem::AtomFont Gem::AtomToolsFramework.Editor + Gem::Blast.Editor ) diff --git a/AutomatedTesting/Gem/PythonTests/Blast/TestSuite_Active.py b/AutomatedTesting/Gem/PythonTests/Blast/TestSuite_Active.py index 947ea8363d..066a55c78c 100755 --- a/AutomatedTesting/Gem/PythonTests/Blast/TestSuite_Active.py +++ b/AutomatedTesting/Gem/PythonTests/Blast/TestSuite_Active.py @@ -27,28 +27,28 @@ from base import TestAutomationBase class TestAutomation(TestAutomationBase): def test_ActorSplitsAfterCollision(self, request, workspace, editor, launcher_platform): from . import ActorSplitsAfterCollision as test_module - self._run_test(request, workspace, editor, test_module, expected_lines=[], unexpected_lines=["Assert"]) + self._run_test(request, workspace, editor, test_module) def test_ActorSplitsAfterRadialDamage(self, request, workspace, editor, launcher_platform): from . import ActorSplitsAfterRadialDamage as test_module - self._run_test(request, workspace, editor, test_module, expected_lines=[], unexpected_lines=["Assert"]) + self._run_test(request, workspace, editor, test_module) def test_ActorSplitsAfterCapsuleDamage(self, request, workspace, editor, launcher_platform): from . import ActorSplitsAfterCapsuleDamage as test_module - self._run_test(request, workspace, editor, test_module, expected_lines=[], unexpected_lines=["Assert"]) + self._run_test(request, workspace, editor, test_module) def test_ActorSplitsAfterImpactSpreadDamage(self, request, workspace, editor, launcher_platform): from . import ActorSplitsAfterImpactSpreadDamage as test_module - self._run_test(request, workspace, editor, test_module, expected_lines=[], unexpected_lines=["Assert"]) + self._run_test(request, workspace, editor, test_module) def test_ActorSplitsAfterShearDamage(self, request, workspace, editor, launcher_platform): from . import ActorSplitsAfterShearDamage as test_module - self._run_test(request, workspace, editor, test_module, expected_lines=[], unexpected_lines=["Assert"]) + self._run_test(request, workspace, editor, test_module) def test_ActorSplitsAfterTriangleDamage(self, request, workspace, editor, launcher_platform): from . import ActorSplitsAfterTriangleDamage as test_module - self._run_test(request, workspace, editor, test_module, expected_lines=[], unexpected_lines=["Assert"]) + self._run_test(request, workspace, editor, test_module) def test_ActorSplitsAfterStressDamage(self, request, workspace, editor, launcher_platform): from . import ActorSplitsAfterStressDamage as test_module - self._run_test(request, workspace, editor, test_module, expected_lines=[], unexpected_lines=["Assert"]) \ No newline at end of file + self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index 3bd5f84312..b7a5f85087 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -135,20 +135,19 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) endif() ## Blast ## -# Disabled until AutomatedTesting runs with Atom. -# if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) -# ly_add_pytest( -# NAME AutomatedTesting::BlastTests -# TEST_SERIAL TRUE -# PATH ${CMAKE_CURRENT_LIST_DIR}/Blast/TestSuite_Active.py -# TIMEOUT 500 -# RUNTIME_DEPENDENCIES -# Legacy::Editor -# Legacy::CryRenderNULL -# AZ::AssetProcessor -# AutomatedTesting.Assets -# ) -# endif() +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_pytest( + NAME AutomatedTesting::BlastTests + TEST_SUITE sandbox + TEST_SERIAL TRUE + PATH ${CMAKE_CURRENT_LIST_DIR}/Blast/TestSuite_Active.py + TIMEOUT 3600 + RUNTIME_DEPENDENCIES + Legacy::Editor + AZ::AssetProcessor + AutomatedTesting.Assets + ) +endif() ############# diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py index 6b4a8bf17c..ccc8c3f101 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py @@ -77,13 +77,13 @@ class TestImageGradient(EditorTestHelper): # 3) Assign the processed gradient signal image as the Image Gradient's image asset and verify success # First, check for the base image in the workspace - base_image = "lumberyard_gsi.png" + base_image = "image_grad_test_gsi.png" base_image_path = os.path.join("AutomatedTesting", "Assets", "ImageGradients", base_image) if os.path.isfile(base_image_path): print(f"{base_image} was found in the workspace") # Next, assign the processed image to the Image Gradient's Image Asset property - processed_image_path = os.path.join("Assets", "ImageGradients", "lumberyard_gsi.gradimage") + processed_image_path = os.path.join("Assets", "ImageGradients", "image_grad_test_gsi.gradimage") asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", processed_image_path, math.Uuid(), False) hydra.get_set_test(image_gradient_entity, 0, "Configuration|Image Asset", asset_id) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/test_ImageGradient.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/test_ImageGradient.py index 11712f8b65..8fc582c2c8 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/test_ImageGradient.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/test_ImageGradient.py @@ -57,7 +57,7 @@ class TestImageGradientRequiresShape(object): "Entity has a Image Gradient component", "Entity has a Gradient Transform Modifier component", "Entity has a Box Shape component", - "lumberyard_gsi.png was found in the workspace", + "image_grad_test_gsi.png was found in the workspace", "Entity Configuration|Image Asset: SUCCESS", "ImageGradient_ProcessedImageAssignedSucessfully: result=SUCCESS", ] diff --git a/AutomatedTesting/default.blastconfiguration b/AutomatedTesting/default.blastconfiguration index 96a23ecbb8..6318a5002f 100644 --- a/AutomatedTesting/default.blastconfiguration +++ b/AutomatedTesting/default.blastconfiguration @@ -1,6 +1,6 @@ - + diff --git a/CMakeLists.txt b/CMakeLists.txt index c09cfc9588..6da92f9c2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,15 +23,11 @@ endif() include(cmake/Version.cmake) -set(INSTALLED_ENGINE TRUE) - if(NOT PROJECT_NAME) project(O3DE LANGUAGES C CXX VERSION ${LY_VERSION_STRING} ) - - set(INSTALLED_ENGINE FALSE) endif() include(cmake/Initialize.cmake) diff --git a/Code/CryEngine/CryCommon/IMovieSystem.h b/Code/CryEngine/CryCommon/IMovieSystem.h index 654ccfd240..7a1125ae2b 100644 --- a/Code/CryEngine/CryCommon/IMovieSystem.h +++ b/Code/CryEngine/CryCommon/IMovieSystem.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -914,9 +915,18 @@ struct IAnimStringTable */ struct IAnimSequence { - AZ_RTTI(IAnimSequence, "{A60F95F5-5A4A-47DB-B3BB-525BBC0BC8DB}") + AZ_RTTI(IAnimSequence, "{A60F95F5-5A4A-47DB-B3BB-525BBC0BC8DB}"); + AZ_CLASS_ALLOCATOR(IAnimSequence, AZ::SystemAllocator, 0); - static const int kSequenceVersion = 4; + static const int kSequenceVersion = 5; + + static void Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context); serializeContext != nullptr) + { + serializeContext->Class(); + } + } //! Flags used for SetFlags(),GetFlags(),SetParentFlags(),GetParentFlags() methods. enum EAnimSequenceFlags diff --git a/Code/CryEngine/RenderDll/Common/PerInstanceConstantBufferPool.cpp b/Code/CryEngine/RenderDll/Common/PerInstanceConstantBufferPool.cpp index 7fa69f6026..f2475c0b48 100644 --- a/Code/CryEngine/RenderDll/Common/PerInstanceConstantBufferPool.cpp +++ b/Code/CryEngine/RenderDll/Common/PerInstanceConstantBufferPool.cpp @@ -274,6 +274,10 @@ void PerInstanceConstantBufferPool::SetConstantBuffer(SRendItem* renderItem) deviceManager.BindConstantBuffer(eHWSC_Vertex, m_PooledIndirectConstantBuffer[indirectId], eConstantBufferShaderSlot_SPIIndex); deviceManager.BindConstantBuffer(eHWSC_Pixel, m_PooledIndirectConstantBuffer[indirectId], eConstantBufferShaderSlot_SPIIndex); #else + AZ::u32 itemIndex = directId % SPI_NUM_INSTS_PER_CB; + AZ::u32 first[1] = {itemIndex * static_cast(sizeof(HLSL_PerInstanceConstantBuffer))}; + AZ::u32 count[1] = {static_cast(sizeof(HLSL_PerInstanceConstantBuffer))}; + deviceManager.BindConstantBuffer(eHWSC_Vertex, m_PooledConstantBuffer[bufferIndex], eConstantBufferShaderSlot_SPI, first[0], count[0]); deviceManager.BindConstantBuffer(eHWSC_Pixel, m_PooledConstantBuffer[bufferIndex], eConstantBufferShaderSlot_SPI, first[0], count[0]); #endif diff --git a/Code/CryEngine/RenderDll/Common/RenderMesh.cpp b/Code/CryEngine/RenderDll/Common/RenderMesh.cpp index 10bbfbf0e6..357731fc6d 100644 --- a/Code/CryEngine/RenderDll/Common/RenderMesh.cpp +++ b/Code/CryEngine/RenderDll/Common/RenderMesh.cpp @@ -755,6 +755,7 @@ lSysUpdate: buffer_handle_t nVB = ~0u; # if BUFFER_ENABLE_DIRECT_ACCESS && !defined(NULL_RENDERER) nVB = MS->m_nID; + int nFrame = gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nFillThreadID].m_nFrameUpdateID; if ((nVB != ~0u && (MS->m_nFrameCreate != nFrame || MS->m_nElements != m_nVerts)) || !CRenderer::CV_r_buffer_enable_lockless_updates) # endif goto lSysCreate; diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index b8d3e12712..a3af2648cf 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -178,14 +178,16 @@ namespace AZ //! on an update to '/Amazon/AzCore/Bootstrap/project_path' key. struct UpdateProjectSettingsEventHandler { - UpdateProjectSettingsEventHandler(AZ::SettingsRegistryInterface& registry) + UpdateProjectSettingsEventHandler(AZ::SettingsRegistryInterface& registry, AZ::CommandLine& commandLine) : m_registry{ registry } + , m_commandLine{ commandLine } { } void operator()(AZStd::string_view path, AZ::SettingsRegistryInterface::Type) { using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + // #1 Update the project settings when the project path is set const auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; AZ::IO::FixedMaxPath newProjectPath; if (SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual(projectPathKey, path) @@ -194,6 +196,7 @@ namespace AZ UpdateProjectSettingsFromProjectPath(AZ::IO::PathView(newProjectPath)); } + // #2 Update the project specialization when the project name is set const auto projectNameKey = FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) + "/project_name"; FixedValueString newProjectName; if (SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual(projectNameKey, path) @@ -201,6 +204,12 @@ namespace AZ { UpdateProjectSpecializationFromProjectName(newProjectName); } + + // #3 Update the ComponentApplication CommandLine instance when the command line settings are merged into the Settings Registry + if (path == AZ::SettingsRegistryMergeUtils::CommandLineValueChangedKey) + { + UpdateCommandLine(); + } } //! Add the project name as a specialization underneath the /Amazon/AzCore/Settings/Specializations path @@ -233,10 +242,16 @@ namespace AZ AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); } + void UpdateCommandLine() + { + AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(m_registry, m_commandLine); + } + private: AZ::IO::FixedMaxPath m_oldProjectPath; AZ::SettingsRegistryInterface::FixedValueString m_oldProjectName; AZ::SettingsRegistryInterface& m_registry; + AZ::CommandLine& m_commandLine; }; void ComponentApplication::Descriptor::AllocatorRemapping::Reflect(ReflectContext* context, ComponentApplication* app) @@ -415,6 +430,12 @@ namespace AZ // Add the Command Line arguments into the SettingsRegistry SettingsRegistryMergeUtils::StoreCommandLineToRegistry(*m_settingsRegistry, m_commandLine); + // Add a notifier to update the project_settings when + // 1. The 'project_path' key changes + // 2. The project specialization when the 'project-name' key changes + // 3. The ComponentApplication command line when the command line is stored to the registry + m_projectChangedHandler = m_settingsRegistry->RegisterNotifier(UpdateProjectSettingsEventHandler{ *m_settingsRegistry, m_commandLine }); + // Merge Command Line arguments constexpr bool executeRegDumpCommands = false; SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands); @@ -429,10 +450,6 @@ namespace AZ // for the application root. CalculateAppRoot(); - // Add a notifier to update the /Amazon/AzCore/Settings/Specializations - // when the 'project_path' property changes within the SettingsRegistry - m_projectChangedHandler = m_settingsRegistry->RegisterNotifier(UpdateProjectSettingsEventHandler{ *m_settingsRegistry }); - // Merge the bootstrap.cfg file into the Settings Registry as soon as the OSAllocator has been created. SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(*m_settingsRegistry); SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {}); @@ -909,6 +926,8 @@ namespace AZ SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer); SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true); #endif + // Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry); } void ComponentApplication::SetSettingsRegistrySpecializations(SettingsRegistryInterface::Specializations& specializations) diff --git a/Code/Framework/AzCore/AzCore/Component/NonUniformScaleBus.h b/Code/Framework/AzCore/AzCore/Component/NonUniformScaleBus.h index af2fe20a50..9b52fc1794 100644 --- a/Code/Framework/AzCore/AzCore/Component/NonUniformScaleBus.h +++ b/Code/Framework/AzCore/AzCore/Component/NonUniformScaleBus.h @@ -19,9 +19,6 @@ namespace AZ { class Vector3; - //! Do not allow the scale to be zero to avoid problems with inverting scale. - static constexpr float MinNonUniformScale = 1e-3f; - using NonUniformScaleChangedEvent = AZ::Event; //! Requests for working with non-uniform scale. diff --git a/Code/Framework/AzCore/AzCore/Component/TransformBus.h b/Code/Framework/AzCore/AzCore/Component/TransformBus.h index b0e617f220..2003b949e2 100644 --- a/Code/Framework/AzCore/AzCore/Component/TransformBus.h +++ b/Code/Framework/AzCore/AzCore/Component/TransformBus.h @@ -26,7 +26,7 @@ namespace AZ { class Transform; - using TransformChangedEvent = Event; + using TransformChangedEvent = Event; using ParentChangedEvent = Event; diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.h b/Code/Framework/AzCore/AzCore/Math/Transform.h index 6c96a9a6a6..eb1a12a912 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.h +++ b/Code/Framework/AzCore/AzCore/Math/Transform.h @@ -38,6 +38,13 @@ namespace AZ bool CompareValueData(const void* lhs, const void* rhs) override; }; + //! Limits for transform scale values. + //! The scale should not be zero to avoid problems with inverting. + //! @{ + static constexpr float MinTransformScale = 1e-2f; + static constexpr float MaxTransformScale = 1e9f; + //! @} + //! The basic transformation class, represented using a quaternion rotation, vector scale and vector translation. //! By design, cannot represent skew transformations. class Transform diff --git a/Code/Framework/AzCore/AzCore/Math/Vector2.cpp b/Code/Framework/AzCore/AzCore/Math/Vector2.cpp index bc5f6ecdad..c2a3a934b0 100644 --- a/Code/Framework/AzCore/AzCore/Math/Vector2.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Vector2.cpp @@ -167,14 +167,14 @@ namespace AZ } } - AZ_MATH_INLINE Vector2::Vector2(const Vector3& source) + Vector2::Vector2(const Vector3& source) : m_x(source.GetX()) , m_y(source.GetY()) { } - AZ_MATH_INLINE Vector2::Vector2(const Vector4& source) + Vector2::Vector2(const Vector4& source) : m_x(source.GetX()) , m_y(source.GetY()) { diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp new file mode 100644 index 0000000000..63aad1ecf4 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp @@ -0,0 +1,340 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +#include + +namespace AZ +{ + inline namespace PlatformDefaults + { + static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient }; + + const char* PlatformIdToPalFolder(AZ::PlatformId platform) + { +#ifdef IOS +#define AZ_REDEFINE_IOS_AT_END IOS +#undef IOS +#endif + switch (platform) + { + case AZ::PC: + return "PC"; + case AZ::ES3: + return "Android"; + case AZ::IOS: + return "iOS"; + case AZ::OSX: + return "Mac"; + case AZ::PROVO: + return "Provo"; + case AZ::SALEM: + return "Salem"; + case AZ::JASPER: + return "Jasper"; + case AZ::SERVER: + return "Server"; + case AZ::ALL: + case AZ::ALL_CLIENT: + case AZ::NumPlatformIds: + case AZ::Invalid: + default: + return ""; + } + +#ifdef AZ_REDEFINE_IOS_AT_END +#define IOS AZ_REDEFINE_IOS_AT_END +#endif + } + + const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform) + { + if (osPlatform == PlatformCodeNameWindows || osPlatform == PlatformCodeNameLinux) + { + return PlatformPC; + } + else if (osPlatform == PlatformCodeNameMac) + { + return PlatformOSX; + } + else if (osPlatform == PlatformCodeNameAndroid) + { + return PlatformES3; + } + else if (osPlatform == PlatformCodeNameiOS) + { + return PlatformIOS; + } + else if (osPlatform == PlatformCodeNameProvo) + { + return PlatformProvo; + } + else if (osPlatform == PlatformCodeNameSalem) + { + return PlatformSalem; + } + else if (osPlatform == PlatformCodeNameJasper) + { + return PlatformJasper; + } + + AZ_Error("PlatformDefault", false, R"(Supplied OS platform "%.*s" does not have a corresponding default asset platform)", + aznumeric_cast(osPlatform.size()), osPlatform.data()); + return ""; + } + + PlatformFlags PlatformHelper::GetPlatformFlagFromPlatformIndex(PlatformId platformIndex) + { + if (platformIndex < 0 || platformIndex > PlatformId::NumPlatformIds) + { + return PlatformFlags::Platform_NONE; + } + if (platformIndex == PlatformId::ALL) + { + return PlatformFlags::Platform_ALL; + } + if (platformIndex == PlatformId::ALL_CLIENT) + { + return PlatformFlags::Platform_ALL_CLIENT; + } + return static_cast(1 << platformIndex); + } + + AZStd::fixed_vector PlatformHelper::GetPlatforms(PlatformFlags platformFlags) + { + AZStd::fixed_vector platforms; + for (int platformNum = 0; platformNum < PlatformId::NumPlatformIds; ++platformNum) + { + const bool isAllPlatforms = PlatformId::ALL == static_cast(platformNum) + && ((platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE); + + const bool isAllClientPlatforms = PlatformId::ALL_CLIENT == static_cast(platformNum) + && ((platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE); + + if (isAllPlatforms || isAllClientPlatforms + || (platformFlags & static_cast(1 << platformNum)) != PlatformFlags::Platform_NONE) + { + platforms.push_back(PlatformNames[platformNum]); + } + } + + return platforms; + } + + AZStd::fixed_vector PlatformHelper::GetPlatformsInterpreted(PlatformFlags platformFlags) + { + return GetPlatforms(GetPlatformFlagsInterpreted(platformFlags)); + } + + AZStd::fixed_vector PlatformHelper::GetPlatformIndices(PlatformFlags platformFlags) + { + AZStd::fixed_vector platformIndices; + for (int i = 0; i < PlatformId::NumPlatformIds; i++) + { + PlatformId index = static_cast(i); + if ((GetPlatformFlagFromPlatformIndex(index) & platformFlags) != PlatformFlags::Platform_NONE) + { + platformIndices.emplace_back(index); + } + } + return platformIndices; + } + + AZStd::fixed_vector PlatformHelper::GetPlatformIndicesInterpreted(PlatformFlags platformFlags) + { + return GetPlatformIndices(GetPlatformFlagsInterpreted(platformFlags)); + } + + PlatformFlags PlatformHelper::GetPlatformFlag(AZStd::string_view platform) + { + int platformIndex = GetPlatformIndexFromName(platform); + if (platformIndex == PlatformId::Invalid) + { + AZ_Error("PlatformDefault", false, "Invalid Platform ( %.*s ).\n", static_cast(platform.length()), platform.data()); + return PlatformFlags::Platform_NONE; + } + + if (platformIndex == PlatformId::ALL) + { + return PlatformFlags::Platform_ALL; + } + + if (platformIndex == PlatformId::ALL_CLIENT) + { + return PlatformFlags::Platform_ALL_CLIENT; + } + + return static_cast(1 << platformIndex); + } + + const char* PlatformHelper::GetPlatformName(PlatformId platform) + { + if (platform < 0 || platform > PlatformId::NumPlatformIds) + { + return "invalid"; + } + return PlatformNames[platform]; + } + + void PlatformHelper::AppendPlatformCodeNames(AZStd::fixed_vector& platformCodes, AZStd::string_view platformId) + { + PlatformId platform = GetPlatformIdFromName(platformId); + AZ_Assert(platform != PlatformId::Invalid, "Unsupported Platform ID: %.*s", static_cast(platformId.length()), platformId.data()); + AppendPlatformCodeNames(platformCodes, platform); + } + + void PlatformHelper::AppendPlatformCodeNames(AZStd::fixed_vector& platformCodes, PlatformId platformId) + { + // The IOS SDK has a macro that defines IOS as 1 which causes the enum below to be incorrectly converted to "PlatformId::1". +#pragma push_macro("IOS") +#undef IOS + // To reduce work the Asset Processor groups assets that can be shared between hardware platforms together. For this + // reason "PC" can for instance cover both the Windows and Linux platforms and "IOS" can cover AppleTV and iOS. + switch (platformId) + { + case PlatformId::PC: + platformCodes.emplace_back(PlatformCodeNameWindows); + platformCodes.emplace_back(PlatformCodeNameLinux); + break; + case PlatformId::ES3: + platformCodes.emplace_back(PlatformCodeNameAndroid); + break; + case PlatformId::IOS: + platformCodes.emplace_back(PlatformCodeNameiOS); + break; + case PlatformId::OSX: + platformCodes.emplace_back(PlatformCodeNameMac); + break; + case PlatformId::PROVO: + platformCodes.emplace_back(PlatformCodeNameProvo); + break; + case PlatformId::SALEM: + platformCodes.emplace_back(PlatformCodeNameSalem); + break; + case PlatformId::JASPER: + platformCodes.emplace_back(PlatformCodeNameJasper); + break; + case PlatformId::SERVER: + // Server is not a hardware platform + break; + default: + AZ_Assert(false, "Unsupported Platform ID: %i", platformId); + break; + } +#pragma pop_macro("IOS") + } + + int PlatformHelper::GetPlatformIndexFromName(AZStd::string_view platformName) + { + for (int idx = 0; idx < PlatformId::NumPlatformIds; idx++) + { + if (platformName == PlatformNames[idx]) + { + return idx; + } + } + + return PlatformId::Invalid; + } + + PlatformId PlatformHelper::GetPlatformIdFromName(AZStd::string_view platformName) + { + return aznumeric_caster(GetPlatformIndexFromName(platformName)); + } + + AssetPlatformCombinedString PlatformHelper::GetCommaSeparatedPlatformList(PlatformFlags platformFlags) + { + AZStd::fixed_vector platformNames = GetPlatforms(platformFlags); + AssetPlatformCombinedString platformsString; + AZ::StringFunc::Join(platformsString, platformNames.begin(), platformNames.end(), ", "); + return platformsString; + } + + PlatformFlags PlatformHelper::GetPlatformFlagsInterpreted(PlatformFlags platformFlags) + { + PlatformFlags returnFlags = PlatformFlags::Platform_NONE; + + if ((platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE) + { + for (int i = 0; i < NumPlatforms; ++i) + { + auto platformId = static_cast(i); + + if (platformId != PlatformId::ALL && platformId != PlatformId::ALL_CLIENT) + { + returnFlags |= GetPlatformFlagFromPlatformIndex(platformId); + } + } + } + else if ((platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE) + { + for (int i = 0; i < NumPlatforms; ++i) + { + auto platformId = static_cast(i); + + if (platformId != PlatformId::ALL && platformId != PlatformId::ALL_CLIENT && platformId != PlatformId::SERVER) + { + returnFlags |= GetPlatformFlagFromPlatformIndex(platformId); + } + } + } + else + { + returnFlags = platformFlags; + } + + return returnFlags; + } + + bool PlatformHelper::IsSpecialPlatform(PlatformFlags platformFlags) + { + return (platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE + || (platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE; + } + + bool HasFlagHelper(PlatformFlags flags, PlatformFlags checkPlatform) + { + return (flags & checkPlatform) == checkPlatform; + } + + + bool PlatformHelper::HasPlatformFlag(PlatformFlags flags, PlatformId checkPlatform) + { + // If checkPlatform contains any kind of invalid id, just exit out here + if (checkPlatform == PlatformId::Invalid || checkPlatform == NumPlatforms) + { + return false; + } + + // ALL_CLIENT + SERVER = ALL + if (HasFlagHelper(flags, PlatformFlags::Platform_ALL_CLIENT | PlatformFlags::Platform_SERVER)) + { + flags = PlatformFlags::Platform_ALL; + } + + if (HasFlagHelper(flags, PlatformFlags::Platform_ALL)) + { + // It doesn't matter what checkPlatform is set to in this case, just return true + return true; + } + + if (HasFlagHelper(flags, PlatformFlags::Platform_ALL_CLIENT)) + { + return checkPlatform != PlatformId::SERVER; + } + + return HasFlagHelper(flags, GetPlatformFlagFromPlatformIndex(checkPlatform)); + } + } +} diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h new file mode 100644 index 0000000000..2d67c860cd --- /dev/null +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h @@ -0,0 +1,157 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +// On IOS builds IOS will be defined and interfere with the below enums +#pragma push_macro("IOS") +#undef IOS + +namespace AZ +{ + inline namespace PlatformDefaults + { + constexpr char PlatformPC[] = "pc"; + constexpr char PlatformES3[] = "es3"; + constexpr char PlatformIOS[] = "ios"; + constexpr char PlatformOSX[] = "osx_gl"; + constexpr char PlatformProvo[] = "provo"; + constexpr char PlatformSalem[] = "salem"; + constexpr char PlatformJasper[] = "jasper"; + constexpr char PlatformServer[] = "server"; + + constexpr char PlatformCodeNameWindows[] = "Windows"; + constexpr char PlatformCodeNameLinux[] = "Linux"; + constexpr char PlatformCodeNameAndroid[] = "Android"; + constexpr char PlatformCodeNameiOS[] = "iOS"; + constexpr char PlatformCodeNameMac[] = "Mac"; + constexpr char PlatformCodeNameProvo[] = "Provo"; + constexpr char PlatformCodeNameSalem[] = "Salem"; + constexpr char PlatformCodeNameJasper[] = "Jasper"; + constexpr char PlatformAll[] = "all"; + constexpr char PlatformAllClient[] = "all_client"; + + // Used for the capacity of a fixed vector to store the code names of platforms + // The value needs to be higher than the number of unique OS platforms that are supported(at this time 8) + constexpr size_t MaxPlatformCodeNames = 16; + + //! This platform enum have platform values in sequence and can also be used to get the platform count. + AZ_ENUM_WITH_UNDERLYING_TYPE(PlatformId, int, + (Invalid, -1), + PC, + ES3, + IOS, + OSX, + PROVO, + SALEM, + JASPER, + SERVER, // Corresponds to the customer's flavor of "server" which could be windows, ubuntu, etc + ALL, + ALL_CLIENT, + + // Add new platforms above this + NumPlatformIds + ); + constexpr int NumClientPlatforms = 7; + constexpr int NumPlatforms = NumClientPlatforms + 1; // 1 "Server" platform currently + enum class PlatformFlags : AZ::u32 + { + Platform_NONE = 0x00, + Platform_PC = 1 << PlatformId::PC, + Platform_ES3 = 1 << PlatformId::ES3, + Platform_IOS = 1 << PlatformId::IOS, + Platform_OSX = 1 << PlatformId::OSX, + Platform_PROVO = 1 << PlatformId::PROVO, + Platform_SALEM = 1 << PlatformId::SALEM, + Platform_JASPER = 1 << PlatformId::JASPER, + Platform_SERVER = 1 << PlatformId::SERVER, + + // A special platform that will always correspond to all platforms, even if new ones are added + Platform_ALL = 1ULL << 30, + + // A special platform that will always correspond to all non-server platforms, even if new ones are added + Platform_ALL_CLIENT = 1ULL << 31, + + AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER, + }; + + AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags); + + // 32 characters should be more than enough to store a platform name + using AssetPlatformFixedString = AZStd::fixed_string<32>; + // Fixed string which can store a comma separated list of platforms names + // Additional byte is added to take into account the comma + using AssetPlatformCombinedString = AZStd::fixed_string < (AssetPlatformFixedString{}.max_size() + 1)* PlatformId::NumPlatformIds > ; + + const char* PlatformIdToPalFolder(PlatformId platform); + + const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform); + + //! Platform Helper is an utility class that can be used to retrieve platform related information + class PlatformHelper + { + public: + + //! Given a platformIndex returns the platform name + static const char* GetPlatformName(PlatformId platform); + + //! Converts the platform name to the platform code names as defined in AZ_TRAIT_OS_PLATFORM_CODENAME. + static void AppendPlatformCodeNames(AZStd::fixed_vector& platformCodes, AZStd::string_view platformName); + + //! Converts the platform name to the platform code names as defined in AZ_TRAIT_OS_PLATFORM_CODENAME. + static void AppendPlatformCodeNames(AZStd::fixed_vector& platformCodes, PlatformId platformId); + + //! Given a platform name returns a platform index. + //! If the platform is not found, the method returns -1. + static int GetPlatformIndexFromName(AZStd::string_view platformName); + + //! Given a platform name returns a platform id. + //! If the platform is not found, the method returns -1. + static PlatformId GetPlatformIdFromName(AZStd::string_view platformName); + + //! Given a platformIndex returns the platformFlags + static PlatformFlags GetPlatformFlagFromPlatformIndex(PlatformId platform); + + //! Given a platformFlags returns all the platform identifiers that are set. + static AZStd::fixed_vector GetPlatforms(PlatformFlags platformFlags); + //! Given a platformFlags returns all the platform identifiers that are set, with special flags interpreted. Do not use the result for saving + static AZStd::fixed_vector GetPlatformsInterpreted(PlatformFlags platformFlags); + + //! Given a platformFlags return a list of PlatformId indices + static AZStd::fixed_vector GetPlatformIndices(PlatformFlags platformFlags); + //! Given a platformFlags return a list of PlatformId indices, with special flags interpreted. Do not use the result for saving + static AZStd::fixed_vector GetPlatformIndicesInterpreted(PlatformFlags platformFlags); + + //! Given a platform identifier returns its corresponding platform flag. + static PlatformFlags GetPlatformFlag(AZStd::string_view platform); + + //! Given any platformFlags returns a string listing the input platforms + static AssetPlatformCombinedString GetCommaSeparatedPlatformList(PlatformFlags platformFlags); + + //! If platformFlags contains any special flags, they are removed and replaced with the normal flags they represent + static PlatformFlags GetPlatformFlagsInterpreted(PlatformFlags platformFlags); + + //! Returns true if platformFlags contains any special flags + static bool IsSpecialPlatform(PlatformFlags platformFlags); + + //! Returns true if platformFlags has checkPlatform flag set. + static bool HasPlatformFlag(PlatformFlags platformFlags, PlatformId checkPlatform); + }; + } +} +#pragma pop_macro("IOS") diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.cpp new file mode 100644 index 0000000000..6077a66682 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.cpp @@ -0,0 +1,100 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "ByteStreamSerializer.h" + +#include +#include +#include +#include + +namespace AZ +{ + namespace ByteSerializerInternal + { + static JsonSerializationResult::Result Load(void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + using JsonSerializationResult::Outcomes; + using JsonSerializationResult::Tasks; + + AZ_Assert(outputValue, "Expected a valid pointer to load from json value."); + + switch (inputValue.GetType()) + { + case rapidjson::kStringType: { + JsonByteStream buffer; + if (AZ::StringFunc::Base64::Decode(buffer, inputValue.GetString(), inputValue.GetStringLength())) + { + JsonByteStream* valAsByteStream = static_cast(outputValue); + *valAsByteStream = AZStd::move(buffer); + return context.Report(Tasks::ReadField, Outcomes::Success, "Successfully read ByteStream."); + } + return context.Report(Tasks::ReadField, Outcomes::Invalid, "Decode of Base64 encoded ByteStream failed."); + } + case rapidjson::kArrayType: + case rapidjson::kObjectType: + case rapidjson::kNullType: + case rapidjson::kFalseType: + case rapidjson::kTrueType: + case rapidjson::kNumberType: + return context.Report( + Tasks::ReadField, Outcomes::Unsupported, + "Unsupported type. ByteStream values cannot be read from arrays, objects, nulls, booleans or numbers."); + default: + return context.Report(Tasks::ReadField, Outcomes::Unknown, "Unknown json type encountered for ByteStream value."); + } + } + + static JsonSerializationResult::Result StoreWithDefault( + rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, JsonSerializerContext& context) + { + using JsonSerializationResult::Outcomes; + using JsonSerializationResult::Tasks; + + const JsonByteStream& valAsByteStream = *static_cast(inputValue); + if (context.ShouldKeepDefaults() || !defaultValue || (valAsByteStream != *static_cast(defaultValue))) + { + const auto base64ByteStream = AZ::StringFunc::Base64::Encode(valAsByteStream.data(), valAsByteStream.size()); + outputValue.SetString(base64ByteStream.c_str(), base64ByteStream.size(), context.GetJsonAllocator()); + return context.Report(Tasks::WriteValue, Outcomes::Success, "ByteStream successfully stored."); + } + + return context.Report(Tasks::WriteValue, Outcomes::DefaultsUsed, "Default ByteStream used."); + } + } // namespace ByteSerializerInternal + + AZ_CLASS_ALLOCATOR_IMPL(JsonByteStreamSerializer, SystemAllocator, 0); + + JsonSerializationResult::Result JsonByteStreamSerializer::Load( + void* outputValue, [[maybe_unused]] const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) + { + AZ_Assert( + azrtti_typeid() == outputValueTypeId, + "Unable to deserialize AZStd::vector> to json because the provided type is %s", + outputValueTypeId.ToString().c_str()); + + return ByteSerializerInternal::Load(outputValue, inputValue, context); + } + + JsonSerializationResult::Result JsonByteStreamSerializer::Store( + rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, [[maybe_unused]] const Uuid& valueTypeId, + JsonSerializerContext& context) + { + AZ_Assert( + azrtti_typeid() == valueTypeId, + "Unable to serialize AZStd::vector to json because the provided type is %s", + valueTypeId.ToString().c_str()); + + return ByteSerializerInternal::StoreWithDefault(outputValue, inputValue, defaultValue, context); + } +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.h new file mode 100644 index 0000000000..4f01e28319 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.h @@ -0,0 +1,38 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace AZ +{ + using JsonByteStream = AZStd::vector; //!< Alias for AZStd::vector. + + //! Serialize a stream of bytes (usually binary data) as a json string value. + //! @note Related to GenericClassByteStream (part of SerializeGenericTypeInfo> - see AZStdContainers.inl for more + //! details). + class JsonByteStreamSerializer : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonByteStreamSerializer, "{30F0EA5A-CD13-4BA7-BAE1-D50D851CAC45}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + JsonSerializationResult::Result Load( + void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + JsonSerializationResult::Result Store( + rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId, + JsonSerializerContext& context) override; + }; +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp index e33466d8dc..6e3c8cec60 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,8 @@ namespace AZ jsonContext->Serializer()->HandlesType(); jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer() ->HandlesType() ->HandlesType() diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 392e95bf6e..52085757fe 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -132,23 +133,14 @@ namespace AZ::Internal AZ::IO::FixedMaxPath ScanUpRootLocator(AZStd::string_view rootFileToLocate) { - - AZStd::fixed_string executableDir; - if (AZ::Utils::GetExecutableDirectory(executableDir.data(), executableDir.capacity()) == Utils::ExecutablePathResult::Success) - { - // Update the size value of the executable directory fixed string to correctly be the length of the null-terminated string - // stored within it - executableDir.resize_no_construct(AZStd::char_traits::length(executableDir.data())); - } - - AZ::IO::FixedMaxPath engineRootCandidate{ executableDir }; + AZ::IO::FixedMaxPath rootCandidate{ AZ::Utils::GetExecutableDirectory() }; bool rootPathVisited = false; do { - if (AZ::IO::SystemFile::Exists((engineRootCandidate / rootFileToLocate).c_str())) + if (AZ::IO::SystemFile::Exists((rootCandidate / rootFileToLocate).c_str())) { - return engineRootCandidate; + return rootCandidate; } // Note for posix filesystems the parent directory of '/' is '/' and for windows @@ -156,38 +148,69 @@ namespace AZ::Internal // Validate that the parent directory isn't itself, that would imply // that it is the filesystem root path - AZ::IO::PathView parentPath = engineRootCandidate.ParentPath(); - rootPathVisited = (engineRootCandidate == parentPath); + AZ::IO::PathView parentPath = rootCandidate.ParentPath(); + rootPathVisited = (rootCandidate == parentPath); // Recurse upwards one directory - engineRootCandidate = AZStd::move(parentPath); + rootCandidate = AZStd::move(parentPath); } while (!rootPathVisited); return {}; } + void InjectSettingToCommandLineFront(AZ::SettingsRegistryInterface& settingsRegistry, + AZStd::string_view path, AZStd::string_view value) + { + AZ::CommandLine commandLine; + AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(settingsRegistry, commandLine); + AZ::CommandLine::ParamContainer paramContainer; + commandLine.Dump(paramContainer); + + auto projectPathOverride = AZStd::string::format(R"(--regset="%.*s=%.*s")", + aznumeric_cast(path.size()), path.data(), aznumeric_cast(value.size()), value.data()); + paramContainer.emplace(paramContainer.begin(), AZStd::move(projectPathOverride)); + commandLine.Parse(paramContainer); + AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine); + } } // namespace AZ::Internal namespace AZ::SettingsRegistryMergeUtils { + constexpr AZStd::string_view InternalScanUpEngineRootKey{ "/O3DE/Settings/Internal/engine_root_scan_up_path" }; + constexpr AZStd::string_view InternalScanUpProjectRootKey{ "/O3DE/Settings/Internal/project_root_scan_up_path" }; + AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry) { AZ::IO::FixedMaxPath engineRoot; - // This is the 'external' engine root key, as in passed from command-line or .setreg files. auto engineRootKey = SettingsRegistryInterface::FixedValueString::format("%s/engine_path", BootstrapSettingsRootKey); - if (settingsRegistry.Get(engineRoot.Native(), engineRootKey); !engineRoot.empty()) + + // Step 1 Run the scan upwards logic once to find the location of the engine.json if it exist + // Once this step is run the {InternalScanUpEngineRootKey} is set in the Settings Registry + // to have this scan logic only run once InternalScanUpEngineRootKey the supplied registry + if (settingsRegistry.GetType(InternalScanUpEngineRootKey) == SettingsRegistryInterface::Type::NoType) { - return engineRoot; + // We can scan up from exe directory to find engine.json, use that for engine root if it exists. + engineRoot = Internal::ScanUpRootLocator("engine.json"); + // Set the {InternalScanUpEngineRootKey} to make sure this code path isn't called again for this settings registry + settingsRegistry.Set(InternalScanUpEngineRootKey, engineRoot.Native()); + if (!engineRoot.empty()) + { + settingsRegistry.Set(engineRootKey, engineRoot.Native()); + // Inject the engine root into the front of the command line settings + Internal::InjectSettingToCommandLineFront(settingsRegistry, engineRootKey, engineRoot.Native()); + return engineRoot; + } } - // We can scan up from exe directory to find engine.json, use that for engine root if it exists. - if (engineRoot = Internal::ScanUpRootLocator("engine.json"); !engineRoot.empty()) + // Step 2 check if the engine_path key has been supplied + if (settingsRegistry.Get(engineRoot.Native(), engineRootKey); !engineRoot.empty()) { - settingsRegistry.Set(engineRootKey, engineRoot.c_str()); return engineRoot; } + // Step 3 locate the project root and attempt to find the engine root using the registered engine + // for the project in the project.json file AZ::IO::FixedMaxPath projectRoot = FindProjectRoot(settingsRegistry); if (projectRoot.empty()) { @@ -207,16 +230,30 @@ namespace AZ::SettingsRegistryMergeUtils AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry) { AZ::IO::FixedMaxPath projectRoot; - // This is the 'external' project root key, as in passed from command-line or .setreg files. - auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey); - if (settingsRegistry.Get(projectRoot.Native(), projectRootKey)) + const auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey); + + // Step 1 Run the scan upwards logic once to find the location of the project.json if it exist + // Once this step is run the {InternalScanUpProjectRootKey} is set in the Settings Registry + // to have this scan logic only run once for the supplied registry + // SettingsRegistryInterface::GetType is used to check if a key is set + if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == SettingsRegistryInterface::Type::NoType) { - return projectRoot; + projectRoot = Internal::ScanUpRootLocator("project.json"); + // Set the {InternalScanUpProjectRootKey} to make sure this code path isn't called again for this settings registry + settingsRegistry.Set(InternalScanUpProjectRootKey, projectRoot.Native()); + if (!projectRoot.empty()) + { + settingsRegistry.Set(projectRootKey, projectRoot.c_str()); + // Inject the project root into the front of the command line settings + Internal::InjectSettingToCommandLineFront(settingsRegistry, projectRootKey, projectRoot.Native()); + return projectRoot; + } } - if (projectRoot = Internal::ScanUpRootLocator("project.json"); !projectRoot.empty()) + // Step 2 Check the project-path key + // This is the project path root key, as in passed from command-line or .setreg files. + if (settingsRegistry.Get(projectRoot.Native(), projectRootKey)) { - settingsRegistry.Set(projectRootKey, projectRoot.c_str()); return projectRoot; } @@ -463,18 +500,6 @@ namespace AZ::SettingsRegistryMergeUtils void MergeSettingsToRegistry_Bootstrap(SettingsRegistryInterface& registry) { ConfigParserSettings parserSettings; - parserSettings.m_commentPrefixFunc = [](AZStd::string_view line) -> AZStd::string_view - { - constexpr AZStd::string_view commentPrefixes[]{ "--", ";","#" }; - for (AZStd::string_view commentPrefix : commentPrefixes) - { - if (size_t commentOffset = line.find(commentPrefix); commentOffset != AZStd::string_view::npos) - { - return line.substr(0, commentOffset); - } - } - return line; - }; parserSettings.m_registryRootPointerPath = BootstrapSettingsRootKey; MergeSettingsToRegistry_ConfigFile(registry, "bootstrap.cfg", parserSettings); } @@ -501,9 +526,10 @@ namespace AZ::SettingsRegistryMergeUtils // and if that's missing just get "assets". constexpr char platformName[] = AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER; - SettingsRegistryInterface::FixedValueString assetPlatform; buffer = AZStd::fixed_string::format("%s/%s_assets", BootstrapSettingsRootKey, platformName); AZStd::string_view assetPlatformKey(buffer); + // Use the platform codename to retrieve the default asset platform value + SettingsRegistryInterface::FixedValueString assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME); if (!registry.Get(assetPlatform, assetPlatformKey)) { buffer = AZStd::fixed_string::format("%s/assets", BootstrapSettingsRootKey); @@ -807,6 +833,11 @@ namespace AZ::SettingsRegistryMergeUtils ++argumentIndex; commandLinePath.resize(commandLineRootSize); } + + // This key is used allow Notification Handlers to know when the command line has been updated within the + // registry. The value itself is meaningless. The JSON path of {CommandLineValueChangedKey} + // being passed to the Notification Event Handler indicates that the command line has be updated + registry.Set(CommandLineValueChangedKey, true); } bool GetCommandLineFromRegistry(SettingsRegistryInterface& registry, AZ::CommandLine& commandLine) @@ -823,10 +854,16 @@ namespace AZ::SettingsRegistryMergeUtils } else if (valueName == "Value" && !value.empty()) { - m_arguments.push_back(value); + // Make sure value types are in quotes in case they start with a command option prefix + m_arguments.push_back(QuoteArgument(value)); } } + AZStd::string QuoteArgument(AZStd::string_view arg) + { + return !arg.empty() ? AZStd::string::format(R"("%.*s")", aznumeric_cast(arg.size()), arg.data()) : AZStd::string{ arg }; + } + // The first parameter is skipped by the ComamndLine::Parse function so initialize // the container with one empty element AZ::CommandLine::ParamContainer m_arguments{ 1 }; diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h index dad6c36d0f..10b3c2f18b 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h @@ -57,6 +57,9 @@ namespace AZ::SettingsRegistryMergeUtils //! Root key for where command line are stored at within the settings registry inline static constexpr char CommandLineRootKey[] = "/Amazon/AzCore/Runtime/CommandLine"; + //! Key set to trigger a notification that the CommandLine has been stored within the settings registry + //! The value of the key has no meaning. Notification Handlers only need to check if the key was supplied + inline static constexpr char CommandLineValueChangedKey[] = "/Amazon/AzCore/Runtime/CommandLineChanged"; //! Root key where raw project settings (project.json) file is merged to settings registry inline static constexpr char ProjectSettingsRootKey[] = "/Amazon/Project/Settings"; @@ -74,6 +77,20 @@ namespace AZ::SettingsRegistryMergeUtils //! If it's still not found, attempt to find the project (by similar means) then reconcile the //! engine root by inspecting project.json and the engine manifest file. AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry); + + //! The algorithm that is used to find the project root is as follows + //! 1. The first time this function is it performs a upward scan for a project.json file from + //! the executable directory and if found stores that path to an internal key. + //! In the same step it injects the path into the front of list of command line parameters + //! using the --regset="{BootstrapSettingsRootKey}/project_path=" value + //! 2. Next the "{BootstrapSettingsRootKey}/project_path" is checked to see if it has a project path set + //! + //! The order in which the project path settings are overridden proceeds in the following order + //! 1. project_path set in the /bootstrap.cfg file + //! 2. project_path set in a *.setreg/*.setregpatch file + //! 3. project_path found by scanning upwards from the executable directory to the project.json path + //! 4. project_path set on the Command line via either --regset="{BootstrapSettingsRootKey}/project_path=" + //! or --project_path= AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry); //! Query the specializations that will be used when loading the Settings Registry. diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 97b86f2432..5357ed66a6 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -505,6 +505,8 @@ set(FILES Serialization/Json/BasicContainerSerializer.cpp Serialization/Json/BoolSerializer.h Serialization/Json/BoolSerializer.cpp + Serialization/Json/ByteStreamSerializer.h + Serialization/Json/ByteStreamSerializer.cpp Serialization/Json/CastingHelpers.h Serialization/Json/DoubleSerializer.h Serialization/Json/DoubleSerializer.cpp @@ -605,6 +607,8 @@ set(FILES Utils/Utils.h Script/lua/lua.h Memory/HeapSchema.cpp + PlatformId/PlatformDefaults.h + PlatformId/PlatformDefaults.cpp PlatformId/PlatformId.h PlatformId/PlatformId.cpp Socket/AzSocket_fwd.h diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/ByteStreamSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/ByteStreamSerializerTests.cpp new file mode 100644 index 0000000000..110466d1a0 --- /dev/null +++ b/Code/Framework/AzCore/Tests/Serialization/Json/ByteStreamSerializerTests.cpp @@ -0,0 +1,59 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +namespace JsonSerializationTests +{ + class ByteStreamSerializerTestDescription : public JsonSerializerConformityTestDescriptor + { + public: + AZStd::shared_ptr CreateSerializer() override + { + return AZStd::make_shared(); + } + + AZStd::shared_ptr CreateDefaultInstance() override + { + return AZStd::make_shared(); + } + + AZStd::shared_ptr CreateFullySetInstance() override + { + // create a JsonByteStream (AZStd::vector) with ten 'a's + return AZStd::make_shared(10, 'a'); + } + + AZStd::string_view GetJsonForFullySetInstance() override + { + // Base64 encoded version of 'aaaaaaaaaa' (see CreateFullySetInstance) + return R"("YWFhYWFhYWFhYQ==")"; + } + + void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override + { + features.EnableJsonType(rapidjson::kStringType); + features.m_supportsPartialInitialization = false; + features.m_supportsInjection = false; + } + + bool AreEqual(const AZ::JsonByteStream& lhs, const AZ::JsonByteStream& rhs) override + { + return lhs == rhs; + } + }; + + using ByteStreamConformityTestTypes = ::testing::Types; + INSTANTIATE_TYPED_TEST_CASE_P(JsonByteStreamSerialzier, JsonSerializerConformityTests, ByteStreamConformityTestTypes); +} // namespace JsonSerializationTests diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index c51cf46a37..2129761bfe 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -98,6 +98,7 @@ set(FILES Serialization/Json/BaseJsonSerializerTests.cpp Serialization/Json/BasicContainerSerializerTests.cpp Serialization/Json/BoolSerializerTests.cpp + Serialization/Json/ByteStreamSerializerTests.cpp Serialization/Json/ColorSerializerTests.cpp Serialization/Json/DoubleSerializerTests.cpp Serialization/Json/IntSerializerTests.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp index 630dbb0322..095d986fa1 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -81,13 +82,13 @@ namespace AzFramework void NonUniformScaleComponent::SetScale(const AZ::Vector3& scale) { - if (scale.GetMinElement() >= AZ::MinNonUniformScale) + if (scale.GetMinElement() >= AZ::MinTransformScale && scale.GetMaxElement() <= AZ::MaxTransformScale) { m_scale = scale; } else { - AZ::Vector3 clampedScale = scale.GetMax(AZ::Vector3(AZ::MinNonUniformScale)); + AZ::Vector3 clampedScale = scale.GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)); AZ_Warning("Non-uniform Scale Component", false, "SetScale value was clamped from %s to %s for entity %s", AZ::ToString(scale).c_str(), AZ::ToString(clampedScale).c_str(), GetEntity()->GetName().c_str()); m_scale = clampedScale; diff --git a/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h b/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h index a5d3e657f5..f84f0276e1 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h @@ -105,8 +105,6 @@ namespace AzFramework virtual bool SetDrawInFrontMode(bool bOn) { (void)bOn; return false; } virtual AZ::u32 GetState() { return 0; } virtual AZ::u32 SetState(AZ::u32 state) { (void)state; return 0; } - virtual AZ::u32 SetStateFlag(AZ::u32 state) { (void)state; return 0; } - virtual AZ::u32 ClearStateFlag(AZ::u32 state) { (void)state; return 0; } virtual void PushMatrix(const AZ::Transform& tm) { (void)tm; } virtual void PopMatrix() {} diff --git a/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h b/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h new file mode 100644 index 0000000000..7c5bcce6d6 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h @@ -0,0 +1,85 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace AzFramework +{ + using FontId = uint32_t; + static constexpr FontId InvalidFontId = 0xffffffffu; + + enum class TextHorizontalAlignment : uint16_t + { + Left, + Right, + Center + }; + + enum class TextVerticalAlignment : uint16_t + { + Top, + Bottom, + Center, + }; + + //! Standard parameters for drawing text on screen + struct TextDrawParameters + { + ViewportId m_drawViewportId = InvalidViewportId; //! Viewport to draw into + AZ::Vector3 m_position; //! world space position for 3d draws, screen space x,y,depth for 2d. + AZ::Color m_color = AZ::Colors::White; //! Color to draw the text + AZ::Vector2 m_scale = AZ::Vector2(1.0f); //! font scale + TextHorizontalAlignment m_hAlign = TextHorizontalAlignment::Left; //! Horizontal text alignment + TextVerticalAlignment m_vAlign = TextVerticalAlignment::Top; //! Vertical text alignment + bool m_monospace = false; //! disable character proportional spacing + bool m_depthTest = false; //! Test character against the depth buffer + bool m_virtual800x600ScreenSize = true; //! Text placement and size are scaled relative to a virtual 800x600 resolution + bool m_scaleWithWindow = false; //! Font gets bigger as the window gets bigger + bool m_multiline = true; //! text respects ascii newline characters + }; + + class FontDrawInterface + { + public: + AZ_RTTI(FontDrawInterface, "{545A7C14-CB3E-4A5B-B435-13EA606708EE}"); + + FontDrawInterface() = default; + virtual ~FontDrawInterface() = default; + + virtual void DrawScreenAlignedText2d( + const TextDrawParameters& params, + const AZStd::string_view& string) = 0; + virtual void DrawScreenAlignedText3d( + const TextDrawParameters& params, + const AZStd::string_view& string) = 0; + }; + + class FontQueryInterface + { + public: + AZ_RTTI(FontQueryInterface, "{4BDD8520-EBC1-4680-B25E-421BDF31750F}"); + + FontQueryInterface() = default; + virtual ~FontQueryInterface() = default; + + FontId GetFontId(const AZStd::string_view& fontName) const {return FontId(AZ::Crc32(fontName));} + virtual FontDrawInterface* GetFontDrawInterface(FontId) const = 0; + virtual FontDrawInterface* GetDefaultFontDrawInterface() const = 0; + + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp index 44c24758f3..f01e42a443 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp @@ -142,6 +142,7 @@ namespace Physics ->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset) ->Field("AssetScale", &PhysicsAssetShapeConfiguration::m_assetScale) ->Field("UseMaterialsFromAsset", &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset) + ->Field("SubdivisionLevel", &PhysicsAssetShapeConfiguration::m_subdivisionLevel) ; if (auto editContext = serializeContext->GetEditContext()) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h index 8afe6851b7..b3d04a10c9 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h @@ -141,6 +141,7 @@ namespace Physics AZ::Data::Asset m_asset{ AZ::Data::AssetLoadBehavior::PreLoad }; AZ::Vector3 m_assetScale = AZ::Vector3::CreateOne(); bool m_useMaterialsFromAsset = true; + AZ::u8 m_subdivisionLevel = 4; ///< The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling. }; class NativeShapeConfiguration : public ShapeConfiguration diff --git a/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h b/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h index c303e14636..717f9e023c 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h @@ -142,13 +142,6 @@ namespace Physics virtual AZStd::shared_ptr CreateShape(const ColliderConfiguration& colliderConfiguration, const ShapeConfiguration& configuration) = 0; - /// Adds an appropriate collider component to the entity based on the provided shape configuration. - /// @param entity Entity where the component should be added to. - /// @param colliderConfiguration Configuration of the collider. - /// @param shapeConfiguration Configuration of the shape of the collider. - /// @param addEditorComponents Tells whether to add the Editor version of the collider component or the Game one. - virtual void AddColliderComponentToEntity(AZ::Entity* entity, const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& shapeConfiguration, bool addEditorComponents = false) = 0; - /// Releases the mesh object created by the physics backend. /// @param nativeMeshObject Pointer to the mesh object. virtual void ReleaseNativeMeshObject(void* nativeMeshObject) = 0; diff --git a/Code/Framework/AzFramework/AzFramework/Platform/PlatformDefaults.cpp b/Code/Framework/AzFramework/AzFramework/Platform/PlatformDefaults.cpp deleted file mode 100644 index 81154bae35..0000000000 --- a/Code/Framework/AzFramework/AzFramework/Platform/PlatformDefaults.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include -#include - -#include - -namespace AzFramework -{ - static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient }; - - const char* PlatformIdToPalFolder(AzFramework::PlatformId platform) - { -#ifdef IOS -#define AZ_REDEFINE_IOS_AT_END IOS -#undef IOS -#endif - switch (platform) - { - case AzFramework::PC: - return "PC"; - case AzFramework::ES3: - return "Android"; - case AzFramework::IOS: - return "iOS"; - case AzFramework::OSX: - return "Mac"; - case AzFramework::PROVO: - return "Provo"; - case AzFramework::SALEM: - return "Salem"; - case AzFramework::JASPER: - return "Jasper"; - case AzFramework::SERVER: - return "Server"; - case AzFramework::ALL: - case AzFramework::ALL_CLIENT: - case AzFramework::NumPlatformIds: - case AzFramework::Invalid: - default: - return ""; - } - -#ifdef AZ_REDEFINE_IOS_AT_END -#define IOS AZ_REDEFINE_IOS_AT_END -#endif - } - - const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform) - { - if (osPlatform == PlatformCodeNameWindows || osPlatform == PlatformCodeNameLinux) - { - return PlatformPC; - } - else if (osPlatform == PlatformCodeNameMac) - { - return PlatformOSX; - } - else if (osPlatform == PlatformCodeNameAndroid) - { - return PlatformES3; - } - else if (osPlatform == PlatformCodeNameiOS) - { - return PlatformIOS; - } - else if (osPlatform == PlatformCodeNameProvo) - { - return PlatformProvo; - } - else if (osPlatform == PlatformCodeNameSalem) - { - return PlatformSalem; - } - else if (osPlatform == PlatformCodeNameJasper) - { - return PlatformJasper; - } - - AZ_Error("PlatformDefault", false, R"(Supplied OS platform "%.*s" does not have a corresponding default asset platform)", - aznumeric_cast(osPlatform.size()), osPlatform.data()); - return ""; - } - - PlatformFlags PlatformHelper::GetPlatformFlagFromPlatformIndex(PlatformId platformIndex) - { - if (platformIndex < 0 || platformIndex > PlatformId::NumPlatformIds) - { - return PlatformFlags::Platform_NONE; - } - if (platformIndex == PlatformId::ALL) - { - return PlatformFlags::Platform_ALL; - } - if (platformIndex == PlatformId::ALL_CLIENT) - { - return PlatformFlags::Platform_ALL_CLIENT; - } - return static_cast(1 << platformIndex); - } - - AZStd::fixed_vector PlatformHelper::GetPlatforms(PlatformFlags platformFlags) - { - AZStd::fixed_vector platforms; - for (int platformNum = 0; platformNum < PlatformId::NumPlatformIds; ++platformNum) - { - const bool isAllPlatforms = PlatformId::ALL == static_cast(platformNum) - && ((platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE); - - const bool isAllClientPlatforms = PlatformId::ALL_CLIENT == static_cast(platformNum) - && ((platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE); - - if (isAllPlatforms || isAllClientPlatforms - || (platformFlags & static_cast(1 << platformNum)) != PlatformFlags::Platform_NONE) - { - platforms.push_back(PlatformNames[platformNum]); - } - } - - return platforms; - } - - AZStd::fixed_vector PlatformHelper::GetPlatformsInterpreted(PlatformFlags platformFlags) - { - return GetPlatforms(GetPlatformFlagsInterpreted(platformFlags)); - } - - AZStd::fixed_vector PlatformHelper::GetPlatformIndices(PlatformFlags platformFlags) - { - AZStd::fixed_vector platformIndices; - for (int i = 0; i < PlatformId::NumPlatformIds; i++) - { - PlatformId index = static_cast(i); - if ((GetPlatformFlagFromPlatformIndex(index) & platformFlags) != PlatformFlags::Platform_NONE) - { - platformIndices.emplace_back(index); - } - } - return platformIndices; - } - - AZStd::fixed_vector PlatformHelper::GetPlatformIndicesInterpreted(PlatformFlags platformFlags) - { - return GetPlatformIndices(GetPlatformFlagsInterpreted(platformFlags)); - } - - PlatformFlags PlatformHelper::GetPlatformFlag(AZStd::string_view platform) - { - int platformIndex = GetPlatformIndexFromName(platform); - if (platformIndex == PlatformId::Invalid) - { - AZ_Error("PlatformDefault", false, "Invalid Platform ( %.*s ).\n", static_cast(platform.length()), platform.data()); - return PlatformFlags::Platform_NONE; - } - - if(platformIndex == PlatformId::ALL) - { - return PlatformFlags::Platform_ALL; - } - - if (platformIndex == PlatformId::ALL_CLIENT) - { - return PlatformFlags::Platform_ALL_CLIENT; - } - - return static_cast(1 << platformIndex); - } - - const char* PlatformHelper::GetPlatformName(PlatformId platform) - { - if (platform < 0 || platform > PlatformId::NumPlatformIds) - { - return "invalid"; - } - return PlatformNames[platform]; - } - - void PlatformHelper::AppendPlatformCodeNames(AZStd::fixed_vector& platformCodes, AZStd::string_view platformId) - { - PlatformId platform = GetPlatformIdFromName(platformId); - AZ_Assert(platform != PlatformId::Invalid, "Unsupported Platform ID: %.*s", static_cast(platformId.length()), platformId.data()); - AppendPlatformCodeNames(platformCodes, platform); - } - - void PlatformHelper::AppendPlatformCodeNames(AZStd::fixed_vector& platformCodes, PlatformId platformId) - { -// The IOS SDK has a macro that defines IOS as 1 which causes the enum below to be incorrectly converted to "PlatformId::1". -#pragma push_macro("IOS") -#undef IOS - // To reduce work the Asset Processor groups assets that can be shared between hardware platforms together. For this - // reason "PC" can for instance cover both the Windows and Linux platforms and "IOS" can cover AppleTV and iOS. - switch (platformId) - { - case PlatformId::PC: - platformCodes.emplace_back(PlatformCodeNameWindows); - platformCodes.emplace_back(PlatformCodeNameLinux); - break; - case PlatformId::ES3: - platformCodes.emplace_back(PlatformCodeNameAndroid); - break; - case PlatformId::IOS: - platformCodes.emplace_back(PlatformCodeNameiOS); - break; - case PlatformId::OSX: - platformCodes.emplace_back(PlatformCodeNameMac); - break; - case PlatformId::PROVO: - platformCodes.emplace_back(PlatformCodeNameProvo); - break; - case PlatformId::SALEM: - platformCodes.emplace_back(PlatformCodeNameSalem); - break; - case PlatformId::JASPER: - platformCodes.emplace_back(PlatformCodeNameJasper); - break; - case PlatformId::SERVER: - // Server is not a hardware platform - break; - default: - AZ_Assert(false, "Unsupported Platform ID: %i", platformId); - break; - } -#pragma pop_macro("IOS") - } - - int PlatformHelper::GetPlatformIndexFromName(AZStd::string_view platformName) - { - for (int idx = 0; idx < PlatformId::NumPlatformIds; idx++) - { - if (platformName == PlatformNames[idx]) - { - return idx; - } - } - - return PlatformId::Invalid; - } - - PlatformId PlatformHelper::GetPlatformIdFromName(AZStd::string_view platformName) - { - return aznumeric_caster(GetPlatformIndexFromName(platformName)); - } - - AssetPlatformCombinedString PlatformHelper::GetCommaSeparatedPlatformList(PlatformFlags platformFlags) - { - AZStd::fixed_vector platformNames = GetPlatforms(platformFlags); - AssetPlatformCombinedString platformsString; - AZ::StringFunc::Join(platformsString, platformNames.begin(), platformNames.end(), ", "); - return platformsString; - } - - PlatformFlags PlatformHelper::GetPlatformFlagsInterpreted(PlatformFlags platformFlags) - { - PlatformFlags returnFlags = PlatformFlags::Platform_NONE; - - if((platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE) - { - for (int i = 0; i < NumPlatforms; ++i) - { - auto platformId = static_cast(i); - - if (platformId != PlatformId::ALL && platformId != PlatformId::ALL_CLIENT) - { - returnFlags |= GetPlatformFlagFromPlatformIndex(platformId); - } - } - } - else if((platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE) - { - for (int i = 0; i < NumPlatforms; ++i) - { - auto platformId = static_cast(i); - - if (platformId != PlatformId::ALL && platformId != PlatformId::ALL_CLIENT && platformId != PlatformId::SERVER) - { - returnFlags |= GetPlatformFlagFromPlatformIndex(platformId); - } - } - } - else - { - returnFlags = platformFlags; - } - - return returnFlags; - } - - bool PlatformHelper::IsSpecialPlatform(PlatformFlags platformFlags) - { - return (platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE - || (platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE; - } - - bool HasFlagHelper(PlatformFlags flags, PlatformFlags checkPlatform) - { - return (flags & checkPlatform) == checkPlatform; - } - - - bool PlatformHelper::HasPlatformFlag(PlatformFlags flags, PlatformId checkPlatform) - { - // If checkPlatform contains any kind of invalid id, just exit out here - if(checkPlatform == PlatformId::Invalid || checkPlatform == NumPlatforms) - { - return false; - } - - // ALL_CLIENT + SERVER = ALL - if(HasFlagHelper(flags, PlatformFlags::Platform_ALL_CLIENT | PlatformFlags::Platform_SERVER)) - { - flags = PlatformFlags::Platform_ALL; - } - - if(HasFlagHelper(flags, PlatformFlags::Platform_ALL)) - { - // It doesn't matter what checkPlatform is set to in this case, just return true - return true; - } - - if(HasFlagHelper(flags, PlatformFlags::Platform_ALL_CLIENT)) - { - return checkPlatform != PlatformId::SERVER; - } - - return HasFlagHelper(flags, GetPlatformFlagFromPlatformIndex(checkPlatform)); - } - -} diff --git a/Code/Framework/AzFramework/AzFramework/Platform/PlatformDefaults.h b/Code/Framework/AzFramework/AzFramework/Platform/PlatformDefaults.h index d7f467ec0f..13f7fa20e5 100644 --- a/Code/Framework/AzFramework/AzFramework/Platform/PlatformDefaults.h +++ b/Code/Framework/AzFramework/AzFramework/Platform/PlatformDefaults.h @@ -12,144 +12,12 @@ #pragma once -#include -#include -#include -#include -#include - -// On IOS builds IOS will be defined and interfere with the below enums -#pragma push_macro("IOS") -#undef IOS +#include +// As the Platform defaults is needed within AzCore, +// those structures have been moved to AzCore and brought into +// The AzFramework namespace for backwards compatibility namespace AzFramework { - constexpr char PlatformPC[] = "pc"; - constexpr char PlatformES3[] = "es3"; - constexpr char PlatformIOS[] = "ios"; - constexpr char PlatformOSX[] = "osx_gl"; - constexpr char PlatformProvo[] = "provo"; - constexpr char PlatformSalem[] = "salem"; - constexpr char PlatformJasper[] = "jasper"; - constexpr char PlatformServer[] = "server"; - - constexpr char PlatformCodeNameWindows[] = "Windows"; - constexpr char PlatformCodeNameLinux[] = "Linux"; - constexpr char PlatformCodeNameAndroid[] = "Android"; - constexpr char PlatformCodeNameiOS[] = "iOS"; - constexpr char PlatformCodeNameMac[] = "Mac"; - constexpr char PlatformCodeNameProvo[] = "Provo"; - constexpr char PlatformCodeNameSalem[] = "Salem"; - constexpr char PlatformCodeNameJasper[] = "Jasper"; - constexpr char PlatformAll[] = "all"; - constexpr char PlatformAllClient[] = "all_client"; - - // Used for the capacity of a fixed vector to store the code names of platforms - // The value needs to be higher than the number of unique OS platforms that are supported(at this time 8) - constexpr size_t MaxPlatformCodeNames = 16; - - //! This platform enum have platform values in sequence and can also be used to get the platform count. - AZ_ENUM_WITH_UNDERLYING_TYPE(PlatformId, int, - (Invalid, -1), - PC, - ES3, - IOS, - OSX, - PROVO, - SALEM, - JASPER, - SERVER, // Corresponds to the customer's flavor of "server" which could be windows, ubuntu, etc - ALL, - ALL_CLIENT, - - // Add new platforms above this - NumPlatformIds - ); - constexpr int NumClientPlatforms = 7; - constexpr int NumPlatforms = NumClientPlatforms + 1; // 1 "Server" platform currently - enum class PlatformFlags : AZ::u32 - { - Platform_NONE = 0x00, - Platform_PC = 1 << PlatformId::PC, - Platform_ES3 = 1 << PlatformId::ES3, - Platform_IOS = 1 << PlatformId::IOS, - Platform_OSX = 1 << PlatformId::OSX, - Platform_PROVO = 1 << PlatformId::PROVO, - Platform_SALEM = 1 << PlatformId::SALEM, - Platform_JASPER = 1 << PlatformId::JASPER, - Platform_SERVER = 1 << PlatformId::SERVER, - - // A special platform that will always correspond to all platforms, even if new ones are added - Platform_ALL = 1ULL << 30, - - // A special platform that will always correspond to all non-server platforms, even if new ones are added - Platform_ALL_CLIENT = 1ULL << 31, - - AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER, - }; - - AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags); - - // 32 characters should be more than enough to store a platform name - using AssetPlatformFixedString = AZStd::fixed_string<32>; - // Fixed string which can store a comma separated list of platforms names - // Additional byte is added to take into account the comma - using AssetPlatformCombinedString = AZStd::fixed_string<(AssetPlatformFixedString{}.max_size() + 1) * PlatformId::NumPlatformIds>; - - const char* PlatformIdToPalFolder(AzFramework::PlatformId platform); - - const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform); - - //! Platform Helper is an utility class that can be used to retrieve platform related information - class PlatformHelper - { - public: - - //! Given a platformIndex returns the platform name - static const char* GetPlatformName(PlatformId platform); - - //! Converts the platform name to the platform code names as defined in AZ_TRAIT_OS_PLATFORM_CODENAME. - static void AppendPlatformCodeNames(AZStd::fixed_vector& platformCodes, AZStd::string_view platformName); - - //! Converts the platform name to the platform code names as defined in AZ_TRAIT_OS_PLATFORM_CODENAME. - static void AppendPlatformCodeNames(AZStd::fixed_vector& platformCodes, PlatformId platformId); - - //! Given a platform name returns a platform index. - //! If the platform is not found, the method returns -1. - static int GetPlatformIndexFromName(AZStd::string_view platformName); - - //! Given a platform name returns a platform id. - //! If the platform is not found, the method returns -1. - static PlatformId GetPlatformIdFromName(AZStd::string_view platformName); - - //! Given a platformIndex returns the platformFlags - static PlatformFlags GetPlatformFlagFromPlatformIndex(PlatformId platform); - - //! Given a platformFlags returns all the platform identifiers that are set. - static AZStd::fixed_vector GetPlatforms(PlatformFlags platformFlags); - //! Given a platformFlags returns all the platform identifiers that are set, with special flags interpreted. Do not use the result for saving - static AZStd::fixed_vector GetPlatformsInterpreted(PlatformFlags platformFlags); - - //! Given a platformFlags return a list of PlatformId indices - static AZStd::fixed_vector GetPlatformIndices(PlatformFlags platformFlags); - //! Given a platformFlags return a list of PlatformId indices, with special flags interpreted. Do not use the result for saving - static AZStd::fixed_vector GetPlatformIndicesInterpreted(PlatformFlags platformFlags); - - //! Given a platform identifier returns its corresponding platform flag. - static PlatformFlags GetPlatformFlag(AZStd::string_view platform); - - //! Given any platformFlags returns a string listing the input platforms - static AssetPlatformCombinedString GetCommaSeparatedPlatformList(PlatformFlags platformFlags); - - //! If platformFlags contains any special flags, they are removed and replaced with the normal flags they represent - static PlatformFlags GetPlatformFlagsInterpreted(PlatformFlags platformFlags); - - //! Returns true if platformFlags contains any special flags - static bool IsSpecialPlatform(PlatformFlags platformFlags); - - //! Returns true if platformFlags has checkPlatform flag set. - static bool HasPlatformFlag(PlatformFlags platformFlags, PlatformId checkPlatform); - }; + using namespace AZ::PlatformDefaults; } - -#pragma pop_macro("IOS") diff --git a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp index 5acf4e75d1..7e968ffcd8 100644 --- a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp @@ -42,8 +42,14 @@ namespace AzFramework::ProjectManager AZ::CommandLine commandLine; commandLine.Parse(argc, argv); AZ::SettingsRegistryImpl settingsRegistry; + // Store the Command line to the Setting Registry + + AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(settingsRegistry); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {}); + // Retrieve Command Line from Settings Registry, it may have been updated by the call to FindEngineRoot() + // in MergeSettingstoRegistry_ConfigFile + AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(settingsRegistry, commandLine); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(settingsRegistry, commandLine, false); engineRootPath = AZ::SettingsRegistryMergeUtils::FindEngineRoot(settingsRegistry); projectRootPath = AZ::SettingsRegistryMergeUtils::FindProjectRoot(settingsRegistry); diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ScreenGeometry.h b/Code/Framework/AzFramework/AzFramework/Viewport/ScreenGeometry.h index 1b570ebb8e..d3ae4e16f3 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ScreenGeometry.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ScreenGeometry.h @@ -14,6 +14,7 @@ #include #include +#include #include namespace AZ @@ -133,6 +134,18 @@ namespace AzFramework return !operator==(lhs, rhs); } + inline ScreenPoint ScreenPointFromNDC(const AZ::Vector3& screenNDC, const AZ::Vector2& viewportSize) + { + return ScreenPoint( + aznumeric_caster(std::round(screenNDC.GetX() * viewportSize.GetX())), + aznumeric_caster(std::round((1.0f - screenNDC.GetY()) * viewportSize.GetY()))); + } + + inline AZ::Vector2 NDCFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize) + { + return AZ::Vector2(aznumeric_cast(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast(screenPoint.m_y)) / viewportSize; + } + //! Return an AZ::Vector2 from a ScreenPoint. inline AZ::Vector2 Vector2FromScreenPoint(const ScreenPoint& screenPoint) { diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp index 3ca16897ba..5d2d02a398 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp @@ -101,20 +101,25 @@ namespace AzFramework cameraState.m_nearClip, cameraState.m_farClip); } - ScreenPoint WorldToScreen( - const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection, - const AZ::Vector2& viewportSize) + AZ::Vector3 WorldToScreenNDC( + const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection) { // transform the world space position to clip space const auto clipSpacePosition = cameraProjection * cameraView * AZ::Vector3ToVector4(worldPosition, 1.0f); // transform the clip space position to ndc space (perspective divide) const auto ndcPosition = clipSpacePosition / clipSpacePosition.GetW(); // transform ndc space from <-1,1> to <0, 1> range - const auto ndcNormalizedPosition = (AZ::Vector4ToVector2(ndcPosition) + AZ::Vector2::CreateOne()) * 0.5f; + return (AZ::Vector4ToVector3(ndcPosition) + AZ::Vector3::CreateOne()) * 0.5f; + } + + + ScreenPoint WorldToScreen( + const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection, + const AZ::Vector2& viewportSize) + { + const auto ndcNormalizedPosition = WorldToScreenNDC(worldPosition, cameraView, cameraProjection); // scale ndc position by screen dimensions to return screen position - return ScreenPoint( - aznumeric_caster(std::round(ndcNormalizedPosition.GetX() * viewportSize.GetX())), - aznumeric_caster(std::round(viewportSize.GetY() - (ndcNormalizedPosition.GetY() * viewportSize.GetY())))); + return ScreenPointFromNDC(ndcNormalizedPosition, viewportSize); } ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState) @@ -127,12 +132,8 @@ namespace AzFramework const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize) { - const auto screenHeight = viewportSize.GetY(); - const auto flippedScreenPosition = - AZ::Vector2(aznumeric_caster(screenPosition.m_x), aznumeric_caster(screenHeight - screenPosition.m_y)); - - // convert screen space coordinates to <-1,1> range - const auto ndcPosition = (flippedScreenPosition / viewportSize) * 2.0f - AZ::Vector2::CreateOne(); + // convert screen space coordinates from <0, 1> to <-1,1> range + const auto ndcPosition = NDCFromScreenPoint(screenPosition, viewportSize) * 2.0f - AZ::Vector2::CreateOne(); // transform ndc space position to clip space const auto clipSpacePosition = inverseCameraProjection * Vector2ToVector4(ndcPosition, -1.0f, 1.0f); diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h index 582e051bbb..a2c650465f 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h @@ -28,6 +28,11 @@ namespace AzFramework struct ScreenPoint; struct ViewportInfo; + //! Projects a position in world space to screen space normalized device coordinates for the given camera. + AZ::Vector3 WorldToScreenNDC( + const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection); + + //! Projects a position in world space to screen space for the given camera. ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState); diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index b37c1b259f..312a3766ac 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -145,6 +145,7 @@ set(FILES Components/NonUniformScaleComponent.cpp FileFunc/FileFunc.h FileFunc/FileFunc.cpp + Font/FontInterface.h Gem/GemInfo.cpp Gem/GemInfo.h StringFunc/StringFunc.h @@ -316,7 +317,6 @@ set(FILES Terrain/TerrainDataRequestBus.h Terrain/TerrainDataRequestBus.cpp Platform/PlatformDefaults.h - Platform/PlatformDefaults.cpp Windowing/WindowBus.h Windowing/NativeWindow.cpp Windowing/NativeWindow.h diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp index f4fc9364f7..edd4af293d 100644 --- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp +++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp @@ -80,6 +80,8 @@ namespace AzGameFramework AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true); #endif + // Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry); } AZ::ComponentTypeList GameApplication::GetRequiredSystemComponents() const diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeBitset.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeBitset.h index 21e12cd305..3d064b5ecc 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeBitset.h +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeBitset.h @@ -114,6 +114,9 @@ namespace AzNetworking void ClearUnusedBits(); ContainerType m_container; + + template + friend class FixedSizeVectorBitset; }; } diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeVectorBitset.inl b/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeVectorBitset.inl index a8aceb4dd5..038e68d1d0 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeVectorBitset.inl +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/FixedSizeVectorBitset.inl @@ -192,19 +192,11 @@ namespace AzNetworking template inline void FixedSizeVectorBitset::ClearUnusedBits() { - constexpr ElementType AllOnes = static_cast(~0); - const ElementType LastUsedBits = (GetSize() % BitsetType::ElementTypeBits); -#pragma warning(push) -#pragma warning(disable : 4293) // shift count negative or too big, undefined behaviour -#pragma warning(disable : 6326) // constant constant comparison - const ElementType ShiftAmount = (LastUsedBits == 0) ? 0 : BitsetType::ElementTypeBits - LastUsedBits; - const ElementType ClearBitMask = AllOnes >> ShiftAmount; -#pragma warning(pop) uint32_t usedElementSize = (GetSize() + BitsetType::ElementTypeBits - 1) / BitsetType::ElementTypeBits; - for (uint32_t i = usedElementSize + 1; i < CAPACITY; ++i) + for (uint32_t i = usedElementSize + 1; i < BitsetType::ElementCount; ++i) { m_bitset.GetContainer()[i] = 0; } - m_bitset.GetContainer()[m_bitset.GetContainer().size() - 1] &= ClearBitMask; + m_bitset.ClearUnusedBits(); } } diff --git a/Code/Framework/AzNetworking/AzNetworking/Serialization/AzContainerSerializers.h b/Code/Framework/AzNetworking/AzNetworking/Serialization/AzContainerSerializers.h index 70dc7847d2..a43a09165c 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Serialization/AzContainerSerializers.h +++ b/Code/Framework/AzNetworking/AzNetworking/Serialization/AzContainerSerializers.h @@ -270,7 +270,7 @@ namespace AzNetworking value.StoreToFloat3(values); serializer.Serialize(values[0], "xValue"); serializer.Serialize(values[1], "yValue"); - serializer.Serialize(values[1], "zValue"); + serializer.Serialize(values[2], "zValue"); value = AZ::Vector3::CreateFromFloat3(values); return serializer.IsValid(); } @@ -285,8 +285,8 @@ namespace AzNetworking value.StoreToFloat4(values); serializer.Serialize(values[0], "xValue"); serializer.Serialize(values[1], "yValue"); - serializer.Serialize(values[1], "zValue"); - serializer.Serialize(values[1], "wValue"); + serializer.Serialize(values[2], "zValue"); + serializer.Serialize(values[3], "wValue"); value = AZ::Vector4::CreateFromFloat4(values); return serializer.IsValid(); } @@ -301,8 +301,8 @@ namespace AzNetworking value.StoreToFloat4(values); serializer.Serialize(values[0], "xValue"); serializer.Serialize(values[1], "yValue"); - serializer.Serialize(values[1], "zValue"); - serializer.Serialize(values[1], "wValue"); + serializer.Serialize(values[2], "zValue"); + serializer.Serialize(values[3], "wValue"); value = AZ::Quaternion::CreateFromFloat4(values); return serializer.IsValid(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h index 0c631cf09e..f12ab71936 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h @@ -578,16 +578,6 @@ namespace AzToolsFramework */ virtual bool IsEditorInIsolationMode() = 0; - /*! - * Get the engine root path that the current tool is running under. - */ - virtual const char* GetEngineRootPath() const = 0; - - /** - * Get the version of the engine the current tools application is running under - */ - virtual const char* GetEngineVersion() const = 0; - /** * Creates and adds a new entity to the tools application from components which match at least one of the requiredTags * The tag matching occurs on AZ::Edit::SystemComponentTags attribute from the reflected class data in the serialization context diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index 352048f5f0..22c1390828 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -224,112 +224,6 @@ namespace AzToolsFramework } // Internal -#define AZ_MAX_ENGINE_VERSION_LEN 64 - // Private Implementation class to manage the engine root and version - // Note: We are not using any AzCore classes because the ToolsApplication - // initialization happens early on, before the Allocators get instantiated, - // so we are using Qt privately instead - class ToolsApplication::EngineConfigImpl - { - private: - friend class ToolsApplication; - - typedef QMap EngineJsonMap; - - EngineConfigImpl(const char* logWindow, const char* fileName) - : m_logWindow(logWindow) - , m_fileName(fileName) - { - m_engineRoot[0] = '\0'; - m_engineVersion[0] = '\0'; - } - - char m_engineRoot[AZ_MAX_PATH_LEN]; - char m_engineVersion[AZ_MAX_ENGINE_VERSION_LEN]; - EngineJsonMap m_engineConfigMap; - const char* m_logWindow; - const char* m_fileName; - - - // Read an engine configuration into a map of key/value pairs - bool ReadEngineConfigIntoMap(QString engineJsonPath, EngineJsonMap& engineJsonMap) - { - QFile engineJsonFile(engineJsonPath); - if (!engineJsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) - { - AZ_Warning(m_logWindow, false, "Unable to open file '%s' in the current root directory", engineJsonPath.toUtf8().data()); - return false; - } - - QByteArray engineJsonData = engineJsonFile.readAll(); - engineJsonFile.close(); - QJsonDocument engineJsonDoc(QJsonDocument::fromJson(engineJsonData)); - if (engineJsonDoc.isNull()) - { - AZ_Warning(m_logWindow, false, "Unable to read file '%s' in the current root directory", engineJsonPath.toUtf8().data()); - return false; - } - - QJsonObject engineJsonRoot = engineJsonDoc.object(); - for (const QString& configKey : engineJsonRoot.keys()) - { - QJsonValue configValue = engineJsonRoot[configKey]; - if (configValue.isString() || configValue.isDouble()) - { - // Only map strings and numbers, ignore every other type - engineJsonMap[configKey] = configValue.toString(); - } - else - { - AZ_Warning(m_logWindow, false, "Ignoring key '%s' from '%s', unsupported type.", configKey.toUtf8().data(), engineJsonPath.toUtf8().data()); - } - } - return true; - } - - // Initialize the engine config object based on the current - bool Initialize(const char* currentEngineRoot) - { - // Start with the app root as the engine root (legacy), but check to see if the engine root - // is external to the app root - azstrncpy(m_engineRoot, AZ_ARRAY_SIZE(m_engineRoot), currentEngineRoot, strlen(currentEngineRoot) + 1); - - // From the appRoot, check and see if we can read any external engine reference in engine.json - QString engineJsonFileName = QString(m_fileName); - QString engineJsonFilePath = QDir(currentEngineRoot).absoluteFilePath(engineJsonFileName); - - // From the appRoot, check and see if we can read any external engine reference in engine.json - if (!QFile::exists(engineJsonFilePath)) - { - AZ_Warning(m_logWindow, false, "Unable to find '%s' in the current app root directory.", m_fileName); - return false; - } - if (!ReadEngineConfigIntoMap(engineJsonFilePath, m_engineConfigMap)) - { - AZ_Warning(m_logWindow, false, "Defaulting root engine path to '%s'", currentEngineRoot); - return false; - } - - // Read in the local engine version value - auto localEngineVersionValue = m_engineConfigMap.find(QString(AzToolsFramework::Internal::s_engineConfigEngineVersionKey)); - QString localEngineVersion(localEngineVersionValue.value()); - azstrncpy(m_engineVersion, AZ_ARRAY_SIZE(m_engineVersion), localEngineVersion.toUtf8().data(), localEngineVersion.length() + 1); - - return true; - } - - const char* GetEngineRoot() const - { - return m_engineRoot; - } - - const char* GetEngineVersion() const - { - return m_engineVersion; - } - }; - - ToolsApplication::ToolsApplication(int* argc, char*** argv) : AzFramework::Application(argc, argv) , m_selectionBounds(AZ::Aabb()) @@ -339,7 +233,6 @@ namespace AzToolsFramework , m_isInIsolationMode(false) { ToolsApplicationRequests::Bus::Handler::BusConnect(); - m_engineConfigImpl.reset(new ToolsApplication::EngineConfigImpl(AzToolsFramework::Internal::s_startupLogWindow, AzToolsFramework::Internal::s_engineConfigFileName)); m_undoCache.RegisterToUndoCacheInterface(); } @@ -391,7 +284,6 @@ namespace AzToolsFramework void ToolsApplication::Start(const Descriptor& descriptor, const StartupParameters& startupParameters/* = StartupParameters()*/) { Application::Start(descriptor, startupParameters); - InitializeEngineConfig(); m_editorEntityManager.Start(); @@ -399,14 +291,6 @@ namespace AzToolsFramework AZ_Assert(m_editorEntityAPI, "ToolsApplication - Could not retrieve instance of EditorEntityAPI"); } - void ToolsApplication::InitializeEngineConfig() - { - if (!m_engineConfigImpl->Initialize(GetEngineRoot())) - { - AZ_Warning(AzToolsFramework::Internal::s_startupLogWindow, false, "Defaulting engine root path to '%s'", GetEngineRoot()); - } - } - void ToolsApplication::StartCommon(AZ::Entity* systemEntity) { Application::StartCommon(systemEntity); @@ -1832,16 +1716,6 @@ namespace AzToolsFramework return m_isInIsolationMode; } - const char* ToolsApplication::GetEngineRootPath() const - { - return m_engineConfigImpl->GetEngineRoot(); - } - - const char* ToolsApplication::GetEngineVersion() const - { - return m_engineConfigImpl->GetEngineVersion(); - } - void ToolsApplication::CreateAndAddEntityFromComponentTags(const AZStd::vector& requiredTags, const char* entityName) { if (!entityName || !entityName[0]) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h index ef9f190309..0f038422ce 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h @@ -150,8 +150,6 @@ namespace AzToolsFramework void EnterEditorIsolationMode() override; void ExitEditorIsolationMode() override; bool IsEditorInIsolationMode() override; - const char* GetEngineRootPath() const override; - const char* GetEngineVersion() const override; void CreateAndAddEntityFromComponentTags(const AZStd::vector& requiredTags, const char* entityName) override; @@ -174,7 +172,6 @@ namespace AzToolsFramework void CreateUndosForDirtyEntities(); void ConsistencyCheckUndoCache(); - void InitializeEngineConfig(); AZ::Aabb m_selectionBounds; EntityIdList m_selectedEntities; EntityIdList m_highlightedEntities; @@ -186,9 +183,6 @@ namespace AzToolsFramework bool m_isInIsolationMode; EntityIdSet m_isolatedEntityIdSet; - class EngineConfigImpl; - AZStd::unique_ptr m_engineConfigImpl; - EditorEntityAPI* m_editorEntityAPI = nullptr; EditorEntityManager m_editorEntityManager; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp index 603c7a8023..706d8243e2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp @@ -256,6 +256,8 @@ namespace AzToolsFramework m_userSettings = AZ::UserSettings::CreateFind(k_assetEditorWidgetSettings, AZ::UserSettings::CT_LOCAL); + UpdateRecentFileListState(); + QObject::connect(m_recentFileMenu, &QMenu::aboutToShow, this, &AssetEditorWidget::PopulateRecentMenu); } @@ -952,7 +954,8 @@ namespace AzToolsFramework void AssetEditorWidget::AddRecentPath(const AZStd::string& recentPath) { - m_userSettings->AddRecentPath(recentPath); + m_userSettings->AddRecentPath(recentPath); + UpdateRecentFileListState(); } void AssetEditorWidget::PopulateRecentMenu() @@ -989,6 +992,21 @@ namespace AzToolsFramework m_saveAsAssetAction->setEnabled(true); } + void AssetEditorWidget::UpdateRecentFileListState() + { + if (m_recentFileMenu) + { + if (!m_userSettings || m_userSettings->m_recentPaths.empty()) + { + m_recentFileMenu->setEnabled(false); + } + else + { + m_recentFileMenu->setEnabled(true); + } + } + } + } // namespace AssetEditor } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.h index b27d69dba5..4379fc27ed 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.h @@ -122,6 +122,8 @@ namespace AzToolsFramework void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override; void OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo) override; + void UpdateRecentFileListState(); + private: void DirtyAsset(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 417a524e77..26191c97af 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -58,7 +58,7 @@ namespace AzToolsFramework m_prefabUndoCache.Destroy(); } - PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector& entityIds, AZStd::string_view filePath) + PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) { // Retrieve entityList from entityIds EntityList inputEntityList; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 46a7f946ba..80f28d7edb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -42,7 +42,7 @@ namespace AzToolsFramework void UnregisterPrefabPublicHandlerInterface(); // PrefabPublicInterface... - PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZStd::string_view filePath) override; + PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) override; PrefabOperationResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, AZ::Vector3 position) override; PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override; PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h index 81a5258d91..4e59729ab2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h @@ -49,7 +49,7 @@ namespace AzToolsFramework * @param filePath The path for the new prefab file. * @return An outcome object; on failure, it comes with an error message detailing the cause of the error. */ - virtual PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZStd::string_view filePath) = 0; + virtual PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) = 0; /** * Instantiate a prefab from a prefab file. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp index 7c4d72dbbf..5e928a382a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace AzToolsFramework @@ -44,7 +45,9 @@ namespace AzToolsFramework ->DataElement( AZ::Edit::UIHandlers::Default, &EditorNonUniformScaleComponent::m_scale, "Non-uniform Scale", "Non-uniform scale for this entity only (does not propagate through hierarchy)") - ->Attribute(AZ::Edit::Attributes::Min, AZ::MinNonUniformScale) + ->Attribute(AZ::Edit::Attributes::Min, AZ::MinTransformScale) + ->Attribute(AZ::Edit::Attributes::Max, AZ::MaxTransformScale) + ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorNonUniformScaleComponent::OnScaleChanged) ; } @@ -106,13 +109,13 @@ namespace AzToolsFramework void EditorNonUniformScaleComponent::SetScale(const AZ::Vector3& scale) { - if (scale.GetMinElement() >= AZ::MinNonUniformScale) + if (scale.GetMinElement() >= AZ::MinTransformScale && scale.GetMaxElement() <= AZ::MaxTransformScale) { m_scale = scale; } else { - AZ::Vector3 clampedScale = scale.GetMax(AZ::Vector3(AZ::MinNonUniformScale)); + AZ::Vector3 clampedScale = scale.GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)); AZ_Warning("Editor Non-uniform Scale Component", false, "SetScale value was clamped from %s to %s for entity %s", AZ::ToString(scale).c_str(), AZ::ToString(clampedScale).c_str(), GetEntity()->GetName().c_str()); m_scale = clampedScale; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp index 2ceb97adfd..7ce11e5957 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp @@ -1276,7 +1276,6 @@ namespace AzToolsFramework Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)-> DataElement(TransformScaleHandler, &EditorTransform::m_scale, "Scale", "Local Scale")-> Attribute(AZ::Edit::Attributes::Step, 0.1f)-> - Attribute(AZ::Edit::Attributes::Min, 0.01f)-> Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked) ; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.cpp index 0105d9bbad..94d0113bcf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.cpp @@ -12,6 +12,7 @@ #include "AzToolsFramework_precompiled.h" #include +#include #include namespace AzToolsFramework @@ -36,8 +37,8 @@ namespace AzToolsFramework AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, newCtrl); }); - newCtrl->setMinimum(0.01f); - newCtrl->setMaximum(std::numeric_limits::max()); + newCtrl->setMinimum(AZ::MinTransformScale); + newCtrl->setMaximum(AZ::MaxTransformScale); return newCtrl; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 1e71b545b5..6ce3fdc755 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -39,9 +40,12 @@ namespace AzToolsFramework { namespace Prefab { + EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr; PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr; + PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr; + const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab"; void PrefabUserSettings::Reflect(AZ::ReflectContext* context) @@ -79,6 +83,13 @@ namespace AzToolsFramework return; } + s_prefabLoaderInterface = AZ::Interface::Get(); + if (s_prefabLoaderInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabLoaderInterface on PrefabIntegrationManager construction."); + return; + } + EditorContextMenuBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); @@ -320,14 +331,15 @@ namespace AzToolsFramework GenerateSuggestedFilenameFromEntities(prefabRootEntities, suggestedName); - if (!QueryUserForPrefabSaveLocation(suggestedName, targetDirectory, AZ_CRC("PrefabUserSettings"), activeWindow, prefabName, prefabFilePath)) + if (!QueryUserForPrefabSaveLocation( + suggestedName, targetDirectory, AZ_CRC("PrefabUserSettings"), activeWindow, prefabName, prefabFilePath)) { // User canceled prefab creation, or error prevented continuation. return; } } - auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, prefabFilePath); + auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, s_prefabLoaderInterface->GetRelativePathToProject(prefabFilePath.data())); if (!createPrefabOutcome.IsSuccess()) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 66a047df28..c9b846aa5b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -29,6 +29,9 @@ namespace AzToolsFramework { namespace Prefab { + + class PrefabLoaderInterface; + //! Structure for saving/retrieving user settings related to prefab workflows. class PrefabUserSettings : public AZ::UserSettings @@ -129,6 +132,7 @@ namespace AzToolsFramework static EditorEntityUiInterface* s_editorEntityUiInterface; static PrefabPublicInterface* s_prefabPublicInterface; static PrefabEditInterface* s_prefabEditInterface; + static PrefabLoaderInterface* s_prefabLoaderInterface; }; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index bc27ffa5e1..261645e79a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -38,6 +38,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include #include @@ -1212,9 +1213,8 @@ namespace AzToolsFramework if (!QFile::exists(path)) { - const char* engineRoot = nullptr; - AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath); - QDir engineDir = engineRoot ? QDir(engineRoot) : QDir::current(); + AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath(); + QDir engineDir = !engineRoot.empty() ? QDir(QString(engineRoot.c_str())) : QDir::current(); path = engineDir.absoluteFilePath(iconPath.c_str()); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 27222e9aac..433602e6d8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -53,7 +53,7 @@ namespace AzToolsFramework float, cl_viewportGizmoAxisLabelOffset, 1.15f, nullptr, AZ::ConsoleFunctorFlags::Null, "The offset of the label for the viewport axis gizmo"); AZ_CVAR( - float, cl_viewportGizmoAxisLabelSize, 2.0f, nullptr, AZ::ConsoleFunctorFlags::Null, + float, cl_viewportGizmoAxisLabelSize, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The size of each label for the viewport axis gizmo"); AZ_CVAR( AZ::Vector2, cl_viewportGizmoAxisScreenPosition, AZ::Vector2(0.045f, 0.9f), nullptr, @@ -1603,7 +1603,7 @@ namespace AzToolsFramework const AZ::Vector3 uniformScale = AZ::Vector3(action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset())); const AZ::Vector3 scale = (AZ::Vector3::CreateOne() + - (uniformScale / initialScale)).GetMax(AZ::Vector3(0.01f)); + (uniformScale / initialScale)).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)); const AZ::Transform scaleTransform = AZ::Transform::CreateScale(scale); if (action.m_modifiers.Alt()) @@ -3434,19 +3434,22 @@ namespace AzToolsFramework const auto cameraProjection = AzFramework::CameraProjection(gizmoCameraState); // screen space offset to move the 2d gizmo around - const AZ::Vector3 screenPosition = - (AZ::Vector2ToVector3(cl_viewportGizmoAxisScreenPosition) - AZ::Vector3(0.5f, 0.5f, 0.0f)) * - AZ::Vector2ToVector3(gizmoCameraState.m_viewportSize); + const AZ::Vector2 screenOffset = AZ::Vector2(cl_viewportGizmoAxisScreenPosition) - AZ::Vector2(0.5f, 0.5f); // map from a position in world space (relative to the the gizmo camera near the origin) to a position in // screen space const auto calculateGizmoAxis = - [&cameraView, &cameraProjection, &gizmoCameraState, &screenPosition] - (const AZ::Vector3& position) - { - return AZ::Vector2ToVector3(AzFramework::Vector2FromScreenPoint( - AzFramework::WorldToScreen( - position, cameraView, cameraProjection, gizmoCameraState.m_viewportSize))) + screenPosition; + [&cameraView, &cameraProjection, &screenOffset] + (const AZ::Vector3& axis) + { + auto result = AZ::Vector2( + AzFramework::WorldToScreenNDC( + axis, + cameraView, + cameraProjection) + ); + result.SetY(1.0f - result.GetY()); + return result + screenOffset; }; // get all important axis positions in screen space @@ -3456,31 +3459,31 @@ namespace AzToolsFramework const auto gizmoEndAxisY = calculateGizmoAxis(-AZ::Vector3::CreateAxisY() * lineLength); const auto gizmoEndAxisZ = calculateGizmoAxis(-AZ::Vector3::CreateAxisZ() * lineLength); - const AZ::Vector3 gizmoAxisX = gizmoEndAxisX - gizmoStart; - const AZ::Vector3 gizmoAxisY = gizmoEndAxisY - gizmoStart; - const AZ::Vector3 gizmoAxisZ = gizmoEndAxisZ - gizmoStart; + const AZ::Vector2 gizmoAxisX = gizmoEndAxisX - gizmoStart; + const AZ::Vector2 gizmoAxisY = gizmoEndAxisY - gizmoStart; + const AZ::Vector2 gizmoAxisZ = gizmoEndAxisZ - gizmoStart; // draw the axes of the gizmo debugDisplay.SetLineWidth(cl_viewportGizmoAxisLineWidth); debugDisplay.SetColor(AZ::Colors::Red); - debugDisplay.DrawLine(gizmoStart, gizmoEndAxisX); + debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisX, 1.0f); debugDisplay.SetColor(AZ::Colors::Lime); - debugDisplay.DrawLine(gizmoStart, gizmoEndAxisY); + debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisY, 1.0f); debugDisplay.SetColor(AZ::Colors::Blue); - debugDisplay.DrawLine(gizmoStart, gizmoEndAxisZ); + debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisZ, 1.0f); debugDisplay.SetLineWidth(1.0f); const float labelOffset = cl_viewportGizmoAxisLabelOffset; - const auto labelOffsetX = gizmoStart + gizmoAxisX * labelOffset; - const auto labelOffsetY = gizmoStart + gizmoAxisY * labelOffset; - const auto labelOffsetZ = gizmoStart + gizmoAxisZ * labelOffset; + const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize; + const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize; + const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize; // draw the label of of each axis for the gizmo const float labelSize = cl_viewportGizmoAxisLabelSize; debugDisplay.SetColor(AZ::Colors::White); - debugDisplay.Draw2dTextLabel(labelOffsetX.GetX(), labelOffsetX.GetY(), labelSize, "X", true); - debugDisplay.Draw2dTextLabel(labelOffsetY.GetX(), labelOffsetY.GetY(), labelSize, "Y", true); - debugDisplay.Draw2dTextLabel(labelOffsetZ.GetX(), labelOffsetZ.GetY(), labelSize, "Z", true); + debugDisplay.Draw2dTextLabel(labelXScreenPosition.GetX(), labelXScreenPosition.GetY(), labelSize, "X", true); + debugDisplay.Draw2dTextLabel(labelYScreenPosition.GetX(), labelYScreenPosition.GetY(), labelSize, "Y", true); + debugDisplay.Draw2dTextLabel(labelZScreenPosition.GetX(), labelZScreenPosition.GetY(), labelSize, "Z", true); } void EditorTransformComponentSelection::DisplayViewportSelection2d( diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp index 2003f8eafd..5ccbd95f09 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp @@ -68,7 +68,7 @@ namespace UnitTest { assets[idx] = AssetId(AZ::Uuid::CreateRandom(), 0); AZ::Data::AssetInfo info; - info.m_relativePath = AZStd::string::format("Asset%d.txt", idx); + info.m_relativePath = AZStd::string::format("asset%d.txt", idx); m_assetsPath[idx] = info.m_relativePath; info.m_assetId = assets[idx]; m_assetRegistry->RegisterAsset(assets[idx], info); @@ -623,7 +623,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList1, assets[fileIndex])); if (m_fileStreams[0][fileIndex].Open(m_assetsPathFull[0][fileIndex].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { - AZStd::string fileContent = AZStd::string::format("Asset%d.txt", fileIndex); + AZStd::string fileContent = AZStd::string::format("asset%d.txt", fileIndex); m_fileStreams[0][fileIndex].Write(fileContent.size(), fileContent.c_str()); m_fileStreams[0][fileIndex].Close(); } @@ -654,7 +654,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList1, assets[fileIndex])); if (m_fileStreams[0][fileIndex].Open(m_assetsPathFull[0][fileIndex].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { - AZStd::string fileContent = AZStd::string::format("Asset%d.txt", fileIndex + 1);// changing file content + AZStd::string fileContent = AZStd::string::format("asset%d.txt", fileIndex + 1);// changing file content m_fileStreams[0][fileIndex].Write(fileContent.size(), fileContent.c_str()); m_fileStreams[0][fileIndex].Close(); } @@ -987,7 +987,7 @@ namespace UnitTest m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); - m_assetSeedManager->RemoveSeedAsset("Asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(secondSeedList.size(), 0); } @@ -1003,7 +1003,7 @@ namespace UnitTest m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); - m_assetSeedManager->RemoveSeedAsset("Asset0.txt", AzFramework::PlatformFlags::Platform_PC); + m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(secondSeedList.size(), 1); } @@ -1017,7 +1017,7 @@ namespace UnitTest EXPECT_EQ(seedList.size(), 1); - m_assetSeedManager->RemoveSeedAsset("Asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset("asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(secondSeedList.size(), 1); } diff --git a/Code/Sandbox/Editor/AboutDialog.cpp b/Code/Sandbox/Editor/AboutDialog.cpp index e1cd4c7bb0..d9cf722d1f 100644 --- a/Code/Sandbox/Editor/AboutDialog.cpp +++ b/Code/Sandbox/Editor/AboutDialog.cpp @@ -56,7 +56,7 @@ CAboutDialog::CAboutDialog(QString versionText, QString richTextCopyrightNotice, m_backgroundImage = QPixmap::fromImage(backgroundImage.scaled(m_enforcedWidth, m_enforcedHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Draw the Open 3D Engine logo from svg - m_ui->m_logo->load(QStringLiteral(":/StartupLogoDialog/lumberyard_logo.svg")); + m_ui->m_logo->load(QStringLiteral(":/StartupLogoDialog/o3de_logo.svg")); // Prevent re-sizing setFixedSize(m_enforcedWidth, m_enforcedHeight); diff --git a/Code/Sandbox/Editor/AboutDialog.ui b/Code/Sandbox/Editor/AboutDialog.ui index 0eb2881600..67767c0de1 100644 --- a/Code/Sandbox/Editor/AboutDialog.ui +++ b/Code/Sandbox/Editor/AboutDialog.ui @@ -60,35 +60,35 @@ 5 - - - - 4 - - - 12 - - - 9 - - - - - - 250 - 60 - - - - - 250 - 60 - - - - - - + + + + 4 + + + 12 + + + 9 + + + + + + 161 + 49 + + + + + 161 + 49 + + + + + + @@ -251,6 +251,11 @@ + + QSvgWidget + QWidget +
qsvgwidget.h
+
ClickableLabel QLabel diff --git a/Code/Sandbox/Editor/AssetEditor/AssetEditorWindow.cpp b/Code/Sandbox/Editor/AssetEditor/AssetEditorWindow.cpp index 4645019261..53e2884943 100644 --- a/Code/Sandbox/Editor/AssetEditor/AssetEditorWindow.cpp +++ b/Code/Sandbox/Editor/AssetEditor/AssetEditorWindow.cpp @@ -20,6 +20,7 @@ // AzCore #include #include +#include // AzToolsFramework #include @@ -104,13 +105,9 @@ void AssetEditorWindow::SaveAssetAs(const AZStd::string_view assetPath) return; } - const char* engineRoot; - AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath); + auto absoluteAssetPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / assetPath; - AZStd::string absoluteAssetPath; - AzFramework::StringFunc::Path::Join(engineRoot, assetPath.data(), absoluteAssetPath); - - if (!m_ui->m_assetEditorWidget->SaveAssetToPath(absoluteAssetPath)) + if (!m_ui->m_assetEditorWidget->SaveAssetToPath(absoluteAssetPath.Native())) { AZ_Warning("Asset Editor", false, "File was not saved correctly via SaveAssetAs."); } diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index a3b2542418..695a0fa5f1 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -1617,11 +1617,6 @@ void EditorViewportWidget::keyPressEvent(QKeyEvent* event) // because we want the movement to be butter smooth. if (!event->isAutoRepeat()) { - if (m_keyDown.isEmpty()) - { - grabKeyboard(); - } - m_keyDown.insert(event->key()); } diff --git a/Code/Sandbox/Editor/LegacyViewportCameraController.cpp b/Code/Sandbox/Editor/LegacyViewportCameraController.cpp index 44b722d222..7a33dff377 100644 --- a/Code/Sandbox/Editor/LegacyViewportCameraController.cpp +++ b/Code/Sandbox/Editor/LegacyViewportCameraController.cpp @@ -96,6 +96,11 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove( speedScale *= gSettings.cameraFastMoveSpeed; } + if (m_inMoveMode || m_inOrbitMode || m_inRotateMode || m_inZoomMode) + { + m_totalMouseMoveDelta += (QPoint(currentMousePos.m_x, currentMousePos.m_y)-QPoint(previousMousePos.m_x, previousMousePos.m_y)).manhattanLength(); + } + if ((m_inRotateMode && m_inMoveMode) || m_inZoomMode) { Matrix34 m = AZTransformToLYTransform(viewportContext->GetCameraTransform()); @@ -343,11 +348,15 @@ bool LegacyViewportCameraControllerInstance::HandleInputChannelEvent(const AzFra } shouldCaptureCursor = true; + // Record how much the cursor has been moved to see if we should own the mouse up event. + m_totalMouseMoveDelta = 0; } else if (state == InputChannel::State::Ended) { m_inZoomMode = false; m_inRotateMode = false; + // If we've moved the cursor more than a couple pixels, we should eat this mouse up event to prevent the context menu controller from seeing it. + shouldConsumeEvent = m_totalMouseMoveDelta > 2; shouldCaptureCursor = false; } } diff --git a/Code/Sandbox/Editor/LegacyViewportCameraController.h b/Code/Sandbox/Editor/LegacyViewportCameraController.h index 3f211f49b7..b4a36f44a5 100644 --- a/Code/Sandbox/Editor/LegacyViewportCameraController.h +++ b/Code/Sandbox/Editor/LegacyViewportCameraController.h @@ -58,6 +58,7 @@ namespace SandboxEditor bool m_inMoveMode = false; bool m_inOrbitMode = false; bool m_inZoomMode = false; + int m_totalMouseMoveDelta = 0; float m_orbitDistance = 10.f; float m_moveSpeed = 1.f; AZ::Vector3 m_orbitTarget = {}; diff --git a/Code/Sandbox/Editor/Objects/DisplayContextShared.inl b/Code/Sandbox/Editor/Objects/DisplayContextShared.inl index a4baf1fe89..dd5c248357 100644 --- a/Code/Sandbox/Editor/Objects/DisplayContextShared.inl +++ b/Code/Sandbox/Editor/Objects/DisplayContextShared.inl @@ -1245,28 +1245,6 @@ uint32 DisplayContext::SetState(uint32 state) return old; } -//! Set a new render state flags. -//! @param returns previous render state. -uint32 DisplayContext::SetStateFlag(uint32 state) -{ - uint32 old = m_renderState; - m_renderState |= state; - m_renderState = pRenderAuxGeom->GetRenderFlags().m_renderFlags; - pRenderAuxGeom->SetRenderFlags(m_renderState); - return old; -} - -//! Clear specified flags in render state. -//! @param returns previous render state. -uint32 DisplayContext::ClearStateFlag(uint32 state) -{ - uint32 old = m_renderState; - m_renderState &= ~state; - m_renderState = pRenderAuxGeom->GetRenderFlags().m_renderFlags; - pRenderAuxGeom->SetRenderFlags(m_renderState); - return old; -} - ////////////////////////////////////////////////////////////////////////// void DisplayContext::DepthTestOff() { diff --git a/Code/Sandbox/Editor/PythonEditorFuncs.cpp b/Code/Sandbox/Editor/PythonEditorFuncs.cpp index 5c74a950c2..f5de22387c 100644 --- a/Code/Sandbox/Editor/PythonEditorFuncs.cpp +++ b/Code/Sandbox/Editor/PythonEditorFuncs.cpp @@ -19,6 +19,8 @@ #include #include +#include + // AzToolsFramework #include #include @@ -293,9 +295,8 @@ namespace // If not found try editor folder if (!CFileUtil::FileExists(path)) { - const char* engineRoot = nullptr; - AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath); - QDir engineDir = engineRoot ? QDir(engineRoot) : QDir::current(); + AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath(); + QDir engineDir = !engineRoot.empty() ? QDir(QString(engineRoot.c_str())) : QDir::current(); QString scriptFolder = engineDir.absoluteFilePath("Editor/Scripts/"); Path::ConvertBackSlashToSlash(scriptFolder); diff --git a/Code/Sandbox/Editor/StartupLogoDialog.cpp b/Code/Sandbox/Editor/StartupLogoDialog.cpp index 38cf1bc5f6..c3d584b030 100644 --- a/Code/Sandbox/Editor/StartupLogoDialog.cpp +++ b/Code/Sandbox/Editor/StartupLogoDialog.cpp @@ -49,7 +49,7 @@ CStartupLogoDialog::CStartupLogoDialog(QString versionText, QString richTextCopy m_backgroundImage = QPixmap::fromImage(backgroundImage.scaled(m_enforcedWidth, m_enforcedHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Draw the Open 3D Engine logo from svg - m_ui->m_logo->load(QStringLiteral(":/StartupLogoDialog/lumberyard_logo.svg")); + m_ui->m_logo->load(QStringLiteral(":/StartupLogoDialog/o3de_logo.svg")); m_ui->m_TransparentConfidential->setObjectName("copyrightNotice"); m_ui->m_TransparentConfidential->setTextFormat(Qt::RichText); diff --git a/Code/Sandbox/Editor/StartupLogoDialog.qrc b/Code/Sandbox/Editor/StartupLogoDialog.qrc index 55c93b427c..29730ef9c7 100644 --- a/Code/Sandbox/Editor/StartupLogoDialog.qrc +++ b/Code/Sandbox/Editor/StartupLogoDialog.qrc @@ -1,6 +1,6 @@ - lumberyard_logo.svg + o3de_logo.svg splashscreen_1_27.png diff --git a/Code/Sandbox/Editor/StartupLogoDialog.ui b/Code/Sandbox/Editor/StartupLogoDialog.ui index f14355de8c..6e01808a84 100644 --- a/Code/Sandbox/Editor/StartupLogoDialog.ui +++ b/Code/Sandbox/Editor/StartupLogoDialog.ui @@ -42,14 +42,14 @@ - 250 - 60 + 161 + 49 - 250 - 60 + 161 + 50 diff --git a/Code/Sandbox/Editor/ToolBox.cpp b/Code/Sandbox/Editor/ToolBox.cpp index def7329e83..46c01ce864 100644 --- a/Code/Sandbox/Editor/ToolBox.cpp +++ b/Code/Sandbox/Editor/ToolBox.cpp @@ -18,6 +18,8 @@ #include "ToolBox.h" +#include + // AzToolsFramework #include #include @@ -419,9 +421,8 @@ void CToolBoxManager::Load(QString xmlpath, AmazonToolbar* pToolbar, bool bToolb } } - const char* engineRoot = nullptr; - AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath); - QDir engineDir = engineRoot ? QDir(engineRoot) : QDir::current(); + AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath(); + QDir engineDir = !engineRoot.empty() ? QDir(QString(engineRoot.c_str())) : QDir::current(); string enginePath = PathUtil::AddSlash(engineDir.absolutePath().toUtf8().data()); diff --git a/Code/Sandbox/Editor/lumberyard_logo.svg b/Code/Sandbox/Editor/lumberyard_logo.svg deleted file mode 100644 index fe5f2fbdcd..0000000000 --- a/Code/Sandbox/Editor/lumberyard_logo.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - background - - - - Layer 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Code/Sandbox/Editor/o3de_logo.svg b/Code/Sandbox/Editor/o3de_logo.svg new file mode 100644 index 0000000000..ac746c07a5 --- /dev/null +++ b/Code/Sandbox/Editor/o3de_logo.svg @@ -0,0 +1,22 @@ + + + Group 12 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index e5311dbea9..d9e3d9bb8c 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -2777,26 +2777,6 @@ AZ::u32 SandboxIntegrationManager::SetState(AZ::u32 state) return 0; } -AZ::u32 SandboxIntegrationManager::SetStateFlag(AZ::u32 state) -{ - if (m_dc) - { - return m_dc->SetStateFlag(state); - } - - return 0; -} - -AZ::u32 SandboxIntegrationManager::ClearStateFlag(AZ::u32 state) -{ - if (m_dc) - { - return m_dc->ClearStateFlag(state); - } - - return 0; -} - void SandboxIntegrationManager::PushMatrix(const AZ::Transform& tm) { if (m_dc) diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h index e35fb5087a..36767605b2 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h @@ -268,8 +268,6 @@ private: bool SetDrawInFrontMode(bool bOn) override; AZ::u32 GetState() override; AZ::u32 SetState(AZ::u32 state) override; - AZ::u32 SetStateFlag(AZ::u32 state) override; - AZ::u32 ClearStateFlag(AZ::u32 state) override; void PushMatrix(const AZ::Transform& tm) override; void PopMatrix() override; diff --git a/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationNotificationBusBehaviorHandler.h b/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationNotificationBusBehaviorHandler.h index c5e3c46cd4..c44327a5f2 100644 --- a/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationNotificationBusBehaviorHandler.h +++ b/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationNotificationBusBehaviorHandler.h @@ -12,6 +12,7 @@ #pragma once #include +#include namespace AWSClientAuth { @@ -28,74 +29,104 @@ namespace AWSClientAuth OnPasswordGrantMultiFactorConfirmSignInSuccess, OnPasswordGrantMultiFactorConfirmSignInFail, OnDeviceCodeGrantSignInSuccess, OnDeviceCodeGrantSignInFail, OnDeviceCodeGrantConfirmSignInSuccess, OnDeviceCodeGrantConfirmSignInFail, - OnRefreshTokensSuccess, OnRefreshTokensFail, - OnSignOut + OnRefreshTokensSuccess, OnRefreshTokensFail ); void OnPasswordGrantSingleFactorSignInSuccess(const AuthenticationTokens& authenticationToken) override { - Call(FN_OnPasswordGrantSingleFactorSignInSuccess, authenticationToken); + AZ::TickBus::QueueFunction([authenticationToken, this]() + { + Call(FN_OnPasswordGrantSingleFactorSignInSuccess, authenticationToken); + }); } void OnPasswordGrantSingleFactorSignInFail(const AZStd::string& error) override { - Call(FN_OnPasswordGrantSingleFactorSignInFail, error); + AZ::TickBus::QueueFunction([error, this]() + { + Call(FN_OnPasswordGrantSingleFactorSignInFail, error); + }); } void OnPasswordGrantMultiFactorSignInSuccess() override { - Call(FN_OnPasswordGrantMultiFactorSignInSuccess); + AZ::TickBus::QueueFunction([this]() + { + Call(FN_OnPasswordGrantMultiFactorSignInSuccess); + }); } void OnPasswordGrantMultiFactorSignInFail(const AZStd::string& error) override { - Call(FN_OnPasswordGrantMultiFactorSignInFail, error); + AZ::TickBus::QueueFunction([error, this]() + { + Call(FN_OnPasswordGrantMultiFactorSignInFail, error); + }); } void OnPasswordGrantMultiFactorConfirmSignInSuccess(const AuthenticationTokens& authenticationToken) override { - Call(FN_OnPasswordGrantMultiFactorConfirmSignInSuccess, authenticationToken); + AZ::TickBus::QueueFunction([authenticationToken, this]() + { + Call(FN_OnPasswordGrantMultiFactorConfirmSignInSuccess, authenticationToken); + }); } void OnPasswordGrantMultiFactorConfirmSignInFail(const AZStd::string& error) override { - Call(FN_OnPasswordGrantMultiFactorConfirmSignInFail, error); + AZ::TickBus::QueueFunction([error, this]() + { + Call(FN_OnPasswordGrantMultiFactorConfirmSignInFail, error); + }); } void OnDeviceCodeGrantSignInSuccess( const AZStd::string& userCode, const AZStd::string& verificationUrl, const int codeExpiresInSeconds) override { - Call(FN_OnDeviceCodeGrantSignInSuccess, userCode, verificationUrl, codeExpiresInSeconds); + AZ::TickBus::QueueFunction([userCode, verificationUrl, codeExpiresInSeconds, this]() + { + Call(FN_OnDeviceCodeGrantSignInSuccess, userCode, verificationUrl, codeExpiresInSeconds); + }); } void OnDeviceCodeGrantSignInFail(const AZStd::string& error) override { - Call(FN_OnDeviceCodeGrantSignInFail, error); + AZ::TickBus::QueueFunction([error, this]() + { + Call(FN_OnDeviceCodeGrantSignInFail, error); + }); } void OnDeviceCodeGrantConfirmSignInSuccess(const AuthenticationTokens& authenticationToken) override { - Call(FN_OnDeviceCodeGrantConfirmSignInSuccess, authenticationToken); + AZ::TickBus::QueueFunction([authenticationToken, this]() + { + Call(FN_OnDeviceCodeGrantConfirmSignInSuccess, authenticationToken); + }); } void OnDeviceCodeGrantConfirmSignInFail(const AZStd::string& error) override { - Call(FN_OnDeviceCodeGrantConfirmSignInFail, error); + AZ::TickBus::QueueFunction([error, this]() + { + Call(FN_OnDeviceCodeGrantConfirmSignInFail, error); + }); } void OnRefreshTokensSuccess(const AuthenticationTokens& authenticationToken) override { - Call(FN_OnRefreshTokensSuccess, authenticationToken); + AZ::TickBus::QueueFunction([authenticationToken, this]() + { + Call(FN_OnRefreshTokensSuccess, authenticationToken); + }); } void OnRefreshTokensFail(const AZStd::string& error) override { - Call(FN_OnRefreshTokensFail, error); - } - - void OnSignOut(const ProviderNameEnum& provideName) override - { - Call(FN_OnSignOut, provideName); + AZ::TickBus::QueueFunction([error, this]() + { + Call(FN_OnRefreshTokensFail, error); + }); } }; } // namespace AWSClientAuth diff --git a/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderManager.h b/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderManager.h index a9a2f2ad6e..a09a96ba5c 100644 --- a/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderManager.h +++ b/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderManager.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -23,7 +24,8 @@ namespace AWSClientAuth { //! Manages various authentication provider implementations and implements AuthenticationProvider Request bus. class AuthenticationProviderManager - : AuthenticationProviderRequestBus::Handler + : public AuthenticationProviderRequestBus::Handler + , public AuthenticationProviderScriptCanvasRequestBus::Handler { public: AZ_RTTI(AuthenticationProviderManager, "{45813BA5-9A46-4A2A-A923-C79CFBA0E63D}", IAuthenticationProviderRequests); @@ -43,6 +45,22 @@ namespace AWSClientAuth bool IsSignedIn(const ProviderNameEnum& providerName) override; bool SignOut(const ProviderNameEnum& providerName) override; AuthenticationTokens GetAuthenticationTokens(const ProviderNameEnum& providerName) override; + + // AuthenticationProviderScriptCanvasRequest interface + bool Initialize(const AZStd::vector& providerNames, const AZStd::string& settingsRegistryPath) override; + void PasswordGrantSingleFactorSignInAsync( + const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& password) override; + void PasswordGrantMultiFactorSignInAsync( + const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& password) override; + void PasswordGrantMultiFactorConfirmSignInAsync( + const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& confirmationCode) override; + void DeviceCodeGrantSignInAsync(const AZStd::string& providerName) override; + void DeviceCodeGrantConfirmSignInAsync(const AZStd::string& providerName) override; + void RefreshTokensAsync(const AZStd::string& providerName) override; + void GetTokensWithRefreshAsync(const AZStd::string& providerName) override; + bool IsSignedIn(const AZStd::string& providerName) override; + bool SignOut(const AZStd::string& providerName) override; + AuthenticationTokens GetAuthenticationTokens(const AZStd::string& providerName) override; virtual AZStd::unique_ptr CreateAuthenticationProviderObject(const ProviderNameEnum& providerName); AZStd::map> m_authenticationProvidersMap; @@ -50,9 +68,9 @@ namespace AWSClientAuth private: bool IsProviderInitialized(const ProviderNameEnum& providerName); void ResetProviders(); + ProviderNameEnum GetProviderNameEnum(AZStd::string name); AZStd::shared_ptr m_settingsRegistry; - }; } // namespace AWSClientAuth diff --git a/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderScriptCanvasBus.h b/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderScriptCanvasBus.h new file mode 100644 index 0000000000..9ce508b497 --- /dev/null +++ b/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderScriptCanvasBus.h @@ -0,0 +1,103 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include + +namespace AWSClientAuth +{ + //! Abstract class for authentication provider script canvas requests. + //! Private class to allow provide names to be string type instead of an enum as behavior context does not work well with enum's. + class IAuthenticationProviderScriptCanvasRequests + { + public: + AZ_TYPE_INFO(IAuthenticationProviderRequests, "{A8FD915F-9FF2-4BA3-8AA0-8CF7A94A323B}"); + + //! Parse the settings file for required settings for authentication providers. Instantiate and initialize authentication providers + //! @param providerNames List of provider names to instantiate and initialize for Authentication. + //! @param settingsRegistryPath Path for the settings registry file to use to configure providers. + //! @return bool True: if all providers initialized successfully. False: If any provider fails initialization. + virtual bool Initialize(const AZStd::vector& providerNames, const AZStd::string& settingsRegistryPath) = 0; + + //! Checks if user is signed in. + //! If access tokens are available and not expired. + //! @param providerName Provider to check signed in for + //! @return bool True if valid access token available, else False + virtual bool IsSignedIn(const AZStd::string& providerName) = 0; + + //! Get cached tokens from last last successful sign-in for the provider. + //! @param providerName Provider to get authentication tokens + //! @return AuthenticationTokens tokens from successful authentication. + virtual AuthenticationTokens GetAuthenticationTokens(const AZStd::string& providerName) = 0; + + // Below methods have corresponding notifications for success and failures. + + //! Call sign in endpoint for provider password grant flow. + //! @param providerName Provider to call sign in. + //! @param username Username to use to for sign in. + //! @param password Password to use to for sign in. + virtual void PasswordGrantSingleFactorSignInAsync(const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& password) = 0; + + //! Call sign in endpoint for provider password grant multi factor authentication flow. + //! @param providerName Provider to call MFA sign in. + //! @param username Username to use for MFA sign in. + //! @param password Password to use for MFA sign in. + virtual void PasswordGrantMultiFactorSignInAsync(const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& password) = 0; + + //! Call confirm endpoint for provider password grant multi factor authentication flow . + //! @param providerName Provider to call MFA confirm sign in. + //! @param username Username to use for MFA confirm. + //! @param confirmationCode Confirmation code (sent to email/text) to use for MFA confirm. + virtual void PasswordGrantMultiFactorConfirmSignInAsync(const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& confirmationCode) = 0; + + //! Call code-pair endpoint for provider device grant flow. + //! @param providerName Provider to call device sign in. + virtual void DeviceCodeGrantSignInAsync(const AZStd::string& providerName) = 0; + + //! Call tokens endpoint for provider device grant flow. + //! @param providerName Provider to call device confirm sign in. + virtual void DeviceCodeGrantConfirmSignInAsync(const AZStd::string& providerName) = 0; + + //! Call refresh endpoint for provider refresh grant flow. + //! @param providerName Provider to call refresh tokens. + virtual void RefreshTokensAsync(const AZStd::string& providerName) = 0; + + //! Call refresh token if token not valid. If token valid, fires corresponding event. + //! @param providerName Provider to get access token for. + //! events: OnRefreshTokensSuccess, OnRefreshTokensFail + virtual void GetTokensWithRefreshAsync(const AZStd::string& providerName) = 0; + + //! Signs user out. + //! Clears all cached tokens. + //! @param providerName Provider to sign out. + //! @return bool True: Successfully sign out. + virtual bool SignOut(const AZStd::string& providerName) = 0; + + ////////////////////////////////////////////////////////////////////////// + }; + + //! Authentication Request bus for different supported providers. + class AuthenticationProviderScriptCanvasRequests + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + using MutexType = AZ::NullMutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + using AuthenticationProviderScriptCanvasRequestBus = AZ::EBus; + +} // namespace AWSClientAuth diff --git a/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderTypes.h b/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderTypes.h index 1e10e66307..8bd31d6aeb 100644 --- a/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderTypes.h +++ b/Gems/AWSClientAuth/Code/Include/Private/Authentication/AuthenticationProviderTypes.h @@ -15,6 +15,14 @@ namespace AWSClientAuth { + constexpr char ProvideNameEnumStringNone[] = "None"; + constexpr char ProvideNameEnumStringAWSCognitoIDP[] = "AWSCognitoIDP"; + constexpr char ProvideNameEnumStringLoginWithAmazon[] = "LoginWithAmazon"; + constexpr char ProvideNameEnumStringGoogle[] = "Google"; + constexpr char ProvideNameEnumStringApple[] = "Apple"; + constexpr char ProvideNameEnumStringFacebook[] = "Facebook"; + constexpr char ProvideNameEnumStringTwitch[] = "Twitch"; + //! Holds Login with Amazon provider serialized settings class LWAProviderSetting { diff --git a/Gems/AWSClientAuth/Code/Include/Private/Authorization/AWSCognitoAuthorizationNotificationBusBehaviorHandler.h b/Gems/AWSClientAuth/Code/Include/Private/Authorization/AWSCognitoAuthorizationNotificationBusBehaviorHandler.h index f690445c26..e26896fa06 100644 --- a/Gems/AWSClientAuth/Code/Include/Private/Authorization/AWSCognitoAuthorizationNotificationBusBehaviorHandler.h +++ b/Gems/AWSClientAuth/Code/Include/Private/Authorization/AWSCognitoAuthorizationNotificationBusBehaviorHandler.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include namespace AWSClientAuth @@ -28,12 +29,18 @@ namespace AWSClientAuth void OnRequestAWSCredentialsSuccess(const ClientAuthAWSCredentials& awsCredentials) override { - Call(FN_OnRequestAWSCredentialsSuccess, awsCredentials); + AZ::TickBus::QueueFunction([awsCredentials, this]() + { + Call(FN_OnRequestAWSCredentialsSuccess, awsCredentials); + }); } void OnRequestAWSCredentialsFail(const AZStd::string& error) override { - Call(FN_OnRequestAWSCredentialsFail, error); + AZ::TickBus::QueueFunction([error, this]() + { + Call(FN_OnRequestAWSCredentialsFail, error); + }); } }; } // namespace AWSClientAuth diff --git a/Gems/AWSClientAuth/Code/Include/Private/UserManagement/UserManagementNotificationBusBehaviorHandler.h b/Gems/AWSClientAuth/Code/Include/Private/UserManagement/UserManagementNotificationBusBehaviorHandler.h index 06f2ae6e58..f5c2ca7ed1 100644 --- a/Gems/AWSClientAuth/Code/Include/Private/UserManagement/UserManagementNotificationBusBehaviorHandler.h +++ b/Gems/AWSClientAuth/Code/Include/Private/UserManagement/UserManagementNotificationBusBehaviorHandler.h @@ -12,6 +12,7 @@ #pragma once #include +#include namespace AWSClientAuth { @@ -32,62 +33,86 @@ namespace AWSClientAuth void OnEmailSignUpSuccess(const AZStd::string& uuid) override { - Call(FN_OnEmailSignUpSuccess, uuid); + AZ::TickBus::QueueFunction([uuid, this]() { + Call(FN_OnEmailSignUpSuccess, uuid); + }); } void OnEmailSignUpFail(const AZStd::string& error) override { - Call(FN_OnEmailSignUpFail, error); + AZ::TickBus::QueueFunction([error, this]() { + Call(FN_OnEmailSignUpFail, error); + }); } void OnPhoneSignUpSuccess(const AZStd::string& uuid) override { - Call(FN_OnPhoneSignUpSuccess, uuid); + AZ::TickBus::QueueFunction([uuid, this]() { + Call(FN_OnPhoneSignUpSuccess, uuid); + }); } void OnPhoneSignUpFail(const AZStd::string& error) override { - Call(FN_OnPhoneSignUpFail, error); + AZ::TickBus::QueueFunction([error, this]() { + Call(FN_OnPhoneSignUpFail, error); + }); } void OnConfirmSignUpSuccess() override { - Call(FN_OnConfirmSignUpSuccess); + AZ::TickBus::QueueFunction([this]() { + Call(FN_OnConfirmSignUpSuccess); + }); } void OnConfirmSignUpFail(const AZStd::string& error) override { - Call(FN_OnConfirmSignUpFail, error); + AZ::TickBus::QueueFunction([error, this]() { + Call(FN_OnConfirmSignUpFail, error); + }); } void OnForgotPasswordSuccess() override { - Call(FN_OnForgotPasswordSuccess); + AZ::TickBus::QueueFunction([this]() { + Call(FN_OnForgotPasswordSuccess); + }); } void OnForgotPasswordFail(const AZStd::string& error) override { - Call(FN_OnForgotPasswordFail, error); + AZ::TickBus::QueueFunction([error, this]() { + Call(FN_OnForgotPasswordFail, error); + }); } void OnConfirmForgotPasswordSuccess() override { - Call(FN_OnConfirmForgotPasswordSuccess); + AZ::TickBus::QueueFunction([this]() { + Call(FN_OnConfirmForgotPasswordSuccess); + }); } void OnConfirmForgotPasswordFail(const AZStd::string& error) override { - Call(FN_OnConfirmForgotPasswordFail, error); + AZ::TickBus::QueueFunction([error, this]() { + Call(FN_OnConfirmForgotPasswordFail, error); + }); } void OnEnableMFASuccess() override { - Call(FN_OnEnableMFASuccess); + AZ::TickBus::QueueFunction([this]() { + Call(FN_OnEnableMFASuccess); + }); } void OnEnableMFAFail(const AZStd::string& error) override { - Call(FN_OnEnableMFAFail, error); + AZ::TickBus::QueueFunction([error, this]() { + Call(FN_OnEnableMFAFail, error); + }); } }; } // namespace AWSClientAuth diff --git a/Gems/AWSClientAuth/Code/Include/Public/Authentication/AuthenticationProviderBus.h b/Gems/AWSClientAuth/Code/Include/Public/Authentication/AuthenticationProviderBus.h index 693adf1e65..9e822849bc 100644 --- a/Gems/AWSClientAuth/Code/Include/Public/Authentication/AuthenticationProviderBus.h +++ b/Gems/AWSClientAuth/Code/Include/Public/Authentication/AuthenticationProviderBus.h @@ -16,7 +16,7 @@ namespace AWSClientAuth { - //@ Abstract class for authentication provider requests. + //! Abstract class for authentication provider requests. class IAuthenticationProviderRequests { public: @@ -35,32 +35,40 @@ namespace AWSClientAuth virtual bool IsSignedIn(const ProviderNameEnum& providerName) = 0; //! Get cached tokens from last last successful sign-in for the provider. + //! @param providerName Provider to get authentication tokens. + //! @return AuthenticationTokens tokens from successful authentication. virtual AuthenticationTokens GetAuthenticationTokens(const ProviderNameEnum& providerName) = 0; // Below methods have corresponding notifications for success and failures. //! Call sign in endpoint for provider password grant flow. + //! @param providerName Provider to call sign in. //! @param username Username to use to for sign in. //! @param password Password to use to for sign in. virtual void PasswordGrantSingleFactorSignInAsync(const ProviderNameEnum& providerName, const AZStd::string& username, const AZStd::string& password) = 0; //! Call sign in endpoint for provider password grant multi factor authentication flow. + //! @param providerName Provider to call MFA sign in. //! @param username Username to use for MFA sign in. //! @param password Password to use for MFA sign in. virtual void PasswordGrantMultiFactorSignInAsync(const ProviderNameEnum& providerName, const AZStd::string& username, const AZStd::string& password) = 0; //! Call confirm endpoint for provider password grant multi factor authentication flow . + //! @param providerName Provider to call MFA confirm sign in. //! @param username Username to use for MFA confirm. //! @param confirmationCode Confirmation code (sent to email/text) to use for MFA confirm. virtual void PasswordGrantMultiFactorConfirmSignInAsync(const ProviderNameEnum& providerName, const AZStd::string& username, const AZStd::string& confirmationCode) = 0; //! Call code-pair endpoint for provider device grant flow. + //! @param providerName Provider to call device sign in. virtual void DeviceCodeGrantSignInAsync(const ProviderNameEnum& providerName) = 0; //! Call tokens endpoint for provider device grant flow. + //! @param providerName Provider to call device confirm sign in. virtual void DeviceCodeGrantConfirmSignInAsync(const ProviderNameEnum& providerName) = 0; //! Call refresh endpoint for provider refresh grant flow. + //! @param providerName Provider to call refresh tokens. virtual void RefreshTokensAsync(const ProviderNameEnum& providerName) = 0; //! Call refresh token if token not valid. If token valid, fires corresponding event. diff --git a/Gems/AWSClientAuth/Code/Include/Public/Authentication/AuthenticationTokens.h b/Gems/AWSClientAuth/Code/Include/Public/Authentication/AuthenticationTokens.h index 80138efd1b..3bb781f262 100644 --- a/Gems/AWSClientAuth/Code/Include/Public/Authentication/AuthenticationTokens.h +++ b/Gems/AWSClientAuth/Code/Include/Public/Authentication/AuthenticationTokens.h @@ -11,20 +11,15 @@ */ #pragma once +#include #include #include +#include +#include namespace AWSClientAuth { - enum class ProviderNameEnum - { - None, - AWSCognitoIDP, - LoginWithAmazon, - Google, - Apple, - Facebook - }; + AZ_ENUM_CLASS(ProviderNameEnum, None, AWSCognitoIDP, LoginWithAmazon, Twitch, Google, Apple, Facebook); //! Used to share authentication tokens to caller and to AWSCognitoAuthorizationController. class AuthenticationTokens @@ -55,6 +50,8 @@ namespace AWSClientAuth //! @return Expiration time in seconds. int GetTokensExpireTimeSeconds() const; + static void Reflect(AZ::ReflectContext* context); + private: int m_tokensExpireTimeSeconds = 0; AZStd::string m_accessToken; diff --git a/Gems/AWSClientAuth/Code/Include/Public/Authorization/ClientAuthAWSCredentials.h b/Gems/AWSClientAuth/Code/Include/Public/Authorization/ClientAuthAWSCredentials.h index 71a5703efd..2daac42a49 100644 --- a/Gems/AWSClientAuth/Code/Include/Public/Authorization/ClientAuthAWSCredentials.h +++ b/Gems/AWSClientAuth/Code/Include/Public/Authorization/ClientAuthAWSCredentials.h @@ -12,7 +12,7 @@ #pragma once -#include +#include #include #include @@ -24,6 +24,14 @@ namespace AWSClientAuth { public: AZ_TYPE_INFO(ClientAuthAWSCredentials, "{02FB32C4-B94E-4084-9049-3DF32F87BD76}"); + ClientAuthAWSCredentials() = default; + ClientAuthAWSCredentials(const ClientAuthAWSCredentials& other) + : m_accessKeyId(other.m_accessKeyId) + , m_secretKey(other.m_secretKey) + , m_sessionToken(other.m_sessionToken) + + { + } ClientAuthAWSCredentials(const AZStd::string& accessKeyId, const AZStd::string& secretKey, const AZStd::string& sessionToken) { @@ -50,6 +58,32 @@ namespace AWSClientAuth return m_sessionToken; } + static void Reflect(AZ::ReflectContext* context) + { + auto serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Field("AWSAccessKeyId", &ClientAuthAWSCredentials::m_accessKeyId) + ->Field("AWSSecretKey", &ClientAuthAWSCredentials::m_secretKey) + ->Field("AWSSessionToken", &ClientAuthAWSCredentials::m_sessionToken); + } + + AZ::BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Category, "AWSClientAuth") + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Constructor() + ->Constructor() + ->Property("AWSAccessKeyId", BehaviorValueGetter(&ClientAuthAWSCredentials::m_accessKeyId), BehaviorValueSetter(&ClientAuthAWSCredentials::m_accessKeyId)) + ->Property("AWSSecretKey", BehaviorValueGetter(&ClientAuthAWSCredentials::m_secretKey), BehaviorValueSetter(&ClientAuthAWSCredentials::m_secretKey)) + ->Property("AWSSessionToken", BehaviorValueGetter(&ClientAuthAWSCredentials::m_sessionToken), BehaviorValueSetter(&ClientAuthAWSCredentials::m_sessionToken)); + } + } + private: AZStd::string m_accessKeyId; AZStd::string m_secretKey; diff --git a/Gems/AWSClientAuth/Code/Include/Public/UserManagement/AWSCognitoUserManagementBus.h b/Gems/AWSClientAuth/Code/Include/Public/UserManagement/AWSCognitoUserManagementBus.h index 89ddf0a999..aff68db365 100644 --- a/Gems/AWSClientAuth/Code/Include/Public/UserManagement/AWSCognitoUserManagementBus.h +++ b/Gems/AWSClientAuth/Code/Include/Public/UserManagement/AWSCognitoUserManagementBus.h @@ -22,11 +22,12 @@ namespace AWSClientAuth public: AZ_TYPE_INFO(IAWSCognitoUserManagementRequests, "{A4C90F21-7056-4827-8C6B-401E6945697D}"); - //! Initialize Cognito User pool. + //! Initialize Cognito User pool using settings from resource mappings. //! @param settingsRegistryPath settingsRegistryPath Path for the settings registry file to use. virtual bool Initialize() = 0; // Requests interface + //! Cognito user pool email sign up start. //! @param username User name to use for sign up. //! @param password Password to use for sign up. @@ -59,7 +60,7 @@ namespace AWSClientAuth virtual void EnableMFAAsync(const AZStd::string& accessToken) = 0; }; - //! Manages various authentication provider implementations and implements AuthenticationProvider Request bus. + //! Implements AWS Cognito user pool user management requests. class AWSCognitoUserManagementRequests : public AZ::EBusTraits { diff --git a/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp b/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp index 20efa354cc..008d56da0b 100644 --- a/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp +++ b/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp @@ -22,6 +22,11 @@ #include #include +namespace AZ +{ + AZ_TYPE_INFO_SPECIALIZE(AWSClientAuth::ProviderNameEnum, "{FB34B23A-B249-47A2-B1F1-C05284B50CCC}"); +} + namespace AWSClientAuth { constexpr char SerializeComponentName[] = "AWSClientAuth"; @@ -44,20 +49,35 @@ namespace AWSClientAuth AWSClientAuth::GoogleProviderSetting::Reflect(*serialize); } + AWSClientAuth::AuthenticationTokens::Reflect(context); + AWSClientAuth::ClientAuthAWSCredentials::Reflect(context); + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - behaviorContext->EBus("AuthenticationProviderRequestBus") + behaviorContext->Enum<(int)ProviderNameEnum::None>("ProviderNameEnum_None") + ->Enum<(int)ProviderNameEnum::AWSCognitoIDP>("ProviderNameEnum_AWSCognitoIDP") + ->Enum<(int)ProviderNameEnum::LoginWithAmazon>("ProviderNameEnum_LoginWithAmazon") + ->Enum<(int)ProviderNameEnum::Google>("ProviderNameEnum_Google"); + + behaviorContext->EBus("AuthenticationProviderRequestBus") ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) - ->Event("Initialize", &AuthenticationProviderRequestBus::Events::Initialize) - ->Event("IsSignedIn", &AuthenticationProviderRequestBus::Events::IsSignedIn) - ->Event("GetAuthenticationTokens", &AuthenticationProviderRequestBus::Events::GetAuthenticationTokens) + ->Event("Initialize", &AuthenticationProviderScriptCanvasRequestBus::Events::Initialize) + ->Event("IsSignedIn", &AuthenticationProviderScriptCanvasRequestBus::Events::IsSignedIn) + ->Event("GetAuthenticationTokens", &AuthenticationProviderScriptCanvasRequestBus::Events::GetAuthenticationTokens) + ->Event( + "PasswordGrantSingleFactorSignInAsync", + &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantSingleFactorSignInAsync) ->Event( - "PasswordGrantSingleFactorSignInAsync", &AuthenticationProviderRequestBus::Events::PasswordGrantSingleFactorSignInAsync) - ->Event("DeviceCodeGrantSignInAsync", &AuthenticationProviderRequestBus::Events::DeviceCodeGrantSignInAsync) - ->Event("DeviceCodeGrantConfirmSignInAsync", &AuthenticationProviderRequestBus::Events::DeviceCodeGrantConfirmSignInAsync) - ->Event("RefreshTokensAsync", &AuthenticationProviderRequestBus::Events::RefreshTokensAsync) - ->Event("GetTokensWithRefreshAsync", &AuthenticationProviderRequestBus::Events::GetTokensWithRefreshAsync) - ->Event("SignOut", &AuthenticationProviderRequestBus::Events::SignOut); + "PasswordGrantMultiFactorSignInAsync", + &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantMultiFactorSignInAsync) + ->Event( + "PasswordGrantMultiFactorConfirmSignInAsync", + &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantMultiFactorConfirmSignInAsync) + ->Event("DeviceCodeGrantSignInAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::DeviceCodeGrantSignInAsync) + ->Event("DeviceCodeGrantConfirmSignInAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::DeviceCodeGrantConfirmSignInAsync) + ->Event("RefreshTokensAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::RefreshTokensAsync) + ->Event("GetTokensWithRefreshAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::GetTokensWithRefreshAsync) + ->Event("SignOut", &AuthenticationProviderScriptCanvasRequestBus::Events::SignOut); behaviorContext->EBus("AWSCognitoAuthorizationRequestBus") ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) @@ -77,11 +97,15 @@ namespace AWSClientAuth ->Event("ConfirmForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmForgotPasswordAsync) ->Event("EnableMFAAsync", &AWSCognitoUserManagementRequestBus::Events::EnableMFAAsync); + behaviorContext->EBus("AuthenticationProviderNotificationBus") + ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) ->Handler(); behaviorContext->EBus("AWSCognitoUserManagementNotificationBus") + ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) ->Handler(); behaviorContext->EBus("AWSCognitoAuthorizationNotificationBus") + ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) ->Handler(); } } diff --git a/Gems/AWSClientAuth/Code/Source/Authentication/AWSCognitoAuthenticationProvider.cpp b/Gems/AWSClientAuth/Code/Source/Authentication/AWSCognitoAuthenticationProvider.cpp index eca5ba22c2..74865c0044 100644 --- a/Gems/AWSClientAuth/Code/Source/Authentication/AWSCognitoAuthenticationProvider.cpp +++ b/Gems/AWSClientAuth/Code/Source/Authentication/AWSCognitoAuthenticationProvider.cpp @@ -40,7 +40,7 @@ namespace AWSClientAuth AZ_UNUSED(settingsRegistry); AWSCore::AWSResourceMappingRequestBus::BroadcastResult( m_cognitoAppClientId, &AWSCore::AWSResourceMappingRequests::GetResourceNameId, CognitoAppClientIdResourceMappingKey); - AZ_Warning("AWSCognitoAuthenticationProvider", m_cognitoAppClientId.empty(), "Missing Cognito App Client Id from resource mappings. Calls to Cognito will fail."); + AZ_Warning("AWSCognitoAuthenticationProvider", !m_cognitoAppClientId.empty(), "Missing Cognito App Client Id from resource mappings. Calls to Cognito will fail."); return !m_cognitoAppClientId.empty(); } diff --git a/Gems/AWSClientAuth/Code/Source/Authentication/AuthenticationProviderManager.cpp b/Gems/AWSClientAuth/Code/Source/Authentication/AuthenticationProviderManager.cpp index 76d7c45c71..f6e5efd106 100644 --- a/Gems/AWSClientAuth/Code/Source/Authentication/AuthenticationProviderManager.cpp +++ b/Gems/AWSClientAuth/Code/Source/Authentication/AuthenticationProviderManager.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -25,12 +26,14 @@ namespace AWSClientAuth { AZ::Interface::Register(this); AuthenticationProviderRequestBus::Handler::BusConnect(); + AuthenticationProviderScriptCanvasRequestBus::Handler::BusConnect(); } AuthenticationProviderManager::~AuthenticationProviderManager() { ResetProviders(); m_settingsRegistry.reset(); + AuthenticationProviderScriptCanvasRequestBus::Handler::BusDisconnect(); AuthenticationProviderRequestBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); } @@ -38,12 +41,19 @@ namespace AWSClientAuth bool AuthenticationProviderManager::Initialize(const AZStd::vector& providerNames, const AZStd::string& settingsRegistryPath) { ResetProviders(); + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ_Assert(fileIO, "File IO is not initialized."); + m_settingsRegistry.reset(); m_settingsRegistry = AZStd::make_shared(); - if (!m_settingsRegistry->MergeSettingsFile(settingsRegistryPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch)) + AZStd::array resolvedPath{}; + AZ::IO::FileIOBase::GetInstance()->ResolvePath(settingsRegistryPath.data(), resolvedPath.data(), resolvedPath.size()); + + + if (!m_settingsRegistry->MergeSettingsFile(resolvedPath.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch)) { - AZ_Error("AuthenticationProviderManager", true, "Error merging settings registry for path: %s", settingsRegistryPath.c_str()); + AZ_Error("AuthenticationProviderManager", true, "Error merging settings registry for path: %s", resolvedPath.data()); return false; } @@ -112,6 +122,7 @@ namespace AWSClientAuth { AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensFail , "Provider is not initialized"); + return; } AuthenticationTokens tokens = m_authenticationProvidersMap[providerName]->GetAuthenticationTokens(); @@ -181,5 +192,77 @@ namespace AWSClientAuth } } + ProviderNameEnum AuthenticationProviderManager::GetProviderNameEnum(AZStd::string name) + { + auto enumValue = ProviderNameEnumNamespace::FromStringToProviderNameEnum(name); + if (enumValue.has_value()) + { + return enumValue.value(); + } + AZ_Warning("AuthenticationProviderManager", true, "Incorrect string value for enum: %s", name.c_str()); + return ProviderNameEnum::None; + } + + bool AuthenticationProviderManager::Initialize( + const AZStd::vector& providerNames, const AZStd::string& settingsRegistryPath) + { + AZStd::vector providerNamesEnum; + for (auto name : providerNames) + { + providerNamesEnum.push_back(GetProviderNameEnum(name)); + } + return Initialize(providerNamesEnum, settingsRegistryPath); + } + + void AuthenticationProviderManager::PasswordGrantSingleFactorSignInAsync(const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& password) + { + PasswordGrantSingleFactorSignInAsync(GetProviderNameEnum(providerName), username, password); + } + + void AuthenticationProviderManager::PasswordGrantMultiFactorSignInAsync(const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& password) + { + PasswordGrantMultiFactorSignInAsync(GetProviderNameEnum(providerName), username, password); + } + + void AuthenticationProviderManager::PasswordGrantMultiFactorConfirmSignInAsync(const AZStd::string& providerName, const AZStd::string& username, const AZStd::string& confirmationCode) + { + PasswordGrantMultiFactorConfirmSignInAsync(GetProviderNameEnum(providerName), username, confirmationCode); + } + + void AuthenticationProviderManager::DeviceCodeGrantSignInAsync(const AZStd::string& providerName) + { + DeviceCodeGrantSignInAsync(GetProviderNameEnum(providerName)); + } + + void AuthenticationProviderManager::DeviceCodeGrantConfirmSignInAsync(const AZStd::string& providerName) + { + DeviceCodeGrantConfirmSignInAsync(GetProviderNameEnum(providerName)); + } + + void AuthenticationProviderManager::RefreshTokensAsync(const AZStd::string& providerName) + { + RefreshTokensAsync(GetProviderNameEnum(providerName)); + } + + void AuthenticationProviderManager::GetTokensWithRefreshAsync(const AZStd::string& providerName) + { + GetTokensWithRefreshAsync(GetProviderNameEnum(providerName)); + } + + bool AuthenticationProviderManager::IsSignedIn(const AZStd::string& providerName) + { + return IsSignedIn(GetProviderNameEnum(providerName)); + } + + bool AuthenticationProviderManager::SignOut(const AZStd::string& providerName) + { + return SignOut(GetProviderNameEnum(providerName)); + } + + AuthenticationTokens AuthenticationProviderManager::GetAuthenticationTokens(const AZStd::string& providerName) + { + return GetAuthenticationTokens(GetProviderNameEnum(providerName)); + } + } // namespace AWSClientAuth diff --git a/Gems/AWSClientAuth/Code/Source/Authentication/AuthenticationTokens.cpp b/Gems/AWSClientAuth/Code/Source/Authentication/AuthenticationTokens.cpp index 9c078ad99c..737d6ae929 100644 --- a/Gems/AWSClientAuth/Code/Source/Authentication/AuthenticationTokens.cpp +++ b/Gems/AWSClientAuth/Code/Source/Authentication/AuthenticationTokens.cpp @@ -79,4 +79,30 @@ namespace AWSClientAuth { return m_tokensExpireTimeSeconds; } + + void AuthenticationTokens::Reflect(AZ::ReflectContext* context) + { + auto serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Field("AccessToken", &AuthenticationTokens::m_accessToken) + ->Field("OpenIdToken", &AuthenticationTokens::m_openIdToken) + ->Field("RefreshToken", &AuthenticationTokens::m_refreshToken); + } + + AZ::BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Category, "AWSClientAuth") + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Constructor() + ->Constructor() + ->Property("AccessToken", BehaviorValueGetter(&AuthenticationTokens::m_accessToken), BehaviorValueSetter(&AuthenticationTokens::m_accessToken)) + ->Property("OpenIdToken", BehaviorValueGetter(&AuthenticationTokens::m_openIdToken), BehaviorValueSetter(&AuthenticationTokens::m_accessToken)) + ->Property("RefreshToken", BehaviorValueGetter(&AuthenticationTokens::m_refreshToken), BehaviorValueSetter(&AuthenticationTokens::m_accessToken)); + } + } } // namespace AWSClientAuth diff --git a/Gems/AWSClientAuth/Code/Source/Authorization/AWSCognitoAuthorizationController.cpp b/Gems/AWSClientAuth/Code/Source/Authorization/AWSCognitoAuthorizationController.cpp index 4c10d968fc..5e2c07bdbb 100644 --- a/Gems/AWSClientAuth/Code/Source/Authorization/AWSCognitoAuthorizationController.cpp +++ b/Gems/AWSClientAuth/Code/Source/Authorization/AWSCognitoAuthorizationController.cpp @@ -71,15 +71,15 @@ namespace AWSClientAuth if (m_awsAccountId.empty() || m_cognitoIdentityPoolId.empty()) { - AZ_Warning("AWSCognitoUserManagementController", m_awsAccountId.empty(), "Missing AWS account id in resource mappings."); - AZ_Warning("AWSCognitoUserManagementController", m_cognitoIdentityPoolId.empty(), "Missing Cognito Identity pool id in resource mappings."); + AZ_Warning("AWSCognitoAuthorizationController", !m_awsAccountId.empty(), "Missing AWS account id not configured."); + AZ_Warning("AWSCognitoAuthorizationController", !m_cognitoIdentityPoolId.empty(), "Missing Cognito Identity pool id in resource mappings."); return false; } AZStd::string userPoolId; AWSCore::AWSResourceMappingRequestBus::BroadcastResult( userPoolId, &AWSCore::AWSResourceMappingRequests::GetResourceNameId, CognitoUserPoolIdResourceMappingKey); - AZ_Warning("AWSCognitoUserManagementController", userPoolId.empty(), "Missing Cognito USer pool id in resource mappings. Cognito IDP authenticated identities will no work."); + AZ_Warning("AWSCognitoAuthorizationController", !userPoolId.empty(), "Missing Cognito User pool id in resource mappings. Cognito IDP authenticated identities will no work."); AZStd::string defaultRegion; AWSCore::AWSResourceMappingRequestBus::BroadcastResult( diff --git a/Gems/AWSClientAuth/Code/Source/UserManagement/AWSCognitoUserManagementController.cpp b/Gems/AWSClientAuth/Code/Source/UserManagement/AWSCognitoUserManagementController.cpp index 19d5d47d33..755b0e1f15 100644 --- a/Gems/AWSClientAuth/Code/Source/UserManagement/AWSCognitoUserManagementController.cpp +++ b/Gems/AWSClientAuth/Code/Source/UserManagement/AWSCognitoUserManagementController.cpp @@ -54,7 +54,7 @@ namespace AWSClientAuth AWSCore::AWSResourceMappingRequestBus::BroadcastResult( m_cognitoAppClientId, &AWSCore::AWSResourceMappingRequests::GetResourceNameId, CognitoAppClientIdResourceMappingKey); AZ_Warning( - "AWSCognitoUserManagementController", m_cognitoAppClientId.empty(), "Missing Cognito App Client Id from resource mappings. Calls to Cognito will fail."); + "AWSCognitoUserManagementController", !m_cognitoAppClientId.empty(), "Missing Cognito App Client Id from resource mappings. Calls to Cognito will fail."); return !m_cognitoAppClientId.empty(); } diff --git a/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerMock.h b/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerMock.h new file mode 100644 index 0000000000..e7b233c786 --- /dev/null +++ b/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerMock.h @@ -0,0 +1,55 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AWSClientAuthUnitTest +{ + class AuthenticationProviderManagerLocalMock + : public AWSClientAuth::AuthenticationProviderManager + { + public: + using AWSClientAuth::AuthenticationProviderManager::DeviceCodeGrantConfirmSignInAsync; + using AWSClientAuth::AuthenticationProviderManager::DeviceCodeGrantSignInAsync; + using AWSClientAuth::AuthenticationProviderManager::GetAuthenticationTokens; + using AWSClientAuth::AuthenticationProviderManager::GetTokensWithRefreshAsync; + using AWSClientAuth::AuthenticationProviderManager::Initialize; + using AWSClientAuth::AuthenticationProviderManager::IsSignedIn; + using AWSClientAuth::AuthenticationProviderManager::m_authenticationProvidersMap; + using AWSClientAuth::AuthenticationProviderManager::PasswordGrantMultiFactorConfirmSignInAsync; + using AWSClientAuth::AuthenticationProviderManager::PasswordGrantMultiFactorSignInAsync; + using AWSClientAuth::AuthenticationProviderManager::PasswordGrantSingleFactorSignInAsync; + using AWSClientAuth::AuthenticationProviderManager::RefreshTokensAsync; + using AWSClientAuth::AuthenticationProviderManager::SignOut; + + AZStd::unique_ptr CreateAuthenticationProviderObjectMock( + const AWSClientAuth::ProviderNameEnum& providerName) + { + auto providerObject = AWSClientAuth::AuthenticationProviderManager::CreateAuthenticationProviderObject(providerName); + providerObject.reset(); + return AZStd::make_unique>(); + } + + AuthenticationProviderManagerLocalMock() + { + ON_CALL(*this, CreateAuthenticationProviderObject(testing::_)) + .WillByDefault(testing::Invoke(this, &AuthenticationProviderManagerLocalMock::CreateAuthenticationProviderObjectMock)); + } + + MOCK_METHOD1( + CreateAuthenticationProviderObject, + AZStd::unique_ptr(const AWSClientAuth::ProviderNameEnum&)); + }; +} // namespace AWSClientAuthUnitTest diff --git a/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerScriptCanvasBusTest.cpp b/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerScriptCanvasBusTest.cpp new file mode 100644 index 0000000000..7673840299 --- /dev/null +++ b/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerScriptCanvasBusTest.cpp @@ -0,0 +1,261 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class AuthenticationProviderManagerScriptCanvasTest + : public AWSClientAuthUnitTest::AWSClientAuthGemAllocatorFixture +{ +protected: + void SetUp() override + { + AWSClientAuthUnitTest::AWSClientAuthGemAllocatorFixture::SetUp(); + + AWSClientAuth::LWAProviderSetting::Reflect(*m_serializeContext); + AWSClientAuth::GoogleProviderSetting::Reflect(*m_serializeContext); + + m_settingspath = AZStd::string::format("%s/%s/authenticationProvider.setreg", + m_testFolder->c_str(), AZ::SettingsRegistryInterface::RegistryFolder); + CreateTestFile("authenticationProvider.setreg" + , R"({ + "AWS": + { + "LoginWithAmazon": + { + "AppClientId": "TestLWAClientId", + "GrantType": "device_code", + "Scope": "profile", + "ResponseType": "device_code", + "OAuthCodeURL": "https://api.amazon.com/auth/o2/create/codepair", + "OAuthTokensURL": "https://oauth2.googleapis.com/token" + }, + "Google": + { + "AppClientId": "TestGoogleClientId", + "ClientSecret": "123", + "GrantType": "urn:ietf:params:oauth:grant-type:device_code", + "Scope": "profile", + "OAuthCodeURL": "https://oauth2.googleapis.com/device/code", + "OAuthTokensURL": "https://oauth2.googleapis.com/token" + } + } + })"); + + m_mockController = AZStd::make_unique>(); + } + + void TearDown() override + { + m_mockController.reset(); + AWSClientAuthUnitTest::AWSClientAuthGemAllocatorFixture::TearDown(); + } + +public: + AZStd::unique_ptr> m_mockController; + AZStd::string m_settingspath; + AZStd::vector m_enabledProviderNames { AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP, + AWSClientAuth::ProvideNameEnumStringLoginWithAmazon, AWSClientAuth::ProvideNameEnumStringGoogle}; +}; + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, Initialize_Success) +{ + ASSERT_TRUE(m_mockController->Initialize(m_enabledProviderNames, m_settingspath)); + ASSERT_TRUE(m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP] != nullptr); +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, PasswordGrantSingleFactorSignInAsync_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock *cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + + EXPECT_CALL(*cognitoProviderMock, PasswordGrantSingleFactorSignInAsync(testing::_, testing::_)).Times(1); + m_mockController->PasswordGrantSingleFactorSignInAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP, AWSClientAuthUnitTest::TEST_USERNAME, AWSClientAuthUnitTest::TEST_PASSWORD); + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, PasswordGrantSingleFactorSignInAsync_Fail_NonConfiguredProviderError) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + m_mockController->PasswordGrantSingleFactorSignInAsync(AWSClientAuth::ProvideNameEnumStringApple, AWSClientAuthUnitTest::TEST_USERNAME, AWSClientAuthUnitTest::TEST_PASSWORD); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, PasswordGrantMultiFactorSignInAsync_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock* cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + testing::NiceMock* lwaProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::LoginWithAmazon].get(); + + EXPECT_CALL(*cognitoProviderMock, PasswordGrantMultiFactorSignInAsync(testing::_, testing::_)).Times(1); + m_mockController->PasswordGrantMultiFactorSignInAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP, AWSClientAuthUnitTest::TEST_USERNAME, AWSClientAuthUnitTest::TEST_PASSWORD); + + EXPECT_CALL(*lwaProviderMock, PasswordGrantMultiFactorSignInAsync(testing::_, testing::_)).Times(1); + m_mockController->PasswordGrantMultiFactorSignInAsync(AWSClientAuth::ProvideNameEnumStringLoginWithAmazon, AWSClientAuthUnitTest::TEST_USERNAME, AWSClientAuthUnitTest::TEST_PASSWORD); + + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, PasswordGrantMultiFactorConfirmSignInAsync_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock *cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + testing::NiceMock *lwaProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::LoginWithAmazon].get(); + + EXPECT_CALL(*cognitoProviderMock, PasswordGrantMultiFactorConfirmSignInAsync(testing::_, testing::_)).Times(1); + m_mockController->PasswordGrantMultiFactorConfirmSignInAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP, AWSClientAuthUnitTest::TEST_USERNAME, AWSClientAuthUnitTest::TEST_PASSWORD); + + EXPECT_CALL(*lwaProviderMock, PasswordGrantMultiFactorConfirmSignInAsync(testing::_, testing::_)).Times(1); + m_mockController->PasswordGrantMultiFactorConfirmSignInAsync(AWSClientAuth::ProvideNameEnumStringLoginWithAmazon, AWSClientAuthUnitTest::TEST_USERNAME, AWSClientAuthUnitTest::TEST_PASSWORD); + + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, DeviceCodeGrantSignInAsync_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock* cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + testing::NiceMock* lwaProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::LoginWithAmazon].get(); + + EXPECT_CALL(*cognitoProviderMock, DeviceCodeGrantSignInAsync()).Times(1); + m_mockController->DeviceCodeGrantSignInAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP); + + EXPECT_CALL(*lwaProviderMock, DeviceCodeGrantSignInAsync()).Times(1); + m_mockController->DeviceCodeGrantSignInAsync(AWSClientAuth::ProvideNameEnumStringLoginWithAmazon); + + cognitoProviderMock = nullptr; +} + + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, DeviceCodeGrantConfirmSignInAsync_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock* cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + testing::NiceMock* lwaProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::LoginWithAmazon].get(); + + EXPECT_CALL(*cognitoProviderMock, DeviceCodeGrantConfirmSignInAsync()).Times(1); + m_mockController->DeviceCodeGrantConfirmSignInAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP); + + EXPECT_CALL(*lwaProviderMock, DeviceCodeGrantConfirmSignInAsync()).Times(1); + m_mockController->DeviceCodeGrantConfirmSignInAsync(AWSClientAuth::ProvideNameEnumStringLoginWithAmazon); + + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, RefreshTokenAsync_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock *cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + testing::NiceMock *lwaProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::LoginWithAmazon].get(); + + EXPECT_CALL(*cognitoProviderMock, RefreshTokensAsync()).Times(1); + m_mockController->RefreshTokensAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP); + + EXPECT_CALL(*lwaProviderMock, RefreshTokensAsync()).Times(1); + m_mockController->RefreshTokensAsync(AWSClientAuth::ProvideNameEnumStringLoginWithAmazon); + + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, GetTokensWithRefreshAsync_ValidToken_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock* cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + + AWSClientAuth::AuthenticationTokens tokens( + AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN, + AWSClientAuth::ProviderNameEnum::AWSCognitoIDP, 600); + EXPECT_CALL(*cognitoProviderMock, GetAuthenticationTokens()).Times(1).WillOnce(testing::Return(tokens)); + EXPECT_CALL(*cognitoProviderMock, RefreshTokensAsync()).Times(0); + EXPECT_CALL(m_authenticationProviderNotificationsBusMock, OnRefreshTokensSuccess(testing::_)).Times(1); + m_mockController->GetTokensWithRefreshAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP); + + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, GetTokensWithRefreshAsync_InvalidToken_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock* cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + AWSClientAuth::AuthenticationTokens tokens; + EXPECT_CALL(*cognitoProviderMock, GetAuthenticationTokens()).Times(1).WillOnce(testing::Return(tokens)); + EXPECT_CALL(*cognitoProviderMock, RefreshTokensAsync()).Times(1); + m_mockController->GetTokensWithRefreshAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP); + + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, GetTokensWithRefreshAsync_NotInitializedProvider_Fail) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + EXPECT_CALL(m_authenticationProviderNotificationsBusMock, OnRefreshTokensSuccess(testing::_)).Times(0); + EXPECT_CALL(m_authenticationProviderNotificationsBusMock, OnRefreshTokensFail(testing::_)).Times(1); + m_mockController->GetTokensWithRefreshAsync(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, GetTokens_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock* cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + + AWSClientAuth::AuthenticationTokens tokens( + AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN, + AWSClientAuth::ProviderNameEnum::AWSCognitoIDP, 60); + + EXPECT_CALL(*cognitoProviderMock, GetAuthenticationTokens()).Times(1).WillOnce(testing::Return(tokens)); + m_mockController->GetAuthenticationTokens(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP); + + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, IsSignedIn_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock* cognitoProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::AWSCognitoIDP].get(); + + AWSClientAuth::AuthenticationTokens tokens( + AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN, + AWSClientAuth::ProviderNameEnum::AWSCognitoIDP, 60); + EXPECT_CALL(*cognitoProviderMock, GetAuthenticationTokens()).Times(1).WillOnce(testing::Return(tokens)); + m_mockController->IsSignedIn(AWSClientAuth::ProvideNameEnumStringAWSCognitoIDP); + + cognitoProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, SignOut_Success) +{ + m_mockController->Initialize(m_enabledProviderNames, m_settingspath); + testing::NiceMock* googleProviderMock = (testing::NiceMock*)m_mockController->m_authenticationProvidersMap[AWSClientAuth::ProviderNameEnum::Google].get(); + + EXPECT_CALL(*googleProviderMock, SignOut()).Times(1); + EXPECT_CALL(m_authenticationProviderNotificationsBusMock, OnSignOut(testing::_)).Times(1); + m_mockController->SignOut(AWSClientAuth::ProvideNameEnumStringGoogle); + + googleProviderMock = nullptr; +} + +TEST_F(AuthenticationProviderManagerScriptCanvasTest, Initialize_Fail_InvalidPath) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + ASSERT_FALSE(m_mockController->Initialize(m_enabledProviderNames, "")); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); +} diff --git a/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerTest.cpp b/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerTest.cpp index 9e7d189f5d..4b5bdfb841 100644 --- a/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerTest.cpp +++ b/Gems/AWSClientAuth/Code/Tests/Authentication/AuthenticationProviderManagerTest.cpp @@ -10,8 +10,6 @@ * */ -#include -#include #include #include #include @@ -20,42 +18,7 @@ #include #include #include - -namespace AWSClientAuthUnitTest -{ - class AuthenticationProviderManagerLocalMock - : public AWSClientAuth::AuthenticationProviderManager - { - public: - using AWSClientAuth::AuthenticationProviderManager::m_authenticationProvidersMap; - using AWSClientAuth::AuthenticationProviderManager::Initialize; - using AWSClientAuth::AuthenticationProviderManager::PasswordGrantSingleFactorSignInAsync; - using AWSClientAuth::AuthenticationProviderManager::PasswordGrantMultiFactorSignInAsync; - using AWSClientAuth::AuthenticationProviderManager::PasswordGrantMultiFactorConfirmSignInAsync; - using AWSClientAuth::AuthenticationProviderManager::DeviceCodeGrantSignInAsync; - using AWSClientAuth::AuthenticationProviderManager::DeviceCodeGrantConfirmSignInAsync; - using AWSClientAuth::AuthenticationProviderManager::RefreshTokensAsync; - using AWSClientAuth::AuthenticationProviderManager::GetTokensWithRefreshAsync; - using AWSClientAuth::AuthenticationProviderManager::GetAuthenticationTokens; - using AWSClientAuth::AuthenticationProviderManager::SignOut; - using AWSClientAuth::AuthenticationProviderManager::IsSignedIn; - - AZStd::unique_ptr CreateAuthenticationProviderObjectMock(const AWSClientAuth::ProviderNameEnum& providerName) - { - auto providerObject = AWSClientAuth::AuthenticationProviderManager::CreateAuthenticationProviderObject(providerName); - providerObject.reset(); - return AZStd::make_unique>(); - } - - AuthenticationProviderManagerLocalMock() - { - ON_CALL(*this, CreateAuthenticationProviderObject(testing::_)).WillByDefault( - testing::Invoke(this, &AuthenticationProviderManagerLocalMock::CreateAuthenticationProviderObjectMock)); - } - - MOCK_METHOD1(CreateAuthenticationProviderObject, AZStd::unique_ptr(const AWSClientAuth::ProviderNameEnum&)); - }; -} +#include class AuthenticationProviderManagerTest @@ -239,6 +202,15 @@ TEST_F(AuthenticationProviderManagerTest, GetTokensWithRefreshAsync_InvalidToken cognitoProviderMock = nullptr; } +TEST_F(AuthenticationProviderManagerTest, GetTokensWithRefreshAsync_NotInitializedProvider_Fail) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + EXPECT_CALL(m_authenticationProviderNotificationsBusMock, OnRefreshTokensSuccess(testing::_)).Times(0); + EXPECT_CALL(m_authenticationProviderNotificationsBusMock, OnRefreshTokensFail(testing::_)).Times(1); + m_mockController->GetTokensWithRefreshAsync(AWSClientAuth::ProviderNameEnum::AWSCognitoIDP); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); +} + TEST_F(AuthenticationProviderManagerTest, GetTokens_Success) { m_mockController->Initialize(m_enabledProviderNames, m_settingspath); diff --git a/Gems/AWSClientAuth/Code/awsclientauth_files.cmake b/Gems/AWSClientAuth/Code/awsclientauth_files.cmake index 79da00076c..7e4734992f 100644 --- a/Gems/AWSClientAuth/Code/awsclientauth_files.cmake +++ b/Gems/AWSClientAuth/Code/awsclientauth_files.cmake @@ -20,6 +20,7 @@ set(FILES Include/Private/AWSClientAuthBus.h Include/Private/AWSClientAuthResourceMappingConstants.h Include/Private/Authentication/AuthenticationProviderTypes.h + Include/Private/Authentication/AuthenticationProviderScriptCanvasBus.h Include/Private/Authentication/AuthenticationProviderManager.h Include/Private/Authentication/AuthenticationNotificationBusBehaviorHandler.h diff --git a/Gems/AWSClientAuth/Code/awsclientauth_test_files.cmake b/Gems/AWSClientAuth/Code/awsclientauth_test_files.cmake index 6d3de7cc54..18aaa697dd 100644 --- a/Gems/AWSClientAuth/Code/awsclientauth_test_files.cmake +++ b/Gems/AWSClientAuth/Code/awsclientauth_test_files.cmake @@ -14,7 +14,9 @@ set(FILES Tests/AWSClientAuthGemTest.cpp Tests/AWSClientAuthSystemComponentTest.cpp + Tests/Authentication/AuthenticationProviderManagerMock.h Tests/Authentication/AuthenticationProviderManagerTest.cpp + Tests/Authentication/AuthenticationProviderManagerScriptCanvasBusTest.cpp Tests/Authentication/AWSCognitoAuthenticationProviderTest.cpp Tests/Authentication/LWAAuthenticationProviderTest.cpp Tests/Authentication/GoogleAuthenticationProviderTest.cpp diff --git a/Gems/AWSClientAuth/cdk/auth/cognito_identity_pool_role.py b/Gems/AWSClientAuth/cdk/auth/cognito_identity_pool_role.py index 0ebef83eba..3a2e413617 100755 --- a/Gems/AWSClientAuth/cdk/auth/cognito_identity_pool_role.py +++ b/Gems/AWSClientAuth/cdk/auth/cognito_identity_pool_role.py @@ -56,8 +56,7 @@ class CognitoIdentityPoolRole: # basic permissions stack_statement = iam.PolicyStatement( actions=[ - 's3:Get*', - 's3:List*' + 's3:ListBuckets' ], effect=iam.Effect.ALLOW, resources=[ diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp index 034d0c7cf2..1e3c7b6759 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp @@ -162,7 +162,7 @@ namespace AZ AZStd::string expectedHigherPrecedenceFileFullPath; AzFramework::StringFunc::Path::Join(gameProjectPath, RPI::ShaderVariantTreeAsset::CommonSubFolder, expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */); AzFramework::StringFunc::Path::Join(expectedHigherPrecedenceFileFullPath.c_str(), shaderProductFileRelativePath.c_str(), expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */); - AzFramework::StringFunc::Path::ReplaceExtension(expectedHigherPrecedenceFileFullPath, AZ::RPI::ShaderVariantAsset::Extension); + AzFramework::StringFunc::Path::ReplaceExtension(expectedHigherPrecedenceFileFullPath, AZ::RPI::ShaderVariantListSourceData::Extension); AzFramework::StringFunc::Path::Normalize(expectedHigherPrecedenceFileFullPath); AZStd::string normalizedShaderVariantListFileFullPath = shaderVariantListFileFullPath; diff --git a/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/Materials/basic_grey.material b/Gems/Atom/Feature/Common/Assets/Materials/basic_grey.material similarity index 100% rename from Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/Materials/basic_grey.material rename to Gems/Atom/Feature/Common/Assets/Materials/basic_grey.material diff --git a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass index 60de5e10b9..aa422aa227 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass @@ -49,11 +49,6 @@ "IsArray": 1 } }, - { - "Name": "DepthStencilInputOutput", - "SlotType": "InputOutput", - "ScopeAttachmentUsage": "DepthStencil" - }, { "Name": "TileLightData", "SlotType": "Input", @@ -66,6 +61,13 @@ "ShaderInputName": "m_lightListRemapped", "ScopeAttachmentUsage": "Shader" }, + // Input/Outputs... + { + "Name": "DepthStencilInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil" + }, + // Outputs... { "Name": "DiffuseOutput", "SlotType": "Output", @@ -222,11 +224,12 @@ "Attachment": "Output" } }, + "MultisampleSource": { + "Pass": "This", + "Attachment": "DepthStencilInputOutput" + }, "ImageDescriptor": { "Format": "R8G8B8A8_UNORM", - "MultisampleState": { - "samples": 4 - }, "SharedQueueMask": "Graphics" } }, diff --git a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapPipeline.pass index 79a42b11f7..0e2fb93359 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapPipeline.pass @@ -9,7 +9,8 @@ "Slots": [ { "Name": "Output", - "SlotType": 2 + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" } ], "PassRequests": [ @@ -101,13 +102,8 @@ ] }, { - "Name": "DepthMSAAPass", - "TemplateName": "EnvironmentCubeMapDepthMSAAPassTemplate", - "PassData": { - "$type": "RasterPassData", - "DrawListTag": "depth", - "PipelineViewTag": "MainCamera" - }, + "Name": "DepthPrePass", + "TemplateName": "DepthMSAAParentTemplate", "Connections": [ { "LocalSlot": "SkinnedMeshes", @@ -115,38 +111,19 @@ "Pass": "SkinningPass", "Attachment": "SkinnedMeshOutputStream" } - } - ] - }, - // The light culling system can do highly accurate culling of transparent objects but it needs - // more depth information than the opaque geometry pass can provide - // Specifically the minimum and maximum depth of transparent objects - { - "Name": "DepthTransparentMinPass", - "TemplateName": "DepthPassTemplate", - "PassData": { - "$type": "RasterPassData", - "DrawListTag": "depthTransparentMin", - "PipelineViewTag": "MainCamera" - }, - "Connections": [ + }, { - "LocalSlot": "SkinnedMeshes", + "LocalSlot": "SwapChainOutput", "AttachmentRef": { - "Pass": "SkinningPass", - "Attachment": "SkinnedMeshOutputStream" + "Pass": "Parent", + "Attachment": "Output" } } ] }, { - "Name": "DepthTransparentMaxPass", - "TemplateName": "DepthMaxPassTemplate", - "PassData": { - "$type": "RasterPassData", - "DrawListTag": "depthTransparentMax", - "PipelineViewTag": "MainCamera" - }, + "Name": "LightCullingPass", + "TemplateName": "LightCullingParentTemplate", "Connections": [ { "LocalSlot": "SkinnedMeshes", @@ -154,76 +131,23 @@ "Pass": "SkinningPass", "Attachment": "SkinnedMeshOutputStream" } - } - ] - }, - { - "Name": "LightCullingTilePreparePass", - "TemplateName": "LightCullingTilePrepareMSAATemplate", - "Connections": [ - { - "LocalSlot": "Depth", - "AttachmentRef": { - "Pass": "DepthMSAAPass", - "Attachment": "Output" - } }, { - "LocalSlot": "DepthTransparentMin", + "LocalSlot": "DepthMSAA", "AttachmentRef": { - "Pass": "DepthTransparentMinPass", - "Attachment": "Output" + "Pass": "DepthPrePass", + "Attachment": "DepthMSAA" } }, { - "LocalSlot": "DepthTransparentMax", + "LocalSlot": "SwapChainOutput", "AttachmentRef": { - "Pass": "DepthTransparentMaxPass", + "Pass": "Parent", "Attachment": "Output" } } ] }, - { - "Name": "LightCullingPass", - "TemplateName": "LightCullingTemplate", - "Connections": [ - { - "LocalSlot": "TileLightData", - "AttachmentRef": { - "Pass": "LightCullingTilePreparePass", - "Attachment": "TileLightData" - } - } - ] - }, - { - "Name": "LightCullingRemapPass", - "TemplateName": "LightCullingRemapTemplate", - "Connections": [ - { - "LocalSlot": "TileLightData", - "AttachmentRef": { - "Pass": "LightCullingTilePreparePass", - "Attachment": "TileLightData" - } - }, - { - "LocalSlot": "LightCount", - "AttachmentRef": { - "Pass": "LightCullingPass", - "Attachment": "LightCount" - } - }, - { - "LocalSlot": "LightList", - "AttachmentRef": { - "Pass": "LightCullingPass", - "Attachment": "LightList" - } - } - ] - }, { "Name": "ForwardMSAAPass", "TemplateName": "EnvironmentCubeMapForwardMSAAPassTemplate", @@ -259,21 +183,21 @@ { "LocalSlot": "DepthStencilInputOutput", "AttachmentRef": { - "Pass": "DepthMSAAPass", - "Attachment": "Output" + "Pass": "DepthPrePass", + "Attachment": "DepthMSAA" } }, { "LocalSlot": "TileLightData", "AttachmentRef": { - "Pass": "LightCullingRemapPass", + "Pass": "LightCullingPass", "Attachment": "TileLightData" } }, { "LocalSlot": "LightListRemapped", "AttachmentRef": { - "Pass": "LightCullingRemapPass", + "Pass": "LightCullingPass", "Attachment": "LightListRemapped" } } diff --git a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapSkyBox.pass b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapSkyBox.pass index b16cd1cbcc..4c74f9e967 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapSkyBox.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapSkyBox.pass @@ -32,11 +32,12 @@ "Attachment": "SpecularInputOutput" } }, + "MultisampleSource": { + "Pass": "This", + "Attachment": "SpecularInputOutput" + }, "ImageDescriptor": { "Format": "R16G16B16A16_FLOAT", - "MultisampleState": { - "samples": 4 - }, "SharedQueueMask": "Graphics" } } diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h index cc26ff862a..bd0ede749d 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace AZ { @@ -160,8 +161,14 @@ namespace AZ // called when reflection probes are modified in the editor so that meshes can re-evaluate their probes void UpdateMeshReflectionProbes(); - private: + void ForceRebuildDrawPackets(const AZ::ConsoleCommandContainer& arguments); + AZ_CONSOLEFUNC(MeshFeatureProcessor, + ForceRebuildDrawPackets, + AZ::ConsoleFunctorFlags::Null, + "(For Testing) Invalidates all mesh draw packets, causing them to rebuild on the next frame." + ); + MeshFeatureProcessor(const MeshFeatureProcessor&) = delete; // RPI::SceneNotificationBus::Handler overrides... diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp index c412746676..7845c8f402 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp @@ -289,7 +289,7 @@ namespace AZ void AuxGeomDrawQueue::DrawQuad( float width, float height, - const AZ::Transform& transform, + const AZ::Matrix3x4& transform, const AZ::Color& color, DrawStyle style, DepthTest depthTest, @@ -302,8 +302,8 @@ namespace AZ return; } - Transform noScaleTransform = transform; - noScaleTransform.ExtractScale(); + AZ::Matrix3x4 noScaleTransform = transform; + AZ::Vector3 scale = noScaleTransform.ExtractScale(); ShapeBufferEntry shape; shape.m_shapeType = ShapeType_Quad; @@ -311,9 +311,9 @@ namespace AZ shape.m_depthWrite = ConvertRPIDepthWriteFlag(depthWrite); shape.m_faceCullMode = ConvertRPIFaceCullFlag(faceCull); shape.m_color = color; - shape.m_rotationMatrix = Matrix3x3::CreateFromTransform(noScaleTransform); + shape.m_rotationMatrix = Matrix3x3::CreateFromMatrix3x4(noScaleTransform); shape.m_position = transform.GetTranslation(); - shape.m_scale = transform.GetScale() * Vector3(width, 1.0f, height); + shape.m_scale = scale * Vector3(width, 1.0f, height); shape.m_pointSize = m_pointSize; shape.m_viewProjOverrideIndex = viewProjOverrideIndex; diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.h b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.h index b959173ed1..53220c031d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.h +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.h @@ -62,7 +62,7 @@ namespace AZ void DrawTriangles(const AuxGeomDynamicIndexedDrawArguments& args, FaceCullMode faceCull = FaceCullMode::None) override; // Fixed shape draws - void DrawQuad(float width, float height, const AZ::Transform& transform, const AZ::Color& color, DrawStyle style, DepthTest depthTest, DepthWrite depthWrite, FaceCullMode faceCull, int32_t viewProjOverrideIndex) override; + void DrawQuad(float width, float height, const AZ::Matrix3x4& transform, const AZ::Color& color, DrawStyle style, DepthTest depthTest, DepthWrite depthWrite, FaceCullMode faceCull, int32_t viewProjOverrideIndex) override; void DrawSphere(const AZ::Vector3& center, float radius, const AZ::Color& color, DrawStyle style, DepthTest depthTest, DepthWrite depthWrite, FaceCullMode faceCull, int32_t viewProjOverrideIndex) override; void DrawDisk(const AZ::Vector3& center, const AZ::Vector3& direction, float radius, const AZ::Color& color, DrawStyle style, DepthTest depthTest, DepthWrite depthWrite, FaceCullMode faceCull, int32_t viewProjOverrideIndex) override; void DrawCone(const AZ::Vector3& center, const AZ::Vector3& direction, float radius, float height, const AZ::Color& color, DrawStyle style, DepthTest depthTest, DepthWrite depthWrite, FaceCullMode faceCull, int32_t viewProjOverrideIndex) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 7ef5736325..409ff6ceaf 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -387,6 +387,11 @@ namespace AZ } } + void MeshFeatureProcessor::ForceRebuildDrawPackets([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) + { + m_forceRebuildDrawPackets = true; + } + void MeshFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) { m_forceRebuildDrawPackets = true;; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp index 206e58d5e6..e0ff253301 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp @@ -131,8 +131,13 @@ namespace AZ AZ_Assert(m_blendedLut.m_lutImage != nullptr, "BlendColorGradingLutsPass unable to acquire LUT image"); AZ::RHI::AttachmentId imageAttachmentId = AZ::RHI::AttachmentId("BlendColorGradingLutImageAttachmentId"); - [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(imageAttachmentId, m_blendedLut.m_lutImage); - AZ_Error("BlendColorGradingLutsPass", result == RHI::ResultCode::Success, "Failed to import compute buffer with error %d", result); + + // import this attachment if it wasn't imported + if (!frameGraph.GetAttachmentDatabase().IsAttachmentValid(imageAttachmentId)) + { + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(imageAttachmentId, m_blendedLut.m_lutImage); + AZ_Error("BlendColorGradingLutsPass", result == RHI::ResultCode::Success, "Failed to import BlendColorGradingLutImageAttachmentId with error %d", result); + } RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = imageAttachmentId; diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index 38c30d67a6..9cc98a15ec 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -186,7 +186,7 @@ namespace AZ { for (const RPI::Pass* pass : passes) { - m_timestampEntries.push_back({ pass->GetName(), pass->GetTimestampResult().GetTimestampInNanoseconds() }); + m_timestampEntries.push_back({pass->GetName(), pass->GetLatestTimestampResult().GetDurationInNanoseconds()}); } } @@ -223,7 +223,7 @@ namespace AZ { for (const RPI::Pass* pass : passes) { - m_pipelineStatisticsEntries.push_back({ pass->GetName(), pass->GetPipelineStatisticsResult() }); + m_pipelineStatisticsEntries.push_back({pass->GetName(), pass->GetLatestPipelineStatisticsResult()}); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index 2f44a6962b..7fe51d6c37 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -248,6 +248,9 @@ namespace AZ AZ::RPI::RenderPipelineDescriptor environmentCubeMapPipelineDesc; environmentCubeMapPipelineDesc.m_mainViewTagName = "MainCamera"; + environmentCubeMapPipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4; + environmentCubeMapPipelineDesc.m_renderSettings.m_size.m_width = RPI::EnvironmentCubeMapPass::CubeMapFaceSize; + environmentCubeMapPipelineDesc.m_renderSettings.m_size.m_height = RPI::EnvironmentCubeMapPass::CubeMapFaceSize; // create a unique name for the pipeline AZ::Uuid uuid = AZ::Uuid::CreateRandom(); diff --git a/Gems/Atom/RHI/Code/Source/RHI.Reflect/Base.cpp b/Gems/Atom/RHI/Code/Source/RHI.Reflect/Base.cpp index 4d35411e09..21eb634159 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Reflect/Base.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Reflect/Base.cpp @@ -16,6 +16,6 @@ namespace AZ { namespace RHI { - bool Validation::s_isEnabled = BuildOptions::IsDebugBuild; + bool Validation::s_isEnabled = BuildOptions::IsDebugBuild || BuildOptions::IsProfileBuild; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp index 1ae8a4ae96..7fc1db179a 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp @@ -404,6 +404,12 @@ namespace AZ Buffer& buffer = static_cast(*bufferFrameAttachment.GetBuffer()); RHI::BufferScopeAttachment* scopeAttachment = bufferFrameAttachment.GetFirstScopeAttachment(); + if (scopeAttachment == nullptr) + { + AZ_WarningOnce("RHI", false, "Imported BufferFrameAttachment isn't used in any Scope"); + return; + } + D3D12_RESOURCE_TRANSITION_BARRIER transition; transition.pResource = buffer.GetMemoryView().GetMemory(); transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; @@ -471,6 +477,12 @@ namespace AZ Image& image = static_cast(*imageFrameAttachment.GetImage()); RHI::ImageScopeAttachment* scopeAttachment = imageFrameAttachment.GetFirstScopeAttachment(); + if (scopeAttachment == nullptr) + { + AZ_WarningOnce("RHI", false, "Imported ImageFrameAttachment isn't used in any Scope"); + return; + } + D3D12_RESOURCE_TRANSITION_BARRIER transition; transition.pResource = image.GetMemoryView().GetMemory(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/AuxGeom/AuxGeomDraw.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/AuxGeom/AuxGeomDraw.h index afba71a457..0e7f11e46e 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/AuxGeom/AuxGeomDraw.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/AuxGeom/AuxGeomDraw.h @@ -138,7 +138,7 @@ namespace AZ //! @param depthWrite If depth writing should be enabled //! @param faceCull Which (if any) facing triangles should be culled //! @param viewProjOverrideIndex Which view projection override entry to use, -1 if unused - virtual void DrawQuad(float width, float height, const AZ::Transform& transform, const AZ::Color& color, DrawStyle style = DrawStyle::Shaded, DepthTest depthTest = DepthTest::On, DepthWrite depthWrite = DepthWrite::On, FaceCullMode faceCull = FaceCullMode::Back, int32_t viewProjOverrideIndex = -1) = 0; + virtual void DrawQuad(float width, float height, const AZ::Matrix3x4& transform, const AZ::Color& color, DrawStyle style = DrawStyle::Shaded, DepthTest depthTest = DepthTest::On, DepthWrite depthWrite = DepthWrite::On, FaceCullMode faceCull = FaceCullMode::Back, int32_t viewProjOverrideIndex = -1) = 0; //! Draw a sphere. //! @param center The center of the sphere. diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/GpuQuery/GpuQueryTypes.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/GpuQuery/GpuQueryTypes.h index 80a72aaf55..ae86557170 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/GpuQuery/GpuQueryTypes.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/GpuQuery/GpuQueryTypes.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -43,15 +44,19 @@ namespace AZ { public: TimestampResult() = default; - TimestampResult(uint64_t timestampInTicks); - TimestampResult(uint64_t timestampQueryResultLow, uint64_t timestampQueryResultHigh); - TimestampResult(AZStd::array_view&& timestampResultArray); + TimestampResult(uint64_t beginTick, uint64_t endTick, RHI::HardwareQueueClass hardwareQueueClass); - uint64_t GetTimestampInNanoseconds() const; - uint64_t GetTimestampInTicks() const; + uint64_t GetDurationInNanoseconds() const; + uint64_t GetDurationInTicks() const; + uint64_t GetTimestampBeginInTicks() const; + + void Add(const TimestampResult& extent); private: - uint64_t m_timestampInTicks = 0u; + // the timestamp of begin and duration in ticks. + uint64_t m_begin = 0; + uint64_t m_duration = 0; + RHI::HardwareQueueClass m_hardwareQueueClass = RHI::HardwareQueueClass::Graphics; }; //! The structure that is used to read back the results form the PipelineStatistics queries diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h index 9317158437..b4f63a7099 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h @@ -122,7 +122,6 @@ namespace AZ private: // RPI::Pass overrides... - TimestampResult GetTimestampResultInternal() const override; PipelineStatisticsResult GetPipelineStatisticsResultInternal() const override; // --- Hierarchy related functions --- diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index ae249a5f45..b0d6bd4117 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -211,11 +211,11 @@ namespace AZ //! Prints the pass virtual void DebugPrint() const; - //! Return the Timestamp result of this pass - TimestampResult GetTimestampResult() const; + //! Return the latest Timestamp result of this pass + TimestampResult GetLatestTimestampResult() const; - //! Return the PipelineStatistic result of this pass - PipelineStatisticsResult GetPipelineStatisticsResult() const; + //! Return the latest PipelineStatistic result of this pass + PipelineStatisticsResult GetLatestPipelineStatisticsResult() const; //! Enables/Disables Timestamp queries for this pass virtual void SetTimestampQueryEnabled(bool enable); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/ResourcePool/ResourcePoolBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/ResourcePool/ResourcePoolBuilder.cpp index 7f617f4c6c..92c30c28b1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/ResourcePool/ResourcePoolBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/ResourcePool/ResourcePoolBuilder.cpp @@ -45,7 +45,7 @@ namespace AZ AssetBuilderSDK::AssetBuilderDesc builderDescriptor; builderDescriptor.m_name = "Atom Resource Pool Asset Builder"; - builderDescriptor.m_version = 1; + builderDescriptor.m_version = 2; //ATOM-15196 builderDescriptor.m_patterns.emplace_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string("*.") + s_sourcePoolAssetExt, AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); builderDescriptor.m_busId = azrtti_typeid(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp index fd2d029ddb..491f57deec 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp @@ -100,12 +100,12 @@ namespace AZ bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write; break; case CommonBufferPoolType::StaticInputAssembly: - bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly; + bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly | RHI::BufferBindFlags::ShaderRead; bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device; bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write; break; case CommonBufferPoolType::DynamicInputAssembly: - bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::DynamicInputAssembly; + bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::DynamicInputAssembly | RHI::BufferBindFlags::ShaderRead; bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host; bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write; break; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQueryTypes.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQueryTypes.cpp index a1b2ece4dc..715964651f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQueryTypes.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQueryTypes.cpp @@ -21,41 +21,39 @@ namespace AZ namespace RPI { // --- TimestampResult --- - - TimestampResult::TimestampResult(uint64_t timestampInTicks) + TimestampResult::TimestampResult(uint64_t beginTick, uint64_t endTick, RHI::HardwareQueueClass hardwareQueueClass) { - m_timestampInTicks = timestampInTicks; + AZ_Assert(endTick >= beginTick, "TimestampResult: bad inputs"); + m_begin = beginTick; + m_duration = endTick - beginTick; + m_hardwareQueueClass = hardwareQueueClass; } - TimestampResult::TimestampResult(uint64_t timestampQueryResultLow, uint64_t timestampQueryResultHigh) + uint64_t TimestampResult::GetDurationInNanoseconds() const { - const uint64_t low = AZStd::min(timestampQueryResultLow, timestampQueryResultHigh); - const uint64_t high = AZStd::max(timestampQueryResultLow, timestampQueryResultHigh); + const RHI::Ptr device = RHI::GetRHIDevice(); + const AZStd::chrono::microseconds timeInMicroseconds = device->GpuTimestampToMicroseconds(m_duration, m_hardwareQueueClass); + const auto timeInNanoseconds = AZStd::chrono::nanoseconds(timeInMicroseconds); - m_timestampInTicks = high - low; + return static_cast(timeInNanoseconds.count()); } - TimestampResult::TimestampResult(AZStd::array_view&& timestampResultArray) + uint64_t TimestampResult::GetDurationInTicks() const { - // Loop through all the child passes, and accumulate all the timestampTicks - for (const TimestampResult& timestampResult : timestampResultArray) - { - m_timestampInTicks += timestampResult.m_timestampInTicks; - } + return m_duration; } - uint64_t TimestampResult::GetTimestampInNanoseconds() const + uint64_t TimestampResult::GetTimestampBeginInTicks() const { - const RHI::Ptr device = RHI::GetRHIDevice(); - const AZStd::chrono::microseconds timeInMicroseconds = device->GpuTimestampToMicroseconds(m_timestampInTicks, RHI::HardwareQueueClass::Graphics); - const auto timeInNanoseconds = AZStd::chrono::nanoseconds(timeInMicroseconds); - - return static_cast(timeInNanoseconds.count()); + return m_begin; } - uint64_t TimestampResult::GetTimestampInTicks() const + void TimestampResult::Add(const TimestampResult& extent) { - return m_timestampInTicks; + uint64_t end1 = m_begin + m_duration; + uint64_t end2 = extent.m_begin + extent.m_duration; + m_begin = m_begin < extent.m_begin ? m_begin : extent.m_begin; + m_duration = (end1 > end2 ? end1 : end2) - m_begin; } // --- PipelineStatisticsResult --- diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp index 1e5eaab20c..d0a277304e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp @@ -17,11 +17,20 @@ #include #include #include +#include namespace AZ { namespace RPI { + AZ_CVAR(bool, + r_forceRootShaderVariantUsage, + false, + [](const bool&) { AZ::Interface::Get()->PerformCommand("MeshFeatureProcessor.ForceRebuildDrawPackets"); }, + ConsoleFunctorFlags::Null, + "(For Testing) Forces usage of root shader variant in the mesh draw packet level, ignoring any other shader variants that may exist." + ); + MeshDrawPacket::MeshDrawPacket( ModelLod& modelLod, size_t modelLodMeshIndex, @@ -187,7 +196,7 @@ namespace AZ } const ShaderVariantId finalVariantId = shaderOptions.GetShaderVariantId(); - const ShaderVariant& variant = shader->GetVariant(finalVariantId); + const ShaderVariant& variant = r_forceRootShaderVariantUsage ? shader->GetRootVariant() : shader->GetVariant(finalVariantId); Data::Instance drawSrg; if (drawSrgAsset) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp index 6314ae376e..106ef82bf1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp @@ -393,19 +393,6 @@ namespace AZ } } - TimestampResult ParentPass::GetTimestampResultInternal() const - { - AZStd::vector timestampResultArray; - timestampResultArray.reserve(m_children.size()); - - // Calculate the Timestamp result by summing all of its child's TimestampResults - for (const Ptr& childPass : m_children) - { - timestampResultArray.emplace_back(childPass->GetTimestampResult()); - } - return TimestampResult(timestampResultArray); - } - PipelineStatisticsResult ParentPass::GetPipelineStatisticsResultInternal() const { AZStd::vector pipelineStatisticsResultArray; @@ -414,7 +401,7 @@ namespace AZ // Calculate the PipelineStatistics result by summing all of its child's PipelineStatistics for (const Ptr& childPass : m_children) { - pipelineStatisticsResultArray.emplace_back(childPass->GetPipelineStatisticsResult()); + pipelineStatisticsResultArray.emplace_back(childPass->GetLatestPipelineStatisticsResult()); } return PipelineStatisticsResult(pipelineStatisticsResultArray); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index 5ecf293efe..9401d1a9e0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -1273,24 +1273,14 @@ namespace AZ } } - TimestampResult Pass::GetTimestampResult() const + TimestampResult Pass::GetLatestTimestampResult() const { - if (IsEnabled() && IsTimestampQueryEnabled()) - { - return GetTimestampResultInternal(); - } - - return TimestampResult(); + return GetTimestampResultInternal(); } - PipelineStatisticsResult Pass::GetPipelineStatisticsResult() const + PipelineStatisticsResult Pass::GetLatestPipelineStatisticsResult() const { - if (IsEnabled() && IsPipelineStatisticsQueryEnabled()) - { - return GetPipelineStatisticsResultInternal(); - } - - return PipelineStatisticsResult(); + return GetPipelineStatisticsResultInternal(); } TimestampResult Pass::GetTimestampResultInternal() const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp index 19a4f3d302..1fad385fa6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -539,7 +539,7 @@ namespace AZ const uint32_t TimestampResultQueryCount = 2u; uint64_t timestampResult[TimestampResultQueryCount] = {0}; query->GetLatestResult(×tampResult, sizeof(uint64_t) * TimestampResultQueryCount); - m_timestampResult = TimestampResult(timestampResult[0], timestampResult[1]); + m_timestampResult = TimestampResult(timestampResult[0], timestampResult[1], RHI::HardwareQueueClass::Graphics); }); ExecuteOnPipelineStatisticsQuery([this](RHI::Ptr query) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp index 5ba43f9f7e..c72712bce5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp @@ -57,7 +57,7 @@ namespace AZ PassConnection childInputConnection; childInputConnection.m_localSlot = "Output"; childInputConnection.m_attachmentRef.m_pass = "Parent"; - childInputConnection.m_attachmentRef.m_attachment = "CubeMapOutput"; + childInputConnection.m_attachmentRef.m_attachment = "Output"; childRequest.m_connections.emplace_back(childInputConnection); PassSystemInterface* passSystem = PassSystemInterface::Get(); @@ -120,15 +120,16 @@ namespace AZ // create output PassAttachment m_passAttachment = aznew PassAttachment(); - m_passAttachment->m_name = "CubeMapOutput"; - m_passAttachment->m_path = "CubeMapOutput"; + m_passAttachment->m_name = "Output"; + AZ::Name attachmentPath(AZStd::string::format("%s.%s", GetPathName().GetCStr(), m_passAttachment->m_name.GetCStr())); + m_passAttachment->m_path = attachmentPath; m_passAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient; m_passAttachment->m_descriptor = m_outputImageDesc; // create pass attachment binding PassAttachmentBinding outputAttachment; - outputAttachment.m_name = "CubeMapOutput"; - outputAttachment.m_slotType = PassSlotType::Output; + outputAttachment.m_name = "Output"; + outputAttachment.m_slotType = PassSlotType::InputOutput; outputAttachment.m_attachment = m_passAttachment; outputAttachment.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::RenderTarget; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Base.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Base.cpp index 7012a7cf88..8addb16a23 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Base.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Base.cpp @@ -16,6 +16,6 @@ namespace AZ { namespace RPI { - bool Validation::s_isEnabled = RHI::BuildOptions::IsDebugBuild; + bool Validation::s_isEnabled = RHI::BuildOptions::IsDebugBuild || RHI::BuildOptions::IsProfileBuild; } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Viewport/InputController/MaterialEditorViewportInputControllerBus.h b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Viewport/InputController/MaterialEditorViewportInputControllerBus.h index 04250c4efd..2acdc79286 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Viewport/InputController/MaterialEditorViewportInputControllerBus.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Viewport/InputController/MaterialEditorViewportInputControllerBus.h @@ -53,6 +53,9 @@ namespace MaterialEditor //! Modify camera's field of view //! @param value field of view in degrees virtual void SetFieldOfView(float value) = 0; + + //! Check if camera is looking directly at a model + virtual bool IsCameraCentered() const = 0; }; using MaterialEditorViewportInputControllerRequestBus = AZ::EBus; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.cpp index 8355b390ef..159d3339be 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.cpp @@ -76,11 +76,35 @@ namespace MaterialEditor void Behavior::TickInternal([[maybe_unused]] float x, [[maybe_unused]] float y, float z) { m_distanceToTarget = m_distanceToTarget - z; + + bool isCameraCentered = false; + MaterialEditorViewportInputControllerRequestBus::BroadcastResult( + isCameraCentered, + &MaterialEditorViewportInputControllerRequestBus::Handler::IsCameraCentered); + + // if camera is looking at the model (locked to the model) we don't want to zoom past the model's center + if (isCameraCentered) + { + m_distanceToTarget = AZ::GetMax(m_distanceToTarget, 0.0f); + } + AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM); AZ::Vector3 position = m_targetPosition - transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(m_distanceToTarget)); AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTranslation, position); + + // if camera is not locked to the model, move its focal point so we can free look + if (!isCameraCentered) + { + m_targetPosition += transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(z)); + MaterialEditorViewportInputControllerRequestBus::Broadcast( + &MaterialEditorViewportInputControllerRequestBus::Handler::SetTargetPosition, + m_targetPosition); + MaterialEditorViewportInputControllerRequestBus::BroadcastResult( + m_distanceToTarget, + &MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget); + } } float Behavior::GetSensitivityX() diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp index e180a5d979..83ec5a41c5 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp @@ -96,6 +96,7 @@ namespace MaterialEditor void MaterialEditorViewportInputController::SetTargetPosition(const AZ::Vector3& targetPosition) { m_targetPosition = targetPosition; + m_isCameraCentered = false; } float MaterialEditorViewportInputController::GetDistanceToTarget() const @@ -246,6 +247,7 @@ namespace MaterialEditor cameraPosition = cameraRotation.TransformVector(cameraPosition); AZ::Transform cameraTransform = AZ::Transform::CreateFromQuaternionAndTranslation(cameraRotation, cameraPosition); AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTM, cameraTransform); + m_isCameraCentered = true; // reset model AZ::Transform modelTransform = AZ::Transform::CreateIdentity(); @@ -258,6 +260,12 @@ namespace MaterialEditor AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); + + if (m_behavior) + { + m_behavior->End(); + m_behavior->Start(); + } } void MaterialEditorViewportInputController::SetFieldOfView(float value) @@ -265,6 +273,11 @@ namespace MaterialEditor Camera::CameraRequestBus::Event(m_cameraEntityId, &Camera::CameraRequestBus::Events::SetFovDegrees, value); } + bool MaterialEditorViewportInputController::IsCameraCentered() const + { + return m_isCameraCentered; + } + void MaterialEditorViewportInputController::CalculateExtents() { AZ::TransformBus::EventResult(m_modelCenter, m_targetEntityId, &AZ::TransformBus::Events::GetLocalTranslation); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.h index aa3dd836d6..ee40b5c259 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.h @@ -45,6 +45,7 @@ namespace MaterialEditor void GetExtents(float& distanceMin, float& distanceMax) const override; void Reset() override; void SetFieldOfView(float value) override; + bool IsCameraCentered() const override; // AzFramework::ViewportControllerInstance interface overrides... bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override; @@ -95,6 +96,8 @@ namespace MaterialEditor float m_distanceMin = 1.0f; //! Maximum distance from camera to target float m_distanceMax = 10.0f; + //! True if camera is centered on a model + bool m_isCameraCentered = true; static constexpr float MaxDistanceMultiplier = 2.5f; static constexpr float StartingDistanceMultiplier = 2.0f; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.cpp index 087591e6ea..e36a335129 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.cpp @@ -34,10 +34,8 @@ namespace MaterialEditor targetPosition); } - void PanCameraBehavior::TickInternal(float x, float y, float z) + void PanCameraBehavior::TickInternal(float x, float y, [[maybe_unused]] float z) { - Behavior::TickInternal(x, y, z); - AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM); AZ::Quaternion rotation = transform.GetRotation(); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp index afe3cc6fb6..513bedb55f 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp @@ -109,8 +109,8 @@ namespace MaterialEditor AZ::RHI::Ptr rootPass = AZ::RPI::PassSystemInterface::Get()->GetRootPass(); if (rootPass) { - AZ::RPI::TimestampResult timestampResult = rootPass->GetTimestampResult(); - double gpuFrameTimeMs = aznumeric_cast(timestampResult.GetTimestampInNanoseconds()) / 1000000; + AZ::RPI::TimestampResult timestampResult = rootPass->GetLatestTimestampResult(); + double gpuFrameTimeMs = aznumeric_cast(timestampResult.GetDurationInNanoseconds()) / 1000000; m_gpuFrameTimeMs.PushSample(gpuFrameTimeMs); } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp index d3b5432624..76aee99e52 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp @@ -37,6 +37,8 @@ namespace MaterialEditor //Connect ok and cancel buttons QObject::connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); QObject::connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + setModal(true); } void CreateMaterialDialog::InitMaterialTypeSelection() @@ -58,13 +60,17 @@ namespace MaterialEditor AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, enumerateCB, nullptr); //Update the material type file info whenever the combo box selection changes - QObject::connect(m_ui->m_materialTypeComboBox, static_cast(&QComboBox::currentIndexChanged), m_ui->m_materialTypeComboBox, [this](int index) { - QVariant data = m_ui->m_materialTypeComboBox->itemData(index); - m_materialTypeFileInfo = QFileInfo(data.toString()); - }); + QObject::connect(m_ui->m_materialTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, [this]() { UpdateMaterialTypeSelection(); }); + QObject::connect(m_ui->m_materialTypeComboBox, &QComboBox::currentTextChanged, this, [this]() { UpdateMaterialTypeSelection(); }); + + // Select StandardPBR by default but we will later data drive this with editor settings + const int index = m_ui->m_materialTypeComboBox->findText("StandardPBR"); + if (index >= 0) + { + m_ui->m_materialTypeComboBox->setCurrentIndex(index); + } - //Select StandardPBR by default but we will later data drive this with editor settings - m_ui->m_materialTypeComboBox->setCurrentText("StandardPBR"); + UpdateMaterialTypeSelection(); } void CreateMaterialDialog::InitMaterialFileSelection() @@ -86,15 +92,24 @@ namespace MaterialEditor m_materialFileInfo.absoluteFilePath(), QString("Material (*.material)")); - //Reject empty or invalid filenames which indicate user cancellation + // Reject empty or invalid filenames which indicate user cancellation if (!fileInfo.absoluteFilePath().isEmpty()) { m_materialFileInfo = fileInfo; m_ui->m_materialFilePicker->setText(m_materialFileInfo.fileName()); } - }); + }); } + void CreateMaterialDialog::UpdateMaterialTypeSelection() + { + const int index = m_ui->m_materialTypeComboBox->currentIndex(); + if (index >= 0) + { + const QVariant itemData = m_ui->m_materialTypeComboBox->itemData(index); + m_materialTypeFileInfo = QFileInfo(itemData.toString()); + } + } } // namespace MaterialEditor #include diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.h index bf39671343..54d7c2175d 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.h @@ -36,5 +36,6 @@ namespace MaterialEditor QScopedPointer m_ui; void InitMaterialTypeSelection(); void InitMaterialFileSelection(); + void UpdateMaterialTypeSelection(); }; } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialBrowserInteractions.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialBrowserInteractions.cpp index c4601baff2..b0412c001c 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialBrowserInteractions.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialBrowserInteractions.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -61,6 +62,8 @@ namespace MaterialEditor m_caller = nullptr; }); + AddGenericContextMenuActions(caller, menu, entry); + if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source) { const auto source = azalias_cast(entry); @@ -84,6 +87,18 @@ namespace MaterialEditor } } + void MaterialBrowserInteractions::AddGenericContextMenuActions([[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) + { + menu->addAction(QObject::tr("Copy Name To Clipboard"), [=]() + { + QApplication::clipboard()->setText(entry->GetName().c_str()); + }); + menu->addAction(QObject::tr("Copy Path To Clipboard"), [=]() + { + QApplication::clipboard()->setText(entry->GetFullPath().c_str()); + }); + } + void MaterialBrowserInteractions::AddContextMenuActionsForMaterialTypeSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry) { menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]() diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialBrowserInteractions.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialBrowserInteractions.h index 07e2be2d59..2806cee6d4 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialBrowserInteractions.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialBrowserInteractions.h @@ -44,6 +44,7 @@ namespace MaterialEditor //! AssetBrowserInteractionNotificationBus::Handler overrides... void AddContextMenuActions(QWidget* caller, QMenu* menu, const AZStd::vector& entries) override; + void AddGenericContextMenuActions(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry); void AddContextMenuActionsForOtherSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry); void AddContextMenuActionsForMaterialSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry); void AddContextMenuActionsForMaterialTypeSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PresetBrowserDialogs/PresetBrowserDialog.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PresetBrowserDialogs/PresetBrowserDialog.cpp index a00490c0b9..f37a047126 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PresetBrowserDialogs/PresetBrowserDialog.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PresetBrowserDialogs/PresetBrowserDialog.cpp @@ -35,6 +35,7 @@ namespace MaterialEditor SetupPresetList(); SetupSearchWidget(); SetupDialogButtons(); + setModal(true); } void PresetBrowserDialog::SetupPresetList() diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h index 8936f15e55..ce6915699b 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h @@ -93,7 +93,9 @@ namespace AZ ImGuiPipelineStatisticsView(); //! Draw the PipelineStatistics window. - void DrawPipelineStatisticsWindow(bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& m_timestampEntryDatabase); + void DrawPipelineStatisticsWindow(bool& draw, const PassEntry* rootPassEntry, + AZStd::unordered_map& m_timestampEntryDatabase, + AZ::RHI::Ptr rootPass); //! Total number of columns (Attribute columns + PassName column). static const uint32_t HeaderAttributeCount = PassEntry::PipelineStatisticsAttributeCount + 1u; @@ -139,6 +141,9 @@ namespace AZ // ImGui filter used to filter passes by the user's input. ImGuiTextFilter m_passFilter; + + // Pause and showing the pipeline statistics result when it's paused. + bool m_paused = false; }; class ImGuiTimestampView @@ -180,9 +185,19 @@ namespace AZ Count }; + // Timestamp refresh type . + enum class RefreshType : int32_t + { + Realtime = 0, + OncePerSecond, + Count + }; + public: //! Draw the Timestamp window. - void DrawTimestampWindow(bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& m_timestampEntryDatabase); + void DrawTimestampWindow(bool& draw, const PassEntry* rootPassEntry, + AZStd::unordered_map& m_timestampEntryDatabase, + AZ::RHI::Ptr rootPass); private: // Draw option for the hierarchical view of the passes. @@ -223,6 +238,20 @@ namespace AZ // ImGui filter used to filter passes. ImGuiTextFilter m_passFilter; + + // Pause and showing the timestamp result when it's paused. + bool m_paused = false; + + // Hide non-parent passes which has 0 execution time. + bool m_hideZeroPasses = false; + + // Show pass execution timeline + bool m_showTimeline = false; + + // Controls how often the timestamp data is refreshed + RefreshType m_refreshType = RefreshType::Realtime; + AZStd::sys_time_t m_lastUpdateTimeMicroSecond; + }; class ImGuiGpuProfiler diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl index a2b29f3fb7..fa9395dffc 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl @@ -16,6 +16,8 @@ #include #include +#include + namespace AZ { namespace Render @@ -105,9 +107,9 @@ namespace AZ // [GFX TODO][ATOM-4001] Cache the timestamp and PipelineStatistics results. // Get the query results from the passes. - m_timestampResult = pass->GetTimestampResult(); + m_timestampResult = pass->GetLatestTimestampResult(); - const RPI::PipelineStatisticsResult rps = pass->GetPipelineStatisticsResult(); + const RPI::PipelineStatisticsResult rps = pass->GetLatestPipelineStatisticsResult(); m_pipelineStatistics = { rps.m_vertexCount, rps.m_primitiveCount, rps.m_vertexShaderInvocationCount, rps.m_rasterizedPrimitiveCount, rps.m_renderedPrimitiveCount, rps.m_pixelShaderInvocationCount, rps.m_computeShaderInvocationCount }; @@ -153,7 +155,9 @@ namespace AZ } - inline void ImGuiPipelineStatisticsView::DrawPipelineStatisticsWindow(bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& passEntryDatabase) + inline void ImGuiPipelineStatisticsView::DrawPipelineStatisticsWindow(bool& draw, + const PassEntry* rootPassEntry, AZStd::unordered_map& passEntryDatabase, + AZ::RHI::Ptr rootPass) { // Early out if nothing is supposed to be drawn if (!draw) @@ -188,12 +192,6 @@ namespace AZ continue; } - // Filter out disabled passes for the PipelineStatistics window if necessary. - if (!m_showDisabledPasses && !passEntry.IsPipelineStatisticsEnabled()) - { - continue; - } - // Filter out parent passes if necessary. if (!m_showParentPasses && passEntry.m_isParent) { @@ -230,6 +228,13 @@ namespace AZ // Start drawing the PipelineStatistics window. if (ImGui::Begin("PipelineStatistics Window", &draw, ImGuiWindowFlags_NoResize)) { + // Pause/unpause the profiling + if (ImGui::Button(m_paused ? "Resume" : "Pause")) + { + m_paused = !m_paused; + rootPass->SetPipelineStatisticsQueryEnabled(!m_paused); + } + ImGui::Columns(2, "HeaderColumns"); // Draw the statistics of the RootPass. @@ -426,23 +431,16 @@ namespace AZ } AZStd::string label; - if (passEntry->IsPipelineStatisticsEnabled()) + if (rootEntry && m_showAttributeContribution) { - if (rootEntry && m_showAttributeContribution) - { - label = AZStd::string::format("%llu (%u%%)", - static_cast(passEntry->m_pipelineStatistics[attributeIdx]), - static_cast(normalized * 100.0f)); - } - else - { - label = AZStd::string::format("%llu", - static_cast(passEntry->m_pipelineStatistics[attributeIdx])); - } + label = AZStd::string::format("%llu (%u%%)", + static_cast(passEntry->m_pipelineStatistics[attributeIdx]), + static_cast(normalized * 100.0f)); } else { - label = "-"; + label = AZStd::string::format("%llu", + static_cast(passEntry->m_pipelineStatistics[attributeIdx])); } if (rootEntry) @@ -523,7 +521,9 @@ namespace AZ // --- ImGuiTimestampView --- - inline void ImGuiTimestampView::DrawTimestampWindow(bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& timestampEntryDatabase) + inline void ImGuiTimestampView::DrawTimestampWindow( + bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& timestampEntryDatabase, + AZ::RHI::Ptr rootPass) { // Early out if nothing is supposed to be drawn if (!draw) @@ -534,10 +534,28 @@ namespace AZ // Clear the references from the previous frame. m_passEntryReferences.clear(); + // pass entry grid based on its timestamp + AZStd::vector sortedPassEntries; + AZStd::vector> sortedPassGrid; + // Set the child of the parent, only if it passes the filter. for (auto& passEntryIt : timestampEntryDatabase) { PassEntry* passEntry = &passEntryIt.second; + + // Collect all pass entries with non-zero durations + if (passEntry->m_timestampResult.GetDurationInTicks() > 0) + { + sortedPassEntries.push_back(passEntry); + } + + // Skip the pass if the pass' timestamp duration is 0 + if (m_hideZeroPasses && (!passEntry->m_isParent) && passEntry->m_timestampResult.GetDurationInTicks() == 0) + { + continue; + } + + // Only add pass if it pass the filter. if (m_passFilter.PassFilter(passEntry->m_name.GetCStr())) { if (passEntry->m_parent && !passEntry->m_linked) @@ -545,19 +563,94 @@ namespace AZ passEntry->m_parent->LinkChild(passEntry); } - AZ_Assert(m_passEntryReferences.size() < TimestampEntryCount, "Too many PassEntry references. Increase the size of the array."); + AZ_Assert( + m_passEntryReferences.size() < TimestampEntryCount, + "Too many PassEntry references. Increase the size of the array."); m_passEntryReferences.push_back(passEntry); } } + // Sort the pass entries based on their starting time and duration + AZStd::sort(sortedPassEntries.begin(), sortedPassEntries.end(), [](const PassEntry* passEntry1, const PassEntry* passEntry2) { + if (passEntry1->m_timestampResult.GetTimestampBeginInTicks() == passEntry2->m_timestampResult.GetTimestampBeginInTicks()) + { + return passEntry1->m_timestampResult.GetDurationInTicks() < passEntry2->m_timestampResult.GetDurationInTicks(); + } + return passEntry1->m_timestampResult.GetTimestampBeginInTicks() < passEntry2->m_timestampResult.GetTimestampBeginInTicks(); + }); + + // calculate the total GPU duration. + RPI::TimestampResult gpuTimestamp; + if (sortedPassEntries.size() > 0) + { + gpuTimestamp = sortedPassEntries.front()->m_timestampResult; + gpuTimestamp.Add(sortedPassEntries.back()->m_timestampResult); + } + + // Add a pass to the pass grid which none of the pass's timestamp range won't overlap each other. + // Search each row until the pass can be added to the end of row without overlap the previous one. + for (auto& passEntry : sortedPassEntries) + { + auto row = sortedPassGrid.begin(); + for (; row != sortedPassGrid.end(); row++) + { + if (row->empty()) + { + break; + } + auto last = (*row).back(); + if (passEntry->m_timestampResult.GetTimestampBeginInTicks() >= + last->m_timestampResult.GetTimestampBeginInTicks() + last->m_timestampResult.GetDurationInTicks()) + { + row->push_back(passEntry); + break; + } + } + if (row == sortedPassGrid.end()) + { + sortedPassGrid.push_back(); + sortedPassGrid.back().push_back(passEntry); + } + } + + // Refresh timestamp query + bool needEnable = false; + if (!m_paused) + { + if (m_refreshType == RefreshType::OncePerSecond) + { + auto now = AZStd::GetTimeNowMicroSecond(); + if (m_lastUpdateTimeMicroSecond == 0 || now - m_lastUpdateTimeMicroSecond > 1000000) + { + needEnable = true; + m_lastUpdateTimeMicroSecond = now; + } + } + else if (m_refreshType == RefreshType::Realtime) + { + needEnable = true; + } + } + + if (rootPass->IsTimestampQueryEnabled() != needEnable) + { + rootPass->SetTimestampQueryEnabled(needEnable); + } + const ImVec2 windowSize(680.0f, 620.0f); ImGui::SetNextWindowSize(windowSize, ImGuiCond_Always); if (ImGui::Begin("Timestamp View", &draw, ImGuiWindowFlags_NoResize)) { // Draw the header. { + // Pause/unpause the profiling + if (ImGui::Button(m_paused? "Resume":"Pause")) + { + m_paused = !m_paused; + } + // Draw the frame time (GPU). - const AZStd::string formattedTimestamp = FormatTimestampLabel(rootPassEntry->m_interpolatedTimestampInNanoseconds); + const AZStd::string formattedTimestamp = FormatTimestampLabel(gpuTimestamp.GetDurationInNanoseconds()); const AZStd::string headerFrameTime = AZStd::string::format("Total frame duration (GPU): %s", formattedTimestamp.c_str()); ImGui::Text(headerFrameTime.c_str()); @@ -566,6 +659,17 @@ namespace AZ ImGui::SameLine(); ImGui::RadioButton("Flat", reinterpret_cast(&m_viewType), static_cast(ProfilerViewType::Flat)); + // Draw the refresh option + ImGui::RadioButton("Realtime", reinterpret_cast(&m_refreshType), static_cast(RefreshType::Realtime)); + ImGui::SameLine(); + ImGui::RadioButton("Once Per Second", reinterpret_cast(&m_refreshType), static_cast(RefreshType::OncePerSecond)); + + // Show/hide non-parent passes which have zero execution time + ImGui::Checkbox("Hide Zero Cost Passes", &m_hideZeroPasses); + + // Show/hide the timeline bar of all the passes which has non-zero execution time + ImGui::Checkbox("Show Timeline", &m_showTimeline); + // Draw advanced options. const ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_None; GpuProfilerImGuiHelper::TreeNode("Advanced options", flags, [this](bool unrolled) @@ -587,6 +691,56 @@ namespace AZ ImGui::Separator(); + // Draw the pass entry grid + if (!sortedPassEntries.empty() && m_showTimeline) + { + const float passBarHeight = 20.f; + const float passBarSpace = 3.f; + float areaWidth = ImGui::GetContentRegionAvail().x - 20.f; + + if (ImGui::BeginChild("Timeline", ImVec2(areaWidth, (passBarHeight + passBarSpace) * sortedPassGrid.size()), false)) + { + // start tick and end tick for the area + uint64_t areaStartTick = sortedPassEntries.front()->m_timestampResult.GetTimestampBeginInTicks(); + uint64_t areaEndTick = sortedPassEntries.back()->m_timestampResult.GetTimestampBeginInTicks() + + sortedPassEntries.back()->m_timestampResult.GetDurationInTicks(); + uint64_t areaDurationInTicks = areaEndTick - areaStartTick; + + float rowStartY = 0.f; + for (auto& row : sortedPassGrid) + { + // row start y + for (auto passEntry : row) + { + // button start and end + float buttonStartX = (passEntry->m_timestampResult.GetTimestampBeginInTicks() - areaStartTick) * areaWidth / + areaDurationInTicks; + float buttonWidth = passEntry->m_timestampResult.GetDurationInTicks() * areaWidth / areaDurationInTicks; + ImGui::SetCursorPosX(buttonStartX); + ImGui::SetCursorPosY(rowStartY); + + // Adds a button and the hover colors. + ImGui::Button(passEntry->m_name.GetCStr(), ImVec2(buttonWidth, passBarHeight)); + + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Name: %s", passEntry->m_name.GetCStr()); + ImGui::Text("Path: %s", passEntry->m_path.GetCStr()); + ImGui::Text("Duration in ticks: %" PRIu64, passEntry->m_timestampResult.GetDurationInTicks()); + ImGui::Text("Duration in microsecond: %.3f us", passEntry->m_timestampResult.GetDurationInNanoseconds()/1000.f); + ImGui::EndTooltip(); + } + } + + rowStartY += passBarHeight + passBarSpace; + } + } + ImGui::EndChild(); + + ImGui::Separator(); + } + // Draw the timestamp view. { static const AZStd::array(TimestampMetricUnit::Count)> MetricUnitText = @@ -713,20 +867,18 @@ namespace AZ const auto drawWorkloadBar = [this](const AZStd::string& entryTime, const PassEntry* entry) { ImGui::NextColumn(); - ImGui::Text(entryTime.c_str()); - ImGui::NextColumn(); - - // Only draw the workload bar when the entry is enabled. - if (entry->IsTimestampEnabled()) + if (entry->m_isParent) { - DrawFrameWorkloadBar(NormalizeFrameWorkload(entry->m_interpolatedTimestampInNanoseconds)); + ImGui::NextColumn(); + ImGui::NextColumn(); } else { - ImGui::ProgressBar(0.0f, ImVec2(-1.0f, 0.0f), "Disabled"); + ImGui::Text(entryTime.c_str()); + ImGui::NextColumn(); + DrawFrameWorkloadBar(NormalizeFrameWorkload(entry->m_interpolatedTimestampInNanoseconds)); + ImGui::NextColumn(); } - - ImGui::NextColumn(); }; static const auto createHoverMarker = [](const char* text) @@ -800,23 +952,17 @@ namespace AZ // Draw the flat view. for (const PassEntry* entry : m_passEntryReferences) { + if (entry->m_isParent) + { + continue; + } const AZStd::string entryTime = FormatTimestampLabel(entry->m_interpolatedTimestampInNanoseconds); ImGui::Text(entry->m_name.GetCStr()); ImGui::NextColumn(); ImGui::Text(entryTime.c_str()); ImGui::NextColumn(); - - // Only draw the workload bar if the entry is enabled. - if (entry->IsTimestampEnabled()) - { - DrawFrameWorkloadBar(NormalizeFrameWorkload(entry->m_interpolatedTimestampInNanoseconds)); - } - else - { - ImGui::ProgressBar(0.0f, ImVec2(-1.0f, 0.0f), "Disabled"); - } - + DrawFrameWorkloadBar(NormalizeFrameWorkload(entry->m_interpolatedTimestampInNanoseconds)); ImGui::NextColumn(); } } @@ -890,23 +1036,33 @@ namespace AZ // Update the PassEntry database. const PassEntry* rootPassEntryRef = CreatePassEntries(rootPass); + bool wasDraw = draw; + GpuProfilerImGuiHelper::Begin("Gpu Profiler", &draw, ImGuiWindowFlags_NoResize, [this, &rootPass]() { - ImGui::Checkbox("Enable TimestampView", &m_drawTimestampView); + if (ImGui::Checkbox("Enable TimestampView", &m_drawTimestampView)) + { + rootPass->SetTimestampQueryEnabled(m_drawTimestampView); + } ImGui::Spacing(); - ImGui::Checkbox("Enable PipelineStatisticsView", &m_drawPipelineStatisticsView); + if(ImGui::Checkbox("Enable PipelineStatisticsView", &m_drawPipelineStatisticsView)) + { + rootPass->SetPipelineStatisticsQueryEnabled(m_drawPipelineStatisticsView); + } }); // Draw the PipelineStatistics window. - m_timestampView.DrawTimestampWindow(m_drawTimestampView, rootPassEntryRef, m_passEntryDatabase); + m_timestampView.DrawTimestampWindow(m_drawTimestampView, rootPassEntryRef, m_passEntryDatabase, rootPass); // Draw the PipelineStatistics window. - m_pipelineStatisticsView.DrawPipelineStatisticsWindow(m_drawPipelineStatisticsView, rootPassEntryRef, m_passEntryDatabase); + m_pipelineStatisticsView.DrawPipelineStatisticsWindow(m_drawPipelineStatisticsView, rootPassEntryRef, m_passEntryDatabase, rootPass); - // [GFX TODO][ATOM-13792] Optimization: ImGui GpuProfiler Pass hierarchy traversal. - // Enable/Disable the Timestamp and PipelineStatistics on the RootPass - rootPass->SetTimestampQueryEnabled(draw && m_drawTimestampView); - rootPass->SetPipelineStatisticsQueryEnabled(draw && m_drawPipelineStatisticsView); + //closing window + if (wasDraw && !draw) + { + rootPass->SetTimestampQueryEnabled(false); + rootPass->SetPipelineStatisticsQueryEnabled(false); + } } inline void ImGuiGpuProfiler::InterpolatePassEntries(AZStd::unordered_map& passEntryDatabase, float weight) const @@ -918,7 +1074,7 @@ namespace AZ { // Interpolate the timestamps. const double interpolated = Lerp(static_cast(oldEntryIt->second.m_interpolatedTimestampInNanoseconds), - static_cast(entry.second.m_timestampResult.GetTimestampInNanoseconds()), + static_cast(entry.second.m_timestampResult.GetDurationInNanoseconds()), static_cast(weight)); entry.second.m_interpolatedTimestampInNanoseconds = static_cast(interpolated); } diff --git a/Gems/AtomContent/Sponza/ArtSource/objects/sponza_cleanup.mb b/Gems/AtomContent/Sponza/ArtSource/objects/sponza_cleanup.mb new file mode 100644 index 0000000000..882496f2b0 --- /dev/null +++ b/Gems/AtomContent/Sponza/ArtSource/objects/sponza_cleanup.mb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d3c18d76f00688d15c54736ef3d8c953df08baf46a796fa71627de18bdb3c0f +size 22804332 diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza.fbx b/Gems/AtomContent/Sponza/Assets/objects/sponza.fbx index 80cb76941e..9061666968 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza.fbx +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza.fbx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6cbfc4d0a6722726070468da4368df7c76be67e8d067a7e3130fd29cdcdd5c6b -size 7613840 +oid sha256:35a880abc018520d4b30d21a64f7a14fca74d936593320d5afd03ddf25771bf3 +size 9176416 diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material index 8f5edb6968..da72f8a430 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/arch_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,17 @@ ], "textureMap": "Textures/arch_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.885053813457489, + 0.801281750202179, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/arch_1k_metallic.png" }, diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material index 5ba48a758e..5a195fd0b6 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/background_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,23 @@ ], "textureMap": "Textures/background_1k_basecolor.png" }, + "clearCoat": { + "enable": true, + "factor": 0.5, + "normalMap": "Textures/background_1k_normal.jpg", + "roughness": 0.4000000059604645 + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.8911573886871338, + 0.7894102334976196, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/background_1k_metallic.png" }, @@ -24,7 +45,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.03099999949336052, "pdo": true, "quality": "High", diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material index c32beebd5c..d2089b5537 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/bricks_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,23 @@ ], "textureMap": "Textures/bricks_1k_basecolor.png" }, + "clearCoat": { + "enable": true, + "factor": 0.5, + "normalMap": "Textures/bricks_1k_normal.jpg", + "roughness": 0.5 + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.9703211784362793, + 0.9703211784362793, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/bricks_1k_metallic.png" }, @@ -23,11 +44,9 @@ "factor": 1.0 }, "parallax": { - "algorithm": "POM", - "enable": true, + "algorithm": "ContactRefinement", "factor": 0.03500000014901161, - "pdo": true, - "quality": "High", + "quality": "Medium", "textureMap": "Textures/bricks_1k_height.png" }, "roughness": { diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material index cbccd8ed88..40e476305d 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material @@ -4,17 +4,55 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/ceiling_1k_ao.png" + }, "baseColor": { + "textureBlendMode": "Lerp", + "textureMap": "Textures/ceiling_1k_basecolor.png" + }, + "clearCoat": { + "enable": true, + "factor": 0.5, + "influenceMap": "Textures/ceiling_1k_ao.png", + "normalMap": "Textures/ceiling_1k_normal.png", + "roughness": 0.30000001192092898 + }, + "emissive": { "color": [ - 0.800000011920929, - 0.800000011920929, - 0.800000011920929, + 0.0, + 0.0, + 0.0, 1.0 - ], - "textureMap": "Textures/ceiling_1k_basecolor.png" + ] + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.7591058015823364, + 0.43776607513427737, + 1.0 + ] + }, + "normal": { + "textureMap": "Textures/ceiling_1k_normal.png" }, "opacity": { "factor": 1.0 + }, + "parallax": { + "algorithm": "ContactRefinement", + "factor": 0.019999999552965165, + "pdo": true, + "quality": "Medium", + "textureMap": "Textures/ceiling_1k_height.png" + }, + "roughness": { + "textureMap": "Textures/ceiling_1k_roughness.png" } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_chain.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_chain.material index cdb28a8a6f..4e39112383 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_chain.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_chain.material @@ -13,6 +13,17 @@ ], "textureMap": "Textures/chain_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.4891279339790344, + 0.7931944727897644, + 1.0, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/chain_alpha.png" }, @@ -21,7 +32,7 @@ }, "opacity": { "alphaSource": "Split", - "factor": 1.0, + "factor": 0.30000001192092898, "mode": "Cutout", "textureMap": "Textures/chain_alpha.png" }, diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material index 1889e33313..6c89c94021 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/columnA_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,23 @@ ], "textureMap": "Textures/columnA_1k_basecolor.png" }, + "clearCoat": { + "enable": true, + "factor": 0.5, + "normalMap": "Textures/columnA_1k_normal.jpg", + "roughness": 0.30000001192092898 + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.8964369893074036, + 0.8264744281768799, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/columnA_1k_metallic.png" }, @@ -24,7 +45,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.017000000923871995, "pdo": true, "quality": "High", diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material index 095afc0ab1..0be26ba553 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/columnB_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,23 @@ ], "textureMap": "Textures/columnB_1k_basecolor.png" }, + "clearCoat": { + "enable": true, + "factor": 0.5, + "normalMap": "Textures/columnB_1k_normal.jpg", + "roughness": 0.30000001192092898 + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.9015335440635681, + 0.8348516225814819, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/columnB_1k_metallic.png" }, @@ -23,7 +44,6 @@ "factor": 1.0 }, "parallax": { - "enable": true, "factor": 0.020999999716877939, "pdo": true, "quality": "High", diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material index e78cb485cc..2f1512fa4a 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/columnC_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,23 @@ ], "textureMap": "Textures/columnC_1k_basecolor.png" }, + "clearCoat": { + "enable": true, + "factor": 0.5, + "normalMap": "Textures/columnC_1k_normal.jpg", + "roughness": 0.30000001192092898 + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.9050736427307129, + 0.9050736427307129, + 1.0, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/columnC_1k_metallic.png" }, @@ -24,7 +45,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.014000000432133675, "pdo": true, "quality": "High", diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtainblue.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtainblue.material index 62942faa87..e68bc7a41a 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtainblue.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtainblue.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/curtain_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,10 +17,22 @@ ], "textureMap": "Textures/curtainBlue_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.06195162981748581, + 0.2056153267621994, + 1.0, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/curtain_metallic.png" }, "normal": { + "factor": 0.5, "textureMap": "Textures/curtain_normal.jpg" }, "opacity": { @@ -24,6 +40,9 @@ }, "roughness": { "textureMap": "Textures/curtain_roughness.png" + }, + "specularF0": { + "enableMultiScatterCompensation": true } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtaingreen.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtaingreen.material index 3284cfa837..85a5ef9775 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtaingreen.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtaingreen.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/curtain_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,10 +17,22 @@ ], "textureMap": "Textures/curtainGreen_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.0, + 1.0, + 0.029526207596063615, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/curtain_metallic.png" }, "normal": { + "factor": 0.5, "textureMap": "Textures/curtain_normal.jpg" }, "opacity": { diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtainred.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtainred.material index d07cf6d172..086f34727c 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtainred.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_curtainred.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/curtain_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,17 @@ ], "textureMap": "Textures/curtainRed_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.023315785452723504, + 0.048538949340581897, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/curtain_metallic.png" }, @@ -24,6 +39,12 @@ }, "roughness": { "textureMap": "Textures/curtain_roughness.png" + }, + "uv": { + "center": [ + 16.0, + 0.0 + ] } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material index fc5f5761d8..18bd2a307b 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/details_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,15 @@ ], "textureMap": "Textures/details_1k_basecolor.png" }, + "clearCoat": { + "enable": true, + "factor": 0.5, + "normalMap": "Textures/details_1k_normal.png", + "roughness": 0.25 + }, + "general": { + "applySpecularAA": true + }, "metallic": { "textureMap": "Textures/details_1k_metallic.png" }, @@ -24,7 +37,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.02500000037252903, "pdo": true, "textureMap": "Textures/details_1k_height.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricblue.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricblue.material index 3a555c0824..fb0490f9a9 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricblue.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricblue.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/fabric_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,10 +17,22 @@ ], "textureMap": "Textures/fabricBlue_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.0, + 0.15049973130226136, + 1.0, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/fabric_metallic.png" }, "normal": { + "factor": 0.5, "textureMap": "Textures/fabric_normal.jpg" }, "opacity": { diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricgreen.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricgreen.material index 6a95d0d275..c6074bf894 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricgreen.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricgreen.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/fabric_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,10 +17,22 @@ ], "textureMap": "Textures/fabricGreen_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.0, + 1.0, + 0.15378041565418244, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/fabric_metallic.png" }, "normal": { + "factor": 0.5, "textureMap": "Textures/fabric_normal.jpg" }, "opacity": { diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricred.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricred.material index 2558c86819..4215d8dde5 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricred.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_fabricred.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/fabric_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,10 +17,22 @@ ], "textureMap": "Textures/fabricRed_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.08197146654129029, + 0.10267795622348786, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/fabric_metallic.png" }, "normal": { + "factor": 0.5, "textureMap": "Textures/fabric_normal.jpg" }, "opacity": { diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material index c13baae474..cbba302103 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/flagpole_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,17 @@ ], "textureMap": "Textures/flagpole_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.6520485281944275, + 0.7122911214828491, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/flagpole_1k_metallic.png" }, @@ -24,7 +39,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.014000000432133675, "pdo": true, "quality": "High", @@ -32,6 +46,9 @@ }, "roughness": { "textureMap": "Textures/flagpole_1k_roughness.png" + }, + "specularF0": { + "enableMultiScatterCompensation": true } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material index cb4affe1cf..2c2abe3931 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/floor_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,23 @@ ], "textureMap": "Textures/floor_1k_basecolor.png" }, + "clearCoat": { + "enable": true, + "influenceMap": "Textures/floor_1k_ao.png", + "normalMap": "Textures/floor_1k_normal.png", + "roughness": 0.25 + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.9404135346412659, + 0.8688944578170776, + 1.0 + ] + }, "normal": { "textureMap": "Textures/floor_1k_normal.png" }, @@ -21,7 +42,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.012000000104308129, "pdo": true, "textureMap": "Textures/floor_1k_height.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material index bb226d14cb..4508a68f3f 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material @@ -13,6 +13,23 @@ ], "textureMap": "Textures/thorn_basecolor.png" }, + "clearCoat": { + "enable": true, + "factor": 0.05000000074505806, + "normalMap": "Textures/thorn_normal.jpg", + "roughness": 0.10000000149011612 + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.46506446599960329, + 1.0, + 0.3944609761238098, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/thorn_metallic.png" }, @@ -20,7 +37,8 @@ "textureMap": "Textures/thorn_normal.jpg" }, "opacity": { - "factor": 0.5699999928474426, + "doubleSided": true, + "factor": 0.20000000298023225, "mode": "Cutout" }, "parallax": { @@ -28,6 +46,25 @@ }, "roughness": { "textureMap": "Textures/thorn_roughness.png" + }, + "subsurfaceScattering": { + "enableSubsurfaceScattering": true, + "quality": 1.0, + "scatterColor": [ + 0.28143739700317385, + 1.0, + 0.13000686466693879, + 1.0 + ], + "scatterDistance": 1.0, + "thickness": 0.10000000149011612, + "transmissionMode": "ThinObject", + "transmissionTint": [ + 0.07225146889686585, + 0.16981765627861024, + 0.04444953054189682, + 1.0 + ] } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material index 4b47e1b7a1..55f44b2f63 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/lion_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,17 @@ ], "textureMap": "Textures/lion_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.7364919781684876, + 0.3672388792037964, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/lion_1k_metallic.png" }, @@ -23,14 +38,18 @@ "factor": 1.0 }, "parallax": { - "algorithm": "POM", + "algorithm": "ContactRefinement", "enable": true, - "factor": 0.023000000044703485, + "factor": 0.009999999776482582, "pdo": true, + "quality": "Ultra", "textureMap": "Textures/lion_1k_height.png" }, "roughness": { "textureMap": "Textures/lion_1k_roughness.png" + }, + "specularF0": { + "enableMultiScatterCompensation": true } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material index 3b245b251f..a64486309d 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/roof_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -11,24 +15,27 @@ 0.800000011920929, 1.0 ], + "textureBlendMode": "Lerp", "textureMap": "Textures/roof_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, "metallic": { - "factor": 0.009999999776482582, "useTexture": false }, "normal": { + "factor": 0.5, + "flipY": true, "textureMap": "Textures/roof_1k_normal.jpg" }, "opacity": { "factor": 1.0 }, "parallax": { - "algorithm": "POM", - "enable": true, + "algorithm": "ContactRefinement", "factor": 0.019999999552965165, - "pdo": true, - "quality": "High", + "quality": "Medium", "textureMap": "Textures/roof_1k_height.png" }, "roughness": { diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material index e471de4ab9..867943642e 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/vase_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,17 @@ ], "textureMap": "Textures/vase_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.8713664412498474, + 0.6021667718887329, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/vase_1k_metallic.png" }, @@ -24,7 +39,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.027000000700354577, "pdo": true, "quality": "High", @@ -32,6 +46,9 @@ }, "roughness": { "textureMap": "Textures/vase_1k_roughness.png" + }, + "specularF0": { + "enableMultiScatterCompensation": true } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material index 6bb8a205d1..9e96fb983d 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/vaseHanging_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,17 @@ ], "textureMap": "Textures/vaseHanging_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.765606164932251, + 1.0, + 0.7052567601203919, + 1.0 + ] + }, "metallic": { "textureMap": "Textures/vaseHanging_1k_metallic.png" }, @@ -24,7 +39,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.04600000008940697, "pdo": true, "quality": "High", diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseplant.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseplant.material index 0fe458b4ac..c5bfe5c6b4 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseplant.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseplant.material @@ -13,9 +13,39 @@ ], "textureMap": "Textures/vasePlant_1k_basecolor.png" }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 0.6788738965988159, + 1.0, + 0.026138704270124437, + 1.0 + ] + }, "opacity": { - "factor": 0.8399999737739563, + "doubleSided": true, + "factor": 0.28999999165534975, "mode": "Cutout" + }, + "subsurfaceScattering": { + "enableSubsurfaceScattering": true, + "quality": 1.0, + "scatterColor": [ + 0.07421988248825073, + 0.10223544389009476, + 0.0, + 1.0 + ], + "subsurfaceScatterFactor": 0.0, + "transmissionMode": "ThinObject", + "transmissionTint": [ + 0.33716335892677309, + 0.4620737135410309, + 0.0, + 1.0 + ] } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material index d2ef595c0d..ebb4e537f5 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "ambientOcclusion": { + "enable": true, + "textureMap": "Textures/vaseRound_1k_ao.png" + }, "baseColor": { "color": [ 0.800000011920929, @@ -13,6 +17,24 @@ ], "textureMap": "Textures/vaseRound_1k_basecolor.png" }, + "clearCoat": { + "enable": true, + "factor": 0.5, + "influenceMap": "Textures/vaseRound_1k_ao.png", + "normalMap": "Textures/vaseRound_1k_normal.jpg", + "roughness": 0.25 + }, + "general": { + "applySpecularAA": true + }, + "irradiance": { + "color": [ + 1.0, + 0.5939116477966309, + 0.29176774621009829, + 1.0 + ] + }, "normal": { "textureMap": "Textures/vaseRound_1k_normal.jpg" }, @@ -21,7 +43,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.019999999552965165, "pdo": true, "quality": "High", @@ -29,6 +50,9 @@ }, "roughness": { "textureMap": "Textures/vaseRound_1k_roughness.png" + }, + "specularF0": { + "enableMultiScatterCompensation": true } } } \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Project_Env.bat b/Gems/AtomContent/Sponza/Project_Env.bat index ae21e6ebc4..46d4663c34 100644 --- a/Gems/AtomContent/Sponza/Project_Env.bat +++ b/Gems/AtomContent/Sponza/Project_Env.bat @@ -44,7 +44,7 @@ CD /d %LY_PROJECT_PATH%\%DEV_REL_PATH% set LY_DEV=%CD% echo LY_DEV = %LY_DEV% -CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env.bat +CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat rem :: Constant Vars (Global) rem SET LYPY_GDEBUG=0 diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index ec7aa17151..77f15284f1 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -15,811 +15,1206 @@ #include #include #include - +#include +#include #include +#include #include #include -namespace AZ +#include + +namespace // unnamed namespace to hold copies of Cry AuxGeom state enum's, this is to avoid creating a dependency on IRenderAuxGeom.h { - namespace AtomBridge + // Notes: + // Don't change the xxxShift values, they need to match the values from legacy cry rendering + // This also applies to the individual flags in EAuxGeomPublicRenderflags_*! + // Remarks: + // Bits 0 - 22 are currently reserved for prim type and per draw call render parameters (point size, etc.) + // Check RenderAuxGeom.h in ../RenderDll/Common + enum EAuxGeomPublicRenderflagBitMasks + { + e_Mode2D3DShift = 31, + e_Mode2D3DMask = 0x1 << e_Mode2D3DShift, + + e_AlphaBlendingShift = 29, + e_AlphaBlendingMask = 0x3 << e_AlphaBlendingShift, + + e_DrawInFrontShift = 28, + e_DrawInFrontMask = 0x1 << e_DrawInFrontShift, + + e_FillModeShift = 26, + e_FillModeMask = 0x3 << e_FillModeShift, + + e_CullModeShift = 24, + e_CullModeMask = 0x3 << e_CullModeShift, + + e_DepthWriteShift = 23, + e_DepthWriteMask = 0x1 << e_DepthWriteShift, + + e_DepthTestShift = 22, + e_DepthTestMask = 0x1 << e_DepthTestShift, + + e_PublicParamsMask = e_Mode2D3DMask | e_AlphaBlendingMask | e_DrawInFrontMask | e_FillModeMask | + e_CullModeMask | e_DepthWriteMask | e_DepthTestMask + }; + + // Notes: + // e_Mode2D renders in normalized [0.. 1] screen space. + // Don't change the xxxShift values blindly as they affect the rendering output + // that is two primitives have to be rendered after 3d primitives, alpha blended + // geometry have to be rendered after opaque ones, etc. + // This also applies to the individual flags in EAuxGeomPublicRenderflagBitMasks! + // Remarks: + // Bits 0 - 22 are currently reserved for prim type and per draw call render parameters (point size, etc.) + // Check RenderAuxGeom.h in ../RenderDll/Common + // See also: + // EAuxGeomPublicRenderflagBitMasks + enum EAuxGeomPublicRenderflags_Mode2D3D + { + e_Mode3D = 0x0 << e_Mode2D3DShift, + e_Mode2D = 0x1 << e_Mode2D3DShift, + }; + + // Notes: + // Don't change the xxxShift values blindly as they affect the rendering output + // that is two primitives have to be rendered after 3d primitives, alpha blended + // geometry have to be rendered after opaque ones, etc. + // This also applies to the individual flags in EAuxGeomPublicRenderflagBitMasks! + // Remarks: + // Bits 0 - 22 are currently reserved for prim type and per draw call render parameters (point size, etc.) + // Check RenderAuxGeom.h in ../RenderDll/Common + // See also: + // EAuxGeomPublicRenderflagBitMasks + enum EAuxGeomPublicRenderflags_AlphaBlendMode + { + e_AlphaNone = 0x0 << e_AlphaBlendingShift, + e_AlphaAdditive = 0x1 << e_AlphaBlendingShift, + e_AlphaBlended = 0x2 << e_AlphaBlendingShift, + }; + + // Notes: + // Don't change the xxxShift values blindly as they affect the rendering output + // that is two primitives have to be rendered after 3d primitives, alpha blended + // geometry have to be rendered after opaque ones, etc. + // This also applies to the individual flags in EAuxGeomPublicRenderflagBitMasks! + // Remarks: + // Bits 0 - 22 are currently reserved for prim type and per draw call render parameters (point size, etc.) + // Check RenderAuxGeom.h in ../RenderDll/Common + // See also: + // EAuxGeomPublicRenderflagBitMasks + enum EAuxGeomPublicRenderflags_DrawInFrontMode + { + e_DrawInFrontOff = 0x0 << e_DrawInFrontShift, + e_DrawInFrontOn = 0x1 << e_DrawInFrontShift, + }; + + // Notes: + // Don't change the xxxShift values blindly as they affect the rendering output + // that is two primitives have to be rendered after 3d primitives, alpha blended + // geometry have to be rendered after opaque ones, etc. + // This also applies to the individual flags in EAuxGeomPublicRenderflagBitMasks! + // Remarks: + // Bits 0 - 22 are currently reserved for prim type and per draw call render parameters (point size, etc.) + // Check RenderAuxGeom.h in ../RenderDll/Common + // See also: + // EAuxGeomPublicRenderflagBitMasks + enum EAuxGeomPublicRenderflags_FillMode { + e_FillModeSolid = 0x0 << e_FillModeShift, + e_FillModeWireframe = 0x1 << e_FillModeShift, + e_FillModePoint = 0x2 << e_FillModeShift, + }; + + // Notes: + // Don't change the xxxShift values blindly as they affect the rendering output + // that is two primitives have to be rendered after 3d primitives, alpha blended + // geometry have to be rendered after opaque ones, etc. + // This also applies to the individual flags in EAuxGeomPublicRenderflagBitMasks! + // Remarks: + // Bits 0 - 22 are currently reserved for prim type and per draw call render parameters (point size, etc.) + // Check RenderAuxGeom.h in ../RenderDll/Common + // See also: + // EAuxGeomPublicRenderflagBitMasks + enum EAuxGeomPublicRenderflags_CullMode + { + e_CullModeNone = 0x0 << e_CullModeShift, + e_CullModeFront = 0x1 << e_CullModeShift, + e_CullModeBack = 0x2 << e_CullModeShift, + }; + + // Notes: + // Don't change the xxxShift values blindly as they affect the rendering output + // that is two primitives have to be rendered after 3d primitives, alpha blended + // geometry have to be rendered after opaque ones, etc. + // This also applies to the individual flags in EAuxGeomPublicRenderflagBitMasks! + // Remarks: + // Bits 0 - 22 are currently reserved for prim type and per draw call render parameters (point size, etc.) + // Check RenderAuxGeom.h in ../RenderDll/Common + // See also: + // EAuxGeomPublicRenderflagBitMasks + enum EAuxGeomPublicRenderflags_DepthWrite + { + e_DepthWriteOn = 0x0 << e_DepthWriteShift, + e_DepthWriteOff = 0x1 << e_DepthWriteShift, + }; + + // Notes: + // Don't change the xxxShift values blindly as they affect the rendering output + // that is two primitives have to be rendered after 3d primitives, alpha blended + // geometry have to be rendered after opaque ones, etc. + // This also applies to the individual flags in EAuxGeomPublicRenderflagBitMasks! + // Remarks: + // Bits 0 - 22 are currently reserved for prim type and per draw call render parameters (point size, etc.) + // Check RenderAuxGeom.h in ../RenderDll/Common + // See also: + // EAuxGeomPublicRenderflagBitMasks + enum EAuxGeomPublicRenderflags_DepthTest + { + e_DepthTestOn = 0x0 << e_DepthTestShift, + e_DepthTestOff = 0x1 << e_DepthTestShift, + }; +}; - //////////////////////////////////////////////////////////////////////// - SingleColorDynamicSizeLineHelper::SingleColorDynamicSizeLineHelper( - int estimatedNumLineSegments - ) +namespace AZ::AtomBridge +{ + + //////////////////////////////////////////////////////////////////////// + SingleColorDynamicSizeLineHelper::SingleColorDynamicSizeLineHelper( + int estimatedNumLineSegments + ) + { + m_points.reserve(estimatedNumLineSegments * 2); + } + + void SingleColorDynamicSizeLineHelper::AddLineSegment( + const AZ::Vector3& lineStart, + const AZ::Vector3& lineEnd + ) + { + m_points.push_back(lineStart); + m_points.push_back(lineEnd); + } + + void SingleColorDynamicSizeLineHelper::Draw( + AZ::RPI::AuxGeomDrawPtr auxGeomDrawPtr, + const RenderState& rendState + ) const + { + if (auxGeomDrawPtr && !m_points.empty()) { - m_points.reserve(estimatedNumLineSegments * 2); + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = m_points.data(); + drawArgs.m_vertCount = aznumeric_cast(m_points.size()); + drawArgs.m_colors = &rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = rendState.m_lineWidth; + drawArgs.m_opacityType = rendState.m_opacityType; + drawArgs.m_depthTest = rendState.m_depthTest; + drawArgs.m_depthWrite = rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = rendState.m_viewProjOverrideIndex; + auxGeomDrawPtr->DrawLines( drawArgs ); } + } - void SingleColorDynamicSizeLineHelper::AddLineSegment( - const AZ::Vector3& lineStart, - const AZ::Vector3& lineEnd - ) + void SingleColorDynamicSizeLineHelper::Draw2d( + AZ::RPI::AuxGeomDrawPtr auxGeomDrawPtr, + const RenderState& rendState + ) const + { + if (auxGeomDrawPtr && !m_points.empty()) { - m_points.push_back(lineStart); - m_points.push_back(lineEnd); + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = m_points.data(); + drawArgs.m_vertCount = aznumeric_cast(m_points.size()); + drawArgs.m_colors = &rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = rendState.m_lineWidth; + drawArgs.m_opacityType = rendState.m_opacityType; + drawArgs.m_depthTest = rendState.m_depthTest; + drawArgs.m_depthWrite = rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = auxGeomDrawPtr->GetOrAdd2DViewProjOverride(); + auxGeomDrawPtr->DrawLines( drawArgs ); } + } + + void SingleColorDynamicSizeLineHelper::Reset() + { + m_points.clear(); + } + //////////////////////////////////////////////////////////////////////// - void SingleColorDynamicSizeLineHelper::Draw( - AZ::RPI::AuxGeomDrawPtr auxGeomDrawPtr, - const RenderState& rendState - ) const + // Partial implementation of the DebugDisplayRequestBus on Atom. + // Commented out function prototypes are waiting to be implemented. + // work tracked in [ATOM-3459] + AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(AZ::RPI::ViewportContextPtr viewportContextPtr) + { + ResetRenderState(); + m_viewportId = viewportContextPtr->GetId(); + m_defaultInstance = false; + auto setupScene = [this](RPI::ScenePtr scene) { - if (auxGeomDrawPtr && !m_points.empty()) - { - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = m_points.data(); - drawArgs.m_vertCount = aznumeric_cast(m_points.size()); - drawArgs.m_colors = &rendState.m_color; - drawArgs.m_colorCount = 1; - drawArgs.m_size = rendState.m_lineWidth; - drawArgs.m_opacityType = rendState.m_opacityType; - drawArgs.m_depthTest = rendState.m_depthTest; - drawArgs.m_depthWrite = rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = rendState.m_viewProjOverrideIndex; - auxGeomDrawPtr->DrawLines( drawArgs ); - } - } + auto viewportContextManager = AZ::Interface::Get(); + AZ::RPI::ViewportContextPtr viewportContextPtr = viewportContextManager->GetViewportContextById(m_viewportId); + InitInternal(scene.get(), viewportContextPtr); + }; + setupScene(viewportContextPtr->GetRenderScene()); + m_sceneChangeHandler = AZ::RPI::ViewportContext::SceneChangedEvent::Handler(setupScene); + viewportContextPtr->ConnectSceneChangedHandler(m_sceneChangeHandler); + } - void SingleColorDynamicSizeLineHelper::Reset() + AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress) + { + ResetRenderState(); + m_viewportId = defaultInstanceAddress; + m_defaultInstance = true; + RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + InitInternal(scene, nullptr); + } + + void AtomDebugDisplayViewportInterface::InitInternal(RPI::Scene* scene, AZ::RPI::ViewportContextPtr viewportContextPtr) + { + AzFramework::DebugDisplayRequestBus::Handler::BusDisconnect(m_viewportId); + if (!scene) { - m_points.clear(); + m_auxGeomPtr = nullptr; + return; } - //////////////////////////////////////////////////////////////////////// - - // Partial implementation of the DebugDisplayRequestBus on Atom. - // Commented out function prototypes are waiting to be implemented. - // work tracked in [ATOM-3459] - AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(AZ::RPI::ViewportContextPtr viewportContextPtr) + auto auxGeomFP = scene->GetFeatureProcessor(); + if (!auxGeomFP) { - ResetRenderState(); - m_viewportId = viewportContextPtr->GetId(); - m_defaultInstance = false; - auto setupScene = [this](RPI::ScenePtr scene) - { - auto viewportContextManager = AZ::Interface::Get(); - AZ::RPI::ViewportContextPtr viewportContextPtr = viewportContextManager->GetViewportContextById(m_viewportId); - InitInternal(scene.get(), viewportContextPtr); - }; - setupScene(viewportContextPtr->GetRenderScene()); - m_sceneChangeHandler = AZ::RPI::ViewportContext::SceneChangedEvent::Handler(setupScene); - viewportContextPtr->ConnectSceneChangedHandler(m_sceneChangeHandler); + m_auxGeomPtr = nullptr; + return; } - - AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress) + if (m_defaultInstance) { - ResetRenderState(); - m_viewportId = defaultInstanceAddress; - m_defaultInstance = true; - RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); - InitInternal(scene, nullptr); + m_auxGeomPtr = auxGeomFP->GetDrawQueue(); } + else + { + m_auxGeomPtr = auxGeomFP->GetOrCreateDrawQueueForView(viewportContextPtr->GetDefaultView().get()); + } + AzFramework::DebugDisplayRequestBus::Handler::BusConnect(m_viewportId); + } - void AtomDebugDisplayViewportInterface::InitInternal(RPI::Scene* scene, AZ::RPI::ViewportContextPtr viewportContextPtr) + AtomDebugDisplayViewportInterface::~AtomDebugDisplayViewportInterface() + { + AzFramework::DebugDisplayRequestBus::Handler::BusDisconnect(m_viewportId); + m_viewportId = AzFramework::InvalidViewportId; + m_auxGeomPtr = nullptr; + } + + void AtomDebugDisplayViewportInterface::ResetRenderState() + { + m_rendState = RenderState(); + for (int index = 0; index < RenderState::TransformStackSize; ++index) { - AzFramework::DebugDisplayRequestBus::Handler::BusDisconnect(m_viewportId); - if (!scene) - { - m_auxGeomPtr = nullptr; - return; - } - auto auxGeomFP = scene->GetFeatureProcessor(); - if (!auxGeomFP) - { - m_auxGeomPtr = nullptr; - return; - } - if (m_defaultInstance) - { - m_auxGeomPtr = auxGeomFP->GetDrawQueue(); - } - else - { - m_auxGeomPtr = auxGeomFP->GetOrCreateDrawQueueForView(viewportContextPtr->GetDefaultView().get()); - } - AzFramework::DebugDisplayRequestBus::Handler::BusConnect(m_viewportId); + m_rendState.m_transformStack[index] = AZ::Matrix3x4::Identity(); } + } + + void AtomDebugDisplayViewportInterface::SetColor(float r, float g, float b, float a) + { + m_rendState.m_color = AZ::Color(r, g, b, a); + } - AtomDebugDisplayViewportInterface::~AtomDebugDisplayViewportInterface() + void AtomDebugDisplayViewportInterface::SetColor(const AZ::Color& color) + { + m_rendState.m_color = color; + } + + void AtomDebugDisplayViewportInterface::SetColor(const AZ::Vector4& color) + { + m_rendState.m_color = AZ::Color(color); + } + + void AtomDebugDisplayViewportInterface::SetAlpha(float a) + { + m_rendState.m_color.SetA(a); + if (a < 1.0f) { - AzFramework::DebugDisplayRequestBus::Handler::BusDisconnect(m_viewportId); - m_viewportId = AzFramework::InvalidViewportId; - m_auxGeomPtr = nullptr; + m_rendState.m_opacityType = AZ::RPI::AuxGeomDraw::OpacityType::Opaque; } + else + { + m_rendState.m_opacityType = AZ::RPI::AuxGeomDraw::OpacityType::Translucent; + } + } - void AtomDebugDisplayViewportInterface::ResetRenderState() + void AtomDebugDisplayViewportInterface::DrawQuad( + const AZ::Vector3& p1, + const AZ::Vector3& p2, + const AZ::Vector3& p3, + const AZ::Vector3& p4) + { + if (m_auxGeomPtr) { - m_rendState = RenderState(); - for (int index = 0; index < RenderState::TransformStackSize; ++index) - { - m_rendState.m_transformStack[index] = AZ::Matrix3x4::Identity(); - } + AZ::Vector3 wsPoints[4] = { ToWorldSpacePosition(p1), ToWorldSpacePosition(p2), ToWorldSpacePosition(p3), ToWorldSpacePosition(p4) }; + AZ::Vector3 triangles[6]; + triangles[0] = wsPoints[0]; + triangles[1] = wsPoints[1]; + triangles[2] = wsPoints[2]; + triangles[3] = wsPoints[2]; + triangles[4] = wsPoints[3]; + triangles[5] = wsPoints[0]; + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = triangles; + drawArgs.m_vertCount = 6; + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawTriangles(drawArgs); } + } - void AtomDebugDisplayViewportInterface::SetColor(float r, float g, float b, float a) + void AtomDebugDisplayViewportInterface::DrawQuad(float width, float height) + { + if (!m_auxGeomPtr || width <= 0.0f || height <= 0.0f) { - m_rendState.m_color = AZ::Color(r, g, b, a); + return; } - void AtomDebugDisplayViewportInterface::SetColor(const AZ::Color& color) + m_auxGeomPtr->DrawQuad( + width, + height, + GetCurrentTransform(), + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Shaded, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex); + } + + void AtomDebugDisplayViewportInterface::DrawWireQuad( + const AZ::Vector3& p1, + const AZ::Vector3& p2, + const AZ::Vector3& p3, + const AZ::Vector3& p4) + { + if (m_auxGeomPtr) { - m_rendState.m_color = color; + AZ::Vector3 wsPoints[4] = { ToWorldSpacePosition(p1), ToWorldSpacePosition(p2), ToWorldSpacePosition(p3), ToWorldSpacePosition(p4) }; + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = wsPoints; + drawArgs.m_vertCount = 4; + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawPolylines(drawArgs, AZ::RPI::AuxGeomDraw::PolylineEnd::Closed); } + } - void AtomDebugDisplayViewportInterface::SetColor(const AZ::Vector4& color) + void AtomDebugDisplayViewportInterface::DrawWireQuad(float width, float height) + { + if (!m_auxGeomPtr || width <= 0.0f || height <= 0.0f) { - m_rendState.m_color = AZ::Color(color); + return; } - void AtomDebugDisplayViewportInterface::SetAlpha(float a) + m_auxGeomPtr->DrawQuad( + width, + height, + GetCurrentTransform(), + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Line, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex); + } + + void AtomDebugDisplayViewportInterface::DrawQuadGradient( + const AZ::Vector3& p1, + const AZ::Vector3& p2, + const AZ::Vector3& p3, + const AZ::Vector3& p4, + const AZ::Vector4& firstColor, + const AZ::Vector4& secondColor) + { + if (m_auxGeomPtr) { - m_rendState.m_color.SetA(a); - if (a < 1.0f) - { - m_rendState.m_opacityType = AZ::RPI::AuxGeomDraw::OpacityType::Opaque; - } - else - { - m_rendState.m_opacityType = AZ::RPI::AuxGeomDraw::OpacityType::Translucent; - } + AZ::Vector3 wsPoints[4] = { ToWorldSpacePosition(p1), ToWorldSpacePosition(p2), ToWorldSpacePosition(p3), ToWorldSpacePosition(p4) }; + AZ::Vector3 triangles[6]; + AZ::Color colors[6]; + triangles[0] = wsPoints[0]; colors[0] = firstColor; + triangles[1] = wsPoints[1]; colors[1] = firstColor; + triangles[2] = wsPoints[2]; colors[2] = secondColor; + triangles[3] = wsPoints[2]; colors[3] = secondColor; + triangles[4] = wsPoints[3]; colors[4] = secondColor; + triangles[5] = wsPoints[0]; colors[5] = firstColor; + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = triangles; + drawArgs.m_vertCount = 6; + drawArgs.m_colors = colors; + drawArgs.m_colorCount = 6; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawTriangles(drawArgs); } + } - void AtomDebugDisplayViewportInterface::DrawQuad( - const AZ::Vector3& p1, - const AZ::Vector3& p2, - const AZ::Vector3& p3, - const AZ::Vector3& p4) + void AtomDebugDisplayViewportInterface::DrawTri(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::Vector3 wsPoints[4] = { ToWorldSpacePosition(p1), ToWorldSpacePosition(p2), ToWorldSpacePosition(p3), ToWorldSpacePosition(p4) }; - AZ::Vector3 triangles[6]; - triangles[0] = wsPoints[0]; - triangles[1] = wsPoints[1]; - triangles[2] = wsPoints[2]; - triangles[3] = wsPoints[2]; - triangles[4] = wsPoints[3]; - triangles[5] = wsPoints[0]; - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = triangles; - drawArgs.m_vertCount = 6; - drawArgs.m_colors = &m_rendState.m_color; - drawArgs.m_colorCount = 1; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawTriangles(drawArgs); - } + AZ::Vector3 verts[3] = {ToWorldSpacePosition(p1), ToWorldSpacePosition(p2), ToWorldSpacePosition(p3)}; + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = verts; + drawArgs.m_vertCount = 3; + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawTriangles(drawArgs); } + } - // void DrawQuad(float width, float height) override - // void DrawWireQuad(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, const AZ::Vector3& p4) override; - // void DrawWireQuad(float width, float height) override; + void AtomDebugDisplayViewportInterface::DrawTriangles(const AZStd::vector& vertices, const AZ::Color& color) + { + if (m_auxGeomPtr) + { + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = vertices.data(); + drawArgs.m_vertCount = aznumeric_cast(vertices.size()); + drawArgs.m_colors = &color; + drawArgs.m_colorCount = 1; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawTriangles(drawArgs); + } + } - void AtomDebugDisplayViewportInterface::DrawQuadGradient( - const AZ::Vector3& p1, - const AZ::Vector3& p2, - const AZ::Vector3& p3, - const AZ::Vector3& p4, - const AZ::Vector4& firstColor, - const AZ::Vector4& secondColor) + void AtomDebugDisplayViewportInterface::DrawTrianglesIndexed( + const AZStd::vector& vertices, + const AZStd::vector& indices, + const AZ::Color& color) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::Vector3 wsPoints[4] = { ToWorldSpacePosition(p1), ToWorldSpacePosition(p2), ToWorldSpacePosition(p3), ToWorldSpacePosition(p4) }; - AZ::Vector3 triangles[6]; - AZ::Color colors[6]; - triangles[0] = wsPoints[0]; colors[0] = firstColor; - triangles[1] = wsPoints[1]; colors[1] = firstColor; - triangles[2] = wsPoints[2]; colors[2] = secondColor; - triangles[3] = wsPoints[2]; colors[3] = secondColor; - triangles[4] = wsPoints[3]; colors[4] = secondColor; - triangles[5] = wsPoints[0]; colors[5] = firstColor; - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = triangles; - drawArgs.m_vertCount = 6; - drawArgs.m_colors = colors; - drawArgs.m_colorCount = 6; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawTriangles(drawArgs); - } + AZ::RPI::AuxGeomDraw::AuxGeomDynamicIndexedDrawArguments drawArgs; + drawArgs.m_verts = vertices.data(); + drawArgs.m_vertCount = aznumeric_cast(vertices.size()); + drawArgs.m_indices = indices.data(); + drawArgs.m_indexCount = aznumeric_cast(indices.size()); + drawArgs.m_colors = &color; + drawArgs.m_colorCount = 1; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawTriangles(drawArgs); } + } - void AtomDebugDisplayViewportInterface::DrawTri(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3) + void AtomDebugDisplayViewportInterface::DrawWireBox(const AZ::Vector3& min, const AZ::Vector3& max) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::Vector3 verts[3] = {ToWorldSpacePosition(p1), ToWorldSpacePosition(p2), ToWorldSpacePosition(p3)}; - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = verts; - drawArgs.m_vertCount = 3; - drawArgs.m_colors = &m_rendState.m_color; - drawArgs.m_colorCount = 1; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawTriangles(drawArgs); - } + m_auxGeomPtr->DrawAabb( + AZ::Aabb::CreateFromMinMax(min, max), + GetCurrentTransform(), + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Line, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex + ); } + } - void AtomDebugDisplayViewportInterface::DrawTriangles(const AZStd::vector& vertices, const AZ::Color& color) + void AtomDebugDisplayViewportInterface::DrawSolidBox(const AZ::Vector3& min, const AZ::Vector3& max) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = vertices.data(); - drawArgs.m_vertCount = aznumeric_cast(vertices.size()); - drawArgs.m_colors = &color; - drawArgs.m_colorCount = 1; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawTriangles(drawArgs); - } + m_auxGeomPtr->DrawAabb( + AZ::Aabb::CreateFromMinMax(min, max), + GetCurrentTransform(), + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Solid, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex); } + } - void AtomDebugDisplayViewportInterface::DrawTrianglesIndexed( - const AZStd::vector& vertices, - const AZStd::vector& indices, - const AZ::Color& color) + void AtomDebugDisplayViewportInterface::DrawSolidOBB( + const AZ::Vector3& center, + const AZ::Vector3& axisX, + const AZ::Vector3& axisY, + const AZ::Vector3& axisZ, + const AZ::Vector3& halfExtents) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::RPI::AuxGeomDraw::AuxGeomDynamicIndexedDrawArguments drawArgs; - drawArgs.m_verts = vertices.data(); - drawArgs.m_vertCount = aznumeric_cast(vertices.size()); - drawArgs.m_indices = indices.data(); - drawArgs.m_indexCount = aznumeric_cast(indices.size()); - drawArgs.m_colors = &color; - drawArgs.m_colorCount = 1; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawTriangles(drawArgs); - } + AZ::Quaternion rotation = AZ::Quaternion::CreateFromMatrix3x3(AZ::Matrix3x3::CreateFromColumns(axisX, axisY, axisZ)); + AZ::Obb obb = AZ::Obb::CreateFromPositionRotationAndHalfLengths(center, rotation, halfExtents); + m_auxGeomPtr->DrawObb( + obb, + AZ::Vector3::CreateZero(), + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Solid, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex); } + } - void AtomDebugDisplayViewportInterface::DrawWireBox(const AZ::Vector3& min, const AZ::Vector3& max) + void AtomDebugDisplayViewportInterface::DrawPoint(const AZ::Vector3& p, int nSize) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - m_auxGeomPtr->DrawAabb( - AZ::Aabb::CreateFromMinMax(min, max), - GetCurrentTransform(), - m_rendState.m_color, - AZ::RPI::AuxGeomDraw::DrawStyle::Line, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex - ); - } + AZ::Vector3 wsPoint = ToWorldSpacePosition(p); + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = &wsPoint; + drawArgs.m_vertCount = 1; + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = aznumeric_cast(nSize); + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawPoints(drawArgs); } + } - void AtomDebugDisplayViewportInterface::DrawSolidBox(const AZ::Vector3& min, const AZ::Vector3& max) + void AtomDebugDisplayViewportInterface::DrawLine(const AZ::Vector3& p1, const AZ::Vector3& p2) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - m_auxGeomPtr->DrawAabb( - AZ::Aabb::CreateFromMinMax(min, max), - GetCurrentTransform(), - m_rendState.m_color, - AZ::RPI::AuxGeomDraw::DrawStyle::Solid, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex); - } + AZ::Vector3 verts[2] = {ToWorldSpacePosition(p1), ToWorldSpacePosition(p2)}; + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = verts; + drawArgs.m_vertCount = 2; + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = m_rendState.m_lineWidth; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawLines(drawArgs); } + } - void AtomDebugDisplayViewportInterface::DrawSolidOBB( - const AZ::Vector3& center, - const AZ::Vector3& axisX, - const AZ::Vector3& axisY, - const AZ::Vector3& axisZ, - const AZ::Vector3& halfExtents) + void AtomDebugDisplayViewportInterface::DrawLine(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector4& col1, const AZ::Vector4& col2) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::Quaternion rotation = AZ::Quaternion::CreateFromMatrix3x3(AZ::Matrix3x3::CreateFromColumns(axisX, axisY, axisZ)); - AZ::Obb obb = AZ::Obb::CreateFromPositionRotationAndHalfLengths(center, rotation, halfExtents); - m_auxGeomPtr->DrawObb( - obb, - AZ::Vector3::CreateZero(), - m_rendState.m_color, - AZ::RPI::AuxGeomDraw::DrawStyle::Solid, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex); - } + AZ::Vector3 verts[2] = {ToWorldSpacePosition(p1), ToWorldSpacePosition(p2)}; + AZ::Color colors[2] = {col1, col2}; + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = verts; + drawArgs.m_vertCount = 2; + drawArgs.m_colors = colors; + drawArgs.m_colorCount = 2; + drawArgs.m_size = m_rendState.m_lineWidth; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawLines(drawArgs); } + } - void AtomDebugDisplayViewportInterface::DrawPoint(const AZ::Vector3& p, int nSize) + void AtomDebugDisplayViewportInterface::DrawLines(const AZStd::vector& lines, const AZ::Color& color) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::Vector3 wsPoint = ToWorldSpacePosition(p); - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = &wsPoint; - drawArgs.m_vertCount = 1; - drawArgs.m_colors = &m_rendState.m_color; - drawArgs.m_colorCount = 1; - drawArgs.m_size = aznumeric_cast(nSize); - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawPoints(drawArgs); - } + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = lines.data(); + drawArgs.m_vertCount = aznumeric_cast(lines.size()); + drawArgs.m_colors = &color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = m_rendState.m_lineWidth; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawLines(drawArgs); } + } - void AtomDebugDisplayViewportInterface::DrawLine(const AZ::Vector3& p1, const AZ::Vector3& p2) + void AtomDebugDisplayViewportInterface::DrawPolyLine(const AZ::Vector3* pnts, int numPoints, bool cycled) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) + AZStd::vector wsPoints(static_cast(numPoints)); + for (int index = 0; index < numPoints; ++index) { - AZ::Vector3 verts[2] = {ToWorldSpacePosition(p1), ToWorldSpacePosition(p2)}; - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = verts; - drawArgs.m_vertCount = 2; - drawArgs.m_colors = &m_rendState.m_color; - drawArgs.m_colorCount = 1; - drawArgs.m_size = m_rendState.m_lineWidth; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawLines(drawArgs); + wsPoints[index] = ToWorldSpacePosition(pnts[index]); } + AZ::RPI::AuxGeomDraw::PolylineEnd polylineEnd = cycled ? AZ::RPI::AuxGeomDraw::PolylineEnd::Closed : AZ::RPI::AuxGeomDraw::PolylineEnd::Open; + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = wsPoints.data(); + drawArgs.m_vertCount = aznumeric_cast(numPoints); + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = m_rendState.m_lineWidth; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + m_auxGeomPtr->DrawPolylines(drawArgs, polylineEnd); } + } - void AtomDebugDisplayViewportInterface::DrawLine(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector4& col1, const AZ::Vector4& col2) + void AtomDebugDisplayViewportInterface::DrawWireQuad2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::Vector3 verts[2] = {ToWorldSpacePosition(p1), ToWorldSpacePosition(p2)}; - AZ::Color colors[2] = {col1, col2}; - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = verts; - drawArgs.m_vertCount = 2; - drawArgs.m_colors = colors; - drawArgs.m_colorCount = 2; - drawArgs.m_size = m_rendState.m_lineWidth; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawLines(drawArgs); - } + AZ::Vector3 points[4]; + points[0] = AZ::Vector3(p1.GetX(), p1.GetY(), z); + points[1] = AZ::Vector3(p2.GetX(), p1.GetY(), z); + points[2] = AZ::Vector3(p2.GetX(), p2.GetY(), z); + points[3] = AZ::Vector3(p1.GetX(), p2.GetY(), z); + + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = points; + drawArgs.m_vertCount = 4; + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = m_rendState.m_lineWidth; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_auxGeomPtr->GetOrAdd2DViewProjOverride(); + m_auxGeomPtr->DrawPolylines(drawArgs, AZ::RPI::AuxGeomDraw::PolylineEnd::Closed); } + } - void AtomDebugDisplayViewportInterface::DrawLines(const AZStd::vector& lines, const AZ::Color& color) + void AtomDebugDisplayViewportInterface::DrawLine2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = lines.data(); - drawArgs.m_vertCount = aznumeric_cast(lines.size()); - drawArgs.m_colors = &color; - drawArgs.m_colorCount = 1; - drawArgs.m_size = m_rendState.m_lineWidth; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawLines(drawArgs); - } + AZ::Vector3 points[2]; + points[0] = AZ::Vector3(p1.GetX(), p1.GetY(), z); + points[1] = AZ::Vector3(p2.GetX(), p2.GetY(), z); + + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = points; + drawArgs.m_vertCount = 2; + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = m_rendState.m_lineWidth; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_auxGeomPtr->GetOrAdd2DViewProjOverride(); + m_auxGeomPtr->DrawLines(drawArgs); } + } - void AtomDebugDisplayViewportInterface::DrawPolyLine(const AZ::Vector3* pnts, int numPoints, bool cycled) + void AtomDebugDisplayViewportInterface::DrawLine2dGradient(const AZ::Vector2& p1, const AZ::Vector2& p2, float z, const AZ::Vector4& firstColor, const AZ::Vector4& secondColor) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - AZStd::vector wsPoints(static_cast(numPoints)); - for (int index = 0; index < numPoints; ++index) - { - wsPoints[index] = ToWorldSpacePosition(pnts[index]); - } - AZ::RPI::AuxGeomDraw::PolylineEnd polylineEnd = cycled ? AZ::RPI::AuxGeomDraw::PolylineEnd::Closed : AZ::RPI::AuxGeomDraw::PolylineEnd::Open; - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = wsPoints.data(); - drawArgs.m_vertCount = aznumeric_cast(numPoints); - drawArgs.m_colors = &m_rendState.m_color; - drawArgs.m_colorCount = 1; - drawArgs.m_size = m_rendState.m_lineWidth; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - m_auxGeomPtr->DrawPolylines(drawArgs, polylineEnd); - } + AZ::Vector3 points[2]; + points[0] = AZ::Vector3(p1.GetX(), p1.GetY(), z); + points[1] = AZ::Vector3(p2.GetX(), p2.GetY(), z); + AZ::Color colors[2] = {firstColor, secondColor}; + + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = points; + drawArgs.m_vertCount = 2; + drawArgs.m_colors = colors; + drawArgs.m_colorCount = 2; + drawArgs.m_size = m_rendState.m_lineWidth; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_auxGeomPtr->GetOrAdd2DViewProjOverride(); + m_auxGeomPtr->DrawLines(drawArgs); } + } - // void AtomDebugDisplayViewportInterface::DrawWireQuad2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) override; - // void AtomDebugDisplayViewportInterface::DrawLine2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) override; - // void AtomDebugDisplayViewportInterface::DrawLine2dGradient(const AZ::Vector2& p1, const AZ::Vector2& p2, float z, const AZ::Vector4& firstColor, const AZ::Vector4& secondColor) override; - // void AtomDebugDisplayViewportInterface::DrawWireCircle2d(const AZ::Vector2& center, float radius, float z) override; + void AtomDebugDisplayViewportInterface::DrawWireCircle2d(const AZ::Vector2& center, float radius, float z) + { + if (m_auxGeomPtr) + { + // Draw axis aligned arc + constexpr float angularStepDegrees = 10.0f; + constexpr float startAngleDegrees = 0.0f; + constexpr float sweepAngleDegrees = 360.0f; + const float stepAngle = DegToRad(angularStepDegrees); + const float startAngle = DegToRad(startAngleDegrees); + const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle; + SingleColorDynamicSizeLineHelper lines(1+static_cast(sweepAngleDegrees/angularStepDegrees)); + AZ::Vector3 radiusV3 = AZ::Vector3(radius); + AZ::Vector3 pos = AZ::Vector3(center.GetX(), center.GetY(), z); + CreateAxisAlignedArc( + lines, + stepAngle, + startAngle, + stopAngle, + pos, + radiusV3, + CircleAxis::CircleAxisZ + ); + lines.Draw2d(m_auxGeomPtr, m_rendState); + } + } - void AtomDebugDisplayViewportInterface::DrawArc( - const AZ::Vector3& pos, - float radius, - float startAngleDegrees, - float sweepAngleDegrees, - float angularStepDegrees, - int referenceAxis) + void AtomDebugDisplayViewportInterface::DrawArc( + const AZ::Vector3& pos, + float radius, + float startAngleDegrees, + float sweepAngleDegrees, + float angularStepDegrees, + int referenceAxis) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - // Draw axis aligned arc - const float stepAngle = DegToRad(angularStepDegrees); - const float startAngle = DegToRad(startAngleDegrees); - const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle; - SingleColorDynamicSizeLineHelper lines(1+static_cast(sweepAngleDegrees/angularStepDegrees)); - AZ::Vector3 radiusV3 = AZ::Vector3(radius); - CreateAxisAlignedArc( - lines, - stepAngle, - startAngle, - stopAngle, - pos, - radiusV3, - static_cast(referenceAxis) - ); - lines.Draw(m_auxGeomPtr, m_rendState); - } + // Draw axis aligned arc + const float stepAngle = DegToRad(angularStepDegrees); + const float startAngle = DegToRad(startAngleDegrees); + const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle; + SingleColorDynamicSizeLineHelper lines(1+static_cast(sweepAngleDegrees/angularStepDegrees)); + AZ::Vector3 radiusV3 = AZ::Vector3(radius); + CreateAxisAlignedArc( + lines, + stepAngle, + startAngle, + stopAngle, + pos, + radiusV3, + static_cast(referenceAxis) + ); + lines.Draw(m_auxGeomPtr, m_rendState); } + } - void AtomDebugDisplayViewportInterface::DrawArc( - const AZ::Vector3& pos, - float radius, - float startAngleDegrees, - float sweepAngleDegrees, - float angularStepDegrees, - const AZ::Vector3& fixedAxis) + void AtomDebugDisplayViewportInterface::DrawArc( + const AZ::Vector3& pos, + float radius, + float startAngleDegrees, + float sweepAngleDegrees, + float angularStepDegrees, + const AZ::Vector3& fixedAxis) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - // Draw arbitraty axis arc - const float stepAngle = DegToRad(angularStepDegrees); - const float startAngle = DegToRad(startAngleDegrees); - const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle; - SingleColorDynamicSizeLineHelper lines(1+static_cast(sweepAngleDegrees/angularStepDegrees)); - AZ::Vector3 radiusV3 = AZ::Vector3(radius); - CreateArbitraryAxisArc( - lines, - stepAngle, - startAngle, - stopAngle, - pos, - radiusV3, - fixedAxis - ); - lines.Draw(m_auxGeomPtr, m_rendState); - } + // Draw arbitraty axis arc + const float stepAngle = DegToRad(angularStepDegrees); + const float startAngle = DegToRad(startAngleDegrees); + const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle; + SingleColorDynamicSizeLineHelper lines(1+static_cast(sweepAngleDegrees/angularStepDegrees)); + AZ::Vector3 radiusV3 = AZ::Vector3(radius); + CreateArbitraryAxisArc( + lines, + stepAngle, + startAngle, + stopAngle, + pos, + radiusV3, + fixedAxis + ); + lines.Draw(m_auxGeomPtr, m_rendState); } - - void AtomDebugDisplayViewportInterface::DrawCircle(const AZ::Vector3& pos, float radius, int nUnchangedAxis) + } + + void AtomDebugDisplayViewportInterface::DrawCircle(const AZ::Vector3& pos, float radius, int nUnchangedAxis) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - // Draw circle with default radius. - const float step = DegToRad(10.0f); - const float maxAngle = DegToRad(360.0f) + step; - SingleColorStaticSizeLineHelper<40> lines; // hard code 40 lines until DegToRad is constexpr. - AZ::Vector3 radiusV3 = AZ::Vector3(radius); - CreateAxisAlignedArc( - lines, - step, - 0.0f, - maxAngle, - pos, - radiusV3, - static_cast(nUnchangedAxis)); - lines.Draw(m_auxGeomPtr, m_rendState); - } + // Draw circle with default radius. + const float step = DegToRad(10.0f); + const float maxAngle = DegToRad(360.0f) + step; + SingleColorStaticSizeLineHelper<40> lines; // hard code 40 lines until DegToRad is constexpr. + AZ::Vector3 radiusV3 = AZ::Vector3(radius); + CreateAxisAlignedArc( + lines, + step, + 0.0f, + maxAngle, + pos, + radiusV3, + static_cast(nUnchangedAxis)); + lines.Draw(m_auxGeomPtr, m_rendState); } + } - void AtomDebugDisplayViewportInterface::DrawHalfDottedCircle(const AZ::Vector3& pos, float radius, const AZ::Vector3& viewPos, int nUnchangedAxis) + void AtomDebugDisplayViewportInterface::DrawHalfDottedCircle(const AZ::Vector3& pos, float radius, const AZ::Vector3& viewPos, int nUnchangedAxis) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - // Draw circle with single radius. - const float step = DegToRad(10.0f); - const float maxAngle = DegToRad(360.0f) + step; - SingleColorStaticSizeLineHelper<40> lines; // hard code 40 lines until DegToRad is constexpr. - - AZ::Vector3 radiusV3 = AZ::Vector3(radius); - const AZ::Vector3 worldPos = ToWorldSpacePosition(pos); - const AZ::Vector3 worldView = ToWorldSpacePosition(viewPos); - const AZ::Vector3 worldDir = worldView - worldPos; - - CreateAxisAlignedArc(lines, step, 0.0f, maxAngle, pos, radiusV3, static_cast(nUnchangedAxis%CircleAxisMax), - [&worldPos, &worldDir](const AZ::Vector3& lineStart, const AZ::Vector3& lineEnd, int segmentIndex) + // Draw circle with single radius. + const float step = DegToRad(10.0f); + const float maxAngle = DegToRad(360.0f) + step; + SingleColorStaticSizeLineHelper<40> lines; // hard code 40 lines until DegToRad is constexpr. + + AZ::Vector3 radiusV3 = AZ::Vector3(radius); + const AZ::Vector3 worldPos = ToWorldSpacePosition(pos); + const AZ::Vector3 worldView = ToWorldSpacePosition(viewPos); + const AZ::Vector3 worldDir = worldView - worldPos; + + CreateAxisAlignedArc(lines, step, 0.0f, maxAngle, pos, radiusV3, static_cast(nUnchangedAxis%CircleAxisMax), + [&worldPos, &worldDir](const AZ::Vector3& lineStart, const AZ::Vector3& lineEnd, int segmentIndex) + { + AZ_UNUSED(lineEnd); + const float dot = (lineStart - worldPos).Dot(worldDir); + const bool facing = dot > 0.0f; + // if so skip every other line to produce a dotted effect + if (facing || segmentIndex % 2 == 0) { - AZ_UNUSED(lineEnd); - const float dot = (lineStart - worldPos).Dot(worldDir); - const bool facing = dot > 0.0f; - // if so skip every other line to produce a dotted effect - if (facing || segmentIndex % 2 == 0) - { - return true; - } - return false; - }); - lines.Draw(m_auxGeomPtr, m_rendState); - } + return true; + } + return false; + }); + lines.Draw(m_auxGeomPtr, m_rendState); } + } - void AtomDebugDisplayViewportInterface::DrawCone(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius, float height, bool drawShaded) + void AtomDebugDisplayViewportInterface::DrawCone(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius, float height, bool drawShaded) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - const AZ::Vector3 worldPos = ToWorldSpacePosition(pos); - const AZ::Vector3 worldDir = ToWorldSpaceVector(dir); - m_auxGeomPtr->DrawCone( - worldPos, - worldDir, - radius, - height, - m_rendState.m_color, - drawShaded ? AZ::RPI::AuxGeomDraw::DrawStyle::Shaded : AZ::RPI::AuxGeomDraw::DrawStyle::Solid, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex - ); - } + const AZ::Vector3 worldPos = ToWorldSpacePosition(pos); + const AZ::Vector3 worldDir = ToWorldSpaceVector(dir); + m_auxGeomPtr->DrawCone( + worldPos, + worldDir, + radius, + height, + m_rendState.m_color, + drawShaded ? AZ::RPI::AuxGeomDraw::DrawStyle::Shaded : AZ::RPI::AuxGeomDraw::DrawStyle::Solid, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex + ); } - - void AtomDebugDisplayViewportInterface::DrawWireCylinder(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float height) + } + + void AtomDebugDisplayViewportInterface::DrawWireCylinder(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float height) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - const AZ::Vector3 worldCenter = ToWorldSpacePosition(center); - const AZ::Vector3 worldAxis = ToWorldSpaceVector(axis); - m_auxGeomPtr->DrawCylinder( - worldCenter, - worldAxis, - radius, - height, - m_rendState.m_color, - AZ::RPI::AuxGeomDraw::DrawStyle::Line, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex - ); - } + const AZ::Vector3 worldCenter = ToWorldSpacePosition(center); + const AZ::Vector3 worldAxis = ToWorldSpaceVector(axis); + m_auxGeomPtr->DrawCylinder( + worldCenter, + worldAxis, + radius, + height, + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Line, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex + ); } + } - void AtomDebugDisplayViewportInterface::DrawSolidCylinder( - const AZ::Vector3& center, - const AZ::Vector3& axis, - float radius, - float height, - bool drawShaded) + void AtomDebugDisplayViewportInterface::DrawSolidCylinder( + const AZ::Vector3& center, + const AZ::Vector3& axis, + float radius, + float height, + bool drawShaded) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - const AZ::Vector3 worldCenter = ToWorldSpacePosition(center); - const AZ::Vector3 worldAxis = ToWorldSpaceVector(axis); - m_auxGeomPtr->DrawCylinder( - worldCenter, - worldAxis, - radius, - height, - m_rendState.m_color, - drawShaded ? AZ::RPI::AuxGeomDraw::DrawStyle::Shaded : AZ::RPI::AuxGeomDraw::DrawStyle::Solid, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex - ); - } + const AZ::Vector3 worldCenter = ToWorldSpacePosition(center); + const AZ::Vector3 worldAxis = ToWorldSpaceVector(axis); + m_auxGeomPtr->DrawCylinder( + worldCenter, + worldAxis, + radius, + height, + m_rendState.m_color, + drawShaded ? AZ::RPI::AuxGeomDraw::DrawStyle::Shaded : AZ::RPI::AuxGeomDraw::DrawStyle::Solid, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex + ); } + } - void AtomDebugDisplayViewportInterface::DrawWireCapsule( - const AZ::Vector3& center, - const AZ::Vector3& axis, - float radius, - float heightStraightSection) + void AtomDebugDisplayViewportInterface::DrawWireCapsule( + const AZ::Vector3& center, + const AZ::Vector3& axis, + float radius, + float heightStraightSection) + { + if (m_auxGeomPtr && radius > FLT_EPSILON && axis.GetLengthSq() > FLT_EPSILON) { - if (m_auxGeomPtr && radius > FLT_EPSILON && axis.GetLengthSq() > FLT_EPSILON) - { - AZ::Vector3 axisNormalized = axis.GetNormalizedEstimate(); - SingleColorStaticSizeLineHelper<(16+1) * 5> lines; // 360/22.5 = 16, 5 possible calls to CreateArbitraryAxisArc - AZ::Vector3 radiusV3 = AZ::Vector3(radius); - float stepAngle = DegToRad(22.5f); - float Deg0 = DegToRad(0.0f); - - - // Draw cylinder part (or just a circle around the middle) - if (heightStraightSection > FLT_EPSILON) - { - DrawWireCylinder(center, axis, radius, heightStraightSection); - } - else - { - float Deg360 = DegToRad(360.0f); - CreateArbitraryAxisArc( - lines, - stepAngle, - Deg0, - Deg360, - center, - radiusV3, - axisNormalized - ); - } - - float Deg90 = DegToRad(90.0f); - float Deg180 = DegToRad(180.0f); - - AZ::Vector3 ortho1Normalized, ortho2Normalized; - CalcBasisVectors(axisNormalized, ortho1Normalized, ortho2Normalized); - AZ::Vector3 centerToTopCircleCenter = axisNormalized * heightStraightSection * 0.5f; - AZ::Vector3 topCenter = center + centerToTopCircleCenter; - AZ::Vector3 bottomCenter = center - centerToTopCircleCenter; - - // Draw top cap as two criss-crossing 180deg arcs - CreateArbitraryAxisArc( - lines, - stepAngle, - Deg90, - Deg90 + Deg180, - topCenter, - radiusV3, - ortho1Normalized - ); + AZ::Vector3 axisNormalized = axis.GetNormalizedEstimate(); + SingleColorStaticSizeLineHelper<(16+1) * 5> lines; // 360/22.5 = 16, 5 possible calls to CreateArbitraryAxisArc + AZ::Vector3 radiusV3 = AZ::Vector3(radius); + float stepAngle = DegToRad(22.5f); + float Deg0 = DegToRad(0.0f); - CreateArbitraryAxisArc( - lines, - stepAngle, - Deg180, - Deg180 + Deg180, - topCenter, - radiusV3, - ortho2Normalized - ); - - // Draw bottom cap - CreateArbitraryAxisArc( - lines, - stepAngle, - -Deg90, - -Deg90 + Deg180, - bottomCenter, - radiusV3, - ortho1Normalized - ); + // Draw cylinder part (or just a circle around the middle) + if (heightStraightSection > FLT_EPSILON) + { + DrawWireCylinder(center, axis, radius, heightStraightSection); + } + else + { + float Deg360 = DegToRad(360.0f); CreateArbitraryAxisArc( - lines, - stepAngle, - Deg0, - Deg0 + Deg180, - bottomCenter, - radiusV3, - ortho2Normalized - ); - - lines.Draw(m_auxGeomPtr, m_rendState); + lines, + stepAngle, + Deg0, + Deg360, + center, + radiusV3, + axisNormalized + ); } + + float Deg90 = DegToRad(90.0f); + float Deg180 = DegToRad(180.0f); + + AZ::Vector3 ortho1Normalized, ortho2Normalized; + CalcBasisVectors(axisNormalized, ortho1Normalized, ortho2Normalized); + AZ::Vector3 centerToTopCircleCenter = axisNormalized * heightStraightSection * 0.5f; + AZ::Vector3 topCenter = center + centerToTopCircleCenter; + AZ::Vector3 bottomCenter = center - centerToTopCircleCenter; + + // Draw top cap as two criss-crossing 180deg arcs + CreateArbitraryAxisArc( + lines, + stepAngle, + Deg90, + Deg90 + Deg180, + topCenter, + radiusV3, + ortho1Normalized + ); + + CreateArbitraryAxisArc( + lines, + stepAngle, + Deg180, + Deg180 + Deg180, + topCenter, + radiusV3, + ortho2Normalized + ); + + // Draw bottom cap + CreateArbitraryAxisArc( + lines, + stepAngle, + -Deg90, + -Deg90 + Deg180, + bottomCenter, + radiusV3, + ortho1Normalized + ); + + CreateArbitraryAxisArc( + lines, + stepAngle, + Deg0, + Deg0 + Deg180, + bottomCenter, + radiusV3, + ortho2Normalized + ); + + lines.Draw(m_auxGeomPtr, m_rendState); } + } - void AtomDebugDisplayViewportInterface::DrawWireSphere(const AZ::Vector3& pos, float radius) + void AtomDebugDisplayViewportInterface::DrawWireSphere(const AZ::Vector3& pos, float radius) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - m_auxGeomPtr->DrawSphere( - ToWorldSpacePosition(pos), - radius, - m_rendState.m_color, - AZ::RPI::AuxGeomDraw::DrawStyle::Line, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex - ); - } + m_auxGeomPtr->DrawSphere( + ToWorldSpacePosition(pos), + radius, + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Line, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex + ); } + } - void AtomDebugDisplayViewportInterface::DrawWireSphere(const AZ::Vector3& pos, const AZ::Vector3 radius) + void AtomDebugDisplayViewportInterface::DrawWireSphere(const AZ::Vector3& pos, const AZ::Vector3 radius) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - // This matches Cry behavior, the DrawWireSphere above may need modifying to use the same approach. - // Draw 3 axis aligned circles - const float step = DegToRad(10.0f); - const float maxAngle = DegToRad(360.0f) + step; - SingleColorStaticSizeLineHelper<40*3> lines; // hard code to 40 lines * 3 circles until DegToRad is constexpr. - - // Z Axis - AZ::Vector3 axisRadius(radius.GetX(), radius.GetY(), 0.0f); - CreateAxisAlignedArc(lines, step, 0.0f, maxAngle, pos, axisRadius, CircleAxisZ); - - // X Axis - axisRadius = AZ::Vector3(0.0f, radius.GetY(), radius.GetZ()); - CreateAxisAlignedArc(lines, step, 0.0f, maxAngle, pos, axisRadius, CircleAxisX); - - // Y Axis - axisRadius = AZ::Vector3(radius.GetX(), 0.0f, radius.GetZ()); - CreateAxisAlignedArc(lines, step, 0.0f, maxAngle, pos, axisRadius, CircleAxisY); - lines.Draw(m_auxGeomPtr, m_rendState); - } + // This matches Cry behavior, the DrawWireSphere above may need modifying to use the same approach. + // Draw 3 axis aligned circles + const float step = DegToRad(10.0f); + const float maxAngle = DegToRad(360.0f) + step; + SingleColorStaticSizeLineHelper<40*3> lines; // hard code to 40 lines * 3 circles until DegToRad is constexpr. + + // Z Axis + AZ::Vector3 axisRadius(radius.GetX(), radius.GetY(), 0.0f); + CreateAxisAlignedArc(lines, step, 0.0f, maxAngle, pos, axisRadius, CircleAxisZ); + + // X Axis + axisRadius = AZ::Vector3(0.0f, radius.GetY(), radius.GetZ()); + CreateAxisAlignedArc(lines, step, 0.0f, maxAngle, pos, axisRadius, CircleAxisX); + + // Y Axis + axisRadius = AZ::Vector3(radius.GetX(), 0.0f, radius.GetZ()); + CreateAxisAlignedArc(lines, step, 0.0f, maxAngle, pos, axisRadius, CircleAxisY); + lines.Draw(m_auxGeomPtr, m_rendState); } + } - void AtomDebugDisplayViewportInterface::DrawWireDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) + void AtomDebugDisplayViewportInterface::DrawWireDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) - { - // Draw 3 axis aligned circles - const float stepAngle = DegToRad(11.25f); - const float startAngle = DegToRad(0.0f); - const float stopAngle = DegToRad(360.0f) + startAngle; - SingleColorDynamicSizeLineHelper lines(2+static_cast(360.0f/11.25f)); // num disk segments + 1 for azis line + 1 for spare - const AZ::Vector3 radiusV3 = AZ::Vector3(radius); - CreateArbitraryAxisArc( - lines, - stepAngle, - startAngle, - stopAngle, - pos, - radiusV3, - dir + // Draw 3 axis aligned circles + const float stepAngle = DegToRad(11.25f); + const float startAngle = DegToRad(0.0f); + const float stopAngle = DegToRad(360.0f) + startAngle; + SingleColorDynamicSizeLineHelper lines(2+static_cast(360.0f/11.25f)); // num disk segments + 1 for azis line + 1 for spare + const AZ::Vector3 radiusV3 = AZ::Vector3(radius); + CreateArbitraryAxisArc( + lines, + stepAngle, + startAngle, + stopAngle, + pos, + radiusV3, + dir + ); + + lines.AddLineSegment(ToWorldSpacePosition(pos), ToWorldSpacePosition(pos + dir * (radius * 0.2f))); // 0.2f comes from Code\Sandbox\Editor\Objects\DisplayContextShared.inl DisplayContext::DrawWireDisk + lines.Draw(m_auxGeomPtr, m_rendState); + } + } + + void AtomDebugDisplayViewportInterface::DrawBall(const AZ::Vector3& pos, float radius, bool drawShaded) + { + if (m_auxGeomPtr) + { + // get the max scaled radius in case the transform on the stack is scaled non-uniformly + const float transformedRadiusX = ToWorldSpaceVector(AZ::Vector3(radius, 0.0f, 0.0f)).GetLengthEstimate(); + const float transformedRadiusY = ToWorldSpaceVector(AZ::Vector3(0.0f, radius, 0.0f)).GetLengthEstimate(); + const float transformedRadiusZ = ToWorldSpaceVector(AZ::Vector3(0.0f, 0.0f, radius)).GetLengthEstimate(); + const float maxTransformedRadius = + AZ::GetMax(transformedRadiusX, AZ::GetMax(transformedRadiusY, transformedRadiusZ)); + + AZ::RPI::AuxGeomDraw::DrawStyle drawStyle = drawShaded ? AZ::RPI::AuxGeomDraw::DrawStyle::Shaded : AZ::RPI::AuxGeomDraw::DrawStyle::Solid; + m_auxGeomPtr->DrawSphere( + ToWorldSpacePosition(pos), + maxTransformedRadius, + m_rendState.m_color, + drawStyle, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex ); + } + } - lines.AddLineSegment(ToWorldSpacePosition(pos), ToWorldSpacePosition(pos + dir * (radius * 0.2f))); // 0.2f comes from Code\Sandbox\Editor\Objects\DisplayContextShared.inl DisplayContext::DrawWireDisk - lines.Draw(m_auxGeomPtr, m_rendState); - } + void AtomDebugDisplayViewportInterface::DrawDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) + { + if (m_auxGeomPtr) + { + const AZ::Vector3 worldPos = ToWorldSpacePosition(pos); + const AZ::Vector3 worldDir = ToWorldSpaceVector(dir); + m_auxGeomPtr->DrawDisk( + worldPos, + worldDir, + radius, + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Shaded, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex + ); } + } - void AtomDebugDisplayViewportInterface::DrawBall(const AZ::Vector3& pos, float radius, bool drawShaded) + void AtomDebugDisplayViewportInterface::DrawArrow(const AZ::Vector3& src, const AZ::Vector3& trg, float headScale, bool dualEndedArrow) + { + if (m_auxGeomPtr) { - if (m_auxGeomPtr) + float f2dScale = 1.0f; + float arrowLen = 0.4f * headScale; + float arrowRadius = 0.1f * headScale; + // if (flags & DISPLAY_2D) + // { + // f2dScale = 1.2f * ToWorldSpaceVector(Vec3(1, 0, 0)).GetLength(); + // } + AZ::Vector3 dir = trg - src; + dir = ToWorldSpaceVector(dir.GetNormalized()); + AZ::Vector3 verts[2] = {ToWorldSpacePosition(src), ToWorldSpacePosition(trg)}; + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = verts; + drawArgs.m_vertCount = 2; + drawArgs.m_colors = &m_rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = m_rendState.m_lineWidth; + drawArgs.m_opacityType = m_rendState.m_opacityType; + drawArgs.m_depthTest = m_rendState.m_depthTest; + drawArgs.m_depthWrite = m_rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; + if (!dualEndedArrow) { - // get the max scaled radius in case the transform on the stack is scaled non-uniformly - const float transformedRadiusX = ToWorldSpaceVector(AZ::Vector3(radius, 0.0f, 0.0f)).GetLengthEstimate(); - const float transformedRadiusY = ToWorldSpaceVector(AZ::Vector3(0.0f, radius, 0.0f)).GetLengthEstimate(); - const float transformedRadiusZ = ToWorldSpaceVector(AZ::Vector3(0.0f, 0.0f, radius)).GetLengthEstimate(); - const float maxTransformedRadius = - AZ::GetMax(transformedRadiusX, AZ::GetMax(transformedRadiusY, transformedRadiusZ)); - - AZ::RPI::AuxGeomDraw::DrawStyle drawStyle = drawShaded ? AZ::RPI::AuxGeomDraw::DrawStyle::Shaded : AZ::RPI::AuxGeomDraw::DrawStyle::Solid; - m_auxGeomPtr->DrawSphere( - ToWorldSpacePosition(pos), - maxTransformedRadius, - m_rendState.m_color, - drawStyle, + verts[1] -= dir * arrowLen; + m_auxGeomPtr->DrawLines(drawArgs); + m_auxGeomPtr->DrawCone( + verts[1], + dir, + arrowRadius * f2dScale, + arrowLen * f2dScale, + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Shaded, m_rendState.m_depthTest, m_rendState.m_depthWrite, m_rendState.m_faceCullMode, m_rendState.m_viewProjOverrideIndex ); } - } - - void AtomDebugDisplayViewportInterface::DrawDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) - { - if (m_auxGeomPtr) + else { - const AZ::Vector3 worldPos = ToWorldSpacePosition(pos); - const AZ::Vector3 worldDir = ToWorldSpaceVector(dir); - m_auxGeomPtr->DrawDisk( - worldPos, - worldDir, - radius, + verts[0] += dir * arrowLen; + verts[1] -= dir * arrowLen; + m_auxGeomPtr->DrawLines(drawArgs); + m_auxGeomPtr->DrawCone( + verts[0], + -dir, + arrowRadius * f2dScale, + arrowLen * f2dScale, + m_rendState.m_color, + AZ::RPI::AuxGeomDraw::DrawStyle::Shaded, + m_rendState.m_depthTest, + m_rendState.m_depthWrite, + m_rendState.m_faceCullMode, + m_rendState.m_viewProjOverrideIndex + ); + m_auxGeomPtr->DrawCone( + verts[1], + dir, + arrowRadius * f2dScale, + arrowLen * f2dScale, m_rendState.m_color, AZ::RPI::AuxGeomDraw::DrawStyle::Shaded, m_rendState.m_depthTest, @@ -829,179 +1224,297 @@ namespace AZ ); } } + } - void AtomDebugDisplayViewportInterface::DrawArrow(const AZ::Vector3& src, const AZ::Vector3& trg, float headScale, bool dualEndedArrow) + void AtomDebugDisplayViewportInterface::DrawTextLabel( + const AZ::Vector3& pos, + float size, + const char* text, + const bool center, + int srcOffsetX [[maybe_unused]], + int srcOffsetY [[maybe_unused]]) + { + AzFramework::FontDrawInterface* fontDrawInterface = AZ::Interface::Get()->GetDefaultFontDrawInterface(); + if (!fontDrawInterface || !text || size == 0.0f) { - if (m_auxGeomPtr) - { - float f2dScale = 1.0f; - float arrowLen = 0.4f * headScale; - float arrowRadius = 0.1f * headScale; - // if (flags & DISPLAY_2D) - // { - // f2dScale = 1.2f * ToWorldSpaceVector(Vec3(1, 0, 0)).GetLength(); - // } - AZ::Vector3 dir = trg - src; - dir = ToWorldSpaceVector(dir.GetNormalized()); - AZ::Vector3 verts[2] = {ToWorldSpacePosition(src), ToWorldSpacePosition(trg)}; - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; - drawArgs.m_verts = verts; - drawArgs.m_vertCount = 2; - drawArgs.m_colors = &m_rendState.m_color; - drawArgs.m_colorCount = 1; - drawArgs.m_size = m_rendState.m_lineWidth; - drawArgs.m_opacityType = m_rendState.m_opacityType; - drawArgs.m_depthTest = m_rendState.m_depthTest; - drawArgs.m_depthWrite = m_rendState.m_depthWrite; - drawArgs.m_viewProjectionOverrideIndex = m_rendState.m_viewProjOverrideIndex; - if (!dualEndedArrow) - { - verts[1] -= dir * arrowLen; - m_auxGeomPtr->DrawLines(drawArgs); - m_auxGeomPtr->DrawCone( - verts[1], - dir, - arrowRadius * f2dScale, - arrowLen * f2dScale, - m_rendState.m_color, - AZ::RPI::AuxGeomDraw::DrawStyle::Shaded, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex - ); - } - else - { - verts[0] += dir * arrowLen; - verts[1] -= dir * arrowLen; - m_auxGeomPtr->DrawLines(drawArgs); - m_auxGeomPtr->DrawCone( - verts[0], - -dir, - arrowRadius * f2dScale, - arrowLen * f2dScale, - m_rendState.m_color, - AZ::RPI::AuxGeomDraw::DrawStyle::Shaded, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex - ); - m_auxGeomPtr->DrawCone( - verts[1], - dir, - arrowRadius * f2dScale, - arrowLen * f2dScale, - m_rendState.m_color, - AZ::RPI::AuxGeomDraw::DrawStyle::Shaded, - m_rendState.m_depthTest, - m_rendState.m_depthWrite, - m_rendState.m_faceCullMode, - m_rendState.m_viewProjOverrideIndex - ); - } - } + return; } + // if 2d draw need to project pos to screen first + AzFramework::TextDrawParameters params; + AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); + params.m_drawViewportId = viewportContext->GetId(); // get the viewport ID so default viewport works + params.m_position = pos; + params.m_color = m_rendState.m_color; + params.m_scale = AZ::Vector2(size); + params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment + params.m_monospace = false; //! disable character proportional spacing + params.m_depthTest = false; //! Test character against the depth buffer + params.m_virtual800x600ScreenSize = true; //! Text placement and size are scaled relative to a virtual 800x600 resolution + params.m_scaleWithWindow = false; //! Font gets bigger as the window gets bigger + params.m_multiline = true; //! text respects ascii newline characters + + fontDrawInterface->DrawScreenAlignedText3d(params, text); + } - // void AtomDebugDisplayViewportInterface::DrawTextLabel(const AZ::Vector3& pos, float size, const char* text, const bool bCenter = false, int srcOffsetX = 0, int srcOffsetY = 0) override; - // void AtomDebugDisplayViewportInterface::Draw2dTextLabel(float x, float y, float size, const char* text, bool bCenter = false) override; - // void AtomDebugDisplayViewportInterface::DrawTextOn2DBox(const AZ::Vector3& pos, const char* text, float textScale, const AZ::Vector4& TextColor, const AZ::Vector4& TextBackColor) override; - // unhandledled on Atom - virtual void DrawTextureLabel(ITexture* texture, const AZ::Vector3& pos, float sizeX, float sizeY, int texIconFlags) override; - // void AtomDebugDisplayViewportInterface::DrawTextureLabel(int textureId, const AZ::Vector3& pos, float sizeX, float sizeY, int texIconFlags) override; - - void AtomDebugDisplayViewportInterface::SetLineWidth(float width) + void AtomDebugDisplayViewportInterface::Draw2dTextLabel( + float x, + float y, + float size, + const char* text, + bool center) + { + AzFramework::FontDrawInterface* fontDrawInterface = AZ::Interface::Get()->GetDefaultFontDrawInterface(); + if (!fontDrawInterface || !text || size == 0.0f) { - AZ_Assert(width >= 0.0f && width <= 255.0f, "Width (%f) exceeds allowable range [0 - 255]", width); - m_rendState.m_lineWidth = static_cast(width); + return; } + // if 2d draw need to project pos to screen first + AzFramework::TextDrawParameters params; + AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); + params.m_drawViewportId = viewportContext->GetId(); // get the viewport ID so default viewport works + params.m_position = AZ::Vector3(x, y, 1.0f); + params.m_color = m_rendState.m_color; + params.m_scale = AZ::Vector2(size); + params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment + params.m_monospace = false; //! disable character proportional spacing + params.m_depthTest = false; //! Test character against the depth buffer + params.m_virtual800x600ScreenSize = true; //! Text placement and size are scaled relative to a virtual 800x600 resolution + params.m_scaleWithWindow = false; //! Font gets bigger as the window gets bigger + params.m_multiline = true; //! text respects ascii newline characters + + fontDrawInterface->DrawScreenAlignedText2d(params, text); + } - // bool AtomDebugDisplayViewportInterface::IsVisible(const AZ::Aabb& bounds) override; - // int AtomDebugDisplayViewportInterface::SetFillMode(int nFillMode) override; - float AtomDebugDisplayViewportInterface::GetLineWidth() - { - return m_rendState.m_lineWidth; - } + void AtomDebugDisplayViewportInterface::DrawTextOn2DBox( + const AZ::Vector3& pos [[maybe_unused]], + const char* text [[maybe_unused]], + float textScale [[maybe_unused]], + const AZ::Vector4& TextColor [[maybe_unused]], + const AZ::Vector4& TextBackColor [[maybe_unused]]) + { + AZ_Assert(false, "Unexpected use of legacy api, please file a feature request with the rendering team to get this implemented!"); + } + // unhandledled on Atom - virtual void DrawTextureLabel(ITexture* texture, const AZ::Vector3& pos, float sizeX, float sizeY, int texIconFlags) override; + // void AtomDebugDisplayViewportInterface::DrawTextureLabel(int textureId, const AZ::Vector3& pos, float sizeX, float sizeY, int texIconFlags) override; + + void AtomDebugDisplayViewportInterface::SetLineWidth(float width) + { + AZ_Assert(width >= 0.0f && width <= 255.0f, "Width (%f) exceeds allowable range [0 - 255]", width); + m_rendState.m_lineWidth = static_cast(width); + } + + bool AtomDebugDisplayViewportInterface::IsVisible(const AZ::Aabb& bounds) + { + AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); + const AZ::Matrix4x4& worldToClip = viewportContext->GetDefaultView()->GetWorldToClipMatrix(); + AZ::Frustum frustum = AZ::Frustum::CreateFromMatrixColumnMajor(worldToClip, Frustum::ReverseDepth::True); + return frustum.IntersectAabb(bounds) != AZ::IntersectResult::Exterior; + } + // int AtomDebugDisplayViewportInterface::SetFillMode(int nFillMode) override; + float AtomDebugDisplayViewportInterface::GetLineWidth() + { + return m_rendState.m_lineWidth; + } + + float AtomDebugDisplayViewportInterface::GetAspectRatio() + { + AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); + auto windowSize = viewportContext->GetViewportSize(); + return aznumeric_cast(windowSize.m_width)/aznumeric_cast(windowSize.m_height); + } + + void AtomDebugDisplayViewportInterface::DepthTestOff() + { + m_rendState.m_depthTest = AZ::RPI::AuxGeomDraw::DepthTest::Off; + } + + void AtomDebugDisplayViewportInterface::DepthTestOn() + { + m_rendState.m_depthTest = AZ::RPI::AuxGeomDraw::DepthTest::On; + } + + void AtomDebugDisplayViewportInterface::DepthWriteOff() + { + m_rendState.m_depthWrite = AZ::RPI::AuxGeomDraw::DepthWrite::Off; + } + + void AtomDebugDisplayViewportInterface::DepthWriteOn() + { + m_rendState.m_depthWrite = AZ::RPI::AuxGeomDraw::DepthWrite::On; + } + + void AtomDebugDisplayViewportInterface::CullOff() + { + m_rendState.m_faceCullMode = AZ::RPI::AuxGeomDraw::FaceCullMode::None; + } + + void AtomDebugDisplayViewportInterface::CullOn() + { + m_rendState.m_faceCullMode = AZ::RPI::AuxGeomDraw::FaceCullMode::Back; + } + + bool AtomDebugDisplayViewportInterface::SetDrawInFrontMode(bool on) + { + AZ_UNUSED(on); + return false; + } + + AZ::u32 AtomDebugDisplayViewportInterface::GetState() + { + return ConvertRenderStateToCry(); + } + + AZ::u32 AtomDebugDisplayViewportInterface::SetState(AZ::u32 state) + { + uint32_t currentState = ConvertRenderStateToCry(); + uint32_t changedState = (state & e_PublicParamsMask) ^ currentState; - float AtomDebugDisplayViewportInterface::GetAspectRatio() + if (changedState & e_Mode2D3DMask) { - auto viewContextManager = AZ::Interface::Get(); - AZ::RPI::ViewportContextPtr viewportContext; - if (m_defaultInstance) + // this is the only way to turn on 2d Mode under Atom + if (state & e_Mode2D) { - viewportContext = viewContextManager->GetViewportContextByName(viewContextManager->GetDefaultViewportContextName()); + AZ_Assert((currentState & e_DrawInFrontOn) == 0 && (changedState & e_DrawInFrontOn) == 0, "Atom doesnt support Draw In Front and 2d at the same time"); + m_rendState.m_viewProjOverrideIndex = m_auxGeomPtr->GetOrAdd2DViewProjOverride(); + m_rendState.m_2dMode = true; } - else + else // switch back to mode 3d { - viewportContext = viewContextManager->GetViewportContextById(m_viewportId); + m_rendState.m_viewProjOverrideIndex = -1; + m_rendState.m_2dMode = false; } - auto windowSize = viewportContext->GetViewportSize(); - return aznumeric_cast(windowSize.m_width)/aznumeric_cast(windowSize.m_height); } - void AtomDebugDisplayViewportInterface::DepthTestOff() + if (changedState & e_AlphaBlendingMask) { - m_rendState.m_depthTest = AZ::RPI::AuxGeomDraw::DepthTest::Off; + switch (state&e_AlphaBlendingMask) + { + case e_AlphaNone: + m_rendState.m_opacityType = AZ::RPI::AuxGeomDraw::OpacityType::Opaque; + break; + case e_AlphaAdditive: + [[fallthrough]]; // Additive not currently supported in Atom AuxGeom implementation + case e_AlphaBlended: + m_rendState.m_opacityType = AZ::RPI::AuxGeomDraw::OpacityType::Translucent; + break; + } } - void AtomDebugDisplayViewportInterface::DepthTestOn() + if (changedState & e_DrawInFrontMask) { - m_rendState.m_depthTest = AZ::RPI::AuxGeomDraw::DepthTest::On; + AZ_Assert( // either state is turning DrawInFront off or Mode 2D has to be off + (state & e_DrawInFrontOn) == 0 || + ((currentState & e_Mode2D) == 0 && (changedState & e_Mode2D) == 0), + "Atom doesnt support Draw In Front and 2d at the same time"); + SetDrawInFrontMode(changedState & e_DrawInFrontOn); } - - void AtomDebugDisplayViewportInterface::DepthWriteOff() + + if (changedState & e_CullModeMask) { - m_rendState.m_depthWrite = AZ::RPI::AuxGeomDraw::DepthWrite::Off; + switch (state & e_CullModeMask) + { + case e_CullModeNone: + CullOff(); + break; + case e_CullModeFront: + // Currently no other way to set front face culling in DebugDisplayRequestBus + m_rendState.m_faceCullMode = AZ::RPI::AuxGeomDraw::FaceCullMode::Front; + break; + case e_CullModeBack: + CullOn(); + break; + } } - - void AtomDebugDisplayViewportInterface::DepthWriteOn() + + if (changedState & e_DepthWriteMask) { - m_rendState.m_depthWrite = AZ::RPI::AuxGeomDraw::DepthWrite::On; + if (state & e_DepthWriteOff) + { + DepthWriteOff(); + } + else + { + DepthWriteOn(); + } } - - void AtomDebugDisplayViewportInterface::CullOff() + + if (changedState & e_DepthTestMask) { - m_rendState.m_faceCullMode = AZ::RPI::AuxGeomDraw::FaceCullMode::None; + if (state & e_DepthTestOff) + { + DepthTestOff(); + } + else + { + DepthTestOn(); + } } - - void AtomDebugDisplayViewportInterface::CullOn() + + return currentState; + } + + void AtomDebugDisplayViewportInterface::PushMatrix(const AZ::Transform& tm) + { + AZ_Assert(m_rendState.m_currentTransform < RenderState::TransformStackSize, "Exceeded AtomDebugDisplayViewportInterface matrix stack size"); + if (m_rendState.m_currentTransform < RenderState::TransformStackSize) { - m_rendState.m_faceCullMode = AZ::RPI::AuxGeomDraw::FaceCullMode::Back; + m_rendState.m_currentTransform++; + m_rendState.m_transformStack[m_rendState.m_currentTransform] = m_rendState.m_transformStack[m_rendState.m_currentTransform - 1] * AZ::Matrix3x4::CreateFromTransform(tm); } - - bool AtomDebugDisplayViewportInterface::SetDrawInFrontMode(bool on) + } + + void AtomDebugDisplayViewportInterface::PopMatrix() + { + AZ_Assert(m_rendState.m_currentTransform > 0, "Underflowed AtomDebugDisplayViewportInterface matrix stack"); + if (m_rendState.m_currentTransform > 0) { - AZ_UNUSED(on); - return false; + m_rendState.m_currentTransform--; } - - // AZ::u32 AtomDebugDisplayViewportInterface::GetState() override; - // AZ::u32 AtomDebugDisplayViewportInterface::SetState(AZ::u32 state) override; - // AZ::u32 AtomDebugDisplayViewportInterface::SetStateFlag(AZ::u32 state) override; - // AZ::u32 AtomDebugDisplayViewportInterface::ClearStateFlag(AZ::u32 state) override; + } + + const AZ::Matrix3x4& AtomDebugDisplayViewportInterface::GetCurrentTransform() const + { + return m_rendState.m_transformStack[m_rendState.m_currentTransform]; + } - void AtomDebugDisplayViewportInterface::PushMatrix(const AZ::Transform& tm) + AZ::RPI::ViewportContextPtr AtomDebugDisplayViewportInterface::GetViewportContext() const + { + auto viewContextManager = AZ::Interface::Get(); + if (m_defaultInstance) { - AZ_Assert(m_rendState.m_currentTransform < RenderState::TransformStackSize, "Exceeded AtomDebugDisplayViewportInterface matrix stack size"); - if (m_rendState.m_currentTransform < RenderState::TransformStackSize) - { - m_rendState.m_currentTransform++; - m_rendState.m_transformStack[m_rendState.m_currentTransform] = m_rendState.m_transformStack[m_rendState.m_currentTransform - 1] * AZ::Matrix3x4::CreateFromTransform(tm); - } + return viewContextManager->GetViewportContextByName(viewContextManager->GetDefaultViewportContextName()); } - - void AtomDebugDisplayViewportInterface::PopMatrix() + else { - AZ_Assert(m_rendState.m_currentTransform > 0, "Underflowed AtomDebugDisplayViewportInterface matrix stack"); - if (m_rendState.m_currentTransform > 0) - { - m_rendState.m_currentTransform--; - } + return viewContextManager->GetViewportContextById(m_viewportId); } + } - const AZ::Matrix3x4& AtomDebugDisplayViewportInterface::GetCurrentTransform() const + uint32_t AtomDebugDisplayViewportInterface::ConvertRenderStateToCry() const + { + uint32_t result = 0; + + result |= m_rendState.m_2dMode ? e_Mode2D : e_Mode3D; + result |= m_rendState.m_opacityType == AZ::RPI::AuxGeomDraw::OpacityType::Opaque ? e_AlphaNone : e_AlphaBlended; + result |= m_rendState.m_drawInFront ? e_DrawInFrontOn : e_DrawInFrontOff; + result |= m_rendState.m_depthTest == AZ::RPI::AuxGeomDraw::DepthTest::On ? e_DepthTestOn : e_DepthTestOff; + result |= m_rendState.m_depthWrite == AZ::RPI::AuxGeomDraw::DepthWrite::On ? e_DepthWriteOn : e_DepthWriteOff; + switch (m_rendState.m_faceCullMode) { - return m_rendState.m_transformStack[m_rendState.m_currentTransform]; + case AZ::RPI::AuxGeomDraw::FaceCullMode::None: + result |= e_CullModeNone; + break; + case AZ::RPI::AuxGeomDraw::FaceCullMode::Front: + result |= e_CullModeFront; + break; + case AZ::RPI::AuxGeomDraw::FaceCullMode::Back: + result |= e_CullModeBack; + break; + default: + AZ_Assert(false, "Trying to convert an unknown culling mode to cry!"); + break; } + + return result; } } diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h index 48091ea8d8..021d816ca4 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h @@ -42,6 +42,10 @@ namespace AZ::AtomBridge AZ::RPI::AuxGeomDraw::DepthWrite m_depthWrite = AZ::RPI::AuxGeomDraw::DepthWrite::On; AZ::RPI::AuxGeomDraw::FaceCullMode m_faceCullMode = AZ::RPI::AuxGeomDraw::FaceCullMode::Back; int32_t m_viewProjOverrideIndex = -1; // will be used to implement SetDrawInFrontMode & 2D mode + + // separate tracking for Cry only state + bool m_drawInFront = false; + bool m_2dMode = false; }; //! Utility class to collect line segments when the number of segments is known at compile time. @@ -77,6 +81,24 @@ namespace AZ::AtomBridge } } + void Draw2d(AZ::RPI::AuxGeomDrawPtr auxGeomDrawPtr, const RenderState& rendState) const + { + if (auxGeomDrawPtr && !m_points.empty()) + { + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs; + drawArgs.m_verts = m_points.data(); + drawArgs.m_vertCount = aznumeric_cast(m_points.size()); + drawArgs.m_colors = &rendState.m_color; + drawArgs.m_colorCount = 1; + drawArgs.m_size = rendState.m_lineWidth; + drawArgs.m_opacityType = rendState.m_opacityType; + drawArgs.m_depthTest = rendState.m_depthTest; + drawArgs.m_depthWrite = rendState.m_depthWrite; + drawArgs.m_viewProjectionOverrideIndex = auxGeomDrawPtr->GetOrAdd2DViewProjOverride(); + auxGeomDrawPtr->DrawLines( drawArgs ); + } + } + void Reset() { m_points.clear(); @@ -91,6 +113,7 @@ namespace AZ::AtomBridge SingleColorDynamicSizeLineHelper(int estimatedNumLineSegments); void AddLineSegment(const AZ::Vector3& lineStart, const AZ::Vector3& lineEnd); void Draw(AZ::RPI::AuxGeomDrawPtr auxGeomDrawPtr, const RenderState& rendState) const; + void Draw2d(AZ::RPI::AuxGeomDrawPtr auxGeomDrawPtr, const RenderState& rendState) const; void Reset(); AZStd::vector m_points; @@ -119,9 +142,9 @@ namespace AZ::AtomBridge void SetColor(const AZ::Vector4& color) override; void SetAlpha(float a) override; void DrawQuad(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, const AZ::Vector3& p4) override; - // void DrawQuad(float width, float height) overr - // void DrawWireQuad(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, const AZ::Vector3& p4) override; - // void DrawWireQuad(float width, float height) override; + void DrawQuad(float width, float height) override; + void DrawWireQuad(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, const AZ::Vector3& p4) override; + void DrawWireQuad(float width, float height) override; void DrawQuadGradient(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, const AZ::Vector3& p4, const AZ::Vector4& firstColor, const AZ::Vector4& secondColor) override; void DrawTri(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3) override; void DrawTriangles(const AZStd::vector& vertices, const AZ::Color& color) override; @@ -134,10 +157,10 @@ namespace AZ::AtomBridge void DrawLine(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector4& col1, const AZ::Vector4& col2) override; void DrawLines(const AZStd::vector& lines, const AZ::Color& color) override; void DrawPolyLine(const AZ::Vector3* pnts, int numPoints, bool cycled = true) override; - // void DrawWireQuad2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) override; - // void DrawLine2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) override; - // void DrawLine2dGradient(const AZ::Vector2& p1, const AZ::Vector2& p2, float z, const AZ::Vector4& firstColor, const AZ::Vector4& secondColor) override; - // void DrawWireCircle2d(const AZ::Vector2& center, float radius, float z) override; + void DrawWireQuad2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) override; + void DrawLine2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) override; + void DrawLine2dGradient(const AZ::Vector2& p1, const AZ::Vector2& p2, float z, const AZ::Vector4& firstColor, const AZ::Vector4& secondColor) override; + void DrawWireCircle2d(const AZ::Vector2& center, float radius, float z) override; void DrawArc(const AZ::Vector3& pos, float radius, float startAngleDegrees, float sweepAngleDegrees, float angularStepDegrees, int referenceAxis = 2) override; void DrawArc(const AZ::Vector3& pos, float radius, float startAngleDegrees, float sweepAngleDegrees, float angularStepDegrees, const AZ::Vector3& fixedAxis) override; void DrawCircle(const AZ::Vector3& pos, float radius, int nUnchangedAxis = 2 /*z axis*/) override; @@ -152,13 +175,13 @@ namespace AZ::AtomBridge void DrawBall(const AZ::Vector3& pos, float radius, bool drawShaded) override; void DrawDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) override; void DrawArrow(const AZ::Vector3& src, const AZ::Vector3& trg, float headScale = 1.0f, bool dualEndedArrow = false) override; - // void DrawTextLabel(const AZ::Vector3& pos, float size, const char* text, const bool bCenter = false, int srcOffsetX = 0, int srcOffsetY = 0) override; - // void Draw2dTextLabel(float x, float y, float size, const char* text, bool bCenter = false) override; - // void DrawTextOn2DBox(const AZ::Vector3& pos, const char* text, float textScale, const AZ::Vector4& TextColor, const AZ::Vector4& TextBackColor) override; + void DrawTextLabel(const AZ::Vector3& pos, float size, const char* text, const bool bCenter = false, int srcOffsetX = 0, int srcOffsetY = 0) override; + void Draw2dTextLabel(float x, float y, float size, const char* text, bool bCenter = false) override; + void DrawTextOn2DBox(const AZ::Vector3& pos, const char* text, float textScale, const AZ::Vector4& TextColor, const AZ::Vector4& TextBackColor) override; // unhandled on Atom - virtual void DrawTextureLabel(ITexture* texture, const AZ::Vector3& pos, float sizeX, float sizeY, int texIconFlags) override; // void DrawTextureLabel(int textureId, const AZ::Vector3& pos, float sizeX, float sizeY, int texIconFlags) override; void SetLineWidth(float width) override; - // bool IsVisible(const AZ::Aabb& bounds) override; + bool IsVisible(const AZ::Aabb& bounds) override; // int SetFillMode(int nFillMode) override; float GetLineWidth() override; float GetAspectRatio() override; @@ -169,10 +192,8 @@ namespace AZ::AtomBridge void CullOff() override; void CullOn() override; bool SetDrawInFrontMode(bool on) override; - // AZ::u32 GetState() override; - // AZ::u32 SetState(AZ::u32 state) override; - // AZ::u32 SetStateFlag(AZ::u32 state) override; - // AZ::u32 ClearStateFlag(AZ::u32 state) override; + AZ::u32 GetState() override; + AZ::u32 SetState(AZ::u32 state) override; void PushMatrix(const AZ::Transform& tm) override; void PopMatrix() override; @@ -226,6 +247,10 @@ namespace AZ::AtomBridge void InitInternal(RPI::Scene* scene, AZ::RPI::ViewportContextPtr viewportContextPtr); + AZ::RPI::ViewportContextPtr GetViewportContext() const; + + uint32_t ConvertRenderStateToCry() const; + RenderState m_rendState; AZ::RPI::AuxGeomDrawPtr m_auxGeomPtr; diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h index b1feaf1d87..2969e35a51 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h +++ b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h @@ -20,8 +20,14 @@ #include #include #include +#include #include +#include +#include + +#include + namespace AZ { class FFont; @@ -33,6 +39,8 @@ namespace AZ //! and manages their loading & saving together. class AtomFont : public ICryFont + , public AzFramework::FontQueryInterface + , public AzFramework::SceneSystemNotificationBus::Handler { friend class FFont; @@ -79,16 +87,31 @@ namespace AZ void ReloadAllFonts() override; ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + // FontQueryInterface implementation + AzFramework::FontDrawInterface* GetFontDrawInterface(AzFramework::FontId fontId) const override; + AzFramework::FontDrawInterface* GetDefaultFontDrawInterface() const override; + + // SceneSystemNotificationBus handlers + void SceneAboutToBeRemoved(AzFramework::Scene& scene) override; + + + // Atom DynamicDraw interface management + AZ::RHI::Ptr GetOrCreateDynamicDrawForScene(AZ::RPI::Scene* scene); + + public: void UnregisterFont(const char* fontName); private: - typedef std::map FontMap; - typedef FontMap::iterator FontMapItor; - typedef FontMap::const_iterator FontMapConstItor; + using FontMap = std::unordered_map; + using FontMapItor = FontMap::iterator; + using FontMapConstItor = FontMap::const_iterator; - typedef AZStd::map> FontFamilyMap; - typedef AZStd::map FontFamilyReverseLookupMap; + using FontFamilyMap = AZStd::unordered_map>; + using FontFamilyReverseLookupMap = AZStd::unordered_map; + + using SceneToDynamicDrawMap = AZStd::unordered_map>; private: //! Convenience method for loading fonts @@ -119,9 +142,13 @@ namespace AZ FontFamilyReverseLookupMap m_fontFamilyReverseLookup; // m_persistedFontFamilies; //!< Stores persisted fonts (if "persist font families" is enabled) + SceneToDynamicDrawMap m_sceneToDynamicDrawMap; + AZStd::shared_mutex m_sceneToDynamicDrawMutex; }; } #endif diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h index 4f87334b46..96f5e09fc3 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h +++ b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,8 @@ namespace AZ void operator () (const AZStd::intrusive_refcount* ptr) const; }; + using TextDrawContext = STextDrawContext; + //! FFont is the implementation of IFFont used to draw text with a particular font (e.g. Consolas Italic) //! FFont manages creation of a gpu texture to cache the font and generates draw commands that use that texture. //! FFont's are managed by AtomFont as either individual font instances or a font family @@ -64,12 +67,12 @@ namespace AZ class FFont : public IFFont , public AZStd::intrusive_refcount + , public AzFramework::FontDrawInterface , private AZ::Render::Bootstrap::NotificationBus::Handler { using ref_count = AZStd::intrusive_refcount; friend FontDeleter; public: - using TextDrawContext = STextDrawContext; //! Determines how characters of different sizes should be handled during render. enum class SizeBehavior { @@ -201,6 +204,14 @@ namespace AZ uint32_t GetFontTextureVersion() override; ///////////////////////////////////////////////////////////////////////////////////////////////////// + // AzFramework::FontDrawInterface implementation + void DrawScreenAlignedText2d( + const AzFramework::TextDrawParameters& params, + const AZStd::string_view& string) override; + + void DrawScreenAlignedText3d( + const AzFramework::TextDrawParameters& params, + const AZStd::string_view& string) override; public: FFont(AtomFont* atomFont, const char* fontName); @@ -220,8 +231,18 @@ namespace AZ bool InitCache(); void Prepare(const char* str, bool updateTexture, const AtomFont::GlyphSize& glyphSize = AtomFont::defaultGlyphSize); - void DrawStringUInternal(float x, float y, float z, const char* str, const bool asciiMultiLine, const TextDrawContext& ctx); - Vec2 GetTextSizeUInternal(const char* str, const bool asciiMultiLine, const TextDrawContext& ctx); + void DrawStringUInternal( + const RHI::Viewport& viewport, + RPI::ViewportContext* viewportContext, + float x, + float y, + float z, + const char* str, + const bool asciiMultiLine, + const TextDrawContext& ctx); + Vec2 GetTextSizeUInternal(const RHI::Viewport& viewport, const char* str, const bool asciiMultiLine, const TextDrawContext& ctx); + Vec2 GetKerningInternal(const RHI::Viewport& viewport, uint32_t leftGlyph, uint32_t rightGlyph, const TextDrawContext& ctx) const; + float GetBaselineInternal(const RHI::Viewport& viewport, const TextDrawContext& ctx) const; // returns true if add operation was successful, false otherwise using AddFunction = AZStd::function; @@ -229,6 +250,7 @@ namespace AZ //! This function is used by both DrawStringUInternal and WriteTextQuadsToBuffers //! To do this is takes a function pointer that implement the appropriate AddQuad behavior int CreateQuadsForText( + const RHI::Viewport& viewport, float x, float y, float z, @@ -247,16 +269,16 @@ namespace AZ float rcpCellWidth; }; - TextScaleInfoInternal CalculateScaleInternal(const TextDrawContext& ctx) const; + TextScaleInfoInternal CalculateScaleInternal(const RHI::Viewport& viewport, const TextDrawContext& ctx) const; Vec2 GetRestoredFontSize(const TextDrawContext& ctx) const; bool UpdateTexture(); - void ScaleCoord(float& x, float& y) const; + void ScaleCoord(const RHI::Viewport& viewport, float& x, float& y) const; - void InitWindowContext(); - void InitViewportContext(); + void InitDefaultWindowContext(); + void InitDefaultViewportContext(); void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) override; @@ -272,8 +294,8 @@ namespace AZ size_t m_fontBufferSize = 0; unsigned char* m_fontBuffer = nullptr; - AZStd::shared_ptr m_windowContext; - AZStd::shared_ptr m_viewportContext; + AZStd::shared_ptr m_defaultWindowContext; + AZStd::shared_ptr m_defaultViewportContext; AZ::Data::Instance m_fontStreamingImage; AZ::RHI::Ptr m_fontImage; @@ -296,8 +318,6 @@ namespace AZ FontShaderData m_fontShaderData; - AZ::RHI::Ptr m_dynamicDraw; - bool m_monospacedFont = false; //!< True if this font is fixed/monospaced, false otherwise (obtained from FreeType) float m_sizeRatio = IFFontConstants::defaultSizeRatio; @@ -325,25 +345,25 @@ namespace AZ } } -inline void AZ::FFont::InitWindowContext() +inline void AZ::FFont::InitDefaultWindowContext() { - if (!m_windowContext) + if (!m_defaultWindowContext) { // font is created before window & viewport in the editor so need to do late init // TODO need to deal with multiple windows, such as the editor - AZ::Render::Bootstrap::DefaultWindowBus::BroadcastResult(m_windowContext, &AZ::Render::Bootstrap::DefaultWindowInterface::GetDefaultWindowContext); - AZ_Assert(m_windowContext, "Unable to get the main window context"); + AZ::Render::Bootstrap::DefaultWindowBus::BroadcastResult(m_defaultWindowContext, &AZ::Render::Bootstrap::DefaultWindowInterface::GetDefaultWindowContext); + AZ_Assert(m_defaultWindowContext, "Unable to get the main window context"); } } -inline void AZ::FFont::InitViewportContext() +inline void AZ::FFont::InitDefaultViewportContext() { - if (!m_viewportContext) + if (!m_defaultViewportContext) { // font is created before window & viewport in the editor so need to do late init auto viewContextManager = AZ::Interface::Get(); - m_viewportContext = viewContextManager->GetViewportContextByName(viewContextManager->GetDefaultViewportContextName()); - AZ_Assert(m_viewportContext, "Unable to get the viewport context"); + m_defaultViewportContext = viewContextManager->GetViewportContextByName(viewContextManager->GetDefaultViewportContextName()); + AZ_Assert(m_defaultViewportContext, "Unable to get the viewport context"); } } diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp index 0a081d01f1..636583fe98 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp @@ -28,8 +28,12 @@ #include #include +#include +#include #include +#include +#include // Static member definitions const AZ::AtomFont::GlyphSize AZ::AtomFont::defaultGlyphSize = AZ::AtomFont::GlyphSize(ICryFont::defaultGlyphSizeX, ICryFont::defaultGlyphSizeY); @@ -348,10 +352,14 @@ AZ::AtomFont::AtomFont(ISystem* system) REGISTER_COMMAND("r_ReloadFonts", ReloadFonts, VF_NULL, "Reload all fonts"); #endif + AZ::Interface::Register(this); } AZ::AtomFont::~AtomFont() { + AZ::Interface::Unregister(this); + m_defaultFontDrawInterface = nullptr; + // Persist fonts for application lifetime to prevent unnecessary work m_persistedFontFamilies.clear(); @@ -372,24 +380,41 @@ IFFont* AZ::AtomFont::NewFont(const char* fontName) { string name = fontName; name.MakeLower(); + AzFramework::FontId fontId = GetFontId(name.c_str()); - FontMapItor it = m_fonts.find(CONST_TEMP_STRING(name.c_str())); + FontMapItor it = m_fonts.find(fontId); if (it != m_fonts.end()) { return it->second; } FFont* font = new FFont(this, name.c_str()); - m_fonts.insert(FontMapItor::value_type(name, font)); + m_fonts.insert(FontMapItor::value_type(fontId, font)); + if(!m_defaultFontDrawInterface) + { + m_defaultFontDrawInterface = static_cast(font); + } return font; } IFFont* AZ::AtomFont::GetFont(const char* fontName) const { - FontMapConstItor it = m_fonts.find(CONST_TEMP_STRING(string(fontName).MakeLower())); + AzFramework::FontId fontId = GetFontId(string(fontName).MakeLower().c_str()); + FontMapConstItor it = m_fonts.find(fontId); return it != m_fonts.end() ? it->second : 0; } +AzFramework::FontDrawInterface* AZ::AtomFont::GetFontDrawInterface(AzFramework::FontId fontId) const +{ + FontMapConstItor it = m_fonts.find(fontId); + return (it != m_fonts.end()) ? it->second : nullptr; +} + +AzFramework::FontDrawInterface* AZ::AtomFont::GetDefaultFontDrawInterface() const +{ + return m_defaultFontDrawInterface; +} + FontFamilyPtr AZ::AtomFont::LoadFontFamily(const char* fontFamilyName) { FontFamilyPtr fontFamily(nullptr); @@ -648,7 +673,8 @@ void AZ::AtomFont::ReloadAllFonts() void AZ::AtomFont::UnregisterFont(const char* fontName) { - FontMapItor it = m_fonts.find(CONST_TEMP_STRING(fontName)); + AzFramework::FontId fontId = GetFontId(string(fontName).MakeLower().c_str()); + FontMapItor it = m_fonts.find(fontId); #if defined(AZ_ENABLE_TRACING) IFFont* fontPtr = it->second; @@ -823,5 +849,49 @@ XmlNodeRef AZ::AtomFont::LoadFontFamilyXml(const char* fontFamilyName, string& o return root; } +void AZ::AtomFont::SceneAboutToBeRemoved(AzFramework::Scene& scene) +{ + AZ::RPI::Scene* rpiScene = scene.GetSubsystem(); + + AZStd::lock_guard lock(m_sceneToDynamicDrawMutex); + if ( auto it = m_sceneToDynamicDrawMap.find(rpiScene); it != m_sceneToDynamicDrawMap.end()) + { + m_sceneToDynamicDrawMap.erase(it); + } +} + +AZ::RHI::Ptr AZ::AtomFont::GetOrCreateDynamicDrawForScene(AZ::RPI::Scene* scene) +{ + static const char* shaderFilepath = "Shaders/SimpleTextured.azshader"; + + { + // shared lock while reading + AZStd::shared_lock lock(m_sceneToDynamicDrawMutex); + + if (auto it = m_sceneToDynamicDrawMap.find(scene); it != m_sceneToDynamicDrawMap.end()) + { + return it->second; + } + } + + // Create and initialize DynamicDrawContext for font draw + AZ::RHI::Ptr dynamicDraw = RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext(scene); + + Data::Instance shader = AZ::RPI::LoadShader(shaderFilepath); + AZ::RPI::ShaderOptionList shaderOptions; + shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false"))); + shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true"))); + dynamicDraw->InitShaderWithVariant(shader, &shaderOptions); + dynamicDraw->InitVertexFormat({{"POSITION", RHI::Format::R32G32B32_FLOAT}, {"COLOR", RHI::Format::R8G8B8A8_UNORM}, {"TEXCOORD0", RHI::Format::R32G32_FLOAT}}); + dynamicDraw->EndInit(); + + // exclusive lock while writing + AZStd::lock_guard lock(m_sceneToDynamicDrawMutex); + m_sceneToDynamicDrawMap.insert(AZStd::make_pair(scene, dynamicDraw)); + + return dynamicDraw; +} + + #endif diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp index bb4eb23d8f..d32302a07b 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp @@ -23,6 +23,9 @@ #include #include +#include +#include + #include #include @@ -42,6 +45,8 @@ #include #include #include +#include +#include #include #include @@ -49,6 +54,7 @@ #include +static const AZ::Vector2 UiDraw_TextSizeFactor = AZ::Vector2(12.0f, 12.0f); static const int TabCharCount = 4; // set buffer sizes to hold max characters that can be drawn in 1 DrawString call static const size_t MaxVerts = 8 * 1024; // 2048 quads @@ -78,6 +84,7 @@ AZ::FFont::FFont(AtomFont* atomFont, const char* fontName) AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect(); } + bool AZ::FFont::InitFont() { if (m_fontInitialized) @@ -85,24 +92,14 @@ bool AZ::FFont::InitFont() return true; } - InitWindowContext(); - InitViewportContext(); - - const char* shaderFilepath = "Shaders/SimpleTextured.azshader"; + InitDefaultWindowContext(); + InitDefaultViewportContext(); // Create and initialize DynamicDrawContext for font draw - m_dynamicDraw = RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext(m_viewportContext->GetRenderScene().get()); - - Data::Instance shader = AZ::RPI::LoadShader(shaderFilepath); - AZ::RPI::ShaderOptionList shaderOptions; - shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false"))); - shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true"))); - m_dynamicDraw->InitShaderWithVariant(shader, &shaderOptions); - m_dynamicDraw->InitVertexFormat({{"POSITION", RHI::Format::R32G32B32_FLOAT}, {"COLOR", RHI::Format::R8G8B8A8_UNORM}, {"TEXCOORD0", RHI::Format::R32G32_FLOAT}}); - m_dynamicDraw->EndInit(); + AZ::RPI::Ptr dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(m_defaultViewportContext->GetRenderScene().get()); // Save draw srg input indices for later use - Data::Instance drawSrg = m_dynamicDraw->NewDrawSrg(); + Data::Instance drawSrg = dynamicDraw->NewDrawSrg(); const RHI::ShaderResourceGroupLayout* layout = drawSrg->GetAsset()->GetLayout(); m_fontShaderData.m_imageInputIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::TextureIndexName)); @@ -257,27 +254,39 @@ void AZ::FFont::Free() void AZ::FFont::DrawString(float x, float y, const char* str, const bool asciiMultiLine, const TextDrawContext& ctx) { - if (!str || !m_vertexBuffer) + if (!str) { return; } - DrawStringUInternal(x, y, 1.0f, str, asciiMultiLine, ctx); + DrawStringUInternal(m_defaultWindowContext->GetViewport(), m_defaultViewportContext.get(), x, y, 1.0f, str, asciiMultiLine, ctx); } void AZ::FFont::DrawString(float x, float y, float z, const char* str, const bool asciiMultiLine, const TextDrawContext& ctx) { - if (!str || !m_vertexBuffer) + if (!str) { return; } - DrawStringUInternal(x, y, z, str, asciiMultiLine, ctx); + DrawStringUInternal(m_defaultWindowContext->GetViewport(), m_defaultViewportContext.get(), x, y, z, str, asciiMultiLine, ctx); } -void AZ::FFont::DrawStringUInternal(float x, float y, float z, const char* str, const bool asciiMultiLine, const TextDrawContext& ctx) +void AZ::FFont::DrawStringUInternal( + const RHI::Viewport& viewport, + RPI::ViewportContext* viewportContext, + float x, + float y, + float z, + const char* str, + const bool asciiMultiLine, + const TextDrawContext& ctx) { - if (!str || !m_fontTexture || ctx.m_fxIdx >= m_effects.size() || m_effects[ctx.m_fxIdx].m_passes.empty()) + if (!str + || !m_vertexBuffer // vertex buffer isn't created until BootstrapScene is ready, Editor tries to render text before that. + || !m_fontTexture + || ctx.m_fxIdx >= m_effects.size() + || m_effects[ctx.m_fxIdx].m_passes.empty()) { return; } @@ -296,7 +305,6 @@ void AZ::FFont::DrawStringUInternal(float x, float y, float z, const char* str, const bool orthoMode = ctx.m_overrideViewProjMatrices; - const RHI::Viewport& viewport = m_windowContext->GetViewport(); const float viewX = viewport.m_minX; const float viewY = viewport.m_minY; const float viewWidth = viewport.m_maxX - viewport.m_minX; @@ -307,7 +315,7 @@ void AZ::FFont::DrawStringUInternal(float x, float y, float z, const char* str, Matrix4x4 modelViewProjMat; if (!orthoMode) { - AZ::RPI::ViewPtr view = m_viewportContext->GetDefaultView(); + AZ::RPI::ViewPtr view = viewportContext->GetDefaultView(); modelViewProjMat = view->GetWorldToClipMatrix(); } else @@ -322,7 +330,7 @@ void AZ::FFont::DrawStringUInternal(float x, float y, float z, const char* str, size_t startingVertexCount = m_vertexCount; // Local function that is passed into CreateQuadsForText as the AddQuad function - AddFunction AddQuad = [this, startingVertexCount] + AZ::FFont::AddFunction AddQuad = [this, startingVertexCount] (const Vec3& v0, const Vec3& v1, const Vec3& v2, const Vec3& v3, const Vec2& tc0, const Vec2& tc1, const Vec2& tc2, const Vec2& tc3, uint32_t packedColor) { const bool vertexSpaceLeft = m_vertexCount + 4 < MaxVerts; @@ -367,18 +375,19 @@ void AZ::FFont::DrawStringUInternal(float x, float y, float z, const char* str, int numQuads = 0; { AZStd::lock_guard lock(m_vertexDataMutex); - numQuads = CreateQuadsForText(x, y, z, str, asciiMultiLine, ctx, AddQuad); + numQuads = CreateQuadsForText(viewport, x, y, z, str, asciiMultiLine, ctx, AddQuad); } if (numQuads) { + auto dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(viewportContext->GetRenderScene().get()); //setup per draw srg - auto drawSrg = m_dynamicDraw->NewDrawSrg(); + auto drawSrg = dynamicDraw->NewDrawSrg(); drawSrg->SetConstant(m_fontShaderData.m_viewProjInputIndex, modelViewProjMat); drawSrg->SetImageView(m_fontShaderData.m_imageInputIndex, m_fontStreamingImage->GetImageView()); drawSrg->Compile(); - m_dynamicDraw->DrawIndexed(m_vertexBuffer, m_vertexCount, m_indexBuffer, m_indexCount, RHI::IndexFormat::Uint16, drawSrg); + dynamicDraw->DrawIndexed(m_vertexBuffer, m_vertexCount, m_indexBuffer, m_indexCount, RHI::IndexFormat::Uint16, drawSrg); m_indexCount = 0; m_vertexCount = 0; } @@ -391,10 +400,14 @@ Vec2 AZ::FFont::GetTextSize(const char* str, const bool asciiMultiLine, const Te return Vec2(0.0f, 0.0f); } - return GetTextSizeUInternal(str, asciiMultiLine, ctx); + return GetTextSizeUInternal(m_defaultWindowContext->GetViewport(), str, asciiMultiLine, ctx); } -Vec2 AZ::FFont::GetTextSizeUInternal(const char* str, const bool asciiMultiLine, const TextDrawContext& ctx) +Vec2 AZ::FFont::GetTextSizeUInternal( + const RHI::Viewport& viewport, + const char* str, + const bool asciiMultiLine, + const TextDrawContext& ctx) { const size_t fxSize = m_effects.size(); @@ -411,12 +424,12 @@ Vec2 AZ::FFont::GetTextSizeUInternal(const char* str, const bool asciiMultiLine, Vec2 size = ctx.m_size; if (ctx.m_sizeIn800x600) { - ScaleCoord(size.x, size.y); + ScaleCoord(viewport, size.x, size.y); } // This scaling takes into account the logical size of the font relative // to any additional scaling applied (such as from "size ratio"). - const TextScaleInfoInternal scaleInfo(CalculateScaleInternal(ctx)); + const TextScaleInfoInternal scaleInfo(CalculateScaleInternal(viewport, ctx)); float maxW = 0; float maxH = 0; @@ -733,7 +746,7 @@ uint32_t AZ::FFont::WriteTextQuadsToBuffers(SVF_P2F_C4B_T2F_F4B* verts, uint16_t return true; }; - CreateQuadsForText(x, y, z, str, asciiMultiLine, ctx, AddQuad); + CreateQuadsForText(m_defaultWindowContext->GetViewport(), x, y, z, str, asciiMultiLine, ctx, AddQuad); return numQuadsWritten; } @@ -743,7 +756,7 @@ uint32_t AZ::FFont::GetFontTextureVersion() return m_fontImageVersion; } -int AZ::FFont::CreateQuadsForText(float x, float y, float z, const char* str, const bool asciiMultiLine, const TextDrawContext& ctx, +int AZ::FFont::CreateQuadsForText(const RHI::Viewport& viewport, float x, float y, float z, const char* str, const bool asciiMultiLine, const TextDrawContext& ctx, AddFunction AddQuad) { int numQuads = 0; @@ -768,17 +781,17 @@ int AZ::FFont::CreateQuadsForText(float x, float y, float z, const char* str, co Vec2 size = ctx.m_size; if (ctx.m_sizeIn800x600) { - ScaleCoord(size.x, size.y); + ScaleCoord(viewport, size.x, size.y); } // This scaling takes into account the logical size of the font relative // to any additional scaling applied (such as from "size ratio"). - const TextScaleInfoInternal scaleInfo(CalculateScaleInternal(ctx)); + const TextScaleInfoInternal scaleInfo(CalculateScaleInternal(viewport, ctx)); Vec2 baseXY = Vec2(x, y); // in pixels if (ctx.m_sizeIn800x600) { - ScaleCoord(baseXY.x, baseXY.y); + ScaleCoord(viewport, baseXY.x, baseXY.y); } // snap for pixel perfect rendering (better quality for text) @@ -826,7 +839,7 @@ int AZ::FFont::CreateQuadsForText(float x, float y, float z, const char* str, co ColorB tempColor(255, 255, 255, 255); uint32_t frameColor = tempColor.pack_abgr8888(); //note: this ends up in r,g,b,a order on little-endian machines - Vec2 textSize = GetTextSizeUInternal(str, asciiMultiLine, ctx); + Vec2 textSize = GetTextSizeUInternal(viewport, str, asciiMultiLine, ctx); float x0 = baseXY.x - 12; float y0 = baseXY.y - 6; @@ -1107,13 +1120,13 @@ int AZ::FFont::CreateQuadsForText(float x, float y, float z, const char* str, co return numQuads; } -AZ::FFont::TextScaleInfoInternal AZ::FFont::CalculateScaleInternal(const TextDrawContext& ctx) const +AZ::FFont::TextScaleInfoInternal AZ::FFont::CalculateScaleInternal(const RHI::Viewport& viewport, const TextDrawContext& ctx) const { Vec2 size = GetRestoredFontSize(ctx); // in pixel if (ctx.m_sizeIn800x600) { - ScaleCoord(size.x, size.y); + ScaleCoord(viewport, size.x, size.y); } float rcpCellWidth; @@ -1196,7 +1209,7 @@ void AZ::FFont::WrapText(string& result, float maxWidth, const char* str, const maxWidth = gEnv->pRenderer->ScaleCoordX(maxWidth); } - Vec2 strSize = GetTextSizeUInternal(result.c_str(), true, ctx); + Vec2 strSize = GetTextSize(result.c_str(), true, ctx); if (strSize.x <= maxWidth) { @@ -1245,7 +1258,7 @@ void AZ::FFont::WrapText(string& result, float maxWidth, const char* str, const // Note: This is not unicode compatible, since char-width depends on surrounding context (ie, combining diacritics etc) char codepoint[5]; Unicode::Convert(codepoint, ch); - curCharWidth = GetTextSizeUInternal(codepoint, true, ctx).x; + curCharWidth = GetTextSize(codepoint, true, ctx).x; // keep track of spaces // they are good for splitting the string @@ -1425,7 +1438,12 @@ void AZ::FFont::AddCharsToFontTexture(const char* chars, int glyphSizeX, int gly Vec2 AZ::FFont::GetKerning(uint32_t leftGlyph, uint32_t rightGlyph, const TextDrawContext& ctx) const { - const TextScaleInfoInternal scaleInfo(CalculateScaleInternal(ctx)); + return GetKerningInternal(m_defaultWindowContext->GetViewport(), leftGlyph, rightGlyph, ctx); +} + +Vec2 AZ::FFont::GetKerningInternal(const RHI::Viewport& viewport, uint32_t leftGlyph, uint32_t rightGlyph, const TextDrawContext& ctx) const +{ + const TextScaleInfoInternal scaleInfo(CalculateScaleInternal(viewport, ctx)); return m_fontTexture->GetKerning(leftGlyph, rightGlyph) * scaleInfo.scale.x; } @@ -1436,12 +1454,18 @@ float AZ::FFont::GetAscender(const TextDrawContext& ctx) const float AZ::FFont::GetBaseline(const TextDrawContext& ctx) const { - const TextScaleInfoInternal scaleInfo(CalculateScaleInternal(ctx)); + return GetBaselineInternal(m_defaultWindowContext->GetViewport(), ctx); +} + +float AZ::FFont::GetBaselineInternal(const RHI::Viewport& viewport, const TextDrawContext& ctx) const +{ + const TextScaleInfoInternal scaleInfo(CalculateScaleInternal(viewport, ctx)); // Calculate baseline the same way as the font renderer which uses the glyph height * size ratio. // Adding 1 because FontTexture always adds 1 to the char height in GetTextureCoord return (round(m_fontTexture->GetCellHeight() * GetSizeRatio()) + 1.0f) * scaleInfo.scale.y; } + bool AZ::FFont::InitTexture() { using namespace AZ; @@ -1565,14 +1589,8 @@ Vec2 AZ::FFont::GetRestoredFontSize(const TextDrawContext& ctx) const return Vec2(ctx.m_size.x * restoringScale, ctx.m_size.y * restoringScale); } -void AZ::FFont::ScaleCoord(float& x, float& y) const +void AZ::FFont::ScaleCoord(const RHI::Viewport& viewport, float& x, float& y) const { - if (!m_windowContext) - { - return; - } - - const RHI::Viewport& viewport = m_windowContext->GetViewport(); float width = viewport.m_maxX - viewport.m_minX; float height = viewport.m_maxY - viewport.m_minY; @@ -1580,11 +1598,161 @@ void AZ::FFont::ScaleCoord(float& x, float& y) const y *= height / WindowScaleHeight; } + void AZ::FFont::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene) { InitFont(); } +static void SetCommonContextFlags(AZ::TextDrawContext& ctx, const AzFramework::TextDrawParameters& params) +{ + if (params.m_hAlign == AzFramework::TextHorizontalAlignment::Center) + { + ctx.m_drawTextFlags |= eDrawText_Center; + } + + if (params.m_hAlign == AzFramework::TextHorizontalAlignment::Right) + { + ctx.m_drawTextFlags |= eDrawText_Right; + } + + if (params.m_vAlign == AzFramework::TextVerticalAlignment::Center) + { + ctx.m_drawTextFlags |= eDrawText_CenterV; + } + + if (params.m_vAlign == AzFramework::TextVerticalAlignment::Bottom) + { + ctx.m_drawTextFlags |= eDrawText_Bottom; + } + + if (params.m_monospace) + { + ctx.m_drawTextFlags |= eDrawText_Monospace; + } + + if (params.m_depthTest) + { + ctx.m_drawTextFlags |= eDrawText_DepthTest; + } + + if (params.m_virtual800x600ScreenSize) + { + ctx.m_drawTextFlags |= eDrawText_800x600; + } + + if (!params.m_scaleWithWindow) + { + ctx.m_drawTextFlags |= eDrawText_FixedSize; + } +} + +void AZ::FFont::DrawScreenAlignedText2d( + const AzFramework::TextDrawParameters& params, + const AZStd::string_view& string) +{ + if (params.m_drawViewportId == AzFramework::InvalidViewportId || + string.empty()) + { + return; + } + + //Code mostly duplicated from CRenderer::Draw2dTextWithDepth + float posX = params.m_position.GetX(); + float posY = params.m_position.GetY(); + AZ::RPI::ViewportContext* viewportContext = AZ::Interface::Get()->GetViewportContextById(params.m_drawViewportId).get(); + const AZ::RHI::Viewport& viewport = viewportContext->GetWindowContext()->GetViewport(); + if (params.m_virtual800x600ScreenSize) + { + posX *= WindowScaleWidth / (viewport.m_maxX - viewport.m_minX); + posY *= WindowScaleHeight / (viewport.m_maxY - viewport.m_minY); + } + TextDrawContext ctx; + ctx.SetBaseState(GS_NODEPTHTEST); + ctx.SetColor(AZColorToLYColorF(params.m_color)); + ctx.SetCharWidthScale((params.m_monospace || params.m_scaleWithWindow) ? 0.5f : 1.0f); + ctx.EnableFrame(false); + ctx.SetProportional(!params.m_monospace && params.m_scaleWithWindow); + ctx.SetSizeIn800x600(params.m_scaleWithWindow && params.m_virtual800x600ScreenSize); + ctx.SetSize(AZVec2ToLYVec2(UiDraw_TextSizeFactor * params.m_scale)); + if (params.m_monospace || !params.m_scaleWithWindow) + { + ScaleCoord(viewport, posX, posY); + } + + if (params.m_hAlign != AzFramework::TextHorizontalAlignment::Left || + params.m_vAlign != AzFramework::TextVerticalAlignment::Top) + { + Vec2 textSize = GetTextSizeUInternal(viewport, string.data(), params.m_multiline, ctx); + + // If we're using virtual 800x600 coordinates, convert the text size from + // pixels to that before using it as an offset. + if (ctx.m_sizeIn800x600) + { + float width = 1.0f; + float height = 1.0f; + ScaleCoord(viewport, width, height); + textSize.x /= width; + textSize.y /= height; + } + + if (params.m_hAlign == AzFramework::TextHorizontalAlignment::Center) + { + posX -= textSize.x * 0.5f; + } + else if (params.m_hAlign == AzFramework::TextHorizontalAlignment::Right) + { + posX -= textSize.x; + } + + if (params.m_vAlign == AzFramework::TextVerticalAlignment::Center) + { + posY -= textSize.y * 0.5f; + } + else if (params.m_vAlign == AzFramework::TextVerticalAlignment::Bottom) + { + posY -= textSize.y; + } + } + SetCommonContextFlags(ctx, params); + ctx.m_drawTextFlags |= eDrawText_2D; + + DrawStringUInternal( + viewport, + viewportContext, + posX, + posY, + params.m_position.GetZ(), // Z + string.data(), + params.m_multiline, + ctx + ); +} + +void AZ::FFont::DrawScreenAlignedText3d( + const AzFramework::TextDrawParameters& params, + const AZStd::string_view& string) +{ + if (params.m_drawViewportId == AzFramework::InvalidViewportId || + string.empty()) + { + return; + } + AZ::RPI::ViewportContext* viewportContext = AZ::Interface::Get()->GetViewportContextById(params.m_drawViewportId).get(); + AZ::RPI::ViewPtr currentView = viewportContext->GetDefaultView(); + if (!currentView) + { + return; + } + AZ::Vector3 positionNDC = AzFramework::WorldToScreenNDC( + params.m_position, + currentView->GetViewToWorldMatrix(), + currentView->GetViewToClipMatrix() + ); + AzFramework::TextDrawParameters param2d = params; + param2d.m_position = positionNDC; + DrawScreenAlignedText2d(param2d, string); +} -#endif +#endif //USE_NULLFONT_ALWAYS diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h index 857e795257..fe40cabc12 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h @@ -73,6 +73,9 @@ namespace AZ bool RequiresShapeComponent() const; + //! Returns true if the light type is anything other than unknown. + bool LightTypeIsSelected() const; + //! Returns true if m_attenuationRadiusMode is set to LightAttenuationRadiusMode::Automatic bool IsAttenuationRadiusModeAutomatic() const; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp index 2afa6514cd..2dc439b846 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp @@ -73,6 +73,11 @@ namespace AZ || m_lightType == LightType::Polygon; } + bool AreaLightComponentConfig::LightTypeIsSelected() const + { + return m_lightType != LightType::Unknown; + } + bool AreaLightComponentConfig::IsAttenuationRadiusModeAutomatic() const { return m_attenuationRadiusMode == LightAttenuationRadiusMode::Automatic; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp index 1bbc2a411e..c1c957ed4c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp @@ -234,6 +234,11 @@ namespace AZ::Render !(m_configuration.m_lightType == AreaLightComponentConfig::LightType::Polygon && m_configuration.m_shapeType != PoylgonShapeTypeId), "The light type is a polygon, but the shape component is not."); } + + if (m_configuration.m_lightType == AreaLightComponentConfig::LightType::SimpleSpot) + { + m_configuration.m_enableShutters = true; // Simple spot always has shutters. + } } void AreaLightComponentController::ConfigurationChanged() @@ -620,6 +625,10 @@ namespace AZ::Render break; } } + if (m_lightShapeDelegate) + { + m_lightShapeDelegate->SetConfig(&m_configuration); + } } } // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp index 32b6a9f0b7..2758da2b38 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp @@ -51,14 +51,52 @@ namespace AZ::Render return m_shapeBus->GetRadius() * GetTransform().GetScale().GetMaxElement(); } - void DiskLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const + void DiskLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& /*color*/, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const { if (isSelected) { - debugDisplay.SetColor(color); - - // Draw a disk for the attenuation radius - debugDisplay.DrawWireSphere(transform.GetTranslation(), CalculateAttenuationRadius(AreaLightComponentConfig::CutoffIntensity)); + debugDisplay.PushMatrix(transform); + float radius = GetConfig()->m_attenuationRadius; + + if (GetConfig()->m_enableShutters) + { + + float innerRadians = DegToRad(GetConfig()->m_innerShutterAngleDegrees); + float outerRadians = DegToRad(GetConfig()->m_outerShutterAngleDegrees); + + // Draw a cone using the cone angle and attenuation radius + innerRadians = GetMin(innerRadians, outerRadians); + float coneRadiusInner = sin(innerRadians) * radius; + float coneHeightInner = cos(innerRadians) * radius; + float coneRadiusOuter = sin(outerRadians) * radius; + float coneHeightOuter = cos(outerRadians) * radius; + + auto DrawConicalFrustum = [&debugDisplay](uint32_t numRadiusLines, float topRadius, float bottomRadius, float height, float brightness) + { + debugDisplay.SetColor(Color(brightness, brightness, brightness, 1.0f)); + debugDisplay.DrawWireDisk(Vector3(0.0, 0.0, height), Vector3::CreateAxisZ(), bottomRadius); + + for (uint32_t i = 0; i < numRadiusLines; ++i) + { + float radiusLineAngle = float(i) / numRadiusLines * Constants::TwoPi; + debugDisplay.DrawLine( + Vector3(cos(radiusLineAngle) * topRadius, sin(radiusLineAngle) * topRadius, 0), + Vector3(cos(radiusLineAngle) * bottomRadius, sin(radiusLineAngle) * bottomRadius, height) + ); + } + }; + + DrawConicalFrustum(16, m_shapeBus->GetRadius(), m_shapeBus->GetRadius() + coneRadiusInner, coneHeightInner, 1.0f); + DrawConicalFrustum(16, m_shapeBus->GetRadius(), m_shapeBus->GetRadius() + coneRadiusOuter, coneHeightOuter, 0.65f); + + } + else + { + debugDisplay.DrawWireDisk(Vector3::CreateZero(), Vector3::CreateAxisZ(), radius); + debugDisplay.DrawArc(Vector3::CreateZero(), radius, 90.0f, 180.0f, -3.0f, 0); + debugDisplay.DrawArc(Vector3::CreateZero(), radius, 0.0f, 180.0f, 3.0f, 1); + } + debugDisplay.PopMatrix(); } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index bdff97b48e..6a07a8a737 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp @@ -67,50 +67,56 @@ namespace AZ editContext->Class( "AreaLightComponentConfig", "") ->ClassElement(Edit::ClassElements::EditorData, "") - ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_lightType, "Light Type", "Which type of light this component represents.") - ->EnumAttribute(AreaLightComponentConfig::LightType::Unknown, "Choose a Light Type") - ->EnumAttribute(AreaLightComponentConfig::LightType::Sphere, "Point (Sphere)") - ->EnumAttribute(AreaLightComponentConfig::LightType::SimplePoint, "Point (Simple)") - ->EnumAttribute(AreaLightComponentConfig::LightType::SpotDisk, "Spot (Disk)") - ->EnumAttribute(AreaLightComponentConfig::LightType::SimpleSpot, "Spot (Simple)") + ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_lightType, "Light type", "Which type of light this component represents.") + ->EnumAttribute(AreaLightComponentConfig::LightType::Unknown, "Choose a light type") + ->EnumAttribute(AreaLightComponentConfig::LightType::Sphere, "Point (sphere)") + ->EnumAttribute(AreaLightComponentConfig::LightType::SimplePoint, "Point (simple punctual)") + ->EnumAttribute(AreaLightComponentConfig::LightType::SpotDisk, "Spot (disk)") + ->EnumAttribute(AreaLightComponentConfig::LightType::SimpleSpot, "Spot (simple punctual)") ->EnumAttribute(AreaLightComponentConfig::LightType::Capsule, "Capsule") ->EnumAttribute(AreaLightComponentConfig::LightType::Quad, "Quad") ->EnumAttribute(AreaLightComponentConfig::LightType::Polygon, "Polygon") ->DataElement(Edit::UIHandlers::Color, &AreaLightComponentConfig::m_color, "Color", "Color of the light") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::LightTypeIsSelected) ->Attribute("ColorEditorConfiguration", RPI::ColorUtils::GetLinearRgbEditorConfig()) - ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_intensityMode, "Intensity Mode", "Allows specifying which photometric unit to work in.") + ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_intensityMode, "Intensity mode", "Allows specifying which photometric unit to work in.") ->Attribute(AZ::Edit::Attributes::EnumValues, &AreaLightComponentConfig::GetValidPhotometricUnits) + ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::LightTypeIsSelected) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_intensity, "Intensity", "Intensity of the light in the set photometric unit.") ->Attribute(Edit::Attributes::Min, &AreaLightComponentConfig::GetIntensityMin) ->Attribute(Edit::Attributes::Max, &AreaLightComponentConfig::GetIntensityMax) ->Attribute(Edit::Attributes::SoftMin, &AreaLightComponentConfig::GetIntensitySoftMin) ->Attribute(Edit::Attributes::SoftMax, &AreaLightComponentConfig::GetIntensitySoftMax) ->Attribute(Edit::Attributes::Suffix, &AreaLightComponentConfig::GetIntensitySuffix) - ->DataElement(Edit::UIHandlers::CheckBox, &AreaLightComponentConfig::m_lightEmitsBothDirections, "Both Directions", "Whether light should emit from both sides of the surface or just the front") + ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::LightTypeIsSelected) + ->DataElement(Edit::UIHandlers::CheckBox, &AreaLightComponentConfig::m_lightEmitsBothDirections, "Both directions", "Whether light should emit from both sides of the surface or just the front") ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsBothDirections) - ->DataElement(Edit::UIHandlers::CheckBox, &AreaLightComponentConfig::m_useFastApproximation, "Fast Approximation", "Whether the light should use the default high quality linear transformed cosine technique or a faster approximation.") + ->DataElement(Edit::UIHandlers::CheckBox, &AreaLightComponentConfig::m_useFastApproximation, "Fast approximation", "Whether the light should use the default high quality linear transformed cosine technique or a faster approximation.") ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsFastApproximation) - ->ClassElement(Edit::ClassElements::Group, "Attenuation Radius") + ->ClassElement(Edit::ClassElements::Group, "Attenuation radius") ->Attribute(Edit::Attributes::AutoExpand, true) + ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::LightTypeIsSelected) ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_attenuationRadiusMode, "Mode", "Controls whether the attenation radius is calculated automatically or set explicitly.") ->EnumAttribute(LightAttenuationRadiusMode::Automatic, "Automatic") ->EnumAttribute(LightAttenuationRadiusMode::Explicit, "Explicit") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) + ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::LightTypeIsSelected) ->DataElement(Edit::UIHandlers::Default, &AreaLightComponentConfig::m_attenuationRadius, "Radius", "The distance at which this light no longer has an affect.") ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsAttenuationRadiusModeAutomatic) + ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::LightTypeIsSelected) ->ClassElement(Edit::ClassElements::Group, "Shutters") ->Attribute(Edit::Attributes::AutoExpand, true) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) - ->DataElement(Edit::UIHandlers::Default, &AreaLightComponentConfig::m_enableShutters, "Enable Shutters", "Restrict the light to a specific beam angle depending on shape.") + ->DataElement(Edit::UIHandlers::Default, &AreaLightComponentConfig::m_enableShutters, "Enable shutters", "Restrict the light to a specific beam angle depending on shape.") ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::ShuttersMustBeEnabled) - ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_innerShutterAngleDegrees, "Inner Angle", "The inner angle of the shutters where the light beam begins to be occluded.") + ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_innerShutterAngleDegrees, "Inner angle", "The inner angle of the shutters where the light beam begins to be occluded.") ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 180.0f) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShuttersDisabled) - ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_outerShutterAngleDegrees, "Outer Angle", "The outer angle of the shutters where the light beam is completely occluded.") + ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_outerShutterAngleDegrees, "Outer angle", "The outer angle of the shutters where the light beam is completely occluded.") ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 180.0f) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) @@ -119,9 +125,9 @@ namespace AZ ->ClassElement(Edit::ClassElements::Group, "Shadows") ->Attribute(Edit::Attributes::AutoExpand, true) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) - ->DataElement(Edit::UIHandlers::Default, &AreaLightComponentConfig::m_enableShadow, "Enable Shadow", "Enable shadow for the light") + ->DataElement(Edit::UIHandlers::Default, &AreaLightComponentConfig::m_enableShadow, "Enable shadow", "Enable shadow for the light") ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) - ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_shadowmapMaxSize, "Shadowmap Size", "Width/Height of shadowmap") + ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_shadowmapMaxSize, "Shadowmap size", "Width and height of shadowmap") ->EnumAttribute(ShadowmapSize::Size256, " 256") ->EnumAttribute(ShadowmapSize::Size512, " 512") ->EnumAttribute(ShadowmapSize::Size1024, "1024") @@ -129,13 +135,13 @@ namespace AZ ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled) - ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_shadowFilterMethod, "Shadow Filter Method", + ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_shadowFilterMethod, "Shadow filter method", "Filtering method of edge-softening of shadows.\n" " None: no filtering\n" - " PCF: Percentage-Closer Filtering\n" - " ESM: Exponential Shadow Maps\n" + " PCF: Percentage-closer Filtering\n" + " ESM: Exponential shadow maps\n" " ESM+PCF: ESM with a PCF fallback\n" - "For BehaviorContext (or TrackView), None=0, PCF=1, ESM=2, ESM+PCF=3") + "For BehaviorContext (or track view), None=0, PCF=1, ESM=2, ESM+PCF=3") ->EnumAttribute(ShadowFilterMethod::None, "None") ->EnumAttribute(ShadowFilterMethod::Pcf, "PCF") ->EnumAttribute(ShadowFilterMethod::Esm, "ESM") @@ -143,7 +149,7 @@ namespace AZ ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled) - ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_boundaryWidthInDegrees, "Softening Boundary Width", + ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_boundaryWidthInDegrees, "Softening boundary width", "Width of the boundary between shadowed area and lit one. " "Units are in degrees. " "If this is 0, softening edge is disabled.") @@ -152,26 +158,27 @@ namespace AZ ->Attribute(Edit::Attributes::Suffix, " deg") ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsPcfBoundarySearchDisabled) - ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_predictionSampleCount, "Prediction Sample Count", + ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_predictionSampleCount, "Prediction sample count", "Sample Count for prediction of whether the pixel is on the boundary. Specific to PCF and ESM+PCF.") ->Attribute(Edit::Attributes::Min, 4) ->Attribute(Edit::Attributes::Max, 16) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsPcfBoundarySearchDisabled) - ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_filteringSampleCount, "Filtering Sample Count", + ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_filteringSampleCount, "Filtering sample count", "It is used only when the pixel is predicted to be on the boundary. Specific to PCF and ESM+PCF.") ->Attribute(Edit::Attributes::Min, 4) ->Attribute(Edit::Attributes::Max, 64) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsShadowPcfDisabled) ->DataElement( - Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_pcfMethod, "Pcf Method", - "Type of Pcf to use.\n" + Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_pcfMethod, "Pcf method", + "Type of PCF to use.\n" " Boundary search: do several taps to first determine if we are on a shadow boundary\n" " Bicubic: a smooth, fixed-size kernel \n") - ->EnumAttribute(PcfMethod::BoundarySearch, "Boundary Search") + ->EnumAttribute(PcfMethod::BoundarySearch, "Boundary search") ->EnumAttribute(PcfMethod::Bicubic, "Bicubic") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsShadowPcfDisabled); ; } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h index 4cf94c1a58..da221341c3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -40,6 +41,8 @@ namespace AZ LightDelegateBase(EntityId entityId, bool isVisible); virtual ~LightDelegateBase(); + void SetConfig(const AreaLightComponentConfig* config) override; + // LightDelegateInterface overrides... void SetChroma(const AZ::Color& chroma) override; void SetIntensity(float intensity) override; @@ -66,6 +69,7 @@ namespace AZ // Trivial getters FeatureProcessorType* GetFeatureProcessor() const { return m_featureProcessor; }; + const AreaLightComponentConfig* GetConfig() const { return m_componentConfig; }; typename FeatureProcessorType::LightHandle GetLightHandle() const { return m_lightHandle; }; const AZ::Transform& GetTransform() const { return m_transform; }; bool GetShuttersEnabled() { return m_shuttersEnabled; }; @@ -81,6 +85,7 @@ namespace AZ private: FeatureProcessorType* m_featureProcessor = nullptr; typename FeatureProcessorType::LightHandle m_lightHandle; + const AreaLightComponentConfig* m_componentConfig = nullptr; LmbrCentral::ShapeComponentRequests* m_shapeBus; AZ::Transform m_transform; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.inl b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.inl index ee4a29f1b0..406df42547 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.inl +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.inl @@ -59,6 +59,12 @@ namespace AZ m_featureProcessor->SetRgbIntensity(m_lightHandle, m_photometricValue.GetCombinedRgb()); } } + + template + void LightDelegateBase::SetConfig(const AreaLightComponentConfig* config) + { + m_componentConfig = config; + } template void LightDelegateBase::SetChroma(const AZ::Color& color) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h index 110a7d73ad..b3f5fb6014 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h @@ -33,6 +33,9 @@ namespace AZ { public: virtual ~LightDelegateInterface() {}; + + //! Sets the area light component config so delegates don't have to cache the same data locally. + virtual void SetConfig(const AreaLightComponentConfig* config) = 0; //! Sets the color of the light independent of light intensity. The color is a mask on the total light intensity. virtual void SetChroma(const AZ::Color& chroma) = 0; //! Sets the light intensity diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.cpp index 255f527557..705a71571a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.cpp @@ -44,15 +44,40 @@ namespace AZ::Render { GetFeatureProcessor()->SetConeAngles(GetLightHandle(), DegToRad(innerAngleDegrees), DegToRad(outerAngleDegrees)); } - - void SimpleSpotLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const + + void SimpleSpotLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& /*color*/, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const { if (isSelected) { - debugDisplay.SetColor(color); + float innerRadians = DegToRad(GetConfig()->m_innerShutterAngleDegrees); + float outerRadians = DegToRad(GetConfig()->m_outerShutterAngleDegrees); + float radius = GetConfig()->m_attenuationRadius; + + // Draw a cone using the cone angle and attenuation radius + innerRadians = GetMin(innerRadians, outerRadians); + float coneRadiusInner = sin(innerRadians) * radius; + float coneHeightInner = cos(innerRadians) * radius; + float coneRadiusOuter = sin(outerRadians) * radius; + float coneHeightOuter = cos(outerRadians) * radius; + + debugDisplay.PushMatrix(transform); + + auto DrawCone = [&debugDisplay](uint32_t numRadiusLines, float radius, float height, float brightness) + { + debugDisplay.SetColor(Color(brightness, brightness, brightness, 1.0f)); + debugDisplay.DrawWireDisk(Vector3(0.0, 0.0, height), Vector3::CreateAxisZ(), radius); + + for (uint32_t i = 0; i < numRadiusLines; ++i) + { + float radiusLineAngle = float(i) / numRadiusLines * Constants::TwoPi; + debugDisplay.DrawLine(Vector3::CreateZero(), Vector3(cos(radiusLineAngle) * radius, sin(radiusLineAngle) * radius, height)); + } + }; + + DrawCone(16, coneRadiusInner, coneHeightInner, 1.0f); + DrawCone(16, coneRadiusOuter, coneHeightOuter, 0.65f); - // Draw a cone for the cone angle and attenuation radius - debugDisplay.DrawCone(transform.GetTranslation(), transform.GetBasisX(), CalculateAttenuationRadius(AreaLightComponentConfig::CutoffIntensity), false); + debugDisplay.PopMatrix(); } } } // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.h index 66569fc27c..28359daaef 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.h @@ -25,6 +25,8 @@ namespace AZ class SimpleSpotLightDelegate final : public LightDelegateBase { + using Base = LightDelegateBase; + public: SimpleSpotLightDelegate(EntityId entityId, bool isVisible); @@ -34,9 +36,9 @@ namespace AZ float GetSurfaceArea() const override; float GetEffectiveSolidAngle() const override { return PhotometricValue::DirectionalEffectiveSteradians; } void SetShutterAngles(float innerAngleDegrees, float outerAngleDegrees) override; + private: virtual void HandleShapeChanged(); - }; } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp index b8d1b1df28..34cc53a326 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp @@ -25,6 +25,7 @@ AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnin #include #include #include +#include #include #include #include @@ -215,19 +216,10 @@ namespace AZ tableWidget->sortItems(MaterialSlotColumn); // Create the bottom row of the dialog with action buttons for exporting or canceling the operation - QWidget* buttonRow = new QWidget(&dialog); - buttonRow->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - - QPushButton* confirmButton = new QPushButton("Confirm", buttonRow); - QObject::connect(confirmButton, &QPushButton::clicked, confirmButton, [&dialog] { dialog.accept(); }); - - QPushButton* cancelButton = new QPushButton("Cancel", buttonRow); - QObject::connect(cancelButton, &QPushButton::clicked, cancelButton, [&dialog] { dialog.reject(); }); - - QHBoxLayout* buttonLayout = new QHBoxLayout(buttonRow); - buttonLayout->addStretch(); - buttonLayout->addWidget(confirmButton); - buttonLayout->addWidget(cancelButton); + QDialogButtonBox* buttonBox = new QDialogButtonBox(&dialog); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); // Create a heading label for the top of the dialog QLabel* labelWidget = new QLabel("\nSelect the material slots that you want to generate new source materials for. Edit the material file name and location using the file picker.\n", &dialog); @@ -236,8 +228,9 @@ namespace AZ QVBoxLayout* dialogLayout = new QVBoxLayout(&dialog); dialogLayout->addWidget(labelWidget); dialogLayout->addWidget(tableWidget); - dialogLayout->addWidget(buttonRow); + dialogLayout->addWidget(buttonBox); dialog.setLayout(dialogLayout); + dialog.setModal(true); // Forcing the initial dialog size to accomodate typical content. // Temporarily settng fixed size because dialog.show/exec invokes WindowDecorationWrapper::showEvent. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index d2400f52a3..38e41a74b4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -37,6 +37,7 @@ AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include #include +#include #include #include #include @@ -534,7 +535,7 @@ namespace AZ inspector->Populate(); inspector->SetOverrides(propertyOverrideMap); - // Create the menu bottom row with actions for exporting or canceling the operation + // Create the menu button QToolButton* menuButton = new QToolButton(&dialog); menuButton->setAutoRaise(true); menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg")); @@ -546,10 +547,6 @@ namespace AZ action = menu.addAction("Clear Overrides", [&] { inspector->SetOverrides(MaterialPropertyOverrideMap()); }); action = menu.addAction("Revert Changes", [&] { inspector->SetOverrides(propertyOverrideMap); }); - menu.addSeparator(); - action = menu.addAction("Confirm Changes", [&] { dialog.accept(); }); - action = menu.addAction("Cancel Changes", [&] { dialog.reject(); }); - menu.addSeparator(); action = menu.addAction("Save Material", [&] { inspector->SaveMaterial(); }); action = menu.addAction("Save Material To Source", [&] { inspector->SaveMaterialToSource(); }); @@ -563,12 +560,19 @@ namespace AZ menu.exec(QCursor::pos()); }); + QDialogButtonBox* buttonBox = new QDialogButtonBox(&dialog); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + QObject::connect(&dialog, &QDialog::rejected, &dialog, [&] { inspector->SetOverrides(propertyOverrideMap); }); QVBoxLayout* dialogLayout = new QVBoxLayout(&dialog); dialogLayout->addWidget(menuButton); dialogLayout->addWidget(inspector); + dialogLayout->addWidget(buttonBox); dialog.setLayout(dialogLayout); + dialog.setModal(true); // Forcing the initial dialog size to accomodate typical content. // Temporarily settng fixed size because dialog.show/exec invokes WindowDecorationWrapper::showEvent. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index b7690f60e5..2f74991756 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -269,6 +269,8 @@ namespace AZ QAction* action = nullptr; + menu.addAction("Open Material Editor", [this]() { EditorMaterialSystemComponentRequestBus::Broadcast(&EditorMaterialSystemComponentRequestBus::Events::OpenInMaterialEditor, ""); }); + action = menu.addAction("Clear", [this]() { Clear(); }); action->setEnabled(m_materialAsset.GetId().IsValid() || !m_propertyOverrides.empty() || !m_matModUvOverrides.empty()); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialModelUvNameMapInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialModelUvNameMapInspector.cpp index f562f555f0..1c6e338ee9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialModelUvNameMapInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialModelUvNameMapInspector.cpp @@ -32,8 +32,11 @@ AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include #include -#include +#include #include +#include +#include +#include #include AZ_POP_DISABLE_WARNING @@ -286,42 +289,31 @@ namespace AZ MaterialModelUvNameMapInspector* inspector = new MaterialModelUvNameMapInspector(assetId, matModUvOverrides, modelUvNames, matModUvOverrideMapChangedCallBack, &dialog); inspector->Populate(); - // Create the bottom row of the dialog with action buttons for exporting or canceling the operation - QWidget* buttonRow = new QWidget(&dialog); - buttonRow->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - - QPushButton* revertButton = new QPushButton("Revert", buttonRow); - QObject::connect(revertButton, &QPushButton::clicked, revertButton, [inspector, matModUvOverrides] { - inspector->SetUvNameMap(matModUvOverrides); - }); - - QPushButton* clearButton = new QPushButton("Clear", buttonRow); - QObject::connect(clearButton, &QPushButton::clicked, clearButton, [inspector] { - inspector->SetUvNameMap(RPI::MaterialModelUvOverrideMap()); - }); - - QPushButton* confirmButton = new QPushButton("Confirm", buttonRow); - QObject::connect(confirmButton, &QPushButton::clicked, confirmButton, [&dialog] { - dialog.accept(); - }); - - QPushButton* cancelButton = new QPushButton("Cancel", buttonRow); - QObject::connect(cancelButton, &QPushButton::clicked, cancelButton, [inspector, matModUvOverrides, &dialog] { - inspector->SetUvNameMap(matModUvOverrides); - dialog.reject(); - }); - - QHBoxLayout* buttonLayout = new QHBoxLayout(buttonRow); - buttonLayout->addStretch(); - buttonLayout->addWidget(revertButton); - buttonLayout->addWidget(clearButton); - buttonLayout->addWidget(confirmButton); - buttonLayout->addWidget(cancelButton); + // Create the menu button + QToolButton* menuButton = new QToolButton(&dialog); + menuButton->setAutoRaise(true); + menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg")); + menuButton->setVisible(true); + QObject::connect(menuButton, &QToolButton::clicked, &dialog, [&]() { + QAction* action = nullptr; + + QMenu menu(&dialog); + action = menu.addAction("Clear", [&] { inspector->SetUvNameMap(RPI::MaterialModelUvOverrideMap()); }); + action = menu.addAction("Revert", [&] { inspector->SetUvNameMap(matModUvOverrides);; }); + menu.exec(QCursor::pos()); + }); + + QDialogButtonBox* buttonBox = new QDialogButtonBox(&dialog); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); QVBoxLayout* dialogLayout = new QVBoxLayout(&dialog); + dialogLayout->addWidget(menuButton); dialogLayout->addWidget(inspector); - dialogLayout->addWidget(buttonRow); + dialogLayout->addWidget(buttonBox); dialog.setLayout(dialogLayout); + dialog.setModal(true); // Forcing the initial dialog size to accomodate typical content. // Temporarily settng fixed size because dialog.show/exec invokes WindowDecorationWrapper::showEvent. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h index 9c8ea57cea..8a7bace6aa 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h @@ -36,7 +36,7 @@ namespace AZ struct ThumbnailRendererData final { static constexpr const char* LightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset"; - static constexpr const char* DefaultModelPath = "materialeditor/viewportmodels/quadsphere.azmodel"; + static constexpr const char* DefaultModelPath = "models/sphere.azmodel"; static constexpr const char* DefaultMaterialPath = "materials/basic_grey.azmaterial"; RPI::ScenePtr m_scene; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp index 238cbb3a3c..2a447ce3ec 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp @@ -54,8 +54,9 @@ namespace AZ RPI::SceneDescriptor sceneDesc; sceneDesc.m_featureProcessorNames.push_back("AZ::Render::TransformServiceFeatureProcessor"); sceneDesc.m_featureProcessorNames.push_back("AZ::Render::MeshFeatureProcessor"); + sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimplePointLightFeatureProcessor"); + sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimpleSpotLightFeatureProcessor"); sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PointLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SpotLightFeatureProcessor"); // There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow flickering [ATOM-13568] // as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now. // Possibly re-enable with [GFX TODO][ATOM-13639] diff --git a/Gems/AtomLyIntegration/CryRenderAtomShim/AtomShim_RenderAuxGeom.cpp b/Gems/AtomLyIntegration/CryRenderAtomShim/AtomShim_RenderAuxGeom.cpp index 093e068e2a..955b240514 100644 --- a/Gems/AtomLyIntegration/CryRenderAtomShim/AtomShim_RenderAuxGeom.cpp +++ b/Gems/AtomLyIntegration/CryRenderAtomShim/AtomShim_RenderAuxGeom.cpp @@ -519,8 +519,8 @@ void CAtomShimRenderAuxGeom::DrawQuad(float width, float height, const Matrix34& if (auto auxGeom = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(defaultScene)) { AZ::RPI::AuxGeomDraw::DrawStyle drawStyle = drawShaded ? AZ::RPI::AuxGeomDraw::DrawStyle::Shaded : AZ::RPI::AuxGeomDraw::DrawStyle::Solid; - AZ::Transform transform = LYTransformToAZTransform(matWorld); - auxGeom->DrawQuad(width, height, transform, LYColorBToAZColor(col), drawStyle, m_drawArgs.m_depthTest); + AZ::Matrix3x4 local2World = LYTransformToAZMatrix3x4(matWorld); + auxGeom->DrawQuad(width, height, local2World, LYColorBToAZColor(col), drawStyle, m_drawArgs.m_depthTest); } } diff --git a/Gems/AtomLyIntegration/ImguiAtom/Code/Source/DebugConsole.h b/Gems/AtomLyIntegration/ImguiAtom/Code/Source/DebugConsole.h index 29b2bfc4b5..44fa5b09e1 100644 --- a/Gems/AtomLyIntegration/ImguiAtom/Code/Source/DebugConsole.h +++ b/Gems/AtomLyIntegration/ImguiAtom/Code/Source/DebugConsole.h @@ -34,7 +34,6 @@ namespace AZ #if !defined(IMGUI_ENABLED) class DebugConsole {}; #else -#endif // defined(IMGUI_ENABLED) //////////////////////////////////////////////////////////////////////////////////////////////// //! A debug console used to enter debug console commands and display debug log messages. //! @@ -132,4 +131,5 @@ namespace AZ bool m_autoScroll = true; //!< Should we auto-scroll as new entries are added? bool m_forceScroll = false; //!< Do we need to force scroll after input entered? }; +#endif // defined(IMGUI_ENABLED) } // namespace AZ diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py index 483e14a8ec..0b75ea4330 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py @@ -158,10 +158,10 @@ def bootstrap_dccsi_py_libs(dccsi_dirpath=return_stub_dir()): """Builds and adds local site dir libs based on py version""" from azpy.constants import STR_DCCSI_PYTHON_LIB_PATH # a path string constructor - _DCCSI_PYTHON_LIB_PATH = "E:\\P4\\jromnoa_spectra_atom_2\\dev\\Tools\\Python\\3.7.5\\windows\\Lib\\site-packages" - # _DCCSI_PYTHON_LIB_PATH = STR_DCCSI_PYTHON_LIB_PATH.format(dccsi_dirpath, - # sys.version_info[0], - # sys.version_info[1]) + #_DCCSI_PYTHON_LIB_PATH = "E:\\P4\\jromnoa_spectra_atom_2\\dev\\Tools\\Python\\3.7.5\\windows\\Lib\\site-packages" + _DCCSI_PYTHON_LIB_PATH = STR_DCCSI_PYTHON_LIB_PATH.format(dccsi_dirpath, + sys.version_info[0], + sys.version_info[1]) if os.path.exists(_DCCSI_PYTHON_LIB_PATH): _LOGGER.debug('Performed site.addsitedir({})'.format(_DCCSI_PYTHON_LIB_PATH)) diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp index 5fd9c678dc..6227fc4d45 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp @@ -179,7 +179,7 @@ namespace Blast void EditorBlastMeshDataComponent::RegisterModel() { - if (m_meshFeatureProcessor && m_meshAssets[0].GetId().IsValid()) + if (m_meshFeatureProcessor && !m_meshAssets.empty() && m_meshAssets[0].GetId().IsValid()) { AZ::Render::MaterialAssignmentMap materials; AZ::Render::MaterialComponentRequestBus::EventResult( diff --git a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h index 0ac1f6c27e..95a16c311f 100644 --- a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h +++ b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h @@ -213,9 +213,6 @@ namespace Blast CreateShape, AZStd::shared_ptr( const Physics::ColliderConfiguration&, const Physics::ShapeConfiguration&)); - MOCK_METHOD4( - AddColliderComponentToEntity, - void(AZ::Entity*, const Physics::ColliderConfiguration&, const Physics::ShapeConfiguration&, bool)); MOCK_METHOD1(ReleaseNativeMeshObject, void(void*)); MOCK_METHOD1(CreateMaterial, AZStd::shared_ptr(const Physics::MaterialConfiguration&)); MOCK_METHOD0(GetDefaultMaterial, AZStd::shared_ptr()); diff --git a/Gems/EMotionFX/Code/CMakeLists.txt b/Gems/EMotionFX/Code/CMakeLists.txt index d46c67fafb..b90902a948 100644 --- a/Gems/EMotionFX/Code/CMakeLists.txt +++ b/Gems/EMotionFX/Code/CMakeLists.txt @@ -68,12 +68,6 @@ ly_add_target( ) if (PAL_TRAIT_BUILD_HOST_TOOLS) - - find_package(OpenGL QUIET REQUIRED) - # Imported targets (like OpenGL::GL) are scoped to a directory. Add a - # a global scope - add_library(3rdParty::OpenGLInterface INTERFACE IMPORTED GLOBAL) - target_link_libraries(3rdParty::OpenGLInterface INTERFACE OpenGL::GL) ly_add_target( NAME EMotionFX.Editor.Static STATIC diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLRenderUtil.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLRenderUtil.cpp index 3ef3908271..b8b4843555 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLRenderUtil.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLRenderUtil.cpp @@ -131,8 +131,14 @@ namespace RenderGL void GLRenderUtil::Validate() { - mLineShader->Validate(); - mMeshShader->Validate(); + if (mLineShader) + { + mLineShader->Validate(); + } + if (mMeshShader) + { + mMeshShader->Validate(); + } } // destroy the allocated memory diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLSLShader.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLSLShader.cpp index 54b926c4fa..b11c3cb955 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLSLShader.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLSLShader.cpp @@ -116,12 +116,12 @@ namespace RenderGL } - bool GLSLShader::CompileShader(const GLenum type, unsigned int* outShader, const char* filename) + bool GLSLShader::CompileShader(const GLenum type, unsigned int* outShader, AZ::IO::PathView filename) { - QFile file(filename); + QFile file(QString::fromUtf8(filename.Native().data(), aznumeric_caster(filename.Native().size()))); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - AZ_Error("EMotionFX", false, "[GLSL] Failed to open shader file '%s'.", filename); + AZ_Error("EMotionFX", false, "[GLSL] Failed to open shader file '%.*s'.", AZ_STRING_ARG(filename.Native())); return false; } @@ -156,7 +156,7 @@ namespace RenderGL if (success == false) { - MCore::LogError("[GLSL] Failed to compile shader '%s'.", filename); + MCore::LogError("[GLSL] Failed to compile shader '%.*s'.", AZ_STRING_ARG(filename.Native())); return false; } @@ -212,7 +212,7 @@ namespace RenderGL // Init - bool GLSLShader::Init(const char* vFile, const char* pFile, MCore::Array& defines) + bool GLSLShader::Init(AZ::IO::PathView vertexFileName, AZ::IO::PathView pixelFileName, MCore::Array& defines) { initializeOpenGLFunctions(); /*const char* args[] = { "unroll all", @@ -225,24 +225,24 @@ namespace RenderGL glUseProgram(0); // compile shaders - if (vFile && CompileShader(GL_VERTEX_SHADER, &mVertexShader, vFile) == false) + if (!vertexFileName.empty() && CompileShader(GL_VERTEX_SHADER, &mVertexShader, vertexFileName) == false) { return false; } - if (pFile && CompileShader(GL_FRAGMENT_SHADER, &mPixelShader, pFile) == false) + if (!pixelFileName.empty() && CompileShader(GL_FRAGMENT_SHADER, &mPixelShader, pixelFileName) == false) { return false; } // create program mProgram = glCreateProgram(); - if (vFile) + if (!vertexFileName.empty()) { glAttachShader(mProgram, mVertexShader); } - if (pFile) + if (!pixelFileName.empty()) { glAttachShader(mProgram, mPixelShader); } @@ -256,7 +256,7 @@ namespace RenderGL if (!success) { - MCore::LogInfo("[OpenGL] Failed to link shaders '%s' and '%s' ", vFile, pFile); + MCore::LogInfo("[OpenGL] Failed to link shaders '%.*s' and '%.*s' ", AZ_STRING_ARG(vertexFileName.Native()), AZ_STRING_ARG(pixelFileName.Native())); InfoLog(mProgram, &QOpenGLExtraFunctions::glGetProgramInfoLog); return false; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLSLShader.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLSLShader.h index b9251dd718..73aee4313f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLSLShader.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLSLShader.h @@ -14,6 +14,7 @@ #define __RENDERGL_GLSLSHADER_H #include +#include #include "Shader.h" // include OpenGL @@ -45,7 +46,7 @@ namespace RenderGL MCORE_INLINE unsigned int GetProgram() const { return mProgram; } bool CheckIfIsDefined(const char* attributeName); - bool Init(const char* vertexFileName, const char* pixelFileName, MCore::Array& defines); + bool Init(AZ::IO::PathView vertexFileName, AZ::IO::PathView pixelFileName, MCore::Array& defines); void SetAttribute(const char* name, uint32 dim, uint32 type, uint32 stride, size_t offset) override; void SetUniform(const char* name, float value) override; @@ -81,11 +82,11 @@ namespace RenderGL ShaderParameter* FindAttribute(const char* name); ShaderParameter* FindUniform(const char* name); - bool CompileShader(const GLenum type, unsigned int* outShader, const char* filename); + bool CompileShader(const GLenum type, unsigned int* outShader, AZ::IO::PathView filename); template void InfoLog(GLuint object, T func); - AZStd::string mFileName; + AZ::IO::Path mFileName; MCore::Array mActivatedAttribs; MCore::Array mActivatedTextures; diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GraphicsManager.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GraphicsManager.cpp index 127f18ba67..5512028ad5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GraphicsManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GraphicsManager.cpp @@ -203,7 +203,7 @@ namespace RenderGL // try to initialize the graphics system - bool GraphicsManager::Init(const char* shaderPath) + bool GraphicsManager::Init(AZ::IO::PathView shaderPath) { initializeOpenGLFunctions(); @@ -364,7 +364,7 @@ namespace RenderGL // try to load a texture - Texture* GraphicsManager::LoadTexture([[maybe_unused]] const char* filename, [[maybe_unused]] bool createMipMaps) + Texture* GraphicsManager::LoadTexture([[maybe_unused]] AZ::IO::PathView filename, [[maybe_unused]] bool createMipMaps) { //Texture Library is no longer used //temporarily blank @@ -373,19 +373,19 @@ namespace RenderGL // try to load a texture - Texture* GraphicsManager::LoadTexture(const char* filename) + Texture* GraphicsManager::LoadTexture(AZ::IO::PathView filename) { return LoadTexture(filename, mCreateMipMaps); } // LoadPostProcessShader - PostProcessShader* GraphicsManager::LoadPostProcessShader(const char* cFileName) + PostProcessShader* GraphicsManager::LoadPostProcessShader(AZ::IO::PathView cFileName) { - AZStd::string filename = mShaderPath + AZStd::string(cFileName); + AZ::IO::PathView filename = mShaderPath / cFileName; // check if the shader is already in the cache - Shader* s = mShaderCache.FindShader(filename.c_str()); + Shader* s = mShaderCache.FindShader(filename.Native()); if (s) { return (PostProcessShader*)s; @@ -393,19 +393,19 @@ namespace RenderGL // load the shader from disk PostProcessShader* shader = new PostProcessShader(); - if (!shader->Init(filename.c_str())) + if (!shader->Init(filename)) { delete shader; return nullptr; } - mShaderCache.AddShader(filename.c_str(), shader); + mShaderCache.AddShader(filename.Native(), shader); return shader; } // LoadShader - GLSLShader* GraphicsManager::LoadShader(const char* vertexFileName, const char* pixelFileName) + GLSLShader* GraphicsManager::LoadShader(AZ::IO::PathView vertexFileName, AZ::IO::PathView pixelFileName) { MCore::Array defines; return LoadShader(vertexFileName, pixelFileName, defines); @@ -413,34 +413,21 @@ namespace RenderGL // LoadShader - GLSLShader* GraphicsManager::LoadShader(const char* vFile, const char* pFile, MCore::Array& defines) + GLSLShader* GraphicsManager::LoadShader(AZ::IO::PathView vertexFileName, AZ::IO::PathView pixelFileName, MCore::Array& defines) { - AZStd::string vStr; - AZStd::string pStr; - - if (vFile) - { - vStr = AZStd::string::format("%s%s", mShaderPath.c_str(), vFile); - } - - if (pFile) - { - pStr = AZStd::string::format("%s%s", mShaderPath.c_str(), pFile); - } + const AZ::IO::Path vertexPath {vertexFileName.empty() ? AZ::IO::Path{} : mShaderPath / vertexFileName}; + const AZ::IO::Path pixelPath {pixelFileName.empty() ? AZ::IO::Path{} : mShaderPath / pixelFileName}; // construct the lookup string for the shader cache - AZStd::string dStr; + AZStd::string cacheLookupStr = vertexPath.Native() + pixelPath.Native(); const uint32 numDefines = defines.GetLength(); for (uint32 n = 0; n < numDefines; n++) { - dStr += AZStd::string::format("#%s", defines[n].c_str()); + cacheLookupStr += AZStd::string::format("#%s", defines[n].c_str()); } - AZStd::string cStr; - cStr = AZStd::string::format("%s%s%s", vStr.c_str(), pStr.c_str(), dStr.c_str()); - // check if the shader is already in the cache - Shader* cShader = mShaderCache.FindShader(cStr.c_str()); + Shader* cShader = mShaderCache.FindShader(cacheLookupStr); if (cShader) { return (GLSLShader*)cShader; @@ -448,13 +435,13 @@ namespace RenderGL // load the shader from disk GLSLShader* shader = new GLSLShader(); - if (!shader->Init(vFile ? vStr.c_str() : nullptr, pFile ? pStr.c_str() : nullptr, defines)) + if (!shader->Init(vertexPath, pixelPath, defines)) { delete shader; return nullptr; } - mShaderCache.AddShader(cStr.c_str(), shader); + mShaderCache.AddShader(cacheLookupStr, shader); return shader; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GraphicsManager.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GraphicsManager.h index 811fe2998b..4aaf6a16d1 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GraphicsManager.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GraphicsManager.h @@ -13,6 +13,7 @@ #ifndef __RENDERGL_GRAPHICSMANAGER__H #define __RENDERGL_GRAPHICSMANAGER__H +#include #include #include #include @@ -57,21 +58,21 @@ namespace RenderGL const char* GetDeviceName(); const char* GetDeviceVendor(); MCORE_INLINE RenderTexture* GetRenderTexture() { return mRenderTexture; } - MCORE_INLINE const char* GetShaderPath() const { return mShaderPath.c_str(); } + MCORE_INLINE AZ::IO::PathView GetShaderPath() const { return mShaderPath; } MCORE_INLINE TextureCache* GetTextureCache() { return &mTextureCache; } - bool Init(const char* shaderPath = "Shaders/"); + bool Init(AZ::IO::PathView shaderPath = "Shaders"); bool GetIsPostProcessingEnabled() const { return mPostProcessing; } - PostProcessShader* LoadPostProcessShader(const char* filename); - GLSLShader* LoadShader(const char* vertexFileName, const char* pixelFileName); - GLSLShader* LoadShader(const char* vertexFileName, const char* pixelFileName, MCore::Array& defines); + PostProcessShader* LoadPostProcessShader(AZ::IO::PathView filename); + GLSLShader* LoadShader(AZ::IO::PathView vertexFileName, AZ::IO::PathView pixelFileName); + GLSLShader* LoadShader(AZ::IO::PathView vertexFileName, AZ::IO::PathView pixelFileName, MCore::Array& defines); MCORE_INLINE void SetGBuffer(GBuffer* gBuffer) { mGBuffer = gBuffer; } MCORE_INLINE GBuffer* GetGBuffer() { return mGBuffer; } - Texture* LoadTexture(const char* filename, bool createMipMaps); - Texture* LoadTexture(const char* filename); + Texture* LoadTexture(AZ::IO::PathView filename, bool createMipMaps); + Texture* LoadTexture(AZ::IO::PathView filename); void SetCreateMipMaps(bool createMipMaps) { mCreateMipMaps = createMipMaps; } MCORE_INLINE bool GetCreateMipMaps() const { return mCreateMipMaps; } @@ -96,7 +97,7 @@ namespace RenderGL void SetShader(Shader* shader); MCORE_INLINE void SetRenderTexture(RenderTexture* texture) { mRenderTexture = texture; } - MCORE_INLINE void SetShaderPath(const char* shaderPath) { mShaderPath = shaderPath; } + MCORE_INLINE void SetShaderPath(AZ::IO::PathView shaderPath) { mShaderPath = shaderPath; } MCORE_INLINE void SetBloomEnabled(bool enabled) { mBloomEnabled = enabled; } MCORE_INLINE void SetBloomThreshold(float threshold) { mBloomThreshold = threshold; } @@ -154,7 +155,7 @@ namespace RenderGL MCommon::Camera* mCamera; /**< The camera used for rendering. */ ShaderCache mShaderCache; /**< The shader manager used to load and manage vertex and pixel shaders. */ - AZStd::string mShaderPath; /**< The absolute path to the directory where the shaders are located. This string will be added as prefix to each shader file the user tries to load. */ + AZ::IO::Path mShaderPath; /**< The absolute path to the directory where the shaders are located. This string will be added as prefix to each shader file the user tries to load. */ MCore::RGBAColor mClearColor; /**< The scene background color. */ MCore::RGBAColor mGradientSourceColor; /**< The background gradient source color. */ MCore::RGBAColor mGradientTargetColor; /**< The background gradient target color. */ diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/PostProcessShader.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/PostProcessShader.cpp index 11f46d2d13..8c5c79391f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/PostProcessShader.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/PostProcessShader.cpp @@ -11,6 +11,7 @@ */ #include +#include #include "PostProcessShader.h" #include "GraphicsManager.h" @@ -82,7 +83,7 @@ namespace RenderGL // Init - bool PostProcessShader::Init(const char* filename) + bool PostProcessShader::Init(AZ::IO::PathView filename) { MCore::Array defines; return GLSLShader::Init(nullptr, filename, defines); diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/PostProcessShader.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/PostProcessShader.h index c16554faa7..b3068a9b8a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/PostProcessShader.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/PostProcessShader.h @@ -13,6 +13,7 @@ #ifndef __RENDERGL_POSTPROCESS_SHADER_H #define __RENDERGL_POSTPROCESS_SHADER_H +#include #include "GLSLShader.h" #include "RenderTexture.h" @@ -34,7 +35,7 @@ namespace RenderGL void Deactivate() override; - bool Init(const char* filename); + bool Init(AZ::IO::PathView filename); void Render(); private: diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/ShaderCache.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/ShaderCache.cpp index cef9c93388..b3f2accb66 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/ShaderCache.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/ShaderCache.cpp @@ -48,7 +48,7 @@ namespace RenderGL // add the shader to the cache (assume there are no duplicate names) - void ShaderCache::AddShader(const char* filename, Shader* shader) + void ShaderCache::AddShader(AZStd::string_view filename, Shader* shader) { mEntries.AddEmpty(); mEntries.GetLast().mName = filename; @@ -57,12 +57,12 @@ namespace RenderGL // try to locate a shader based on its name - Shader* ShaderCache::FindShader(const char* filename) const + Shader* ShaderCache::FindShader(AZStd::string_view filename) const { const uint32 numEntries = mEntries.GetLength(); for (uint32 i = 0; i < numEntries; ++i) { - if (AzFramework::StringFunc::Equal(mEntries[i].mName.c_str(), filename, false /* no case */)) // non-case-sensitive name compare + if (AzFramework::StringFunc::Equal(mEntries[i].mName, filename, false /* no case */)) // non-case-sensitive name compare { return mEntries[i].mShader; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/shadercache.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/shadercache.h index d7ec884e6a..98bb87d373 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/shadercache.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/shadercache.h @@ -33,8 +33,8 @@ namespace RenderGL ~ShaderCache(); // automatically calls Release void Release(); - void AddShader(const char* filename, Shader* shader); - Shader* FindShader(const char* filename) const; + void AddShader(AZStd::string_view filename, Shader* shader); + Shader* FindShader(AZStd::string_view filename) const; bool CheckIfHasShader(Shader* shader) const; private: diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.cpp index 99f203d6e7..bfd6a3921f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.cpp @@ -64,7 +64,7 @@ namespace EMStudio // create graphics manager and initialize it mGraphicsManager = new RenderGL::GraphicsManager(); - if (mGraphicsManager->Init(shaderPath.c_str()) == false) + if (mGraphicsManager->Init(shaderPath) == false) { MCore::LogError("Could not initialize OpenGL graphics manager."); return false; diff --git a/Gems/EMotionFX/Code/Editor/Platform/Mac/platform_mac.cmake b/Gems/EMotionFX/Code/Editor/Platform/Mac/platform_mac.cmake index 821e7d1f25..95df062a93 100644 --- a/Gems/EMotionFX/Code/Editor/Platform/Mac/platform_mac.cmake +++ b/Gems/EMotionFX/Code/Editor/Platform/Mac/platform_mac.cmake @@ -13,10 +13,4 @@ # based on the active platform # NOTE: functions in cmake are global, therefore adding functions to this file # is being avoided to prevent overriding functions declared in other targets platfrom -# specific cmake files - -target_compile_definitions(3rdParty::OpenGLInterface - INTERFACE - # MacOS 10.14 deprecates OpenGL. This silences the warnings for now. - GL_SILENCE_DEPRECATION -) \ No newline at end of file +# specific cmake files \ No newline at end of file diff --git a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h index 0b20632884..1219e34448 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h @@ -33,7 +33,6 @@ namespace Physics BusDisconnect(); } MOCK_METHOD2(CreateShape, AZStd::shared_ptr(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& configuration)); - MOCK_METHOD4(AddColliderComponentToEntity, void(AZ::Entity* entity, const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& shapeConfiguration, bool addEditorComponents)); MOCK_METHOD1(ReleaseNativeMeshObject, void(void* nativeMeshObject)); MOCK_METHOD1(CreateMaterial, AZStd::shared_ptr(const Physics::MaterialConfiguration& materialConfiguration)); MOCK_METHOD0(GetDefaultMaterial, AZStd::shared_ptr()); diff --git a/Gems/ImGui/Code/Source/ImGuiManager.cpp b/Gems/ImGui/Code/Source/ImGuiManager.cpp index 697cc61f86..c446c98175 100644 --- a/Gems/ImGui/Code/Source/ImGuiManager.cpp +++ b/Gems/ImGui/Code/Source/ImGuiManager.cpp @@ -222,6 +222,10 @@ void ImGuiManager::Initialize() io.DisplaySize.x = 1920; io.DisplaySize.y = 1080; + // Create a default font + io.Fonts->AddFontDefault(); + io.Fonts->Build(); + // Broadcast ImGui Ready to Listeners ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnImGuiInitialize); m_currentControllerIndex = -1; diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorPolygonPrismShapeComponentMode.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorPolygonPrismShapeComponentMode.cpp index 0ea6be5e4d..3f36840e3d 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorPolygonPrismShapeComponentMode.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorPolygonPrismShapeComponentMode.cpp @@ -65,6 +65,7 @@ namespace LmbrCentral ShapeComponentNotificationsBus::Handler::BusDisconnect(); PolygonPrismShapeComponentNotificationBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(); + m_nonUniformScaleChangedHandler.Disconnect(); DestroyManipulators(); } diff --git a/Gems/LmbrCentral/Code/Source/Shape/PolygonPrismShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/PolygonPrismShape.cpp index 28056c6e60..89c5028e93 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/PolygonPrismShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/PolygonPrismShape.cpp @@ -217,6 +217,7 @@ namespace LmbrCentral AZ::TransformBus::EventResult(m_currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM); m_currentNonUniformScale = AZ::Vector3::CreateOne(); AZ::NonUniformScaleRequestBus::EventResult(m_currentNonUniformScale, m_entityId, &AZ::NonUniformScaleRequests::GetScale); + m_polygonPrism->SetNonUniformScale(m_currentNonUniformScale); m_intersectionDataCache.InvalidateCache(InvalidateShapeCacheReason::ShapeChange); AZ::TransformNotificationBus::Handler::BusConnect(entityId); diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimSequence.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimSequence.cpp index 8d6c547c0e..ffbdce8ed6 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimSequence.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimSequence.cpp @@ -823,20 +823,37 @@ void CAnimSequence::SetId(uint32 newId) } ////////////////////////////////////////////////////////////////////////// -void CAnimSequence::Reflect(AZ::SerializeContext* serializeContext) -{ - serializeContext->Class() - ->Version(4) - ->Field("Name", &CAnimSequence::m_name) - ->Field("SequenceEntityId", &CAnimSequence::m_sequenceEntityId) - ->Field("Flags", &CAnimSequence::m_flags) - ->Field("TimeRange", &CAnimSequence::m_timeRange) - ->Field("ID", &CAnimSequence::m_id) - ->Field("Nodes", &CAnimSequence::m_nodes) - ->Field("SequenceType", &CAnimSequence::m_sequenceType) - ->Field("Events", &CAnimSequence::m_events) - ->Field("Expanded", &CAnimSequence::m_expanded) - ->Field("ActiveDirectorNodeId", &CAnimSequence::m_activeDirectorNodeId); +static bool AnimSequenceVersionConverter( + AZ::SerializeContext& serializeContext, + AZ::SerializeContext::DataElementNode& rootElement) +{ + if (rootElement.GetVersion() < 5) + { + rootElement.AddElement(serializeContext, "BaseClass1", azrtti_typeid()); + } + + return true; +} + +void CAnimSequence::Reflect(AZ::ReflectContext* context) +{ + IAnimSequence::Reflect(context); + + if (auto serializeContext = azrtti_cast(context); serializeContext != nullptr) + { + serializeContext->Class() + ->Version(IAnimSequence::kSequenceVersion, &AnimSequenceVersionConverter) + ->Field("Name", &CAnimSequence::m_name) + ->Field("SequenceEntityId", &CAnimSequence::m_sequenceEntityId) + ->Field("Flags", &CAnimSequence::m_flags) + ->Field("TimeRange", &CAnimSequence::m_timeRange) + ->Field("ID", &CAnimSequence::m_id) + ->Field("Nodes", &CAnimSequence::m_nodes) + ->Field("SequenceType", &CAnimSequence::m_sequenceType) + ->Field("Events", &CAnimSequence::m_events) + ->Field("Expanded", &CAnimSequence::m_expanded) + ->Field("ActiveDirectorNodeId", &CAnimSequence::m_activeDirectorNodeId); + } } ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimSequence.h b/Gems/Maestro/Code/Source/Cinematics/AnimSequence.h index 3069896f50..d54b48c9eb 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimSequence.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimSequence.h @@ -154,7 +154,7 @@ public: return m_nextTrackId++; } - static void Reflect(AZ::SerializeContext* serializeContext); + static void Reflect(AZ::ReflectContext* context); private: void ComputeTimeRange(); diff --git a/Gems/Multiplayer/Code/Include/IMultiplayer.h b/Gems/Multiplayer/Code/Include/IMultiplayer.h index fd2b0e6cce..94744dbb54 100644 --- a/Gems/Multiplayer/Code/Include/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/IMultiplayer.h @@ -95,4 +95,20 @@ namespace Multiplayer private: MultiplayerStats m_stats; }; + + inline const char* GetEnumString(MultiplayerAgentType value) + { + switch (value) + { + case MultiplayerAgentType::Uninitialized: + return "Uninitialized"; + case MultiplayerAgentType::Client: + return "Client"; + case MultiplayerAgentType::ClientServer: + return "ClientServer"; + case MultiplayerAgentType::DedicatedServer: + return "DedicatedServer"; + } + return "INVALID"; + } } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja index 22365e685c..090bd4f0e0 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja @@ -1,6 +1,7 @@ #pragma once #include +#include namespace AZ { @@ -17,7 +18,13 @@ namespace {{ Namespace }} {% set ComponentName = Component.attrib['Name'] %} {{ ComponentName }}, {% endfor %} + Count }; + static_assert(ComponentTypes::Count < static_cast(Multiplayer::InvalidNetComponentId), "ComponentId overflow"); + //! For reflecting multiplayer components into the serialize, edit, and behaviour contexts. void CreateComponentDescriptors(AZStd::list& descriptors); + + //! For creating multiplayer component network inputs. + void CreateComponentNetworkInput(); } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 33509a61c7..f5774b07c0 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -33,22 +33,20 @@ const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}() const; #} {% macro DeclareNetworkPropertySetter(Property) %} {% set PropertyName = UpperFirst(Property.attrib['Name']) %} -{% if Property.attrib['IsPredictable'] | booleanTrue %} -{% if Property.attrib['Container'] == 'Array' %} -void Set{{ PropertyName }}(const Multiplayer::NetworkInput&, int32_t index, const {{ Property.attrib['Type'] }}& value); -{{ Property.attrib['Type'] }}& Modify{{ PropertyName }}(const Multiplayer::NetworkInput&, int32_t index); -{% elif Property.attrib['Container'] == 'Vector' %} -void Set{{ PropertyName }}(const Multiplayer::NetworkInput&, int32_t index, const {{ Property.attrib['Type'] }}& value); -{{ Property.attrib['Type'] }}& Modify{{ PropertyName }}(const Multiplayer::NetworkInput&, int32_t index); -bool {{ PropertyName }}PushBack(const Multiplayer::NetworkInput&, const {{ Property.attrib['Type'] }}& value); -bool {{ PropertyName }}PopBack(const Multiplayer::NetworkInput&); -void {{ PropertyName }}Clear(const Multiplayer::NetworkInput&); -{% elif Property.attrib['Container'] == 'Object' %} -void Set{{ PropertyName }}(const Multiplayer::NetworkInput&, const {{ Property.attrib['Type'] }}& value); -{{ Property.attrib['Type'] }}& Modify{{ PropertyName }}(const Multiplayer::NetworkInput&); -{% else %} -void Set{{ PropertyName }}(const Multiplayer::NetworkInput&, const {{ Property.attrib['Type'] }}& value); -{% endif %} +{% if Property.attrib['Container'] == 'Array' %} +void Set{{ PropertyName }}(int32_t index, const {{ Property.attrib['Type'] }}& value); +{{ Property.attrib['Type'] }}& Modify{{ PropertyName }}(int32_t index); +{% elif Property.attrib['Container'] == 'Vector' %} +void Set{{ PropertyName }}(int32_t index, const {{ Property.attrib['Type'] }}& value); +{{ Property.attrib['Type'] }}& Modify{{ PropertyName }}(int32_t index); +bool {{ PropertyName }}PushBack(const {{ Property.attrib['Type'] }}& value); +bool {{ PropertyName }}PopBack(); +void {{ PropertyName }}Clear(); +{% elif Property.attrib['Container'] == 'Object' %} +void Set{{ PropertyName }}(const {{ Property.attrib['Type'] }}& value); +{{ Property.attrib['Type'] }}& Modify{{ PropertyName }}(); +{% else %} +void Set{{ PropertyName }}(const {{ Property.attrib['Type'] }}& value); {% endif %} {% endmacro %} {# @@ -417,6 +415,7 @@ namespace {{ Component.attrib['Namespace'] }} static const Multiplayer::NetComponentId s_componentId = static_cast({{ Component.attrib['Namespace'] }}::ComponentTypes::{{ Component.attrib['Name'] }}); static void Reflect(AZ::ReflectContext* context); + static void ReflectToEditContext(AZ::ReflectContext* context); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 5dc470fcd1..aee15bc190 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -73,18 +73,17 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Even {# #} -{% macro DefineNetworkPropertyPredictableSet(Component, ReplicateFrom, ReplicateTo, ClassName, Property) %} -{% if Property.attrib['IsPredictable'] | booleanTrue %} -{% if Property.attrib['Container'] == 'Array' %} -void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const Multiplayer::NetworkInput& inputCommand, int32_t index, const {{ Property.attrib['Type'] }}& value) +{% macro DefineNetworkPropertySet(Component, ReplicateFrom, ReplicateTo, ClassName, Property) %} +{% if Property.attrib['Container'] == 'Array' %} +void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index, const {{ Property.attrib['Type'] }}& value) { if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}[index] != value) { - Modify{{ UpperFirst(Property.attrib['Name']) }}(inputCommand, index) = value; + Modify{{ UpperFirst(Property.attrib['Name']) }}(index) = value; } } -{{ Property.attrib['Type'] }}& {{ ClassName }}::Modify{{ UpperFirst(Property.attrib['Name']) }}(const Multiplayer::NetworkInput&, int32_t index) +{{ Property.attrib['Type'] }}& {{ ClassName }}::Modify{{ UpperFirst(Property.attrib['Name']) }}(int32_t index) { int32_t bitIndex = index + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); @@ -92,16 +91,16 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const Multipl return GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}[index]; } -{% elif Property.attrib['Container'] == 'Vector' %} -void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const Multiplayer::NetworkInput& inputCommand, int32_t index, const {{ Property.attrib['Type'] }}& value) +{% elif Property.attrib['Container'] == 'Vector' %} +void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index, const {{ Property.attrib['Type'] }}& value) { if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}[index] != value) { - Modify{{ UpperFirst(Property.attrib['Name']) }}(inputCommand, index) = value; + Modify{{ UpperFirst(Property.attrib['Name']) }}(index) = value; } } -{{ Property.attrib['Type'] }}& {{ ClassName }}::Modify{{ UpperFirst(Property.attrib['Name']) }}(const Multiplayer::NetworkInput&, int32_t index) +{{ Property.attrib['Type'] }}& {{ ClassName }}::Modify{{ UpperFirst(Property.attrib['Name']) }}(int32_t index) { int32_t bitIndex = index + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); @@ -109,7 +108,7 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const Multipl return GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}[index]; } -bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const Multiplayer::NetworkInput& inputCommand, const {{ Property.attrib['Type'] }} &value) +bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value) { int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.GetSize(); GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.PushBack(value); @@ -134,24 +133,24 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}Clear(const Multi GetParent().MarkDirty(); } -{% elif Property.attrib['Container'] == 'Object' %} -void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const Multiplayer::NetworkInput& inputCommand, const {{ Property.attrib['Type'] }}& value) +{% elif Property.attrib['Container'] == 'Object' %} +void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Property.attrib['Type'] }}& value) { if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }} != value) { - Modify{{ UpperFirst(Property.attrib['Name']) }}(inputCommand) = value; + Modify{{ UpperFirst(Property.attrib['Name']) }}() = value; } } -{{ Property.attrib['Type'] }}& {{ ClassName }}::Modify{{ UpperFirst(Property.attrib['Name']) }}(const Multiplayer::NetworkInput&) +{{ Property.attrib['Type'] }}& {{ ClassName }}::Modify{{ UpperFirst(Property.attrib['Name']) }}() { GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property) }}), true); GetParent().MarkDirty(); return GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}{% if Property.attrib['IsRewindable']|booleanTrue %}.Modify(){% endif %}; } -{% else %} -void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const Multiplayer::NetworkInput&, const {{ Property.attrib['Type'] }}& value) +{% else %} +void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Property.attrib['Type'] }}& value) { if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }} != value) { @@ -161,7 +160,6 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const Multipl } } -{% endif %} {% endif %} {% endmacro %} {# @@ -273,7 +271,7 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Prop {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} {% if Property.attrib['IsPublic'] | booleanTrue != IsProtected %} {{ DefineNetworkPropertyGet(ClassName, Property, "GetParent().") }} -{{ DefineNetworkPropertyPredictableSet(Component, ReplicateFrom, ReplicateTo, ClassName, Property) }} +{{ DefineNetworkPropertySet(Component, ReplicateFrom, ReplicateTo, ClassName, Property) }} {% endif %} {% endcall %} {% endmacro %} @@ -478,6 +476,7 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re {%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} {% endcall %} {% if networkPropertyCount.value > 0 %} + MultiplayerStats& stats = AZ::Interface::Get()->GetStats(); // We modify the record if we are writing an update so that we don't notify for a change that really didn't change the value (just a duplicated send from the server) [[maybe_unused]] bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject; {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} @@ -509,7 +508,8 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property) }}), m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ Property.attrib['Name'] }}", - GetNetComponentId() + GetNetComponentId(), + stats ); {% endif %} {% endcall %} @@ -1111,23 +1111,29 @@ namespace {{ Component.attrib['Namespace'] }} {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(16) -}} {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(16) -}} {{ DefineNetworkPropertyReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(16) }} - {{ DefineArchetypePropertyReflection(Component, ComponentBaseName)|indent(16) }} - ; + {{ DefineArchetypePropertyReflection(Component, ComponentBaseName)|indent(16) }}; + } + ReflectToEditContext(context); + } + void {{ ComponentBaseName }}::{{ ComponentBaseName }}::ReflectToEditContext(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { AZ::EditContext* editContext = serializeContext->GetEditContext(); if (editContext) { - editContext->Class<{{ ComponentBaseName }}>("{{ ComponentName }}", "{{ Component.attrib['Description'] }}") + editContext->Class<{{ ComponentName }}>("{{ ComponentName }}", "{{ Component.attrib['Description'] }}") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) - {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Authority', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(20) }} - {{ DefineArchetypePropertyEditReflection(Component, ComponentBaseName)|indent(20) }} - ; + {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Authority', ComponentName)|indent(20) -}} +{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentName)|indent(20) -}} +{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Client', ComponentName)|indent(20) -}} +{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Autonomous', ComponentName)|indent(20) -}} +{{ DefineNetworkPropertyEditReflection(Component, 'Autonomous', 'Authority', ComponentName)|indent(20) }} + {{ DefineArchetypePropertyEditReflection(Component, ComponentName)|indent(20) }}; } } } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index faeaf8d0cc..d38ebbb1b8 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -16,7 +16,7 @@ - + @@ -25,7 +25,7 @@ - + diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 0f96fd45e1..24986148c4 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -24,7 +24,6 @@ namespace Multiplayer serializeContext->Class() ->Version(1); } - LocalPredictionPlayerInputComponentBase::Reflect(context); } @@ -48,7 +47,7 @@ namespace Multiplayer void LocalPredictionPlayerInputComponentController::HandleSendClientInputCorrection ( - [[maybe_unused]] const Multiplayer::NetworkInputId& inputId, + [[maybe_unused]] const Multiplayer::ClientInputId& inputId, [[maybe_unused]] const AzNetworking::PacketEncodingBuffer& correction ) { diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.h b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.h index d7029ed0a1..b332315799 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.h +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.h @@ -42,6 +42,6 @@ namespace Multiplayer void HandleSendClientInput(const Multiplayer::NetworkInputVector& inputArray, const uint32_t& stateHash, const AzNetworking::PacketEncodingBuffer& clientState) override; void HandleSendMigrateClientInput(const Multiplayer::MigrateNetworkInputVector& inputArray) override; - void HandleSendClientInputCorrection(const Multiplayer::NetworkInputId& inputId, const AzNetworking::PacketEncodingBuffer& correction) override; + void HandleSendClientInputCorrection(const Multiplayer::ClientInputId& inputId, const AzNetworking::PacketEncodingBuffer& correction) override; }; } diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp index 8dc8c6d303..fcdad87416 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp @@ -43,8 +43,12 @@ namespace Multiplayer NetEntityId MultiplayerComponent::GetNetEntityId() const { - const NetBindComponent* netBindComponent = GetNetBindComponent(); - return netBindComponent ? netBindComponent->GetNetEntityId() : InvalidNetEntityId; + return m_netBindComponent ? m_netBindComponent->GetNetEntityId() : InvalidNetEntityId; + } + + NetEntityRole MultiplayerComponent::GetNetEntityRole() const + { + return m_netBindComponent ? m_netBindComponent->GetNetEntityRole() : NetEntityRole::InvalidRole; } ConstNetworkEntityHandle MultiplayerComponent::GetEntityHandle() const diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.h b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.h index 46823f4c92..9efc13ed4b 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.h +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.h @@ -62,6 +62,7 @@ namespace Multiplayer //! @} NetEntityId GetNetEntityId() const; + NetEntityRole GetNetEntityRole() const; ConstNetworkEntityHandle GetEntityHandle() const; NetworkEntityHandle GetEntityHandle(); void MarkDirty(); @@ -109,7 +110,8 @@ namespace Multiplayer int32_t bitIndex, TYPE& value, const char* name, - [[maybe_unused]] NetComponentId componentId + [[maybe_unused]] NetComponentId componentId, + MultiplayerStats& stats ) { if (bitset.GetBit(bitIndex)) @@ -119,6 +121,7 @@ namespace Multiplayer serializer.Serialize(value, name); if (modifyRecord && !serializer.GetTrackedChangesFlag()) { + // If the serializer didn't change any values, then lower the flag so we don't unnecessarily notify bitset.SetBit(bitIndex, false); } const uint32_t postUpdateSize = serializer.GetSize(); @@ -126,8 +129,7 @@ namespace Multiplayer const uint32_t updateSize = (postUpdateSize - prevUpdateSize); if (updateSize > 0) { - MultiplayerStats& stats = AZ::Interface::Get()->GetStats(); - if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) + if (modifyRecord) { stats.m_propertyUpdatesRecv++; stats.m_propertyUpdatesRecvBytes += updateSize; diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerController.cpp b/Gems/Multiplayer/Code/Source/Components/MultiplayerController.cpp index 97746c3f6c..737ecc10cc 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerController.cpp +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerController.cpp @@ -27,6 +27,11 @@ namespace Multiplayer return m_owner.GetNetEntityId(); } + NetEntityRole MultiplayerController::GetNetEntityRole() const + { + return GetNetBindComponent()->GetNetEntityRole(); + } + AZ::Entity* MultiplayerController::GetEntity() const { return m_owner.GetEntity(); diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerController.h b/Gems/Multiplayer/Code/Source/Components/MultiplayerController.h index 3495893812..9e3c7d68ab 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerController.h +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerController.h @@ -47,6 +47,10 @@ namespace Multiplayer //! @return the networkId for the entity that owns this controller NetEntityId GetNetEntityId() const; + //! Returns the networkRole for the entity that owns this controller. + //! @return the networkRole for the entity that owns this controller + NetEntityRole GetNetEntityRole() const; + //! Returns the raw AZ::Entity pointer for the entity that owns this controller. //! @return the raw AZ::Entity pointer for the entity that owns this controller AZ::Entity* GetEntity() const; diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index 2e6ae60558..607e2813ec 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include namespace Multiplayer { @@ -24,7 +26,81 @@ namespace Multiplayer serializeContext->Class() ->Version(1); } - NetworkTransformComponentBase::Reflect(context); } + + NetworkTransformComponent::NetworkTransformComponent() + : m_rotationEventHandler([this](const AZ::Quaternion& rotation) { OnRotationChangedEvent(rotation); }) + , m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); }) + , m_scaleEventHandler([this](const AZ::Vector3& scale) { OnScaleChangedEvent(scale); }) + { + ; + } + + void NetworkTransformComponent::OnInit() + { + ; + } + + void NetworkTransformComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + RotationAddEvent(m_rotationEventHandler); + TranslationAddEvent(m_translationEventHandler); + ScaleAddEvent(m_scaleEventHandler); + } + + void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + ; + } + + void NetworkTransformComponent::OnRotationChangedEvent(const AZ::Quaternion& rotation) + { + AZ::Transform worldTm = GetTransformComponent()->GetWorldTM(); + worldTm.SetRotation(rotation); + GetTransformComponent()->SetWorldTM(worldTm); + } + + void NetworkTransformComponent::OnTranslationChangedEvent(const AZ::Vector3& translation) + { + AZ::Transform worldTm = GetTransformComponent()->GetWorldTM(); + worldTm.SetTranslation(translation); + GetTransformComponent()->SetWorldTM(worldTm); + } + + void NetworkTransformComponent::OnScaleChangedEvent(const AZ::Vector3& scale) + { + AZ::Transform worldTm = GetTransformComponent()->GetWorldTM(); + worldTm.SetScale(scale); + GetTransformComponent()->SetWorldTM(worldTm); + } + + + NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent) + : NetworkTransformComponentControllerBase(parent) + , m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformChangedEvent(worldTm); }) + { + ; + } + + void NetworkTransformComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + GetParent().GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler); + OnTransformChangedEvent(GetParent().GetTransformComponent()->GetWorldTM()); + } + + void NetworkTransformComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + ; + } + + void NetworkTransformComponentController::OnTransformChangedEvent(const AZ::Transform& worldTm) + { + if (GetNetEntityRole() == NetEntityRole::Authority) + { + SetRotation(worldTm.GetRotation()); + SetTranslation(worldTm.GetTranslation()); + SetScale(worldTm.GetScale()); + } + } } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.h index 4719f11a7a..2a3b5fb3cc 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.h @@ -13,6 +13,7 @@ #pragma once #include +#include namespace Multiplayer { @@ -22,20 +23,36 @@ namespace Multiplayer public: AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkTransformComponent, s_networkTransformComponentConcreteUuid, Multiplayer::NetworkTransformComponentBase); - static void Reflect([[maybe_unused]] AZ::ReflectContext* context); + static void Reflect(AZ::ReflectContext* context); - void OnInit() override {} - void OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {} - void OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {} + NetworkTransformComponent(); + + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + private: + void OnRotationChangedEvent(const AZ::Quaternion& rotation); + void OnTranslationChangedEvent(const AZ::Vector3& translation); + void OnScaleChangedEvent(const AZ::Vector3& scale); + + AZ::Event::Handler m_rotationEventHandler; + AZ::Event::Handler m_translationEventHandler; + AZ::Event::Handler m_scaleEventHandler; }; class NetworkTransformComponentController : public NetworkTransformComponentControllerBase { public: - NetworkTransformComponentController(NetworkTransformComponent& parent) : NetworkTransformComponentControllerBase(parent) {} + NetworkTransformComponentController(NetworkTransformComponent& parent); + + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + private: + void OnTransformChangedEvent(const AZ::Transform& worldTm); - void OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {} - void OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {} + AZ::TransformChangedEvent::Handler m_transformChangedHandler; }; } diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp new file mode 100644 index 0000000000..1388c1f5d2 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp @@ -0,0 +1,59 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +namespace Multiplayer +{ + static constexpr uint32_t Uint32Max = AZStd::numeric_limits::max(); + + // This can be used to help mitigate client side performance when large numbers of entities are created off the network + AZ_CVAR(uint32_t, cl_ClientMaxRemoteEntitiesPendingCreationCount, Uint32Max, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client"); + AZ_CVAR(AZ::TimeMs, cl_ClientEntityReplicatorPendingRemovalTimeMs, AZ::TimeMs{ 10000 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "How long should wait prior to removing an entity for the client through a change in the replication window, entity deletes are still immediate"); + + ClientToServerConnectionData::ClientToServerConnectionData + ( + AzNetworking::IConnection* connection, + AzNetworking::IConnectionListener& connectionListener + ) + : m_connection(connection) + , m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer) + { + m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(cl_ClientMaxRemoteEntitiesPendingCreationCount); + m_entityReplicationManager.SetEntityPendingRemovalMs(cl_ClientEntityReplicatorPendingRemovalTimeMs); + } + + ClientToServerConnectionData::~ClientToServerConnectionData() + { + m_entityReplicationManager.Clear(false); + } + + ConnectionDataType ClientToServerConnectionData::GetConnectionDataType() const + { + return ConnectionDataType::ClientToServer; + } + + AzNetworking::IConnection* ClientToServerConnectionData::GetConnection() const + { + return m_connection; + } + + EntityReplicationManager& ClientToServerConnectionData::GetReplicationManager() + { + return m_entityReplicationManager; + } + + void ClientToServerConnectionData::Update([[maybe_unused]] AZ::TimeMs serverGameTimeMs) + { + m_entityReplicationManager.ActivatePendingEntities(); + } +} diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h new file mode 100644 index 0000000000..b63ffee9a3 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h @@ -0,0 +1,47 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace Multiplayer +{ + class ClientToServerConnectionData final + : public IConnectionData + { + public: + ClientToServerConnectionData + ( + AzNetworking::IConnection* connection, + AzNetworking::IConnectionListener& connectionListener + ); + ~ClientToServerConnectionData() override; + + //! IConnectionData interface + //! @{ + ConnectionDataType GetConnectionDataType() const override; + AzNetworking::IConnection* GetConnection() const override; + EntityReplicationManager& GetReplicationManager() override; + void Update(AZ::TimeMs serverGameTimeMs) override; + //! @} + + bool CanSendUpdates(); + + private: + EntityReplicationManager m_entityReplicationManager; + AzNetworking::IConnection* m_connection = nullptr; + bool m_canSendUpdates = true; + }; +} + +#include diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl new file mode 100644 index 0000000000..1ee5711341 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl @@ -0,0 +1,19 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +namespace Multiplayer +{ + inline bool ClientToServerConnectionData::CanSendUpdates() + { + return m_canSendUpdates; + } +} diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/IConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/IConnectionData.h index ba2541ec7b..ebff75fd9b 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/IConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/IConnectionData.h @@ -19,6 +19,7 @@ namespace Multiplayer { enum class ConnectionDataType { + ClientToServer, ServerToClient, ServerToServer }; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index a622814088..70323d7de3 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -66,6 +68,7 @@ namespace Multiplayer AZ_CVAR(AZ::CVarFixedString, sv_gamerules, "norules", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "GameRules server works with"); AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking"); AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server"); + AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything"); void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context) { @@ -126,23 +129,26 @@ namespace Multiplayer // Handle deferred local rpc messages that were generated during the updates m_networkEntityManager.DispatchLocalDeferredRpcMessages(); m_networkEntityManager.NotifyEntitiesChanged(); + // Let the network system know the frame is done and we can collect dirty bits m_networkEntityManager.NotifyEntitiesDirtied(); - MultiplayerStats& stats = GetStats(); - stats.m_entityCount = GetNetworkEntityManager()->GetEntityCount(); - - auto sendNetworkUpdates = [serverGameTimeMs](IConnection& connection) + // Send out the game state update to all connections { - if (connection.GetUserData() != nullptr) + auto sendNetworkUpdates = [serverGameTimeMs](IConnection& connection) { - IConnectionData* connectionData = reinterpret_cast(connection.GetUserData()); - connectionData->Update(serverGameTimeMs); - } - }; + if (connection.GetUserData() != nullptr) + { + IConnectionData* connectionData = reinterpret_cast(connection.GetUserData()); + connectionData->Update(serverGameTimeMs); + } + }; - // Send out the game state update to all connections - m_networkInterface->GetConnectionSet().VisitConnections(sendNetworkUpdates); + m_networkInterface->GetConnectionSet().VisitConnections(sendNetworkUpdates); + } + + MultiplayerStats& stats = GetStats(); + stats.m_entityCount = GetNetworkEntityManager()->GetEntityCount(); MultiplayerPackets::SyncConsole packet; AZ::ThreadSafeDeque::DequeType cvarUpdates; @@ -245,12 +251,8 @@ namespace Multiplayer AZ::CVarFixedString commandString = "sv_map " + packet.GetMap(); AZ::Interface::Get()->PerformCommand(commandString.c_str()); - // This is a bit tricky, so it warrants extra commenting - // The cry level loader has a 'map' command used to invoke the level load system - // We don't want any explicit cry dependencies, so instead we rely on the - // az console binding inside SystemInit to echo any unhandled commands to - // the cry console by stripping off the prefix 'sv_' - AZ::Interface::Get()->PerformCommand(commandString.c_str() + 3); + AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap(); + AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); return true; } @@ -410,6 +412,16 @@ namespace Multiplayer AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window)); } + else + { + if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so + { + connection->SetUserData(new ClientToServerConnectionData(connection, *this)); + } + + AZStd::unique_ptr window = AZStd::make_unique(); + reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs); + } } bool MultiplayerSystemComponent::OnPacketReceived(AzNetworking::IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer) @@ -463,6 +475,7 @@ namespace Multiplayer } } m_agentType = multiplayerType; + AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType)); } void MultiplayerSystemComponent::AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) @@ -535,14 +548,15 @@ namespace Multiplayer void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer; + AZ::Interface::Get()->InitializeMultiplayer(serverType); INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); networkInterface->Listen(sv_port); - AZ::Interface::Get()->InitializeMultiplayer(serverType); } AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to"); void connect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { + AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); if (arguments.size() < 1) @@ -567,12 +581,12 @@ namespace Multiplayer int32_t portNumber = atol(portStr); const IpAddress ipAddress(addressStr, aznumeric_cast(portNumber), networkInterface->GetType()); networkInterface->Connect(ipAddress); - AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); } AZ_CONSOLEFREEFUNC(connect, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection to a remote host"); void disconnect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { + AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Uninitialized); INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; networkInterface->GetConnectionSet().VisitConnections(visitor); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index f249124fb0..1e10f9841e 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -21,8 +21,8 @@ #include #include #include -#include #include +#include namespace AzNetworking { diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index e58b7fde9d..8c076f759d 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -126,9 +127,9 @@ namespace Multiplayer MultiplayerPackets::EntityUpdates entityUpdatePacket; entityUpdatePacket.SetHostTimeMs(serverGameTimeMs); // Serialize everything - for (auto it = toSendList.begin(); it != toSendList.end();) + while (!toSendList.empty()) { - EntityReplicator* replicator = *it; + EntityReplicator* replicator = toSendList.front(); NetworkEntityUpdateMessage updateMessage(replicator->GenerateUpdatePacket()); const uint32_t nextMessageSize = updateMessage.GetEstimatedSerializeSize(); @@ -144,15 +145,15 @@ namespace Multiplayer pendingPacketSize += nextMessageSize; entityUpdatePacket.ModifyEntityMessages().push_back(updateMessage); - replicatorUpdatedList.push_back(*it); - it = toSendList.erase(it); + replicatorUpdatedList.push_back(replicator); + toSendList.pop_front(); if (largeEntityDetected) { AZLOG_WARN("\n\n*******************************"); AZLOG_WARN ( - "Serializing Extremely Large Entity (%u) - MaxPayload: %d NeededSize %d", + "Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d", aznumeric_cast(replicator->GetEntityHandle().GetNetEntityId()), maxPayloadSize, nextMessageSize @@ -173,16 +174,16 @@ namespace Multiplayer EntityReplicationManager::EntityReplicatorList EntityReplicationManager::GenerateEntityUpdateList() { + if (m_replicationWindow == nullptr) + { + return EntityReplicatorList(); + } + // Generate a list of all our entities that need updates - EntityReplicatorList autonomousReplicators; - autonomousReplicators.reserve(m_replicatorsPendingSend.size()); - EntityReplicatorList proxyReplicators; - proxyReplicators.reserve(m_replicatorsPendingSend.size()); + EntityReplicatorList toSendList; uint32_t elementsAdded = 0; - for (auto iter = m_replicatorsPendingSend.begin(); - iter != m_replicatorsPendingSend.end() - && elementsAdded < m_replicationWindow->GetMaxEntityReplicatorSendCount();) + for (auto iter = m_replicatorsPendingSend.begin(); iter != m_replicatorsPendingSend.end() && elementsAdded < m_replicationWindow->GetMaxEntityReplicatorSendCount(); ) { EntityReplicator* replicator = GetEntityReplicator(*iter); bool clearPendingSend = true; @@ -218,13 +219,13 @@ namespace Multiplayer if (replicator->GetRemoteNetworkRole() == NetEntityRole::Autonomous) { - autonomousReplicators.push_back(replicator); + toSendList.push_back(replicator); } else { if (elementsAdded < m_replicationWindow->GetMaxEntityReplicatorSendCount()) { - proxyReplicators.push_back(replicator); + toSendList.push_back(replicator); } } } @@ -243,9 +244,6 @@ namespace Multiplayer } } - EntityReplicatorList toSendList; - toSendList.swap(autonomousReplicators); - toSendList.insert(toSendList.end(), proxyReplicators.begin(), proxyReplicators.end()); return toSendList; } @@ -543,6 +541,7 @@ namespace Multiplayer // Create an entity if we don't have one if (createEntity) { + // @pereslav //replicatorEntity = GetNetworkEntityManager()->CreateSingleEntityImmediateInternal(prefabEntityId, EntitySpawnType::Replicate, AutoActivate::DoNotActivate, netEntityId, localNetworkRole, AZ::Transform::Identity()); AZ_Assert(replicatorEntity != nullptr, "Failed to create entity from prefab");// %s", prefabEntityId.GetString()); if (replicatorEntity == nullptr) @@ -765,7 +764,7 @@ namespace Multiplayer return HandleEntityDeleteMessage(entityReplicator, packetHeader, updateMessage); } - AzNetworking::NetworkOutputSerializer outputSerializer(updateMessage.GetData()->GetBuffer(), updateMessage.GetData()->GetSize()); + AzNetworking::TrackChangedSerializer outputSerializer(updateMessage.GetData()->GetBuffer(), updateMessage.GetData()->GetSize()); PrefabEntityId prefabEntityId; if (updateMessage.GetHasValidPrefabId()) @@ -1125,7 +1124,7 @@ namespace Multiplayer { if (message.GetPropertyUpdateData().GetSize() > 0) { - AzNetworking::NetworkOutputSerializer outputSerializer(message.ModifyPropertyUpdateData().GetBuffer(), message.ModifyPropertyUpdateData().GetSize()); + AzNetworking::TrackChangedSerializer outputSerializer(message.ModifyPropertyUpdateData().GetBuffer(), message.ModifyPropertyUpdateData().GetSize()); if (!HandlePropertyChangeMessage ( replicator, diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h index 1385a33208..a6470e6c34 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -114,7 +115,7 @@ namespace Multiplayer using RpcMessages = AZStd::list; bool DispatchOrphanedRpc(NetworkEntityRpcMessage& message, EntityReplicator* entityReplicator); - using EntityReplicatorList = AZStd::vector; + using EntityReplicatorList = AZStd::deque; EntityReplicatorList GenerateEntityUpdateList(); void SendEntityUpdatesPacketHelper(AZ::TimeMs serverGameTimeMs, EntityReplicatorList& toSendList, uint32_t maxPayloadSize, AzNetworking::IConnection& connection); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index b0d0328ba8..0edb5db250 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -283,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) @@ -311,9 +311,9 @@ namespace Multiplayer { bool ret(false); bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server) - && (GetRemoteNetworkRole() == NetEntityRole::Authority); + && (GetRemoteNetworkRole() == NetEntityRole::Authority); bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client) - || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); + || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); if (isServer || isClient) { ret = true; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 46d1617760..23be4fb4fb 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,12 @@ namespace Multiplayer , m_entityRemovedEventHandler([this](AZ::Entity* entity) { OnEntityRemoved(entity); }) { AZ::Interface::Register(this); + if (AZ::Interface::Get() != nullptr) + { + // Null guard needed for unit tests + AZ::Interface::Get()->RegisterEntityAddedEventHandler(m_entityAddedEventHandler); + AZ::Interface::Get()->RegisterEntityRemovedEventHandler(m_entityRemovedEventHandler); + } } NetworkEntityManager::~NetworkEntityManager() @@ -43,13 +50,6 @@ namespace Multiplayer void NetworkEntityManager::Initialize(HostId hostId, AZStd::unique_ptr entityDomain) { - if (AZ::Interface::Get() != nullptr) - { - // Null guard needed for unit tests - AZ::Interface::Get()->RegisterEntityAddedEventHandler(m_entityAddedEventHandler); - AZ::Interface::Get()->RegisterEntityRemovedEventHandler(m_entityRemovedEventHandler); - } - m_hostId = hostId; m_entityDomain = AZStd::move(entityDomain); m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true); @@ -282,8 +282,13 @@ namespace Multiplayer NetBindComponent* netBindComponent = entity->FindComponent(); if (netBindComponent != nullptr) { + // @pereslav + // Note that this is a total hack.. we should not be listening to this event on a client + // Entities should instead be spawned by the prefabEntityId inside EntityReplicationManager::HandlePropertyChangeMessage() + const bool isClient = AZ::Interface::Get()->GetAgentType() == MultiplayerAgentType::Client; + const NetEntityRole netEntityRole = isClient ? NetEntityRole::Client: NetEntityRole::Authority; const NetEntityId netEntityId = m_nextEntityId++; - netBindComponent->PreInit(entity, PrefabEntityId(), netEntityId, NetEntityRole::Authority); + netBindComponent->PreInit(entity, PrefabEntityId(), netEntityId, netEntityRole); } } diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp index 5991e0391d..a02bc4209d 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp @@ -33,22 +33,36 @@ namespace Multiplayer return *this; } - void NetworkInput::SetNetworkInputId(NetworkInputId inputId) + void NetworkInput::SetClientInputId(ClientInputId inputId) { m_inputId = inputId; } - NetworkInputId NetworkInput::GetNetworkInputId() const + ClientInputId NetworkInput::GetClientInputId() const { return m_inputId; } - - NetworkInputId& NetworkInput::ModifyNetworkInputId() + ClientInputId& NetworkInput::ModifyClientInputId() { return m_inputId; } + void NetworkInput::SetServerTimeMs(AZ::TimeMs serverTimeMs) + { + m_serverTimeMs = serverTimeMs; + } + + AZ::TimeMs NetworkInput::GetServerTimeMs() const + { + return m_serverTimeMs; + } + + AZ::TimeMs& NetworkInput::ModifyServerTimeMs() + { + return m_serverTimeMs; + } + void NetworkInput::AttachNetBindComponent(NetBindComponent* netBindComponent) { m_wasAttached = true; @@ -62,7 +76,6 @@ namespace Multiplayer bool NetworkInput::Serialize(AzNetworking::ISerializer& serializer) { - //static_assert(UINT8_MAX >= Multiplayer::ComponentTypes::c_Count, "Expected fewer than 255 components, this code needs to be updated"); if (!serializer.Serialize(m_inputId, "InputId")) { return false; @@ -135,8 +148,9 @@ namespace Multiplayer void NetworkInput::CopyInternal(const NetworkInput& rhs) { m_inputId = rhs.m_inputId; + m_serverTimeMs = rhs.m_serverTimeMs; m_componentInputs.resize(rhs.m_componentInputs.size()); - for (int i = 0; i < rhs.m_componentInputs.size(); ++i) + for (int32_t i = 0; i < rhs.m_componentInputs.size(); ++i) { if (m_componentInputs[i] == nullptr || m_componentInputs[i]->GetComponentId() != rhs.m_componentInputs[i]->GetComponentId()) { diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.h index 9d0cb6849d..43768c4196 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.h @@ -21,7 +21,7 @@ namespace Multiplayer // Forwards class NetBindComponent; - AZ_TYPE_SAFE_INTEGRAL(NetworkInputId, uint16_t); + AZ_TYPE_SAFE_INTEGRAL(ClientInputId, uint16_t); //! @class NetworkInput //! @brief A single networked client input command. @@ -38,9 +38,13 @@ namespace Multiplayer NetworkInput(const NetworkInput&); NetworkInput& operator= (const NetworkInput&); - void SetNetworkInputId(NetworkInputId inputId); - NetworkInputId GetNetworkInputId() const; - NetworkInputId& ModifyNetworkInputId(); + void SetClientInputId(ClientInputId inputId); + ClientInputId GetClientInputId() const; + ClientInputId& ModifyClientInputId(); + + void SetServerTimeMs(AZ::TimeMs serverTimeMs); + AZ::TimeMs GetServerTimeMs() const; + AZ::TimeMs& ModifyServerTimeMs(); void AttachNetBindComponent(NetBindComponent* netBindComponent); @@ -67,10 +71,11 @@ namespace Multiplayer void CopyInternal(const NetworkInput& rhs); MultiplayerComponentInputVector m_componentInputs; - NetworkInputId m_inputId = NetworkInputId{ 0 }; + ClientInputId m_inputId = ClientInputId{ 0 }; + AZ::TimeMs m_serverTimeMs = AZ::TimeMs{ 0 }; ConstNetworkEntityHandle m_owner; bool m_wasAttached = false; }; } -AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetworkInputId); +AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::ClientInputId); diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputVector.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputVector.cpp index 6f35bdc5fa..466a112e12 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputVector.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputVector.cpp @@ -48,12 +48,12 @@ namespace Multiplayer return m_inputs[index].m_networkInput; } - void NetworkInputVector::SetPreviousInputId(NetworkInputId previousInputId) + void NetworkInputVector::SetPreviousInputId(ClientInputId previousInputId) { m_previousInputId = previousInputId; } - NetworkInputId NetworkInputVector::GetPreviousInputId() const + ClientInputId NetworkInputVector::GetPreviousInputId() const { return m_previousInputId; } diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputVector.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputVector.h index 63332aa9c2..be41495577 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputVector.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputVector.h @@ -32,8 +32,8 @@ namespace Multiplayer NetworkInput& operator[](uint32_t index); const NetworkInput& operator[](uint32_t index) const; - void SetPreviousInputId(NetworkInputId previousInputId); - NetworkInputId GetPreviousInputId() const; + void SetPreviousInputId(ClientInputId previousInputId); + ClientInputId GetPreviousInputId() const; bool Serialize(AzNetworking::ISerializer& serializer); @@ -48,7 +48,7 @@ namespace Multiplayer ConstNetworkEntityHandle m_owner; AZStd::fixed_vector m_inputs; - NetworkInputId m_previousInputId; + ClientInputId m_previousInputId; }; //! @class MigrateNetworkInputVector diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/RewindableObject.inl b/Gems/Multiplayer/Code/Source/NetworkTime/RewindableObject.inl index d5936e6718..69752210bb 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/RewindableObject.inl +++ b/Gems/Multiplayer/Code/Source/NetworkTime/RewindableObject.inl @@ -73,7 +73,7 @@ namespace Multiplayer template inline BASE_TYPE& RewindableObject::Modify() { - ApplicationFrameId frameTime = GetCurrentTimeForProperty(); + const ApplicationFrameId frameTime = GetCurrentTimeForProperty(); if (frameTime < m_headTime) { AZ_Assert(false, "Trying to mutate a rewindable in the past"); @@ -82,7 +82,7 @@ namespace Multiplayer { SetValueForTime(GetValueForTime(frameTime), frameTime); } - const BASE_TYPE& returnValue = GetValueForTime(GetCurrentTimeForProperty()); + const BASE_TYPE& returnValue = GetValueForTime(frameTime); return const_cast(returnValue); } @@ -103,10 +103,11 @@ namespace Multiplayer template inline bool RewindableObject::Serialize(AzNetworking::ISerializer& serializer) { - BASE_TYPE current = GetValueForTime(GetCurrentTimeForProperty()); - if (serializer.Serialize(current, "Element") && (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)) + const ApplicationFrameId frameTime = GetCurrentTimeForProperty(); + BASE_TYPE value = GetValueForTime(frameTime); + if (serializer.Serialize(value, "Element") && (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)) { - SetValueForTime(current, GetCurrentTimeForProperty()); + SetValueForTime(value, frameTime); } return serializer.IsValid(); } diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.cpp new file mode 100644 index 0000000000..698376dccf --- /dev/null +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.cpp @@ -0,0 +1,47 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "NullReplicationWindow.h" + +namespace Multiplayer +{ + bool NullReplicationWindow::ReplicationSetUpdateReady() + { + return true; + } + + const ReplicationSet& NullReplicationWindow::GetReplicationSet() const + { + return m_emptySet; + } + + uint32_t NullReplicationWindow::GetMaxEntityReplicatorSendCount() const + { + return 0; + } + + bool NullReplicationWindow::IsInWindow([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const + { + outNetworkRole = NetEntityRole::InvalidRole; + return false; + } + + void NullReplicationWindow::UpdateWindow() + { + ; + } + + void NullReplicationWindow::DebugDraw() const + { + // Nothing to draw + } +} diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.h b/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.h new file mode 100644 index 0000000000..76562a34e2 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.h @@ -0,0 +1,38 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace Multiplayer +{ + class NullReplicationWindow + : public IReplicationWindow + { + public: + NullReplicationWindow() = default; + + //! IReplicationWindow interface + //! @{ + bool ReplicationSetUpdateReady() override; + const ReplicationSet& GetReplicationSet() const override; + uint32_t GetMaxEntityReplicatorSendCount() const override; + bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const override; + void UpdateWindow() override; + void DebugDraw() const override; + //! @} + + private: + ReplicationSet m_emptySet; + }; +} diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp index a74001d647..f6aa6b2de9 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp @@ -202,8 +202,7 @@ namespace Multiplayer void ServerToClientReplicationWindow::OnEntityActivated(const AZ::EntityId& entityId) { - AZ::Entity* entity = nullptr; - EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, entityId); + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(entityId); ConstNetworkEntityHandle entityHandle(entity, GetNetworkEntityTracker()); NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); @@ -234,8 +233,7 @@ namespace Multiplayer void ServerToClientReplicationWindow::OnEntityDeactivated(const AZ::EntityId& entityId) { - AZ::Entity* entity = nullptr; - EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, entityId); + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(entityId); ConstNetworkEntityHandle entityHandle(entity, GetNetworkEntityTracker()); NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); diff --git a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp index 13adc6d774..c18c2ee5e3 100644 --- a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp +++ b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp @@ -97,6 +97,10 @@ namespace UnitTest m_mpComponent->OnConnect(&connMock2); EXPECT_EQ(m_connectionAcquiredCount, 25); + + // Clean up connection data + m_mpComponent->OnDisconnect(&connMock1, AzNetworking::DisconnectReason::None, AzNetworking::TerminationEndpoint::Local); + m_mpComponent->OnDisconnect(&connMock2, AzNetworking::DisconnectReason::None, AzNetworking::TerminationEndpoint::Local); } } diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index eea9379df9..275625b8b4 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -34,6 +34,9 @@ set(FILES Source/Components/NetBindComponent.h Source/Components/NetworkTransformComponent.cpp Source/Components/NetworkTransformComponent.h + Source/ConnectionData/ClientToServerConnectionData.cpp + Source/ConnectionData/ClientToServerConnectionData.h + Source/ConnectionData/ClientToServerConnectionData.inl Source/ConnectionData/IConnectionData.h Source/ConnectionData/ServerToClientConnectionData.cpp Source/ConnectionData/ServerToClientConnectionData.h @@ -81,6 +84,8 @@ set(FILES Source/NetworkTime/NetworkTime.h Source/NetworkTime/RewindableObject.h Source/NetworkTime/RewindableObject.inl + Source/ReplicationWindows/NullReplicationWindow.cpp + Source/ReplicationWindows/NullReplicationWindow.h Source/ReplicationWindows/IReplicationWindow.h Source/ReplicationWindows/ServerToClientReplicationWindow.cpp Source/ReplicationWindows/ServerToClientReplicationWindow.h diff --git a/Gems/PhysX/Code/Editor/EditorWindow.cpp b/Gems/PhysX/Code/Editor/EditorWindow.cpp index 41b2991c2a..c8d6715838 100644 --- a/Gems/PhysX/Code/Editor/EditorWindow.cpp +++ b/Gems/PhysX/Code/Editor/EditorWindow.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,8 @@ #include #include #include +#include +#include namespace PhysX { diff --git a/Gems/PhysX/Code/Editor/EditorWindow.h b/Gems/PhysX/Code/Editor/EditorWindow.h index 34f1906d43..86f72f70f2 100644 --- a/Gems/PhysX/Code/Editor/EditorWindow.h +++ b/Gems/PhysX/Code/Editor/EditorWindow.h @@ -19,6 +19,7 @@ namespace AzPhysics { class CollisionConfiguration; + struct SceneConfiguration; } namespace Ui @@ -28,6 +29,12 @@ namespace Ui namespace PhysX { + struct PhysXSystemConfiguration; + namespace Debug + { + struct DebugConfiguration; + } + namespace Editor { /// Window pane wrapper for the PhysX Configuration Widget. diff --git a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp index 2f4b389795..516b958adb 100644 --- a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp +++ b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp @@ -18,13 +18,15 @@ #include #include #include -#include -#include #include #include #include +#include +#include +#include +#include #include namespace PhysX @@ -116,18 +118,20 @@ namespace PhysX { AzPhysics::SceneConfiguration editorWorldConfiguration = physicsSystem->GetDefaultSceneConfiguration(); editorWorldConfiguration.m_sceneName = AzPhysics::EditorPhysicsSceneName; - editorWorldConfiguration.m_sceneName = "EditorScene"; m_editorWorldSceneHandle = physicsSystem->AddScene(editorWorldConfiguration); } PhysX::RegisterConfigStringLineEditHandler(); // Register custom unique string line edit control + PhysX::Editor::RegisterPropertyTypes(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect(); } void EditorSystemComponent::Deactivate() { AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); Physics::EditorWorldBus::Handler::BusDisconnect(); if (auto* physicsSystem = AZ::Interface::Get()) @@ -164,6 +168,16 @@ namespace PhysX } } + void EditorSystemComponent::PopulateEditorGlobalContextMenu([[maybe_unused]] QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) + { + + } + + void EditorSystemComponent::NotifyRegisterViews() + { + PhysX::Editor::EditorWindow::RegisterViewClass(); + } + AZ::Data::AssetId EditorSystemComponent::GenerateSurfaceTypesLibrary() { AZ::Data::AssetId resultAssetId; diff --git a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h index cba542ce84..9ebca05ccd 100644 --- a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h +++ b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h @@ -30,6 +30,7 @@ namespace PhysX : public AZ::Component , public Physics::EditorWorldBus::Handler , private AzToolsFramework::EditorEntityContextNotificationBus::Handler + , private AzToolsFramework::EditorEvents::Bus::Handler { public: AZ_COMPONENT(EditorSystemComponent, "{560F08DC-94F5-4D29-9AD4-CDFB3B57C654}"); @@ -60,6 +61,10 @@ namespace PhysX void OnStartPlayInEditorBegin() override; void OnStopPlayInEditor() override; + // AztoolsFramework::EditorEvents::Bus::Handler + void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; + void NotifyRegisterViews() override; + AZ::Data::AssetId GenerateSurfaceTypesLibrary(); AzPhysics::SceneHandle m_editorWorldSceneHandle = AzPhysics::InvalidSceneHandle; diff --git a/Gems/PhysX/Code/Source/BaseColliderComponent.cpp b/Gems/PhysX/Code/Source/BaseColliderComponent.cpp index f4bc86ef93..c8581efcb7 100644 --- a/Gems/PhysX/Code/Source/BaseColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/BaseColliderComponent.cpp @@ -330,10 +330,8 @@ namespace PhysX } const bool hasNonUniformScale = (AZ::NonUniformScaleRequestBus::FindFirstHandler(GetEntityId()) != nullptr); - // the value for the subdivision level doesn't matter in the runtime, because any approximation of primitives will already have - // happened in the editor, so can pass an arbitrary value here - AZ::u8 subdivisionLevel = 0; - Utils::GetShapesFromAsset(physicsAssetConfiguration, componentColliderConfiguration, hasNonUniformScale, subdivisionLevel, m_shapes); + Utils::GetShapesFromAsset(physicsAssetConfiguration, componentColliderConfiguration, hasNonUniformScale, + physicsAssetConfiguration.m_subdivisionLevel, m_shapes); return true; } diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index ba1349c9af..c470c05c58 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -391,6 +391,8 @@ namespace PhysX Physics::WorldBodyRequestBus::Handler::BusDisconnect(); m_colliderDebugDraw.Disconnect(); AZ::Data::AssetBus::MultiHandler::BusDisconnect(); + m_nonUniformScaleChangedHandler.Disconnect(); + EditorColliderComponentRequestBus::Handler::BusDisconnect(); AZ::Render::MeshComponentNotificationBus::Handler::BusDisconnect(); LmbrCentral::MeshComponentNotificationBus::Handler::BusDisconnect(); ColliderShapeRequestBus::Handler::BusDisconnect(); @@ -514,6 +516,8 @@ namespace PhysX break; case Physics::ShapeType::PhysicsAsset: colliderComponent = gameEntity->CreateComponent(); + + m_shapeConfiguration.m_physicsAsset.m_configuration.m_subdivisionLevel = m_shapeConfiguration.m_subdivisionLevel; colliderComponent->SetShapeConfigurationList({ AZStd::make_pair(sharedColliderConfig, AZStd::make_shared(m_shapeConfiguration.m_physicsAsset.m_configuration)) }); @@ -561,6 +565,8 @@ namespace PhysX void EditorColliderComponent::CreateStaticEditorCollider() { + m_cachedAabbDirty = true; + // Don't create static rigid body in the editor if current entity components // don't allow creation of runtime static rigid body component if (!StaticRigidBodyUtils::CanCreateRuntimeComponent(*GetEntity())) @@ -1014,11 +1020,17 @@ namespace PhysX // PhysX::ColliderShapeBus AZ::Aabb EditorColliderComponent::GetColliderShapeAabb() { - return PhysX::Utils::GetColliderAabb(GetWorldTM() - , m_hasNonUniformScale - , m_shapeConfiguration.m_subdivisionLevel - , m_shapeConfiguration.GetCurrent() - , m_configuration); + if (m_cachedAabbDirty) + { + m_cachedAabb = PhysX::Utils::GetColliderAabb(GetWorldTM() + , m_hasNonUniformScale + , m_shapeConfiguration.m_subdivisionLevel + , m_shapeConfiguration.GetCurrent() + , m_configuration); + m_cachedAabbDirty = false; + } + + return m_cachedAabb; } void EditorColliderComponent::UpdateShapeConfigurationScale() diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.h b/Gems/PhysX/Code/Source/EditorColliderComponent.h index 65c7a9c67f..f179525802 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.h @@ -261,6 +261,8 @@ namespace PhysX bool m_hasNonUniformScale = false; //!< Whether there is a non-uniform scale component on this entity. AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne(); //!< Caches the current non-uniform scale. mutable AZStd::optional m_scaledPrimitive; //!< Approximation for non-uniformly scaled primitive. + AZ::Aabb m_cachedAabb = AZ::Aabb::CreateNull(); //!< Cache the Aabb to avoid recalculating it. + bool m_cachedAabbDirty = true; //!< Track whether the cached Aabb needs to be recomputed. AZ::ComponentDescriptor::StringWarningArray m_componentWarnings; }; diff --git a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp index 1dd25ced32..b4b730538d 100644 --- a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp @@ -277,6 +277,7 @@ namespace PhysX force.Deactivate(); } + m_nonUniformScaleChangedHandler.Disconnect(); AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); EditorComponentBase::Deactivate(); } diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index eff1997473..5445f7fc69 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -36,7 +36,7 @@ namespace PhysX const bool hasNonUniformScaleComponent = (AZ::NonUniformScaleRequestBus::FindFirstHandler(entity->GetId()) != nullptr); - const AZStd::vector colliders = entity->FindComponents(); + const AZStd::vector colliders = entity->FindComponents(); for (const EditorColliderComponent* collider : colliders) { const EditorProxyShapeConfig& shapeConfigurationProxy = collider->GetShapeConfiguration(); @@ -45,12 +45,14 @@ namespace PhysX continue; } - const Physics::ColliderConfiguration colliderConfiguration = collider->GetColliderConfigurationScaled(); + const Physics::ColliderConfiguration colliderConfigurationScaled = collider->GetColliderConfigurationScaled(); + const Physics::ColliderConfiguration colliderConfigurationUnscaled = collider->GetColliderConfiguration(); + if (shapeConfigurationProxy.IsAssetConfig()) { AZStd::vector> shapes; Utils::GetShapesFromAsset(shapeConfigurationProxy.m_physicsAsset.m_configuration, - colliderConfiguration, hasNonUniformScaleComponent, shapeConfigurationProxy.m_subdivisionLevel, shapes); + colliderConfigurationUnscaled, hasNonUniformScaleComponent, shapeConfigurationProxy.m_subdivisionLevel, shapes); for (const auto& shape : shapes) { @@ -64,7 +66,7 @@ namespace PhysX if (!hasNonUniformScaleComponent) { AZStd::shared_ptr shape = AZ::Interface::Get()->CreateShape( - colliderConfiguration, shapeConfiguration); + colliderConfigurationScaled, shapeConfiguration); AZ_Assert(shape, "CreateEditorWorldRigidBody: Shape must not be null!"); if (shape) { @@ -73,7 +75,6 @@ namespace PhysX } else { - const Physics::ColliderConfiguration colliderConfigurationUnscaled = collider->GetColliderConfiguration(); auto convexConfig = Utils::CreateConvexFromPrimitive(colliderConfigurationUnscaled, shapeConfiguration, shapeConfigurationProxy.m_subdivisionLevel, shapeConfiguration.m_scale); auto colliderConfigurationNoOffset = colliderConfigurationUnscaled; @@ -272,6 +273,7 @@ namespace PhysX m_debugDisplayDataChangeHandler.Disconnect(); Physics::WorldBodyRequestBus::Handler::BusDisconnect(); + m_nonUniformScaleChangedHandler.Disconnect(); m_sceneStartSimHandler.Disconnect(); Physics::ColliderComponentEventBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(); @@ -377,7 +379,7 @@ namespace PhysX configuration.m_kinematic = m_config.m_kinematic; configuration.m_colliderAndShapeData = Internal::GetCollisionShapes(GetEntity()); - if (auto* sceneInterface = AZ::Interface::Get()) + if (auto* sceneInterface = AZ::Interface::Get()) { m_rigidBodyHandle = sceneInterface->AddSimulatedBody(m_editorSceneHandle, &configuration); m_editorBody = azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_rigidBodyHandle)); diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index a1b8516150..639a079c02 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -666,6 +666,7 @@ namespace PhysX Physics::WorldBodyRequestBus::Handler::BusDisconnect(); m_colliderDebugDraw.Disconnect(); + m_nonUniformScaleChangedHandler.Disconnect(); PhysX::ColliderShapeRequestBus::Handler::BusDisconnect(); LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(); diff --git a/Gems/PhysX/Code/Source/SystemComponent.cpp b/Gems/PhysX/Code/Source/SystemComponent.cpp index 6de54a7593..8210492fe7 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.cpp +++ b/Gems/PhysX/Code/Source/SystemComponent.cpp @@ -15,38 +15,18 @@ #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 -#ifdef PHYSX_EDITOR -#include -#include -#include -#include -#include -#endif - #include #include @@ -233,21 +213,11 @@ namespace PhysX Physics::CollisionRequestBus::Handler::BusConnect(); Physics::CharacterSystemRequestBus::Handler::BusConnect(); -#ifdef PHYSX_EDITOR - PhysX::Editor::RegisterPropertyTypes(); - AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect(); - AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); -#endif - ActivatePhysXSystem(); } void SystemComponent::Deactivate() { -#ifdef PHYSX_EDITOR - AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); - AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect(); -#endif AZ::TickBus::Handler::BusDisconnect(); Physics::CharacterSystemRequestBus::Handler::BusDisconnect(); Physics::CollisionRequestBus::Handler::BusDisconnect(); @@ -272,19 +242,6 @@ namespace PhysX m_assetHandlers.clear(); //this need to be after m_physXSystem->Shutdown(); For it will drop the default material library reference. } -#ifdef PHYSX_EDITOR - - // AztoolsFramework::EditorEvents::Bus::Handler overrides - void SystemComponent::PopulateEditorGlobalContextMenu([[maybe_unused]] QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) - { - } - - void SystemComponent::NotifyRegisterViews() - { - PhysX::Editor::EditorWindow::RegisterViewClass(); - } -#endif - physx::PxConvexMesh* SystemComponent::CreateConvexMesh(const void* vertices, AZ::u32 vertexNum, AZ::u32 vertexStride) { physx::PxConvexMeshDesc desc; @@ -464,54 +421,6 @@ namespace PhysX } } - void SystemComponent::AddColliderComponentToEntity(AZ::Entity* entity, const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& shapeConfiguration, [[maybe_unused]] bool addEditorComponents) - { - [[maybe_unused]] Physics::ShapeType shapeType = shapeConfiguration.GetShapeType(); - -#ifdef PHYSX_EDITOR - if (addEditorComponents) - { - entity->CreateComponent(colliderConfiguration, shapeConfiguration); - } - else -#else - { - if (shapeType == Physics::ShapeType::Sphere) - { - const Physics::SphereShapeConfiguration& sphereConfiguration = static_cast(shapeConfiguration); - auto sphereColliderComponent = entity->CreateComponent(); - sphereColliderComponent->SetShapeConfigurationList({ AZStd::make_pair( - AZStd::make_shared(colliderConfiguration), - AZStd::make_shared(sphereConfiguration)) }); - } - else if (shapeType == Physics::ShapeType::Box) - { - const Physics::BoxShapeConfiguration& boxConfiguration = static_cast(shapeConfiguration); - auto boxColliderComponent = entity->CreateComponent(); - boxColliderComponent->SetShapeConfigurationList({ AZStd::make_pair( - AZStd::make_shared(colliderConfiguration), - AZStd::make_shared(boxConfiguration)) }); - } - else if (shapeType == Physics::ShapeType::Capsule) - { - const Physics::CapsuleShapeConfiguration& capsuleConfiguration = static_cast(shapeConfiguration); - auto capsuleColliderComponent = entity->CreateComponent(); - capsuleColliderComponent->SetShapeConfigurationList({ AZStd::make_pair( - AZStd::make_shared(colliderConfiguration), - AZStd::make_shared(capsuleConfiguration)) }); - } - } - - AZ_Error("PhysX System", !addEditorComponents, "AddColliderComponentToEntity(): Trying to add an Editor collider component in a stand alone build.", - static_cast(shapeType)); - -#endif - { - AZ_Error("PhysX System", shapeType == Physics::ShapeType::Sphere || shapeType == Physics::ShapeType::Box || shapeType == Physics::ShapeType::Capsule, - "AddColliderComponentToEntity(): Using Shape of type %d is not implemented.", static_cast(shapeType)); - } - } - // Physics::CharacterSystemRequestBus AZStd::unique_ptr SystemComponent::CreateCharacter(const Physics::CharacterConfiguration& characterConfig, const Physics::ShapeConfiguration& shapeConfig, AzPhysics::SceneHandle& sceneHandle) diff --git a/Gems/PhysX/Code/Source/SystemComponent.h b/Gems/PhysX/Code/Source/SystemComponent.h index c074a2032d..8c4ac0d836 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.h +++ b/Gems/PhysX/Code/Source/SystemComponent.h @@ -36,9 +36,6 @@ #include #include -#ifdef PHYSX_EDITOR -#include -#endif namespace AzPhysics { struct StaticRigidBodyConfiguration; @@ -61,10 +58,6 @@ namespace PhysX , public Physics::SystemRequestBus::Handler , public PhysX::SystemRequestsBus::Handler , public Physics::CharacterSystemRequestBus::Handler -#ifdef PHYSX_EDITOR - , public AzToolsFramework::EditorEntityContextNotificationBus::Handler - , private AzToolsFramework::EditorEvents::Bus::Handler -#endif , private Physics::CollisionRequestBus::Handler , private AZ::TickBus::Handler { @@ -100,8 +93,6 @@ namespace PhysX bool CookTriangleMeshToMemory(const AZ::Vector3* vertices, AZ::u32 vertexCount, const AZ::u32* indices, AZ::u32 indexCount, AZStd::vector& result) override; - void AddColliderComponentToEntity(AZ::Entity* entity, const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& shapeConfiguration, bool addEditorComponents = false) override; - physx::PxFilterData CreateFilterData(const AzPhysics::CollisionLayer& layer, const AzPhysics::CollisionGroup& group) override; physx::PxCooking* GetCooking() override; @@ -125,13 +116,6 @@ namespace PhysX void Activate() override; void Deactivate() override; -#ifdef PHYSX_EDITOR - - // AztoolsFramework::EditorEvents::Bus::Handler overrides - void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; - void NotifyRegisterViews() override; -#endif - // Physics::SystemRequestBus::Handler AZStd::shared_ptr CreateShape(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& configuration) override; AZStd::shared_ptr CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) override; diff --git a/Gems/PhysX/Code/Tests/CharacterControllerTests.cpp b/Gems/PhysX/Code/Tests/CharacterControllerTests.cpp index d5b961ada7..218571c01c 100644 --- a/Gems/PhysX/Code/Tests/CharacterControllerTests.cpp +++ b/Gems/PhysX/Code/Tests/CharacterControllerTests.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,6 +33,51 @@ namespace PhysX { + namespace Internal + { + void AddColliderComponentToEntity(AZ::Entity* entity, const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& shapeConfiguration) + { + Physics::ShapeType shapeType = shapeConfiguration.GetShapeType(); + + switch (shapeType) + { + case Physics::ShapeType::Sphere: + { + const Physics::SphereShapeConfiguration& sphereConfiguration = static_cast(shapeConfiguration); + auto sphereColliderComponent = entity->CreateComponent(); + sphereColliderComponent->SetShapeConfigurationList({ AZStd::make_pair( + AZStd::make_shared(colliderConfiguration), + AZStd::make_shared(sphereConfiguration)) }); + } + break; + case Physics::ShapeType::Box: + { + const Physics::BoxShapeConfiguration& boxConfiguration = static_cast(shapeConfiguration); + auto boxColliderComponent = entity->CreateComponent(); + boxColliderComponent->SetShapeConfigurationList({ AZStd::make_pair( + AZStd::make_shared(colliderConfiguration), + AZStd::make_shared(boxConfiguration)) }); + } + break; + case Physics::ShapeType::Capsule: + { + const Physics::CapsuleShapeConfiguration& capsuleConfiguration = static_cast(shapeConfiguration); + auto capsuleColliderComponent = entity->CreateComponent(); + capsuleColliderComponent->SetShapeConfigurationList({ AZStd::make_pair( + AZStd::make_shared(colliderConfiguration), + AZStd::make_shared(capsuleConfiguration)) }); + } + break; + default: + { + AZ_Error("PhysX", false, + "AddColliderComponentToEntity(): Using Shape of type %d is not implemented.", static_cast(shapeType)); + } + break; + } + } + } + // transform for a floor centred at x = 0, y = 0, with top at level z = 0 static const AZ::Transform DefaultFloorTransform = AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisZ(-0.5f)); @@ -367,8 +414,7 @@ namespace PhysX auto triggerEntity = AZStd::make_unique("TriggerEntity"); triggerEntity->CreateComponent()->SetWorldTM(AZ::Transform::Identity()); triggerEntity->CreateComponent(PhysX::StaticRigidBodyComponentTypeId); - Physics::SystemRequestBus::Broadcast(&Physics::SystemRequests::AddColliderComponentToEntity, - triggerEntity.get(), triggerConfig, boxConfig, false); + Internal::AddColliderComponentToEntity(triggerEntity.get(), triggerConfig, boxConfig); triggerEntity->Init(); triggerEntity->Activate(); diff --git a/Gems/PhysX/Code/physx_editor_shared_files.cmake b/Gems/PhysX/Code/physx_editor_shared_files.cmake index 2a84c4e380..b743efb6aa 100644 --- a/Gems/PhysX/Code/physx_editor_shared_files.cmake +++ b/Gems/PhysX/Code/physx_editor_shared_files.cmake @@ -11,6 +11,4 @@ set(FILES Source/Module.cpp - Source/SystemComponent.cpp - Source/SystemComponent.h ) diff --git a/Gems/PhysX/Code/physx_editor_tests_files.cmake b/Gems/PhysX/Code/physx_editor_tests_files.cmake index 202e688a76..1e6ccde75f 100644 --- a/Gems/PhysX/Code/physx_editor_tests_files.cmake +++ b/Gems/PhysX/Code/physx_editor_tests_files.cmake @@ -11,8 +11,6 @@ set(FILES Source/Module.cpp - Source/SystemComponent.cpp - Source/SystemComponent.h Tests/PhysXTestCommon.cpp Tests/PhysXTestCommon.h Tests/ColliderScalingTests.cpp diff --git a/Gems/PhysX/Code/physx_files.cmake b/Gems/PhysX/Code/physx_files.cmake index c850479f53..ed2d59b8aa 100644 --- a/Gems/PhysX/Code/physx_files.cmake +++ b/Gems/PhysX/Code/physx_files.cmake @@ -12,6 +12,8 @@ set(FILES Source/PhysX_precompiled.cpp Source/PhysX_precompiled.h + Source/SystemComponent.cpp + Source/SystemComponent.h Include/PhysX/SystemComponentBus.h Include/PhysX/ColliderComponentBus.h Include/PhysX/NativeTypeIdentifiers.h diff --git a/Gems/PhysX/Code/physx_shared_files.cmake b/Gems/PhysX/Code/physx_shared_files.cmake index 9451f44648..1b7c17f5f5 100644 --- a/Gems/PhysX/Code/physx_shared_files.cmake +++ b/Gems/PhysX/Code/physx_shared_files.cmake @@ -11,8 +11,6 @@ set(FILES Source/Module.cpp - Source/SystemComponent.cpp - Source/SystemComponent.h Source/ComponentDescriptors.cpp Source/ComponentDescriptors.h ) diff --git a/Gems/PhysX/Code/physx_tests_files.cmake b/Gems/PhysX/Code/physx_tests_files.cmake index adf0f587d2..406aed64a7 100644 --- a/Gems/PhysX/Code/physx_tests_files.cmake +++ b/Gems/PhysX/Code/physx_tests_files.cmake @@ -10,8 +10,6 @@ # set(FILES - Source/SystemComponent.cpp - Source/SystemComponent.h Source/ComponentDescriptors.cpp Source/ComponentDescriptors.h Tests/PhysXComponentBusTests.cpp diff --git a/Gems/WhiteBox/Code/Include/WhiteBox/WhiteBoxToolApi.h b/Gems/WhiteBox/Code/Include/WhiteBox/WhiteBoxToolApi.h index f47c5c6b0e..8e74c09f52 100644 --- a/Gems/WhiteBox/Code/Include/WhiteBox/WhiteBoxToolApi.h +++ b/Gems/WhiteBox/Code/Include/WhiteBox/WhiteBoxToolApi.h @@ -102,6 +102,9 @@ namespace WhiteBox //! Alias for a collection of faces. using Faces = AZStd::vector; + //! Underlying representation of the White Box mesh (serialized halfedge data). + using WhiteBoxMeshStream = AZStd::vector; + //! Represents the vertex handles to be used to form a new face. struct FaceVertHandles { @@ -726,19 +729,33 @@ namespace WhiteBox /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Serialization + //! The result of attempting to deserialize a white box mesh from a white box mesh stream. + enum class ReadResult + { + Full, //!< The white box mesh stream was full and was read into white box mesh (it is now initialized). + Empty, //!< The white box mesh stream was empty so no white box mesh was loaded. + Error //!< An error occurred while trying to deserialize white box mesh stream. + }; + //! Take an input stream of bytes and create a white box mesh from the deserialized data. - //! @return Will return false if any error was encountered during deserialization, true otherwise. + //! @return Will return ReadResult::Full if the white box mesh stream was filled with data and + //! the white box mesh was initialized, ReadResult::Empty if white box mesh stream did not contain + //! any data (white box mesh will be left empty) or ReadResult::Error if any error was encountered + //! during deserialization. //! @note A white box mesh must have been created first. - bool ReadMesh(WhiteBoxMesh& whiteBox, const AZStd::vector& input); + ReadResult ReadMesh(WhiteBoxMesh& whiteBox, const WhiteBoxMeshStream& input); //! Take an input stream and create a white box mesh from the deserialized data. - //! @return Will return false if any error was encountered during deserialization, true otherwise. + //! @return Will return ReadResult::Full if the white box mesh stream was filled with data and + //! the white box mesh was initialized, ReadResult::Empty if white box mesh stream did not contain + //! any data (white box mesh will be left empty) or ReadResult::Error if any error was encountered + //! during deserialization. //! @note The input stream must not skip white space characters (std::noskipws must be set on the stream). - bool ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input); + ReadResult ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input); //! Take a white box mesh and write it out to a stream of bytes. //! @return Will return false if any error was encountered during serialization, true otherwise. - bool WriteMesh(const WhiteBoxMesh& whiteBox, AZStd::vector& output); + bool WriteMesh(const WhiteBoxMesh& whiteBox, WhiteBoxMeshStream& output); //! Clones the white box mesh object into a new mesh. //! @return Will return null if any error was encountered during serialization, otherwise the cloned mesh. diff --git a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp index 69d7c03fce..cd308aef0f 100644 --- a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp +++ b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp @@ -86,7 +86,7 @@ namespace WhiteBox { success = assetHandler->SaveAssetData(meshAsset, &fileStream); AZ_Printf( - "EditorWhiteBoxComponent", "Save %s. Location: %s", success ? "succeeded" : "failed", + "EditorWhiteBoxMeshAsset", "Save %s. Location: %s", success ? "succeeded" : "failed", absoluteFilePath.c_str()); } } @@ -229,7 +229,15 @@ namespace WhiteBox { if (asset == m_meshAsset) { - AZ_Warning("EditorWhiteBoxComponent", false, "OnAssetError: %s", asset.GetHint().c_str()); + AZ_Warning("EditorWhiteBoxMeshAsset", false, "OnAssetError: %s", asset.GetHint().c_str()); + } + } + + void EditorWhiteBoxMeshAsset::OnAssetReloadError(AZ::Data::Asset asset) + { + if (asset == m_meshAsset) + { + AZ_Warning("EditorWhiteBoxMeshAsset", false, "OnAssetReloadError: %s", asset.GetHint().c_str()); } } diff --git a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.h b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.h index 2001808668..caa56dce1a 100644 --- a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.h +++ b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.h @@ -82,6 +82,7 @@ namespace WhiteBox void OnAssetReady(AZ::Data::Asset asset) override; void OnAssetReloaded(AZ::Data::Asset asset) override; void OnAssetError(AZ::Data::Asset asset) override; + void OnAssetReloadError(AZ::Data::Asset asset) override; // WhiteBoxMeshAssetNotificationBus ... void OnWhiteBoxMeshAssetModified(AZ::Data::Asset asset) override; diff --git a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAsset.h b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAsset.h index f5915d033b..d25af2fed2 100644 --- a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAsset.h +++ b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAsset.h @@ -51,12 +51,12 @@ namespace WhiteBox return AZStd::move(m_mesh); } - void SetWhiteBoxData(AZStd::vector whiteBoxData) + void SetWhiteBoxData(Api::WhiteBoxMeshStream whiteBoxData) { m_whiteBoxData = AZStd::move(whiteBoxData); } - const AZStd::vector& GetWhiteBoxData() const + const Api::WhiteBoxMeshStream& GetWhiteBoxData() const { return m_whiteBoxData; } @@ -73,7 +73,7 @@ namespace WhiteBox } Api::WhiteBoxMeshPtr m_mesh; - AZStd::vector m_whiteBoxData; //! Data used for creating undo commands. + Api::WhiteBoxMeshStream m_whiteBoxData; //! Data used for creating undo commands. }; } // namespace Pipeline } // namespace WhiteBox diff --git a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetHandler.cpp b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetHandler.cpp index a64c552bb6..3ed22a3d1b 100644 --- a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetHandler.cpp +++ b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetHandler.cpp @@ -112,16 +112,21 @@ namespace WhiteBox const auto size = stream->GetLength(); - AZStd::vector whiteBoxData(size); + Api::WhiteBoxMeshStream whiteBoxData; + whiteBoxData.resize(size); + stream->Read(size, whiteBoxData.data()); auto whiteBoxMesh = WhiteBox::Api::CreateWhiteBoxMesh(); - const bool success = WhiteBox::Api::ReadMesh(*whiteBoxMesh, whiteBoxData); + const auto result = WhiteBox::Api::ReadMesh(*whiteBoxMesh, whiteBoxData); + // if result is not 'Full', then whiteBoxMeshAsset could be empty which is most likely an error + // as no data was loaded from the asset, or it was not correctly read in stream->Read(..) + const auto success = result == Api::ReadResult::Full; if (success) { whiteBoxMeshAsset->SetMesh(AZStd::move(whiteBoxMesh)); - whiteBoxMeshAsset->SetWhiteBoxData(whiteBoxData); + whiteBoxMeshAsset->SetWhiteBoxData(AZStd::move(whiteBoxData)); } return success ? AZ::Data::AssetHandler::LoadResult::LoadComplete diff --git a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.cpp b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.cpp index ef11b8d47d..19cbc802dd 100644 --- a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.cpp +++ b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.cpp @@ -29,12 +29,12 @@ namespace WhiteBox m_asset = asset; } - void WhiteBoxMeshAssetUndoCommand::SetUndoState(const AZStd::vector& undoState) + void WhiteBoxMeshAssetUndoCommand::SetUndoState(const Api::WhiteBoxMeshStream& undoState) { m_undoState = undoState; } - void WhiteBoxMeshAssetUndoCommand::SetRedoState(const AZStd::vector& redoState) + void WhiteBoxMeshAssetUndoCommand::SetRedoState(const Api::WhiteBoxMeshStream& redoState) { m_redoState = redoState; } diff --git a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.h b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.h index eb657c5a85..3f6258c1b4 100644 --- a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.h +++ b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.h @@ -34,8 +34,8 @@ namespace WhiteBox ~WhiteBoxMeshAssetUndoCommand() override = default; void SetAsset(AZ::Data::Asset asset); - void SetUndoState(const AZStd::vector& undoState); - void SetRedoState(const AZStd::vector& redoState); + void SetUndoState(const Api::WhiteBoxMeshStream& undoState); + void SetRedoState(const Api::WhiteBoxMeshStream& redoState); // AzToolsFramework::UndoSystem::URSequencePoint ... void Undo() override; @@ -44,7 +44,7 @@ namespace WhiteBox protected: AZ::Data::Asset m_asset; - AZStd::vector m_undoState; - AZStd::vector m_redoState; + Api::WhiteBoxMeshStream m_undoState; + Api::WhiteBoxMeshStream m_redoState; }; } // namespace WhiteBox diff --git a/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp b/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp index 49e713810e..5803f066ae 100644 --- a/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp +++ b/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp @@ -3379,7 +3379,7 @@ namespace WhiteBox CalculatePlanarUVs(whiteBox); } - bool WriteMesh(const WhiteBoxMesh& whiteBox, AZStd::vector& output) + bool WriteMesh(const WhiteBoxMesh& whiteBox, WhiteBoxMeshStream& output) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -3403,10 +3403,15 @@ namespace WhiteBox return false; } - bool ReadMesh(WhiteBoxMesh& whiteBox, const AZStd::vector& input) + ReadResult ReadMesh(WhiteBoxMesh& whiteBox, const WhiteBoxMeshStream& input) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); + if (input.empty()) + { + return ReadResult::Empty; + } + std::string inputStr; inputStr.reserve(input.size()); AZStd::copy(input.cbegin(), input.cend(), AZStd::back_inserter(inputStr)); @@ -3418,33 +3423,33 @@ namespace WhiteBox return ReadMesh(whiteBox, whiteBoxStream); } - bool ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input) + ReadResult ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input) { const auto skipws = input.flags() & std::ios_base::skipws; AZ_Assert(skipws == 0, "Input stream must not skip white space characters"); if (skipws != 0) { - return false; + return ReadResult::Error; } AZStd::lock_guard lg(g_omSerializationLock); OpenMesh::IO::Options options{OpenMesh::IO::Options::FaceTexCoord | OpenMesh::IO::Options::FaceNormal}; - return OpenMesh::IO::read_mesh(whiteBox.mesh, input, ".om", options); + return OpenMesh::IO::read_mesh(whiteBox.mesh, input, ".om", options) ? ReadResult::Full : ReadResult::Error; } WhiteBoxMeshPtr CloneMesh(const WhiteBoxMesh& whiteBox) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - AZStd::vector clonedData; + WhiteBoxMeshStream clonedData; if (!WriteMesh(whiteBox, clonedData)) { return nullptr; } WhiteBoxMeshPtr newMesh = CreateWhiteBoxMesh(); - if (!ReadMesh(*newMesh, clonedData)) + if (ReadMesh(*newMesh, clonedData) != ReadResult::Full) { return nullptr; } @@ -3461,7 +3466,7 @@ namespace WhiteBox bool SaveToWbm(const WhiteBoxMesh& whiteBox, AZ::IO::GenericStream& stream) { - AZStd::vector buffer; + WhiteBoxMeshStream buffer; const bool success = WhiteBox::Api::WriteMesh(whiteBox, buffer); const auto bytesWritten = stream.Write(buffer.size(), buffer.data()); diff --git a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp index 61012b8577..e88b4ce686 100644 --- a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp +++ b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp @@ -348,14 +348,14 @@ namespace WhiteBox else { // attempt to load the mesh - if (Api::ReadMesh(*m_whiteBox, m_whiteBoxData)) + const auto result = Api::ReadMesh(*m_whiteBox, m_whiteBoxData); + AZ_Error("EditorWhiteBoxComponent", result != WhiteBox::Api::ReadResult::Error, "Error deserializing white box mesh stream"); + + // if the read was successful but the byte stream is empty + // (there was nothing to load), create a default mesh + if (result == Api::ReadResult::Empty) { - // if the read was successful but the byte stream is empty - // (there was nothing to load), create a default mesh - if (m_whiteBoxData.empty()) - { - Api::InitializeAsUnitCube(*m_whiteBox); - } + Api::InitializeAsUnitCube(*m_whiteBox); } } } diff --git a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.h b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.h index 5d7ac3b303..02b50a9407 100644 --- a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.h +++ b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.h @@ -124,7 +124,7 @@ namespace WhiteBox AZStd::optional> m_renderMesh; //!< The render mesh to use for the White Box mesh data. AZ::Transform m_worldFromLocal = AZ::Transform::CreateIdentity(); //!< Cached world transform of Entity. - AZStd::vector m_whiteBoxData; //!< Serialized White Box mesh data. + Api::WhiteBoxMeshStream m_whiteBoxData; //!< Serialized White Box mesh data. //! Holds a reference to an optional WhiteBoxMeshAsset and manages the lifecycle of adding/removing an asset. EditorWhiteBoxMeshAsset* m_editorMeshAsset = nullptr; AZStd::optional m_worldAabb; //!< Cached world aabb (used for selection/view determination). diff --git a/Gems/WhiteBox/Code/Source/Rendering/Atom/WhiteBoxBuffer.h b/Gems/WhiteBox/Code/Source/Rendering/Atom/WhiteBoxBuffer.h index 0a6cfafee7..bea6de5898 100644 --- a/Gems/WhiteBox/Code/Source/Rendering/Atom/WhiteBoxBuffer.h +++ b/Gems/WhiteBox/Code/Source/Rendering/Atom/WhiteBoxBuffer.h @@ -98,7 +98,7 @@ namespace WhiteBox // specify the data format for vertex stream data AZ::RHI::BufferDescriptor bufferDescriptor; - bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly; + bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly | AZ::RHI::BufferBindFlags::ShaderRead; bufferDescriptor.m_byteCount = bufferSize; bufferDescriptor.m_alignment = elementSize; diff --git a/Gems/WhiteBox/Code/Tests/WhiteBoxTest.cpp b/Gems/WhiteBox/Code/Tests/WhiteBoxTest.cpp index 1d55539720..1135a915ec 100644 --- a/Gems/WhiteBox/Code/Tests/WhiteBoxTest.cpp +++ b/Gems/WhiteBox/Code/Tests/WhiteBoxTest.cpp @@ -470,6 +470,7 @@ namespace UnitTest TEST_F(WhiteBoxTestFixture, MeshNotDeserializedWithSkipWhiteSpaceStream) { namespace Api = WhiteBox::Api; + using testing::Eq; Api::InitializeAsUnitCube(*m_whiteBox); AZStd::vector serializedWhiteBox; @@ -485,7 +486,7 @@ namespace UnitTest // note: std::stringstream will default to skip white space characters AZ_TEST_START_TRACE_SUPPRESSION; - EXPECT_FALSE(Api::ReadMesh(*m_whiteBox, whiteBoxStream)); + EXPECT_THAT(Api::ReadMesh(*m_whiteBox, whiteBoxStream), Eq(Api::ReadResult::Error)); AZ_TEST_STOP_TRACE_SUPPRESSION(1); } diff --git a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_runtime_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Mac/mac_runtime_dependencies.cmake index e49929c6e1..2821493346 100644 --- a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_runtime_dependencies.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/Mac/mac_runtime_dependencies.cmake @@ -11,4 +11,5 @@ set(GEM_DEPENDENCIES Gem::Atom_RHI_Metal.Private + Gem::Atom_RHI_Null.Private ) diff --git a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_tool_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Mac/mac_tool_dependencies.cmake index eea4bb1dce..adf5485ed4 100644 --- a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_tool_dependencies.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/Mac/mac_tool_dependencies.cmake @@ -14,4 +14,5 @@ set(GEM_DEPENDENCIES Gem::Atom_RHI_Metal.Builders Gem::Atom_RHI_Vulkan.Builders Gem::Atom_RHI_DX12.Builders + Gem::Atom_RHI_Null.Builders ) diff --git a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_runtime_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Windows/windows_runtime_dependencies.cmake index c5e1b4bc2e..514a61aa57 100644 --- a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_runtime_dependencies.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/Windows/windows_runtime_dependencies.cmake @@ -12,4 +12,5 @@ set(GEM_DEPENDENCIES Gem::Atom_RHI_Vulkan.Private Gem::Atom_RHI_DX12.Private + Gem::Atom_RHI_Null.Private ) diff --git a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_tool_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Windows/windows_tool_dependencies.cmake index 304f97d590..b7f4b82126 100644 --- a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_tool_dependencies.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/Windows/windows_tool_dependencies.cmake @@ -14,4 +14,6 @@ set(GEM_DEPENDENCIES Gem::Atom_RHI_Vulkan.Builders Gem::Atom_RHI_DX12.Private Gem::Atom_RHI_DX12.Builders + Gem::Atom_RHI_Null.Private + Gem::Atom_RHI_Null.Builders ) diff --git a/bootstrap.cfg b/bootstrap.cfg index cbe6ed025e..aa7af07645 100644 --- a/bootstrap.cfg +++ b/bootstrap.cfg @@ -1,84 +1,12 @@ --- When you see an option that does not have a platform preceeding it, that is the default --- value for anything not specificly set per platform. So if remote_filesystem=0 and you have --- ios_remote_file_system=1 then remote filesystem will be off for all platforms except ios --- Any of the settings in this file can be prefixed with a platform name: --- android, ios, mac, linux, windows, etc... --- or left unprefixed, to set all platforms not specified. The rules apply in the order they're declared +; This file is deprecated and is only use currently for setting the path when running O3DE in an engine-centric manner +; By engine-centric, what is meant is using CMake to configure from the directory and passing in the LY_PROJECTS value project_path=AutomatedTesting --- remote_filesystem - enable Virtual File System (VFS) --- This feature allows a remote instance of the game to run off assets --- on the asset processor computers cache instead of deploying them the remote device --- By default it is off and can be overridden for any platform -remote_filesystem=0 -provo_remote_filesystem=0 -android_remote_filesystem=0 -ios_remote_filesystem=0 -mac_remote_filesystem=0 - --- What type of assets are we going to load? --- We need to know this before we establish VFS because different platform assets --- are stored in different root folders in the cache. These correspond to the names --- In the asset processor config file. This value also controls what config file is read --- when you read system_xxxx_xxxx.cfg (for example, system_windows_pc.cfg or system_android_es3.cfg) --- by default, pc assets (in the 'pc' folder) are used, with RC being fed 'pc' as the platform --- by default on console we use the default assets=pc for better iteration times --- we should turn on console specific assets only when in release and/or testing assets and/or loading performance --- that way most people will not need to have 3 different caches taking up disk space -assets = pc --- provo_assets = provo --- salem_assets = salem --- jasper_assets = jasper -android_assets = es3 -ios_assets = ios -mac_assets = osx_gl - --- Add the IP address of your console to the allowed list that will connect to the asset processor here --- You can list addresses or CIDR's. CIDR's are helpful if you are using DHCP. A CIDR looks like an ip address with --- a /n on the end means how many bits are significant. 8bits.8bits.8bits.8bits = /32 --- Example: 192.168.1.3 --- Example: 192.168.1.3, 192.168.1.15 --- Example: 192.168.1.0/24 will allow any address starting with 192.168.1. --- Example: 192.168.0.0/16 will allow any address starting with 192.168. --- Example: 192.168.0.0/8 will allow any address starting with 192. --- allowed_list = - --- IP address and optionally port of the asset processor. --- Set your PC IP here: (and uncomment the next line) --- If you are running your asset processor on a windows machine you --- can find out your ip address by opening a cmd prompt and typing in ipconfig --- remote_ip = 127.0.0.1 --- remote_port = 45643 - --- Which way do you want to connect the asset processor to the game: 1=game connects to AP "connect", 0=AP connects to game "listen" --- Note: android and IOS over USB port forwarding may need to listen instead of connect -connect_to_remote=0 -windows_connect_to_remote=1 -provo_connect_to_remote=1 -salem_connect_to_remote=0 -jasper_connect_to_remote=0 -android_connect_to_remote=0 -ios_connect_to_remote=0 -mac_connect_to_remote=0 - --- Should we tell the game to wait and not proceed unless we have a connection to the AP or --- do we allow it to continue to try to connect in the background without waiting --- Note: Certain options REQUIRE that we do not proceed unless we have a connection, and will override this option to 1 when set --- Since remote_filesystem=1 requires a connection to proceed it will override our option to 1 -wait_for_connect=0 -provo_wait_for_connect=0 -salem_wait_for_connect=0 -jasper_wait_for_connect=0 -windows_wait_for_connect=1 -android_wait_for_connect=0 -ios_wait_for_connect=0 -mac_wait_for_connect=0 - --- How long applications should wait while attempting to connect to an already launched AP(in seconds) --- connect_ap_timeout=3 - --- How long application should wait when launching the AP and wait for the AP to connect back to it(in seconds) --- This time is dependent on Machine load as well as how long it takes for the new AP instance to initialize --- A debug AP takes longer to start up than a profile AP --- launch_ap_timeout=15 +; The Asset Processor Specific settings are now the /Engine/Registry/bootstrap.setreg settings +; The Engine specific settings can be overridden in order of least precedence to most +; 1. Override the settings in a "/Registry/*.setreg(patch)" file (Shared per Gem Settings) +; 2. Override the settings in a "/Registry/*.setreg(patch)" file (Shared per Project Settings) +; 3. Override the settings in a "/Registry/*.setreg(patch)" file (User per Project Settings) +; 4. Override the settings in a "~/.o3de/Registry/*.setreg(patch)" file (User Global Settings) +; Where "~" is %USERPROFILE% on Windows and $HOME on Unix like platforms diff --git a/cmake/3rdParty.cmake b/cmake/3rdParty.cmake index 4cd3ab2917..8d0c030302 100644 --- a/cmake/3rdParty.cmake +++ b/cmake/3rdParty.cmake @@ -116,10 +116,6 @@ function(ly_add_external_target) # Setting BASE_PATH variable in the parent scope to allow for the Find<3rdParty>.cmake scripts to use them set(BASE_PATH ${BASE_PATH} PARENT_SCOPE) - if(NOT EXISTS ${BASE_PATH}) - message(FATAL_ERROR "Cannot find 3rdParty library ${ly_add_external_target_NAME} on path ${BASE_PATH}") - endif() - add_library(3rdParty::${NAME_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) if(ly_add_external_target_INCLUDE_DIRECTORIES) diff --git a/cmake/3rdParty/FindOpenGLInterface.cmake b/cmake/3rdParty/FindOpenGLInterface.cmake new file mode 100644 index 0000000000..1e4992290f --- /dev/null +++ b/cmake/3rdParty/FindOpenGLInterface.cmake @@ -0,0 +1,19 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +find_package(OpenGL) + +ly_add_external_target( + NAME OpenGLInterface + VERSION "" + BUILD_DEPENDENCIES + OpenGL::GL +) \ No newline at end of file diff --git a/cmake/3rdParty/Platform/Mac/OpenGLInterface_mac.cmake b/cmake/3rdParty/Platform/Mac/OpenGLInterface_mac.cmake new file mode 100644 index 0000000000..7d0d47740f --- /dev/null +++ b/cmake/3rdParty/Platform/Mac/OpenGLInterface_mac.cmake @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +# MacOS 10.14 deprecates OpenGL. This silences the warnings for now. +set(OPENGLINTERFACE_COMPILE_DEFINITIONS GL_SILENCE_DEPRECATION) \ No newline at end of file diff --git a/cmake/3rdParty/Platform/Mac/cmake_mac_files.cmake b/cmake/3rdParty/Platform/Mac/cmake_mac_files.cmake index f786f80cf7..0e3cc53262 100644 --- a/cmake/3rdParty/Platform/Mac/cmake_mac_files.cmake +++ b/cmake/3rdParty/Platform/Mac/cmake_mac_files.cmake @@ -15,6 +15,7 @@ set(FILES Clang_mac.cmake DirectXShaderCompiler_mac.cmake FbxSdk_mac.cmake + OpenGLInterface_mac.cmake OpenSSL_mac.cmake Wwise_mac.cmake ) diff --git a/cmake/3rdParty/cmake_files.cmake b/cmake/3rdParty/cmake_files.cmake index 3fe15bc0ce..46b612df42 100644 --- a/cmake/3rdParty/cmake_files.cmake +++ b/cmake/3rdParty/cmake_files.cmake @@ -18,6 +18,7 @@ set(FILES Finddyad.cmake FindFbxSdk.cmake Findlibav.cmake + FindOpenGLInterface.cmake FindOpenSSL.cmake FindRadTelemetry.cmake FindVkValidation.cmake diff --git a/cmake/FindTarget.cmake.in b/cmake/FindTarget.cmake.in new file mode 100644 index 0000000000..7d0129d05a --- /dev/null +++ b/cmake/FindTarget.cmake.in @@ -0,0 +1,45 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +# Generated by O3DE + +include(FindPackageHandleStandardArgs) + +ly_add_target( + +NAME @NAME_PLACEHOLDER@ UNKNOWN IMPORTED + +@NAMESPACE_PLACEHOLDER@ + +@INCLUDE_DIRECTORIES_PLACEHOLDER@ + +@BUILD_DEPENDENCIES_PLACEHOLDER@ + +@RUNTIME_DEPENDENCIES_PLACEHOLDER@ + +@COMPILE_DEFINITIONS_PLACEHOLDER@ +) + +# The below if was generated from if (NOT HEADER_ONLY_PLACEHOLDER) +# HEADER_ONLY_PLACEHOLDER evaluates to TRUE or FALSE +if (NOT @HEADER_ONLY_PLACEHOLDER@) + # Load information for each installed configuration. + foreach(config @ALL_CONFIGS@) + set(@NAME_PLACEHOLDER@_${config}_FOUND FALSE) + include("${LY_ROOT_FOLDER}/cmake_autogen/@NAME_PLACEHOLDER@/@NAME_PLACEHOLDER@_${config}.cmake") + endforeach() + + find_package_handle_standard_args(@NAME_PLACEHOLDER@ + "Could not find package @NAME_PLACEHOLDER@" + @TARGET_CONFIG_FOUND_VARS_PLACEHOLDER@) +else() + set(@NAME_PLACEHOLDER@_FOUND TRUE) +endif() \ No newline at end of file diff --git a/cmake/Findo3de.cmake b/cmake/Findo3de.cmake index 30246ebd4b..0b4d0b75e7 100644 --- a/cmake/Findo3de.cmake +++ b/cmake/Findo3de.cmake @@ -27,24 +27,19 @@ if(json_error) message(FATAL_ERROR "Unable to read key 'engine_name' from '${current_path}/../engine.json', error: ${json_error}") endif() -if(NOT this_engine_name STREQUAL LY_ENGINE_NAME_TO_USE) - set(o3de_FOUND FALSE) - set(o3de_NOT_FOUND_MESSAGE) - find_package_handle_standard_args(o3de - "Could not find an engine with matching ${LY_ENGINE_NAME_TO_USE}" - o3de_FOUND - ) - return() +set(found_matching_engine FALSE) +if(this_engine_name STREQUAL LY_ENGINE_NAME_TO_USE) + set(found_matching_engine TRUE) endif() +find_package_handle_standard_args(o3de + "Could not find an engine with matching ${LY_ENGINE_NAME_TO_USE}" + found_matching_engine +) + macro(o3de_initialize) + set(INSTALLED_ENGINE FALSE) set(LY_PROJECTS ${CMAKE_CURRENT_LIST_DIR}) o3de_current_file_path(current_path) add_subdirectory(${current_path}/.. o3de) -endmacro() - -message(STATUS "Found ${this_engine_name} in ${current_path}") -set(o3de_FOUND FALSE) -find_package_handle_standard_args(o3de - o3de_FOUND -) \ No newline at end of file +endmacro() \ No newline at end of file diff --git a/cmake/Findo3de.cmake.in b/cmake/Findo3de.cmake.in new file mode 100644 index 0000000000..7ecb73e874 --- /dev/null +++ b/cmake/Findo3de.cmake.in @@ -0,0 +1,36 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +# Generated by O3DE + +include(FindPackageHandleStandardArgs) + +# This will be called from within the installed engine's CMakeLists.txt +macro(ly_find_o3de_packages) + @FIND_PACKAGES_PLACEHOLDER@ + find_package(LauncherGenerator) +endmacro() + + +function(o3de_current_file_path path) + set(${path} ${CMAKE_CURRENT_FUNCTION_LIST_DIR} PARENT_SCOPE) +endfunction() + + +# We are using the engine's CMakeLists.txt to handle initialization/importing targets +# Since this is external to the project's source, we need to specify an output directory +# even though we don't build +macro(o3de_initialize) + set(INSTALLED_ENGINE TRUE) + set(LY_PROJECTS ${CMAKE_SOURCE_DIR}) + o3de_current_file_path(current_path) + add_subdirectory(${current_path}/.. o3de) +endmacro() \ No newline at end of file diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 9164105f3a..7ac9a3afa8 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -13,13 +13,6 @@ #! ly_install_target: registers the target to be installed by cmake install. # # \arg:NAME name of the target -# \arg:NAMESPACE namespace declaration for this target. It will be used for IDE and dependencies -# \arg:INCLUDE_DIRECTORIES paths to the include directories -# \arg:BUILD_DEPENDENCIES list of interfaces this target depends on (could be a compilation dependency -# if the dependency is only exposing an include path, or could be a linking -# dependency is exposing a lib) -# \arg:RUNTIME_DEPENDENCIES list of dependencies this target depends on at runtime -# \arg:COMPILE_DEFINITIONS list of compilation definitions this target will use to compile function(ly_install_target ly_install_target_NAME) # All include directories marked PUBLIC or INTERFACE will be installed @@ -143,7 +136,7 @@ function(ly_generate_target_find_file) set(HEADER_ONLY_PLACEHOLDER TRUE) endif() - configure_file(${LY_ROOT_FOLDER}/cmake/FindTargetTemplate.cmake ${CMAKE_CURRENT_BINARY_DIR}/Find${ly_generate_target_find_file_NAME}.cmake @ONLY) + configure_file(${LY_ROOT_FOLDER}/cmake/FindTarget.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/Find${ly_generate_target_find_file_NAME}.cmake @ONLY) endfunction() @@ -214,7 +207,7 @@ function(ly_setup_o3de_install) string(REPLACE ";" "\n" FIND_PACKAGES_PLACEHOLDER "${find_package_list}") - configure_file(${LY_ROOT_FOLDER}/cmake/Findo3deTemplate.cmake ${CMAKE_CURRENT_BINARY_DIR}/Findo3de.cmake @ONLY) + configure_file(${LY_ROOT_FOLDER}/cmake/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/Findo3de.cmake @ONLY) ly_install_launcher_target_generator() diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index 1f53c37c49..563408f942 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -628,8 +628,8 @@ def get_test_module_registry(build_dir_path): test_module_items = unit_test_json['Amazon'] for _, test_module_item in test_module_items.items(): - module_file = test_module_item['Modules'] - dep_modules.append(module_file) + module_files = test_module_item['Modules'] + dep_modules.extend(module_files) except FileNotFoundError: raise LmbrCmdError(f"Unit test registry not found ('{str(unit_test_module_path)}')") @@ -659,7 +659,10 @@ def get_validated_test_modules(test_modules, build_dir_path): for test_target_check in test_modules: if test_target_check not in all_test_modules: raise LmbrCmdError(f"Invalid test module {test_target_check}") - validated_test_modules.append(test_target_check) + if isinstance(test_target_check, list): + validated_test_modules.extend(test_target_check) + else: + validated_test_modules.append(test_target_check) else: validated_test_modules = all_test_modules diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 3d9ddd0411..297c406b7b 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -26,8 +26,7 @@ def pipelineParameters = [ booleanParam(defaultValue: false, description: 'Deletes the contents of the output directory before building. This will cause a \"clean\" build. NOTE: does not imply CLEAN_ASSETS', name: 'CLEAN_OUTPUT_DIRECTORY'), booleanParam(defaultValue: false, description: 'Deletes the contents of the output directories of the AssetProcessor before building.', name: 'CLEAN_ASSETS'), booleanParam(defaultValue: false, description: 'Deletes the contents of the workspace and forces a complete pull.', name: 'CLEAN_WORKSPACE'), - booleanParam(defaultValue: false, description: 'Recreates the volume used for the workspace. The volume will be created out of a snapshot taken from main.', name: 'RECREATE_VOLUME'), - string(defaultValue: '', description: 'Filters and overrides the list of jobs to run for each of the below platforms (comma-separated). Can\'t be used during a pull request.', name: 'JOB_LIST_OVERRIDE') + booleanParam(defaultValue: false, description: 'Recreates the volume used for the workspace. The volume will be created out of a snapshot taken from main.', name: 'RECREATE_VOLUME') ] def palSh(cmd, lbl = '', winSlashReplacement = true) { @@ -76,18 +75,22 @@ def palRmDir(path) { } } -def IsJobEnabled(buildTypeMap, pipelineName, platformName) { - def job_list_override = params.JOB_LIST_OVERRIDE.tokenize(',') +def IsPullRequest(branchName) { + // temporarily using the name to detect if we are in a PR + // In the future we will check with github + return branchName.startsWith('PR-') +} + +def IsJobEnabled(branchName, buildTypeMap, pipelineName, platformName) { + if (IsPullRequest(branchName)) { + return buildTypeMap.value.TAGS && buildTypeMap.value.TAGS.contains(pipelineName) + } + def job_list_override = params.JOB_LIST_OVERRIDE ? params.JOB_LIST_OVERRIDE.tokenize(',') : '' if (!job_list_override.isEmpty()) { return params[platformName] && job_list_override.contains(buildTypeMap.key); } else { - if (params[platformName]) { - if(buildTypeMap.value.TAGS) { - return buildTypeMap.value.TAGS.contains(pipelineName) - } - } + return params[platformName] && buildTypeMap.value.TAGS && buildTypeMap.value.TAGS.contains(pipelineName) } - return false } def GetRunningPipelineName(JENKINS_JOB_NAME) { @@ -448,8 +451,12 @@ try { pipelineConfig = LoadPipelineConfig(pipelineName, branchName) // Add each platform as a parameter that the user can disable if needed - pipelineConfig.platforms.each { platform -> - pipelineParameters.add(booleanParam(defaultValue: true, description: '', name: platform.key)) + if (!IsPullRequest(branchName)) { + pipelineParameters.add(stringParam(defaultValue: '', description: 'Filters and overrides the list of jobs to run for each of the below platforms (comma-separated). Can\'t be used during a pull request.', name: 'JOB_LIST_OVERRIDE')) + + pipelineConfig.platforms.each { platform -> + pipelineParameters.add(booleanParam(defaultValue: true, description: '', name: platform.key)) + } } pipelineProperties.add(parameters(pipelineParameters)) properties(pipelineProperties) @@ -462,23 +469,26 @@ try { } } - if(env.BUILD_NUMBER == '1' && !branchName.startsWith('PR-')) { + if(env.BUILD_NUMBER == '1' && !IsPullRequest(branchName)) { // Exit pipeline early on the intial build. This allows Jenkins to load the pipeline for the branch and enables users // to select build parameters on their first actual build. See https://issues.jenkins.io/browse/JENKINS-41929 currentBuild.result = 'SUCCESS' return } + def someBuildHappened = false + // Build and Post-Build Testing Stage def buildConfigs = [:] // Platform Builds run on EC2 pipelineConfig.platforms.each { platform -> platform.value.build_types.each { build_job -> - if (IsJobEnabled(build_job, pipelineName, platform.key)) { // User can filter jobs, jobs are tagged by pipeline + if (IsJobEnabled(branchName, build_job, pipelineName, platform.key)) { // User can filter jobs, jobs are tagged by pipeline def envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, build_job.value.PIPELINE_ENV ?: EMPTY_JSON, pipelineName) envVars['JOB_NAME'] = "${branchName}_${platform.key}_${build_job.key}" // backwards compatibility, some scripts rely on this def nodeLabel = envVars['NODE_LABEL'] + someBuildHappened = true buildConfigs["${platform.key} [${build_job.key}]"] = { node("${nodeLabel}") { @@ -538,6 +548,9 @@ try { echo 'All builds successful' } + if (!someBuildHappened) { + currentBuild.result = 'NOT_BUILT' + } } catch(Exception e) { error "Exception: ${e}" diff --git a/scripts/build/Platform/Android/run_test_on_android_simulator.py b/scripts/build/Platform/Android/run_test_on_android_simulator.py index e81db00537..1fc2e19636 100644 --- a/scripts/build/Platform/Android/run_test_on_android_simulator.py +++ b/scripts/build/Platform/Android/run_test_on_android_simulator.py @@ -20,7 +20,8 @@ import logging CURRENT_PATH = pathlib.Path(os.path.dirname(__file__)).absolute() -ENGINE_ROOT = CURRENT_PATH.parent.parent.parent.parent.parent.parent +# The engine root is based on the location of this file (/scripts/build/Platform/Android). Walk up to calculate the engine root +ENGINE_ROOT = CURRENT_PATH.parents[3] class AndroidEmuError(Exception): @@ -194,6 +195,14 @@ class AndroidEmulatorManager(object): return installed_packages, available_packages, available_updates + def update_installed_sdks(self): + """ + Run an SDK Manager update to make sure the SDKs are all up-to-date + """ + logging.info(f"Updating android SDK...") + self.sdk_manager_cmd.run(['--update']) + + def install_system_package_if_necessary(self): """ Make sure that we have the correct system image installed, and install if not @@ -503,6 +512,9 @@ def process_unit_test_on_simulator(base_android_sdk_path, build_path, build_conf manager = AndroidEmulatorManager(base_android_sdk_path=base_android_sdk_path, force_avd_creation=True) + # Make sure that the android SDK is up to date + manager.update_installed_sdks() + # First Install or overwrite the unit test emulator manager.install_unit_test_avd() diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index d7f5ddc9d0..426bf1d7ce 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -7,14 +7,14 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting" } }, - "profile_pipe": { + "profile_nounity_pipe": { "TAGS": [ "default" ], "steps": [ "profile_nounity", - "asset_profile", - "test_profile" + "asset_profile_nounity", + "test_profile_nounity" ] }, "metrics": { @@ -84,6 +84,18 @@ "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest" } }, + "test_profile_nounity": { + "TAGS": [], + "COMMAND": "build_test_linux.sh", + "PARAMETERS": { + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build/linux", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_LY_PROJECTS": "AutomatedTesting", + "CMAKE_TARGET": "all", + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest" + } + }, "asset_profile": { "TAGS": [ "weekly-build-metrics" @@ -100,6 +112,20 @@ "ASSET_PROCESSOR_PLATFORMS": "pc,server" } }, + "asset_profile_nounity": { + "TAGS": [], + "COMMAND": "build_asset_linux.sh", + "PARAMETERS": { + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build/linux", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_LY_PROJECTS": "AutomatedTesting", + "CMAKE_TARGET": "AssetProcessorBatch", + "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", + "ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode", + "ASSET_PROCESSOR_PLATFORMS": "pc,server" + } + }, "asset_clean_profile": { "TAGS": [ "nightly" diff --git a/scripts/commit_validation/commit_validation/pal_allowedlist.txt b/scripts/commit_validation/commit_validation/pal_allowedlist.txt index 3b1dfdb3b9..278262d59c 100644 --- a/scripts/commit_validation/commit_validation/pal_allowedlist.txt +++ b/scripts/commit_validation/commit_validation/pal_allowedlist.txt @@ -17,6 +17,7 @@ */Code/Framework/AzCore/AzCore/Math/VectorFloat.h */Code/Framework/AzCore/AzCore/Memory/dlmalloc.inl */Code/Framework/AzCore/AzCore/Memory/nedmalloc.inl +*/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.* */Code/Framework/AzCore/AzCore/PlatformDef.h */Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h */Code/Framework/AzCore/AzCore/std/containers/variant.h