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/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/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index a8ea3f9837..c527aea98c 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -157,6 +157,7 @@ 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 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/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/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 97b86f2432..e100b240c2 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 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/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/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/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/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/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/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/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/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/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/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h index a595471813..7ad8bd8283 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/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 95bbe13586..5f39c6bc38 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/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/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/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..20d1b587b5 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() 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..6520844edd 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::OncePerSecond; + 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..eb0e295129 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl @@ -105,9 +105,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 +153,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 +190,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 +226,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 +429,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 +519,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 +532,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 +561,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 +657,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 +689,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: %lu", 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 +865,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 +950,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 +1034,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 +1072,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/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/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/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/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/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/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 )