From 59031d2f17aeba28f3a01b8384ddc51a7e0c5496 Mon Sep 17 00:00:00 2001 From: rbarrand Date: Tue, 21 Sep 2021 15:35:59 -0700 Subject: [PATCH 001/152] Enable material version updates. Signed-off-by: Robin --- .../Material/MaterialTypeSourceData.h | 28 ++++++++ .../Atom/RPI.Reflect/Material/MaterialAsset.h | 9 +++ .../RPI.Reflect/Material/MaterialTypeAsset.h | 14 ++++ .../Material/MaterialTypeAssetCreator.h | 6 ++ .../Material/MaterialVersionUpdate.h | 48 ++++++++++++++ .../Material/MaterialTypeSourceData.cpp | 35 +++++++++- .../RPI.Reflect/Material/MaterialAsset.cpp | 64 ++++++++++++++++++- .../Material/MaterialAssetCreator.cpp | 2 + .../Material/MaterialTypeAsset.cpp | 16 ++++- .../Material/MaterialTypeAssetCreator.cpp | 41 +++++++++++- .../Material/MaterialVersionUpdate.cpp | 57 +++++++++++++++++ .../RPI/Code/atom_rpi_reflect_files.cmake | 2 + 12 files changed, 316 insertions(+), 6 deletions(-) create mode 100644 Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h create mode 100644 Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h index 7019289466..cca7d41e91 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h @@ -119,6 +119,29 @@ namespace AZ using PropertyList = AZStd::vector; + struct VersionUpdatesRenameOperationDefinition + { + AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::VersionUpdatesRenameOperationDefinition, "{F2295489-E15A-46CC-929F-8D42DEDBCF14}"); + + AZStd::string m_operation; + + AZStd::string m_renameFrom; + AZStd::string m_renameTo; + }; + + // TODO: Support script operations. We will only be supporting rename for now. + using VersionUpdateActions = AZStd::vector; + + struct VersionUpdateDefinition + { + AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::VersionUpdateDefinition, "{2C9D3B91-0585-4BC9-91D2-4CF0C71BC4B7}"); + + uint32_t m_toVersion; + VersionUpdateActions m_actions; + }; + + using VersionUpdates = AZStd::vector; + struct PropertyLayout { AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::PropertyLayout, "{AE53CF3F-5C3B-44F5-B2FB-306F0EB06393}"); @@ -135,6 +158,11 @@ namespace AZ AZStd::string m_description; + //! Version 1 is the default and should not contain any version update. + uint32_t m_version = 1; + + VersionUpdates m_versionUpdates; + PropertyLayout m_propertyLayout; //! A list of shader variants that are always used at runtime; they cannot be turned off diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h index 6ca3bca652..2d42cc13ea 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h @@ -119,6 +119,11 @@ namespace AZ //! from m_materialTypeAsset. void RealignPropertyValuesAndNames(); + //! Renames properties in m_propertyNames based on the MaterialTypeAsset's version update. + void RenamePropertyNames(); + template + void RenamePropertyNames(iteratorType start, iteratorType end); + //! Called by asset creators to assign the asset to a ready state. void SetReady(); @@ -143,6 +148,10 @@ namespace AZ //! If empty, this implies that m_propertyValues is aligned with the entries in m_materialPropertiesLayout. AZStd::vector m_propertyNames; + //! The materialTypeVersion this materialAsset was based of. If the versions do not match at runtime when a + //! materialTypeAsset is loaded, an update will be performed on m_propertyNames if populated. + uint32_t m_materialTypeVersion = 1; + //! A flag to determine if m_propertyValues needs to be aligned with MaterialPropertiesLayout. Set to true whenever //! m_materialTypeAsset is reinitializing. bool m_isDirty = true; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h index e705a8041b..ca7de621a4 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace AZ { @@ -123,6 +124,13 @@ namespace AZ //! Returns a map from the UV shader inputs to a custom name. MaterialUvNameMap GetUvNameMap() const; + //! Returns the version of the MaterialTypeAsset. + uint32_t GetVersion() const; + //! Returns the version update containing the actions to perform. If a version + //! is not defined in m_materialVersionUpdatesMap, a material version with the specified + //! version but empty actions will be returned. + const MaterialVersionUpdate GetMaterialVersionUpdate(uint32_t version) const; + private: bool PostLoadInit() override; @@ -162,6 +170,12 @@ namespace AZ //! Index in @m_shaderCollection of the shader asset that contains the ObjectSrg. uint32_t m_objectSrgShaderIndex = InvalidShaderIndex; + //! The version of this MaterialTypeAsset. If the version is greater than 1, actions performed + //! to update this MaterialTypeAsset will be in m_materialVersionUpdateMap + uint32_t m_version = 1; + + //! Contains actions to perform for each material update version. + MaterialVersionUpdateMap m_materialVersionUpdateMap; }; class MaterialTypeAssetHandler : public AssetHandler diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h index 70488245a2..953b201960 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h @@ -38,6 +38,11 @@ namespace AZ void AddShader(const AZ::Data::Asset& shaderAsset, const ShaderVariantId& shaderVaraintId = ShaderVariantId{}, const AZ::Name& shaderTag = Uuid::CreateRandom().ToString()); void AddShader(const AZ::Data::Asset& shaderAsset, const AZ::Name& shaderTag); + //! Sets the version of the MaterialTypeAsset + void SetVersion(uint32_t version); + //! Adds a version update object into the MaterialTypeAsset + void AddVersionUpdate(uint32_t toVersion, MaterialVersionUpdate materialVersionUpdate); + //! Indicates that this MaterialType will own the specified shader option. //! Material-owned shader options can be connected to material properties (either directly or through functors). //! They cannot be accessed externally (for example, through the Material::SetSystemShaderOption() function). @@ -112,6 +117,7 @@ namespace AZ //! Saves the per-material SRG layout in m_shaderResourceGroupLayout for easier access void CacheMaterialSrgLayout(); + bool ValidateMaterialVersion(); bool ValidateBeginMaterialProperty(); bool ValidateEndMaterialProperty(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h new file mode 100644 index 0000000000..9147ec8d82 --- /dev/null +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace RPI + { + struct MaterialVersionUpdate + { + AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate, "{B36E7712-AED8-46AA-AFE0-01F8F884C44A}"); + + static void Reflect(ReflectContext* context); + + struct Action + { + AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate::Action, "{A1FBEB19-EA05-40F0-9700-57D048DF572B}"); + + AZStd::string m_operation; + AZStd::map m_argsMap; + + Action() = default; + Action(const AZStd::string& operation, const AZStd::initializer_list>& args); + void AddArgs(const AZStd::string& key, const AZStd::string& argument); + }; + + MaterialVersionUpdate() = default; + MaterialVersionUpdate(uint32_t toVersion); + + uint32_t m_toVersion; + + using Actions = AZStd::vector; + Actions m_actions; + }; + + using MaterialVersionUpdateMap = AZStd::map; + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 5a95cb35a6..c8ce26d4e7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,23 @@ namespace AZ serializeContext->RegisterGenericType(); + serializeContext->Class() + ->Version(1) + ->Field("op", &VersionUpdatesRenameOperationDefinition::m_operation) + ->Field("from", &VersionUpdatesRenameOperationDefinition::m_renameFrom) + ->Field("to", &VersionUpdatesRenameOperationDefinition::m_renameTo) + ; + + serializeContext->RegisterGenericType(); + + serializeContext->Class() + ->Version(1) + ->Field("toVersion", &VersionUpdateDefinition::m_toVersion) + ->Field("actions", &VersionUpdateDefinition::m_actions) + ; + + serializeContext->RegisterGenericType(); + serializeContext->Class() ->Version(1) ->Field("id", &GroupDefinition::m_nameId) @@ -86,8 +104,10 @@ namespace AZ serializeContext->RegisterGenericType(); serializeContext->Class() - ->Version(3) + ->Version(4) ->Field("description", &MaterialTypeSourceData::m_description) + ->Field("version", &MaterialTypeSourceData::m_version) + ->Field("versionUpdates", &MaterialTypeSourceData::m_versionUpdates) ->Field("propertyLayout", &MaterialTypeSourceData::m_propertyLayout) ->Field("shaders", &MaterialTypeSourceData::m_shaderCollection) ->Field("functors", &MaterialTypeSourceData::m_materialFunctorSourceData) @@ -290,6 +310,19 @@ namespace AZ materialTypeAssetCreator.SetElevateWarnings(elevateWarnings); materialTypeAssetCreator.Begin(assetId); + // Set materialtype version and add each version update object into MaterialTypeAsset. + materialTypeAssetCreator.SetVersion(m_version); + for (const auto& versionUpdate : m_versionUpdates) + { + MaterialVersionUpdate materialVersionUpdate; + for (const auto& action : versionUpdate.m_actions) + { + materialVersionUpdate.m_actions.push_back( + MaterialVersionUpdate::Action("rename", { { "from", action.m_renameFrom }, { "to", action.m_renameTo } })); + } + materialTypeAssetCreator.AddVersionUpdate(versionUpdate.m_toVersion, materialVersionUpdate); + } + // Used to gather all the UV streams used in this material type from its shaders in alphabetical order. auto semanticComp = [](const RHI::ShaderSemantic& lhs, const RHI::ShaderSemantic& rhs) -> bool { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index c3e0221192..eefa259ed3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -31,8 +32,9 @@ namespace AZ if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(10) + ->Version(11) // Material version update ->Field("materialTypeAsset", &MaterialAsset::m_materialTypeAsset) + ->Field("materialTypeVersion", &MaterialAsset::m_materialTypeVersion) ->Field("propertyValues", &MaterialAsset::m_propertyValues) ->Field("propertyNames", &MaterialAsset::m_propertyNames) ; @@ -102,9 +104,19 @@ namespace AZ AZStd::array_view MaterialAsset::GetPropertyValues() const { - if (!m_propertyNames.empty() && m_isDirty) + if (!m_propertyNames.empty()) { - const_cast(this)->RealignPropertyValuesAndNames(); + const uint32_t materialTypeVersion = m_materialTypeAsset->GetVersion(); + if (m_materialTypeVersion != materialTypeVersion) + { + const_cast(this)->RenamePropertyNames(); + const_cast(m_materialTypeVersion) = materialTypeVersion; + } + + if (m_isDirty) + { + const_cast(this)->RealignPropertyValuesAndNames(); + } } return m_propertyValues; @@ -182,6 +194,52 @@ namespace AZ m_isDirty = false; } + template + void MaterialAsset::RenamePropertyNames(iteratorType start, iteratorType end) + { + for (iteratorType it = start; it != end; ++it) + { + for (auto& propertyName : m_propertyNames) + { + const auto toFromIterator = it->find(propertyName.GetCStr()); + if (toFromIterator != it->end()) + { + propertyName = AZ::Name{ toFromIterator->second }; + } + } + } + } + + void MaterialAsset::RenamePropertyNames() + { + // construct toFrom map in ascending order of version. + AZStd::vector> versionToFrom; + + const bool ascending = m_materialTypeVersion < m_materialTypeAsset->GetVersion(); + for (int i = 0; i < AZStd::abs(static_cast(m_materialTypeVersion - m_materialTypeAsset->GetVersion())); ++i) + { + const auto& versionUpdate = m_materialTypeAsset->GetMaterialVersionUpdate((m_materialTypeVersion < m_materialTypeAsset->GetVersion() ? m_materialTypeVersion : m_materialTypeAsset->GetVersion()) + i + 1); + + versionToFrom.push_back(); + for (const auto& action : versionUpdate.m_actions) + { + const AZStd::string from = ascending ? action.m_argsMap.find("from")->second : action.m_argsMap.find("to")->second; + const AZStd::string to = ascending ? action.m_argsMap.find("to")->second : action.m_argsMap.find("from")->second; + + versionToFrom[i][from] = to; + } + } + + if (ascending) + { + RenamePropertyNames(versionToFrom.cbegin(), versionToFrom.cend()); + } + else + { + RenamePropertyNames(versionToFrom.crbegin(), versionToFrom.crend()); + } + } + void MaterialAsset::ReinitializeMaterialTypeAsset(Data::Asset asset) { Data::Asset newMaterialTypeAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAssetCreator.cpp index 79d19c15a2..b62a91a98d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAssetCreator.cpp @@ -23,6 +23,7 @@ namespace AZ if (ValidateIsReady()) { m_asset->m_materialTypeAsset = parentMaterial.m_materialTypeAsset; + m_asset->m_materialTypeVersion = m_asset->m_materialTypeAsset->GetVersion(); if (!m_asset->m_materialTypeAsset) { @@ -69,6 +70,7 @@ namespace AZ ReportError("MaterialTypeAsset is null"); return; } + m_asset->m_materialTypeVersion = m_asset->m_materialTypeAsset->GetVersion(); m_materialPropertiesLayout = m_asset->GetMaterialPropertiesLayout(); if (includeMaterialPropertyNames) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp index 913c3206fb..9209942f28 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp @@ -37,6 +37,7 @@ namespace AZ void MaterialTypeAsset::Reflect(ReflectContext* context) { + MaterialVersionUpdate::Reflect(context); UvNamePair::Reflect(context); if (auto* serializeContext = azrtti_cast(context)) @@ -44,7 +45,9 @@ namespace AZ serializeContext->RegisterGenericType(); serializeContext->Class() - ->Version(4) // ATOM-15472 + ->Version(5) // Material version update + ->Field("Version", &MaterialTypeAsset::m_version) + ->Field("VersionUpdates", &MaterialTypeAsset::m_materialVersionUpdateMap) ->Field("ShaderCollection", &MaterialTypeAsset::m_shaderCollection) ->Field("MaterialFunctors", &MaterialTypeAsset::m_materialFunctors) ->Field("MaterialSrgShaderIndex", &MaterialTypeAsset::m_materialSrgShaderIndex) @@ -161,6 +164,17 @@ namespace AZ return m_uvNameMap; } + uint32_t MaterialTypeAsset::GetVersion() const + { + return m_version; + } + + const AZ::RPI::MaterialVersionUpdate MaterialTypeAsset::GetMaterialVersionUpdate(uint32_t version) const + { + const auto it = m_materialVersionUpdateMap.find(version); + return it != m_materialVersionUpdateMap.end() ? it->second : MaterialVersionUpdate(version); + } + void MaterialTypeAsset::SetReady() { m_status = AssetStatus::Ready; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp index 4405e835d6..f10ae42266 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp @@ -38,7 +38,7 @@ namespace AZ bool MaterialTypeAssetCreator::End(Data::Asset& result) { - if (!ValidateIsReady() || !ValidateEndMaterialProperty()) + if (!ValidateIsReady() || !ValidateEndMaterialProperty() || !ValidateMaterialVersion()) { return false; } @@ -100,6 +100,35 @@ namespace AZ } } + bool MaterialTypeAssetCreator::ValidateMaterialVersion() + { + AZStd::vector renamedPropertieNames; + const auto& materialVersionUpdate = m_asset->GetMaterialVersionUpdate(m_asset->m_version); + for (const auto& action : materialVersionUpdate.m_actions) + { + const auto it = action.m_argsMap.find("to"); + if (it != action.m_argsMap.end()) + { + renamedPropertieNames.push_back(it->second); + } + } + + for (const auto& propertyName : renamedPropertieNames) + { + const auto propertyIndex = m_asset->m_materialPropertiesLayout->FindPropertyIndex(AZ::Name{ propertyName }); + if (!propertyIndex.IsValid()) + { + ReportError(AZStd::string::format( + "Renamed property '%s' not found in material property layout. Check that the property name has been " + "upgraded to the correct version", + propertyName.c_str()) + .c_str()); + return false; + } + } + return true; + } + void MaterialTypeAssetCreator::AddShader(const AZ::Data::Asset& shaderAsset, const ShaderVariantId& shaderVaraintId, const AZ::Name& shaderTag) { if (ValidateIsReady() && ValidateNotNull(shaderAsset, "ShaderAsset")) @@ -123,6 +152,16 @@ namespace AZ AddShader(shaderAsset, ShaderVariantId{}, shaderTag); } + void MaterialTypeAssetCreator::SetVersion(uint32_t version) + { + m_asset->m_version = version; + } + + void MaterialTypeAssetCreator::AddVersionUpdate(uint32_t toVersion, MaterialVersionUpdate materialVersionUpdate) + { + m_asset->m_materialVersionUpdateMap[toVersion] = materialVersionUpdate; + } + void MaterialTypeAssetCreator::ClaimShaderOptionOwnership(const Name& shaderOptionName) { bool optionFound = false; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp new file mode 100644 index 0000000000..fb2143d020 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +namespace AZ +{ + namespace RPI + { + void MaterialVersionUpdate::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("ArgsMap", &Action::m_argsMap) + ->Field("Operation", &Action::m_operation) + ; + + serializeContext->RegisterGenericType(); + + serializeContext->Class() + ->Version(1) + ->Field("ToVersion", &MaterialVersionUpdate::m_toVersion) + ->Field("Actions", &MaterialVersionUpdate::m_actions) + ; + + serializeContext->RegisterGenericType(); + } + } + + MaterialVersionUpdate::MaterialVersionUpdate(uint32_t toVersion) + : m_toVersion(toVersion) + { + } + + MaterialVersionUpdate::Action::Action(const AZStd::string& operation, const AZStd::initializer_list>& args) + : m_operation(operation) + { + for (const auto& arg : args) + { + AddArgs(arg.first, arg.second); + } + } + + void MaterialVersionUpdate::Action::AddArgs(const AZStd::string& key, const AZStd::string& argument) + { + m_argsMap[key] = argument; + } + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/RPI/Code/atom_rpi_reflect_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_reflect_files.cmake index 49c7231fed..4f0e432511 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_reflect_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_reflect_files.cmake @@ -61,6 +61,7 @@ set(FILES Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h Include/Atom/RPI.Reflect/Material/ShaderCollection.h Include/Atom/RPI.Reflect/Material/MaterialFunctor.h + Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h Include/Atom/RPI.Reflect/Pass/ComputePassData.h Include/Atom/RPI.Reflect/Pass/CopyPassData.h Include/Atom/RPI.Reflect/Pass/DownsampleMipChainPassData.h @@ -141,6 +142,7 @@ set(FILES Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp Source/RPI.Reflect/Material/ShaderCollection.cpp Source/RPI.Reflect/Material/MaterialFunctor.cpp + Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp Source/RPI.Reflect/Pass/PassAsset.cpp Source/RPI.Reflect/Pass/PassAttachmentReflect.cpp Source/RPI.Reflect/Pass/PassRequest.cpp From 2607b3471aa38bfb92fd9a378b43fa96162ad24c Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 27 Sep 2021 23:30:41 -0700 Subject: [PATCH 002/152] Resolve PR comments. Add unit tests. Signed-off-by: Robin --- .../Material/MaterialTypeSourceData.h | 6 +-- .../Atom/RPI.Reflect/Material/MaterialAsset.h | 9 ++-- .../RPI.Reflect/Material/MaterialTypeAsset.h | 6 +-- .../Material/MaterialTypeAssetCreator.h | 2 +- .../Material/MaterialVersionUpdate.h | 29 +++++++--- .../Material/MaterialTypeSourceData.cpp | 8 +-- .../RPI.Reflect/Material/MaterialAsset.cpp | 53 ++++--------------- .../Material/MaterialTypeAsset.cpp | 6 +-- .../Material/MaterialTypeAssetCreator.cpp | 14 ++--- .../Material/MaterialVersionUpdate.cpp | 50 +++++++++++++++-- .../Tests/Material/MaterialAssetTests.cpp | 53 +++++++++++++++++++ .../Tests/Material/MaterialTypeAssetTests.cpp | 37 ++++++++++++- .../Material/MaterialTypeSourceDataTests.cpp | 19 +++++-- .../Code/Source/Document/MaterialDocument.cpp | 3 -- .../Material/EditorMaterialComponentUtil.cpp | 1 - 15 files changed, 208 insertions(+), 88 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h index cca7d41e91..6d7c9b556c 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h @@ -129,7 +129,8 @@ namespace AZ AZStd::string m_renameTo; }; - // TODO: Support script operations. We will only be supporting rename for now. + // TODO: Support script operations--At that point, we'll likely need to replace VersionUpdatesRenameOperationDefinition with a more generic + // data structure that has a custom JSON serialize. We will only be supporting rename for now. using VersionUpdateActions = AZStd::vector; struct VersionUpdateDefinition @@ -146,9 +147,6 @@ namespace AZ { AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::PropertyLayout, "{AE53CF3F-5C3B-44F5-B2FB-306F0EB06393}"); - //! Indicates the version of the set of available properties. Can be used to detect materials that might need to be updated. - uint32_t m_version = 0; - //! List of groups that will contain the available properties AZStd::vector m_groups; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h index 2d42cc13ea..e14ff30f87 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h @@ -22,6 +22,7 @@ namespace UnitTest { class MaterialTests; + class MaterialAssetTests; } namespace AZ @@ -42,10 +43,12 @@ namespace AZ , public MaterialReloadNotificationBus::Handler , public AssetInitBus::Handler { + friend class MaterialVersionUpdate; friend class MaterialAssetCreator; friend class MaterialAssetHandler; friend class MaterialAssetCreatorCommon; friend class UnitTest::MaterialTests; + friend class UnitTest::MaterialAssetTests; public: AZ_RTTI(MaterialAsset, "{522C7BE0-501D-463E-92C6-15184A2B7AD8}", AZ::Data::AssetData); @@ -119,10 +122,8 @@ namespace AZ //! from m_materialTypeAsset. void RealignPropertyValuesAndNames(); - //! Renames properties in m_propertyNames based on the MaterialTypeAsset's version update. - void RenamePropertyNames(); - template - void RenamePropertyNames(iteratorType start, iteratorType end); + //! Renames properties in m_propertyNames based on the MaterialTypeAsset's version update. Note that only version upgrades are supported. + void ApplyVersionUpdates(); //! Called by asset creators to assign the asset to a ready state. void SetReady(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h index ca7de621a4..d0f0609c02 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h @@ -126,10 +126,10 @@ namespace AZ //! Returns the version of the MaterialTypeAsset. uint32_t GetVersion() const; - //! Returns the version update containing the actions to perform. If a version - //! is not defined in m_materialVersionUpdatesMap, a material version with the specified + //! Returns the toVersion update containing the actions to perform. If a toVersion + //! is not defined in m_materialVersionUpdatesMap, a material toVersion with the specified //! version but empty actions will be returned. - const MaterialVersionUpdate GetMaterialVersionUpdate(uint32_t version) const; + MaterialVersionUpdate GetMaterialVersionUpdate(uint32_t toVersion) const; private: bool PostLoadInit() override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h index 953b201960..64327ecd03 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h @@ -41,7 +41,7 @@ namespace AZ //! Sets the version of the MaterialTypeAsset void SetVersion(uint32_t version); //! Adds a version update object into the MaterialTypeAsset - void AddVersionUpdate(uint32_t toVersion, MaterialVersionUpdate materialVersionUpdate); + void AddVersionUpdate(uint32_t toVersion, const MaterialVersionUpdate& materialVersionUpdate); //! Indicates that this MaterialType will own the specified shader option. //! Material-owned shader options can be connected to material properties (either directly or through functors). diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h index 9147ec8d82..ee15ebfdcf 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h @@ -11,13 +11,18 @@ #include #include #include +#include namespace AZ { namespace RPI { - struct MaterialVersionUpdate + class MaterialAsset; + + // This class contains a toVersion and a list of actions to specify what operations were performed to upgrade a materialType. + class MaterialVersionUpdate { + public: AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate, "{B36E7712-AED8-46AA-AFE0-01F8F884C44A}"); static void Reflect(ReflectContext* context); @@ -26,20 +31,28 @@ namespace AZ { AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate::Action, "{A1FBEB19-EA05-40F0-9700-57D048DF572B}"); - AZStd::string m_operation; - AZStd::map m_argsMap; + AZ::Name m_operation; + AZStd::unordered_map m_argsMap; Action() = default; - Action(const AZStd::string& operation, const AZStd::initializer_list>& args); - void AddArgs(const AZStd::string& key, const AZStd::string& argument); + Action(const AZ::Name& operation, const AZStd::initializer_list>& args); + void AddArg(const AZ::Name& key, const AZ::Name& argument); }; - MaterialVersionUpdate() = default; - MaterialVersionUpdate(uint32_t toVersion); + explicit MaterialVersionUpdate() = default; + explicit MaterialVersionUpdate(uint32_t toVersion); - uint32_t m_toVersion; + uint32_t GetVersion() const; + void SetVersion(uint32_t toVersion); + + void ApplyVersionUpdates(MaterialAsset& materialAsset) const; using Actions = AZStd::vector; + const Actions& GetActions() const; + void AddAction(const Action& action); + + private: + uint32_t m_toVersion; Actions m_actions; }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index c8ce26d4e7..4d9d40cde2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -95,8 +95,7 @@ namespace AZ ; serializeContext->Class() - ->Version(1) - ->Field("version", &PropertyLayout::m_version) + ->Version(2) ->Field("groups", &PropertyLayout::m_groups) ->Field("properties", &PropertyLayout::m_properties) ; @@ -317,8 +316,9 @@ namespace AZ MaterialVersionUpdate materialVersionUpdate; for (const auto& action : versionUpdate.m_actions) { - materialVersionUpdate.m_actions.push_back( - MaterialVersionUpdate::Action("rename", { { "from", action.m_renameFrom }, { "to", action.m_renameTo } })); + materialVersionUpdate.AddAction(MaterialVersionUpdate::Action(AZ::Name{ "rename" }, { + { AZ::Name{ "from" }, AZ::Name{ action.m_renameFrom } }, + { AZ::Name{ "to" }, AZ::Name{ action.m_renameTo } } })); } materialTypeAssetCreator.AddVersionUpdate(versionUpdate.m_toVersion, materialVersionUpdate); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index eefa259ed3..1695319a64 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -6,6 +6,8 @@ * */ +#pragma optimize("", off) + #include #include #include @@ -107,10 +109,9 @@ namespace AZ if (!m_propertyNames.empty()) { const uint32_t materialTypeVersion = m_materialTypeAsset->GetVersion(); - if (m_materialTypeVersion != materialTypeVersion) + if (m_materialTypeVersion < materialTypeVersion) { - const_cast(this)->RenamePropertyNames(); - const_cast(m_materialTypeVersion) = materialTypeVersion; + const_cast(this)->ApplyVersionUpdates(); } if (m_isDirty) @@ -194,50 +195,16 @@ namespace AZ m_isDirty = false; } - template - void MaterialAsset::RenamePropertyNames(iteratorType start, iteratorType end) + void MaterialAsset::ApplyVersionUpdates() { - for (iteratorType it = start; it != end; ++it) + for (int i = 0; i < static_cast(m_materialTypeAsset->GetVersion() - m_materialTypeVersion); ++i) { - for (auto& propertyName : m_propertyNames) - { - const auto toFromIterator = it->find(propertyName.GetCStr()); - if (toFromIterator != it->end()) - { - propertyName = AZ::Name{ toFromIterator->second }; - } - } - } - } + const auto& versionUpdate = m_materialTypeAsset->GetMaterialVersionUpdate(m_materialTypeVersion + i + 1); - void MaterialAsset::RenamePropertyNames() - { - // construct toFrom map in ascending order of version. - AZStd::vector> versionToFrom; - - const bool ascending = m_materialTypeVersion < m_materialTypeAsset->GetVersion(); - for (int i = 0; i < AZStd::abs(static_cast(m_materialTypeVersion - m_materialTypeAsset->GetVersion())); ++i) - { - const auto& versionUpdate = m_materialTypeAsset->GetMaterialVersionUpdate((m_materialTypeVersion < m_materialTypeAsset->GetVersion() ? m_materialTypeVersion : m_materialTypeAsset->GetVersion()) + i + 1); - - versionToFrom.push_back(); - for (const auto& action : versionUpdate.m_actions) - { - const AZStd::string from = ascending ? action.m_argsMap.find("from")->second : action.m_argsMap.find("to")->second; - const AZStd::string to = ascending ? action.m_argsMap.find("to")->second : action.m_argsMap.find("from")->second; - - versionToFrom[i][from] = to; - } + versionUpdate.ApplyVersionUpdates(*this); } - if (ascending) - { - RenamePropertyNames(versionToFrom.cbegin(), versionToFrom.cend()); - } - else - { - RenamePropertyNames(versionToFrom.crbegin(), versionToFrom.crend()); - } + m_materialTypeVersion = m_materialTypeAsset->GetVersion(); } void MaterialAsset::ReinitializeMaterialTypeAsset(Data::Asset asset) @@ -287,3 +254,5 @@ namespace AZ } // namespace RPI } // namespace AZ + +#pragma optimize("", on) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp index 9209942f28..a876028599 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp @@ -169,10 +169,10 @@ namespace AZ return m_version; } - const AZ::RPI::MaterialVersionUpdate MaterialTypeAsset::GetMaterialVersionUpdate(uint32_t version) const + MaterialVersionUpdate MaterialTypeAsset::GetMaterialVersionUpdate(uint32_t toVersion) const { - const auto it = m_materialVersionUpdateMap.find(version); - return it != m_materialVersionUpdateMap.end() ? it->second : MaterialVersionUpdate(version); + const auto it = m_materialVersionUpdateMap.find(toVersion); + return it != m_materialVersionUpdateMap.end() ? it->second : MaterialVersionUpdate(toVersion); } void MaterialTypeAsset::SetReady() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp index f10ae42266..79336692ec 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp @@ -102,18 +102,18 @@ namespace AZ bool MaterialTypeAssetCreator::ValidateMaterialVersion() { - AZStd::vector renamedPropertieNames; + AZStd::vector renamedPropertyNames; const auto& materialVersionUpdate = m_asset->GetMaterialVersionUpdate(m_asset->m_version); - for (const auto& action : materialVersionUpdate.m_actions) + for (const auto& action : materialVersionUpdate.GetActions()) { - const auto it = action.m_argsMap.find("to"); + const auto it = action.m_argsMap.find(AZ::Name{ "to" }); if (it != action.m_argsMap.end()) { - renamedPropertieNames.push_back(it->second); + renamedPropertyNames.push_back(it->second); } } - for (const auto& propertyName : renamedPropertieNames) + for (const auto& propertyName : renamedPropertyNames) { const auto propertyIndex = m_asset->m_materialPropertiesLayout->FindPropertyIndex(AZ::Name{ propertyName }); if (!propertyIndex.IsValid()) @@ -121,7 +121,7 @@ namespace AZ ReportError(AZStd::string::format( "Renamed property '%s' not found in material property layout. Check that the property name has been " "upgraded to the correct version", - propertyName.c_str()) + propertyName.GetCStr()) .c_str()); return false; } @@ -157,7 +157,7 @@ namespace AZ m_asset->m_version = version; } - void MaterialTypeAssetCreator::AddVersionUpdate(uint32_t toVersion, MaterialVersionUpdate materialVersionUpdate) + void MaterialTypeAssetCreator::AddVersionUpdate(uint32_t toVersion, const MaterialVersionUpdate& materialVersionUpdate) { m_asset->m_materialVersionUpdateMap[toVersion] = materialVersionUpdate; } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp index fb2143d020..faa571e403 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp @@ -7,6 +7,7 @@ */ #include +#include #include namespace AZ @@ -40,16 +41,59 @@ namespace AZ { } - MaterialVersionUpdate::Action::Action(const AZStd::string& operation, const AZStd::initializer_list>& args) + uint32_t MaterialVersionUpdate::GetVersion() const + { + return m_toVersion; + } + + void MaterialVersionUpdate::SetVersion(uint32_t toVersion) + { + m_toVersion = toVersion; + } + + void MaterialVersionUpdate::ApplyVersionUpdates(MaterialAsset& materialAsset) const + { + // collect all renames within this version update + AZStd::unordered_map renameToFrom; + for (const auto& action : m_actions) + { + const AZ::Name from = action.m_argsMap.find(AZ::Name{ "from" })->second; + const AZ::Name to = action.m_argsMap.find(AZ::Name{ "to" })->second; + + renameToFrom[from] = to; + } + + // apply rename actions + for (auto& propertyName : materialAsset.m_propertyNames) + { + const auto toFromIterator = renameToFrom.find(propertyName); + if (toFromIterator != renameToFrom.end()) + { + propertyName = AZ::Name{ toFromIterator->second }; + } + } + } + + const AZ::RPI::MaterialVersionUpdate::Actions& MaterialVersionUpdate::GetActions() const + { + return m_actions; + } + + void MaterialVersionUpdate::AddAction(const Action& action) + { + m_actions.push_back(action); + } + + MaterialVersionUpdate::Action::Action(const AZ::Name& operation, const AZStd::initializer_list>& args) : m_operation(operation) { for (const auto& arg : args) { - AddArgs(arg.first, arg.second); + AddArg(arg.first, arg.second); } } - void MaterialVersionUpdate::Action::AddArgs(const AZStd::string& key, const AZStd::string& argument) + void MaterialVersionUpdate::Action::AddArg(const AZ::Name& key, const AZ::Name& argument) { m_argsMap[key] = argument; } diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp index ce223ceb35..3191797ded 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp @@ -63,6 +63,17 @@ namespace UnitTest RPITestFixture::TearDown(); } + + void UpgradeAndValidateMaterialAsset(Data::Asset materialAsset, Data::Asset upgradedMaterialTypeAsset) + { + // Set materialTypeAsset to the upgraded version. + EXPECT_EQ(1, materialAsset->m_materialTypeVersion); + materialAsset->m_materialTypeAsset = upgradedMaterialTypeAsset; + materialAsset->ApplyVersionUpdates(); + + EXPECT_EQ(2, materialAsset->m_materialTypeVersion); + EXPECT_EQ(AZ::Name{ "MyBoolNext" }, materialAsset->m_propertyNames[0]); + } }; TEST_F(MaterialAssetTests, Basic) @@ -202,6 +213,48 @@ namespace UnitTest EXPECT_EQ(serializedAsset->GetPropertyValues()[8].GetValue>(), streamingImageAsset); } + TEST_F(MaterialAssetTests, UpgradeMaterialAsset) + { + auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); + + auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); + + Data::Asset testMaterialTypeAssetV1; + MaterialTypeAssetCreator materialTypeCreator; + materialTypeCreator.Begin(Uuid::CreateRandom()); + materialTypeCreator.AddShader(shaderAsset); + AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyBool" }, MaterialPropertyDataType::Bool, Name{ "m_bool" }); + materialTypeCreator.SetPropertyValue(Name{ "MyBool" }, true); + EXPECT_TRUE(materialTypeCreator.End(testMaterialTypeAssetV1)); + + // Construct the material asset with materialTypeAsset version 1 + Data::AssetId assetId(Uuid::CreateRandom()); + + MaterialAssetCreator creator; + creator.Begin(assetId, *testMaterialTypeAssetV1); + creator.SetPropertyValue(Name{ "MyBool" }, true); + Data::Asset materialAsset; + EXPECT_TRUE(creator.End(materialAsset)); + + // Prepare material type asset version 2 with the update actions + MaterialVersionUpdate versionUpdate(2); + versionUpdate.AddAction(MaterialVersionUpdate::Action(AZ::Name{ "rename" }, { + { Name{ "from" }, Name{ "MyBool" } }, + { Name{ "to" }, Name{ "MyBoolNext" } } })); + + Data::Asset testMaterialTypeAssetV2; + materialTypeCreator.Begin(Uuid::CreateRandom()); + materialTypeCreator.SetVersion(versionUpdate.GetVersion()); + materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); + materialTypeCreator.AddShader(shaderAsset); + AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyBoolNext" }, MaterialPropertyDataType::Bool, Name{ "m_bool" }); + materialTypeCreator.SetPropertyValue(Name{ "MyBoolNext" }, true); + EXPECT_TRUE(materialTypeCreator.End(testMaterialTypeAssetV2)); + + // Upgrade the material asset with the materialTypeAsset v2 and verify + UpgradeAndValidateMaterialAsset(materialAsset, testMaterialTypeAssetV2); + } + TEST_F(MaterialAssetTests, Error_NoBegin) { Data::AssetId assetId(Uuid::CreateRandom()); diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp index b9774c84d9..6329ef8d48 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -153,6 +154,14 @@ namespace UnitTest MaterialTypeAssetCreator materialTypeCreator; materialTypeCreator.Begin(assetId); + // Version updates + MaterialVersionUpdate versionUpdate(2); + versionUpdate.AddAction(MaterialVersionUpdate::Action(AZ::Name{ "rename" }, { + { Name{ "from" }, Name{ "EnableSpecialPassPrev" } }, + { Name{ "to" }, Name{ "EnableSpecialPass" } } })); + materialTypeCreator.SetVersion(versionUpdate.GetVersion()); + materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); + // Built-in shader materialTypeCreator.AddShader(m_testShaderAsset); @@ -198,7 +207,7 @@ namespace UnitTest { EXPECT_EQ(m_testMaterialSrgLayout, materialTypeAsset->GetMaterialSrgLayout()); EXPECT_EQ(5, materialTypeAsset->GetMaterialPropertiesLayout()->GetPropertyCount()); - + //EXPECT_EQ(2, materialTypeAsset->GetVersion()); // Check aliased properties const MaterialPropertyIndex colorIndex = materialTypeAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(Name{ "MyColor" }); @@ -490,6 +499,32 @@ namespace UnitTest }); } + TEST_F(MaterialTypeAssetTests, Error_InvalidMaterialVersionUpdate) + { + Data::Asset materialTypeAsset; + + Data::AssetId assetId(Uuid::CreateRandom()); + + MaterialTypeAssetCreator materialTypeCreator; + materialTypeCreator.Begin(assetId); + + // Invalid version updates + MaterialVersionUpdate versionUpdate(2); + versionUpdate.AddAction(MaterialVersionUpdate::Action(AZ::Name{ "rename" }, { + { Name{ "from" }, Name{ "EnableSpecialPassPrev" } }, + { Name{ "to" }, Name{ "InvalidPropertyName" } } })); + materialTypeCreator.SetVersion(versionUpdate.GetVersion()); + materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); + materialTypeCreator.AddShader(m_testShaderAsset); + + materialTypeCreator.BeginMaterialProperty(Name{ "EnableSpecialPass" }, MaterialPropertyDataType::Bool); + materialTypeCreator.EndMaterialProperty(); + + AZ_TEST_START_ASSERTTEST; + EXPECT_FALSE(materialTypeCreator.End(materialTypeAsset)); + AZ_TEST_STOP_ASSERTTEST(1); + EXPECT_EQ(1, materialTypeCreator.GetErrorCount()); + } TEST_F(MaterialTypeAssetTests, MaterialTypeWithNoSRGOrProperties) { diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp index 179dc7c966..7766c1f8d8 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp @@ -978,8 +978,16 @@ namespace UnitTest const AZStd::string inputJson = R"( { "description": "This is a general description about the material", + "version": 2, + "versionUpdates": [ + { + "toVersion": 2, + "actions": [ + { "op": "rename", "from": "groupA.fooPrev", "to": "groupA.foo" } + ] + } + ], "propertyLayout": { - "version": 2, "groups": [ { "id": "groupA", @@ -1061,9 +1069,12 @@ namespace UnitTest JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson); EXPECT_EQ(material.m_description, "This is a general description about the material"); - - EXPECT_EQ(material.m_propertyLayout.m_version, 2); - + EXPECT_EQ(material.m_version, 2); + EXPECT_EQ(material.m_versionUpdates.size(), 1); + EXPECT_EQ(material.m_versionUpdates[0].m_toVersion, 2); + EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_operation, "rename"); + EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_renameFrom, "groupA.fooPrev"); + EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_renameTo, "groupA.foo"); EXPECT_EQ(material.m_propertyLayout.m_groups.size(), 2); EXPECT_TRUE(material.FindGroup("groupA") != nullptr); EXPECT_TRUE(material.FindGroup("groupB") != nullptr); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index d032f35e38..ab8f9ff1a4 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -231,7 +231,6 @@ namespace MaterialEditor // create source data from properties MaterialSourceData sourceData; - sourceData.m_propertyLayoutVersion = m_materialTypeSourceData.m_propertyLayout.m_version; sourceData.m_materialType = m_materialSourceData.m_materialType; sourceData.m_parentMaterial = m_materialSourceData.m_parentMaterial; @@ -303,7 +302,6 @@ namespace MaterialEditor // create source data from properties MaterialSourceData sourceData; - sourceData.m_propertyLayoutVersion = m_materialTypeSourceData.m_propertyLayout.m_version; sourceData.m_materialType = m_materialSourceData.m_materialType; sourceData.m_parentMaterial = m_materialSourceData.m_parentMaterial; @@ -374,7 +372,6 @@ namespace MaterialEditor // create source data from properties MaterialSourceData sourceData; - sourceData.m_propertyLayoutVersion = m_materialTypeSourceData.m_propertyLayout.m_version; sourceData.m_materialType = m_materialSourceData.m_materialType; // Only assign a parent path if the source was a .material diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp index e7059ccdf7..65ddef47e5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp @@ -99,7 +99,6 @@ namespace AZ { // Construct the material source data object that will be exported AZ::RPI::MaterialSourceData exportData; - exportData.m_propertyLayoutVersion = editData.m_materialTypeSourceData.m_propertyLayout.m_version; // Converting absolute material paths to relative paths bool result = false; From 503e565a5313c238d5f51ce0e75ee72d3f1dc48e Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 27 Sep 2021 23:37:58 -0700 Subject: [PATCH 003/152] Remove pragma optimize off. Signed-off-by: Robin --- .../RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index 1695319a64..c838ab2794 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -6,8 +6,6 @@ * */ -#pragma optimize("", off) - #include #include #include @@ -254,5 +252,3 @@ namespace AZ } // namespace RPI } // namespace AZ - -#pragma optimize("", on) From ca06e2e82d19b2b08f2a163ea8fd4ae4bd4ab792 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 27 Sep 2021 23:44:13 -0700 Subject: [PATCH 004/152] Update reflection version markers. Signed-off-by: Robin --- .../Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp | 4 ++-- Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 4d9d40cde2..48b2c43435 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -95,7 +95,7 @@ namespace AZ ; serializeContext->Class() - ->Version(2) + ->Version(2) // Material Version Update ->Field("groups", &PropertyLayout::m_groups) ->Field("properties", &PropertyLayout::m_properties) ; @@ -103,7 +103,7 @@ namespace AZ serializeContext->RegisterGenericType(); serializeContext->Class() - ->Version(4) + ->Version(4) // Material Version Update ->Field("description", &MaterialTypeSourceData::m_description) ->Field("version", &MaterialTypeSourceData::m_version) ->Field("versionUpdates", &MaterialTypeSourceData::m_versionUpdates) diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp index 6329ef8d48..8341a8e382 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp @@ -207,7 +207,7 @@ namespace UnitTest { EXPECT_EQ(m_testMaterialSrgLayout, materialTypeAsset->GetMaterialSrgLayout()); EXPECT_EQ(5, materialTypeAsset->GetMaterialPropertiesLayout()->GetPropertyCount()); - //EXPECT_EQ(2, materialTypeAsset->GetVersion()); + EXPECT_EQ(2, materialTypeAsset->GetVersion()); // Check aliased properties const MaterialPropertyIndex colorIndex = materialTypeAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(Name{ "MyColor" }); From acff45446a068bcce2ba88cb711b1b9da3a0ae65 Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 29 Sep 2021 10:04:49 -0700 Subject: [PATCH 005/152] Resolve PR comments. Signed-off-by: Robin --- .../Material/MaterialTypeSourceData.cpp | 19 ++++++++++++------- .../RPI.Reflect/Material/MaterialAsset.cpp | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 48b2c43435..6fe4fa3412 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -311,16 +311,21 @@ namespace AZ // Set materialtype version and add each version update object into MaterialTypeAsset. materialTypeAssetCreator.SetVersion(m_version); - for (const auto& versionUpdate : m_versionUpdates) { - MaterialVersionUpdate materialVersionUpdate; - for (const auto& action : versionUpdate.m_actions) + const AZ::Name rename = AZ::Name{ "rename" }; + const AZ::Name from = AZ::Name{ "from" }; + const AZ::Name to = AZ::Name{ "to" }; + for (const auto& versionUpdate : m_versionUpdates) { - materialVersionUpdate.AddAction(MaterialVersionUpdate::Action(AZ::Name{ "rename" }, { - { AZ::Name{ "from" }, AZ::Name{ action.m_renameFrom } }, - { AZ::Name{ "to" }, AZ::Name{ action.m_renameTo } } })); + MaterialVersionUpdate materialVersionUpdate; + for (const auto& action : versionUpdate.m_actions) + { + materialVersionUpdate.AddAction(MaterialVersionUpdate::Action(rename, { + { from, AZ::Name{ action.m_renameFrom } }, + { to, AZ::Name{ action.m_renameTo } } })); + } + materialTypeAssetCreator.AddVersionUpdate(versionUpdate.m_toVersion, materialVersionUpdate); } - materialTypeAssetCreator.AddVersionUpdate(versionUpdate.m_toVersion, materialVersionUpdate); } // Used to gather all the UV streams used in this material type from its shaders in alphabetical order. diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index c838ab2794..cc460f4234 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -195,7 +195,7 @@ namespace AZ void MaterialAsset::ApplyVersionUpdates() { - for (int i = 0; i < static_cast(m_materialTypeAsset->GetVersion() - m_materialTypeVersion); ++i) + for (int i = 0; i < aznumeric_cast(m_materialTypeAsset->GetVersion() - m_materialTypeVersion); ++i) { const auto& versionUpdate = m_materialTypeAsset->GetMaterialVersionUpdate(m_materialTypeVersion + i + 1); From ef3552df69b27ef0c8bb04934fb5187f2f5f6999 Mon Sep 17 00:00:00 2001 From: pereslav Date: Fri, 1 Oct 2021 00:07:23 +0100 Subject: [PATCH 006/152] First pass for network hierarchy input processing Signed-off-by: pereslav --- .../NetworkHierarchyRootComponent.h | 22 ++++ .../Multiplayer/NetworkInput/NetworkInput.h | 25 +++++ ...rkHierarchyRootComponent.AutoComponent.xml | 4 +- .../NetworkHierarchyRootComponent.cpp | 100 ++++++++++++++++++ .../Code/Source/NetworkInput/NetworkInput.cpp | 49 +++++++++ 5 files changed, 199 insertions(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 4c9f94c004..8e488c1029 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -57,6 +57,8 @@ namespace Multiplayer void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} + const AZStd::vector& GetHierarchicalEntitiesRef() const; + protected: void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); @@ -97,4 +99,24 @@ namespace Multiplayer //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. bool m_isHierarchyEnabled = true; }; + + + //! NetworkCharacterComponentController + //! This is the network controller for NetworkHierarchyRootComponent. + //! Class provides the ability to process input for hierarchies. + class NetworkHierarchyRootComponentController + : public NetworkHierarchyRootComponentControllerBase + { + public: + NetworkHierarchyRootComponentController(NetworkHierarchyRootComponent& parent); + + // NetworkHierarchyRootComponentControllerBase + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + //! MultiplayerController interface + Multiplayer::MultiplayerController::InputPriorityOrder GetInputOrder() const override; + void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override; + void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override; + }; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h index 1e02d6bf56..4575d88e24 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h @@ -29,6 +29,7 @@ namespace Multiplayer friend class NetworkInputMigrationVector; friend class NetworkInputHistory; friend class NetworkInputChild; + friend class NetworkSubInput; NetworkInput(const NetworkInput&); NetworkInput& operator= (const NetworkInput&); @@ -80,4 +81,28 @@ namespace Multiplayer ConstNetworkEntityHandle m_owner; bool m_wasAttached = false; }; + + // Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify dependent entities + // Since it is possible to for the Client/Server to disagree about the state of related entities, this input encodes the entity that + // is associated with it. + class NetworkSubInput final + { + public: + NetworkSubInput() = default; + NetworkSubInput(const NetworkSubInput&) = default; + NetworkSubInput(const ConstNetworkEntityHandle& entityHandle); + + NetworkSubInput& operator=(const NetworkSubInput&) = default; + + void Attach(const ConstNetworkEntityHandle& entityHandle); + const ConstNetworkEntityHandle& GetOwner() const; + NetworkInput& GetNetworkInput(); + const NetworkInput& GetNetworkInput() const; + + bool Serialize(AzNetworking::ISerializer& serializer); + private: + ConstNetworkEntityHandle m_owner; + NetworkInput m_networkInput; + }; + } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml index 0f33e1f642..c93f300da3 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -4,11 +4,13 @@ Name="NetworkHierarchyRootComponent" Namespace="Multiplayer" OverrideComponent="true" - OverrideController="false" + OverrideController="true" OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + + diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 4a5e9ce8d2..ca4bbec66b 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -139,6 +139,11 @@ namespace Multiplayer return m_hierarchicalEntities; } + const AZStd::vector& NetworkHierarchyRootComponent::GetHierarchicalEntitiesRef() const + { + return m_hierarchicalEntities; + } + AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const { if (m_rootEntity) @@ -326,4 +331,99 @@ namespace Multiplayer RebuildHierarchy(); } } + + NetworkHierarchyRootComponentController::NetworkHierarchyRootComponentController(NetworkHierarchyRootComponent& parent) + : NetworkHierarchyRootComponentControllerBase(parent) + { + + } + + void NetworkHierarchyRootComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + + } + + void NetworkHierarchyRootComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + + } + + Multiplayer::MultiplayerController::InputPriorityOrder NetworkHierarchyRootComponentController::GetInputOrder() const + { + return Multiplayer::MultiplayerController::InputPriorityOrder::SubEntities; + } + + void NetworkHierarchyRootComponentController::CreateInput(Multiplayer::NetworkInput& input, float deltaTime) + { + NetworkHierarchyRootComponent& component = GetParent(); + if(!component.IsHierarchicalRoot()) + { + return; + } + + const AZStd::vector& entities = component.GetHierarchicalEntitiesRef(); + + auto* networkInput = input.FindComponentInput(); + networkInput->m_childInputs.clear(); + networkInput->m_childInputs.reserve(entities.size()); + + for (AZ::Entity* child : entities) + { + if(child == component.GetEntity()) + { + return; // Avoid infinite recursion + } + + auto* netComp = child->FindComponent(); + AZ_Assert(netComp, "No NetSystemComponent, this should be impossible"); + // Validate we still have a controller and we aren't in the middle of removing them + if (netComp->HasController()) + { + ConstNetworkEntityHandle childEntityHandle = netComp->GetEntityHandle(); + NetworkSubInput subInput; + subInput.Attach(childEntityHandle); + subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); + + netComp->CreateInput(subInput.GetNetworkInput(), deltaTime); + + // make sure our input sub commands have the same time as the original + subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); + networkInput->m_childInputs.emplace_back(subInput); + } + } + } + + void NetworkHierarchyRootComponentController::ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) + { + // Prevent replaying process input commands for child entities that weren't part of the hierarchy at that time + //if (!m_firstProcessInputOccurred) + //{ + // m_firstProcessInputOccurred = true; + // m_firstProcessInputTime = input.GetInputId().GetGameTimePoint(); + //} + //else if (m_firstProcessInputTime > input.GetInputId().GetGameTimePoint()) + //{ + // return; + //} + + if (auto* networkInput = input.FindComponentInput()) + { + for (NetworkSubInput& subInput : networkInput->m_childInputs) + { + const ConstNetworkEntityHandle& childEntity = subInput.GetOwner(); + if (auto* localChild = childEntity.GetEntity()) + { + auto* netComp = localChild->FindComponent(); + AZ_Assert(netComp, "No NetSystemComponent, this should be impossible"); + // We do not rewind entity role changes, so make sure we are the correct role prior to processing + if (netComp->HasController()) + { + subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); + netComp->ProcessInput(subInput.GetNetworkInput(), deltaTime); + } + } + } + } + } + } diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp index 35ca02bfba..2bbda48551 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp @@ -188,4 +188,53 @@ namespace Multiplayer } m_wasAttached = rhs.m_wasAttached; } + + NetworkSubInput::NetworkSubInput(const ConstNetworkEntityHandle& entityHandle) + : m_owner(entityHandle) + { + Attach(m_owner); + } + + void NetworkSubInput::Attach(const ConstNetworkEntityHandle& entityHandle) + { + m_owner = entityHandle; + if (auto* localEnt = entityHandle.GetEntity()) + { + NetBindComponent* netBindComponent = localEnt->FindComponent(); + if (netBindComponent) + { + m_networkInput.AttachNetBindComponent(netBindComponent); + } + } + } + + const ConstNetworkEntityHandle& NetworkSubInput::GetOwner() const + { + return m_owner; + } + + NetworkInput& NetworkSubInput::GetNetworkInput() + { + return m_networkInput; + } + + const NetworkInput& NetworkSubInput::GetNetworkInput() const + { + return m_networkInput; + } + + bool NetworkSubInput::Serialize(AzNetworking::ISerializer& serializer) + { + NetEntityId tmpId = m_owner.GetNetEntityId(); + serializer.Serialize(tmpId, "OwnerId"); + + if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) + { + m_owner = AZ::Interface::Get()->GetEntity(tmpId); + } + + serializer.Serialize(m_networkInput, "NetInput"); + return serializer.IsValid(); + } + } From c1c870920627b864ac9a998715b23204da1f4dd4 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Mon, 4 Oct 2021 16:57:21 -0700 Subject: [PATCH 007/152] Fix 3-way replication for when an object migrates in rapid succession Signed-off-by: kberg-amzn --- .../EntityReplicationManager.h | 1 - .../NetworkEntityUpdateMessage.h | 8 +------ .../EntityReplicationManager.cpp | 12 +++------- .../EntityReplication/EntityReplicator.cpp | 17 +++++++------- .../NetworkEntityUpdateMessage.cpp | 23 ++++--------------- 5 files changed, 17 insertions(+), 44 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h index 2e1f83ae38..10346ad777 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -57,7 +57,6 @@ namespace Multiplayer EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode); ~EntityReplicationManager() = default; - void SetRemoteHostId(const HostId& hostId); const HostId& GetRemoteHostId() const; void ActivatePendingEntities(); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h index 90f622a8ae..139db2a949 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h @@ -43,8 +43,7 @@ namespace Multiplayer //! Constructor for an entity delete message. //! @param entityId the networkId of the entity being deleted //! @param isMigrated whether or not the entity is being migrated or deleted - //! @param takeOwnership true if the remote replicator should take ownership of the entity - explicit NetworkEntityUpdateMessage(NetEntityId entityId, bool isMigrated, bool takeOwnership); + explicit NetworkEntityUpdateMessage(NetEntityId entityId, bool isMigrated); NetworkEntityUpdateMessage& operator =(NetworkEntityUpdateMessage&& rhs); NetworkEntityUpdateMessage& operator =(const NetworkEntityUpdateMessage& rhs); @@ -71,10 +70,6 @@ namespace Multiplayer //! @return whether or not the entity was migrated bool GetWasMigrated() const; - //! Gets the current value of TakeOwnership. - //! @return the current value of TakeOwnership - bool GetTakeOwnership() const; - //! Gets the current value of HasValidPrefabId. //! @return the current value of HasValidPrefabId bool GetHasValidPrefabId() const; @@ -110,7 +105,6 @@ namespace Multiplayer NetEntityId m_entityId = InvalidNetEntityId; bool m_isDelete = false; bool m_wasMigrated = false; - bool m_takeOwnership = false; bool m_hasValidPrefabId = false; PrefabEntityId m_prefabEntityId; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 6f65d01e50..91ca1bff84 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -47,6 +47,9 @@ namespace Multiplayer , m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); }) , m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); }) { + // Set up our remote host identifier, we use the IP address of the host + m_remoteHostId = connection.GetRemoteAddress(); + // Our max payload size is whatever is passed in, minus room for a udp packetheader m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead; @@ -65,11 +68,6 @@ namespace Multiplayer GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler); } - void EntityReplicationManager::SetRemoteHostId(const HostId& hostId) - { - m_remoteHostId = hostId; - } - const HostId& EntityReplicationManager::GetRemoteHostId() const { return m_remoteHostId; @@ -516,10 +514,6 @@ namespace Multiplayer AZLOG(NET_RepDeletes, "Deleting replicater for entity id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str()); } } - else - { - shouldDeleteEntity = updateMessage.GetTakeOwnership(); - } // Handle entity cleanup if (shouldDeleteEntity) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index 05001b5960..807bbf9e21 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -296,10 +296,10 @@ namespace Multiplayer bool EntityReplicator::OwnsReplicatorLifetime() const { bool ret(false); - if (GetBoundLocalNetworkRole() == NetEntityRole::Authority - || (GetBoundLocalNetworkRole() == NetEntityRole::Server + if (GetBoundLocalNetworkRole() == NetEntityRole::Authority // Authority always owns lifetime + || (GetBoundLocalNetworkRole() == NetEntityRole::Server // Server also owns lifetime if the remote endpoint is a client of some form && (GetRemoteNetworkRole() == NetEntityRole::Client - || GetRemoteNetworkRole() == NetEntityRole::Autonomous))) + || GetRemoteNetworkRole() == NetEntityRole::Autonomous))) { ret = true; } @@ -309,10 +309,9 @@ namespace Multiplayer bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const { bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server) - && (GetRemoteNetworkRole() == NetEntityRole::Authority); + && (GetRemoteNetworkRole() == NetEntityRole::Authority); bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client) - || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); - + || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); return isServer || isClient; } @@ -477,14 +476,14 @@ namespace Multiplayer WasMigrated() ? 1 : 0, m_replicationManager.GetRemoteHostId().GetString().c_str() ); - return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated(), m_propertyPublisher->IsRemoteReplicatorEstablished()); + return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated()); } NetBindComponent* netBindComponent = GetNetBindComponent(); - //const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished(); + const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished(); NetworkEntityUpdateMessage updateMessage(GetRemoteNetworkRole(), GetEntityHandle().GetNetEntityId()); - //if (sendSliceName) + if (sendSliceName) { updateMessage.SetPrefabEntityId(netBindComponent->GetPrefabEntityId()); } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityUpdateMessage.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityUpdateMessage.cpp index 4a2f12ce17..d635dbaf80 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityUpdateMessage.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityUpdateMessage.cpp @@ -18,7 +18,6 @@ namespace Multiplayer , m_entityId(rhs.m_entityId) , m_isDelete(rhs.m_isDelete) , m_wasMigrated(rhs.m_wasMigrated) - , m_takeOwnership(rhs.m_takeOwnership) , m_hasValidPrefabId(rhs.m_hasValidPrefabId) , m_prefabEntityId(rhs.m_prefabEntityId) , m_data(AZStd::move(rhs.m_data)) @@ -31,7 +30,6 @@ namespace Multiplayer , m_entityId(rhs.m_entityId) , m_isDelete(rhs.m_isDelete) , m_wasMigrated(rhs.m_wasMigrated) - , m_takeOwnership(rhs.m_takeOwnership) , m_hasValidPrefabId(rhs.m_hasValidPrefabId) , m_prefabEntityId(rhs.m_prefabEntityId) { @@ -58,11 +56,10 @@ namespace Multiplayer ; } - NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityId entityId, bool wasMigrated, bool takeOwnership) + NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityId entityId, bool wasMigrated) : m_entityId(entityId) , m_isDelete(true) , m_wasMigrated(wasMigrated) - , m_takeOwnership(takeOwnership) { // this is a delete entity message c-tor } @@ -73,7 +70,6 @@ namespace Multiplayer m_entityId = rhs.m_entityId; m_isDelete = rhs.m_isDelete; m_wasMigrated = rhs.m_wasMigrated; - m_takeOwnership = rhs.m_takeOwnership; m_hasValidPrefabId = rhs.m_hasValidPrefabId; m_prefabEntityId = rhs.m_prefabEntityId; m_data = AZStd::move(rhs.m_data); @@ -86,7 +82,6 @@ namespace Multiplayer m_entityId = rhs.m_entityId; m_isDelete = rhs.m_isDelete; m_wasMigrated = rhs.m_wasMigrated; - m_takeOwnership = rhs.m_takeOwnership; m_hasValidPrefabId = rhs.m_hasValidPrefabId; m_prefabEntityId = rhs.m_prefabEntityId; if (rhs.m_data != nullptr) @@ -104,7 +99,6 @@ namespace Multiplayer && (m_entityId == rhs.m_entityId) && (m_isDelete == rhs.m_isDelete) && (m_wasMigrated == rhs.m_wasMigrated) - && (m_takeOwnership == rhs.m_takeOwnership) && (m_hasValidPrefabId == rhs.m_hasValidPrefabId) && (m_prefabEntityId == rhs.m_prefabEntityId)); } @@ -160,11 +154,6 @@ namespace Multiplayer return m_wasMigrated; } - bool NetworkEntityUpdateMessage::GetTakeOwnership() const - { - return m_takeOwnership; - } - bool NetworkEntityUpdateMessage::GetHasValidPrefabId() const { return m_hasValidPrefabId; @@ -210,17 +199,15 @@ namespace Multiplayer serializer.Serialize(m_entityId, "EntityId"); // Use the upper 4 bits for boolean flags, and the lower 4 bits for the network role - uint8_t networkTypeAndFlags = (m_isDelete ? 0x80 : 0x00) - | (m_wasMigrated ? 0x40 : 0x00) - | (m_takeOwnership ? 0x20 : 0x00) + uint8_t networkTypeAndFlags = (m_isDelete ? 0x40 : 0x00) + | (m_wasMigrated ? 0x20 : 0x00) | (m_hasValidPrefabId ? 0x10 : 0x00) | static_cast(m_networkRole); if (serializer.Serialize(networkTypeAndFlags, "TypeAndFlags")) { - m_isDelete = (networkTypeAndFlags & 0x80) == 0x80; - m_wasMigrated = (networkTypeAndFlags & 0x40) == 0x40; - m_takeOwnership = (networkTypeAndFlags & 0x20) == 0x20; + m_isDelete = (networkTypeAndFlags & 0x40) == 0x40; + m_wasMigrated = (networkTypeAndFlags & 0x20) == 0x20; m_hasValidPrefabId = (networkTypeAndFlags & 0x10) == 0x10; m_networkRole = static_cast(networkTypeAndFlags & 0x0F); } From d05d5d1e976f6b7c429c51c800070d1bdbf79676 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:31:23 -0400 Subject: [PATCH 008/152] Hierarchy components optimizations Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../NetworkHierarchyChildComponent.h | 2 - .../NetworkHierarchyRootComponent.h | 18 +--- .../NetworkHierarchyChildComponent.cpp | 68 +++++++------- .../NetworkHierarchyRootComponent.cpp | 88 ++++++++----------- .../Code/Tests/ServerHierarchyTests.cpp | 32 ++++++- 5 files changed, 103 insertions(+), 105 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h index 544fb3d6cb..a31cd90efd 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -64,10 +64,8 @@ namespace Multiplayer private: AZ::ChildChangedEvent::Handler m_childChangedHandler; - AZ::ParentChangedEvent::Handler m_parentChangedHandler; void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child); - void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent); //! Points to the top level root. AZ::Entity* m_rootEntity = nullptr; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 674dc6114d..7c9b0d34cd 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -78,22 +78,12 @@ namespace Multiplayer //! Rebuilds hierarchy starting from this root component's entity. void RebuildHierarchy(); - //! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy - //! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method, - //! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities. - //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. - //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise - bool RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount); - - //! @param entity Add the child entity and any of its relevant children to the hierarchy - //! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method, - //! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities. - //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. - //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise - bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount); + //! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy. + //! Builds the hierarchy using breadth-first iterative method. + void InternalBuildHierarchyList(AZ::Entity* underEntity); void SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity); - + //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. bool m_isHierarchyEnabled = true; diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index 3124eccaa8..5d9b4a40ad 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -55,7 +55,6 @@ namespace Multiplayer NetworkHierarchyChildComponent::NetworkHierarchyChildComponent() : m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); }) - , m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); }) , m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); }) { @@ -75,7 +74,6 @@ namespace Multiplayer if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) { transformComponent->BindChildChangedEventHandler(m_childChangedHandler); - transformComponent->BindParentChangedEventHandler(m_parentChangedHandler); } } @@ -133,29 +131,33 @@ namespace Multiplayer void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) { - m_rootEntity = hierarchyRoot; - if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + if (m_rootEntity != hierarchyRoot) { - NetworkHierarchyChildComponentController* controller = static_cast(GetController()); - if (m_rootEntity) + m_rootEntity = hierarchyRoot; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) { - const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); - controller->SetHierarchyRoot(netRootId); + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + if (m_rootEntity) + { + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); + controller->SetHierarchyRoot(netRootId); - m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); + } + else + { + controller->SetHierarchyRoot(InvalidNetEntityId); + + m_networkHierarchyLeaveEvent.Signal(); + } } - else + + if (m_rootEntity == nullptr) { - controller->SetHierarchyRoot(InvalidNetEntityId); - - m_networkHierarchyLeaveEvent.Signal(); + NotifyChildrenHierarchyDisbanded(); } } - - if (m_rootEntity == nullptr) - { - NotifyChildrenHierarchyDisbanded(); - } } void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child) @@ -169,17 +171,6 @@ namespace Multiplayer } } - void NetworkHierarchyChildComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, [[maybe_unused]] AZ::EntityId parent) - { - if (m_rootEntity) - { - if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) - { - root->RebuildHierarchy(); - } - } - } - void NetworkHierarchyChildComponent::OnHierarchyRootNetIdChanged(NetEntityId rootNetId) { ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(rootNetId); @@ -202,19 +193,24 @@ namespace Multiplayer void NetworkHierarchyChildComponent::NotifyChildrenHierarchyDisbanded() { + AZ::ComponentApplicationRequests* componentApplication = AZ::Interface::Get(); + AZStd::vector allChildren; AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren); for (const AZ::EntityId& childEntityId : allChildren) { - if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(childEntityId)) + if (const AZ::Entity* childEntity = componentApplication->FindEntity(childEntityId)) { - if (auto* hierarchyChildComponent = childEntity->FindComponent()) + for (Component* component : childEntity->GetComponents()) { - hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr); - } - else if (auto* hierarchyRootComponent = childEntity->FindComponent()) - { - hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr); + if (component->GetUnderlyingComponentType() == NetworkHierarchyChildComponent::TYPEINFO_Uuid()) + { + static_cast(component)->SetTopLevelHierarchyRootEntity(nullptr); + } + else if (component->GetUnderlyingComponentType() == NetworkHierarchyRootComponent::TYPEINFO_Uuid()) + { + static_cast(component)->SetTopLevelHierarchyRootEntity(nullptr); + } } } } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 4a5e9ce8d2..5011e3f17b 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -203,10 +203,9 @@ namespace Multiplayer AZStd::vector previousEntities; m_hierarchicalEntities.swap(previousEntities); - m_hierarchicalEntities.push_back(GetEntity()); // Add the root. + m_hierarchicalEntities.reserve(bg_hierarchyEntityMaxLimit); - uint32_t currentEntityCount = aznumeric_cast(m_hierarchicalEntities.size()); - RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount); + InternalBuildHierarchyList(GetEntity()); bool hierarchyChanged = false; @@ -244,6 +243,43 @@ namespace Multiplayer } } + void NetworkHierarchyRootComponent::InternalBuildHierarchyList(AZ::Entity* underEntity) + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + + AZStd::deque candidates; + candidates.push_back(underEntity); + + while (!candidates.empty()) + { + AZ::Entity* candidate = candidates.front(); + candidates.pop_front(); + + if (candidate) + { + auto* hierarchyChildComponent = candidate->FindComponent(); + auto* hierarchyRootComponent = candidate->FindComponent(); + + if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) || + (hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled())) + { + m_hierarchicalEntities.push_back(candidate); + + if (m_hierarchicalEntities.size() >= bg_hierarchyEntityMaxLimit) + { + return; + } + + const AZStd::vector allChildren = candidate->GetTransform()->GetChildren(); + for (const AZ::EntityId& newChildId : allChildren) + { + candidates.push_back(componentApplicationRequests->FindEntity(newChildId)); + } + } + } + } + } + void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity) { if (auto* hierarchyChildComponent = childEntity->FindComponent()) @@ -256,52 +292,6 @@ namespace Multiplayer } } - bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount) - { - AZStd::vector allChildren; - AZ::TransformBus::EventResult(allChildren, underEntity, &AZ::TransformBus::Events::GetChildren); - - for (const AZ::EntityId& newChildId : allChildren) - { - if (!RecursiveAttachHierarchicalChild(newChildId, currentEntityCount)) - { - return false; - } - } - - return true; - } - - bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount) - { - if (currentEntityCount >= bg_hierarchyEntityMaxLimit) - { - AZLOG_WARN("Entity %s is trying to build a network hierarchy that is too large. bg_hierarchyEntityMaxLimit is currently set to (%u)", - GetEntity()->GetName().c_str(), static_cast(bg_hierarchyEntityMaxLimit)); - return false; - } - - if (AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(entity)) - { - auto* hierarchyChildComponent = childEntity->FindComponent(); - auto* hierarchyRootComponent = childEntity->FindComponent(); - - if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) || - (hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled())) - { - m_hierarchicalEntities.push_back(childEntity); - ++currentEntityCount; - - if (!RecursiveAttachHierarchicalEntities(entity, currentEntityCount)) - { - return false; - } - } - } - - return true; - } - void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) { m_rootEntity = hierarchyRoot; diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp index b8e892e71a..1b5aa31780 100644 --- a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -394,8 +394,8 @@ namespace Multiplayer m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); // remake the hierarchy - m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); - m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); EXPECT_EQ( m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), @@ -406,6 +406,17 @@ namespace Multiplayer m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); } + TEST_F(ServerDeepHierarchyTests, ReattachMiddleChildRebuildInvokedTwice) + { + MockNetworkHierarchyCallbackHandler mock; + EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root->m_entity->GetId())).Times(2); + + m_root->m_entity->FindComponent()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler); + + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + } + /* * Parent -> Child -> Child Of Child * -> Child2 -> Child Of Child2 @@ -533,11 +544,11 @@ namespace Multiplayer ); EXPECT_EQ( m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], - m_childOfChild->m_entity.get() + m_child2->m_entity.get() ); EXPECT_EQ( m_root->m_entity->FindComponent()->GetHierarchicalEntities()[3], - m_child2->m_entity.get() + m_childOfChild->m_entity.get() ); EXPECT_EQ( m_root->m_entity->FindComponent()->GetHierarchicalEntities()[4], @@ -1230,4 +1241,17 @@ namespace Multiplayer 3 ); } + + TEST_F(ServerHierarchyWithThreeRoots, ReattachMiddleChildWhileLastChildGetsLeaveEventOnce) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + MockNetworkHierarchyCallbackHandler mock; + EXPECT_CALL(mock, OnNetworkHierarchyLeave()); + + m_childOfChild3->m_entity->FindComponent()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler); + + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + } } From 0a11e8ffc5241df5a474f47d57fc9368983f4019 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Tue, 5 Oct 2021 11:26:31 -0400 Subject: [PATCH 009/152] Optimization for looking up components. Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../NetworkHierarchyChildComponent.cpp | 15 ++++---- .../NetworkHierarchyRootComponent.cpp | 36 +++++++++++++++---- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index 5d9b4a40ad..1f32387893 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -201,16 +201,13 @@ namespace Multiplayer { if (const AZ::Entity* childEntity = componentApplication->FindEntity(childEntityId)) { - for (Component* component : childEntity->GetComponents()) + if (auto* hierarchyChildComponent = childEntity->FindComponent()) { - if (component->GetUnderlyingComponentType() == NetworkHierarchyChildComponent::TYPEINFO_Uuid()) - { - static_cast(component)->SetTopLevelHierarchyRootEntity(nullptr); - } - else if (component->GetUnderlyingComponentType() == NetworkHierarchyRootComponent::TYPEINFO_Uuid()) - { - static_cast(component)->SetTopLevelHierarchyRootEntity(nullptr); - } + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr); + } + else if (auto* hierarchyRootComponent = childEntity->FindComponent()) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr); } } } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 5011e3f17b..eeb61ef450 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -173,6 +173,29 @@ namespace Multiplayer } } + static AZStd::tuple GetHierarchyComponents(const AZ::Entity* entity) + { + NetworkHierarchyChildComponent* childComponent = nullptr; + NetworkHierarchyRootComponent* rootComponent = nullptr; + + for (AZ::Component* component : entity->GetComponents()) + { + if (component->GetUnderlyingComponentType() == NetworkHierarchyChildComponent::TYPEINFO_Uuid()) + { + childComponent = static_cast(component); + break; + } + + if (component->GetUnderlyingComponentType() == NetworkHierarchyRootComponent::TYPEINFO_Uuid()) + { + rootComponent = static_cast(component); + break; + } + } + + return AZStd::tie(rootComponent, childComponent); + } + void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) { // If the parent is part of a hierarchy, it will detect this entity as a new child and rebuild hierarchy. @@ -181,8 +204,8 @@ namespace Multiplayer if (AZ::Entity* parentEntity = AZ::Interface::Get()->FindEntity(newParent)) { - if (parentEntity->FindComponent() == nullptr && - parentEntity->FindComponent() == nullptr) + auto [rootComponent, childComponent] = GetHierarchyComponents(parentEntity); + if (rootComponent == nullptr && childComponent == nullptr) { RebuildHierarchy(); } @@ -257,8 +280,7 @@ namespace Multiplayer if (candidate) { - auto* hierarchyChildComponent = candidate->FindComponent(); - auto* hierarchyRootComponent = candidate->FindComponent(); + auto [hierarchyRootComponent, hierarchyChildComponent] = GetHierarchyComponents(candidate); if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) || (hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled())) @@ -282,11 +304,13 @@ namespace Multiplayer void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity) { - if (auto* hierarchyChildComponent = childEntity->FindComponent()) + auto [hierarchyRootComponent, hierarchyChildComponent] = GetHierarchyComponents(childEntity); + + if (hierarchyChildComponent) { hierarchyChildComponent->SetTopLevelHierarchyRootEntity(root); } - else if (auto* hierarchyRootComponent = childEntity->FindComponent()) + else if (hierarchyRootComponent) { hierarchyRootComponent->SetTopLevelHierarchyRootEntity(root); } From a58325e691209cbe2596fd11c4919c2f4c41e0d1 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 7 Oct 2021 00:38:07 +0100 Subject: [PATCH 010/152] Enabled multi-entities player prefabs. Added updating replication set for hierarchy children. PR feedback addressing Signed-off-by: pereslav --- .../NetworkHierarchyRootComponent.h | 6 +-- .../Multiplayer/NetworkInput/NetworkInput.h | 25 ---------- ...rkHierarchyRootComponent.AutoComponent.xml | 6 ++- .../NetworkHierarchyRootComponent.cpp | 30 ++++++------ .../Source/MultiplayerSystemComponent.cpp | 38 ++++++++------ .../Code/Source/MultiplayerSystemComponent.h | 2 +- .../Code/Source/NetworkInput/NetworkInput.cpp | 49 ------------------- .../Source/NetworkInput/NetworkInputChild.h | 5 +- .../ServerToClientReplicationWindow.cpp | 39 ++++++++------- .../ServerToClientReplicationWindow.h | 3 +- 10 files changed, 69 insertions(+), 134 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 414f488f88..a31f47317a 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -29,6 +29,8 @@ namespace Multiplayer , public NetworkHierarchyRequestBus::Handler { friend class NetworkHierarchyChildComponent; + friend class NetworkHierarchyRootComponentController; + friend class ServerToClientReplicationWindow; public: AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase); @@ -57,8 +59,6 @@ namespace Multiplayer void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} - const AZStd::vector& GetHierarchicalEntitiesRef() const; - protected: void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); @@ -103,7 +103,7 @@ namespace Multiplayer }; - //! NetworkCharacterComponentController + //! NetworkHierarchyRootComponentController //! This is the network controller for NetworkHierarchyRootComponent. //! Class provides the ability to process input for hierarchies. class NetworkHierarchyRootComponentController diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h index 4575d88e24..1e02d6bf56 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h @@ -29,7 +29,6 @@ namespace Multiplayer friend class NetworkInputMigrationVector; friend class NetworkInputHistory; friend class NetworkInputChild; - friend class NetworkSubInput; NetworkInput(const NetworkInput&); NetworkInput& operator= (const NetworkInput&); @@ -81,28 +80,4 @@ namespace Multiplayer ConstNetworkEntityHandle m_owner; bool m_wasAttached = false; }; - - // Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify dependent entities - // Since it is possible to for the Client/Server to disagree about the state of related entities, this input encodes the entity that - // is associated with it. - class NetworkSubInput final - { - public: - NetworkSubInput() = default; - NetworkSubInput(const NetworkSubInput&) = default; - NetworkSubInput(const ConstNetworkEntityHandle& entityHandle); - - NetworkSubInput& operator=(const NetworkSubInput&) = default; - - void Attach(const ConstNetworkEntityHandle& entityHandle); - const ConstNetworkEntityHandle& GetOwner() const; - NetworkInput& GetNetworkInput(); - const NetworkInput& GetNetworkInput() const; - - bool Serialize(AzNetworking::ISerializer& serializer); - private: - ConstNetworkEntityHandle m_owner; - NetworkInput m_networkInput; - }; - } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml index c93f300da3..f8df21a220 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -8,9 +8,11 @@ OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + + - - + + diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index ca4bbec66b..89db2180a5 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -139,11 +139,6 @@ namespace Multiplayer return m_hierarchicalEntities; } - const AZStd::vector& NetworkHierarchyRootComponent::GetHierarchicalEntitiesRef() const - { - return m_hierarchicalEntities; - } - AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const { if (m_rootEntity) @@ -361,7 +356,10 @@ namespace Multiplayer return; } - const AZStd::vector& entities = component.GetHierarchicalEntitiesRef(); + INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); + AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + + const AZStd::vector& entities = component.m_hierarchicalEntities; auto* networkInput = input.FindComponentInput(); networkInput->m_childInputs.clear(); @@ -371,21 +369,23 @@ namespace Multiplayer { if(child == component.GetEntity()) { - return; // Avoid infinite recursion + continue; // Avoid infinite recursion } - auto* netComp = child->FindComponent(); - AZ_Assert(netComp, "No NetSystemComponent, this should be impossible"); + NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId); + NetBindComponent* netComp = childEntityHandle.GetNetBindComponent(); + + AZ_Assert(netComp, "No NetBindComponent, this should be impossible"); // Validate we still have a controller and we aren't in the middle of removing them if (netComp->HasController()) { - ConstNetworkEntityHandle childEntityHandle = netComp->GetEntityHandle(); - NetworkSubInput subInput; + NetworkInputChild subInput; subInput.Attach(childEntityHandle); subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); netComp->CreateInput(subInput.GetNetworkInput(), deltaTime); - + // make sure our input sub commands have the same time as the original subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); networkInput->m_childInputs.emplace_back(subInput); @@ -408,13 +408,13 @@ namespace Multiplayer if (auto* networkInput = input.FindComponentInput()) { - for (NetworkSubInput& subInput : networkInput->m_childInputs) + for (NetworkInputChild& subInput : networkInput->m_childInputs) { const ConstNetworkEntityHandle& childEntity = subInput.GetOwner(); if (auto* localChild = childEntity.GetEntity()) { - auto* netComp = localChild->FindComponent(); - AZ_Assert(netComp, "No NetSystemComponent, this should be impossible"); + auto* netComp = childEntity.GetNetBindComponent(); + AZ_Assert(netComp, "No NetBindComponent, this should be impossible"); // We do not rewind entity role changes, so make sure we are the correct role prior to processing if (netComp->HasController()) { diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 37e87ace84..bda824e953 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -665,12 +665,21 @@ namespace Multiplayer if (GetAgentType() == MultiplayerAgentType::ClientServer || GetAgentType() == MultiplayerAgentType::DedicatedServer) { - NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); - if (controlledEntity.Exists()) + INetworkEntityManager::EntityList entityList = SpawnDefaultPlayerPrefab(); + for (auto& netEntity : entityList) { - controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); + if (netEntity.Exists()) + { + netEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); + } + netEntity.Activate(); + } + + NetworkEntityHandle controlledEntity; + if (entityList.size() > 0) + { + controlledEntity = entityList[0]; } - controlledEntity.Activate(); connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); @@ -780,12 +789,16 @@ namespace Multiplayer // Spawn the default player for this host since the host is also a player (not a dedicated server) if (m_agentType == MultiplayerAgentType::ClientServer) { - NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); - if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent()) + INetworkEntityManager::EntityList entityList = SpawnDefaultPlayerPrefab(); + + for (NetworkEntityHandle controlledEntity : entityList) { - controlledEntityNetBindComponent->SetAllowAutonomy(true); + if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent()) + { + controlledEntityNetBindComponent->SetAllowAutonomy(true); + } + controlledEntity.Activate(); } - controlledEntity.Activate(); } AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType)); @@ -1028,17 +1041,12 @@ namespace Multiplayer } } - NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab() + INetworkEntityManager::EntityList MultiplayerSystemComponent::SpawnDefaultPlayerPrefab() { PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str())); INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate); - NetworkEntityHandle controlledEntity; - if (entityList.size() > 0) - { - controlledEntity = entityList[0]; - } - return controlledEntity; + return entityList; } void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 316ddd41d5..495642b043 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -139,7 +139,7 @@ namespace Multiplayer void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds); void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom); void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector& commands); - NetworkEntityHandle SpawnDefaultPlayerPrefab(); + INetworkEntityManager::EntityList SpawnDefaultPlayerPrefab(); AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session"); diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp index 2bbda48551..35ca02bfba 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp @@ -188,53 +188,4 @@ namespace Multiplayer } m_wasAttached = rhs.m_wasAttached; } - - NetworkSubInput::NetworkSubInput(const ConstNetworkEntityHandle& entityHandle) - : m_owner(entityHandle) - { - Attach(m_owner); - } - - void NetworkSubInput::Attach(const ConstNetworkEntityHandle& entityHandle) - { - m_owner = entityHandle; - if (auto* localEnt = entityHandle.GetEntity()) - { - NetBindComponent* netBindComponent = localEnt->FindComponent(); - if (netBindComponent) - { - m_networkInput.AttachNetBindComponent(netBindComponent); - } - } - } - - const ConstNetworkEntityHandle& NetworkSubInput::GetOwner() const - { - return m_owner; - } - - NetworkInput& NetworkSubInput::GetNetworkInput() - { - return m_networkInput; - } - - const NetworkInput& NetworkSubInput::GetNetworkInput() const - { - return m_networkInput; - } - - bool NetworkSubInput::Serialize(AzNetworking::ISerializer& serializer) - { - NetEntityId tmpId = m_owner.GetNetEntityId(); - serializer.Serialize(tmpId, "OwnerId"); - - if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) - { - m_owner = AZ::Interface::Get()->GetEntity(tmpId); - } - - serializer.Serialize(m_networkInput, "NetInput"); - return serializer.IsValid(); - } - } diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h index 98778cce17..471d86f718 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h @@ -12,10 +12,7 @@ namespace Multiplayer { - //! Max number of entities that can be children of our netbound player entity. - static constexpr uint32_t MaxEntityHierarchyChildren = 16; - - //! Used by the EntityHierarchyComponent. This component allows the gameplay programmer to specify inputs for dependent entities. + //! Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify inputs for dependent entities. //! Since it is possible to for the Client/Server to disagree about the state of related entities, //! this network input encodes the entity that is associated with it. class NetworkInputChild diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp index 9d9cc74d2b..d8195bf29a 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -174,11 +175,11 @@ namespace Multiplayer // Note: Do not add any Client entities after this point, otherwise you stomp over the Autonomous mode m_replicationSet[m_controlledEntity] = { NetEntityRole::Autonomous, 1.0f }; // Always replicate autonomous entities - //auto hierarchyController = FindController(m_ControlledEntity); - //if (hierarchyController != nullptr) - //{ - // CollectControlledEntitiesRecursive(m_replicationSet, *hierarchyController); - //} + auto* hierarchyComponent = m_controlledEntity.FindComponent(); + if (hierarchyComponent != nullptr) + { + UpdateHierarchyReplicationSet(m_replicationSet, *hierarchyComponent); + } } AzNetworking::PacketId ServerToClientReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) @@ -326,18 +327,18 @@ namespace Multiplayer } } - //void ServerToClientReplicationWindow::CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController) - //{ - // auto controlledEnts = hierarchyController.GetChildrenRelatedEntities(); - // for (auto& controlledEnt : controlledEnts) - // { - // AZ_Assert(controlledEnt != nullptr, "We have lost a controlled entity unexpectedly"); - // replicationSet[controlledEnt.GetConstEntity()] = EntityReplicationData(EntityNetworkRoleT::e_Autonomous, EntityPrioritySystem::k_MaxPriority); // Always replicate controlled entities - // auto hierarchyController = controlledEnt.FindController(); - // if (hierarchyController != nullptr) - // { - // CollectControlledEntitiesRecursive(replicationSet, *hierarchyController); - // } - // } - //} + void ServerToClientReplicationWindow::UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent) + { + INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); + AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + + for (const AZ::Entity* controlledEntity : hierarchyComponent.m_hierarchicalEntities) + { + NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId()); + ConstNetworkEntityHandle controlledEntityHandle = networkEntityManager->GetEntity(controlledNetEntitydId); + AZ_Assert(controlledEntityHandle != nullptr, "We have lost a controlled entity unexpectedly"); + + replicationSet[controlledEntityHandle] = { NetEntityRole::Autonomous, 1.0f }; + } + } } diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h index b034bde90c..8816209d7b 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h @@ -20,6 +20,7 @@ namespace Multiplayer { class NetSystemComponent; + class NetworkHierarchyRootComponent; class ServerToClientReplicationWindow : public IReplicationWindow @@ -56,7 +57,7 @@ namespace Multiplayer void OnEntityActivated(AZ::Entity* entity); void OnEntityDeactivated(AZ::Entity* entity); - //void CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController); + void UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent); void EvaluateConnection(); void AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, float distanceSquared); From 18340f2b1bc7626daf91d5fea4282fc3433947eb Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Wed, 6 Oct 2021 18:57:45 -0700 Subject: [PATCH 011/152] Changes to get client migration partially functional Signed-off-by: kberg-amzn --- .../Multiplayer/Components/NetBindComponent.h | 4 +- .../Code/Include/Multiplayer/IMultiplayer.h | 16 +- .../Include/Multiplayer/MultiplayerTypes.h | 19 +++ .../EntityReplicationManager.h | 5 + .../AutoGen/Multiplayer.AutoPackets.xml | 1 + .../Source/Components/NetBindComponent.cpp | 4 +- .../ServerToClientConnectionData.cpp | 39 +++-- .../ServerToClientConnectionData.h | 7 +- .../Source/MultiplayerSystemComponent.cpp | 137 +++++++++++++----- .../Code/Source/MultiplayerSystemComponent.h | 10 +- .../EntityReplicationManager.cpp | 40 ++++- .../EntityReplication/EntityReplicator.cpp | 129 ++++++----------- .../NetworkEntity/NetworkEntityManager.cpp | 12 +- .../ServerToClientReplicationWindow.cpp | 16 +- .../ServerToClientReplicationWindow.h | 2 - 15 files changed, 273 insertions(+), 168 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index f604102e37..b22c8dc51c 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -32,7 +32,7 @@ namespace Multiplayer using EntityStopEvent = AZ::Event; using EntityDirtiedEvent = AZ::Event<>; using EntitySyncRewindEvent = AZ::Event<>; - using EntityServerMigrationEvent = AZ::Event; + using EntityServerMigrationEvent = AZ::Event; using EntityPreRenderEvent = AZ::Event; using EntityCorrectionEvent = AZ::Event<>; @@ -113,7 +113,7 @@ namespace Multiplayer void MarkDirty(); void NotifyLocalChanges(); void NotifySyncRewindState(); - void NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId); + void NotifyServerMigration(const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId); void NotifyPreRender(float deltaTime); void NotifyCorrection(); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 32af39a43b..b4be69ed4c 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -45,7 +45,7 @@ namespace Multiplayer using ClientMigrationStartEvent = AZ::Event; using ClientMigrationEndEvent = AZ::Event<>; using ClientDisconnectedEvent = AZ::Event<>; - using NotifyClientMigrationEvent = AZ::Event; + using NotifyClientMigrationEvent = AZ::Event; using NotifyEntityMigrationEvent = AZ::Event; using ConnectionAcquiredEvent = AZ::Event; using SessionInitEvent = AZ::Event; @@ -131,10 +131,11 @@ namespace Multiplayer virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0; //! Signals a NotifyClientMigrationEvent with the provided parameters. - //! @param hostId the host id of the host the client is migrating to - //! @param userIdentifier the user identifier the client will provide the new host to validate identity - //! @param lastClientInputId the last processed clientInputId by the current host - virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) = 0; + //! @param hostId the host id of the host the client is migrating to + //! @param userIdentifier the user identifier the client will provide the new host to validate identity + //! @param lastClientInputId the last processed clientInputId by the current host + //! @param controlledEntityId the entityId of the clients autonomous entity + virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) = 0; //! Signals a NotifyEntityMigrationEvent with the provided parameters. //! @param entityHandle the network entity handle of the entity being migrated @@ -176,6 +177,11 @@ namespace Multiplayer //! @return pointer to the filtered entity manager, or nullptr if not set virtual IFilterEntityManager* GetFilterEntityManager() = 0; + //! Registers a temp userId to allow a host to look up a players controlled entity in the event of a rejoin or migration event. + //! @param temporaryUserIdentifier the temporary user identifier used to identify a player across hosts + //! @param controlledEntityId the controlled entityId of the players autonomous entity + virtual void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) = 0; + //! Enables or disables automatic instantiation of netbound entities. //! This setting is controlled by the networking layer and should not be touched //! If enabled, netbound entities will instantiate as spawnables are loaded into the game world, generally true for the server diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h index 96035083d8..8284dd7010 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h @@ -68,6 +68,7 @@ namespace Multiplayer Server, // A simulated proxy on a server Authority // An authoritative proxy on a server (full authority) }; + const char* GetEnumString(NetEntityRole value); enum class ComponentSerializationType : uint8_t { @@ -113,6 +114,24 @@ namespace Multiplayer bool Serialize(AzNetworking::ISerializer& serializer); }; + inline const char* GetEnumString(NetEntityRole value) + { + switch (value) + { + case NetEntityRole::InvalidRole: + return "InvalidRole"; + case NetEntityRole::Client: + return "Client"; + case NetEntityRole::Autonomous: + return "Autonomous"; + case NetEntityRole::Server: + return "Server"; + case NetEntityRole::Authority: + return "Authority"; + } + return "Unknown"; + } + inline PrefabEntityId::PrefabEntityId(AZ::Name name, uint32_t entityOffset) : m_prefabName(name) , m_entityOffset(entityOffset) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h index 10346ad777..83f9e238f5 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -57,6 +57,10 @@ namespace Multiplayer EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode); ~EntityReplicationManager() = default; + //! Used to override during client migration if your host has a specially assigned publically routable address. + //! @param remoteHostId the publically routable address to use in place of the remote HostId + void SetMigrateHostId(const HostId& remoteHostId); + const HostId& GetMigrateHostId() const; const HostId& GetRemoteHostId() const; void ActivatePendingEntities(); @@ -207,6 +211,7 @@ namespace Multiplayer AZ::TimeMs m_entityPendingRemovalMs = AZ::TimeMs{ 0 }; AZ::TimeMs m_frameTimeMs = AZ::TimeMs{ 0 }; HostId m_remoteHostId = InvalidHostId; + HostId m_migrateHostId = InvalidHostId; uint32_t m_maxRemoteEntitiesPendingCreationCount = AZStd::numeric_limits::max(); uint32_t m_maxPayloadSize = 0; Mode m_updateMode = Mode::Invalid; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 091043f034..a5bf169fb4 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -9,6 +9,7 @@ + diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index fbf83b6e6c..2e451793ed 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -394,9 +394,9 @@ namespace Multiplayer m_syncRewindEvent.Signal(); } - void NetBindComponent::NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId) + void NetBindComponent::NotifyServerMigration(const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId) { - m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId); + m_entityServerMigrationEvent.Signal(m_netEntityHandle, remoteHostId, migrateHostId, connectionId); } void NetBindComponent::NotifyPreRender(float deltaTime) diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp index a9b8e03126..29be64ac07 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp @@ -23,22 +23,16 @@ namespace Multiplayer ServerToClientConnectionData::ServerToClientConnectionData ( AzNetworking::IConnection* connection, - AzNetworking::IConnectionListener& connectionListener, - NetworkEntityHandle controlledEntity + AzNetworking::IConnectionListener& connectionListener ) : m_connection(connection) , m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); }) - , m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId) { OnControlledEntityMigration(entityHandle, remoteHostId, connectionId); }) - , m_controlledEntity(controlledEntity) + , m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId) + { + OnControlledEntityMigration(entityHandle, remoteHostId, migrateHostId, connectionId); + }) , m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient) { - NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent(); - if (netBindComponent != nullptr) - { - netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler); - netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler); - } - m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(sv_ClientMaxRemoteEntitiesPendingCreationCount); m_entityReplicationManager.SetEntityPendingRemovalMs(sv_ClientEntityReplicatorPendingRemovalTimeMs); } @@ -54,6 +48,20 @@ namespace Multiplayer m_controlledEntityRemovedHandler.Disconnect(); } + void ServerToClientConnectionData::SetControlledEntity(NetworkEntityHandle primaryPlayerEntity) + { + m_controlledEntityRemovedHandler.Disconnect(); + m_controlledEntityMigrationHandler.Disconnect(); + + m_controlledEntity = primaryPlayerEntity; + NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent(); + if (netBindComponent != nullptr) + { + netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler); + netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler); + } + } + ConnectionDataType ServerToClientConnectionData::GetConnectionDataType() const { return ConnectionDataType::ServerToClient; @@ -94,7 +102,8 @@ namespace Multiplayer void ServerToClientConnectionData::OnControlledEntityMigration ( [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, - [[maybe_unused]] const HostId& remoteHostId, + const HostId& remoteHostId, + const HostId& migrateHostId, [[maybe_unused]] AzNetworking::ConnectionId connectionId ) { @@ -109,13 +118,13 @@ namespace Multiplayer } // Generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration - const uint64_t randomUserIdentifier = AzNetworking::CryptoRand64(); + const uint64_t temporaryUserIdentifier = AzNetworking::CryptoRand64(); // Tell the new host that a client is about to (re)join - GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, randomUserIdentifier, migratedClientInputId); + GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, temporaryUserIdentifier, migratedClientInputId, m_controlledEntity.GetNetEntityId()); // Tell the client who to join - MultiplayerPackets::ClientMigration clientMigration(remoteHostId, randomUserIdentifier, migratedClientInputId); + MultiplayerPackets::ClientMigration clientMigration(migrateHostId, temporaryUserIdentifier, migratedClientInputId); GetConnection()->SendReliablePacket(clientMigration); m_controlledEntity = NetworkEntityHandle(); diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h index 8dcf08c480..a894d5e55e 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h @@ -20,11 +20,12 @@ namespace Multiplayer ServerToClientConnectionData ( AzNetworking::IConnection* connection, - AzNetworking::IConnectionListener& connectionListener, - NetworkEntityHandle controlledEntity + AzNetworking::IConnectionListener& connectionListener ); ~ServerToClientConnectionData() override; + void SetControlledEntity(NetworkEntityHandle primaryPlayerEntity); + //! IConnectionData interface //! @{ ConnectionDataType GetConnectionDataType() const override; @@ -42,7 +43,7 @@ namespace Multiplayer private: void OnControlledEntityRemove(); - void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId); + void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId); void OnGameplayStarted(); EntityReplicationManager m_entityReplicationManager; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index b7dde3b9e7..da1ab1cb73 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -76,6 +76,7 @@ namespace Multiplayer "The address of the remote server or host to connect to"); AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic"); AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic"); + AZ_CVAR(uint16_t, sv_portRange, 999, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The range of ports the host will incrementally attempt to bind to when initializing"); AZ_CVAR(AZ::CVarFixedString, sv_map, "nolevel", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The map the server should load"); 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"); @@ -168,6 +169,7 @@ namespace Multiplayer AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom ) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); }) + , m_autonomousEntityReplicatorCreatedHandler([this]([[maybe_unused]] NetEntityId netEntityId) { OnAutonomousEntityReplicatorCreated(); }) { AZ::Interface::Register(this); } @@ -205,8 +207,23 @@ namespace Multiplayer bool MultiplayerSystemComponent::StartHosting(uint16_t port, bool isDedicated) { + if (port != sv_port) + { + sv_port = port; + } + InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer); - return m_networkInterface->Listen(port); + const uint16_t maxPort = sv_port + sv_portRange; + while (sv_port <= maxPort) + { + if (m_networkInterface->Listen(sv_port)) + { + return true; + } + AZLOG_WARN("Failed to start listening on port %u, port is in use?", static_cast(sv_port)); + sv_port = sv_port + 1; + } + return false; } bool MultiplayerSystemComponent::Connect(const AZStd::string& remoteAddress, uint16_t port) @@ -310,6 +327,11 @@ namespace Multiplayer void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { + if (bg_multiplayerDebugDraw) + { + m_networkEntityManager.DebugDraw(); + } + const AZ::TimeMs deltaTimeMs = aznumeric_cast(static_cast(deltaTime * 1000.0f)); const AZ::TimeMs serverRateMs = static_cast(sv_serverSendRateMs); const float serverRateSeconds = static_cast(serverRateMs) / 1000.0f; @@ -394,11 +416,6 @@ namespace Multiplayer { m_networkInterface->GetConnectionSet().VisitConnections(visitor); } - - if (bg_multiplayerDebugDraw) - { - m_networkEntityManager.DebugDraw(); - } } int MultiplayerSystemComponent::GetTickOrder() @@ -469,17 +486,41 @@ namespace Multiplayer auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; m_networkInterface->GetConnectionSet().VisitConnections(visitor); return true; - } + } } reinterpret_cast(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); + // Hosts will spawn a new default player prefab for the user that just connected + if (GetAgentType() == MultiplayerAgentType::ClientServer + || GetAgentType() == MultiplayerAgentType::DedicatedServer) + { + // We use a temporary userId so we can maintain client lookups even in the event of wifi handoff + NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(packet.GetTemporaryUserId()); + if (controlledEntity.Exists()) + { + controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); + } + // Activate the entity if necessary + if (controlledEntity.GetEntity()->GetState() == AZ::Entity::State::Init) + { + controlledEntity.Activate(); + } + + ServerToClientConnectionData* connectionData = reinterpret_cast(connection->GetUserData()); + AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); + connectionData->GetReplicationManager().SetReplicationWindow(AZStd::move(window)); + connectionData->SetControlledEntity(controlledEntity); + } + if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map))) { m_didHandshake = true; - - // Sync our console - ConsoleReplicator consoleReplicator(connection); - AZ::Interface::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); }); + if (packet.GetTemporaryUserId() == 0) + { + // Sync our console + ConsoleReplicator consoleReplicator(connection); + AZ::Interface::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); }); + } return true; } return false; @@ -493,10 +534,24 @@ namespace Multiplayer ) { m_didHandshake = true; - AZ::CVarFixedString commandString = "sv_map " + packet.GetMap(); - AZ::Interface::Get()->PerformCommand(commandString.c_str()); - AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap(); - AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); + if (m_temporaryUserIdentifier == 0) + { + AZ::CVarFixedString commandString = "sv_map " + packet.GetMap(); + AZ::Interface::Get()->PerformCommand(commandString.c_str()); + AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap(); + AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); + } + else + { + OnAutonomousEntityReplicatorCreated(); +// IConnectionData* connectionData = reinterpret_cast(connection->GetUserData()); +// if (connectionData) +// { +// // @nt: TODO - delete once dropped RPC problem fixed +// // Connection has migrated, we are now waiting for the autonomous entity replicator to be created +// connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler); +// } + } return true; } @@ -617,13 +672,17 @@ namespace Multiplayer // Store the temporary user identifier so we can transmit it with our next Connect packet // The new server will use this to re-attach our set of autonomous entities + m_temporaryUserIdentifier = packet.GetTemporaryUserIdentifier(); // Disconnect our existing server connection auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); }; m_networkInterface->GetConnectionSet().VisitConnections(visitor); AZLOG_INFO("Migrating to new server shard"); m_clientMigrationStartEvent.Signal(packet.GetLastClientInputId()); - m_networkInterface->Connect(packet.GetRemoteServerAddress()); + if (m_networkInterface->Connect(packet.GetRemoteServerAddress()) == AzNetworking::InvalidConnectionId) + { + AZLOG_ERROR("Failed to connect to new host during client migration event"); + } return true; } @@ -653,7 +712,7 @@ namespace Multiplayer providerTicket = m_pendingConnectionTickets.front(); m_pendingConnectionTickets.pop(); } - connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket.c_str())); + connection->SendReliablePacket(MultiplayerPackets::Connect(0, m_temporaryUserIdentifier, providerTicket.c_str())); } else { @@ -661,20 +720,10 @@ namespace Multiplayer m_connectionAcquiredEvent.Signal(datum); } - // Hosts will spawn a new default player prefab for the user that just connected if (GetAgentType() == MultiplayerAgentType::ClientServer || GetAgentType() == MultiplayerAgentType::DedicatedServer) { - NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); - if (controlledEntity.Exists()) - { - controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); - } - controlledEntity.Activate(); - - connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); - AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); - reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window)); + connection->SetUserData(new ServerToClientConnectionData(connection, *this)); } else { @@ -696,9 +745,9 @@ namespace Multiplayer void MultiplayerSystemComponent::OnDisconnect(AzNetworking::IConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint) { - const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remote host disconnected"; + const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remotely disconnected"; AZStd::string reasonString = ToString(reason); - AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str()); + AZLOG_INFO("%s from remote address %s due to %s", endpointString, connection->GetRemoteAddress().GetString().c_str(), reasonString.c_str()); // The client is disconnecting if (GetAgentType() == MultiplayerAgentType::Client) @@ -780,7 +829,7 @@ namespace Multiplayer // Spawn the default player for this host since the host is also a player (not a dedicated server) if (m_agentType == MultiplayerAgentType::ClientServer) { - NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); + NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(0); if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent()) { controlledEntityNetBindComponent->SetAllowAutonomy(true); @@ -831,9 +880,9 @@ namespace Multiplayer handler.Connect(m_shutdownEvent); } - void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) + void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) { - m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId); + m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId, controlledEntityId); } void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) @@ -887,6 +936,11 @@ namespace Multiplayer return m_filterEntityManager; } + void MultiplayerSystemComponent::RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) + { + m_playerRejoinData[temporaryUserIdentifier] = controlledEntityId; + } + void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value) { m_spawnNetboundEntities = value; @@ -1017,6 +1071,13 @@ namespace Multiplayer m_cvarCommands.PushBackItem(AZStd::move(replicateString)); } + void MultiplayerSystemComponent::OnAutonomousEntityReplicatorCreated() + { + m_autonomousEntityReplicatorCreatedHandler.Disconnect(); + //m_networkEntityManager.GetNetworkEntityAuthorityTracker()->ResetTimeoutTime(AZ::TimeMs{ 2000 }); + m_clientMigrationEndEvent.Signal(); + } + void MultiplayerSystemComponent::ExecuteConsoleCommandList(IConnection* connection, const AZStd::fixed_vector& commands) { AZ::IConsole* console = AZ::Interface::Get(); @@ -1028,8 +1089,14 @@ namespace Multiplayer } } - NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab() + NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab(uint64_t temporaryUserIdentifier) { + const auto node = m_playerRejoinData.find(temporaryUserIdentifier); + if (node != m_playerRejoinData.end()) + { + return m_networkEntityManager.GetNetworkEntityTracker()->Get(node->second); + } + PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str())); INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate); @@ -1045,7 +1112,7 @@ namespace Multiplayer { if (!AZ::Interface::Get()->StartHosting(sv_port, sv_isDedicated)) { - AZLOG_ERROR("Failed to start listening on port %u, port is in use?", static_cast(sv_port)); + AZLOG_ERROR("Failed to start listening on any allocated port"); } } AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to"); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 262168b536..f07aa0b68f 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -116,7 +116,7 @@ namespace Multiplayer void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override; void AddSessionInitHandler(SessionInitEvent::Handler& handler) override; void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override; - void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override; + void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override; void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override; void SendReadyForEntityUpdates(bool readyForEntityUpdates) override; AZ::TimeMs GetCurrentHostTimeMs() const override; @@ -125,6 +125,7 @@ namespace Multiplayer INetworkEntityManager* GetNetworkEntityManager() override; void SetFilterEntityManager(IFilterEntityManager* entityFilter) override; IFilterEntityManager* GetFilterEntityManager() override; + void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) override; void SetShouldSpawnNetworkEntities(bool value) override; bool GetShouldSpawnNetworkEntities() const override; //! @} @@ -138,8 +139,9 @@ namespace Multiplayer void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds); void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom); + void OnAutonomousEntityReplicatorCreated(); void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector& commands); - NetworkEntityHandle SpawnDefaultPlayerPrefab(); + NetworkEntityHandle SpawnDefaultPlayerPrefab(uint64_t temporaryUserIdentifier); AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session"); @@ -162,12 +164,16 @@ namespace Multiplayer ClientMigrationEndEvent m_clientMigrationEndEvent; NotifyClientMigrationEvent m_notifyClientMigrationEvent; NotifyEntityMigrationEvent m_notifyEntityMigrationEvent; + AZ::Event::Handler m_autonomousEntityReplicatorCreatedHandler; AZStd::queue m_pendingConnectionTickets; + AZStd::unordered_map m_playerRejoinData; AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; HostFrameId m_lastReplicatedHostFrameId = HostFrameId(0); + uint64_t m_temporaryUserIdentifier = 0; // Used in the event of a migration or rejoin + double m_serverSendAccumulator = 0.0; float m_renderBlendFactor = 0.0f; float m_tickFactor = 0.0f; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 91ca1bff84..31232cb3f4 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -47,8 +47,9 @@ namespace Multiplayer , m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); }) , m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); }) { - // Set up our remote host identifier, we use the IP address of the host + // Set up our remote host identifier, by default we use the IP address of the remote host m_remoteHostId = connection.GetRemoteAddress(); + m_migrateHostId = m_remoteHostId; // Our max payload size is whatever is passed in, minus room for a udp packetheader m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead; @@ -65,7 +66,21 @@ namespace Multiplayer networkEntityManager->AddEntityExitDomainHandler(m_entityExitDomainEventHandler); } - GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler); + if (m_updateMode == Mode::LocalServerToRemoteServer) + { + GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler); + } + } + + void EntityReplicationManager::SetMigrateHostId(const HostId& remoteHostId) + { + // Allows overriding the remote HostId + m_migrateHostId = remoteHostId; + } + + const HostId& EntityReplicationManager::GetMigrateHostId() const + { + return m_migrateHostId; } const HostId& EntityReplicationManager::GetRemoteHostId() const @@ -362,15 +377,28 @@ namespace Multiplayer const bool changedRemoteRole = (remoteNetworkRole != entityReplicator->GetRemoteNetworkRole()); // Check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client bool changedLocalRole(false); - if (AZ::Entity* localEnt = entityReplicator->GetEntityHandle().GetEntity()) + NetBindComponent* netBindComponent = entityReplicator->GetEntityHandle().GetNetBindComponent(); + if (netBindComponent != nullptr) { - NetBindComponent* netBindComponent = entityReplicator->GetEntityHandle().GetNetBindComponent(); - AZ_Assert(netBindComponent != nullptr, "No NetBindComponent"); changedLocalRole = (netBindComponent->GetNetEntityRole() != entityReplicator->GetBoundLocalNetworkRole()); } if (changedRemoteRole || changedLocalRole) { + const uint32_t intEntityId = static_cast(netBindComponent->GetNetEntityId()); + if (changedLocalRole) + { + const char* oldRoleString = GetEnumString(entityReplicator->GetRemoteNetworkRole()); + const char* newRoleString = GetEnumString(remoteNetworkRole); + AZLOG(NET_ReplicatorRoles, "Replicator %u changed local role, old role = %s, new role = %s", intEntityId, oldRoleString, newRoleString); + } + if (changedRemoteRole) + { + const char* oldRoleString = GetEnumString(entityReplicator->GetBoundLocalNetworkRole()); + const char* newRoleString = GetEnumString(netBindComponent->GetNetEntityRole()); + AZLOG(NET_ReplicatorRoles, "Replicator %u changed remote role, old role = %s, new role = %s", intEntityId, oldRoleString, newRoleString); + } + // If we changed roles, we need to reset everything if (!entityReplicator->IsMarkedForRemoval()) { @@ -1107,7 +1135,7 @@ namespace Multiplayer if (m_updateMode == EntityReplicationManager::Mode::LocalServerToRemoteServer) { - netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetConnection().GetConnectionId()); + netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetMigrateHostId(), GetConnection().GetConnectionId()); } bool didSucceed = true; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index 807bbf9e21..0b97634183 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -176,7 +176,6 @@ namespace Multiplayer switch (GetBoundLocalNetworkRole()) { case NetEntityRole::Authority: - { if (GetRemoteNetworkRole() == NetEntityRole::Client || GetRemoteNetworkRole() == NetEntityRole::Autonomous) { m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent()); @@ -189,10 +188,8 @@ namespace Multiplayer { m_onForwardRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent()); } - } - break; + break; case NetEntityRole::Server: - { if (GetRemoteNetworkRole() == NetEntityRole::Authority) { m_onSendRpcHandler.Connect(netBindComponent->GetSendServerToAuthorityRpcEvent()); @@ -204,23 +201,21 @@ namespace Multiplayer // Listen for these to forward the rpc along to the other Client replicators m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent()); } - // NOTE: e_Autonomous is not connected to e_ServerProxy, it is always connected to an e_Authority - AZ_Assert(GetRemoteNetworkRole() != NetEntityRole::Autonomous, "Unexpected autonomous remote role") - } - break; + else if (GetRemoteNetworkRole() == NetEntityRole::Autonomous) + { + // NOTE: Autonomous is not connected to ServerProxy, it is always connected to an Authority + AZ_Assert(false, "Unexpected autonomous remote role") + } + break; case NetEntityRole::Client: - { // Nothing allowed, no Client to Server communication - } - break; + break; case NetEntityRole::Autonomous: - { if (GetRemoteNetworkRole() == NetEntityRole::Authority) { m_onSendRpcHandler.Connect(netBindComponent->GetSendAutonomousToAuthorityRpcEvent()); } - } - break; + break; default: AZ_Assert(false, "Unexpected network role"); } @@ -255,19 +250,6 @@ namespace Multiplayer AZLOG_WARN("Trying to activate an entity that is not in the Init state (%u)", GetEntityHandle().GetNetEntityId()); } - // First we need to make sure the transform component has been updated with the correct value prior to activation - // This is because vanilla az components may only depend on the transform component, not the multiplayer transform component - //if (auto* locationComponent = FindCommonComponent(GetEntityHandle())) - //{ - // AZ::Transform newTransform = locationComponent->GetTransform(); - // auto* transformComponent = entity->FindComponent(); - // if (transformComponent) - // { - // // We can't use EBus here since the TransFormBus does not get connected until the activate call below - // transformComponent->SetWorldTM(newTransform); - // } - //} - // Ugly, but this is the only time we need to call a non-const function on this entity entity->Activate(); m_replicationManager.m_orphanedEntityRpcs.DispatchOrphanedRpcs(*this); @@ -281,8 +263,7 @@ namespace Multiplayer NetBindComponent* netBindComponent = m_netBindComponent; AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent"); - bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) - && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole()); + bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole()); bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client; bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous; if (isAuthority || isClient || isAutonomous) @@ -428,10 +409,8 @@ namespace Multiplayer if (const NetworkTransformComponent* networkTransform = entity->FindComponent()) { const NetEntityId parentId = networkTransform->GetParentEntityId(); - /* - * For root entities attached to a level, a network parent won't be set. - * In this case, this entity is the root entity of the hierarchy and it will be activated first. - */ + // For root entities attached to a level, a network parent won't be set. + // In this case, this entity is the root entity of the hierarchy and it will be activated first. if (parentId != InvalidNetEntityId) { ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId); @@ -552,42 +531,33 @@ namespace Multiplayer switch (entityRpcMessage.GetRpcDeliveryType()) { case RpcDeliveryType::AuthorityToClient: - { if (((GetBoundLocalNetworkRole() == NetEntityRole::Client) || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous)) && (GetRemoteNetworkRole() == NetEntityRole::Authority)) { // We are a local client, and we are connected to server, aka AuthorityToClient result = RpcValidationResult::HandleRpc; } - if ((GetBoundLocalNetworkRole() == NetEntityRole::Server) - && (GetRemoteNetworkRole() == NetEntityRole::Authority)) + if ((GetBoundLocalNetworkRole() == NetEntityRole::Server) && (GetRemoteNetworkRole() == NetEntityRole::Authority)) { // We are on a server, and we received this message from another server, therefore we should forward this to any connected clients result = RpcValidationResult::ForwardToClient; } - } - break; + break; case RpcDeliveryType::AuthorityToAutonomous: - { - if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous) - && (GetRemoteNetworkRole() == NetEntityRole::Authority)) + if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous) && (GetRemoteNetworkRole() == NetEntityRole::Authority)) { // We are an autonomous client, and we are connected to server, aka AuthorityToAutonomous result = RpcValidationResult::HandleRpc; } - if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) - && (GetRemoteNetworkRole() == NetEntityRole::Server)) + if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server)) { // We are on a server, and we received this message from another server, therefore we should forward this to our autonomous player // This can occur if we've recently migrated result = RpcValidationResult::ForwardToAutonomous; } - } - break; + break; case RpcDeliveryType::AutonomousToAuthority: - { - if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) - && (GetRemoteNetworkRole() == NetEntityRole::Autonomous)) + if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Autonomous)) { if (IsMarkedForRemoval()) { @@ -609,12 +579,9 @@ namespace Multiplayer result = RpcValidationResult::HandleRpc; } } - } - break; + break; case RpcDeliveryType::ServerToAuthority: - { - if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) - && (GetRemoteNetworkRole() == NetEntityRole::Server)) + if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server)) { // if we're marked for removal, then we should forward to whomever now owns this entity if (IsMarkedForRemoval()) @@ -637,9 +604,9 @@ namespace Multiplayer result = RpcValidationResult::HandleRpc; } } + break; } - break; - } + if (result == RpcValidationResult::DropRpcAndDisconnect) { bool isLocalServer = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) || (GetBoundLocalNetworkRole() == NetEntityRole::Server); @@ -653,30 +620,29 @@ namespace Multiplayer { AZLOG_ERROR ( - "Dropping RPC and Connection EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s", + "Dropping RPC and Connection EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", aznumeric_cast(m_entityHandle.GetNetEntityId()), - aznumeric_cast(GetBoundLocalNetworkRole()), - aznumeric_cast(GetRemoteNetworkRole()), + GetEnumString(GetBoundLocalNetworkRole()), + GetEnumString(GetRemoteNetworkRole()), aznumeric_cast(entityRpcMessage.GetRpcDeliveryType()), - aznumeric_cast(entityRpcMessage.GetComponentId()), - aznumeric_cast(entityRpcMessage.GetRpcIndex()), + GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()), entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false", IsMarkedForRemoval() ? "true" : "false" ); } } + if (result == RpcValidationResult::DropRpc) { AZLOG ( NET_Rpc, - "Dropping RPC EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s", + "Dropping RPC EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", aznumeric_cast(m_entityHandle.GetNetEntityId()), - aznumeric_cast(GetBoundLocalNetworkRole()), - aznumeric_cast(GetRemoteNetworkRole()), + GetEnumString(GetBoundLocalNetworkRole()), + GetEnumString(GetRemoteNetworkRole()), aznumeric_cast(entityRpcMessage.GetRpcDeliveryType()), - aznumeric_cast(entityRpcMessage.GetComponentId()), - aznumeric_cast(entityRpcMessage.GetRpcIndex()), + GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()), entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false", IsMarkedForRemoval() ? "true" : "false" ); @@ -695,13 +661,12 @@ namespace Multiplayer { AZLOG_WARN ( - "Dropping RPC since entity deleted EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s", + "Dropping RPC since entity deleted EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", aznumeric_cast(m_entityHandle.GetNetEntityId()), - aznumeric_cast(GetBoundLocalNetworkRole()), - aznumeric_cast(GetRemoteNetworkRole()), + GetEnumString(GetBoundLocalNetworkRole()), + GetEnumString(GetRemoteNetworkRole()), aznumeric_cast(entityRpcMessage.GetRpcDeliveryType()), - aznumeric_cast(entityRpcMessage.GetComponentId()), - aznumeric_cast(entityRpcMessage.GetRpcIndex()), + GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()), entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false", IsMarkedForRemoval() ? "true" : "false" ); @@ -739,23 +704,23 @@ namespace Multiplayer case RpcValidationResult::DropRpcAndDisconnect: return false; case RpcValidationResult::ForwardToClient: - { - ScopedForwardingMessage forwarding(*this); - m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage); + { + ScopedForwardingMessage forwarding(*this); + m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage); + } return true; - } case RpcValidationResult::ForwardToAutonomous: - { - ScopedForwardingMessage forwarding(*this); - m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage); + { + ScopedForwardingMessage forwarding(*this); + m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage); + } return true; - } case RpcValidationResult::ForwardToAuthority: - { - ScopedForwardingMessage forwarding(*this); - m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage); + { + ScopedForwardingMessage forwarding(*this); + m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage); + } return true; - } default: break; } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index db7f7243cc..d1d915ca28 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -225,11 +225,19 @@ namespace Multiplayer { AZ::Entity* entity = it->second; NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity); + AZ::Aabb entityBounds = AZ::Interface::Get()->GetEntityWorldBoundsUnion(entity->GetId()); + entityBounds.Expand(AZ::Vector3(0.01f)); if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority) { - const AZ::Aabb entityBounds = AZ::Interface::Get()->GetEntityWorldBoundsUnion(entity->GetId()); - debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax()); + debugDisplay->SetColor(AZ::Colors::Black); + debugDisplay->SetAlpha(0.5f); } + else + { + debugDisplay->SetColor(AZ::Colors::DeepSkyBlue); + debugDisplay->SetAlpha(0.25f); + } + debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax()); } if (m_entityDomain != nullptr) diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp index 9d9cc74d2b..8807931ae1 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp @@ -91,17 +91,10 @@ namespace Multiplayer return m_isPoorConnection ? sv_MinEntitiesToReplicate : sv_MaxEntitiesToReplicate; } - bool ServerToClientReplicationWindow::IsInWindow(const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const + bool ServerToClientReplicationWindow::IsInWindow([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const { - // TODO: Clean up this interface, this function is used for server->server migrations, and probably shouldn't be exposed in it's current setup AZ_Assert(false, "IsInWindow should not be called on the ServerToClientReplicationWindow"); outNetworkRole = NetEntityRole::InvalidRole; - auto iter = m_replicationSet.find(entityHandle); - if (iter != m_replicationSet.end()) - { - outNetworkRole = iter->second.m_netEntityRole; - return true; - } return false; } @@ -145,7 +138,7 @@ namespace Multiplayer NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker(); IFilterEntityManager* filterEntityManager = GetMultiplayer()->GetFilterEntityManager(); - // Add all the neighbors + // Add all the neighbours for (AzFramework::VisibilityEntry* visEntry : gatheredEntries) { AZ::Entity* entity = static_cast(visEntry->m_userData); @@ -300,7 +293,6 @@ namespace Multiplayer void ServerToClientReplicationWindow::AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, [[maybe_unused]] float distanceSquared) { // Assumption: the entity has been checked for filtering prior to this call. - if (!sv_ReplicateServerProxies) { NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); @@ -311,11 +303,11 @@ namespace Multiplayer } } - const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set + const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set const bool isInReplicationSet = m_replicationSet.find(entityHandle) != m_replicationSet.end(); if (!isInReplicationSet) { - if (isQueueFull) // if our set is full, then we need to remove the worst priority in our set + if (isQueueFull) // If our set is full, then we need to remove the worst priority in our set { ConstNetworkEntityHandle removeEnt = m_candidateQueue.top().m_entityHandle; m_candidateQueue.pop(); diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h index b034bde90c..3c3d7754f6 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h @@ -75,8 +75,6 @@ namespace Multiplayer AZ::EntityActivatedEvent::Handler m_entityActivatedEventHandler; AZ::EntityDeactivatedEvent::Handler m_entityDeactivatedEventHandler; - //NetBindComponent* m_controlledNetBindComponent = nullptr; - AzNetworking::IConnection* m_connection = nullptr; // Cached values to detect a poor network connection From 28a40fa4dfa0a9d51596042ef307b6f1e2addef7 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 7 Oct 2021 16:24:21 +0100 Subject: [PATCH 012/152] Added look up of the child entities when processing input to make sure the entity still exists Signed-off-by: pereslav --- .../Components/NetworkHierarchyRootComponent.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 89db2180a5..d62b103702 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -408,12 +408,16 @@ namespace Multiplayer if (auto* networkInput = input.FindComponentInput()) { + INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); + AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + for (NetworkInputChild& subInput : networkInput->m_childInputs) { - const ConstNetworkEntityHandle& childEntity = subInput.GetOwner(); - if (auto* localChild = childEntity.GetEntity()) + const ConstNetworkEntityHandle& childEntityFromInput = subInput.GetOwner(); + ConstNetworkEntityHandle localChildHandle = networkEntityManager->GetEntity(childEntityFromInput.GetNetEntityId()); + if (localChildHandle.Exists()) { - auto* netComp = childEntity.GetNetBindComponent(); + auto* netComp = localChildHandle.GetNetBindComponent(); AZ_Assert(netComp, "No NetBindComponent, this should be impossible"); // We do not rewind entity role changes, so make sure we are the correct role prior to processing if (netComp->HasController()) From cb53389004b6510f696174ef206049ec17121701 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 7 Oct 2021 16:58:37 +0100 Subject: [PATCH 013/152] Added validation into input processing Signed-off-by: pereslav --- .../NetworkHierarchyRootComponent.cpp | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index d62b103702..19958bace1 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -411,13 +411,35 @@ namespace Multiplayer INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + // Build a set of Net IDs for the children + AZStd::unordered_set currentChildren; // TODO: Cache inside the component if this becomes a performance issue. + NetworkHierarchyRootComponent& component = GetParent(); + for (AZ::Entity* child : component.m_hierarchicalEntities) + { + if (child == component.GetEntity()) // Skip the root entity + { + continue; + } + NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); // TODO: Cache net IDs in the component if this becomes a performance issue + currentChildren.insert(childNetEntitydId); + } + + // Process the input for the child entities for (NetworkInputChild& subInput : networkInput->m_childInputs) { - const ConstNetworkEntityHandle& childEntityFromInput = subInput.GetOwner(); - ConstNetworkEntityHandle localChildHandle = networkEntityManager->GetEntity(childEntityFromInput.GetNetEntityId()); - if (localChildHandle.Exists()) + const ConstNetworkEntityHandle& inputOwnerHandle = subInput.GetOwner(); + NetEntityId inputOwnerNetEntitydId = inputOwnerHandle.GetNetEntityId(); + + if (currentChildren.count(inputOwnerNetEntitydId) == 0) { - auto* netComp = localChildHandle.GetNetBindComponent(); + // Skip the input for entities which are not a part of this hierarchy + continue; + } + + ConstNetworkEntityHandle localEntityHandle = networkEntityManager->GetEntity(inputOwnerNetEntitydId); + if (localEntityHandle.Exists()) + { + auto* netComp = localEntityHandle.GetNetBindComponent(); AZ_Assert(netComp, "No NetBindComponent, this should be impossible"); // We do not rewind entity role changes, so make sure we are the correct role prior to processing if (netComp->HasController()) From 0c2c042fde8835d0dcaa198286d1a7cdda1c0756 Mon Sep 17 00:00:00 2001 From: pereslav Date: Fri, 8 Oct 2021 21:19:22 +0100 Subject: [PATCH 014/152] Fixed crash in ConstEntityHandle. Added hierarchical correction data serialization Signed-off-by: pereslav --- .../LocalPredictionPlayerInputComponent.h | 2 ++ .../NetworkHierarchyRootComponent.h | 2 ++ ...tionPlayerInputComponent.AutoComponent.xml | 1 + .../LocalPredictionPlayerInputComponent.cpp | 25 ++++++++++++++----- .../NetworkHierarchyRootComponent.cpp | 25 +++++++++++++++++++ .../NetworkEntity/NetworkEntityHandle.cpp | 4 +-- 6 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h index bcbea6542f..25fe473135 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h @@ -71,6 +71,8 @@ namespace Multiplayer void UpdateAutonomous(AZ::TimeMs deltaTimeMs); void UpdateBankedTime(AZ::TimeMs deltaTimeMs); + bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer); + using StateHistoryItem = AZStd::unique_ptr; AZStd::map m_predictiveStateHistory; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index a31f47317a..cafd63b223 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -59,6 +59,8 @@ namespace Multiplayer void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} + bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer); + protected: void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index 1a7496a77d..194c05577d 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -9,6 +9,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 747f53d37b..4d0756d9a5 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace Multiplayer { @@ -211,7 +212,7 @@ namespace Multiplayer m_lastCorrectionSentTimeMs = currentTimeMs; AzNetworking::HashSerializer hashSerializer; - GetNetBindComponent()->SerializeEntityCorrection(hashSerializer); + SerializeEntityCorrection(hashSerializer); const AZ::HashValue32 localAuthorityHash = hashSerializer.GetHash(); @@ -233,7 +234,7 @@ namespace Multiplayer // only deserialize if we have data (for client/server profile/debug mismatches) if (correction.GetSize() > 0) { - GetNetBindComponent()->SerializeEntityCorrection(serializer); + SerializeEntityCorrection(serializer); } correction.Resize(serializer.GetSize()); @@ -313,7 +314,7 @@ namespace Multiplayer // Apply the correction AzNetworking::TrackChangedSerializer serializer(correction.GetBuffer(), static_cast(correction.GetSize())); - GetNetBindComponent()->SerializeEntityCorrection(serializer); + SerializeEntityCorrection(serializer); GetNetBindComponent()->NotifyCorrection(); #ifndef AZ_RELEASE_BUILD @@ -325,7 +326,7 @@ namespace Multiplayer { // Read out state values AzNetworking::StringifySerializer serverValues; - GetNetBindComponent()->SerializeEntityCorrection(serverValues); + SerializeEntityCorrection(serverValues); PrintCorrectionDifferences(*iter->second, serverValues); } else @@ -452,7 +453,7 @@ namespace Multiplayer // Generate a hash based on the current client predicted states AzNetworking::HashSerializer hashSerializer; - GetNetBindComponent()->SerializeEntityCorrection(hashSerializer); + SerializeEntityCorrection(hashSerializer); // Save this input and discard move history outside our client rewind window m_inputHistory.PushBack(input); @@ -480,7 +481,7 @@ namespace Multiplayer { m_predictiveStateHistory.erase(m_predictiveStateHistory.begin()); } - GetNetBindComponent()->SerializeEntityCorrection(*inputHistory); + SerializeEntityCorrection(*inputHistory); m_predictiveStateHistory.emplace(m_clientInputId, AZStd::move(inputHistory)); } #endif @@ -493,6 +494,18 @@ namespace Multiplayer } } + bool LocalPredictionPlayerInputComponentController::SerializeEntityCorrection(AzNetworking::ISerializer& serializer) + { + bool result = GetNetBindComponent()->SerializeEntityCorrection(serializer); + + NetworkHierarchyRootComponent* hierarchyComponent = GetParent().GetNetworkHierarchyRootComponent(); + if (result && hierarchyComponent) + { + result = hierarchyComponent->SerializeEntityCorrection(serializer); + } + return result; + } + void LocalPredictionPlayerInputComponentController::UpdateBankedTime(AZ::TimeMs deltaTimeMs) { const double deltaTime = static_cast(deltaTimeMs) / 1000.0; diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 19958bace1..dd83878b25 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -452,4 +452,29 @@ namespace Multiplayer } } + bool NetworkHierarchyRootComponent::SerializeEntityCorrection(AzNetworking::ISerializer& serializer) + { + bool result = true; + + INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); + AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + + for (AZ::Entity* child : m_hierarchicalEntities) + { + if (child == GetEntity()) + { + // Skip the root entity + continue; + } + + NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId); + NetBindComponent* netBindComponent = childEntityHandle.GetNetBindComponent(); + AZ_Assert(netBindComponent, "No NetBindComponent, this should be impossible"); + + result = result && netBindComponent->SerializeEntityCorrection(serializer); + } + + return result; + } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp index ef338840f9..7794d9026b 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp @@ -32,8 +32,8 @@ namespace Multiplayer if (entity) { - AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid"); - m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity); + AZ_Assert(m_networkEntityTracker, "NetworkEntityTracker is not valid"); + m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(entity); if (m_netBindComponent != nullptr) { m_netEntityId = m_netBindComponent->GetNetEntityId(); From 4449e83c3b287f691f3f9fe6bd1506536e684a0c Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Fri, 8 Oct 2021 21:27:31 -0700 Subject: [PATCH 015/152] Further fixes to get client migrations functional, plus a bug fix from the netBindComponent lookup optimization Signed-off-by: kberg-amzn --- .../Multiplayer/Components/NetBindComponent.h | 4 +- .../Code/Include/Multiplayer/IMultiplayer.h | 12 +++++- .../Include/Multiplayer/MultiplayerTypes.h | 2 +- .../Source/Components/NetBindComponent.cpp | 4 +- .../ServerToClientConnectionData.cpp | 16 +++----- .../ServerToClientConnectionData.h | 2 +- .../ServerToClientConnectionData.inl | 1 - .../Source/MultiplayerSystemComponent.cpp | 41 ++++++++++++++----- .../Code/Source/MultiplayerSystemComponent.h | 3 +- .../EntityReplicationManager.cpp | 18 ++++---- .../NetworkEntity/NetworkEntityHandle.cpp | 16 ++------ .../NetworkEntity/NetworkEntityManager.cpp | 14 +++++++ 12 files changed, 82 insertions(+), 51 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index b22c8dc51c..4102e4b850 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -32,7 +32,7 @@ namespace Multiplayer using EntityStopEvent = AZ::Event; using EntityDirtiedEvent = AZ::Event<>; using EntitySyncRewindEvent = AZ::Event<>; - using EntityServerMigrationEvent = AZ::Event; + using EntityServerMigrationEvent = AZ::Event; using EntityPreRenderEvent = AZ::Event; using EntityCorrectionEvent = AZ::Event<>; @@ -113,7 +113,7 @@ namespace Multiplayer void MarkDirty(); void NotifyLocalChanges(); void NotifySyncRewindState(); - void NotifyServerMigration(const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId); + void NotifyServerMigration(const HostId& remoteHostId); void NotifyPreRender(float deltaTime); void NotifyCorrection(); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index b4be69ed4c..8023d21242 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -45,7 +45,7 @@ namespace Multiplayer using ClientMigrationStartEvent = AZ::Event; using ClientMigrationEndEvent = AZ::Event<>; using ClientDisconnectedEvent = AZ::Event<>; - using NotifyClientMigrationEvent = AZ::Event; + using NotifyClientMigrationEvent = AZ::Event; using NotifyEntityMigrationEvent = AZ::Event; using ConnectionAcquiredEvent = AZ::Event; using SessionInitEvent = AZ::Event; @@ -131,11 +131,12 @@ namespace Multiplayer virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0; //! Signals a NotifyClientMigrationEvent with the provided parameters. + //! @param connectionId the connection id of the client that is migrating //! @param hostId the host id of the host the client is migrating to //! @param userIdentifier the user identifier the client will provide the new host to validate identity //! @param lastClientInputId the last processed clientInputId by the current host //! @param controlledEntityId the entityId of the clients autonomous entity - virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) = 0; + virtual void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) = 0; //! Signals a NotifyEntityMigrationEvent with the provided parameters. //! @param entityHandle the network entity handle of the entity being migrated @@ -182,6 +183,13 @@ namespace Multiplayer //! @param controlledEntityId the controlled entityId of the players autonomous entity virtual void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) = 0; + //! Completes a client migration event by informing the appropriate client to migrate between hosts. + //! @param temporaryUserIdentifier the temporary user identifier used to identify a player across hosts + //! @param connectionId the connection id of the player being migrated + //! @param publicHostId the public address of the new host the client should connect to + //! @param migratedClientInputId the last clientInputId processed prior to migration + virtual void CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) = 0; + //! Enables or disables automatic instantiation of netbound entities. //! This setting is controlled by the networking layer and should not be touched //! If enabled, netbound entities will instantiate as spawnables are loaded into the game world, generally true for the server diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h index 8284dd7010..58a0ae63fa 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h @@ -29,7 +29,7 @@ namespace Multiplayer using HostId = AzNetworking::IpAddress; static const HostId InvalidHostId = HostId(); - AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint32_t); + AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint64_t); static constexpr NetEntityId InvalidNetEntityId = static_cast(-1); AZ_TYPE_SAFE_INTEGRAL(NetComponentId, uint16_t); diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index 2e451793ed..cc71000d33 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -394,9 +394,9 @@ namespace Multiplayer m_syncRewindEvent.Signal(); } - void NetBindComponent::NotifyServerMigration(const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId) + void NetBindComponent::NotifyServerMigration(const HostId& remoteHostId) { - m_entityServerMigrationEvent.Signal(m_netEntityHandle, remoteHostId, migrateHostId, connectionId); + m_entityServerMigrationEvent.Signal(m_netEntityHandle, remoteHostId); } void NetBindComponent::NotifyPreRender(float deltaTime) diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp index 29be64ac07..56eed58af7 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp @@ -27,9 +27,9 @@ namespace Multiplayer ) : m_connection(connection) , m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); }) - , m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId) + , m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { - OnControlledEntityMigration(entityHandle, remoteHostId, migrateHostId, connectionId); + OnControlledEntityMigration(entityHandle, remoteHostId); }) , m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient) { @@ -102,9 +102,7 @@ namespace Multiplayer void ServerToClientConnectionData::OnControlledEntityMigration ( [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, - const HostId& remoteHostId, - const HostId& migrateHostId, - [[maybe_unused]] AzNetworking::ConnectionId connectionId + const HostId& remoteHostId ) { ClientInputId migratedClientInputId = ClientInputId{ 0 }; @@ -121,11 +119,9 @@ namespace Multiplayer const uint64_t temporaryUserIdentifier = AzNetworking::CryptoRand64(); // Tell the new host that a client is about to (re)join - GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, temporaryUserIdentifier, migratedClientInputId, m_controlledEntity.GetNetEntityId()); - - // Tell the client who to join - MultiplayerPackets::ClientMigration clientMigration(migrateHostId, temporaryUserIdentifier, migratedClientInputId); - GetConnection()->SendReliablePacket(clientMigration); + GetMultiplayer()->SendNotifyClientMigrationEvent(GetConnection()->GetConnectionId(), remoteHostId, temporaryUserIdentifier, migratedClientInputId, m_controlledEntity.GetNetEntityId()); + // We need to send a MultiplayerPackets::ClientMigration packet to complete this process + // This happens inside MultiplayerSystemComponent, once we're certain the remote host has appropriately prepared m_controlledEntity = NetworkEntityHandle(); m_canSendUpdates = false; diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h index a894d5e55e..7349ee4244 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h @@ -43,7 +43,7 @@ namespace Multiplayer private: void OnControlledEntityRemove(); - void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId); + void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId); void OnGameplayStarted(); EntityReplicationManager m_entityReplicationManager; diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl index 53ba51f36a..956fc4ca1a 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl @@ -18,7 +18,6 @@ namespace Multiplayer m_canSendUpdates = canSendUpdates; } - inline NetworkEntityHandle ServerToClientConnectionData::GetPrimaryPlayerEntity() { return m_controlledEntity; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index da1ab1cb73..cdd61bd740 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -212,12 +212,12 @@ namespace Multiplayer sv_port = port; } - InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer); const uint16_t maxPort = sv_port + sv_portRange; while (sv_port <= maxPort) { if (m_networkInterface->Listen(sv_port)) { + InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer); return true; } AZLOG_WARN("Failed to start listening on port %u, port is in use?", static_cast(sv_port)); @@ -510,6 +510,12 @@ namespace Multiplayer AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); connectionData->GetReplicationManager().SetReplicationWindow(AZStd::move(window)); connectionData->SetControlledEntity(controlledEntity); + + // If this is a migrate or rejoin, immediately ready the connection for updates + if (packet.GetTemporaryUserId() != 0) + { + connectionData->SetCanSendUpdates(true); + } } if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map))) @@ -543,14 +549,16 @@ namespace Multiplayer } else { - OnAutonomousEntityReplicatorCreated(); -// IConnectionData* connectionData = reinterpret_cast(connection->GetUserData()); -// if (connectionData) -// { -// // @nt: TODO - delete once dropped RPC problem fixed -// // Connection has migrated, we are now waiting for the autonomous entity replicator to be created -// connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler); -// } + // Bypass map loading and immediately ready the connection for updates + IConnectionData* connectionData = reinterpret_cast(connection->GetUserData()); + if (connectionData) + { + connectionData->SetCanSendUpdates(true); + + // @nt: TODO - delete once dropped RPC problem fixed + // Connection has migrated, we are now waiting for the autonomous entity replicator to be created + connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler); + } } return true; } @@ -880,9 +888,9 @@ namespace Multiplayer handler.Connect(m_shutdownEvent); } - void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) + void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) { - m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId, controlledEntityId); + m_notifyClientMigrationEvent.Signal(connectionId, hostId, userIdentifier, lastClientInputId, controlledEntityId); } void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) @@ -941,6 +949,17 @@ namespace Multiplayer m_playerRejoinData[temporaryUserIdentifier] = controlledEntityId; } + void MultiplayerSystemComponent::CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) + { + IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId); + if (connection != nullptr) // Make sure the player has not disconnected since the start of migration + { + // Tell the client who to join + MultiplayerPackets::ClientMigration clientMigration(publicHostId, temporaryUserIdentifier, migratedClientInputId); + connection->SendReliablePacket(clientMigration); + } + } + void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value) { m_spawnNetboundEntities = value; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index f07aa0b68f..3af6babb18 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -116,7 +116,7 @@ namespace Multiplayer void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override; void AddSessionInitHandler(SessionInitEvent::Handler& handler) override; void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override; - void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override; + void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override; void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override; void SendReadyForEntityUpdates(bool readyForEntityUpdates) override; AZ::TimeMs GetCurrentHostTimeMs() const override; @@ -126,6 +126,7 @@ namespace Multiplayer void SetFilterEntityManager(IFilterEntityManager* entityFilter) override; IFilterEntityManager* GetFilterEntityManager() override; void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) override; + void CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) override; void SetShouldSpawnNetworkEntities(bool value) override; bool GetShouldSpawnNetworkEntities() const override; //! @} diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 31232cb3f4..9042a3cf6b 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -386,17 +386,18 @@ namespace Multiplayer if (changedRemoteRole || changedLocalRole) { const uint32_t intEntityId = static_cast(netBindComponent->GetNetEntityId()); + const char* entityName = entityReplicator->GetEntityHandle().GetEntity()->GetName().c_str(); if (changedLocalRole) { const char* oldRoleString = GetEnumString(entityReplicator->GetRemoteNetworkRole()); const char* newRoleString = GetEnumString(remoteNetworkRole); - AZLOG(NET_ReplicatorRoles, "Replicator %u changed local role, old role = %s, new role = %s", intEntityId, oldRoleString, newRoleString); + AZLOG(NET_ReplicatorRoles, "Replicator %s(%u) changed local role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString); } if (changedRemoteRole) { const char* oldRoleString = GetEnumString(entityReplicator->GetBoundLocalNetworkRole()); const char* newRoleString = GetEnumString(netBindComponent->GetNetEntityRole()); - AZLOG(NET_ReplicatorRoles, "Replicator %u changed remote role, old role = %s, new role = %s", intEntityId, oldRoleString, newRoleString); + AZLOG(NET_ReplicatorRoles, "Replicator %s(%u) changed remote role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString); } // If we changed roles, we need to reset everything @@ -605,9 +606,9 @@ namespace Multiplayer NetBindComponent* netBindComponent = replicatorEntity.GetNetBindComponent(); AZ_Assert(netBindComponent != nullptr, "No NetBindComponent"); - if (createEntity) + if (netBindComponent->GetOwningConnectionId() != invokingConnection->GetConnectionId()) { - // Always set our invoking connectionId for any newly created entities, since this connection now 'owns' them from a rewind perspective + // Always ensure our owning connectionId is correct for correct rewind behaviour netBindComponent->SetOwningConnectionId(invokingConnection->GetConnectionId()); } @@ -617,10 +618,11 @@ namespace Multiplayer AZ_Assert(localNetworkRole != NetEntityRole::Authority, "UpdateMessage trying to set local role to Authority, this should only happen via migration"); AZLOG_INFO ( - "EntityReplicationManager: Changing network role on entity %u, old role %u new role %u", + "EntityReplicationManager: Changing network role on entity %s(%u), old role %s new role %s", + replicatorEntity.GetEntity()->GetName().c_str(), aznumeric_cast(netEntityId), - aznumeric_cast(netBindComponent->GetNetEntityRole()), - aznumeric_cast(localNetworkRole) + GetEnumString(netBindComponent->GetNetEntityRole()), + GetEnumString(localNetworkRole) ); if (NetworkRoleHasController(localNetworkRole)) @@ -1135,7 +1137,7 @@ namespace Multiplayer if (m_updateMode == EntityReplicationManager::Mode::LocalServerToRemoteServer) { - netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetMigrateHostId(), GetConnection().GetConnectionId()); + netBindComponent->NotifyServerMigration(GetRemoteHostId()); } bool didSucceed = true; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp index ef338840f9..457395a61e 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp @@ -18,22 +18,14 @@ namespace Multiplayer { ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* networkEntityTracker) : m_entity(entity) - , m_networkEntityTracker(networkEntityTracker) + , m_networkEntityTracker((networkEntityTracker != nullptr) ? networkEntityTracker : GetNetworkEntityTracker()) { - if (m_networkEntityTracker == nullptr) - { - m_networkEntityTracker = GetNetworkEntityTracker(); - } - - if (m_networkEntityTracker) - { - m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity); - } + AZ_Assert(m_networkEntityTracker, "NetworkEntityTracker is not valid"); + m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity); if (entity) { - AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid"); - m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity); + m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(entity); if (m_netBindComponent != nullptr) { m_netEntityId = m_netBindComponent->GetNetEntityId(); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index d1d915ca28..5d76b4ed56 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -46,6 +46,20 @@ namespace Multiplayer void NetworkEntityManager::Initialize(const HostId& hostId, AZStd::unique_ptr entityDomain) { m_hostId = hostId; + + // Configure our vended NetEntityIds so that no two hosts generate the same NetEntityId + { + // Needs more thought + const uint64_t addrPortion = hostId.GetAddress(AzNetworking::ByteOrder::Host); + const uint64_t portPortion = hostId.GetPort(AzNetworking::ByteOrder::Host); + const uint64_t hostIdentifier = (portPortion << 32) | addrPortion; + const AZ::HashValue32 hostHash = AZ::TypeHash32(hostIdentifier); + + NetEntityId hostEntityIdOffset = static_cast(hostHash) << 32; + m_nextEntityId &= NetEntityId{ 0x0000000000000000FFFFFFFFFFFFFFFF }; + m_nextEntityId |= hostEntityIdOffset; + } + m_entityDomain = AZStd::move(entityDomain); m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true); m_entityDomain->ActivateTracking(m_ownedEntities); From 05e8b2941abef490aadb4178b20c0c8f9294ca9e Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Fri, 8 Oct 2021 21:36:54 -0700 Subject: [PATCH 016/152] Removing unused member, simplify API Signed-off-by: kberg-amzn --- .../EntityReplication/EntityReplicationManager.h | 5 ----- .../EntityReplication/EntityReplicationManager.cpp | 12 ------------ 2 files changed, 17 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h index 83f9e238f5..10346ad777 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -57,10 +57,6 @@ namespace Multiplayer EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode); ~EntityReplicationManager() = default; - //! Used to override during client migration if your host has a specially assigned publically routable address. - //! @param remoteHostId the publically routable address to use in place of the remote HostId - void SetMigrateHostId(const HostId& remoteHostId); - const HostId& GetMigrateHostId() const; const HostId& GetRemoteHostId() const; void ActivatePendingEntities(); @@ -211,7 +207,6 @@ namespace Multiplayer AZ::TimeMs m_entityPendingRemovalMs = AZ::TimeMs{ 0 }; AZ::TimeMs m_frameTimeMs = AZ::TimeMs{ 0 }; HostId m_remoteHostId = InvalidHostId; - HostId m_migrateHostId = InvalidHostId; uint32_t m_maxRemoteEntitiesPendingCreationCount = AZStd::numeric_limits::max(); uint32_t m_maxPayloadSize = 0; Mode m_updateMode = Mode::Invalid; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 9042a3cf6b..e0023815cc 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -49,7 +49,6 @@ namespace Multiplayer { // Set up our remote host identifier, by default we use the IP address of the remote host m_remoteHostId = connection.GetRemoteAddress(); - m_migrateHostId = m_remoteHostId; // Our max payload size is whatever is passed in, minus room for a udp packetheader m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead; @@ -72,17 +71,6 @@ namespace Multiplayer } } - void EntityReplicationManager::SetMigrateHostId(const HostId& remoteHostId) - { - // Allows overriding the remote HostId - m_migrateHostId = remoteHostId; - } - - const HostId& EntityReplicationManager::GetMigrateHostId() const - { - return m_migrateHostId; - } - const HostId& EntityReplicationManager::GetRemoteHostId() const { return m_remoteHostId; From ea030d2890abfb4c95e87ba816bf823d35aeb92d Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Tue, 12 Oct 2021 17:48:12 -0700 Subject: [PATCH 017/152] Removing unused softening boundary width controls Signed-off-by: mrieggeramzn --- .../Shadow/DirectionalLightShadow.azsli | 76 ------- .../Atom/Features/Shadow/JitterTablePcf.azsli | 185 ------------------ .../Features/Shadow/ProjectedShadow.azsli | 35 ---- .../Atom/Features/Shadow/Shadow.azsli | 3 - .../CoreLights/ViewSrg.azsli | 3 - .../atom_feature_common_asset_files.cmake | 1 - ...irectionalLightFeatureProcessorInterface.h | 6 - .../DiskLightFeatureProcessorInterface.h | 2 - .../PointLightFeatureProcessorInterface.h | 3 - .../Atom/Feature/CoreLights/ShadowConstants.h | 1 - ...ProjectedShadowFeatureProcessorInterface.h | 2 - .../DirectionalLightFeatureProcessor.cpp | 70 +------ .../DirectionalLightFeatureProcessor.h | 7 +- .../CoreLights/DiskLightFeatureProcessor.cpp | 5 - .../CoreLights/DiskLightFeatureProcessor.h | 1 - .../Source/CoreLights/EsmShadowmapsPass.cpp | 22 --- .../Source/CoreLights/EsmShadowmapsPass.h | 10 - .../CoreLights/PointLightFeatureProcessor.cpp | 5 - .../CoreLights/PointLightFeatureProcessor.h | 1 - .../ProjectedShadowFeatureProcessor.cpp | 79 +------- .../Shadows/ProjectedShadowFeatureProcessor.h | 4 +- .../CommonFeatures/CoreLights/AreaLightBus.h | 7 - .../CoreLights/AreaLightComponentConfig.h | 1 - .../CoreLights/DirectionalLightBus.h | 9 - .../DirectionalLightComponentConfig.h | 4 - .../CoreLights/AreaLightComponentConfig.cpp | 1 - .../AreaLightComponentController.cpp | 18 -- .../CoreLights/AreaLightComponentController.h | 2 - .../DirectionalLightComponentConfig.cpp | 1 - .../DirectionalLightComponentController.cpp | 19 -- .../DirectionalLightComponentController.h | 2 - .../Source/CoreLights/DiskLightDelegate.cpp | 8 - .../Source/CoreLights/DiskLightDelegate.h | 1 - .../CoreLights/EditorAreaLightComponent.cpp | 9 - .../EditorDirectionalLightComponent.cpp | 9 - .../Source/CoreLights/LightDelegateBase.h | 1 - .../CoreLights/LightDelegateInterface.h | 2 - .../Source/CoreLights/SphereLightDelegate.cpp | 8 - .../Source/CoreLights/SphereLightDelegate.h | 1 - 39 files changed, 8 insertions(+), 616 deletions(-) delete mode 100644 Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli index dd235fcd3a..633ea85387 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli @@ -10,7 +10,6 @@ #include #include -#include "JitterTablePcf.azsli" #include "Shadow.azsli" #include "ShadowmapAtlasLib.azsli" #include "BicubicPcfFilters.azsli" @@ -82,12 +81,6 @@ class DirectionalLightShadow // result.y == true if the given coordinate is in shadow. bool2 IsShadowed(float3 shadowCoord, uint indexOfCascade); - // This checks if the point is shadowed or not for the given center coordinate and jitter. - bool IsShadowedWithJitter( - float3 jitterUnit, - float jitterDepthDiffBase, - uint jitterIndex); - // This outputs visibility ratio (from 0.0 to 1.0) of the given coordinate // from the light origin without filtering. float GetVisibilityFromLightNoFilter(); @@ -189,75 +182,6 @@ bool2 DirectionalLightShadow::IsShadowed(float3 shadowCoord, uint indexOfCascade return bool2(false, false); } -bool DirectionalLightShadow::IsShadowedWithJitter( - float3 jitterUnit, - float jitterDepthDiffBase, - uint jitterIndex) -{ - const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount; - const float4x4 worldToLightViewMatrices[ViewSrg::MaxCascadeCount] = - ViewSrg::m_directionalLightShadows[m_lightIndex].m_worldToLightViewMatrices; - const float4x4 lightViewToShadowmapMatrices[ViewSrg::MaxCascadeCount] = - ViewSrg::m_directionalLightShadows[m_lightIndex].m_lightViewToShadowmapMatrices; - const float boundaryScale = - ViewSrg::m_directionalLightShadows[m_lightIndex].m_boundaryScale; - - const float2 jitterXY = g_jitterTablePcf[jitterIndex]; - - // jitterLightView is the jittering diff vector from the lighted point on the surface - // in the light view space. It is remarked as "v_J" in the comment - // named "Calculate depth adjusting diff for jittered samples" - // just before the function GetJitterUnitVectorDepthDiffBase. - const float4 jitterLightView = float4(jitterXY, 0., 0.) * boundaryScale; - - // It checks the jittered point is lit or shadowed from the detailed cascade - // to the less detailed one. - for (uint indexOfCascade = 0; indexOfCascade < cascadeCount; ++indexOfCascade) - { - // jitterShadowmap is the jittering diff vector in the shadowmap space. - const float4 jitterShadowmap = mul(lightViewToShadowmapMatrices[indexOfCascade], jitterLightView); - - // Calculation of the jittering for Z-coordinate (light direction) is required - // to check lit/shadowed for the jittered point. - // jitterDepthDiff is the Z-coordinate of the jittering diff vector - // in the shadowmap space. - float jitterDepthDiff = 0.; - - // jitterDepthDiffBase is "1/tan(theta)" in the comment. - if (jitterDepthDiffBase != 0.) - { - // jitterUnitLightView is the unit vector in the light view space - // noted as "v_M" in the comment. - const float3 jitterUnitLightView = - normalize(mul(worldToLightViewMatrices[indexOfCascade], float4(jitterUnit, 0.)).xyz); - const float lightViewToShadowmapZScale = -lightViewToShadowmapMatrices[indexOfCascade]._m22; - // jitterDepthDiff is the "d" in the note, and it is calculated by - // d = (v_J . v_M) / tan(theta) - // in the light view space. Furthermore it have to be converted - // to the light clip space, which can be done by lightViewToShadowmapZScale. - jitterDepthDiff = - dot(jitterLightView.xyz, jitterUnitLightView) * jitterDepthDiffBase * - lightViewToShadowmapZScale; - } - // jitteredCoord is the coordinate of the jittered point in the shadowmap space. - const float3 jitteredCoord = - m_shadowCoords[indexOfCascade] + float3(jitterShadowmap.xy, jitterDepthDiff); - // Check for the jittered point is lit or shadowed. - const bool2 checkedShadowed = IsShadowed( - jitteredCoord, - indexOfCascade); - // If check is done, return the lit/shadowed flag. - // Otherwise make it pend to the next cascade. - if (checkedShadowed.x) - { - m_debugInfo.m_cascadeIndex = indexOfCascade; - return checkedShadowed.y; - } - } - m_debugInfo.m_cascadeIndex = cascadeCount; - return false; -} - float DirectionalLightShadow::GetVisibilityFromLightNoFilter() { const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli deleted file mode 100644 index 9082286758..0000000000 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - /* - The following is the output of - $ python3 pcf_jitter_table.py 6 g_jitterTablePcf 0 - where pcf_jitter_table.py has the following contents. - -@code -#!/usr/bin/env python3 - -import random -import sys -import math - - -""" Returns if a point in the range -[radius_min, radius_sup)*[angle_min, angle_sup) -is contained in the tuple polar coordinates. -""" -def is_point_include(radius_min, radius_sup, angle_min, angle_sup, polars): - for polar in polars: - if (radius_min <= polar[0] and polar[0] < radius_sup and - angle_min <= polar[1] and polar[1] < angle_sup): - return True - return False - - -""" Insert a randomly generated polar coordianted point in each -range [r0, r1)*[a0, a1) if there has not been such a point -in tuple coords yet, where [0, 1)*[0, 2pi) is divided -into the rad_count*agl_count ranges. -""" -def add_jitter_coords(radius_count, angle_count, polars): - radius_base = 1.0 / math.sqrt(radius_count) - for radius_index in range(radius_count): - # range of radius - radius_min = math.sqrt(radius_index) * radius_base - radius_sup = math.sqrt(radius_index + 1) * radius_base - - # randomize angle order - random_state = random.getstate() - angle_indices = list(range(angle_count)) - random.shuffle(angle_indices) - random.setstate(random_state) - - for angle_index in angle_indices: - # range of angle - angle_min = 2 * math.pi * angle_index / angle_count - angle_sup = 2 * math.pi * (angle_index + 1) / angle_count - - # if no point in the radius/angle range, add a new point - if not is_point_include(radius_min, radius_sup, - angle_min, angle_sup, - polars): - radius = radius_min + (radius_sup - radius_min) * random.random() - angle = angle_min + (angle_sup - angle_min) * random.random() - polars += [[radius, angle]] - - -""" Return a formatted string readable as an array of -orthogonal coordinated points which are in inside of the unit disk. -""" -def conv_array_string(polars): - result = "{\n" - for [radius, angle] in polars: - x = radius * math.cos(angle) - y = radius * math.sin(angle) - result += str.format(" float2({: 1.20e}, {: 1.20e}),\n", x, y) - result = result.rstrip(",\n") + "\n};\n" - return result - - -if __name__ == "__main__": - rad_size = 1 - ang_size = 1 - - if len(sys.argv) > 3: - random_seed = int(sys.argv[3]) - else: - random_seed = 0 - - if len(sys.argv) > 2: - array_name = sys.argv[2] - else: - array_name = False - - if len(sys.argv) > 1: - len_log = int(sys.argv[1]) - else: - print(" usage: {} array_len_log2 [array_file_name] [random_seed]".format(__file__)) - print(" array_len_log2 = 2 -> array length = 4") - print(" array_len_log2 = 6 -> array length = 64") - sys.exit() - - random.seed(random_seed) - coords = [] - add_jitter_coords(rad_size, ang_size, coords) - for index in range(len_log): - if index % 2 == 0: - rad_size *= 2 - else: - ang_size *= 2 - add_jitter_coords(rad_size, ang_size, coords) - - if array_name: - print(str.format("static const float2 {}[{}] =", array_name, len(coords))) - print(conv_array_string(coords)) - - @endcode - */ -#pragma once - -static const float2 g_jitterTablePcf[64] = -{ - float2( 4.21857815578105532772e-02, -8.43367430701083664601e-01), - float2(-1.66526814909220763350e-02, 2.96922406531470617352e-01), - float2(-1.06374665780382349212e-01, -3.45521852905696924552e-01), - float2( 5.42648241814168375008e-01, 7.63475573328278533936e-01), - float2(-1.55045122122251910479e-01, 5.78282315712970729216e-01), - float2( 1.01310018770242576264e-02, -6.88001749851880561870e-01), - float2(-5.41276603451248283783e-01, 5.21888233660957712168e-01), - float2(-6.69885071867917680777e-01, -6.72019666097878665134e-01), - float2( 1.22985029409499718039e-02, 4.54706838949524849713e-01), - float2( 4.00334354168925599105e-01, -6.20112671104014120949e-02), - float2( 2.32326155804074424571e-01, 5.14183027524470093184e-01), - float2(-3.26788693165450228051e-01, -6.03339478694129849323e-01), - float2( 7.72374386126136736053e-01, 1.23204314299169448432e-01), - float2(-4.45379212004159807936e-01, -6.35591042627205338178e-01), - float2( 9.86986293787213919693e-01, -5.18195017297516449806e-02), - float2(-9.09197225477999193544e-01, 1.95281945570711268356e-01), - float2( 8.78123785413316704229e-02, -2.77671865082058690055e-02), - float2( 1.93947312440399088906e-01, 4.27852204081567363825e-03), - float2(-2.06133675819526185347e-01, -1.49183652412411493771e-01), - float2(-4.11351098583102647854e-01, 2.36214692717993696158e-01), - float2( 3.50058750095615767162e-01, -3.57193658067260721989e-01), - float2(-5.54174780014121681759e-01, -2.23361040823672196698e-01), - float2(-6.29913348094886860196e-01, 1.29962593232600148729e-01), - float2( 3.96119563669521335125e-01, 4.90495219155295036906e-01), - float2( 7.26077464944819728210e-01, -3.70531027878536270426e-02), - float2(-5.50726266551596621568e-01, 6.48997654184258587762e-01), - float2(-6.98067624269093189859e-01, -3.83843898992943299842e-01), - float2( 8.72900706885875177221e-02, 8.24287559846993866941e-01), - float2( 6.65413234189638491678e-01, -5.66029707430476647367e-01), - float2(-5.97071574457786802270e-01, -6.93417220711863180327e-01), - float2( 6.09778569514949131403e-01, 6.92279483269558570946e-01), - float2(-8.10051800827623957879e-01, 5.82366304247235455627e-01), - float2(-8.77200948157437071506e-02, -1.88326609190753474499e-01), - float2( 9.79306884403889771340e-02, 1.86693151785678163046e-01), - float2( 4.60071424048798319206e-02, -1.98255149016034859510e-01), - float2(-5.37585860722621794450e-02, 3.99205315590760584366e-02), - float2( 2.18621803321778829243e-01, -3.85632280444686503795e-01), - float2(-2.98409571230789372187e-02, 4.22286693608096730390e-01), - float2( 3.58654757584850270025e-01, 2.95175871390239985548e-01), - float2(-3.85631921979480485341e-01, -3.00322047091407640096e-01), - float2( 4.49800763439369810648e-01, 3.98492182500493397068e-01), - float2(-4.97878650048238891035e-01, 2.57984038389083569776e-01), - float2(-3.12055242602567339816e-01, -4.88013525550807125697e-01), - float2( 5.87078632117718268724e-01, -6.97256834327608099322e-02), - float2( 6.23692403999373534695e-01, 3.11519734097943645779e-01), - float2( 6.64426445690903810792e-01, -2.27661844509491811950e-01), - float2(-3.24662942872471160793e-01, 5.68939932480760024447e-01), - float2(-5.31263995010459511015e-01, -4.66108719959298256619e-01), - float2( 5.10323549430644951563e-01, 5.81027848262460677731e-01), - float2( 2.82695533021593392586e-01, -7.03582425015577883620e-01), - float2(-5.98419541732174709026e-01, -4.68015982003612274198e-01), - float2(-3.95281650646674975746e-01, 6.10614720709622194050e-01), - float2( 7.87454411900813555647e-01, 1.37726315874787758053e-01), - float2(-7.36310249594224086600e-01, 4.25723821775386646049e-01), - float2( 6.48232481978769037312e-01, -5.53108138515975955585e-01), - float2(-1.88558544306507869237e-01, -7.79120748356531223067e-01), - float2(-3.78614630625567993860e-01, 7.82366459873827913007e-01), - float2(-8.48582606942172357201e-01, -3.78504015913022351381e-01), - float2( 1.91472859899175090748e-02, -9.13050020447597532325e-01), - float2( 8.08826910050883585157e-01, 4.17202663034078935489e-01), - float2(-9.27062588380768493046e-01, -2.94160352051227980130e-01), - float2( 6.67882607007592055126e-01, -6.88642020601400450808e-01), - float2(-1.59349274307943010454e-02, 9.37629353656756814317e-01), - float2( 9.86975590293644233775e-01, 1.44401793964158337014e-01) -}; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli index b91d0ce915..daed3a2921 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli @@ -13,7 +13,6 @@ #include #include #include "BicubicPcfFilters.azsli" -#include "JitterTablePcf.azsli" #include "Shadow.azsli" // ProjectedShadow calculates shadowed area projected from a light. @@ -44,11 +43,6 @@ class ProjectedShadow float GetThickness(); bool IsShadowed(float3 shadowPosition); - bool IsShadowedWithJitter( - float3 jitterUnitX, - float3 jitterUnitY, - float jitterDepthDiffBase, - uint jitterIndex); void SetShadowPosition(); float3 GetAtlasPosition(float2 texturePosition); static float UnprojectDepth(uint shadowIndex, float depthBufferValue); @@ -321,35 +315,6 @@ bool ProjectedShadow::IsShadowed(float3 shadowPosition) return false; } -bool ProjectedShadow::IsShadowedWithJitter( - float3 jitterUnitX, - float3 jitterUnitY, - float jitterDepthDiffBase, - uint jitterIndex) -{ - ViewSrg::ProjectedShadow shadow = ViewSrg::m_projectedShadows[m_shadowIndex]; - const float4x4 depthBiasMatrix = shadow.m_depthBiasMatrix; - const float boundaryScale = shadow.m_boundaryScale; - - const float2 jitterXY = g_jitterTablePcf[jitterIndex]; - - const float dist = distance(m_worldPosition, m_viewPosition); - const float boundaryRadius = dist * tan(boundaryScale); - // jitterWorldXY is the jittering diff vector from the lighted point on the surface - // in the world space. It is remarked as "v_J" in the comment - // named "Calculate depth adjusting diff for jittered samples" - // just before the function GetJitterUnitVectorDepthDiffBase. - const float3 jitterWorldXY = jitterUnitX * (jitterXY.x * boundaryRadius) + jitterUnitY * (jitterXY.y * boundaryRadius); - // The adjusting diff of depth ("d" in the comment) is calculated by - // jitterXY.y * boundaryRadius * jitterDepthDiffBase. - const float3 jitterWorldZ = m_lightDirection * (jitterXY.y * boundaryRadius * jitterDepthDiffBase); - - const float3 jitteredWorldPosition = m_worldPosition + jitterWorldXY + jitterWorldZ; - const float4 jitteredShadowmapHomogeneous = mul(depthBiasMatrix, float4(jitteredWorldPosition, 1)); - - return IsShadowed(jitteredShadowmapHomogeneous.xyz / jitteredShadowmapHomogeneous.w); -} - void ProjectedShadow::SetShadowPosition() { const float4x4 depthBiasMatrix = ViewSrg::m_projectedShadows[m_shadowIndex].m_depthBiasMatrix; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/Shadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/Shadow.azsli index e05ff076cb..1fe81016cf 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/Shadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/Shadow.azsli @@ -23,14 +23,11 @@ struct FilterParameter uint m_isEnabled; uint2 m_shadowmapOriginInSlice; uint m_shadowmapSize; - uint m_parameterOffset; - uint m_parameterCount; float m_lightDistanceOfCameraViewFrustum; float m_n_f_n; // n / (f - n) float m_n_f; // n - f float m_f; // f // where n: nearDepth, f: farDepth. - float2 m_padding; // explicit padding }; class Shadow diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli index 2065e28703..94d6f20da3 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli @@ -22,14 +22,11 @@ partial ShaderResourceGroup ViewSrg uint m_isEnabled; uint2 m_shadowmapOriginInSlice; uint m_shadowmapSize; - uint m_parameterOffset; - uint m_parameterCount; float m_lightDistanceOfCameraViewFrustum; float m_n_f_n; // n / (f - n) float m_n_f; // n - f float m_f; // f // where n: nearDepth, f: farDepth. - float2 m_padding; // explicit padding }; // Simple Point Lights diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 60614993b5..cf456b2e41 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -286,7 +286,6 @@ set(FILES ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli ShaderLib/Atom/Features/Shadow/BicubicPcfFilters.azsli ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli - ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli ShaderLib/Atom/Features/Shadow/Shadow.azsli ShaderLib/Atom/Features/Shadow/ShadowmapAtlasLib.azsli diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h index 75d266cc52..769c7b95a6 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h @@ -154,12 +154,6 @@ namespace AZ //! @param count Sample Count for filtering (up to 64) virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0; - //! This specifies the width of boundary between shadowed area and lit area. - //! @param handle the light handle. - //! @param width Boundary width. The shadow is gradually changed the degree of shadowed. - //! If width == 0, softening edge is disabled. Units are in meters. - virtual void SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth) = 0; - //! Sets whether the directional shadowmap should use receiver plane bias. //! This attempts to reduce shadow acne when using large pcf filters. virtual void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) = 0; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h index 3ab83200ae..5aa2dfb800 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h @@ -90,8 +90,6 @@ namespace AZ virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0; //! Specifies filter method of shadows. virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0; - //! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on the boundary. 0 disables softening. - virtual void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) = 0; //! Sets sample count for filtering of shadow boundary (up to 64) virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0; //! Sets the Esm exponent to use. Higher values produce a steeper falloff in the border areas between light and shadow. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h index 6752ac4c52..1a5a776cdf 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h @@ -70,9 +70,6 @@ namespace AZ virtual void SetShadowBias(LightHandle handle, float bias) = 0; //! Specifies filter method of shadows. virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0; - //! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on - //! the boundary. 0 disables softening. - virtual void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) = 0; //! Sets sample count for filtering of shadow boundary (up to 64) virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0; //! Sets the Esm exponent to use. Higher values produce a steeper falloff in the border areas between light and shadow. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/ShadowConstants.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/ShadowConstants.h index dbad3af21f..309331dcf5 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/ShadowConstants.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/ShadowConstants.h @@ -42,7 +42,6 @@ namespace AZ // [GFX TODO][ATOM-2408] Make the max number of cascade modifiable at runtime. static constexpr uint16_t MaxNumberOfCascades = 4; static constexpr uint16_t MaxPcfSamplingCount = 64; - static constexpr float MaxSofteningBoundaryWidth = 0.1f; } // namespace Shadow } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h index 3d6c0c3015..46560f435d 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h @@ -54,8 +54,6 @@ namespace AZ::Render virtual void SetShadowBias(ShadowId id, float bias) = 0; //! Sets the shadow filter method virtual void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) = 0; - //! Sets the width of boundary between shadowed area and lit area. - virtual void SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) = 0; //! Sets the sample count for filtering of the shadow boundary, max 64. virtual void SetFilteringSampleCount(ShadowId id, uint16_t count) = 0; //! Sets all of the shadow properites in one call diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 42cca0e57c..0a9f3480ad 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -584,15 +584,6 @@ namespace AZ m_shadowBufferNeedsUpdate = true; } - void DirectionalLightFeatureProcessor::SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth) - { - for (auto& it : m_shadowData) - { - it.second.GetData(handle.GetIndex()).m_boundaryScale = boundaryWidth / 2.f; - } - m_shadowBufferNeedsUpdate = true; - } - void DirectionalLightFeatureProcessor::SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) { m_shadowProperties.GetData(handle.GetIndex()).m_isReceiverPlaneBiasEnabled = enable; @@ -1116,50 +1107,13 @@ namespace AZ for (const auto& passIt : m_esmShadowmapsPasses) { const RPI::View* cameraView = passIt.second.front()->GetRenderPipeline()->GetDefaultView().get(); - UpdateStandardDeviations(handle, cameraView); - UpdateFilterOffsetsCounts(handle, cameraView); + UpdateFilterEnabled(handle, cameraView); UpdateShadowmapPositionInAtlas(handle, cameraView); SetFilterParameterToPass(handle, cameraView); } } - void DirectionalLightFeatureProcessor::UpdateStandardDeviations(LightHandle handle, const RPI::View* cameraView) - { - if (handle != m_shadowingLightHandle) - { - return; - } - - const DirectionalLightShadowData& data = m_shadowData.at(cameraView).GetData(handle.GetIndex()); - const ShadowProperty& property = m_shadowProperties.GetData(handle.GetIndex()); - AZStd::fixed_vector standardDeviations; - for (size_t cascadeIndex = 0; cascadeIndex < property.m_segments.at(cameraView).size(); ++cascadeIndex) - { - const Aabb& aabb = property.m_segments.at(cameraView)[cascadeIndex].m_aabb; - const float aabbDiameter = AZStd::GetMax( - aabb.GetMax().GetX() - aabb.GetMin().GetX(), - aabb.GetMax().GetZ() - aabb.GetMin().GetZ()); - float standardDeviation = 0.f; - if (aabbDiameter > 0.f) - { - const float boundaryWidth = data.m_boundaryScale * 2.f; - const float ratioToAabbWidth = boundaryWidth / aabbDiameter; - const float widthInPixels = ratioToAabbWidth * data.m_shadowmapSize; - standardDeviation = widthInPixels / (2 * GaussianMathFilter::ReliableSectionFactor); - } - standardDeviations.push_back(standardDeviation); - } - - for (const RPI::RenderPipelineId& pipelineId : m_renderPipelineIdsForPersistentView.at(cameraView)) - { - for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses.at(pipelineId)) - { - esmPass->SetFilterParameters(standardDeviations); - } - } - } - - void DirectionalLightFeatureProcessor::UpdateFilterOffsetsCounts(LightHandle handle, const RPI::View* cameraView) + void DirectionalLightFeatureProcessor::UpdateFilterEnabled(LightHandle handle, const RPI::View* cameraView) { if (handle != m_shadowingLightHandle) { @@ -1170,29 +1124,11 @@ namespace AZ if (shadowData.m_shadowFilterMethod == aznumeric_cast(ShadowFilterMethod::Esm) || (shadowData.m_shadowFilterMethod == aznumeric_cast(ShadowFilterMethod::EsmPcf))) { - // Get array of filter counts for the camera view. - const RPI::RenderPipelineId& pipelineId = m_renderPipelineIdsForPersistentView.at(cameraView).front(); - AZ_Assert(!m_esmShadowmapsPasses.at(pipelineId).empty(), "Cannot find a EsmShadowmapsPass."); - const AZStd::array_view filterCounts = m_esmShadowmapsPasses.at(pipelineId).front()->GetFilterCounts(); - AZ_Assert(filterCounts.size() == GetCascadeCount(handle), "FilterCounts differs with cascade count."); - - // Create array of filter offsets - AZStd::vector filterOffsets; - filterOffsets.reserve(filterCounts.size()); - uint32_t filterOffset = 0; - for (const uint32_t count : filterCounts) - { - filterOffsets.push_back(filterOffset); - filterOffset += count; - } - // Write filter offsets and filter counts to ESM data for (uint16_t index = 0; index < GetCascadeCount(handle); ++index) { EsmShadowmapsPass::FilterParameter& filterParameter = m_esmParameterData.at(cameraView).GetData(index); filterParameter.m_isEnabled = true; - filterParameter.m_parameterOffset = filterOffsets[index]; - filterParameter.m_parameterCount = filterCounts[index]; } } else @@ -1202,8 +1138,6 @@ namespace AZ { EsmShadowmapsPass::FilterParameter& filterParameter = m_esmParameterData.at(cameraView).GetData(index); filterParameter.m_isEnabled = false; - filterParameter.m_parameterOffset = 0; - filterParameter.m_parameterCount = 0; } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h index 039f51d549..3c1ff8eabd 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h @@ -217,7 +217,6 @@ namespace AZ void SetDebugFlags(LightHandle handle, DebugDrawFlags flags) override; void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; void SetFilteringSampleCount(LightHandle handle, uint16_t count) override; - void SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth) override; void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) override; const Data::Instance GetLightBuffer() const; @@ -278,10 +277,8 @@ namespace AZ //! This updates the parameter of Gaussian filter used in ESM. void UpdateFilterParameters(LightHandle handle); - //! This updates standard deviations for each cascade. - void UpdateStandardDeviations(LightHandle handle, const RPI::View* cameraView); - //! This updates filter offset and size for each cascade. - void UpdateFilterOffsetsCounts(LightHandle handle, const RPI::View* cameraView); + //! This updates if the filter is enabled. + void UpdateFilterEnabled(LightHandle handle, const RPI::View* cameraView); //! This updates shadowmap position(origin and size) in the atlas for each cascade. void UpdateShadowmapPositionInAtlas(LightHandle handle, const RPI::View* cameraView); //! This set filter parameters to passes which execute filtering. diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp index 26e1757a5a..acf81ede32 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp @@ -322,11 +322,6 @@ namespace AZ { SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowFilterMethod, method); } - - void DiskLightFeatureProcessor::SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) - { - SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle, boundaryWidthRadians); - } void DiskLightFeatureProcessor::SetFilteringSampleCount(LightHandle handle, uint16_t count) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h index d65f587718..275712f84f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h @@ -53,7 +53,6 @@ namespace AZ void SetShadowBias(LightHandle handle, float bias) override; void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override; void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override; void SetFilteringSampleCount(LightHandle handle, uint16_t count) override; void SetEsmExponent(LightHandle handle, float esmExponent) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp index 99eaa8a919..b92d538fb5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp @@ -42,28 +42,6 @@ namespace AZ return m_lightTypeName; } - void EsmShadowmapsPass::SetFilterParameters(const AZStd::array_view& standardDeviations) - { - // Set descriptor for Gaussian filters for given set of standard deviations. - MathFilterDescriptor descriptor; - descriptor.m_kind = MathFilterKind::Gaussian; - descriptor.m_gaussians.reserve(standardDeviations.size()); - for (const float standardDeviation : standardDeviations) - { - descriptor.m_gaussians.emplace_back(GaussianFilterDescriptor{ standardDeviation }); - } - - // Set filter paramter buffer along with element counts for each filter. - MathFilter::BufferWithElementCounts bufferCounts = MathFilter::FindOrCreateFilterBuffer(descriptor); - m_filterTableBuffer = bufferCounts.first; - m_filterCounts = AZStd::move(bufferCounts.second); - } - - AZStd::array_view EsmShadowmapsPass::GetFilterCounts() const - { - return m_filterCounts; - } - void EsmShadowmapsPass::SetShadowmapIndexTableBuffer(const Data::Instance& tableBuffer) { m_shadowmapIndexTableBuffer = tableBuffer; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.h index 6e5e1aa311..5a9898d507 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.h @@ -50,14 +50,11 @@ namespace AZ uint32_t m_isEnabled = false; AZStd::array m_shadowmapOriginInSlice = { {0, 0 } }; // shadowmap origin in the slice of the atlas. uint32_t m_shadowmapSize = static_cast(ShadowmapSize::None); // width and height of shadowmap. - uint32_t m_parameterOffset; // offset of the filter parameter. - uint32_t m_parameterCount; // element count of the filter parameter. float m_lightDistanceOfCameraViewFrustum = 0.f; float m_n_f_n = 0.f; // n / (f - n) float m_n_f = 0.f; // n - f float m_f = 0.f; // f // where n: nearDepth, f: farDepth. - AZStd::array m_padding = {{0.f, 0.f}}; // explicit padding }; virtual ~EsmShadowmapsPass() = default; @@ -65,13 +62,6 @@ namespace AZ const Name& GetLightTypeName() const; - //! This sets the standard deviations of the Gaussian filter - //! for each cascade. - void SetFilterParameters(const AZStd::array_view& standardDeviations); - - //! This returns element count of filters. - AZStd::array_view GetFilterCounts() const; - //! This sets the buffer of the table which enable to get shadowmap index //! from the coordinate in the atlas. //! Note that shadowmpa index is shader light index for a spot light diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp index d3b5646e0b..dcf412c35d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp @@ -292,11 +292,6 @@ namespace AZ SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowFilterMethod, method); } - void PointLightFeatureProcessor::SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) - { - SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle, boundaryWidthRadians); - } - void PointLightFeatureProcessor::SetFilteringSampleCount(LightHandle handle, uint16_t count) { SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetFilteringSampleCount, count); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h index b784eb1bb5..54cb0303cc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h @@ -50,7 +50,6 @@ namespace AZ void SetShadowBias(LightHandle handle, float bias) override; void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override; void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override; void SetFilteringSampleCount(LightHandle handle, uint16_t count) override; void SetEsmExponent(LightHandle handle, float esmExponent) override; void SetPointData(LightHandle handle, const PointLightData& data) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index c0202c7c74..c5c81486ce 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -186,17 +186,6 @@ namespace AZ::Render m_filterParameterNeedsUpdate = true; } - void ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) - { - AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBoundaryWidthAngle()."); - - ShadowData& shadowData = m_shadowData.GetElement(id.GetIndex()); - shadowData.m_boundaryScale = boundaryWidthRadians / 2.0f; - - m_shadowmapPassNeedsUpdate = true; - m_filterParameterNeedsUpdate = true; - } - void ProjectedShadowFeatureProcessor::SetFilteringSampleCount(ShadowId id, uint16_t count) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFilteringSampleCount()."); @@ -368,14 +357,13 @@ namespace AZ::Render { if (m_filterParameterNeedsUpdate) { - UpdateStandardDeviations(); - UpdateFilterOffsetsCounts(); + UpdateEsmPassEnabled(); SetFilterParameterToPass(); m_filterParameterNeedsUpdate = false; } } - void ProjectedShadowFeatureProcessor::UpdateStandardDeviations() + void ProjectedShadowFeatureProcessor::UpdateEsmPassEnabled() { if (m_esmShadowmapsPasses.empty()) { @@ -383,24 +371,7 @@ namespace AZ::Render return; } - AZStd::vector standardDeviations(m_shadowProperties.GetDataCount()); - - for (uint32_t i = 0; i < m_shadowProperties.GetDataCount(); ++i) - { - ShadowProperty& shadowProperty = m_shadowProperties.GetDataVector().at(i); - const ShadowData& shadow = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); - if (!FilterMethodIsEsm(shadow)) - { - continue; - } - const FilterParameter& filter = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); - const float boundaryWidthAngle = shadow.m_boundaryScale * 2.0f; - const float fieldOfView = GetMax(shadowProperty.m_desc.m_fieldOfViewYRadians, MinimumFieldOfView); - const float ratioToEntireWidth = boundaryWidthAngle / fieldOfView; - const float widthInPixels = ratioToEntireWidth * filter.m_shadowmapSize; - standardDeviations.at(i) = widthInPixels / (2.0f * GaussianMathFilter::ReliableSectionFactor); - } - if (standardDeviations.empty()) + if (m_shadowProperties.GetDataCount() == 0) { for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { @@ -411,50 +382,6 @@ namespace AZ::Render for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { esmPass->SetEnabledComputation(true); - esmPass->SetFilterParameters(standardDeviations); - } - } - - void ProjectedShadowFeatureProcessor::UpdateFilterOffsetsCounts() - { - if (m_esmShadowmapsPasses.empty()) - { - AZ_Error("ProjectedShadowFeatureProcessor", false, "Cannot find a required pass."); - return; - } - - // Get array of filter counts for the camera view. - const AZStd::array_view filterCounts = m_esmShadowmapsPasses.front()->GetFilterCounts(); - - // Create array of filter offsets. - AZStd::vector filterOffsets; - filterOffsets.reserve(filterCounts.size()); - uint32_t filterOffset = 0; - for (const uint32_t count : filterCounts) - { - filterOffsets.push_back(filterOffset); - filterOffset += count; - } - - auto& shadowProperties = m_shadowProperties.GetDataVector(); - for (uint32_t i = 0; i < shadowProperties.size(); ++i) - { - ShadowProperty& shadowProperty = shadowProperties.at(i); - const ShadowId shadowId = shadowProperty.m_shadowId; - ShadowData& shadowData = m_shadowData.GetElement(shadowId.GetIndex()); - FilterParameter& filterData = m_shadowData.GetElement(shadowId.GetIndex()); - - if (FilterMethodIsEsm(shadowData)) - { - filterData.m_parameterOffset = filterOffsets[i]; - filterData.m_parameterCount = filterCounts[i]; - } - else - { - // If filter is not required, reset offsets and counts of filter in ESM data. - filterData.m_parameterOffset = 0; - filterData.m_parameterCount = 0; - } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index 0b266b9a40..6269166827 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -49,7 +49,6 @@ namespace AZ::Render void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) override; void SetShadowBias(ShadowId id, float bias) override; void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) override; void SetFilteringSampleCount(ShadowId id, uint16_t count) override; void SetShadowProperties(ShadowId id, const ProjectedShadowDescriptor& descriptor) override; const ProjectedShadowDescriptor& GetShadowProperties(ShadowId id) override; @@ -101,8 +100,7 @@ namespace AZ::Render //! Functions to update the parameter of Gaussian filter used in ESM. void UpdateFilterParameters(); - void UpdateStandardDeviations(); - void UpdateFilterOffsetsCounts(); + void UpdateEsmPassEnabled(); void SetFilterParameterToPass(); bool FilterMethodIsEsm(const ShadowData& shadowData) const; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h index 557e6b3dd2..72c4ef97a8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h @@ -120,13 +120,6 @@ namespace AZ //! Sets the filter method of shadows. virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0; - //! Gets the width of softening boundary between shadowed area and lit area in degrees. - virtual float GetSofteningBoundaryWidthAngle() const = 0; - - //! Sets the width of softening boundary between shadowed area and lit area in degrees. - //! 0 disables softening. - virtual void SetSofteningBoundaryWidthAngle(float degrees) = 0; - //! Gets the sample count for filtering of the shadow boundary. virtual uint32_t GetFilteringSampleCount() const = 0; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h index a6d3c6fbed..c76c922385 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h @@ -59,7 +59,6 @@ namespace AZ float m_bias = 0.1f; ShadowmapSize m_shadowmapMaxSize = ShadowmapSize::Size256; ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None; - float m_boundaryWidthInDegrees = 0.25f; uint16_t m_filteringSampleCount = 12; float m_esmExponent = 87.0f; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h index 644856e768..a8088c63ac 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h @@ -153,15 +153,6 @@ namespace AZ //! @param method filter method. virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0; - //! This gets the width of boundary between shadowed area and lit area. - //! @return Boundary width. The shadow is gradually changed the degree of shadowed. - virtual float GetSofteningBoundaryWidth() const = 0; - - //! This specifies the width of boundary between shadowed area and lit area. - //! @param width Boundary width. The shadow is gradually changed the degree of shadowed. - //! If width == 0, softening edge is disabled. Units are in meters. - virtual void SetSofteningBoundaryWidth(float width) = 0; - //! This gets the sample count for filtering of the shadow boundary. //! @return Sample Count for filtering (up to 64) virtual uint32_t GetFilteringSampleCount() const = 0; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h index 0123d06275..a58acc0114 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h @@ -101,10 +101,6 @@ namespace AZ //! Method of shadow's filtering. ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None; - //! Width of the boundary between shadowed area and lit one. - //! If this is 0, edge softening is disabled. Units are in meters. - float m_boundaryWidth = 0.03f; // 3cm - //! Sample Count for filtering (from 4 to 64) //! It is used only when the pixel is predicted as on the boundary. uint16_t m_filteringSampleCount = 32; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp index c7af44a28e..f0418a5024 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp @@ -36,7 +36,6 @@ namespace AZ ->Field("Shadow Bias", &AreaLightComponentConfig::m_bias) ->Field("Shadowmap Max Size", &AreaLightComponentConfig::m_shadowmapMaxSize) ->Field("Shadow Filter Method", &AreaLightComponentConfig::m_shadowFilterMethod) - ->Field("Softening Boundary Width", &AreaLightComponentConfig::m_boundaryWidthInDegrees) ->Field("Filtering Sample Count", &AreaLightComponentConfig::m_filteringSampleCount) ->Field("Esm Exponent", &AreaLightComponentConfig::m_esmExponent) ; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp index c0204ecac5..36cb2a7f5a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp @@ -74,8 +74,6 @@ namespace AZ::Render ->Event("SetShadowmapMaxSize", &AreaLightRequestBus::Events::SetShadowmapMaxSize) ->Event("GetShadowFilterMethod", &AreaLightRequestBus::Events::GetShadowFilterMethod) ->Event("SetShadowFilterMethod", &AreaLightRequestBus::Events::SetShadowFilterMethod) - ->Event("GetSofteningBoundaryWidthAngle", &AreaLightRequestBus::Events::GetSofteningBoundaryWidthAngle) - ->Event("SetSofteningBoundaryWidthAngle", &AreaLightRequestBus::Events::SetSofteningBoundaryWidthAngle) ->Event("GetFilteringSampleCount", &AreaLightRequestBus::Events::GetFilteringSampleCount) ->Event("SetFilteringSampleCount", &AreaLightRequestBus::Events::SetFilteringSampleCount) ->Event("GetEsmExponent", &AreaLightRequestBus::Events::GetEsmExponent) @@ -95,7 +93,6 @@ namespace AZ::Render ->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias") ->VirtualProperty("ShadowmapMaxSize", "GetShadowmapMaxSize", "SetShadowmapMaxSize") ->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod") - ->VirtualProperty("SofteningBoundaryWidthAngle", "GetSofteningBoundaryWidthAngle", "SetSofteningBoundaryWidthAngle") ->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount") ->VirtualProperty("EsmExponent", "GetEsmExponent", "SetEsmExponent"); ; @@ -307,7 +304,6 @@ namespace AZ::Render m_lightShapeDelegate->SetShadowBias(m_configuration.m_bias); m_lightShapeDelegate->SetShadowmapMaxSize(m_configuration.m_shadowmapMaxSize); m_lightShapeDelegate->SetShadowFilterMethod(m_configuration.m_shadowFilterMethod); - m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(m_configuration.m_boundaryWidthInDegrees); m_lightShapeDelegate->SetFilteringSampleCount(m_configuration.m_filteringSampleCount); m_lightShapeDelegate->SetEsmExponent(m_configuration.m_esmExponent); } @@ -506,20 +502,6 @@ namespace AZ::Render } } - float AreaLightComponentController::GetSofteningBoundaryWidthAngle() const - { - return m_configuration.m_boundaryWidthInDegrees; - } - - void AreaLightComponentController::SetSofteningBoundaryWidthAngle(float width) - { - m_configuration.m_boundaryWidthInDegrees = width; - if (m_lightShapeDelegate) - { - m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(width); - } - } - uint32_t AreaLightComponentController::GetFilteringSampleCount() const { return m_configuration.m_filteringSampleCount; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h index 3bec61551f..cc6223e7e5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h @@ -82,8 +82,6 @@ namespace AZ void SetShadowmapMaxSize(ShadowmapSize size) override; ShadowFilterMethod GetShadowFilterMethod() const override; void SetShadowFilterMethod(ShadowFilterMethod method) override; - float GetSofteningBoundaryWidthAngle() const override; - void SetSofteningBoundaryWidthAngle(float width) override; uint32_t GetFilteringSampleCount() const override; void SetFilteringSampleCount(uint32_t count) override; float GetEsmExponent() const override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp index 37d94f5ed1..9d384c2e24 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp @@ -37,7 +37,6 @@ namespace AZ ->Field("IsCascadeCorrectionEnabled", &DirectionalLightComponentConfig::m_isCascadeCorrectionEnabled) ->Field("IsDebugColoringEnabled", &DirectionalLightComponentConfig::m_isDebugColoringEnabled) ->Field("ShadowFilterMethod", &DirectionalLightComponentConfig::m_shadowFilterMethod) - ->Field("SofteningBoundaryWidth", &DirectionalLightComponentConfig::m_boundaryWidth) ->Field("PcfFilteringSampleCount", &DirectionalLightComponentConfig::m_filteringSampleCount) ->Field("ShadowReceiverPlaneBiasEnabled", &DirectionalLightComponentConfig::m_receiverPlaneBiasEnabled); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp index fbc1ccc35d..78558cfc85 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp @@ -80,8 +80,6 @@ namespace AZ ->Event("SetDebugColoringEnabled", &DirectionalLightRequestBus::Events::SetDebugColoringEnabled) ->Event("GetShadowFilterMethod", &DirectionalLightRequestBus::Events::GetShadowFilterMethod) ->Event("SetShadowFilterMethod", &DirectionalLightRequestBus::Events::SetShadowFilterMethod) - ->Event("GetSofteningBoundaryWidth", &DirectionalLightRequestBus::Events::GetSofteningBoundaryWidth) - ->Event("SetSofteningBoundaryWidth", &DirectionalLightRequestBus::Events::SetSofteningBoundaryWidth) ->Event("GetFilteringSampleCount", &DirectionalLightRequestBus::Events::GetFilteringSampleCount) ->Event("SetFilteringSampleCount", &DirectionalLightRequestBus::Events::SetFilteringSampleCount) ->Event("GetShadowReceiverPlaneBiasEnabled", &DirectionalLightRequestBus::Events::GetShadowReceiverPlaneBiasEnabled) @@ -99,7 +97,6 @@ namespace AZ ->VirtualProperty("ViewFrustumCorrectionEnabled", "GetViewFrustumCorrectionEnabled", "SetViewFrustumCorrectionEnabled") ->VirtualProperty("DebugColoringEnabled", "GetDebugColoringEnabled", "SetDebugColoringEnabled") ->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod") - ->VirtualProperty("SofteningBoundaryWidth", "GetSofteningBoundaryWidth", "SetSofteningBoundaryWidth") ->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount") ->VirtualProperty("ShadowReceiverPlaneBiasEnabled", "GetShadowReceiverPlaneBiasEnabled", "SetShadowReceiverPlaneBiasEnabled"); ; @@ -404,21 +401,6 @@ namespace AZ } } - float DirectionalLightComponentController::GetSofteningBoundaryWidth() const - { - return m_configuration.m_boundaryWidth; - } - - void DirectionalLightComponentController::SetSofteningBoundaryWidth(float width) - { - width = GetMin(Shadow::MaxSofteningBoundaryWidth, GetMax(0.f, width)); - m_configuration.m_boundaryWidth = width; - if (m_featureProcessor) - { - m_featureProcessor->SetShadowBoundaryWidth(m_lightHandle, width); - } - } - uint32_t DirectionalLightComponentController::GetFilteringSampleCount() const { return aznumeric_cast(m_configuration.m_filteringSampleCount); @@ -517,7 +499,6 @@ namespace AZ SetViewFrustumCorrectionEnabled(m_configuration.m_isCascadeCorrectionEnabled); SetDebugColoringEnabled(m_configuration.m_isDebugColoringEnabled); SetShadowFilterMethod(m_configuration.m_shadowFilterMethod); - SetSofteningBoundaryWidth(m_configuration.m_boundaryWidth); SetFilteringSampleCount(m_configuration.m_filteringSampleCount); SetShadowReceiverPlaneBiasEnabled(m_configuration.m_receiverPlaneBiasEnabled); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h index b8052bfc36..933f2705e7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h @@ -76,8 +76,6 @@ namespace AZ void SetDebugColoringEnabled(bool enabled) override; ShadowFilterMethod GetShadowFilterMethod() const override; void SetShadowFilterMethod(ShadowFilterMethod method) override; - float GetSofteningBoundaryWidth() const override; - void SetSofteningBoundaryWidth(float width) override; uint32_t GetFilteringSampleCount() const override; void SetFilteringSampleCount(uint32_t count) override; bool GetShadowReceiverPlaneBiasEnabled() const override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp index baf0cdced1..ebc79abca5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp @@ -147,14 +147,6 @@ namespace AZ::Render } } - void DiskLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees) - { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees)); - } - } - void DiskLightDelegate::SetFilteringSampleCount(uint32_t count) { if (GetShadowsEnabled() && GetLightHandle().IsValid()) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h index e0fd16f6be..2be782c69c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h @@ -44,7 +44,6 @@ namespace AZ void SetShadowBias(float bias) override; void SetShadowmapMaxSize(ShadowmapSize size) override; void SetShadowFilterMethod(ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(float widthInDegrees) override; void SetFilteringSampleCount(uint32_t count) override; void SetEsmExponent(float exponent) override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index 954ec4cad8..1e5b2580f7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp @@ -154,15 +154,6 @@ namespace AZ ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled) - ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_boundaryWidthInDegrees, "Softening boundary width", - "Width of the boundary between shadowed area and lit one. " - "Units are in degrees. " - "If this is 0, softening edge is disabled.") - ->Attribute(Edit::Attributes::Min, 0.f) - ->Attribute(Edit::Attributes::Max, 1.f) - ->Attribute(Edit::Attributes::Suffix, " deg") - ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) - ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsEsmDisabled) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_filteringSampleCount, "Filtering sample count", "This is only used when the pixel is predicted to be on the boundary. Specific to PCF and ESM+PCF.") ->Attribute(Edit::Attributes::Min, 4) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp index 2854244f6b..69ba295e9b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp @@ -133,15 +133,6 @@ namespace AZ ->EnumAttribute(ShadowFilterMethod::Esm, "ESM") ->EnumAttribute(ShadowFilterMethod::EsmPcf, "ESM+PCF") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_boundaryWidth, "Softening boundary width", - "Width of the boundary between shadowed area and lit one. " - "Units are in meters. " - "If this is 0, softening edge is disabled.") - ->Attribute(Edit::Attributes::Min, 0.f) - ->Attribute(Edit::Attributes::Max, 0.1f) - ->Attribute(Edit::Attributes::Suffix, " m") - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->Attribute(Edit::Attributes::ReadOnly, &DirectionalLightComponentConfig::IsEsmDisabled) ->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_filteringSampleCount, "Filtering sample count", "This is used only when the pixel is predicted as on the boundary. " "Specific to PCF and ESM+PCF.") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h index 2bd25b76a3..336c67f55d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h @@ -56,7 +56,6 @@ namespace AZ void SetShadowBias([[maybe_unused]] float bias) override {}; void SetShadowmapMaxSize([[maybe_unused]] ShadowmapSize size) override {}; void SetShadowFilterMethod([[maybe_unused]] ShadowFilterMethod method) override {}; - void SetSofteningBoundaryWidthAngle([[maybe_unused]] float widthInDegrees) override {}; void SetFilteringSampleCount([[maybe_unused]] uint32_t count) override {}; void SetEsmExponent([[maybe_unused]] float esmExponent) override{}; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h index 6d08971542..9bb8188898 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h @@ -75,8 +75,6 @@ namespace AZ virtual void SetShadowmapMaxSize(ShadowmapSize size) = 0; //! Sets the filter method for the shadow virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0; - //! Sets the width of boundary between shadowed area and lit area in degrees. - virtual void SetSofteningBoundaryWidthAngle(float widthInDegrees) = 0; //! Sets the sample count for filtering of the shadow boundary, max 64. virtual void SetFilteringSampleCount(uint32_t count) = 0; //! Sets the Esm exponent to use. Higher values produce a steeper falloff between light and shadow. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp index 8853db5751..661b0c6b25 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp @@ -92,14 +92,6 @@ namespace AZ::Render } } - void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees) - { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees)); - } - } - void SphereLightDelegate::SetFilteringSampleCount(uint32_t count) { if (GetShadowsEnabled() && GetLightHandle().IsValid()) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h index e2903b2d72..8bdee2442a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h @@ -34,7 +34,6 @@ namespace AZ void SetShadowBias(float bias) override; void SetShadowmapMaxSize(ShadowmapSize size) override; void SetShadowFilterMethod(ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(float widthInDegrees) override; void SetFilteringSampleCount(uint32_t count) override; void SetEsmExponent(float esmExponent) override; From d7ac1be72633ed74f6077aa69241d27d8e55ae60 Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Wed, 13 Oct 2021 11:46:29 -0700 Subject: [PATCH 018/152] Adding directional light shadow bias Signed-off-by: mrieggeramzn --- .../Shadow/DirectionalLightShadow.azsli | 16 ++++++++------- .../CoreLights/ViewSrg.azsli | 3 ++- ...irectionalLightFeatureProcessorInterface.h | 3 +++ .../DirectionalLightFeatureProcessor.cpp | 13 ++++++++---- .../DirectionalLightFeatureProcessor.h | 10 ++++------ .../CoreLights/DirectionalLightBus.h | 8 ++++++++ .../DirectionalLightComponentConfig.h | 3 +++ .../DirectionalLightComponentConfig.cpp | 3 ++- .../DirectionalLightComponentController.cpp | 20 ++++++++++++++++++- .../DirectionalLightComponentController.h | 2 ++ .../EditorDirectionalLightComponent.cpp | 17 ++++++++++++---- 11 files changed, 74 insertions(+), 24 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli index 633ea85387..b53dda13aa 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli @@ -111,16 +111,18 @@ void DirectionalLightShadow::GetShadowCoords( float3 worldPosition, out float3 shadowCoords[ViewSrg::MaxCascadeCount]) { - const float4x4 depthBiasMatrices[ViewSrg::MaxCascadeCount] = - ViewSrg::m_directionalLightShadows[lightIndex].m_depthBiasMatrices; const uint cascadeCount = ViewSrg::m_directionalLightShadows[lightIndex].m_cascadeCount; - + const float shadowBias = ViewSrg::m_directionalLightShadows[lightIndex].m_shadowBias; + const float4x4 lightViewToShadowmapMatrices[ViewSrg::MaxCascadeCount] = ViewSrg::m_directionalLightShadows[lightIndex].m_lightViewToShadowmapMatrices; + const float4x4 worldToLightViewMatrices[ViewSrg::MaxCascadeCount] = ViewSrg::m_directionalLightShadows[lightIndex].m_worldToLightViewMatrices; + for (uint index = 0; index < cascadeCount; ++index) { - const float4x4 depthBiasMatrix = depthBiasMatrices[index]; - const float4 shadowCoordHomogeneous = mul(depthBiasMatrix, - float4(worldPosition, 1.)); - shadowCoords[index] = shadowCoordHomogeneous.xyz / shadowCoordHomogeneous.w; + float4 lightSpacePos = mul(worldToLightViewMatrices[index], float4(worldPosition, 1.)); + lightSpacePos.z += shadowBias; + + const float4 clipSpacePos = mul(lightViewToShadowmapMatrices[index], lightSpacePos); + shadowCoords[index] = clipSpacePos.xyz / clipSpacePos.w; } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli index 94d6f20da3..27fa36182c 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli @@ -102,18 +102,19 @@ partial ShaderResourceGroup ViewSrg struct DirectionalLightShadow { - float4x4 m_depthBiasMatrices[MaxCascadeCount]; float4x4 m_lightViewToShadowmapMatrices[MaxCascadeCount]; float4x4 m_worldToLightViewMatrices[MaxCascadeCount]; float m_slopeBiasBase[MaxCascadeCount]; float m_boundaryScale; uint m_shadowmapSize; // width and height of shadowmap uint m_cascadeCount; + float m_shadowBias; uint m_predictionSampleCount; uint m_filteringSampleCount; uint m_debugFlags; uint m_shadowFilterMethod; float m_far_minus_near; + float3 m_padding; }; enum ShadowFilterMethod diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h index 769c7b95a6..2bba1338a1 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h @@ -157,6 +157,9 @@ namespace AZ //! Sets whether the directional shadowmap should use receiver plane bias. //! This attempts to reduce shadow acne when using large pcf filters. virtual void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) = 0; + + //! Reduces acne by applying a small amount of bias along shadow-space z. + virtual void SetShadowBias(LightHandle handle, float bias) = 0; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 0a9f3480ad..4774ac31a1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -589,6 +589,15 @@ namespace AZ m_shadowProperties.GetData(handle.GetIndex()).m_isReceiverPlaneBiasEnabled = enable; } + void DirectionalLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias) + { + for (auto& it : m_shadowData) + { + it.second.GetData(handle.GetIndex()).m_shadowBias = bias; + } + m_shadowBufferNeedsUpdate = true; + } + void DirectionalLightFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) { PrepareForChangingRenderPipelineAndCameraView(); @@ -1522,10 +1531,6 @@ namespace AZ for (uint16_t cascadeIndex = 0; cascadeIndex < GetCascadeCount(handle); ++cascadeIndex) { - const Matrix4x4& worldToLightClipMatrix = property.m_segments.at(cameraView)[cascadeIndex].m_view->GetWorldToClipMatrix(); - const Matrix4x4 depthBiasMatrix = Shadow::GetClipToShadowmapTextureMatrix() * worldToLightClipMatrix; - shadowData.m_depthBiasMatrices[cascadeIndex] = depthBiasMatrix; - const Matrix4x4& lightViewToLightClipMatrix = property.m_segments.at(cameraView)[cascadeIndex].m_view->GetViewToClipMatrix(); const Matrix4x4 lightViewToShadowmapMatrix = Shadow::GetClipToShadowmapTextureMatrix() * lightViewToLightClipMatrix; shadowData.m_lightViewToShadowmapMatrices[cascadeIndex] = lightViewToShadowmapMatrix; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h index 3c1ff8eabd..2cf5b0b1e6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h @@ -72,12 +72,6 @@ namespace AZ // [GFX TODO][ATOM-15172] Look into compacting struct DirectionalLightShadowData struct DirectionalLightShadowData { - AZStd::array m_depthBiasMatrices = - { { - Matrix4x4::CreateIdentity(), - Matrix4x4::CreateIdentity(), - Matrix4x4::CreateIdentity(), - Matrix4x4::CreateIdentity() } }; AZStd::array m_lightViewToShadowmapMatrices = { { Matrix4x4::CreateIdentity(), @@ -97,11 +91,14 @@ namespace AZ float m_boundaryScale = 0.f; uint32_t m_shadowmapSize = 1; // width and height of shadowmap uint32_t m_cascadeCount = 1; + // Reduce acne by applying a small amount of bias to apply along shadow-space z. + float m_shadowBias = 0.0f; uint32_t m_predictionSampleCount = 0; uint32_t m_filteringSampleCount = 0; uint32_t m_debugFlags = 0; uint32_t m_shadowFilterMethod = 0; float m_far_minus_near = 0; + float m_padding[3]; }; class DirectionalLightFeatureProcessor final @@ -218,6 +215,7 @@ namespace AZ void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; void SetFilteringSampleCount(LightHandle handle, uint16_t count) override; void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) override; + void SetShadowBias(LightHandle handle, float bias) override; const Data::Instance GetLightBuffer() const; uint32_t GetLightCount() const; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h index a8088c63ac..9ccc4f329b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h @@ -168,6 +168,14 @@ namespace AZ //! Sets whether the directional shadowmap should use receiver plane bias. //! @param enable flag specifying whether to enable the receiver plane bias feature virtual void SetShadowReceiverPlaneBiasEnabled(bool enable) = 0; + + //! Shadow bias reduces acne by applying a small amount of offset along shadow-space z. + //! @return Returns the amount of bias to apply. + virtual float GetShadowBias() const = 0; + + //! Shadow bias reduces acne by applying a small amount of offset along shadow-space z. + //! @param Sets the amount of bias to apply. + virtual void SetShadowBias(float bias) = 0; }; using DirectionalLightRequestBus = EBus; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h index a58acc0114..20073f437c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h @@ -109,6 +109,9 @@ namespace AZ //! This uses partial derivatives to reduce shadow acne when using large pcf kernels. bool m_receiverPlaneBiasEnabled = true; + //! Reduces shadow acne by applying a small amount of offset along shadow-space z. + float m_shadowBias = 0.0f; + bool IsSplitManual() const; bool IsSplitAutomatic() const; bool IsCascadeCorrectionDisabled() const; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp index 9d384c2e24..98d3f838c0 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp @@ -38,7 +38,8 @@ namespace AZ ->Field("IsDebugColoringEnabled", &DirectionalLightComponentConfig::m_isDebugColoringEnabled) ->Field("ShadowFilterMethod", &DirectionalLightComponentConfig::m_shadowFilterMethod) ->Field("PcfFilteringSampleCount", &DirectionalLightComponentConfig::m_filteringSampleCount) - ->Field("ShadowReceiverPlaneBiasEnabled", &DirectionalLightComponentConfig::m_receiverPlaneBiasEnabled); + ->Field("ShadowReceiverPlaneBiasEnabled", &DirectionalLightComponentConfig::m_receiverPlaneBiasEnabled) + ->Field("Shadow Bias", &DirectionalLightComponentConfig::m_shadowBias); } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp index 78558cfc85..c20a9f8e17 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp @@ -84,6 +84,8 @@ namespace AZ ->Event("SetFilteringSampleCount", &DirectionalLightRequestBus::Events::SetFilteringSampleCount) ->Event("GetShadowReceiverPlaneBiasEnabled", &DirectionalLightRequestBus::Events::GetShadowReceiverPlaneBiasEnabled) ->Event("SetShadowReceiverPlaneBiasEnabled", &DirectionalLightRequestBus::Events::SetShadowReceiverPlaneBiasEnabled) + ->Event("GetShadowBias", &DirectionalLightRequestBus::Events::GetShadowBias) + ->Event("SetShadowBias", &DirectionalLightRequestBus::Events::SetShadowBias) ->VirtualProperty("Color", "GetColor", "SetColor") ->VirtualProperty("Intensity", "GetIntensity", "SetIntensity") ->VirtualProperty("AngularDiameter", "GetAngularDiameter", "SetAngularDiameter") @@ -98,7 +100,8 @@ namespace AZ ->VirtualProperty("DebugColoringEnabled", "GetDebugColoringEnabled", "SetDebugColoringEnabled") ->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod") ->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount") - ->VirtualProperty("ShadowReceiverPlaneBiasEnabled", "GetShadowReceiverPlaneBiasEnabled", "SetShadowReceiverPlaneBiasEnabled"); + ->VirtualProperty("ShadowReceiverPlaneBiasEnabled", "GetShadowReceiverPlaneBiasEnabled", "SetShadowReceiverPlaneBiasEnabled") + ->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias"); ; } } @@ -406,6 +409,20 @@ namespace AZ return aznumeric_cast(m_configuration.m_filteringSampleCount); } + void DirectionalLightComponentController::SetShadowBias(float bias) + { + m_configuration.m_shadowBias = bias; + if (m_featureProcessor) + { + m_featureProcessor->SetShadowBias(m_lightHandle, bias); + } + } + + float DirectionalLightComponentController::GetShadowBias() const + { + return m_configuration.m_shadowBias; + } + void DirectionalLightComponentController::SetFilteringSampleCount(uint32_t count) { const uint16_t count16 = GetMin(Shadow::MaxPcfSamplingCount, aznumeric_cast(count)); @@ -499,6 +516,7 @@ namespace AZ SetViewFrustumCorrectionEnabled(m_configuration.m_isCascadeCorrectionEnabled); SetDebugColoringEnabled(m_configuration.m_isDebugColoringEnabled); SetShadowFilterMethod(m_configuration.m_shadowFilterMethod); + SetShadowBias(m_configuration.m_shadowBias); SetFilteringSampleCount(m_configuration.m_filteringSampleCount); SetShadowReceiverPlaneBiasEnabled(m_configuration.m_receiverPlaneBiasEnabled); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h index 933f2705e7..a0d552cf99 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h @@ -80,6 +80,8 @@ namespace AZ void SetFilteringSampleCount(uint32_t count) override; bool GetShadowReceiverPlaneBiasEnabled() const override; void SetShadowReceiverPlaneBiasEnabled(bool enable) override; + float GetShadowBias() const override; + void SetShadowBias(float width) override; private: friend class EditorDirectionalLightComponent; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp index 69ba295e9b..99e2cc1485 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp @@ -133,8 +133,8 @@ namespace AZ ->EnumAttribute(ShadowFilterMethod::Esm, "ESM") ->EnumAttribute(ShadowFilterMethod::EsmPcf, "ESM+PCF") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_filteringSampleCount, "Filtering sample count", - "This is used only when the pixel is predicted as on the boundary. " + ->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_filteringSampleCount, "Filtering sample count\n", + "This is used only when the pixel is predicted as on the boundary.\n" "Specific to PCF and ESM+PCF.") ->Attribute(Edit::Attributes::Min, 4) ->Attribute(Edit::Attributes::Max, 64) @@ -142,10 +142,19 @@ namespace AZ ->Attribute(Edit::Attributes::ReadOnly, &DirectionalLightComponentConfig::IsShadowPcfDisabled) ->DataElement( Edit::UIHandlers::CheckBox, &DirectionalLightComponentConfig::m_receiverPlaneBiasEnabled, - "Shadow Receiver Plane Bias Enable", + "Shadow Receiver Plane Bias Enable\n", "This reduces shadow acne when using large pcf kernels.") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->Attribute(Edit::Attributes::ReadOnly, &DirectionalLightComponentConfig::IsShadowPcfDisabled); + ->Attribute(Edit::Attributes::ReadOnly, &DirectionalLightComponentConfig::IsShadowPcfDisabled) + ->DataElement( + Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_shadowBias, + "Shadow Bias\n", + "Reduces acne by applying a fixed bias along z in shadow-space.\n" + "If this is 0, no biasing is applied.") + ->Attribute(Edit::Attributes::Min, 0.f) + ->Attribute(Edit::Attributes::Max, 0.2) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ; } } From b1a76feead62a5fc8d0cee7ac104ed47ddbeae93 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 14 Oct 2021 23:21:04 +0100 Subject: [PATCH 019/152] Added tests. Made network spawnable to serialize in binary Signed-off-by: pereslav --- Gems/Multiplayer/Code/CMakeLists.txt | 11 +++ .../Pipeline/NetworkPrefabProcessor.cpp | 2 +- ...TestMultiplayerComponent.AutoComponent.xml | 13 ++++ .../Code/Tests/ClientHierarchyTests.cpp | 70 ++++++++++++++++- .../Code/Tests/CommonHierarchySetup.h | 17 ++++ .../Code/Tests/TestMultiplayerComponent.cpp | 78 +++++++++++++++++++ .../Code/Tests/TestMultiplayerComponent.h | 61 +++++++++++++++ .../Code/multiplayer_tests_files.cmake | 8 ++ 8 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 Gems/Multiplayer/Code/Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml create mode 100644 Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp create mode 100644 Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index 909590a25d..fd2c3f3f06 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -163,6 +163,7 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAMESPACE Gem FILES_CMAKE multiplayer_tests_files.cmake + multiplayer_autogen_files.cmake INCLUDE_DIRECTORIES PRIVATE Tests @@ -175,6 +176,16 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) PRIVATE AZ::AzTest Gem::Multiplayer.Static + AUTOGEN_RULES + *.AutoPackets.xml,AutoPackets_Header.jinja,$path/$fileprefix.AutoPackets.h + *.AutoPackets.xml,AutoPackets_Inline.jinja,$path/$fileprefix.AutoPackets.inl + *.AutoPackets.xml,AutoPackets_Source.jinja,$path/$fileprefix.AutoPackets.cpp + *.AutoPackets.xml,AutoPacketDispatcher_Header.jinja,$path/$fileprefix.AutoPacketDispatcher.h + *.AutoPackets.xml,AutoPacketDispatcher_Inline.jinja,$path/$fileprefix.AutoPacketDispatcher.inl + *.AutoComponent.xml,AutoComponent_Header.jinja,$path/$fileprefix.AutoComponent.h + *.AutoComponent.xml,AutoComponent_Source.jinja,$path/$fileprefix.AutoComponent.cpp + *.AutoComponent.xml,AutoComponentTypes_Header.jinja,$path/AutoComponentTypes.h + *.AutoComponent.xml,AutoComponentTypes_Source.jinja,$path/AutoComponentTypes.cpp ) ly_add_googletest( NAME Gem::Multiplayer.Tests diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index e0990aa785..27e59c0bf4 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -110,7 +110,7 @@ namespace Multiplayer auto serializer = [](AZStd::vector& output, const ProcessedObjectStore& object) -> bool { AZ::IO::ByteContainerStream stream(&output); auto& asset = object.GetAsset(); - return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_JSON, &asset, asset.GetType()); + return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_BINARY, &asset, asset.GetType()); }; auto&& [object, networkSpawnable] = diff --git a/Gems/Multiplayer/Code/Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml new file mode 100644 index 0000000000..6b18e5ac70 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index d6dd068c3f..badee73e04 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include namespace Multiplayer { @@ -175,10 +177,10 @@ namespace Multiplayer void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child) { PopulateHierarchicalEntity(root); - SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Client); + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Autonomous); PopulateHierarchicalEntity(child); - SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Client); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Autonomous); // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); @@ -330,7 +332,7 @@ namespace Multiplayer void CreateDeepHierarchyOnClient(EntityInfo& childOfChild) { PopulateHierarchicalEntity(childOfChild); - SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Client); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Autonomous); // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childOfChild->m_netId); @@ -387,4 +389,66 @@ namespace Multiplayer ); } } + + TEST_F(ClientDeepHierarchyTests, CreateProcessInputTest) + { + using MultiplayerTest::TestMultiplayerComponent; + using MultiplayerTest::TestMultiplayerComponentController; + using MultiplayerTest::TestMultiplayerComponentNetworkInput; + + auto* rootNetBind = m_root->m_entity->FindComponent(); + + NetworkInputArray inputArray(rootNetBind->GetEntityHandle()); + NetworkInput& input = inputArray[0]; + + const float deltaTime = 0.16f; + rootNetBind->CreateInput(input, deltaTime); + + auto ValidateCreatedInput = [](const NetworkInput& input, const HierarchyTests::EntityInfo& entityInfo) + { + // Validate test input for the root entity's TestMultiplayerComponent + auto* testInput = input.FindComponentInput(); + EXPECT_NE(testInput, nullptr); + + auto* testMultiplayerComponent = entityInfo.m_entity->FindComponent(); + EXPECT_NE(testMultiplayerComponent, nullptr); + + EXPECT_EQ(testInput->m_ownerId, testMultiplayerComponent->GetId()); + }; + + // Validate root input + ValidateCreatedInput(input, *m_root); + + // Validate children input + { + NetworkHierarchyRootComponentNetworkInput* rootHierarchyInput = input.FindComponentInput(); + const AZStd::vector& childInputs = rootHierarchyInput->m_childInputs; + EXPECT_EQ(childInputs.size(), 2); + ValidateCreatedInput(childInputs[0].GetNetworkInput(), *m_child); + ValidateCreatedInput(childInputs[1].GetNetworkInput(), *m_childOfChild); + } + + // Test ProcessInput + { + AZStd::unordered_set inputProcessedEntities; + size_t processInputCallCounter = 0; + auto processInputCallback = [&inputProcessedEntities, &processInputCallCounter](NetEntityId netEntityId) + { + inputProcessedEntities.insert(netEntityId); + processInputCallCounter++; + }; + + // Set the callbacks for processing input. This allows us to inspect how many times the input was processed + // and which entity's controller was invoked. + m_root->m_entity->FindComponent()->m_processInputCallback = processInputCallback; + m_child->m_entity->FindComponent()->m_processInputCallback = processInputCallback; + m_childOfChild->m_entity->FindComponent()->m_processInputCallback = processInputCallback; + + rootNetBind->ProcessInput(input, deltaTime); + + EXPECT_EQ(processInputCallCounter, 3); + EXPECT_EQ(inputProcessedEntities, + AZStd::unordered_set({ m_root->m_netId, m_child->m_netId, m_childOfChild->m_netId })); + } + } } diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h index d6f65918c0..38027c11a9 100644 --- a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace Multiplayer { @@ -93,6 +94,12 @@ namespace Multiplayer m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor()); m_netTransformDescriptor->Reflect(m_serializeContext.get()); + m_testMultiplayerComponentDescriptor.reset(MultiplayerTest::TestMultiplayerComponent::CreateDescriptor()); + m_testMultiplayerComponentDescriptor->Reflect(m_serializeContext.get()); + + m_testInputDriverComponentDescriptor.reset(MultiplayerTest::TestInputDriverComponent::CreateDescriptor()); + m_testInputDriverComponentDescriptor->Reflect(m_serializeContext.get()); + m_mockMultiplayer = AZStd::make_unique>(); AZ::Interface::Register(m_mockMultiplayer.get()); @@ -103,6 +110,7 @@ namespace Multiplayer GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0); m_mockNetworkEntityManager = AZStd::make_unique>(); + AZ::Interface::Register(m_mockNetworkEntityManager.get()); ON_CALL(*m_mockNetworkEntityManager, AddEntityToEntityMap(_, _)).WillByDefault(Invoke(this, &HierarchyTests::AddEntityToEntityMap)); ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::GetEntity)); @@ -136,6 +144,7 @@ namespace Multiplayer m_multiplayerComponentRegistry = AZStd::make_unique(); ON_CALL(*m_mockNetworkEntityManager, GetMultiplayerComponentRegistry()).WillByDefault(Return(m_multiplayerComponentRegistry.get())); RegisterMultiplayerComponents(); + MultiplayerTest::RegisterMultiplayerComponents(); } void TearDown() override @@ -157,6 +166,7 @@ namespace Multiplayer AZ::Interface::Unregister(m_mockNetworkTime.get()); AZ::Interface::Unregister(m_mockTime.get()); + AZ::Interface::Unregister(m_mockNetworkEntityManager.get()); AZ::Interface::Unregister(m_mockMultiplayer.get()); AZ::Interface::Unregister(m_mockComponentApplicationRequests.get()); @@ -165,6 +175,8 @@ namespace Multiplayer m_mockNetworkEntityManager.reset(); m_mockMultiplayer.reset(); + m_testInputDriverComponentDescriptor.reset(); + m_testMultiplayerComponentDescriptor.reset(); m_transformDescriptor.reset(); m_netTransformDescriptor.reset(); m_hierarchyRootDescriptor.reset(); @@ -186,6 +198,8 @@ namespace Multiplayer AZStd::unique_ptr m_hierarchyRootDescriptor; AZStd::unique_ptr m_hierarchyChildDescriptor; AZStd::unique_ptr m_netTransformDescriptor; + AZStd::unique_ptr m_testMultiplayerComponentDescriptor; + AZStd::unique_ptr m_testInputDriverComponentDescriptor; AZStd::unique_ptr> m_mockMultiplayer; AZStd::unique_ptr m_mockNetworkEntityManager; @@ -394,6 +408,9 @@ namespace Multiplayer entityInfo.m_entity->CreateComponent(); entityInfo.m_entity->CreateComponent(); entityInfo.m_entity->CreateComponent(); + entityInfo.m_entity->CreateComponent(); + entityInfo.m_entity->CreateComponent(); + switch (entityInfo.m_role) { case EntityInfo::Role::Root: diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp new file mode 100644 index 0000000000..336082eb01 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp @@ -0,0 +1,78 @@ +/* +* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. +* +* SPDX-License-Identifier: Apache-2.0 OR MIT +* +*/ +#pragma once + +#include + +#include + +namespace MultiplayerTest +{ + void TestInputDriverComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + } + } + + void TestMultiplayerComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + } + TestMultiplayerComponentBase::Reflect(context); + } + + void TestMultiplayerComponent::OnInit() + { + } + + void TestMultiplayerComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + } + + void TestMultiplayerComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + } + + TestMultiplayerComponentController::TestMultiplayerComponentController(TestMultiplayerComponent& parent) + : TestMultiplayerComponentControllerBase(parent) + { + } + + void TestMultiplayerComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + } + + void TestMultiplayerComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + } + + void TestMultiplayerComponentController::CreateInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) + { + auto* networkInput = input.FindComponentInput(); + networkInput->m_ownerId = GetParent().GetId(); + } + + void TestMultiplayerComponentController::ProcessInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) + { + auto& component = GetParent(); + auto* networkInput = input.FindComponentInput(); + AZ_Assert(networkInput->m_ownerId == component.GetId(), "Input Id doesn't match the owner component Id"); + + if (component.m_processInputCallback) + { + component.m_processInputCallback(GetNetEntityId()); + } + } +} diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h new file mode 100644 index 0000000000..6b2f8e49a5 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h @@ -0,0 +1,61 @@ +/* +* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. +* +* SPDX-License-Identifier: Apache-2.0 OR MIT +* +*/ +#pragma once + +#include + +namespace MultiplayerTest +{ + // Dummy class for satisfying "MultiplayerInputDriver" component dependency + class TestInputDriverComponent : public AZ::Component + { + public: + AZ_COMPONENT(TestInputDriverComponent, "{C3877905-3B61-45AE-A636-9845C3AAA39D}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.emplace_back(AZ_CRC_CE("MultiplayerInputDriver")); + } + + void Activate(){}; + void Deactivate(){}; + }; + + // Test multiplayer component with ability to create and process network input + class TestMultiplayerComponent + : public TestMultiplayerComponentBase + { + public: + AZ_MULTIPLAYER_COMPONENT(MultiplayerTest::TestMultiplayerComponent, s_testMultiplayerComponentConcreteUuid, MultiplayerTest::TestMultiplayerComponentBase); + + static void Reflect(AZ::ReflectContext* context); + + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + AZStd::function m_processInputCallback; + }; + + // Multiplayer controller for the test component + class TestMultiplayerComponentController + : public TestMultiplayerComponentControllerBase + { + public: + TestMultiplayerComponentController(TestMultiplayerComponent& parent); + + //! TestMultiplayerComponentControllerBase + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + //! MultiplayerController interface + void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override; + void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override; + }; +} diff --git a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake index 2114143b68..d1aea1cb82 100644 --- a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake @@ -7,6 +7,12 @@ # set(FILES + Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja + Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja + Include/Multiplayer/AutoGen/AutoComponent_Common.jinja + Include/Multiplayer/AutoGen/AutoComponent_Header.jinja + Include/Multiplayer/AutoGen/AutoComponent_Source.jinja + Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml Tests/ClientHierarchyTests.cpp Tests/ServerHierarchyBenchmarks.cpp Tests/CommonHierarchySetup.h @@ -19,4 +25,6 @@ set(FILES Tests/RewindableContainerTests.cpp Tests/RewindableObjectTests.cpp Tests/ServerHierarchyTests.cpp + Tests/TestMultiplayerComponent.h + Tests/TestMultiplayerComponent.cpp ) From 3c1a2db636858fa758cfb6d4cef743a5e520c38f Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 14 Oct 2021 23:27:58 +0100 Subject: [PATCH 020/152] Removed unnecessary files from CMakeLists.txt Signed-off-by: pereslav --- Gems/Multiplayer/Code/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index fd2c3f3f06..b971e6a2ce 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -163,7 +163,6 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAMESPACE Gem FILES_CMAKE multiplayer_tests_files.cmake - multiplayer_autogen_files.cmake INCLUDE_DIRECTORIES PRIVATE Tests @@ -177,11 +176,6 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest Gem::Multiplayer.Static AUTOGEN_RULES - *.AutoPackets.xml,AutoPackets_Header.jinja,$path/$fileprefix.AutoPackets.h - *.AutoPackets.xml,AutoPackets_Inline.jinja,$path/$fileprefix.AutoPackets.inl - *.AutoPackets.xml,AutoPackets_Source.jinja,$path/$fileprefix.AutoPackets.cpp - *.AutoPackets.xml,AutoPacketDispatcher_Header.jinja,$path/$fileprefix.AutoPacketDispatcher.h - *.AutoPackets.xml,AutoPacketDispatcher_Inline.jinja,$path/$fileprefix.AutoPacketDispatcher.inl *.AutoComponent.xml,AutoComponent_Header.jinja,$path/$fileprefix.AutoComponent.h *.AutoComponent.xml,AutoComponent_Source.jinja,$path/$fileprefix.AutoComponent.cpp *.AutoComponent.xml,AutoComponentTypes_Header.jinja,$path/AutoComponentTypes.h From a6e7a81b7941e6251e30e2b6ccc15a87518e5b92 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 14 Oct 2021 18:22:41 -0700 Subject: [PATCH 021/152] More fixes to migration, plus some quality of life fixes to TimeoutQueue to allow lambda based handlers and a quick fix to OctreeSystemComponent to more gracefully handle enumerate calls that lie outside the vis system bounds Signed-off-by: kberg-amzn --- .../Visibility/OctreeSystemComponent.cpp | 62 +++++-------------- .../DataStructures/TimeoutQueue.cpp | 10 ++- .../DataStructures/TimeoutQueue.h | 6 ++ .../LocalPredictionPlayerInputComponent.h | 8 +++ .../NetworkInput/NetworkInputArray.h | 0 .../NetworkInput/NetworkInputChild.h | 0 .../NetworkInput/NetworkInputHistory.h | 0 .../NetworkInputMigrationVector.h | 0 ...tionPlayerInputComponent.AutoComponent.xml | 6 +- .../LocalPredictionPlayerInputComponent.cpp | 10 +++ .../Debug/MultiplayerDebugSystemComponent.cpp | 10 ++- .../Source/NetworkInput/NetworkInputArray.cpp | 2 +- .../Source/NetworkInput/NetworkInputChild.cpp | 2 +- .../NetworkInput/NetworkInputHistory.cpp | 2 +- .../NetworkInputMigrationVector.cpp | 2 +- Gems/Multiplayer/Code/multiplayer_files.cmake | 8 +-- 16 files changed, 66 insertions(+), 62 deletions(-) rename Gems/Multiplayer/Code/{Source => Include/Multiplayer}/NetworkInput/NetworkInputArray.h (100%) rename Gems/Multiplayer/Code/{Source => Include/Multiplayer}/NetworkInput/NetworkInputChild.h (100%) rename Gems/Multiplayer/Code/{Source => Include/Multiplayer}/NetworkInput/NetworkInputHistory.h (100%) rename Gems/Multiplayer/Code/{Source => Include/Multiplayer}/NetworkInput/NetworkInputMigrationVector.h (100%) diff --git a/Code/Framework/AzFramework/AzFramework/Visibility/OctreeSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Visibility/OctreeSystemComponent.cpp index b4cad8511f..cd52393df9 100644 --- a/Code/Framework/AzFramework/AzFramework/Visibility/OctreeSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Visibility/OctreeSystemComponent.cpp @@ -14,9 +14,8 @@ namespace AzFramework { AZ_CVAR(bool, bg_octreeUseQuadtree, false, nullptr, AZ::ConsoleFunctorFlags::ReadOnly, "If set to true, the visibility octrees will degenerate to a quadtree split along the X/Y plane"); AZ_CVAR(float, bg_octreeMaxWorldExtents, 16384.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum supported world size by the world octreeSystemComponent"); - AZ_CVAR(uint32_t, bg_octreeNodeMaxEntries, 64, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of entries to allow in any node before forcing a split"); - AZ_CVAR(uint32_t, bg_octreeNodeMinEntries, 32, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of entries to allow in a node resulting from a merge operation"); - + AZ_CVAR(uint32_t, bg_octreeNodeMaxEntries, 64, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of entries to allow in any node before forcing a split"); + AZ_CVAR(uint32_t, bg_octreeNodeMinEntries, 32, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of entries to allow in a node resulting from a merge operation"); static uint32_t GetChildNodeCount() { @@ -25,14 +24,12 @@ namespace AzFramework return (bg_octreeUseQuadtree) ? QuadtreeNodeChildCount : OctreeNodeChildCount; } - OctreeNode::OctreeNode(const AZ::Aabb& bounds) : m_bounds(bounds) { ; } - OctreeNode::OctreeNode(OctreeNode&& rhs) : m_bounds(rhs.m_bounds) , m_parent(rhs.m_parent) @@ -46,7 +43,6 @@ namespace AzFramework } } - OctreeNode& OctreeNode::operator=(OctreeNode&& rhs) { m_bounds = rhs.m_bounds; @@ -63,7 +59,6 @@ namespace AzFramework return *this; } - void OctreeNode::Insert(OctreeScene& octreeScene, VisibilityEntry* entry) { AZ_Assert(entry->m_internalNode == nullptr, "Double-insertion: Insert invoked for an entry already bound to the OctreeScene"); @@ -98,7 +93,6 @@ namespace AzFramework } } - void OctreeNode::Update(OctreeScene& octreeScene, VisibilityEntry* entry) { AZ_Assert(entry->m_internalNode == this, "Update invoked for an entry bound to a different OctreeNode"); @@ -129,7 +123,6 @@ namespace AzFramework } } - void OctreeNode::Remove(OctreeScene& octreeScene, VisibilityEntry* entry) { AZ_Assert(entry->m_internalNode == this, "Remove invoked for an entry bound to a different OctreeNode"); @@ -152,25 +145,30 @@ namespace AzFramework } } - void OctreeNode::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const { - EnumerateHelper(aabb, callback); + if (AZ::ShapeIntersection::Overlaps(aabb, m_bounds)) + { + EnumerateHelper(aabb, callback); + } } - void OctreeNode::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const { - EnumerateHelper(sphere, callback); + if (AZ::ShapeIntersection::Overlaps(sphere, m_bounds)) + { + EnumerateHelper(sphere, callback); + } } - void OctreeNode::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const { - EnumerateHelper(frustum, callback); + if (AZ::ShapeIntersection::Overlaps(frustum, m_bounds)) + { + EnumerateHelper(frustum, callback); + } } - void OctreeNode::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const { // Invoke the callback for the current node @@ -190,25 +188,21 @@ namespace AzFramework } } - const AZStd::vector& OctreeNode::GetEntries() const { return m_entries; } - OctreeNode* OctreeNode::GetChildren() const { return m_children; } - bool OctreeNode::IsLeaf() const { return m_children == nullptr; } - void OctreeNode::TryMerge(OctreeScene& octreeScene) { if (IsLeaf()) @@ -236,7 +230,6 @@ namespace AzFramework } } - template void OctreeNode::EnumerateHelper(const T& boundingVolume, const IVisibilityScene::EnumerateCallback& callback) const { @@ -262,7 +255,6 @@ namespace AzFramework } } - void OctreeNode::Split(OctreeScene& octreeScene) { AZ_Assert(m_children == nullptr, "Split invoked on an octreeScene node that has already been split"); @@ -312,7 +304,6 @@ namespace AzFramework } } - void OctreeNode::Merge(OctreeScene& octreeScene) { AZ_Assert(m_children != nullptr, "Merge invoked on an octreeScene node that does not have children"); @@ -371,7 +362,6 @@ namespace AzFramework } } - void OctreeScene::RemoveEntry(VisibilityEntry& entry) { AZStd::lock_guard lock(m_sharedMutex); @@ -382,35 +372,30 @@ namespace AzFramework } } - void OctreeScene::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const { AZStd::shared_lock lock(m_sharedMutex); m_root.Enumerate(aabb, callback); } - void OctreeScene::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const { AZStd::shared_lock lock(m_sharedMutex); m_root.Enumerate(sphere, callback); } - void OctreeScene::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const { AZStd::shared_lock lock(m_sharedMutex); m_root.Enumerate(frustum, callback); } - void OctreeScene::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const { AZStd::shared_lock lock(m_sharedMutex); m_root.EnumerateNoCull(callback); } - uint32_t OctreeScene::GetEntryCount() const { return m_entryCount; @@ -421,26 +406,22 @@ namespace AzFramework return m_nodeCount; } - uint32_t OctreeScene::GetFreeNodeCount() const { // Each entry represents GetChildNodeCount() nodes return aznumeric_cast(m_freeOctreeNodes.size() * GetChildNodeCount()); } - uint32_t OctreeScene::GetPageCount() const { return aznumeric_cast(m_nodeCache.size()); } - uint32_t OctreeScene::GetChildNodeCount() const { return AzFramework::GetChildNodeCount(); } - void OctreeScene::DumpStats() { AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::EntryCount = %u", GetName().GetCStr(), GetEntryCount()); @@ -450,21 +431,18 @@ namespace AzFramework AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::ChildNodeCount = %u", GetName().GetCStr(), GetChildNodeCount()); } - static inline uint32_t CreateNodeIndex(uint32_t page, uint32_t offset) { AZ_Assert(page <= 0xFFFF && offset <= 0xFFFF, "Out of range values passed to CreateNodeIndex"); return (page << 16) | offset; } - static inline void ExtractPageAndOffsetFromIndex(uint32_t index, uint32_t& page, uint32_t& offset) { offset = index & 0x0000FFFF; page = index >> 16; } - uint32_t OctreeScene::AllocateChildNodes() { const uint32_t childCount = GetChildNodeCount(); @@ -508,14 +486,12 @@ namespace AzFramework return CreateNodeIndex(nextChildPage, nextChildOffset); } - void OctreeScene::ReleaseChildNodes(uint32_t nodeIndex) { m_nodeCount -= GetChildNodeCount(); m_freeOctreeNodes.push(nodeIndex); } - OctreeNode* OctreeScene::GetChildNodesAtIndex(uint32_t nodeIndex) const { uint32_t childPage; @@ -524,7 +500,6 @@ namespace AzFramework return &(*m_nodeCache[childPage])[childOffset]; } - void OctreeSystemComponent::Reflect(AZ::ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) @@ -534,19 +509,16 @@ namespace AzFramework } } - void OctreeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC("OctreeService")); } - void OctreeSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { incompatible.push_back(AZ_CRC("OctreeService")); } - OctreeSystemComponent::OctreeSystemComponent() { AZ::Interface::Register(this); @@ -555,7 +527,6 @@ namespace AzFramework m_defaultScene = aznew OctreeScene(AZ::Name("DefaultVisibilityScene")); } - OctreeSystemComponent::~OctreeSystemComponent() { AZ_Assert(m_scenes.empty(), "All IVisibilityScenes must be destroyed before shutdown"); @@ -566,13 +537,11 @@ namespace AzFramework AZ::Interface::Unregister(this); } - void OctreeSystemComponent::Activate() { ; } - void OctreeSystemComponent::Deactivate() { ; @@ -591,7 +560,6 @@ namespace AzFramework return newScene; } - void OctreeSystemComponent::DestroyVisibilityScene(IVisibilityScene* visScene) { for (auto iter = m_scenes.begin(); iter != m_scenes.end(); ++iter) @@ -606,7 +574,6 @@ namespace AzFramework AZ_Assert(false, "visScene[\"%s\"] not found in the OctreeSystemComponent", visScene->GetName().GetCStr()); } - IVisibilityScene* OctreeSystemComponent::FindVisibilityScene(const AZ::Name& sceneName) { for (OctreeScene* scene : m_scenes) @@ -619,7 +586,6 @@ namespace AzFramework return nullptr; } - void OctreeSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { for (OctreeScene* scene : m_scenes) diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp index 88392c98c0..b0f316cf50 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp @@ -53,7 +53,7 @@ namespace AzNetworking m_timeoutItemMap.erase(timeoutId); } - void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts) + void TimeoutQueue::UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts) { int32_t numTimeouts = 0; if (maxTimeouts < 0) @@ -103,7 +103,7 @@ namespace AzNetworking // By this point, the item is definitely timed out // Invoke the timeout function to see how to proceed - const TimeoutResult result = timeoutHandler.HandleTimeout(mapItem); + const TimeoutResult result = timeoutHandler(mapItem); if (result == TimeoutResult::Refresh) { @@ -122,4 +122,10 @@ namespace AzNetworking m_timeoutItemMap.erase(itemTimeoutId); } } + + void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts) + { + TimeoutHandler handler([&timeoutHandler](TimeoutQueue::TimeoutItem& item) { return timeoutHandler.HandleTimeout(item); }); + UpdateTimeouts(handler, maxTimeouts); + } } diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h index 239af5be0e..63417ea36f 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h @@ -64,6 +64,12 @@ namespace AzNetworking //! @param timeoutId the identifier of the item to remove void RemoveItem(TimeoutId timeoutId); + //! Updates timeouts for all items, invokes the provided timeout functor if required. + //! @param timeoutHandler lambda to invoke for all timeouts + //! @param maxTimeouts the maximum number of timeouts to process before breaking iteration + using TimeoutHandler = AZStd::function; + void UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1); + //! Updates timeouts for all items, invokes timeout handlers if required. //! @param timeoutHandler listener instance to call back on for timeouts //! @param maxTimeouts the maximum number of timeouts to process before breaking iteration diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h index bcbea6542f..b300a2bbf7 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h @@ -57,6 +57,14 @@ namespace Multiplayer const AzNetworking::PacketEncodingBuffer& correction ) override; + //! Forcibly enables ProcessInput to execute on the entity. + //! Note that this function is quite dangerous and should normally never be used + void ForceEnableAutonomousUpdate(); + + //! Forcibly disables ProcessInput from executing on the entity. + //! Note that this function is quite dangerous and should normally never be used + void ForceDisableAutonomousUpdate(); + //! Return true if we're currently migrating from one host to another. //! @return boolean true if we're currently migrating from one host to another bool IsMigrating() const; diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInputArray.h similarity index 100% rename from Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h rename to Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInputArray.h diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInputChild.h similarity index 100% rename from Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h rename to Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInputChild.h diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputHistory.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInputHistory.h similarity index 100% rename from Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputHistory.h rename to Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInputHistory.h diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInputMigrationVector.h similarity index 100% rename from Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h rename to Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInputMigrationVector.h diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index 1a7496a77d..55c7d37d49 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -12,9 +12,9 @@ - - - + + + diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 747f53d37b..5006213e8e 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -353,6 +353,16 @@ namespace Multiplayer } } + void LocalPredictionPlayerInputComponentController::ForceEnableAutonomousUpdate() + { + m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true); + } + + void LocalPredictionPlayerInputComponentController::ForceDisableAutonomousUpdate() + { + m_autonomousUpdateEvent.RemoveFromQueue(); + } + bool LocalPredictionPlayerInputComponentController::IsMigrating() const { return m_lastMigratedInputId != ClientInputId{ 0 }; diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp index 422b2f0cae..048b3ce6d2 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp @@ -28,24 +28,29 @@ namespace Multiplayer ->Version(1); } } + void MultiplayerDebugSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent")); } + void MultiplayerDebugSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) { ; } + void MultiplayerDebugSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatbile) { incompatbile.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent")); } + void MultiplayerDebugSystemComponent::Activate() { #ifdef IMGUI_ENABLED ImGui::ImGuiUpdateListenerBus::Handler::BusConnect(); #endif } + void MultiplayerDebugSystemComponent::Deactivate() { #ifdef IMGUI_ENABLED @@ -74,6 +79,7 @@ namespace Multiplayer ImGui::EndMenu(); } } + void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond) { uint64_t summedCalls = 0; @@ -106,6 +112,7 @@ namespace Multiplayer ImGui::Text("%11.2f", bytesPerSecond); return open; } + bool DrawSummaryRow(const char* name, const MultiplayerStats& stats) { const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics(); @@ -122,6 +129,7 @@ namespace Multiplayer AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond); return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond); } + bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId) { const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId); @@ -138,6 +146,7 @@ namespace Multiplayer AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond); return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond); } + void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId) { MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry(); @@ -479,4 +488,3 @@ void OnDebugEntities_ShowBandwidth_Changed(const bool& showBandwidth) AZ::Interface::Get()->HideEntityBandwidthDebugOverlay(); } } - diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.cpp index 638dc9a900..9d566537c5 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.cpp @@ -6,7 +6,7 @@ * */ -#include +#include #include #include #include diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.cpp index bea110f298..59fb62e1b5 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.cpp @@ -6,7 +6,7 @@ * */ -#include +#include #include #include diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputHistory.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputHistory.cpp index 54813327ab..00fd9f13b8 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputHistory.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputHistory.cpp @@ -6,7 +6,7 @@ * */ -#include +#include namespace Multiplayer { diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.cpp index d95c46261f..ec57891725 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.cpp @@ -6,7 +6,7 @@ * */ -#include +#include #include #include diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index f16483e663..a799278203 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -45,6 +45,10 @@ set(FILES Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h Include/Multiplayer/NetworkInput/NetworkInput.h + Include/Multiplayer/NetworkInput/NetworkInputArray.h + Include/Multiplayer/NetworkInput/NetworkInputChild.h + Include/Multiplayer/NetworkInput/NetworkInputHistory.h + Include/Multiplayer/NetworkInput/NetworkInputMigrationVector.h Include/Multiplayer/NetworkTime/INetworkTime.h Include/Multiplayer/NetworkTime/RewindableArray.h Include/Multiplayer/NetworkTime/RewindableArray.inl @@ -114,13 +118,9 @@ set(FILES Source/NetworkEntity/NetworkSpawnableLibrary.h Source/NetworkInput/NetworkInput.cpp Source/NetworkInput/NetworkInputArray.cpp - Source/NetworkInput/NetworkInputArray.h Source/NetworkInput/NetworkInputChild.cpp - Source/NetworkInput/NetworkInputChild.h Source/NetworkInput/NetworkInputHistory.cpp - Source/NetworkInput/NetworkInputHistory.h Source/NetworkInput/NetworkInputMigrationVector.cpp - Source/NetworkInput/NetworkInputMigrationVector.h Source/NetworkTime/NetworkTime.cpp Source/NetworkTime/NetworkTime.h Source/Pipeline/NetworkSpawnableHolderComponent.cpp From 05493b538f13f8f85131ea301d9204f43e6d3356 Mon Sep 17 00:00:00 2001 From: pereslav Date: Fri, 15 Oct 2021 13:13:19 +0100 Subject: [PATCH 022/152] Added tracking of hierarchy root ownership when hierarchies are migrated Signed-off-by: pereslav --- .../NetworkEntity/NetworkEntityManager.cpp | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index db7f7243cc..b72ea21719 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include namespace Multiplayer @@ -272,15 +274,32 @@ namespace Multiplayer bool safeToExit = true; NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId); - // We also need special handling for the EntityHierarchyComponent as well, since related entities need to be migrated together - //auto* hierarchyController = FindController(nonConstExitingEntityPtr); - //if (hierarchyController) - //{ - // if (hierarchyController->GetParentRelatedEntity()) - // { - // safeToExit = false; - // } - //} + // We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together + NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController(); + NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController(); + + // Find the root entity + AZ::Entity* hierarchyRootEntity = nullptr; + if (hierarchyRootController) + { + hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot(); + } + else if (hierarchyChildController) + { + hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot(); + } + + if (hierarchyRootEntity) + { + NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId()); + ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId); + + // Check if the root entity is still tracked by this authority + if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController()) + { + safeToExit = false; + } + } // Validate that we aren't already planning to remove this entity if (safeToExit) From 1d6542c274a1d734759be2e724515e889f71b04d Mon Sep 17 00:00:00 2001 From: hultonha Date: Thu, 14 Oct 2021 18:23:55 +0100 Subject: [PATCH 023/152] initial wip code to update cursor states Signed-off-by: hultonha --- .../Input/QtEventToAzInputManager.cpp | 31 +++++++++++++++++++ .../Input/QtEventToAzInputManager.h | 5 +++ .../Viewport/ViewportMessages.h | 4 +++ .../ViewportSelection/EditorHelpers.cpp | 6 ++++ .../Viewport/RenderViewportWidget.h | 2 ++ .../Source/Viewport/RenderViewportWidget.cpp | 10 ++++++ 6 files changed, 58 insertions(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index 18acccf049..d146dc91da 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -256,6 +256,14 @@ namespace AzToolsFramework // If our focus changes, go ahead and reset all input devices. HandleFocusChange(event); + if (eventType == QEvent::FocusOut) + { + if (m_capturingCursor) + { + qApp->restoreOverrideCursor(); + } + } + // If we focus in on the source widget and the mouse is contained in its // bounds, refresh the cached cursor position to ensure it is up to date (this // ensures cursor positions are refreshed correctly with context menu focus changes) @@ -264,6 +272,11 @@ namespace AzToolsFramework const auto globalCursorPosition = QCursor::pos(); if (m_sourceWidget->geometry().contains(globalCursorPosition)) { + if (m_capturingCursor) + { + qApp->setOverrideCursor(Qt::ForbiddenCursor); + } + HandleMouseMoveEvent(globalCursorPosition); } } @@ -452,4 +465,22 @@ namespace AzToolsFramework } } } + + void QtEventToAzInputMapper::PushCursor(/*enum*/) + { + if (!m_overrideCursor) + { + qApp->setOverrideCursor(Qt::ForbiddenCursor); + m_overrideCursor = true; + } + } + + void QtEventToAzInputMapper::PopCursor() + { + if (m_overrideCursor) + { + qApp->restoreOverrideCursor(); + m_overrideCursor = false; + } + } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index 5add24ad84..8127738dd3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -55,6 +55,9 @@ namespace AzToolsFramework //! like a dolly or rotation, where mouse movement is important but cursor location is not. void SetCursorCaptureEnabled(bool enabled); + void PushCursor(/*enum*/); + void PopCursor(); + // QObject overrides... bool eventFilter(QObject* object, QEvent* event) override; @@ -164,6 +167,8 @@ namespace AzToolsFramework bool m_enabled = true; // Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement). bool m_capturingCursor = false; + // + bool m_overrideCursor = true; // Our viewport-specific AZ devices. We control their internal input channel states. AZStd::unique_ptr m_mouseDevice; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 9d2f7e9b90..93ca8706eb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -310,6 +310,10 @@ namespace AzToolsFramework virtual void EndCursorCapture() = 0; //! Is the mouse over the viewport. virtual bool IsMouseOver() const = 0; + //! + virtual void PushOverrideCursor(/*enum*/) {} // make virtual + //! + virtual void PopOverrideCursor() {} // make virtual protected: ~ViewportMouseCursorRequests() = default; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index bb718d0dc9..912e1ccff5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -189,9 +189,15 @@ namespace AzToolsFramework // Verify if the entity Id corresponds to an entity that is focused; if not, halt selection. if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor)) { + ViewportInteraction::ViewportMouseCursorRequestBus::Event( + viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::PushOverrideCursor); + return AZ::EntityId(); } + ViewportInteraction::ViewportMouseCursorRequestBus::Event( + viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::PopOverrideCursor); + // Container Entity support - if the entity that is being selected is part of a closed container, // change the selection to the container instead. if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index b407aa2b4c..eebe445f7b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -102,6 +102,8 @@ namespace AtomToolsFramework void BeginCursorCapture() override; void EndCursorCapture() override; bool IsMouseOver() const override; + void PushOverrideCursor(/*enum*/) override; + void PopOverrideCursor() override; // AzFramework::WindowRequestBus::Handler overrides ... void SetWindowTitle(const AZStd::string& title) override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 83926ecc79..df639c8026 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -367,6 +367,16 @@ namespace AtomToolsFramework m_inputChannelMapper->SetCursorCaptureEnabled(false); } + void RenderViewportWidget::PushOverrideCursor(/*enum*/) + { + m_inputChannelMapper->PushCursor(); + } + + void RenderViewportWidget::PopOverrideCursor() + { + m_inputChannelMapper->PopCursor(); + } + void RenderViewportWidget::SetWindowTitle(const AZStd::string& title) { setWindowTitle(QString::fromUtf8(title.c_str())); From 3d4c060fb5101b3cf01226ffc550ee36863c87c2 Mon Sep 17 00:00:00 2001 From: hultonha Date: Fri, 15 Oct 2021 15:19:38 +0100 Subject: [PATCH 024/152] updates to improve cursor lock behavior Signed-off-by: hultonha --- .../Input/QtEventToAzInputManager.cpp | 45 +++++++++++-------- .../Input/QtEventToAzInputManager.h | 11 ++--- .../Viewport/ViewportMessages.h | 14 ++++-- .../ViewportSelection/EditorHelpers.cpp | 7 +-- .../Viewport/RenderViewportWidget.h | 4 +- .../Source/Viewport/RenderViewportWidget.cpp | 8 ++-- 6 files changed, 52 insertions(+), 37 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index d146dc91da..1f30fc1d19 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -244,6 +244,16 @@ namespace AzToolsFramework const auto eventType = event->type(); + if (eventType == QEvent::Type::MouseMove) + { + const auto* mouseEvent = static_cast(event); + if (m_overrideCursor && !m_sourceWidget->geometry().contains(mouseEvent->pos())) + { + qApp->restoreOverrideCursor(); + m_overrideCursor = false; + } + } + // Only accept mouse & key release events that originate from an object that is not our target widget, // as we don't want to erroneously intercept user input meant for another component. if (object != m_sourceWidget && eventType != QEvent::Type::KeyRelease && eventType != QEvent::Type::MouseButtonRelease) @@ -256,27 +266,14 @@ namespace AzToolsFramework // If our focus changes, go ahead and reset all input devices. HandleFocusChange(event); - if (eventType == QEvent::FocusOut) - { - if (m_capturingCursor) - { - qApp->restoreOverrideCursor(); - } - } - // If we focus in on the source widget and the mouse is contained in its // bounds, refresh the cached cursor position to ensure it is up to date (this // ensures cursor positions are refreshed correctly with context menu focus changes) if (eventType == QEvent::FocusIn) { const auto globalCursorPosition = QCursor::pos(); - if (m_sourceWidget->geometry().contains(globalCursorPosition)) + if (m_sourceWidget->geometry().contains(m_sourceWidget->mapFromGlobal(globalCursorPosition))) { - if (m_capturingCursor) - { - qApp->setOverrideCursor(Qt::ForbiddenCursor); - } - HandleMouseMoveEvent(globalCursorPosition); } } @@ -466,16 +463,26 @@ namespace AzToolsFramework } } - void QtEventToAzInputMapper::PushCursor(/*enum*/) + static Qt::CursorShape QtCursorFromAzCursor(const ViewportInteraction::CursorStyleOverride cursorStyleOverride) { - if (!m_overrideCursor) + switch (cursorStyleOverride) { - qApp->setOverrideCursor(Qt::ForbiddenCursor); - m_overrideCursor = true; + case ViewportInteraction::CursorStyleOverride::Forbidden: + return Qt::ForbiddenCursor; + default: + return Qt::ArrowCursor; } } - void QtEventToAzInputMapper::PopCursor() + void QtEventToAzInputMapper::SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride) + { + ClearOverrideCursor(); + + qApp->setOverrideCursor(QtCursorFromAzCursor(cursorStyleOverride)); + m_overrideCursor = true; + } + + void QtEventToAzInputMapper::ClearOverrideCursor() { if (m_overrideCursor) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index 8127738dd3..4cb391e63b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -15,10 +15,11 @@ #include #include #include - #include #include +#include + #include #include #include @@ -55,8 +56,8 @@ namespace AzToolsFramework //! like a dolly or rotation, where mouse movement is important but cursor location is not. void SetCursorCaptureEnabled(bool enabled); - void PushCursor(/*enum*/); - void PopCursor(); + void SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride); + void ClearOverrideCursor(); // QObject overrides... bool eventFilter(QObject* object, QEvent* event) override; @@ -167,8 +168,8 @@ namespace AzToolsFramework bool m_enabled = true; // Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement). bool m_capturingCursor = false; - // - bool m_overrideCursor = true; + // Flags whether the cursor has been overridden. + bool m_overrideCursor = false; // Our viewport-specific AZ devices. We control their internal input channel states. AZStd::unique_ptr m_mouseDevice; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 93ca8706eb..15e871459d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -300,6 +300,12 @@ namespace AzToolsFramework using EditorViewportInputTimeNowRequestBus = AZ::EBus; + //! The style of cursor override. + enum class CursorStyleOverride + { + Forbidden + }; + //! Viewport requests for managing the viewport cursor state. class ViewportMouseCursorRequests { @@ -310,10 +316,10 @@ namespace AzToolsFramework virtual void EndCursorCapture() = 0; //! Is the mouse over the viewport. virtual bool IsMouseOver() const = 0; - //! - virtual void PushOverrideCursor(/*enum*/) {} // make virtual - //! - virtual void PopOverrideCursor() {} // make virtual + //! Set the cursor style override. + virtual void SetOverrideCursor(CursorStyleOverride cursorStyleOverride) = 0; + //! Clear the current cursor style override. + virtual void ClearOverrideCursor() = 0; protected: ~ViewportMouseCursorRequests() = default; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 912e1ccff5..2f39834651 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -187,16 +187,17 @@ namespace AzToolsFramework } // Verify if the entity Id corresponds to an entity that is focused; if not, halt selection. - if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor)) + if (entityIdUnderCursor.IsValid() && !IsSelectableAccordingToFocusMode(entityIdUnderCursor)) { ViewportInteraction::ViewportMouseCursorRequestBus::Event( - viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::PushOverrideCursor); + viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::SetOverrideCursor, + ViewportInteraction::CursorStyleOverride::Forbidden); return AZ::EntityId(); } ViewportInteraction::ViewportMouseCursorRequestBus::Event( - viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::PopOverrideCursor); + viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::ClearOverrideCursor); // Container Entity support - if the entity that is being selected is part of a closed container, // change the selection to the container instead. diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index eebe445f7b..ded9e00535 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -102,8 +102,8 @@ namespace AtomToolsFramework void BeginCursorCapture() override; void EndCursorCapture() override; bool IsMouseOver() const override; - void PushOverrideCursor(/*enum*/) override; - void PopOverrideCursor() override; + void SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) override; + void ClearOverrideCursor() override; // AzFramework::WindowRequestBus::Handler overrides ... void SetWindowTitle(const AZStd::string& title) override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index df639c8026..88897dddf1 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -367,14 +367,14 @@ namespace AtomToolsFramework m_inputChannelMapper->SetCursorCaptureEnabled(false); } - void RenderViewportWidget::PushOverrideCursor(/*enum*/) + void RenderViewportWidget::SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) { - m_inputChannelMapper->PushCursor(); + m_inputChannelMapper->SetOverrideCursor(cursorStyleOverride); } - void RenderViewportWidget::PopOverrideCursor() + void RenderViewportWidget::ClearOverrideCursor() { - m_inputChannelMapper->PopCursor(); + m_inputChannelMapper->ClearOverrideCursor(); } void RenderViewportWidget::SetWindowTitle(const AZStd::string& title) From c955880d9b78e481c1971762154456b47b99bec0 Mon Sep 17 00:00:00 2001 From: hultonha Date: Fri, 15 Oct 2021 15:30:36 +0100 Subject: [PATCH 025/152] ensure cursor position is mapped to the right widget space Signed-off-by: hultonha --- .../AzToolsFramework/Input/QtEventToAzInputManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index 1f30fc1d19..574470ad6e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -246,8 +246,9 @@ namespace AzToolsFramework if (eventType == QEvent::Type::MouseMove) { + // clear override cursor when moving outside of the viewport const auto* mouseEvent = static_cast(event); - if (m_overrideCursor && !m_sourceWidget->geometry().contains(mouseEvent->pos())) + if (m_overrideCursor && !m_sourceWidget->geometry().contains(m_sourceWidget->mapFromGlobal(mouseEvent->globalPos()))) { qApp->restoreOverrideCursor(); m_overrideCursor = false; From 67ccc438c44c9fb352fe0917c0a05af92f989b36 Mon Sep 17 00:00:00 2001 From: hultonha Date: Fri, 15 Oct 2021 15:47:57 +0100 Subject: [PATCH 026/152] remove redundant word from comment Signed-off-by: hultonha --- .../AzToolsFramework/Viewport/ViewportMessages.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 15e871459d..8452831306 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -318,7 +318,7 @@ namespace AzToolsFramework virtual bool IsMouseOver() const = 0; //! Set the cursor style override. virtual void SetOverrideCursor(CursorStyleOverride cursorStyleOverride) = 0; - //! Clear the current cursor style override. + //! Clear the cursor style override. virtual void ClearOverrideCursor() = 0; protected: From bd224b3a5cb88930478b133b4fe14df1f49b24dc Mon Sep 17 00:00:00 2001 From: hultonha Date: Fri, 15 Oct 2021 16:44:25 +0100 Subject: [PATCH 027/152] update bus implementation for integration tests Signed-off-by: hultonha --- .../Tests/test_ModularViewportCameraController.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index ea3653f663..275df11784 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -38,6 +38,8 @@ namespace UnitTest void BeginCursorCapture() override; void EndCursorCapture() override; bool IsMouseOver() const override; + void SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) override; + void ClearOverrideCursor() override; private: AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr; @@ -58,6 +60,17 @@ namespace UnitTest return true; } + void ViewportMouseCursorRequestImpl::SetOverrideCursor( + [[maybe_unused]] AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) + { + // noop + } + + void ViewportMouseCursorRequestImpl::ClearOverrideCursor() + { + // noop + } + class ModularViewportCameraControllerFixture : public AllocatorsTestFixture { public: From 244e1b8e165e2ed0452e54bf5489a7dc929110b0 Mon Sep 17 00:00:00 2001 From: hultonha Date: Fri, 15 Oct 2021 16:55:40 +0100 Subject: [PATCH 028/152] add a switch to enable/disable cursor lock (default off) Signed-off-by: hultonha --- .../ViewportSelection/EditorHelpers.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index b6dbf79a2d..3d48fabe95 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -44,6 +44,13 @@ AZ_CVAR( nullptr, AZ::ConsoleFunctorFlags::Null, "Display the aggregate world bounds for a given entity (the union of all world component Aabbs)"); +AZ_CVAR( + bool, + ed_useCursorLockIconInFocusMode, + false, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Use a lock icon when the cursor is over entities that cannot be interacted with"); namespace AzToolsFramework { @@ -195,9 +202,12 @@ namespace AzToolsFramework // verify if the entity Id corresponds to an entity that is focused; if not, halt selection. if (entityIdUnderCursor.IsValid() && !IsSelectableAccordingToFocusMode(entityIdUnderCursor)) { - ViewportInteraction::ViewportMouseCursorRequestBus::Event( - viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::SetOverrideCursor, - ViewportInteraction::CursorStyleOverride::Forbidden); + if (ed_useCursorLockIconInFocusMode) + { + ViewportInteraction::ViewportMouseCursorRequestBus::Event( + viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::SetOverrideCursor, + ViewportInteraction::CursorStyleOverride::Forbidden); + } if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() && mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down || From 3a0805254bce12051c7c3317e13841d1accb2514 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Fri, 15 Oct 2021 13:42:46 -0700 Subject: [PATCH 029/152] Fixes for 64-bit printfs Signed-off-by: kberg-amzn --- .../EntityReplicationManager.cpp | 50 +++++++++---------- .../EntityReplication/EntityReplicator.cpp | 26 +++++----- .../NetworkEntityAuthorityTracker.cpp | 22 ++++---- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index e0023815cc..03d5f52ff2 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -259,8 +259,8 @@ namespace Multiplayer { AZLOG_WARN ( - "Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d", - aznumeric_cast(replicator->GetEntityHandle().GetNetEntityId()), + "Serializing extremely large entity (%llu) - MaxPayload: %d NeededSize %d", + aznumeric_cast(replicator->GetEntityHandle().GetNetEntityId()), m_maxPayloadSize, nextMessageSize ); @@ -373,19 +373,19 @@ namespace Multiplayer if (changedRemoteRole || changedLocalRole) { - const uint32_t intEntityId = static_cast(netBindComponent->GetNetEntityId()); + const AZ::u64 intEntityId = static_cast(netBindComponent->GetNetEntityId()); const char* entityName = entityReplicator->GetEntityHandle().GetEntity()->GetName().c_str(); if (changedLocalRole) { const char* oldRoleString = GetEnumString(entityReplicator->GetRemoteNetworkRole()); const char* newRoleString = GetEnumString(remoteNetworkRole); - AZLOG(NET_ReplicatorRoles, "Replicator %s(%u) changed local role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString); + AZLOG(NET_ReplicatorRoles, "Replicator %s(%llu) changed local role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString); } if (changedRemoteRole) { const char* oldRoleString = GetEnumString(entityReplicator->GetBoundLocalNetworkRole()); const char* newRoleString = GetEnumString(netBindComponent->GetNetEntityRole()); - AZLOG(NET_ReplicatorRoles, "Replicator %s(%u) changed remote role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString); + AZLOG(NET_ReplicatorRoles, "Replicator %s(%llu) changed remote role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString); } // If we changed roles, we need to reset everything @@ -402,8 +402,8 @@ namespace Multiplayer AZLOG ( NET_RepDeletes, - "Reinited replicator for %u from remote host %s role %d", - entityHandle.GetNetEntityId(), + "Reinited replicator for netEntityId %llu from remote host %s role %d", + static_cast(entityHandle.GetNetEntityId()), GetRemoteHostId().GetString().c_str(), aznumeric_cast(remoteNetworkRole) ); @@ -419,8 +419,8 @@ namespace Multiplayer AZLOG ( NET_RepDeletes, - "Added replicator for %u from remote host %s role %d", - entityHandle.GetNetEntityId(), + "Added replicator for netEntityId %llu from remote host %s role %d", + static_cast(entityHandle.GetNetEntityId()), GetRemoteHostId().GetString().c_str(), aznumeric_cast(remoteNetworkRole) ); @@ -428,7 +428,7 @@ namespace Multiplayer } else { - AZLOG_ERROR("Failed to add entity replicator, entity does not exist, entity id %u", entityHandle.GetNetEntityId()); + AZLOG_ERROR("Failed to add entity replicator, entity does not exist, netEntityId %llu", static_cast(entityHandle.GetNetEntityId())); AZ_Assert(false, "Failed to add entity replicator, entity does not exist"); } return entityReplicator; @@ -517,18 +517,18 @@ namespace Multiplayer { if (entityReplicator->IsMarkedForRemoval()) { - AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %llu remote host %s", static_cast(updateMessage.GetEntityId()), GetRemoteHostId().GetString().c_str()); } else if (entityReplicator->OwnsReplicatorLifetime()) { // This can occur if we migrate entities quickly - if this is a replicator from C to A, A migrates to B, B then migrates to C, and A's delete replicator has not arrived at C - AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %llu remote host %s", static_cast(updateMessage.GetEntityId()), GetRemoteHostId().GetString().c_str()); } else { shouldDeleteEntity = true; entityReplicator->MarkForRemoval(); - AZLOG(NET_RepDeletes, "Deleting replicater for entity id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "Deleting replicater for entity id %llu remote host %s", static_cast(updateMessage.GetEntityId()), GetRemoteHostId().GetString().c_str()); } } @@ -540,17 +540,17 @@ namespace Multiplayer { if (updateMessage.GetWasMigrated()) { - AZLOG(NET_RepDeletes, "Leaving id %u using timeout remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "Leaving id %llu using timeout remote host %s", static_cast(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str()); } else { - AZLOG(NET_RepDeletes, "Deleting entity id %u remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "Deleting entity id %llu remote host %s", static_cast(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str()); GetNetworkEntityManager()->MarkForRemoval(entity); } } else { - AZLOG(NET_RepDeletes, "Trying to delete entity id %u remote host %s, but it has been removed", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "Trying to delete entity id %llu remote host %s, but it has been removed", static_cast(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str()); } } @@ -606,9 +606,9 @@ namespace Multiplayer AZ_Assert(localNetworkRole != NetEntityRole::Authority, "UpdateMessage trying to set local role to Authority, this should only happen via migration"); AZLOG_INFO ( - "EntityReplicationManager: Changing network role on entity %s(%u), old role %s new role %s", + "EntityReplicationManager: Changing network role on entity %s(%llu), old role %s new role %s", replicatorEntity.GetEntity()->GetName().c_str(), - aznumeric_cast(netEntityId), + aznumeric_cast(netEntityId), GetEnumString(netBindComponent->GetNetEntityRole()), GetEnumString(localNetworkRole) ); @@ -720,9 +720,9 @@ namespace Multiplayer AZLOG_WARN ( "Dropping Packet and LocalServerToRemoteClient connection, unexpected packet " - "LocalShard=%s EntityId=%u RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s", + "LocalShard=%s EntityId=%llu RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s", GetNetworkEntityManager()->GetHostId().GetString().c_str(), - aznumeric_cast(entityReplicator->GetEntityHandle().GetNetEntityId()), + aznumeric_cast(entityReplicator->GetEntityHandle().GetNetEntityId()), aznumeric_cast(entityReplicator->GetRemoteNetworkRole()), aznumeric_cast(entityReplicator->GetBoundLocalNetworkRole()), aznumeric_cast(entityReplicator->GetNetBindComponent()->GetNetEntityRole()), @@ -772,13 +772,13 @@ namespace Multiplayer result = UpdateValidationResult::DropMessage; if (updateMessage.GetIsDelete()) { - AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %u, sequence %d latest sequence %d from remote host %s", - updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %llu, sequence %d latest sequence %d from remote host %s", + (AZ::u64)updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str()); } else { - AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %u, sequence %d latest sequence %d from remote host %s", - updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %llu, sequence %d latest sequence %d from remote host %s", + (AZ::u64)updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str()); } } } @@ -1213,7 +1213,7 @@ namespace Multiplayer // Change the role on the replicator AddEntityReplicator(entityHandle, NetEntityRole::Server); - AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote host %s", entityHandle.GetNetEntityId(), GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "Handle Migration %llu new authority from remote host %s", static_cast(entityHandle.GetNetEntityId()), GetRemoteHostId().GetString().c_str()); return true; } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index 0b97634183..67527bf962 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -103,8 +103,8 @@ namespace Multiplayer AZ_Assert ( m_boundLocalNetworkRole != m_remoteNetworkRole, - "Invalid configuration detected, bound local role must differ from remote network role Role: %d", - aznumeric_cast(m_boundLocalNetworkRole) + "Invalid configuration detected, bound local role must differ from remote network role: %s", + GetEnumString(m_boundLocalNetworkRole) ); if (RemoteManagerOwnsEntityLifetime()) @@ -247,7 +247,7 @@ namespace Multiplayer if (entity->GetState() != AZ::Entity::State::Init) { - AZLOG_WARN("Trying to activate an entity that is not in the Init state (%u)", GetEntityHandle().GetNetEntityId()); + AZLOG_WARN("Trying to activate an entity that is not in the Init state (%llu)", static_cast(GetEntityHandle().GetNetEntityId())); } entity->Activate(); @@ -430,9 +430,9 @@ namespace Multiplayer AZLOG ( NET_HierarchyActivationInfo, - "Hierchical entity %s asking for activation - waiting on the parent %u", + "Hierchical entity %s asking for activation - waiting on the parent %llu", entity->GetName().c_str(), - aznumeric_cast(parentId) + aznumeric_cast(parentId) ); return false; } @@ -450,8 +450,8 @@ namespace Multiplayer AZLOG ( NET_RepDeletes, - "Sending delete replicator id %u migrated %d to remote host %s", - aznumeric_cast(GetEntityHandle().GetNetEntityId()), + "Sending delete replicator id %llu migrated %d to remote host %s", + aznumeric_cast(GetEntityHandle().GetNetEntityId()), WasMigrated() ? 1 : 0, m_replicationManager.GetRemoteHostId().GetString().c_str() ); @@ -620,8 +620,8 @@ namespace Multiplayer { AZLOG_ERROR ( - "Dropping RPC and Connection EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", - aznumeric_cast(m_entityHandle.GetNetEntityId()), + "Dropping RPC and Connection EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", + aznumeric_cast(m_entityHandle.GetNetEntityId()), GetEnumString(GetBoundLocalNetworkRole()), GetEnumString(GetRemoteNetworkRole()), aznumeric_cast(entityRpcMessage.GetRpcDeliveryType()), @@ -637,8 +637,8 @@ namespace Multiplayer AZLOG ( NET_Rpc, - "Dropping RPC EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", - aznumeric_cast(m_entityHandle.GetNetEntityId()), + "Dropping RPC EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", + aznumeric_cast(m_entityHandle.GetNetEntityId()), GetEnumString(GetBoundLocalNetworkRole()), GetEnumString(GetRemoteNetworkRole()), aznumeric_cast(entityRpcMessage.GetRpcDeliveryType()), @@ -661,8 +661,8 @@ namespace Multiplayer { AZLOG_WARN ( - "Dropping RPC since entity deleted EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", - aznumeric_cast(m_entityHandle.GetNetEntityId()), + "Dropping RPC since entity deleted EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s", + aznumeric_cast(m_entityHandle.GetNetEntityId()), GetEnumString(GetBoundLocalNetworkRole()), GetEnumString(GetRemoteNetworkRole()), aznumeric_cast(entityRpcMessage.GetRpcDeliveryType()), diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp index 0a99f4b0d4..b3f87ea9ab 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp @@ -33,8 +33,8 @@ namespace Multiplayer AZLOG ( NET_AuthTracker, - "AuthTracker: Removing timeout for networkEntityId %u from %s, new owner is %s", - aznumeric_cast(entityHandle.GetNetEntityId()), + "AuthTracker: Removing timeout for networkEntityId %llu from %s, new owner is %s", + aznumeric_cast(entityHandle.GetNetEntityId()), timeoutData->second.m_previousOwner.GetString().c_str(), newOwner.GetString().c_str() ); @@ -48,8 +48,8 @@ namespace Multiplayer AZLOG ( NET_AuthTracker, - "AuthTracker: Assigning networkEntityId %u from %s to %s", - aznumeric_cast(entityHandle.GetNetEntityId()), + "AuthTracker: Assigning networkEntityId %llu from %s to %s", + aznumeric_cast(entityHandle.GetNetEntityId()), iter->second.back().GetString().c_str(), newOwner.GetString().c_str() ); @@ -59,8 +59,8 @@ namespace Multiplayer AZLOG ( NET_AuthTracker, - "AuthTracker: Assigning networkEntityId %u to %s", - aznumeric_cast(entityHandle.GetNetEntityId()), + "AuthTracker: Assigning networkEntityId %llu to %s", + aznumeric_cast(entityHandle.GetNetEntityId()), newOwner.GetString().c_str() ); } @@ -87,7 +87,7 @@ namespace Multiplayer } } - AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %u from %s", aznumeric_cast(entityHandle.GetNetEntityId()), previousOwner.GetString().c_str()); + AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %llu from %s", aznumeric_cast(entityHandle.GetNetEntityId()), previousOwner.GetString().c_str()); if (auto localEnt = entityHandle.GetEntity()) { if (authorityStack.empty()) @@ -114,14 +114,14 @@ namespace Multiplayer } else { - AZLOG(NET_AuthTracker, "AuthTracker: Skipping timeout for Autonomous networkEntityId %u", aznumeric_cast(entityHandle.GetNetEntityId())); + AZLOG(NET_AuthTracker, "AuthTracker: Skipping timeout for Autonomous networkEntityId %llu", aznumeric_cast(entityHandle.GetNetEntityId())); } } } } else { - AZLOG(NET_AuthTracker, "AuthTracker: Remove authority called on networkEntityId that was never added %u", aznumeric_cast(entityHandle.GetNetEntityId())); + AZLOG(NET_AuthTracker, "AuthTracker: Remove authority called on networkEntityId that was never added %llu", aznumeric_cast(entityHandle.GetNetEntityId())); AZ_Assert(false, "AuthTracker: Remove authority called on entity that was never added"); } } @@ -205,8 +205,8 @@ namespace Multiplayer { AZLOG_ERROR ( - "Timed out entity id %u during migration previous owner %s, removing it", - aznumeric_cast(entityHandle.GetNetEntityId()), + "Timed out entity id %llu during migration previous owner %s, removing it", + aznumeric_cast(entityHandle.GetNetEntityId()), timeoutData->second.m_previousOwner.GetString().c_str() ); m_networkEntityManager.MarkForRemoval(entityHandle); From e2a2ecff44041e24491b3838d504fd1902ad04aa Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Fri, 15 Oct 2021 14:56:06 -0700 Subject: [PATCH 030/152] missed a printf Signed-off-by: kberg-amzn --- .../EntityReplication/EntityReplicationManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 03d5f52ff2..63a80e54fe 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -865,10 +865,10 @@ namespace Multiplayer { AZLOG_INFO ( - "EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %u has already been deleted", + "EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %llu has already been deleted", GetMultiplayerComponentRegistry()->GetComponentName(message.GetComponentId()), GetMultiplayerComponentRegistry()->GetComponentRpcName(message.GetComponentId(), message.GetRpcIndex()), - message.GetEntityId() + static_cast(message.GetEntityId()) ); return false; } From 47a00e9801dec1b3b8622280fcdb90a78a84329b Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Fri, 15 Oct 2021 16:53:58 -0700 Subject: [PATCH 031/152] Missed one more printf Signed-off-by: kberg-amzn --- .../EntityReplication/EntityReplicationManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 63a80e54fe..fa099842c5 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -1157,7 +1157,7 @@ namespace Multiplayer AZ_Assert(didSucceed, "Failed to migrate entity from server"); m_sendMigrateEntityEvent.Signal(m_connection, message); - AZLOG(NET_RepDeletes, "Migration packet sent %u to remote host %s", netEntityId, GetRemoteHostId().GetString().c_str()); + AZLOG(NET_RepDeletes, "Migration packet sent %llu to remote host %s", static_cast(netEntityId), GetRemoteHostId().GetString().c_str()); // Notify all other EntityReplicationManagers that this entity has migrated so they can adjust their own replicators given our new proxy status GetMultiplayer()->SendNotifyEntityMigrationEvent(entityHandle, GetRemoteHostId()); From e6290436fcd954b10a89656b8f12d1f1ac830ef2 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Fri, 15 Oct 2021 18:01:40 -0700 Subject: [PATCH 032/152] Updates mocks for interface changes Signed-off-by: kberg-amzn --- .../Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp | 2 +- Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h | 5 ++++- Gems/Multiplayer/Code/Tests/MockInterfaces.h | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp index 7c38a340eb..ad28307204 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp @@ -74,7 +74,7 @@ namespace Multiplayer { ImGui::Text("%s", entity->GetId().ToString().c_str()); ImGui::NextColumn(); - ImGui::Text("%u", GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId())); + ImGui::Text("%llu", static_cast(GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId()))); ImGui::NextColumn(); ImGui::Text("%s", entity->GetName().c_str()); ImGui::NextColumn(); diff --git a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h index 5a528ed497..803e6cabca 100644 --- a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h @@ -342,8 +342,11 @@ namespace Multiplayer void AddClientMigrationEndEventHandler([[maybe_unused]] ClientMigrationEndEvent::Handler& handler) override {} void AddNotifyClientMigrationHandler([[maybe_unused]] NotifyClientMigrationEvent::Handler& handler) override {} void AddNotifyEntityMigrationEventHandler([[maybe_unused]] NotifyEntityMigrationEvent::Handler& handler) override {} - void SendNotifyClientMigrationEvent([[maybe_unused]] const HostId& hostId, [[maybe_unused]] uint64_t userIdentifier, [[maybe_unused]] ClientInputId lastClientInputId) override {} + void SendNotifyClientMigrationEvent([[maybe_unused]] AzNetworking::ConnectionId connectionId, [[maybe_unused]] const HostId& hostId, + [[maybe_unused]] uint64_t userIdentifier, [[maybe_unused]] ClientInputId lastClientInputId, [[maybe_unused]] NetEntityId netEntityId) override {} void SendNotifyEntityMigrationEvent([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] const HostId& remoteHostId) override {} + void RegisterPlayerIdentifierForRejoin(uint64_t, NetEntityId) override {} + void CompleteClientMigration(uint64_t, AzNetworking::ConnectionId, const HostId&, ClientInputId) override {} void SetShouldSpawnNetworkEntities([[maybe_unused]] bool value) override {} bool GetShouldSpawnNetworkEntities() const override { return true; } diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h index 527aeb51bc..8cebf280b9 100644 --- a/Gems/Multiplayer/Code/Tests/MockInterfaces.h +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -33,7 +33,7 @@ namespace UnitTest MOCK_METHOD1(AddServerAcceptanceReceivedHandler, void(Multiplayer::ServerAcceptanceReceivedEvent::Handler&)); MOCK_METHOD1(AddSessionInitHandler, void(Multiplayer::SessionInitEvent::Handler&)); MOCK_METHOD1(AddSessionShutdownHandler, void(Multiplayer::SessionShutdownEvent::Handler&)); - MOCK_METHOD3(SendNotifyClientMigrationEvent, void(const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId)); + MOCK_METHOD5(SendNotifyClientMigrationEvent, void(AzNetworking::ConnectionId, const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId, Multiplayer::NetEntityId)); MOCK_METHOD2(SendNotifyEntityMigrationEvent, void(const Multiplayer::ConstNetworkEntityHandle&, const Multiplayer::HostId&)); MOCK_METHOD1(SendReadyForEntityUpdates, void(bool)); MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs()); @@ -42,6 +42,8 @@ namespace UnitTest MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ()); MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*)); MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ()); + MOCK_METHOD2(RegisterPlayerIdentifierForRejoin, void(uint64_t, Multiplayer::NetEntityId)); + MOCK_METHOD4(CompleteClientMigration, void(uint64_t, AzNetworking::ConnectionId, const Multiplayer::HostId&, Multiplayer::ClientInputId)); MOCK_METHOD1(SetShouldSpawnNetworkEntities, void(bool)); MOCK_CONST_METHOD0(GetShouldSpawnNetworkEntities, bool()); }; From 268b9da8577f7754a0cf1b54afc23d280faf3afb Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Mon, 18 Oct 2021 14:21:34 -0700 Subject: [PATCH 033/152] atom constant component property class Signed-off-by: Scott Murray --- .../Atom/atom_utils/atom_constants.py | 368 ++++++++++++++++++ .../hydra_AtomEditorComponents_MeshAdded.py | 25 +- 2 files changed, 380 insertions(+), 13 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index 88a959f198..e6c422eb46 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -6,6 +6,374 @@ SPDX-License-Identifier: Apache-2.0 OR MIT Hold constants used across both hydra and non-hydra scripts. """ + +class AtomComponentProperties: + """ + Holds Atom component related constants + """ + + @staticmethod + def actor(property: str = 'name') -> str: + """ + Actor component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Actor', + } + return properties[property] + + @staticmethod + def bloom(property: str = 'name') -> str: + """ + Bloom component properties. Requires PostFX Layer component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Bloom', + 'requires': [AtomComponentProperties.postfx_layer()], + } + return properties[property] + + @staticmethod + def camera(property: str = 'name') -> str: + """ + Camera component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Camera', + } + return properties[property] + + @staticmethod + def decal(property: str = 'name') -> str: + """ + Decal component properties. + 'Material' the material Asset.id of the decal. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Decal', + 'Material': 'Controller|Configuration|Material', + } + return properties[property] + + @staticmethod + def deferred_fog(property: str = 'name') -> str: + """ + Deferred Fog component properties. Requires PostFX Layer component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Deferred Fog', + 'requires': [AtomComponentProperties.postfx_layer()], + } + return properties[property] + + @staticmethod + def depth_of_field(property: str = 'name') -> str: + """ + Depth of Field component properties. Requires PostFX Layer component. + 'Camera Entity' an EditorEntity.id reference to the Camera component required for this effect. + Must be a different entity than the one which hosts Depth of Field component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'DepthOfField', + 'requires': [AtomComponentProperties.postfx_layer()], + 'Camera Entity': 'Controller|Configuration|Camera Entity', + } + return properties[property] + + @staticmethod + def diffuse_probe(property: str = 'name') -> str: + """ + Diffuse Probe Grid component properties. Requires one of 'shapes' listed. + 'shapes' a list of supported shapes as component names. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Diffuse Probe Grid', + 'shapes': ['Axis Aligned Box Shape', 'Box Shape'] + } + return properties[property] + + @staticmethod + def directional_light(property: str = 'name') -> str: + """ + Directional Light component properties. + 'Camera' an EditorEntity.id reference to the Camera component that controls cascaded shadow view frustum. + Must be a different entity than the one which hosts Depth of Field component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Directional Light', + 'Camera': 'Controller|Configuration|Shadow|Camera', + } + return properties[property] + + @staticmethod + def display_mapper(property: str = 'name') -> str: + """ + Display Mapper component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Display Mapper', + } + return properties[property] + + @staticmethod + def entity_reference(property: str = 'name') -> str: + """ + Entity Reference component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Entity Reference', + } + return properties[property] + + @staticmethod + def exposure_control(property: str = 'name') -> str: + """ + Exposure Control component properties. Requires PostFX Layer component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Exposure Control', + 'requires': [AtomComponentProperties.postfx_layer()], + } + return properties[property] + + @staticmethod + def global_skylight(property: str = 'name') -> str: + """ + Global Skylight (IBL) component properties. + 'Diffuse Image' Asset.id for the cubemap image for determining diffuse lighting. + 'Specular Image' Asset.id for the cubemap image for determining specular lighting. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Global Skylight (IBL)', + 'Diffuse Image': 'Controller|Configuration|Diffuse Image', + 'Specular Image': 'Controller|Configuration|Specular Image', + } + return properties[property] + + @staticmethod + def grid(property: str = 'name') -> str: + """ + Grid component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Grid', + } + return properties[property] + + @staticmethod + def hdr_color_grading(property: str = 'name') -> str: + """ + HDR Color Grading component properties. Requires PostFX Layer component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'HDR Color Grading', + 'requires': [AtomComponentProperties.postfx_layer()], + } + return properties[property] + + @staticmethod + def hdri_skybox(property: str = 'name') -> str: + """ + HDRi Skybox component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'HDRi Skybox', + } + return properties[property] + + @staticmethod + def light(property: str = 'name') -> str: + """ + Light component properties. + 'Light type' from atom_constants.py LIGHT_TYPES + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Light', + 'Light type': 'Controller|Configuration|Light type', + } + return properties[property] + + @staticmethod + def look_modification(property: str = 'name') -> str: + """ + Look Modification component properties. Requires PostFX Layer component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Look Modification', + 'requires': [AtomComponentProperties.postfx_layer()], + } + return properties[property] + + @staticmethod + def material(property: str = 'name') -> str: + """ + Material component properties. Requires one of Actor OR Mesh component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Material', + 'requires': [AtomComponentProperties.actor(), AtomComponentProperties.mesh()], + } + return properties[property] + + @staticmethod + def mesh(property: str = 'name') -> str: + """ + Mesh component properties. + 'Mesh Asset' Asset.id of the mesh model. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + :rtype: str + """ + properties = { + 'name': 'Mesh', + 'Mesh Asset': 'Controller|Configuration|Mesh Asset', + } + return properties[property] + + @staticmethod + def occlusion_culling_plane(property: str = 'name') -> str: + """ + Occlusion Culling Plane component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Occlusion Culling Plane', + } + return properties[property] + + @staticmethod + def physical_sky(property: str = 'name') -> str: + """ + Physical Sky component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Physical Sky', + } + return properties[property] + + @staticmethod + def postfx_layer(property: str = 'name') -> str: + """ + PostFX Layer component properties. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'PostFX Layer', + } + return properties[property] + + @staticmethod + def postfx_gradient(property: str = 'name') -> str: + """ + PostFX Gradient Weight Modifier component properties. Requires PostFX Layer component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'PostFX Gradient Weight Modifier', + 'requires': [AtomComponentProperties.postfx_layer()], + } + return properties[property] + + @staticmethod + def postfx_radius(property: str = 'name') -> str: + """ + PostFX Radius Weight Modifier component properties. Requires PostFX Layer component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'PostFX Radius Weight Modifier', + 'requires': [AtomComponentProperties.postfx_layer()], + } + return properties[property] + + @staticmethod + def postfx_shape(property: str = 'name') -> str: + """ + PostFX Shape Weight Modifier component properties. Requires PostFX Layer and one of 'shapes' listed. + 'shapes' a list of supported shapes as component names. 'Tube Shape' is also supported by requires 'Spline'. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'PostFX Shape Weight Modifier', + 'requires': [AtomComponentProperties.postfx_layer()], + 'shapes': ['Axis Aligned Box Shape', 'Box Shape', 'Capsule Shape', 'Compound Shape', 'Cylinder Shape', + 'Disk Shape', 'Polygon Prism Shape', 'Quad Shape', 'Sphere Shape', 'Vegetation Reference Shape'], + } + return properties[property] + + @staticmethod + def reflection_probe(property: str = 'name') -> str: + """ + Reflection Probe component properties. Requires one of 'shapes' listed. + 'shapes' a list of supported shapes as component names. + 'Baked Cubemap Path' Asset.id of the baked cubemap image generated by a call to 'BakeReflectionProbe' ebus. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Reflection Probe', + 'shapes': ['Axis Aligned Box Shape', 'Box Shape'], + 'Baked Cubemap Path': 'Cubemap|Baked Cubemap Path', + } + return properties[property] + + @staticmethod + def ssao(property: str = 'name') -> str: + """ + SSAO component properties. Requires PostFX Layer component. + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'SSAO', + 'requires': [AtomComponentProperties.postfx_layer()], + } + return properties[property] + + # Light type options for the Light component. LIGHT_TYPES = { 'unknown': 0, diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py index fbf5c987d1..82d3b89309 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py @@ -80,25 +80,25 @@ def AtomEditorComponents_Mesh_AddedToEntity(): from editor_python_test_tools.asset_utils import Asset from editor_python_test_tools.editor_entity_utils import EditorEntity - from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + from editor_python_test_tools.utils import Report, Tracer, TestHelper + from Atom.atom_utils.atom_constants import AtomComponentProperties as Atom with Tracer() as error_tracer: # Test setup begins. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. - helper.init_idle() - helper.open_level("", "Base") + TestHelper.init_idle() + TestHelper.open_level("", "Base") # Test steps begin. # 1. Create a Mesh entity with no components. - mesh_name = "Mesh" - mesh_entity = EditorEntity.create_editor_entity(mesh_name) + mesh_entity = EditorEntity.create_editor_entity(Atom.mesh()) Report.critical_result(Tests.mesh_entity_creation, mesh_entity.exists()) # 2. Add a Mesh component to Mesh entity. - mesh_component = mesh_entity.add_component(mesh_name) + mesh_component = mesh_entity.add_component(Atom.mesh()) Report.critical_result( Tests.mesh_component_added, - mesh_entity.has_component(mesh_name)) + mesh_entity.has_component(Atom.mesh())) # 3. UNDO the entity creation and component addition. # -> UNDO component addition. @@ -125,17 +125,16 @@ def AtomEditorComponents_Mesh_AddedToEntity(): Report.result(Tests.creation_redo, mesh_entity.exists()) # 5. Set Mesh component asset property - mesh_property_asset = 'Controller|Configuration|Mesh Asset' model_path = os.path.join('Objects', 'shaderball', 'shaderball_default_1m.azmodel') model = Asset.find_asset_by_path(model_path) - mesh_component.set_component_property_value(mesh_property_asset, model.id) + mesh_component.set_component_property_value(Atom.mesh('Mesh Asset'), model.id) Report.result(Tests.mesh_asset_specified, - mesh_component.get_component_property_value(mesh_property_asset) == model.id) + mesh_component.get_component_property_value(Atom.mesh('Mesh Asset')) == model.id) # 6. Enter/Exit game mode. - helper.enter_game_mode(Tests.enter_game_mode) + TestHelper.enter_game_mode(Tests.enter_game_mode) general.idle_wait_frames(1) - helper.exit_game_mode(Tests.exit_game_mode) + TestHelper.exit_game_mode(Tests.exit_game_mode) # 7. Test IsHidden. mesh_entity.set_visibility_state(False) @@ -159,7 +158,7 @@ def AtomEditorComponents_Mesh_AddedToEntity(): Report.result(Tests.deletion_redo, not mesh_entity.exists()) # 12. Look for errors or asserts. - helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) for error_info in error_tracer.errors: Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") for assert_info in error_tracer.asserts: From 5b70b2a8a95f88e9267d26b93d3b98907cf5269a Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Mon, 18 Oct 2021 14:37:57 -0700 Subject: [PATCH 034/152] reordering constant and class in file Signed-off-by: Scott Murray --- .../Atom/atom_utils/atom_constants.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index e6c422eb46..b66e5f77e4 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -7,6 +7,19 @@ Hold constants used across both hydra and non-hydra scripts. """ +# Light type options for the Light component. +LIGHT_TYPES = { + 'unknown': 0, + 'sphere': 1, + 'spot_disk': 2, + 'capsule': 3, + 'quad': 4, + 'polygon': 5, + 'simple_point': 6, + 'simple_spot': 7, +} + + class AtomComponentProperties: """ Holds Atom component related constants @@ -372,16 +385,3 @@ class AtomComponentProperties: 'requires': [AtomComponentProperties.postfx_layer()], } return properties[property] - - -# Light type options for the Light component. -LIGHT_TYPES = { - 'unknown': 0, - 'sphere': 1, - 'spot_disk': 2, - 'capsule': 3, - 'quad': 4, - 'polygon': 5, - 'simple_point': 6, - 'simple_spot': 7, -} From 9f7c80ce23b32468579143126e81bd8102fb3400 Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Mon, 18 Oct 2021 16:30:01 -0700 Subject: [PATCH 035/152] removing an extra blank line and fixing a docstring Signed-off-by: Scott Murray --- .../Gem/PythonTests/Atom/atom_utils/atom_constants.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index b66e5f77e4..35cec8898b 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -6,7 +6,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT Hold constants used across both hydra and non-hydra scripts. """ - # Light type options for the Light component. LIGHT_TYPES = { 'unknown': 0, @@ -124,7 +123,7 @@ class AtomComponentProperties: """ Directional Light component properties. 'Camera' an EditorEntity.id reference to the Camera component that controls cascaded shadow view frustum. - Must be a different entity than the one which hosts Depth of Field component. + Must be a different entity than the one which hosts Directional Light component. :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ From e5a73fe8ff5e9ac641ec641b1ba3f8beb1ffa48a Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Mon, 18 Oct 2021 16:32:42 -0700 Subject: [PATCH 036/152] Add helper function for UI to return paths to all cached gem jsons for a given repo Signed-off-by: AMZN-Phil --- scripts/o3de/o3de/repo.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/scripts/o3de/o3de/repo.py b/scripts/o3de/o3de/repo.py index 191fdbd3e1..6b6dffe8b3 100644 --- a/scripts/o3de/o3de/repo.py +++ b/scripts/o3de/o3de/repo.py @@ -92,6 +92,42 @@ def process_add_o3de_repo(file_name: str or pathlib.Path, return 0 +def get_gem_json_paths_from_cached_repo(repo_uri : str) -> list: + url = f'{repo_uri}/repo.json' + repo_sha256 = hashlib.sha256(url.encode()) + cache_folder = manifest.get_o3de_cache_folder() + cache_filename = cache_folder / str(repo_sha256.hexdigest() + '.json') + + gem_list = [] + + file_name = pathlib.Path(cache_filename).resolve() + if not file_name.is_file(): + return gem_list + + with file_name.open('r') as f: + try: + repo_data = json.load(f) + except json.JSONDecodeError as e: + logger.error(f'{file_name} failed to load: {str(e)}') + return gem_list + + # Get list of gems, then add all json paths to the list if they exist in the cache + repo_gems = [] + try: + repo_gems.append((repo_data['gems'], 'gem.json')) + except KeyError: + pass + + for o3de_object_uris, manifest_json in repo_gems: + for o3de_object_uri in o3de_object_uris: + manifest_json_uri = f'{o3de_object_uri}/{manifest_json}' + manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode()) + cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json') + if cache_file.is_file(): + gem_list.append(cache_file) + + return gem_list + def refresh_repos() -> int: json_data = manifest.load_o3de_manifest() From c48d95748557d17c246285c8a8b84c4248a17cd6 Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Mon, 18 Oct 2021 16:34:07 -0700 Subject: [PATCH 037/152] Slightly better variable naming Signed-off-by: AMZN-Phil --- scripts/o3de/o3de/repo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/o3de/o3de/repo.py b/scripts/o3de/o3de/repo.py index 6b6dffe8b3..ae8af43338 100644 --- a/scripts/o3de/o3de/repo.py +++ b/scripts/o3de/o3de/repo.py @@ -122,9 +122,9 @@ def get_gem_json_paths_from_cached_repo(repo_uri : str) -> list: for o3de_object_uri in o3de_object_uris: manifest_json_uri = f'{o3de_object_uri}/{manifest_json}' manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode()) - cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json') - if cache_file.is_file(): - gem_list.append(cache_file) + cache_gem_json_filepath = cache_folder / str(manifest_json_sha256.hexdigest() + '.json') + if cache_gem_json_filepath.is_file(): + gem_list.append(cache_gem_json_filepath) return gem_list From 62971fa7bd2a2222ed97fb91f7a8e9f1ef82e23d Mon Sep 17 00:00:00 2001 From: pereslav Date: Tue, 19 Oct 2021 13:52:32 +0100 Subject: [PATCH 038/152] PR feedback, removed unnecessary code Signed-off-by: pereslav --- .../NetworkHierarchyRootComponent.h | 2 +- .../NetworkHierarchyRootComponent.cpp | 21 +++++++------------ .../ServerToClientReplicationWindow.cpp | 2 ++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index cafd63b223..f0ad3baf94 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -108,7 +108,7 @@ namespace Multiplayer //! NetworkHierarchyRootComponentController //! This is the network controller for NetworkHierarchyRootComponent. //! Class provides the ability to process input for hierarchies. - class NetworkHierarchyRootComponentController + class NetworkHierarchyRootComponentController final : public NetworkHierarchyRootComponentControllerBase { public: diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index dd83878b25..e45389b7d9 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -373,6 +373,8 @@ namespace Multiplayer } NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); + ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId); NetBindComponent* netComp = childEntityHandle.GetNetBindComponent(); @@ -395,24 +397,13 @@ namespace Multiplayer void NetworkHierarchyRootComponentController::ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) { - // Prevent replaying process input commands for child entities that weren't part of the hierarchy at that time - //if (!m_firstProcessInputOccurred) - //{ - // m_firstProcessInputOccurred = true; - // m_firstProcessInputTime = input.GetInputId().GetGameTimePoint(); - //} - //else if (m_firstProcessInputTime > input.GetInputId().GetGameTimePoint()) - //{ - // return; - //} - if (auto* networkInput = input.FindComponentInput()) { INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); // Build a set of Net IDs for the children - AZStd::unordered_set currentChildren; // TODO: Cache inside the component if this becomes a performance issue. + AZStd::unordered_set currentChildren; NetworkHierarchyRootComponent& component = GetParent(); for (AZ::Entity* child : component.m_hierarchicalEntities) { @@ -420,7 +411,9 @@ namespace Multiplayer { continue; } - NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); // TODO: Cache net IDs in the component if this becomes a performance issue + + NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); currentChildren.insert(childNetEntitydId); } @@ -468,6 +461,8 @@ namespace Multiplayer } NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); + ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId); NetBindComponent* netBindComponent = childEntityHandle.GetNetBindComponent(); AZ_Assert(netBindComponent, "No NetBindComponent, this should be impossible"); diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp index d8195bf29a..856fd59f9d 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp @@ -335,6 +335,8 @@ namespace Multiplayer for (const AZ::Entity* controlledEntity : hierarchyComponent.m_hierarchicalEntities) { NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId()); + AZ_Assert(controlledNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); + ConstNetworkEntityHandle controlledEntityHandle = networkEntityManager->GetEntity(controlledNetEntitydId); AZ_Assert(controlledEntityHandle != nullptr, "We have lost a controlled entity unexpectedly"); From 725be128aa80effa26846772a529f08d7e856b2a Mon Sep 17 00:00:00 2001 From: pereslav Date: Tue, 19 Oct 2021 16:20:40 +0100 Subject: [PATCH 039/152] tidy up Signed-off-by: pereslav --- Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h index 6b2f8e49a5..4cf0be2c2d 100644 --- a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h @@ -23,8 +23,8 @@ namespace MultiplayerTest provided.emplace_back(AZ_CRC_CE("MultiplayerInputDriver")); } - void Activate(){}; - void Deactivate(){}; + void Activate() override {} + void Deactivate() override {} }; // Test multiplayer component with ability to create and process network input From 7ce376b5b2a6901a55718e3e9f953da6a1c2594b Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Tue, 19 Oct 2021 08:40:36 -0700 Subject: [PATCH 040/152] Add some log output if the cached files cannot be found Signed-off-by: AMZN-Phil --- scripts/o3de/o3de/repo.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/o3de/o3de/repo.py b/scripts/o3de/o3de/repo.py index ae8af43338..5c47c2bee8 100644 --- a/scripts/o3de/o3de/repo.py +++ b/scripts/o3de/o3de/repo.py @@ -92,7 +92,7 @@ def process_add_o3de_repo(file_name: str or pathlib.Path, return 0 -def get_gem_json_paths_from_cached_repo(repo_uri : str) -> list: +def get_gem_json_paths_from_cached_repo(repo_uri: str) -> list: url = f'{repo_uri}/repo.json' repo_sha256 = hashlib.sha256(url.encode()) cache_folder = manifest.get_o3de_cache_folder() @@ -102,6 +102,7 @@ def get_gem_json_paths_from_cached_repo(repo_uri : str) -> list: file_name = pathlib.Path(cache_filename).resolve() if not file_name.is_file(): + logger.error(f'Could not find cached repo json file for {repo_uri}') return gem_list with file_name.open('r') as f: @@ -124,6 +125,7 @@ def get_gem_json_paths_from_cached_repo(repo_uri : str) -> list: manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode()) cache_gem_json_filepath = cache_folder / str(manifest_json_sha256.hexdigest() + '.json') if cache_gem_json_filepath.is_file(): + logger.warn(f'Could not find cached gem json file for {o3de_object_uri} in repo {repo_uri}') gem_list.append(cache_gem_json_filepath) return gem_list From faea65e84007484a9664390baf08127136579169 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Tue, 19 Oct 2021 17:59:06 +0100 Subject: [PATCH 041/152] Removed pragma once from cpp Signed-off-by: Sergey Pereslavtsev --- Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp index 336082eb01..f47c102118 100644 --- a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#pragma once - #include #include From e3a14c08093cab7db003778a1f4a16bdfed8041c Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Tue, 19 Oct 2021 18:42:35 +0100 Subject: [PATCH 042/152] Fixed warning about unused local variable Signed-off-by: Sergey Pereslavtsev --- Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp index f47c102118..ac723300a9 100644 --- a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp @@ -65,7 +65,7 @@ namespace MultiplayerTest void TestMultiplayerComponentController::ProcessInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) { auto& component = GetParent(); - auto* networkInput = input.FindComponentInput(); + [[maybe_unused]] auto* networkInput = input.FindComponentInput(); AZ_Assert(networkInput->m_ownerId == component.GetId(), "Input Id doesn't match the owner component Id"); if (component.m_processInputCallback) From 5bc334be70cd29521ce66010f80025550d78b27f Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Tue, 19 Oct 2021 11:21:00 -0700 Subject: [PATCH 043/152] updating docstrings for formatting and typos Signed-off-by: Scott Murray --- .../Atom/atom_utils/atom_constants.py | 76 ++++++++++++------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index 35cec8898b..86fa3bb7e1 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -37,9 +37,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def bloom(property: str = 'name') -> str: + def bloom(property: str = 'name') -> Union[str, List[str]]: """ Bloom component properties. Requires PostFX Layer component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -65,7 +67,7 @@ class AtomComponentProperties: def decal(property: str = 'name') -> str: """ Decal component properties. - 'Material' the material Asset.id of the decal. + - 'Material' the material Asset.id of the decal. :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -76,9 +78,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def deferred_fog(property: str = 'name') -> str: + def deferred_fog(property: str = 'name') -> Union[str, List[str]]: """ Deferred Fog component properties. Requires PostFX Layer component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -89,11 +93,13 @@ class AtomComponentProperties: return properties[property] @staticmethod - def depth_of_field(property: str = 'name') -> str: + def depth_of_field(property: str = 'name') -> Union[str, List[str]]: """ Depth of Field component properties. Requires PostFX Layer component. - 'Camera Entity' an EditorEntity.id reference to the Camera component required for this effect. - Must be a different entity than the one which hosts Depth of Field component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n + - 'Camera Entity' an EditorEntity.id reference to the Camera component required for this effect. + Must be a different entity than the one which hosts Depth of Field component.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -105,10 +111,10 @@ class AtomComponentProperties: return properties[property] @staticmethod - def diffuse_probe(property: str = 'name') -> str: + def diffuse_probe(property: str = 'name') -> Union[str, List[str]]: """ - Diffuse Probe Grid component properties. Requires one of 'shapes' listed. - 'shapes' a list of supported shapes as component names. + Diffuse Probe Grid component properties. Requires one of 'shapes'. + - 'shapes' a list of supported shapes as component names. :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -122,8 +128,8 @@ class AtomComponentProperties: def directional_light(property: str = 'name') -> str: """ Directional Light component properties. - 'Camera' an EditorEntity.id reference to the Camera component that controls cascaded shadow view frustum. - Must be a different entity than the one which hosts Directional Light component. + - 'Camera' an EditorEntity.id reference to the Camera component that controls cascaded shadow view frustum. + Must be a different entity than the one which hosts Directional Light component.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -158,9 +164,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def exposure_control(property: str = 'name') -> str: + def exposure_control(property: str = 'name') -> Union[str, List[str]]: """ Exposure Control component properties. Requires PostFX Layer component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -174,8 +182,8 @@ class AtomComponentProperties: def global_skylight(property: str = 'name') -> str: """ Global Skylight (IBL) component properties. - 'Diffuse Image' Asset.id for the cubemap image for determining diffuse lighting. - 'Specular Image' Asset.id for the cubemap image for determining specular lighting. + - 'Diffuse Image' Asset.id for the cubemap image for determining diffuse lighting. + - 'Specular Image' Asset.id for the cubemap image for determining specular lighting. :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -199,9 +207,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def hdr_color_grading(property: str = 'name') -> str: + def hdr_color_grading(property: str = 'name') -> Union[str, List[str]]: """ HDR Color Grading component properties. Requires PostFX Layer component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -227,7 +237,7 @@ class AtomComponentProperties: def light(property: str = 'name') -> str: """ Light component properties. - 'Light type' from atom_constants.py LIGHT_TYPES + - 'Light type' from atom_constants.py LIGHT_TYPES :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -238,9 +248,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def look_modification(property: str = 'name') -> str: + def look_modification(property: str = 'name') -> Union[str, List[str]]: """ Look Modification component properties. Requires PostFX Layer component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -251,9 +263,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def material(property: str = 'name') -> str: + def material(property: str = 'name') -> Union[str, List[str]]: """ Material component properties. Requires one of Actor OR Mesh component. + - 'requires' a list of component names as strings required by this component. + Only one of these is required at a time for this component.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -267,7 +281,7 @@ class AtomComponentProperties: def mesh(property: str = 'name') -> str: """ Mesh component properties. - 'Mesh Asset' Asset.id of the mesh model. + - 'Mesh Asset' Asset.id of the mesh model. :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. :rtype: str @@ -315,9 +329,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def postfx_gradient(property: str = 'name') -> str: + def postfx_gradient(property: str = 'name') -> Union[str, List[str]]: """ PostFX Gradient Weight Modifier component properties. Requires PostFX Layer component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -328,9 +344,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def postfx_radius(property: str = 'name') -> str: + def postfx_radius(property: str = 'name') -> Union[str, List[str]]: """ PostFX Radius Weight Modifier component properties. Requires PostFX Layer component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -341,10 +359,12 @@ class AtomComponentProperties: return properties[property] @staticmethod - def postfx_shape(property: str = 'name') -> str: + def postfx_shape(property: str = 'name') -> Union[str, List[str]]: """ PostFX Shape Weight Modifier component properties. Requires PostFX Layer and one of 'shapes' listed. - 'shapes' a list of supported shapes as component names. 'Tube Shape' is also supported by requires 'Spline'. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n + - 'shapes' a list of supported shapes as component names. 'Tube Shape' is also supported but requires 'Spline'. :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -357,11 +377,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def reflection_probe(property: str = 'name') -> str: + def reflection_probe(property: str = 'name') -> Union[str, List[str]]: """ Reflection Probe component properties. Requires one of 'shapes' listed. - 'shapes' a list of supported shapes as component names. - 'Baked Cubemap Path' Asset.id of the baked cubemap image generated by a call to 'BakeReflectionProbe' ebus. + - 'shapes' a list of supported shapes as component names. + - 'Baked Cubemap Path' Asset.id of the baked cubemap image generated by a call to 'BakeReflectionProbe' ebus. :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ @@ -373,9 +393,11 @@ class AtomComponentProperties: return properties[property] @staticmethod - def ssao(property: str = 'name') -> str: + def ssao(property: str = 'name') -> Union[str, List[str]]: """ SSAO component properties. Requires PostFX Layer component. + - 'requires' a list of component names as strings required by this component. + Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. :return: Full property path OR component name if no property specified. """ From c228e77e1c1236bfcf109542db90b526456572ec Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Tue, 19 Oct 2021 12:11:56 -0700 Subject: [PATCH 044/152] Fixes a bunch of bad casts in hierarchy tests Signed-off-by: kberg-amzn --- Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp | 5 ++--- Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h | 10 ++++------ Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h | 10 ++++------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index d6dd068c3f..d1f169cb88 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -211,9 +211,8 @@ namespace Multiplayer constexpr uint32_t bufferSize = 100; AZStd::array buffer = {}; NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); - inSerializer.Serialize(reinterpret_cast(value), - "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ - AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + ISerializer& serializer = inSerializer; + serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); diff --git a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h index 803e6cabca..3c3d77e011 100644 --- a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h @@ -538,9 +538,8 @@ namespace Multiplayer constexpr uint32_t bufferSize = 100; AZStd::array buffer = {}; NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); - inSerializer.Serialize(reinterpret_cast(netParentId), - "parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */ - AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + ISerializer& serializer = inSerializer; + serializer.Serialize(netParentId, "parentEntityId"); // Derived from NetworkTransformComponent.AutoComponent.xml NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); @@ -563,9 +562,8 @@ namespace Multiplayer constexpr uint32_t bufferSize = 100; AZStd::array buffer = {}; NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); - inSerializer.Serialize(reinterpret_cast(value), - "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ - AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + ISerializer& serializer = inSerializer; + serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h index d6f65918c0..214b991dc9 100644 --- a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -303,9 +303,8 @@ namespace Multiplayer constexpr uint32_t bufferSize = 100; AZStd::array buffer = {}; NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); - inSerializer.Serialize(reinterpret_cast(netParentId), - "parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */ - AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + ISerializer& serializer = inSerializer; + serializer.Serialize(netParentId, "parentEntityId"); // Derived from NetworkTransformComponent.AutoComponent.xml NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); @@ -351,9 +350,8 @@ namespace Multiplayer constexpr uint32_t bufferSize = 100; AZStd::array buffer = {}; NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); - inSerializer.Serialize(reinterpret_cast(value), - "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ - AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + ISerializer& serializer = inSerializer; + serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); From f31b47f775a15c4a669dca28ac023eca86c7e69e Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue, 19 Oct 2021 18:51:34 -0500 Subject: [PATCH 045/152] Switch stacktrace output to use Output function instead of AZ_Printf to avoid it being suppressed accidentally (#4805) Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Debug/Trace.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp index cde8c36a4e..357149d096 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp @@ -577,7 +577,9 @@ namespace AZ } azstrcat(lines[i], AZ_ARRAY_SIZE(lines[i]), "\n"); - AZ_Printf(window, "%s", lines[i]); // feed back into the trace system so that listeners can get it. + // Use Output instead of AZ_Printf to be consistent with the exception output code and avoid + // this accidentally being suppressed as a normal message + Output(window, lines[i]); } } } From 02364b869e20adb9828678f0c76910df34b30285 Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Tue, 19 Oct 2021 19:48:36 -0700 Subject: [PATCH 046/152] First part of UI feedback for downloading gems Signed-off-by: AMZN-Phil --- .../Resources/ProjectManager.qss | 12 ++ .../GemCatalog/GemCatalogHeaderWidget.cpp | 119 +++++++++++++++++- .../GemCatalog/GemCatalogHeaderWidget.h | 10 +- .../Source/GemCatalog/GemCatalogScreen.cpp | 6 +- .../Source/GemCatalog/GemCatalogScreen.h | 2 + .../ProjectManager/Source/PythonBindings.cpp | 27 ++++ .../ProjectManager/Source/PythonBindings.h | 2 + .../Source/PythonBindingsInterface.h | 2 + .../Source/UpdateProjectCtrl.cpp | 7 +- .../project_manager_files.cmake | 4 + 10 files changed, 181 insertions(+), 10 deletions(-) diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 5f7826dbac..507cf38726 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -481,6 +481,18 @@ QProgressBar::chunk { font-weight: 600; } +#GemCatalogCartOverlayGemDownloadHeader { + margin:0; + padding: 0px; + background-color: #333333; +} + +#GemCatalogCartOverlayGemDownloadBG { + margin:0; + padding: 0px; + background-color: #444444; +} + #GemCatalogHeaderLabel { font-size: 12px; color: #FFFFFF; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 9fca6040d4..7d25ae180b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -12,13 +12,15 @@ #include #include #include +#include #include namespace O3DE::ProjectManager { - CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, QWidget* parent) + CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, O3DEObjectDownloadController* downloadController, QWidget* parent) : QWidget(parent) , m_gemModel(gemModel) + , m_downloadController(downloadController) { setObjectName("GemCatalogCart"); @@ -42,6 +44,9 @@ namespace O3DE::ProjectManager hLayout->addWidget(closeButton); m_layout->addLayout(hLayout); + // downloading gems + CreateDownloadSection(); + // added CreateGemSection( tr("Gem to be activated"), tr("Gems to be activated"), [=] { @@ -149,6 +154,109 @@ namespace O3DE::ProjectManager update(); } + void CartOverlayWidget::CreateDownloadSection() + { + QWidget* widget = new QWidget(); + widget->setFixedWidth(s_width); + m_layout->addWidget(widget); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + widget->setLayout(layout); + + QLabel* label = new QLabel(); + label->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(label); + + label->setText(tr("Gems to be installed")); + + // Create header section + QWidget* downloadingGemsWidget = new QWidget(); + downloadingGemsWidget->setObjectName("GemCatalogCartOverlayGemDownloadHeader"); + layout->addWidget(downloadingGemsWidget); + QVBoxLayout* gemDownloadLayout = new QVBoxLayout(); + gemDownloadLayout->setMargin(0); + gemDownloadLayout->setAlignment(Qt::AlignTop); + downloadingGemsWidget->setLayout(gemDownloadLayout); + QLabel* processingQueueLabel = new QLabel("Processing Queue"); + gemDownloadLayout->addWidget(processingQueueLabel); + + QWidget* downloadingItemWidget = new QWidget(); + downloadingItemWidget->setObjectName("GemCatalogCartOverlayGemDownloadBG"); + gemDownloadLayout->addWidget(downloadingItemWidget); + QVBoxLayout* downloadingItemLayout = new QVBoxLayout(); + downloadingItemLayout->setAlignment(Qt::AlignTop); + downloadingItemWidget->setLayout(downloadingItemLayout); + + auto update = [=](int downloadProgress) + { + if (m_downloadController->IsDownloadQueueEmpty()) + { + widget->hide(); + } + else + { + widget->setUpdatesEnabled(false); + // remove items + QLayoutItem* layoutItem = nullptr; + while ((layoutItem = downloadingItemLayout->takeAt(0)) != nullptr) + { + if (layoutItem->layout()) + { + // Gem info row + QLayoutItem* rowLayoutItem = nullptr; + while ((rowLayoutItem = layoutItem->layout()->takeAt(0)) != nullptr) + { + rowLayoutItem->widget()->deleteLater(); + } + layoutItem->layout()->deleteLater(); + } + if (layoutItem->widget()) + { + layoutItem->widget()->deleteLater(); + } + } + + // Setup gem download rows + const AZStd::vector& downloadQueue = m_downloadController->GetDownloadQueue(); + + QLabel* downloadsInProgessLabel = new QLabel(""); + downloadsInProgessLabel->setText( + QString("%1 %2").arg(downloadQueue.size()).arg(downloadQueue.size() == 1 ? tr("download in progress...") : tr("downloads in progress..."))); + downloadingItemLayout->addWidget(downloadsInProgessLabel); + + for (int downloadingGemNumber = 0; downloadingGemNumber < downloadQueue.size(); ++downloadingGemNumber) + { + QHBoxLayout* nameProgressLayout = new QHBoxLayout(); + TagWidget* newTag = new TagWidget(downloadQueue[downloadingGemNumber]); + nameProgressLayout->addWidget(newTag); + QLabel* progress = new QLabel(downloadingGemNumber == 0? QString("%1%").arg(downloadProgress) : tr("Queued")); + nameProgressLayout->addWidget(progress); + QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + nameProgressLayout->addSpacerItem(spacer); + QLabel* cancelText = new QLabel(tr("Cancel")); + nameProgressLayout->addWidget(cancelText); + downloadingItemLayout->addLayout(nameProgressLayout); + QProgressBar* downloadProgessBar = new QProgressBar(); + downloadingItemLayout->addWidget(downloadProgessBar); + downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0); + } + + widget->setUpdatesEnabled(true); + widget->show(); + } + }; + + auto downloadEnded = [=](bool /*success*/) + { + update(0); // update the list to remove the gem that has finished + }; + // connect to download controller data changed + connect(m_downloadController, &O3DEObjectDownloadController::GemDownloadProgress, this, update); + connect(m_downloadController, &O3DEObjectDownloadController::Done, this, downloadEnded); + update(0); + } + QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector& gems) const { QStringList gemNames; @@ -160,9 +268,10 @@ namespace O3DE::ProjectManager return gemNames; } - CartButton::CartButton(GemModel* gemModel, QWidget* parent) + CartButton::CartButton(GemModel* gemModel, O3DEObjectDownloadController* downloadController, QWidget* parent) : QWidget(parent) , m_gemModel(gemModel) + , m_downloadController(downloadController) { m_layout = new QHBoxLayout(); m_layout->setMargin(0); @@ -239,7 +348,7 @@ namespace O3DE::ProjectManager delete m_cartOverlay; } - m_cartOverlay = new CartOverlayWidget(m_gemModel, this); + m_cartOverlay = new CartOverlayWidget(m_gemModel, m_downloadController, this); connect(m_cartOverlay, &QWidget::destroyed, this, [=] { // Reset the overlay pointer on destruction to prevent dangling pointers. @@ -265,7 +374,7 @@ namespace O3DE::ProjectManager } } - GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent) + GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, O3DEObjectDownloadController* downloadController, QWidget* parent) : QFrame(parent) { QHBoxLayout* hLayout = new QHBoxLayout(); @@ -293,7 +402,7 @@ namespace O3DE::ProjectManager hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); hLayout->addSpacerItem(new QSpacerItem(75, 0, QSizePolicy::Fixed)); - CartButton* cartButton = new CartButton(gemModel); + CartButton* cartButton = new CartButton(gemModel, downloadController); hLayout->addWidget(cartButton); } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 2cfda4c790..3d977e2b15 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -21,6 +21,7 @@ #include #include #include +#include #endif namespace O3DE::ProjectManager @@ -31,16 +32,18 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - CartOverlayWidget(GemModel* gemModel, QWidget* parent = nullptr); + CartOverlayWidget(GemModel* gemModel, O3DEObjectDownloadController* downloadController, QWidget* parent = nullptr); private: QStringList ConvertFromModelIndices(const QVector& gems) const; using GetTagIndicesCallback = AZStd::function()>; void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices); + void CreateDownloadSection(); QVBoxLayout* m_layout = nullptr; GemModel* m_gemModel = nullptr; + O3DEObjectDownloadController* m_downloadController = nullptr; inline constexpr static int s_width = 240; }; @@ -51,7 +54,7 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - CartButton(GemModel* gemModel, QWidget* parent = nullptr); + CartButton(GemModel* gemModel, O3DEObjectDownloadController* downloadController, QWidget* parent = nullptr); ~CartButton(); void ShowOverlay(); @@ -64,6 +67,7 @@ namespace O3DE::ProjectManager QLabel* m_countLabel = nullptr; QPushButton* m_dropDownButton = nullptr; CartOverlayWidget* m_cartOverlay = nullptr; + O3DEObjectDownloadController* m_downloadController = nullptr; inline constexpr static int s_iconSize = 24; inline constexpr static int s_arrowDownIconSize = 8; @@ -75,7 +79,7 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); + explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, O3DEObjectDownloadController* downloadController, QWidget* parent = nullptr); ~GemCatalogHeaderWidget() = default; void ReinitForProject(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 945878768d..5a4462d14b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,10 @@ namespace O3DE::ProjectManager vLayout->setSpacing(0); setLayout(vLayout); - m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel); + m_downloadController = new O3DEObjectDownloadController(); + m_downloadController->Start(); + + m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController); vLayout->addWidget(m_headerWidget); QHBoxLayout* hLayout = new QHBoxLayout(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 72e8d44f65..ad603138f0 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -39,6 +39,7 @@ namespace O3DE::ProjectManager EnableDisableGemsResult EnableDisableGemsForProject(const QString& projectPath); GemModel* GetGemModel() const { return m_gemModel; } + O3DEObjectDownloadController* GetDownloadController() const { return m_downloadController; } private: void FillModel(const QString& projectPath); @@ -50,5 +51,6 @@ namespace O3DE::ProjectManager GemSortFilterProxyModel* m_proxModel = nullptr; QVBoxLayout* m_filterWidgetLayout = nullptr; GemFilterWidget* m_filterWidget = nullptr; + O3DEObjectDownloadController* m_downloadController = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 633116e8b6..bc8773b0c8 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -301,6 +301,7 @@ namespace O3DE::ProjectManager m_enableGemProject = pybind11::module::import("o3de.enable_gem"); m_disableGemProject = pybind11::module::import("o3de.disable_gem"); m_editProjectProperties = pybind11::module::import("o3de.project_properties"); + m_download = pybind11::module::import("o3de.download"); m_pathlib = pybind11::module::import("pathlib"); // make sure the engine is registered @@ -1075,4 +1076,30 @@ namespace O3DE::ProjectManager std::sort(gemRepos.begin(), gemRepos.end()); return AZ::Success(AZStd::move(gemRepos)); } + + AZ::Outcome PythonBindings::DownloadGem(const QString& gemName, std::function gemProgressCallback) + { + bool downloadSucceeded = false; + auto result = ExecuteWithLockErrorHandling( + [&] + { + auto downloadResult = m_download.attr("download_gem")( + QString_To_Py_String(gemName), // gem name + pybind11::none(), // destination path + false// skip auto register + ); + downloadSucceeded = (downloadResult.cast() == 0); + }); + + if (!result.IsSuccess()) + { + return result; + } + else if (!downloadSucceeded) + { + return AZ::Failure("Failed to download gem."); + } + + return AZ::Success(); + } } diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index d0704d0bd1..638ce6b1d4 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -61,6 +61,7 @@ namespace O3DE::ProjectManager bool AddGemRepo(const QString& repoUri) override; bool RemoveGemRepo(const QString& repoUri) override; AZ::Outcome, AZStd::string> GetAllGemRepoInfos() override; + AZ::Outcome DownloadGem(const QString& gemName, std::function gemProgressCallback) override; private: AZ_DISABLE_COPY_MOVE(PythonBindings); @@ -87,6 +88,7 @@ namespace O3DE::ProjectManager pybind11::handle m_enableGemProject; pybind11::handle m_disableGemProject; pybind11::handle m_editProjectProperties; + pybind11::handle m_download; pybind11::handle m_pathlib; }; } diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index 5daf543c11..19442540a4 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -187,6 +187,8 @@ namespace O3DE::ProjectManager * @return A list of gem repo infos. */ virtual AZ::Outcome, AZStd::string> GetAllGemRepoInfos() = 0; + + virtual AZ::Outcome DownloadGem(const QString& gemName, std::function gemProgressCallback) = 0; }; using PythonBindingsInterface = AZ::Interface; diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index e952ada57a..40f114b82b 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -38,7 +38,7 @@ namespace O3DE::ProjectManager vLayout->addWidget(m_header); m_updateSettingsScreen = new UpdateProjectSettingsScreen(); - m_gemCatalogScreen = new GemCatalogScreen(); + m_gemCatalogScreen = new GemCatalogScreen(nullptr); m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); @@ -136,6 +136,11 @@ namespace O3DE::ProjectManager } else if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen) { + if (!m_gemCatalogScreen->GetDownloadController()->IsDownloadQueueEmpty()) + { + QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing.")); + return; + } // Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project. const GemCatalogScreen::EnableDisableGemsResult result = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path); if (result == GemCatalogScreen::EnableDisableGemsResult::Failed) diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index fd8389ca4f..d53cf6c8c1 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -29,6 +29,10 @@ set(FILES Source/FormImageBrowseEditWidget.cpp Source/GemsSubWidget.h Source/GemsSubWidget.cpp + Source/O3DEObjectDownloadController.h + Source/O3DEObjectDownloadController.cpp + Source/O3DEObjectDownloadWorker.h + Source/O3DEObjectDownloadWorker.cpp Source/PathValidator.h Source/PathValidator.cpp Source/ProjectManagerWindow.h From 8e520c8db8f845f3162d866c58eb4148a4247a2a Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Wed, 20 Oct 2021 12:36:35 +0100 Subject: [PATCH 047/152] PR feedback Signed-off-by: Sergey Pereslavtsev --- .../AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml | 4 ++-- Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml index f8df21a220..fbe6ff2135 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -12,7 +12,7 @@ - - + + diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h index 471d86f718..417d9d65f7 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h @@ -34,4 +34,6 @@ namespace Multiplayer ConstNetworkEntityHandle m_owner; NetworkInput m_networkInput; }; + + using NetworkInputChildList = AZStd::vector; } From 9e4a39f9ed5ea59481f49b4e8e3f09ed8440db8f Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Wed, 20 Oct 2021 09:30:21 -0400 Subject: [PATCH 048/152] Hierarchy code enhancements Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Source/Components/NetworkHierarchyRootComponent.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index eeb61ef450..040792d132 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -20,6 +20,8 @@ AZ_CVAR(uint32_t, bg_hierarchyEntityMaxLimit, 16, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum allowed size of network entity hierarchies, including top level entity."); +static constexpr int CommonHierarchyEntityMaxLimit = 16; // Should match @bg_hierarchyEntityMaxLimit + namespace Multiplayer { void NetworkHierarchyRootComponent::Reflect(AZ::ReflectContext* context) @@ -270,7 +272,7 @@ namespace Multiplayer { AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); - AZStd::deque candidates; + AZStd::deque candidates; candidates.push_back(underEntity); while (!candidates.empty()) @@ -289,6 +291,9 @@ namespace Multiplayer if (m_hierarchicalEntities.size() >= bg_hierarchyEntityMaxLimit) { + AZLOG_WARN("Network hierarchy size exceeded, current limit is %d, root entity was %s", + static_cast(bg_hierarchyEntityMaxLimit), + GetEntity()->GetName().c_str()); return; } From 38f9dcb8ca49284a422a179d6ba801a63f2b55c7 Mon Sep 17 00:00:00 2001 From: AMZN-AlexOteiza <82234181+AMZN-AlexOteiza@users.noreply.github.com> Date: Wed, 20 Oct 2021 14:55:20 +0100 Subject: [PATCH 049/152] Added option to reopen if the current level is the same. This contributes to stability for batched tests (#4043) * Added option to reopen if the current level is the same. This contributes to stability for batched tests Signed-off-by: AMZN-AlexOteiza * Addressed PR comments * Addressed PR and cleaned Base level that had an entity by accident Signed-off-by: AMZN-AlexOteiza * Cleaned up params Signed-off-by: AMZN-AlexOteiza * Addressed PR comments --- AutomatedTesting/Levels/Base/Base.ly | 4 ++-- Code/Editor/CryEdit.cpp | 25 +++++++++++++------------ Code/Editor/CryEdit.h | 14 +++++++++++--- Code/Editor/CryEditPy.cpp | 17 ++++------------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/AutomatedTesting/Levels/Base/Base.ly b/AutomatedTesting/Levels/Base/Base.ly index 8ce19923c0..0d0aa64648 100644 --- a/AutomatedTesting/Levels/Base/Base.ly +++ b/AutomatedTesting/Levels/Base/Base.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4937547ca4c486ef59656314401933217e0e0401fec103e1fb91c25ec60a177 -size 2806 +oid sha256:a5f9e27e0f22c31ca61d866fb594c6fde5b8ceb891e17dda075fa1e0033ec2b9 +size 1666 diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 027198ac80..8f42b1f4a1 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -287,21 +287,22 @@ bool CCryDocManager::DoPromptFileName(QString& fileName, [[maybe_unused]] UINT n return false; } -CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAddToMRU) +CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions) { - assert(lpszFileName != nullptr); + assert(filename != nullptr); + const bool reopenIfSame = openSameLevelOptions == COpenSameLevelOptions::ReopenLevelIfSame; // find the highest confidence auto pos = m_templateList.begin(); CCrySingleDocTemplate::Confidence bestMatch = CCrySingleDocTemplate::noAttempt; CCrySingleDocTemplate* pBestTemplate = nullptr; CCryEditDoc* pOpenDocument = nullptr; - if (lpszFileName[0] == '\"') + if (filename[0] == '\"') { - ++lpszFileName; + ++filename; } - QString szPath = QString::fromUtf8(lpszFileName); + QString szPath = QString::fromUtf8(filename); if (szPath.endsWith('"')) { szPath.remove(szPath.length() - 1, 1); @@ -325,7 +326,7 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAd } } - if (pOpenDocument != nullptr) + if (!reopenIfSame && pOpenDocument != nullptr) { return pOpenDocument; } @@ -336,7 +337,7 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAd return nullptr; } - return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), bAddToMRU, false); + return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), addToMostRecentFileList, false); } ////////////////////////////////////////////////////////////////////////////// @@ -818,7 +819,7 @@ CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, b return OpenDocumentFile(lpszPathName, true, bMakeVisible); } -CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool bAddToMRU, [[maybe_unused]] bool bMakeVisible) +CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, [[maybe_unused]] bool bMakeVisible) { CCryEditDoc* pCurDoc = GetIEditor()->GetDocument(); @@ -848,7 +849,7 @@ CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, b { pCurDoc->OnOpenDocument(lpszPathName); pCurDoc->SetPathName(lpszPathName); - if (bAddToMRU) + if (addToMostRecentFileList) { CCryEditApp::instance()->AddToRecentFileList(lpszPathName); } @@ -3365,7 +3366,7 @@ void CCryEditApp::OnOpenSlice() } ////////////////////////////////////////////////////////////////////////// -CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* lpszFileName) +CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions) { if (m_openingLevel) { @@ -3405,9 +3406,9 @@ CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* lpszFileName) openDocTraceHandler.SetShowWindow(false); } - // in this case, we set bAddToMRU to always be true because adding files to the MRU list + // in this case, we set addToMostRecentFileList to always be true because adding files to the MRU list // automatically culls duplicate and normalizes paths anyway - m_pDocManager->OpenDocumentFile(lpszFileName, true); + m_pDocManager->OpenDocumentFile(filename, addToMostRecentFileList, openSameLevelOptions); if (openDocTraceHandler.HasAnyErrors()) { diff --git a/Code/Editor/CryEdit.h b/Code/Editor/CryEdit.h index 730af7c034..f68cfdc33d 100644 --- a/Code/Editor/CryEdit.h +++ b/Code/Editor/CryEdit.h @@ -85,6 +85,12 @@ public: using EditorIdleProcessingBus = AZ::EBus; +enum class COpenSameLevelOptions +{ + ReopenLevelIfSame, + NotReopenIfSame +}; + AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING class SANDBOX_API CCryEditApp @@ -174,7 +180,9 @@ public: virtual bool InitInstance(); virtual int ExitInstance(int exitCode = 0); virtual bool OnIdle(LONG lCount); - virtual CCryEditDoc* OpenDocumentFile(const char* lpszFileName); + virtual CCryEditDoc* OpenDocumentFile(const char* filename, + bool addToMostRecentFileList=true, + COpenSameLevelOptions openSameLevelOptions = COpenSameLevelOptions::NotReopenIfSame); CCryDocManager* GetDocManager() { return m_pDocManager; } @@ -448,7 +456,7 @@ public: ~CCrySingleDocTemplate() {}; // avoid creating another CMainFrame // close other type docs before opening any things - virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool bAddToMRU, bool bMakeVisible); + virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, bool bMakeVisible); virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool bMakeVisible = TRUE); virtual Confidence MatchDocType(const char* lpszPathName, CCryEditDoc*& rpDocMatch); @@ -468,7 +476,7 @@ public: virtual void OnFileNew(); virtual bool DoPromptFileName(QString& fileName, UINT nIDSTitle, DWORD lFlags, bool bOpenFileDialog, CDocTemplate* pTemplate); - virtual CCryEditDoc* OpenDocumentFile(const char* lpszFileName, bool bAddToMRU); + virtual CCryEditDoc* OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions = COpenSameLevelOptions::NotReopenIfSame); QVector m_templateList; }; diff --git a/Code/Editor/CryEditPy.cpp b/Code/Editor/CryEditPy.cpp index 1b38fe23b8..427737335c 100644 --- a/Code/Editor/CryEditPy.cpp +++ b/Code/Editor/CryEditPy.cpp @@ -143,20 +143,11 @@ namespace return false; } } + const bool addToMostRecentFileList = false; + auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.toUtf8().data(), + addToMostRecentFileList, COpenSameLevelOptions::ReopenLevelIfSame); - auto previousDocument = GetIEditor()->GetDocument(); - QString previousPathName = (previousDocument != nullptr) ? previousDocument->GetLevelPathName() : ""; - auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.toUtf8().data()); - - // the underlying document pointer doesn't change, so we can't check that; use the path name's instead - - bool result = true; - if (newDocument == nullptr || newDocument->IsLevelLoadFailed() || (newDocument->GetLevelPathName() == previousPathName)) - { - result = false; - } - - return result; + return newDocument != nullptr && !newDocument->IsLevelLoadFailed(); } bool PyOpenLevelNoPrompt(const char* pLevelName) From 181d12625fe38bb9670b003d38b4c0e87590d805 Mon Sep 17 00:00:00 2001 From: AMZN-AlexOteiza <82234181+AMZN-AlexOteiza@users.noreply.github.com> Date: Wed, 20 Oct 2021 16:16:04 +0100 Subject: [PATCH 050/152] Reorganized Prefab tests and standarization (#4786) * Reorganized Prefab tests and standarization * Restore changed file by accident --- .../editor_entity_utils.py | 32 ++++++++-- .../editor_python_test_tools/prefab_utils.py} | 63 ++++++++++++------- .../Gem/PythonTests/prefab/TestSuite_Main.py | 18 +++--- .../PythonTests/prefab/{ => data}/Test.prefab | 0 ...fabBasicWorkflow_CreateAndDeletePrefab.py} | 12 ++-- ...bBasicWorkflow_CreateAndReparentPrefab.py} | 8 +-- .../PrefabBasicWorkflow_CreatePrefab.py} | 8 +-- .../PrefabBasicWorkflow_InstantiatePrefab.py} | 10 +-- .../PrefabLevel_OpensLevelWithEntities.py | 6 +- .../PrefabTestUtils.py} | 17 ----- Code/Editor/CryEditPy.cpp | 7 +++ 11 files changed, 104 insertions(+), 77 deletions(-) rename AutomatedTesting/Gem/PythonTests/{prefab/Prefab.py => EditorPythonTestTools/editor_python_test_tools/prefab_utils.py} (85%) rename AutomatedTesting/Gem/PythonTests/prefab/{ => data}/Test.prefab (100%) rename AutomatedTesting/Gem/PythonTests/prefab/{Prefab_BasicWorkflow_CreateAndDeletePrefab.py => tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py} (70%) rename AutomatedTesting/Gem/PythonTests/prefab/{Prefab_BasicWorkflow_CreateAndReparentPrefab.py => tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py} (85%) rename AutomatedTesting/Gem/PythonTests/prefab/{Prefab_BasicWorkflow_CreatePrefab.py => tests/PrefabBasicWorkflow_CreatePrefab.py} (79%) rename AutomatedTesting/Gem/PythonTests/prefab/{Prefab_BasicWorkflow_InstantiatePrefab.py => tests/PrefabBasicWorkflow_InstantiatePrefab.py} (74%) rename AutomatedTesting/Gem/PythonTests/prefab/{ => tests}/PrefabLevel_OpensLevelWithEntities.py (87%) rename AutomatedTesting/Gem/PythonTests/prefab/{Prefab_Test_Utils.py => tests/PrefabTestUtils.py} (81%) diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py index 783f71e06c..844f8de903 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py @@ -122,17 +122,32 @@ class EditorEntity: # Creation functions @classmethod - def find_editor_entity(cls, entity_name: str) -> EditorEntity: + def find_editor_entity(cls, entity_name: str, must_be_unique : bool = False) -> EditorEntity: """ Given Entity name, outputs entity object :param entity_name: Name of entity to find :return: EditorEntity class object """ - entity_id = general.find_editor_entity(entity_name) - assert entity_id.IsValid(), f"Failure: Couldn't find entity with name: '{entity_name}'" - entity = cls(entity_id) + entities = cls.find_editor_entities([entity_name]) + assert len(entities) != 0, f"Failure: Couldn't find entity with name: '{entity_name}'" + if must_be_unique: + assert len(entities) == 1, f"Failure: Multiple entities with name: '{entity_name}' when expected only one" + + entity = cls(entities[0]) return entity + @classmethod + def find_editor_entities(cls, entity_names: List[str]) -> EditorEntity: + """ + Given Entities names, returns a list of EditorEntity + :param entity_name: Name of entity to find + :return: List[EditorEntity] class object + """ + searchFilter = azlmbr.entity.SearchFilter() + searchFilter.names = entity_names + ids = azlmbr.entity.SearchBus(bus.Broadcast, 'SearchEntities', searchFilter) + return [cls(id) for id in ids] + @classmethod def create_editor_entity(cls, name: str = None, parent_id=None) -> EditorEntity: """ @@ -157,8 +172,7 @@ class EditorEntity: cls, entity_position: Union[List, Tuple, math.Vector3], name: str = None, - parent_id: azlmbr.entity.EntityId = None, - ) -> EditorEntity: + parent_id: azlmbr.entity.EntityId = None) -> EditorEntity: """ Used to create entity at position using 'CreateNewEntityAtPosition' Bus. :param entity_position: World Position(X, Y, Z) of entity in viewport. @@ -227,6 +241,12 @@ class EditorEntity: """ return editor.EditorEntityInfoRequestBus(bus.Event, "GetChildren", self.id) + def get_children(self) -> List[EditorEntity]: + """ + :return: List of EditorEntity children. Type: [EditorEntity] + """ + return [EditorEntity(child_id) for child_id in self.get_children_ids()] + def add_component(self, component_name: str) -> EditorComponent: """ Used to add new component to Entity. diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py similarity index 85% rename from AutomatedTesting/Gem/PythonTests/prefab/Prefab.py rename to AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py index 9b4d2d1393..2d8b124125 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py @@ -12,20 +12,39 @@ from os import path from PySide2 import QtWidgets +import azlmbr.legacy.general as general from azlmbr.entity import EntityId from azlmbr.math import Vector3 from editor_python_test_tools.editor_entity_utils import EditorEntity from editor_python_test_tools.utils import Report +import azlmbr.entity as entity import azlmbr.bus as bus import azlmbr.prefab as prefab import editor_python_test_tools.pyside_utils as pyside_utils -import prefab.Prefab_Test_Utils as prefab_test_utils + + +def get_prefab_file_path(prefab_path): + if not path.isabs(prefab_path): + prefab_path = path.join(general.get_file_alias("@projectroot@"), prefab_path) + + # Append prefab if it doesn't contain .prefab on it + name, ext = path.splitext(prefab_path) + if ext != ".prefab": + prefab_path = name + ".prefab" + return prefab_path + + +def get_all_entity_ids(): + return entity.SearchBus(bus.Broadcast, 'SearchEntities', entity.SearchFilter()) + +def wait_for_propagation(): + general.idle_wait_frames(1) # This is a helper class which contains some of the useful information about a prefab instance. class PrefabInstance: - def __init__(self, prefab_file_name: str=None, container_entity: EditorEntity=EntityId()): + def __init__(self, prefab_file_name: str = None, container_entity: EditorEntity = None): self.prefab_file_name: str = prefab_file_name self.container_entity: EditorEntity = container_entity @@ -42,7 +61,7 @@ class PrefabInstance: See if this instance is valid to be used with other prefab operations. :return: Whether the target instance is valid or not. """ - def is_valid() -> bool: + def is_valid(self) -> bool: return self.container_entity.id.IsValid() and self.prefab_file_name in Prefab.existing_prefabs """ @@ -60,7 +79,7 @@ class PrefabInstance: new_parent_before_reparent_children_ids = set(new_parent.get_children_ids()) pyside_utils.run_soon(lambda: self.container_entity.set_parent_entity(parent_entity_id)) - pyside_utils.run_soon(lambda: prefab_test_utils.wait_for_propagation()) + pyside_utils.run_soon(lambda: wait_for_propagation()) try: active_modal_widget = await pyside_utils.wait_for_modal_widget() @@ -94,19 +113,18 @@ class Prefab: existing_prefabs = {} - def __init__(self, file_name: str): - self.file_name:str = file_name - self.file_path: str = prefab_test_utils.get_prefab_file_path(file_name) + def __init__(self, file_path: str): + self.file_path: str = get_prefab_file_path(file_path) self.instances: set[PrefabInstance] = set() """ Check if a prefab is ready to be used to generate its instances. - :param file_name: A unique file name of the target prefab. + :param file_path: A unique file path of the target prefab. :return: Whether the target prefab is loaded or not. """ @classmethod - def is_prefab_loaded(cls, file_name: str) -> bool: - return file_name in Prefab.existing_prefabs + def is_prefab_loaded(cls, file_path: str) -> bool: + return file_path in Prefab.existing_prefabs """ Check if a prefab exists in the directory for files of prefab tests. @@ -114,9 +132,8 @@ class Prefab: :return: Whether the target prefab exists or not. """ @classmethod - def prefab_exists(cls, file_name: str) -> bool: - file_path = prefab_test_utils.get_prefab_file_path(file_name) - return path.exists(file_path) + def prefab_exists(cls, file_path: str) -> bool: + return path.exists(get_prefab_file_path(file_path)) """ Return a prefab which can be used immediately. @@ -125,10 +142,11 @@ class Prefab: """ @classmethod def get_prefab(cls, file_name: str) -> Prefab: + assert file_name, "Received an empty file_name" if Prefab.is_prefab_loaded(file_name): return Prefab.existing_prefabs[file_name] else: - assert Prefab.prefab_exists(file_name), f"Attempted to get a prefab {file_name} that doesn't exist" + assert Prefab.prefab_exists(file_name), f"Attempted to get a prefab \"{file_name}\" that doesn't exist" new_prefab = Prefab(file_name) Prefab.existing_prefabs[file_name] = Prefab(file_name) return new_prefab @@ -141,7 +159,7 @@ class Prefab: :return: Created Prefab object and the very first PrefabInstance object owned by the prefab. """ @classmethod - def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> (Prefab, PrefabInstance): + def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> tuple(Prefab, PrefabInstance): assert not Prefab.is_prefab_loaded(file_name), f"Can't create Prefab '{file_name}' since the prefab already exists" new_prefab = Prefab(file_name) @@ -155,7 +173,7 @@ class Prefab: if prefab_instance_name: container_entity.set_name(prefab_instance_name) - prefab_test_utils.wait_for_propagation() + wait_for_propagation() new_prefab_instance = PrefabInstance(file_name, EditorEntity(container_entity_id)) new_prefab.instances.add(new_prefab_instance) @@ -182,12 +200,13 @@ class Prefab: delete_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'DeleteEntitiesAndAllDescendantsInInstance', container_entity_ids) assert delete_prefab_result.IsSuccess(), f"Prefab operation 'DeleteEntitiesAndAllDescendantsInInstance' failed. Error: {delete_prefab_result.GetError()}" - prefab_test_utils.wait_for_propagation() + wait_for_propagation() - entity_ids_after_delete = set(prefab_test_utils.get_all_entities()) + entity_ids_after_delete = set(get_all_entity_ids()) + for entity_id_removed in entity_ids_to_remove: if entity_id_removed in entity_ids_after_delete: - assert prefab_entities_deleted, "Not all entities and descendants in target prefabs are deleted." + assert False, "Not all entities and descendants in target prefabs are deleted." for instance in prefab_instances: instance_deleted_prefab = Prefab.get_prefab(instance.prefab_file_name) @@ -215,12 +234,10 @@ class Prefab: if name: container_entity.set_name(name) - prefab_test_utils.wait_for_propagation() + wait_for_propagation() - new_prefab_instance = PrefabInstance(self.file_name, EditorEntity(container_entity_id)) + new_prefab_instance = PrefabInstance(self.file_path, EditorEntity(container_entity_id)) assert not new_prefab_instance in self.instances, "This prefab instance is already existed before this instantiation." self.instances.add(new_prefab_instance) - prefab_test_utils.check_entity_at_position(container_entity_id, prefab_position) - return new_prefab_instance diff --git a/AutomatedTesting/Gem/PythonTests/prefab/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/prefab/TestSuite_Main.py index 09df962b0b..30a0055d5a 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/prefab/TestSuite_Main.py @@ -29,21 +29,21 @@ class TestAutomation(TestAutomationBase): autotest_mode=autotest_mode) def test_PrefabLevel_OpensLevelWithEntities(self, request, workspace, editor, launcher_platform): - from . import PrefabLevel_OpensLevelWithEntities as test_module + from .tests import PrefabLevel_OpensLevelWithEntities as test_module self._run_prefab_test(request, workspace, editor, test_module) - def test_Prefab_BasicWorkflow_CreatePrefab(self, request, workspace, editor, launcher_platform): - from . import Prefab_BasicWorkflow_CreatePrefab as test_module + def test_PrefabBasicWorkflow_CreatePrefab(self, request, workspace, editor, launcher_platform): + from .tests import PrefabBasicWorkflow_CreatePrefab as test_module self._run_prefab_test(request, workspace, editor, test_module) - def test_Prefab_BasicWorkflow_InstantiatePrefab(self, request, workspace, editor, launcher_platform): - from . import Prefab_BasicWorkflow_InstantiatePrefab as test_module + def test_PrefabBasicWorkflow_InstantiatePrefab(self, request, workspace, editor, launcher_platform): + from .tests import PrefabBasicWorkflow_InstantiatePrefab as test_module self._run_prefab_test(request, workspace, editor, test_module) - def test_Prefab_BasicWorkflow_CreateAndDeletePrefab(self, request, workspace, editor, launcher_platform): - from . import Prefab_BasicWorkflow_CreateAndDeletePrefab as test_module + def test_PrefabBasicWorkflow_CreateAndDeletePrefab(self, request, workspace, editor, launcher_platform): + from .tests import PrefabBasicWorkflow_CreateAndDeletePrefab as test_module self._run_prefab_test(request, workspace, editor, test_module) - def test_Prefab_BasicWorkflow_CreateAndReparentPrefab(self, request, workspace, editor, launcher_platform): - from . import Prefab_BasicWorkflow_CreateAndReparentPrefab as test_module + def test_PrefabBasicWorkflow_CreateAndReparentPrefab(self, request, workspace, editor, launcher_platform): + from .tests import PrefabBasicWorkflow_CreateAndReparentPrefab as test_module self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False) diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Test.prefab b/AutomatedTesting/Gem/PythonTests/prefab/data/Test.prefab similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/Test.prefab rename to AutomatedTesting/Gem/PythonTests/prefab/data/Test.prefab diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndDeletePrefab.py b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py similarity index 70% rename from AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndDeletePrefab.py rename to AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py index f3fbcfa6ad..9ae4614d80 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndDeletePrefab.py +++ b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py @@ -5,14 +5,14 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -def Prefab_BasicWorkflow_CreateAndDeletePrefab(): +def PrefabBasicWorkflow_CreateAndDeletePrefab(): CAR_PREFAB_FILE_NAME = 'car_prefab' from editor_python_test_tools.editor_entity_utils import EditorEntity - from prefab.Prefab import Prefab + from editor_python_test_tools.prefab_utils import Prefab - import prefab.Prefab_Test_Utils as prefab_test_utils + import PrefabTestUtils as prefab_test_utils prefab_test_utils.open_base_tests_level() @@ -21,13 +21,13 @@ def Prefab_BasicWorkflow_CreateAndDeletePrefab(): car_entity = EditorEntity.create_editor_entity() car_prefab_entities = [car_entity] - # Checks for prefab creation passed or not + # Asserts if prefab creation doesn't succeeds _, car = Prefab.create_prefab( car_prefab_entities, CAR_PREFAB_FILE_NAME) - # Checks for prefab deletion passed or not + # Asserts if prefab deletion fails Prefab.remove_prefabs([car]) if __name__ == "__main__": from editor_python_test_tools.utils import Report - Report.start_test(Prefab_BasicWorkflow_CreateAndDeletePrefab) + Report.start_test(PrefabBasicWorkflow_CreateAndDeletePrefab) diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndReparentPrefab.py b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py similarity index 85% rename from AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndReparentPrefab.py rename to AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py index e5a9d9930a..f78bbf483d 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndReparentPrefab.py +++ b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py @@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -def Prefab_BasicWorkflow_CreateAndReparentPrefab(): +def PrefabBasicWorkflow_CreateAndReparentPrefab(): CAR_PREFAB_FILE_NAME = 'car_prefab' WHEEL_PREFAB_FILE_NAME = 'wheel_prefab' @@ -16,9 +16,9 @@ def Prefab_BasicWorkflow_CreateAndReparentPrefab(): async def run_test(): from editor_python_test_tools.editor_entity_utils import EditorEntity - from prefab.Prefab import Prefab + from editor_python_test_tools.prefab_utils import Prefab - import prefab.Prefab_Test_Utils as prefab_test_utils + import PrefabTestUtils as prefab_test_utils prefab_test_utils.open_base_tests_level() @@ -46,4 +46,4 @@ def Prefab_BasicWorkflow_CreateAndReparentPrefab(): if __name__ == "__main__": from editor_python_test_tools.utils import Report - Report.start_test(Prefab_BasicWorkflow_CreateAndReparentPrefab) + Report.start_test(PrefabBasicWorkflow_CreateAndReparentPrefab) diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreatePrefab.py b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreatePrefab.py similarity index 79% rename from AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreatePrefab.py rename to AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreatePrefab.py index d86b03d5ad..568a2c15b4 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreatePrefab.py +++ b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreatePrefab.py @@ -5,15 +5,15 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -def Prefab_BasicWorkflow_CreatePrefab(): +def PrefabBasicWorkflow_CreatePrefab(): CAR_PREFAB_FILE_NAME = 'car_prefab' from editor_python_test_tools.editor_entity_utils import EditorEntity from editor_python_test_tools.utils import Report - from prefab.Prefab import Prefab + from editor_python_test_tools.prefab_utils import Prefab - import prefab.Prefab_Test_Utils as prefab_test_utils + import PrefabTestUtils as prefab_test_utils prefab_test_utils.open_base_tests_level() @@ -27,4 +27,4 @@ def Prefab_BasicWorkflow_CreatePrefab(): if __name__ == "__main__": from editor_python_test_tools.utils import Report - Report.start_test(Prefab_BasicWorkflow_CreatePrefab) + Report.start_test(PrefabBasicWorkflow_CreatePrefab) diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_InstantiatePrefab.py b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py similarity index 74% rename from AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_InstantiatePrefab.py rename to AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py index 46be669697..1b962d2ca7 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_InstantiatePrefab.py +++ b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py @@ -5,17 +5,17 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -def Prefab_BasicWorkflow_InstantiatePrefab(): +def PrefabBasicWorkflow_InstantiatePrefab(): from azlmbr.math import Vector3 - EXISTING_TEST_PREFAB_FILE_NAME = "Test" + EXISTING_TEST_PREFAB_FILE_NAME = "Gem/PythonTests/Prefab/data/Test.prefab" INSTANTIATED_TEST_PREFAB_POSITION = Vector3(10.00, 20.0, 30.0) EXPECTED_TEST_PREFAB_CHILDREN_COUNT = 1 - from prefab.Prefab import Prefab + from editor_python_test_tools.prefab_utils import Prefab - import prefab.Prefab_Test_Utils as prefab_test_utils + import PrefabTestUtils as prefab_test_utils prefab_test_utils.open_base_tests_level() @@ -31,4 +31,4 @@ def Prefab_BasicWorkflow_InstantiatePrefab(): if __name__ == "__main__": from editor_python_test_tools.utils import Report - Report.start_test(Prefab_BasicWorkflow_InstantiatePrefab) + Report.start_test(PrefabBasicWorkflow_InstantiatePrefab) diff --git a/AutomatedTesting/Gem/PythonTests/prefab/PrefabLevel_OpensLevelWithEntities.py b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabLevel_OpensLevelWithEntities.py similarity index 87% rename from AutomatedTesting/Gem/PythonTests/prefab/PrefabLevel_OpensLevelWithEntities.py rename to AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabLevel_OpensLevelWithEntities.py index bc95596845..0eb7e86a9e 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/PrefabLevel_OpensLevelWithEntities.py +++ b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabLevel_OpensLevelWithEntities.py @@ -7,9 +7,9 @@ SPDX-License-Identifier: Apache-2.0 OR MIT # fmt:off class Tests(): - find_empty_entity = ("Entity: 'EmptyEntity' found", "Entity: 'EmptyEntity' *not* found in level") - empty_entity_pos = ("'EmptyEntity' position is at the expected position", "'EmptyEntity' position is *not* at the expected position") - find_pxentity = ("Entity: 'EntityWithPxCollider' found", "Entity: 'EntityWithPxCollider' *not* found in level") + find_empty_entity = ("Entity: 'EmptyEntity' found", "Entity: 'EmptyEntity' *not* found in level") + empty_entity_pos = ("'EmptyEntity' position is at the expected position", "'EmptyEntity' position is *not* at the expected position") + find_pxentity = ("Entity: 'EntityWithPxCollider' found", "Entity: 'EntityWithPxCollider' *not* found in level") pxentity_component = ("Entity: 'EntityWithPxCollider' has a Physx Collider", "Entity: 'EntityWithPxCollider' does *not* have a Physx Collider") # fmt:on diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_Test_Utils.py b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabTestUtils.py similarity index 81% rename from AutomatedTesting/Gem/PythonTests/prefab/Prefab_Test_Utils.py rename to AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabTestUtils.py index 3e19911449..f82af23023 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_Test_Utils.py +++ b/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabTestUtils.py @@ -18,20 +18,6 @@ import azlmbr.components as components import azlmbr.entity as entity import azlmbr.legacy.general as general -def get_prefab_file_name(prefab_name): - return prefab_name + ".prefab" - -def get_prefab_file_path(prefab_name): - return os.path.join(os.path.dirname(os.path.abspath(__file__)), get_prefab_file_name(prefab_name)) - -def find_entities_by_name(entity_name): - searchFilter = entity.SearchFilter() - searchFilter.names = [entity_name] - return entity.SearchBus(bus.Broadcast, 'SearchEntities', searchFilter) - -def get_all_entities(): - return entity.SearchBus(bus.Broadcast, 'SearchEntities', entity.SearchFilter()) - def check_entity_at_position(entity_id, expected_entity_position): entity_at_expected_position_result = ( "entity is at expected position", @@ -74,9 +60,6 @@ def get_children_ids_by_name(entity_id, entity_name): return result -def wait_for_propagation(): - general.idle_wait_frames(1) - def open_base_tests_level(): helper.init_idle() helper.open_level("Prefab", "Base") diff --git a/Code/Editor/CryEditPy.cpp b/Code/Editor/CryEditPy.cpp index 427737335c..a25a497a4e 100644 --- a/Code/Editor/CryEditPy.cpp +++ b/Code/Editor/CryEditPy.cpp @@ -398,6 +398,11 @@ inline namespace Commands { return AZ::Debug::Trace::WaitForDebugger(timeoutSeconds); } + + AZStd::string PyGetFileAlias(AZStd::string alias) + { + return AZ::IO::FileIOBase::GetInstance()->GetAlias(alias.c_str()); + } } namespace AzToolsFramework @@ -448,6 +453,8 @@ namespace AzToolsFramework addLegacyGeneral(behaviorContext->Method("attach_debugger", PyAttachDebugger, nullptr, "Prompts for attaching the debugger")); addLegacyGeneral(behaviorContext->Method("wait_for_debugger", PyWaitForDebugger, behaviorContext->MakeDefaultValues(-1.f), "Pauses this thread execution until the debugger has been attached")); + addLegacyGeneral(behaviorContext->Method("get_file_alias", PyGetFileAlias, nullptr, "Retrieves path for IO alias")); + // this will put these methods into the 'azlmbr.legacy.checkout_dialog' module auto addCheckoutDialog = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder) { From c6afb1f0a383b8a93ddf1e936f9be166477aba5c Mon Sep 17 00:00:00 2001 From: "rgba16f [Amazon]" <82187279+rgba16f@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:35:31 -0500 Subject: [PATCH 051/152] Update all the hardcoded filenames inside the level.pak file to be lowercase now the editor no longer forces them to be lower case. (#4802) Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> --- Code/Editor/CryEdit.cpp | 2 +- Code/Editor/GameExporter.cpp | 28 +++++++++---------- .../CrySystem/LevelSystem/LevelSystem.cpp | 10 +++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 8f42b1f4a1..ab80e7d23a 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -2632,7 +2632,7 @@ void CCryEditApp::OnShowHelpers() void CCryEditApp::OnEditLevelData() { auto dir = QFileInfo(GetIEditor()->GetDocument()->GetLevelPathName()).dir(); - CFileUtil::EditTextFile(dir.absoluteFilePath("LevelData.xml").toUtf8().data()); + CFileUtil::EditTextFile(dir.absoluteFilePath("leveldata.xml").toUtf8().data()); } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/GameExporter.cpp b/Code/Editor/GameExporter.cpp index 0324629877..ae1095b03a 100644 --- a/Code/Editor/GameExporter.cpp +++ b/Code/Editor/GameExporter.cpp @@ -31,11 +31,11 @@ #include ////////////////////////////////////////////////////////////////////////// -#define MUSIC_LEVEL_LIBRARY_FILE "Music.xml" -#define MATERIAL_LEVEL_LIBRARY_FILE "Materials.xml" -#define RESOURCE_LIST_FILE "ResourceList.txt" -#define USED_RESOURCE_LIST_FILE "UsedResourceList.txt" -#define SHADER_LIST_FILE "ShadersList.txt" +#define MUSIC_LEVEL_LIBRARY_FILE "music.xml" +#define MATERIAL_LEVEL_LIBRARY_FILE "materials.xml" +#define RESOURCE_LIST_FILE "resourcelist.txt" +#define USED_RESOURCE_LIST_FILE "usedresourcelist.txt" +#define SHADER_LIST_FILE "shaderslist.txt" #define GetAValue(rgb) ((BYTE)((rgb) >> 24)) @@ -185,9 +185,9 @@ bool CGameExporter::Export(unsigned int flags, [[maybe_unused]] EEndian eExportE ExportOcclusionMesh(sLevelPath.toUtf8().data()); //! Export Level data. - CLogFile::WriteLine("Exporting LevelData.xml"); + CLogFile::WriteLine("Exporting leveldata.xml"); ExportLevelData(sLevelPath); - CLogFile::WriteLine("Exporting LevelData.xml done."); + CLogFile::WriteLine("Exporting leveldata.xml done."); ExportLevelInfo(sLevelPath); @@ -266,26 +266,26 @@ void CGameExporter::ExportOcclusionMesh(const char* pszGamePath) void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/) { IEditor* pEditor = GetIEditor(); - pEditor->SetStatusText(QObject::tr("Exporting LevelData.xml...")); + pEditor->SetStatusText(QObject::tr("Exporting leveldata.xml...")); char versionString[256]; pEditor->GetFileVersion().ToString(versionString); - XmlNodeRef root = XmlHelpers::CreateXmlNode("LevelData"); + XmlNodeRef root = XmlHelpers::CreateXmlNode("leveldata"); root->setAttr("SandboxVersion", versionString); - XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("LevelDataAction"); + XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("leveldataaction"); rootAction->setAttr("SandboxVersion", versionString); ////////////////////////////////////////////////////////////////////////// // Save Level Data XML ////////////////////////////////////////////////////////////////////////// - QString levelDataFile = path + "LevelData.xml"; + QString levelDataFile = path + "leveldata.xml"; XmlString xmlData = root->getXML(); CCryMemFile file; file.Write(xmlData.c_str(), static_cast(xmlData.length())); m_levelPak.m_pakFile.UpdateFile(levelDataFile.toUtf8().data(), file); - QString levelDataActionFile = path + "LevelDataAction.xml"; + QString levelDataActionFile = path + "leveldataaction.xml"; XmlString xmlDataAction = rootAction->getXML(); CCryMemFile fileAction; fileAction.Write(xmlDataAction.c_str(), static_cast(xmlDataAction.length())); @@ -298,7 +298,7 @@ void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/ if (savedEntities) { QString entitiesFile; - entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "Mission0"); + entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "mission0"); m_levelPak.m_pakFile.UpdateFile(entitiesFile.toUtf8().data(), entitySaveBuffer.begin(), static_cast(entitySaveBuffer.size())); } } @@ -326,7 +326,7 @@ void CGameExporter::ExportLevelInfo(const QString& path) ////////////////////////////////////////////////////////////////////////// // Save LevelInfo file. ////////////////////////////////////////////////////////////////////////// - QString filename = path + "LevelInfo.xml"; + QString filename = path + "levelinfo.xml"; XmlString xmlData = root->getXML(); CCryMemFile file; diff --git a/Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp b/Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp index 49af5080ec..3a2bba64d3 100644 --- a/Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp +++ b/Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp @@ -86,7 +86,7 @@ bool CLevelInfo::ReadInfo() usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled); // Set up a default game type for legacy code. - m_defaultGameTypeName = "Mission0"; + m_defaultGameTypeName = "mission0"; if (usePrefabSystemForLevels) { @@ -96,17 +96,17 @@ bool CLevelInfo::ReadInfo() AZStd::string levelPath(m_levelPath); AZStd::string xmlFile(levelPath); - xmlFile += "/LevelInfo.xml"; + xmlFile += "/levelinfo.xml"; XmlNodeRef rootNode = GetISystem()->LoadXmlFromFile(xmlFile.c_str()); if (rootNode) { AZStd::string dataFile(levelPath); - dataFile += "/LevelDataAction.xml"; + dataFile += "/leveldataaction.xml"; XmlNodeRef dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str()); if (!dataNode) { - dataFile = levelPath + "/LevelData.xml"; + dataFile = levelPath + "/leveldata.xml"; dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str()); } @@ -614,7 +614,7 @@ ILevel* CLevelSystem::LoadLevelInternal(const char* _levelName) } { - AZStd::string missionXml("Mission_"); + AZStd::string missionXml("mission_"); missionXml += pLevelInfo->m_defaultGameTypeName; missionXml += ".xml"; AZStd::string xmlFile(pLevelInfo->GetPath()); From 023e8fcff2d4121e27cc23e819e55a4e55ce7d6a Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Wed, 20 Oct 2021 10:21:15 -0700 Subject: [PATCH 052/152] Add missing files for review Signed-off-by: AMZN-Phil --- .../Source/O3DEObjectDownloadController.cpp | 92 +++++++++++++++++++ .../Source/O3DEObjectDownloadController.h | 65 +++++++++++++ .../Source/O3DEObjectDownloadWorker.cpp | 52 +++++++++++ .../Source/O3DEObjectDownloadWorker.h | 51 ++++++++++ 4 files changed, 260 insertions(+) create mode 100644 Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.cpp create mode 100644 Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.h create mode 100644 Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.cpp create mode 100644 Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.h diff --git a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.cpp b/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.cpp new file mode 100644 index 0000000000..f3be0fd8e4 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include +#include +#include + + +namespace O3DE::ProjectManager +{ + O3DEObjectDownloadController::O3DEObjectDownloadController(QWidget* parent) + : QObject() + , m_lastProgress(0) + , m_parent(parent) + { + m_worker = new O3DEObjectDownloadWorker(); + m_worker->moveToThread(&m_workerThread); + + connect(&m_workerThread, &QThread::started, m_worker, &O3DEObjectDownloadWorker::StartDownload); + connect(m_worker, &O3DEObjectDownloadWorker::Done, this, &O3DEObjectDownloadController::HandleResults); + connect(m_worker, &O3DEObjectDownloadWorker::UpdateProgress, this, &O3DEObjectDownloadController::UpdateUIProgress); + connect(this, &O3DEObjectDownloadController::StartGemDownload, m_worker, &O3DEObjectDownloadWorker::StartDownload); + } + + O3DEObjectDownloadController::~O3DEObjectDownloadController() + { + connect(&m_workerThread, &QThread::finished, m_worker, &O3DEObjectDownloadWorker::deleteLater); + m_workerThread.requestInterruption(); + m_workerThread.quit(); + m_workerThread.wait(); + } + + void O3DEObjectDownloadController::AddGemDownload(const QString& gemName) + { + m_gemNames.push_back(gemName); + if (m_gemNames.size() == 1) + { + m_worker->SetGemToDownload(m_gemNames[0], false); + m_workerThread.start(); + } + } + + void O3DEObjectDownloadController::Start() + { + + } + + void O3DEObjectDownloadController::UpdateUIProgress(int progress) + { + m_lastProgress = progress; + emit GemDownloadProgress(progress); + } + + void O3DEObjectDownloadController::HandleResults(const QString& result) + { + bool succeeded = true; + + if (!result.isEmpty()) + { + QMessageBox::critical(nullptr, tr("Gem download"), result); + succeeded = false; + } + + m_gemNames.erase(m_gemNames.begin()); + emit Done(succeeded); + + if (!m_gemNames.empty()) + { + emit StartGemDownload(m_gemNames[0]); + } + else + { + m_workerThread.quit(); + m_workerThread.wait(); + } + } + + void O3DEObjectDownloadController::HandleCancel() + { + m_workerThread.quit(); + emit Done(false); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.h b/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.h new file mode 100644 index 0000000000..7cd9220f9f --- /dev/null +++ b/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QProcess) + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(O3DEObjectDownloadWorker) + + class O3DEObjectDownloadController : public QObject + { + Q_OBJECT + + public: + explicit O3DEObjectDownloadController(QWidget* parent = nullptr); + ~O3DEObjectDownloadController(); + + void AddGemDownload(const QString& m_gemName); + + bool IsDownloadQueueEmpty() + { + return m_gemNames.empty(); + } + + const AZStd::vector& GetDownloadQueue() const + { + return m_gemNames; + } + + const QString& GetCurrentDownloadingGem() const + { + return m_gemNames[0]; + } + public slots: + void Start(); + void UpdateUIProgress(int progress); + void HandleResults(const QString& result); + void HandleCancel(); + + signals: + void StartGemDownload(const QString& gemName); + void Done(bool success = true); + void GemDownloadProgress(int percentage); + + private: + O3DEObjectDownloadWorker* m_worker; + QThread m_workerThread; + QWidget* m_parent; + AZStd::vector m_gemNames; + + int m_lastProgress; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.cpp b/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.cpp new file mode 100644 index 0000000000..3462600c40 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include +#include +#include + + +namespace O3DE::ProjectManager +{ + O3DEObjectDownloadWorker::O3DEObjectDownloadWorker() + : QObject() + { + } + + void O3DEObjectDownloadWorker::StartDownload() + { + auto gemDownloadProgress = [=](int downloadProgress) + { + m_downloadProgress = downloadProgress; + emit UpdateProgress(downloadProgress); + }; + AZ::Outcome gemInfoResult = PythonBindingsInterface::Get()->DownloadGem(m_gemName, gemDownloadProgress); + if (gemInfoResult.IsSuccess()) + { + emit Done(""); + } + else + { + emit Done(tr("Gem download failed")); + } + } + + void O3DEObjectDownloadWorker::SetGemToDownload(const QString& gemName, bool downloadNow) + { + m_gemName = gemName; + if (downloadNow) + { + StartDownload(); + } + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.h b/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.h new file mode 100644 index 0000000000..4fff71634f --- /dev/null +++ b/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include + +#include +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QProcess) + +namespace O3DE::ProjectManager +{ + class O3DEObjectDownloadWorker : public QObject + { + // QProcess::waitForFinished uses -1 to indicate that the process should not timeout + static constexpr int MaxBuildTimeMSecs = -1; + // Download was cancelled + inline static const QString DownloadCancelled = QObject::tr("Download Cancelled."); + + Q_OBJECT + + public: + explicit O3DEObjectDownloadWorker(); + ~O3DEObjectDownloadWorker() = default; + + public slots: + void StartDownload(); + void SetGemToDownload(const QString& gemName, bool downloadNow = true); + + signals: + void UpdateProgress(int progress); + void Done(QString result = ""); + + private: + + QProcess* m_configProjectProcess = nullptr; + QProcess* m_buildProjectProcess = nullptr; + + QString m_gemName; + int m_downloadProgress; + }; +} // namespace O3DE::ProjectManager From 900aa4e5bc6b28f6ad00df3f90448099a4907234 Mon Sep 17 00:00:00 2001 From: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed, 20 Oct 2021 12:22:11 -0500 Subject: [PATCH 053/152] {lyn7065} adding ProcPrefab Prefab::Tempate flag method (#4765) * {lyn7065} adding ProcPrefab Prefab::Tempate flag method Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> * updated based on comments Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> * moved validation logic to IsValid() Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> * added more guards around the source string Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> --- .../Prefab/Template/Template.cpp | 31 ++++++++++++++++++- .../Prefab/Template/Template.h | 6 ++++ .../Prefab/ProceduralPrefabAssetTests.cpp | 29 +++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Template/Template.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Template/Template.cpp index e5ab403ea6..8da926cd08 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Template/Template.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Template/Template.cpp @@ -66,7 +66,16 @@ namespace AzToolsFramework bool Template::IsValid() const { - return !m_prefabDom.IsNull() && !m_filePath.empty(); + if (m_prefabDom.IsNull() || m_filePath.empty()) + { + return false; + } + else if (!m_prefabDom.IsObject()) + { + return false; + } + auto source = m_prefabDom.FindMember(PrefabDomUtils::SourceName); + return (source != m_prefabDom.MemberEnd()); } bool Template::IsLoadedWithErrors() const @@ -175,6 +184,26 @@ namespace AzToolsFramework return findInstancesResult->get(); } + bool Template::IsProcedural() const + { + if (m_isProcedural.has_value()) + { + return m_isProcedural.value(); + } + else if (!IsValid()) + { + return false; + } + auto source = m_prefabDom.FindMember(PrefabDomUtils::SourceName); + if (!source->value.IsString()) + { + return false; + } + AZ::IO::PathView path(source->value.GetString()); + m_isProcedural = AZStd::make_optional(path.Extension().Match(".procprefab")); + return m_isProcedural.value(); + } + const AZ::IO::Path& Template::GetFilePath() const { return m_filePath; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Template/Template.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Template/Template.h index f7a41431f8..9453edfabc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Template/Template.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Template/Template.h @@ -65,6 +65,9 @@ namespace AzToolsFramework const AZ::IO::Path& GetFilePath() const; void SetFilePath(const AZ::IO::PathView& path); + // To tell if this Template was created from an product asset + bool IsProcedural() const; + private: // Container for keeping links representing the Template's nested instances. Links m_links; @@ -80,6 +83,9 @@ namespace AzToolsFramework // Flag to tell if this Template has changes that have yet to be saved to file. bool m_isDirty = false; + + // Flag to tell if this Template was generated outside the Editor + mutable AZStd::optional m_isProcedural; }; } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp index 0b9e73bbac..fb53fa962a 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace UnitTest { @@ -132,4 +133,32 @@ namespace UnitTest EXPECT_TRUE(outputValue.HasMember("member")); EXPECT_STREQ(outputValue.FindMember("member")->value.GetString(), "value"); } + + TEST_F(ProceduralPrefabAssetTest, Template_IsProcPrefab_DefaultsToNotProcPrefab) + { + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + dom.AddMember("Source", "foo.prefab", dom.GetAllocator()); + AzToolsFramework::Prefab::Template fooTemplate("foo", AZStd::move(dom)); + EXPECT_FALSE(fooTemplate.IsProcedural()); + } + + TEST_F(ProceduralPrefabAssetTest, Template_IsProcPrefab_DomDrivesFlagToTrue) + { + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + dom.AddMember("Source", "foo.procprefab", dom.GetAllocator()); + AzToolsFramework::Prefab::Template fooTemplate("foo", AZStd::move(dom)); + EXPECT_TRUE(fooTemplate.IsProcedural()); + // the second time should use the cached version of the flag + EXPECT_TRUE(fooTemplate.IsProcedural()); + } + + TEST_F(ProceduralPrefabAssetTest, Template_IsProcPrefab_FailsWithNoSource) + { + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + AzToolsFramework::Prefab::Template fooTemplate("foo", AZStd::move(dom)); + EXPECT_FALSE(fooTemplate.IsProcedural()); + } } From 7ccde16b0d3e4c45d3e91861b0c916dad7452051 Mon Sep 17 00:00:00 2001 From: AMZN-AlexOteiza <82234181+AMZN-AlexOteiza@users.noreply.github.com> Date: Wed, 20 Oct 2021 18:50:47 +0100 Subject: [PATCH 054/152] Rename prefab -> Prefab (#4825) * Rename part 1 * Rename part 2 * Rename part 3 --- AutomatedTesting/Gem/PythonTests/CMakeLists.txt | 2 +- .../Gem/PythonTests/{prefab => Prefab}/CMakeLists.txt | 0 .../Gem/PythonTests/{prefab => Prefab}/TestSuite_Main.py | 0 AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/__init__.py | 0 .../Gem/PythonTests/{prefab => Prefab}/data/Test.prefab | 0 .../tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py | 0 .../tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py | 0 .../tests/PrefabBasicWorkflow_CreatePrefab.py | 0 .../tests/PrefabBasicWorkflow_InstantiatePrefab.py | 0 .../tests/PrefabLevel_OpensLevelWithEntities.py | 0 .../Gem/PythonTests/{prefab => Prefab}/tests/PrefabTestUtils.py | 0 11 files changed, 1 insertion(+), 1 deletion(-) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/CMakeLists.txt (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/TestSuite_Main.py (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/__init__.py (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/data/Test.prefab (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/tests/PrefabBasicWorkflow_CreatePrefab.py (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/tests/PrefabBasicWorkflow_InstantiatePrefab.py (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/tests/PrefabLevel_OpensLevelWithEntities.py (100%) rename AutomatedTesting/Gem/PythonTests/{prefab => Prefab}/tests/PrefabTestUtils.py (100%) diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index 466a4b1679..2ca7f7ea2b 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -33,7 +33,7 @@ add_subdirectory(WhiteBox) add_subdirectory(NvCloth) ## Prefab ## -add_subdirectory(prefab) +add_subdirectory(Prefab) ## Editor Python Bindings ## add_subdirectory(EditorPythonBindings) diff --git a/AutomatedTesting/Gem/PythonTests/prefab/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Prefab/CMakeLists.txt similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/CMakeLists.txt rename to AutomatedTesting/Gem/PythonTests/Prefab/CMakeLists.txt diff --git a/AutomatedTesting/Gem/PythonTests/prefab/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/TestSuite_Main.py rename to AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py diff --git a/AutomatedTesting/Gem/PythonTests/prefab/__init__.py b/AutomatedTesting/Gem/PythonTests/Prefab/__init__.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/__init__.py rename to AutomatedTesting/Gem/PythonTests/Prefab/__init__.py diff --git a/AutomatedTesting/Gem/PythonTests/prefab/data/Test.prefab b/AutomatedTesting/Gem/PythonTests/Prefab/data/Test.prefab similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/data/Test.prefab rename to AutomatedTesting/Gem/PythonTests/Prefab/data/Test.prefab diff --git a/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py rename to AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py diff --git a/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py rename to AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py diff --git a/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreatePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreatePrefab.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_CreatePrefab.py rename to AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreatePrefab.py diff --git a/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py rename to AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py diff --git a/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabLevel_OpensLevelWithEntities.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabLevel_OpensLevelWithEntities.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabLevel_OpensLevelWithEntities.py rename to AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabLevel_OpensLevelWithEntities.py diff --git a/AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabTestUtils.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabTestUtils.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/prefab/tests/PrefabTestUtils.py rename to AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabTestUtils.py From 67532ed18fa984a37235d19ab2e6f687a56985db Mon Sep 17 00:00:00 2001 From: LesaelR <89800757+LesaelR@users.noreply.github.com> Date: Wed, 20 Oct 2021 11:12:38 -0700 Subject: [PATCH 055/152] Updating FBX_Tests and test asset files (#4477) * Updating FBX_Tests and test asset files Signed-off-by: LesaelR * Updating fbx_tests to removed marker from helper function. Signed-off-by: LesaelR * Update to add Motion Test to FBX Tests. Signed-off-by: LesaelR * Update fbx_tests.py Removing unnecessary sandbox mark. Signed-off-by: LesaelR --- .../assets/Motion/Jack_Idle_Aim_ZUp.dbgsg | 1515 +++++++++++++++++ .../assets/Motion/Jack_Idle_Aim_ZUp.fbx | 3 + .../single_mesh_multiple_materials.dbgsg | 198 ++- .../onemeshonematerial.dbgsg | 131 +- .../assets/SoftNamingLOD/lodtest.dbgsg | 650 ++++--- .../SoftNamingPhysics/physicstest.dbgsg | 218 ++- .../multiple_mesh_linked_materials.dbgsg | 366 ++-- .../multiple_mesh_one_material.dbgsg | 242 ++- .../multiple_mesh_multiple_material.dbgsg | 256 ++- ...iple_mesh_multiple_material_override.dbgsg | 234 ++- .../assets/VertexColor/vertexcolor.dbgsg | 146 +- .../assetpipeline/fbx_tests/fbx_tests.py | 52 +- 12 files changed, 3338 insertions(+), 673 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.dbgsg create mode 100644 AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.fbx diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.dbgsg new file mode 100644 index 0000000000..8b0132a448 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.dbgsg @@ -0,0 +1,1515 @@ +ProductName: Jack_Idle_Aim_ZUp.dbgsg +debugSceneGraphVersion: 1 +Jack_Idle_Aim_ZUp +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: jack_root +Node Path: RootNode.jack_root +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: animation +Node Path: RootNode.jack_root.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 8622607111849624366 + TimeStepBetweenFrames: 0.033333 + +Node Name: Bip01__pelvis +Node Path: RootNode.jack_root.Bip01__pelvis +Node Type: BoneData + WorldTransform: + BasisX: < 0.987221, 0.158526, -0.016273> + BasisY: <-0.158404, 0.987337, 0.008581> + BasisZ: < 0.017427, -0.005894, 0.999831> + Transl: < 0.002174, 0.008085, 1.014749> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 14058466958533286666 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.transform +Node Type: TransformData + Matrix: + BasisX: < 0.987221, 0.158526, -0.016273> + BasisY: <-0.158404, 0.987337, 0.008581> + BasisZ: < 0.017427, -0.005894, 0.999831> + Transl: < 0.002174, 0.008085, 1.014749> + +Node Name: l_upLeg +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg +Node Type: BoneData + WorldTransform: + BasisX: < 0.922503, 0.382949, -0.048354> + BasisY: <-0.384989, 0.921878, -0.043862> + BasisZ: < 0.027780, 0.059079, 0.997867> + Transl: <-0.099524, 0.003596, 0.983744> + +Node Name: r_upLeg +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg +Node Type: BoneData + WorldTransform: + BasisX: < 0.991893, -0.112768, 0.058580> + BasisY: < 0.104333, 0.985852, 0.131189> + BasisZ: <-0.072545, -0.124013, 0.989625> + Transl: < 0.099120, 0.035493, 0.980470> + +Node Name: spine1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1 +Node Type: BoneData + WorldTransform: + BasisX: <-0.988827, -0.149057, -0.001499> + BasisY: < 0.003444, -0.032898, 0.999453> + BasisZ: <-0.149025, 0.988281, 0.033044> + Transl: <-0.003015, 0.024799, 1.081740> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 15600598202424573783 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.transform +Node Type: TransformData + Matrix: + BasisX: < 0.972208, 0.231557, -0.034526> + BasisY: <-0.233213, 0.970812, -0.055997> + BasisZ: < 0.020552, 0.062493, 0.997834> + Transl: <-0.100606, 0.011412, -0.032745> + +Node Name: l_upLegRoll +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_upLegRoll +Node Type: BoneData + WorldTransform: + BasisX: < 0.922503, 0.382949, -0.048354> + BasisY: <-0.384989, 0.921878, -0.043862> + BasisZ: < 0.027780, 0.059079, 0.997867> + Transl: <-0.105719, -0.009578, 0.761220> + +Node Name: l_loLeg +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg +Node Type: BoneData + WorldTransform: + BasisX: < 0.922503, 0.382949, -0.048355> + BasisY: <-0.385744, 0.910182, -0.150897> + BasisZ: <-0.013775, 0.157856, 0.987366> + Transl: <-0.111914, -0.022752, 0.538696> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 3881375132663304109 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.transform +Node Type: TransformData + Matrix: + BasisX: < 0.960387, -0.267956, 0.076521> + BasisY: < 0.257148, 0.957967, 0.127175> + BasisZ: <-0.107382, -0.102460, 0.988924> + Transl: < 0.100610, 0.011411, -0.032745> + +Node Name: r_upLegRoll +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_upLegRoll +Node Type: BoneData + WorldTransform: + BasisX: < 0.991893, -0.112768, 0.058580> + BasisY: < 0.104333, 0.985852, 0.131189> + BasisZ: <-0.072545, -0.124013, 0.989625> + Transl: < 0.115298, 0.063148, 0.759784> + +Node Name: r_loLeg +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg +Node Type: BoneData + WorldTransform: + BasisX: < 0.991893, -0.112768, 0.058580> + BasisY: < 0.118181, 0.988037, -0.099079> + BasisZ: <-0.046706, 0.105199, 0.993354> + Transl: < 0.131475, 0.090804, 0.539097> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 14286132743595604583 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.transform +Node Type: TransformData + Matrix: + BasisX: <-0.999796, 0.009451, -0.017853> + BasisY: <-0.018079, -0.024451, 0.999538> + BasisZ: < 0.009010, 0.999656, 0.024617> + Transl: <-0.003564, 0.017900, 0.066792> + +Node Name: spine2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2 +Node Type: BoneData + WorldTransform: + BasisX: <-0.984677, -0.174366, -0.002764> + BasisY: <-0.004079, 0.007184, 0.999966> + BasisZ: <-0.174340, 0.984655, -0.007786> + Transl: <-0.002499, 0.019865, 1.231658> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_upLegRoll.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 11375302191090723133 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_upLegRoll.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, -0.223000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 10115359583353800560 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, -0.000000> + BasisY: <-0.000000, 0.994203, -0.107519> + BasisZ: < 0.000000, 0.107519, 0.994203> + Transl: < 0.000000, 0.000001, -0.446000> + +Node Name: l_ankle +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg.l_ankle +Node Type: BoneData + WorldTransform: + BasisX: < 0.963606, 0.267327, 0.000000> + BasisY: <-0.267327, 0.963606, 0.000000> + BasisZ: <-0.000000, -0.000000, 1.000000> + Transl: <-0.105700, -0.093967, 0.093254> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_upLegRoll.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 3742546113399553362 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_upLegRoll.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, -0.223000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 1660638182832107032 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.973390, -0.229154> + BasisZ: <-0.000000, 0.229154, 0.973390> + Transl: < 0.000000, 0.000001, -0.446000> + +Node Name: r_ankle +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg.r_ankle +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, -0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: <-0.000000, -0.000000, 1.000000> + Transl: < 0.152547, 0.043345, 0.090955> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 10080837380932203667 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.transform +Node Type: TransformData + Matrix: + BasisX: < 0.999670, -0.000417, -0.025672> + BasisY: < 0.001464, 0.999168, 0.040751> + BasisZ: < 0.025634, -0.040775, 0.998839> + Transl: <-0.000000, 0.150000, -0.000000> + +Node Name: spine3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3 +Node Type: BoneData + WorldTransform: + BasisX: <-0.979537, -0.201214, -0.004467> + BasisY: < 0.008889, -0.065425, 0.997818> + BasisZ: <-0.201067, 0.977360, 0.065875> + Transl: <-0.003111, 0.020942, 1.381653> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg.l_ankle.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 4299361259356538796 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg.l_ankle.transform +Node Type: TransformData + Matrix: + BasisX: < 0.991302, -0.128389, 0.028926> + BasisY: < 0.122402, 0.980177, 0.155793> + BasisZ: <-0.048355, -0.150898, 0.987366> + Transl: <-0.000000, 0.000001, -0.451141> + +Node Name: l_ball +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg.l_ankle.l_ball +Node Type: BoneData + WorldTransform: + BasisX: < 0.963606, 0.267327, 0.000000> + BasisY: <-0.267327, 0.963606, 0.000000> + BasisZ: <-0.000000, -0.000000, 1.000000> + Transl: <-0.145845, 0.050741, 0.026066> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg.r_ankle.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 11146861401725178254 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg.r_ankle.transform +Node Type: TransformData + Matrix: + BasisX: < 0.991893, 0.118181, -0.046706> + BasisY: <-0.112767, 0.988037, 0.105199> + BasisZ: < 0.058580, -0.099080, 0.993354> + Transl: <-0.000000, 0.000001, -0.451141> + +Node Name: r_ball +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg.r_ankle.r_ball +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, -0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: <-0.000000, -0.000000, 1.000000> + Transl: < 0.152547, 0.193519, 0.023766> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 12335240115610862178 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.transform +Node Type: TransformData + Matrix: + BasisX: < 0.999625, -0.001917, -0.027319> + BasisY: <-0.000102, 0.997277, -0.073739> + BasisZ: < 0.027386, 0.073714, 0.996903> + Transl: <-0.000000, 0.150000, -0.000000> + +Node Name: neck +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.neck +Node Type: BoneData + WorldTransform: + BasisX: <-0.994865, -0.098844, 0.021771> + BasisY: < 0.011332, 0.104974, 0.994410> + BasisZ: <-0.100577, 0.989550, -0.103314> + Transl: <-0.001422, 0.008511, 1.571239> + +Node Name: l_shldr +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr +Node Type: BoneData + WorldTransform: + BasisX: <-0.253998, 0.966419, -0.038967> + BasisY: <-0.966745, -0.254913, -0.020579> + BasisZ: <-0.029821, 0.032444, 0.999029> + Transl: <-0.080266, -0.013371, 1.496066> + +Node Name: r_shldr +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr +Node Type: BoneData + WorldTransform: + BasisX: < 0.391300, -0.910895, 0.130973> + BasisY: <-0.901965, -0.407856, -0.141823> + BasisZ: < 0.182604, -0.062637, -0.981189> + Transl: < 0.080319, 0.019616, 1.496799> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg.l_ankle.l_ball.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 14823184327075459882 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.l_upLeg.l_loLeg.l_ankle.l_ball.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: <-0.000000, 0.150173, -0.067188> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg.r_ankle.r_ball.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 7047410889883943662 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.r_upLeg.r_loLeg.r_ankle.r_ball.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: <-0.000000, 0.150173, -0.067188> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.neck.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 17654777499449634575 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.neck.transform +Node Type: TransformData + Matrix: + BasisX: < 0.994299, 0.019348, 0.104862> + BasisY: <-0.036664, 0.985473, 0.165825> + BasisZ: <-0.100131, -0.168724, 0.980564> + Transl: <-0.000000, 0.190000, -0.000000> + +Node Name: head +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.neck.head +Node Type: BoneData + WorldTransform: + BasisX: <-0.999482, -0.031040, -0.008521> + BasisY: <-0.007897, -0.020177, 0.999765> + BasisZ: <-0.031204, 0.999314, 0.019921> + Transl: <-0.002055, 0.035117, 1.663090> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 8014068480245264692 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.transform +Node Type: TransformData + Matrix: + BasisX: < 0.054517, -0.104367, 0.993043> + BasisY: < 0.998347, -0.012450, -0.056117> + BasisZ: < 0.018220, 0.994461, 0.103516> + Transl: < 0.081970, 0.115722, -0.010486> + +Node Name: l_upArm +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm +Node Type: BoneData + WorldTransform: + BasisX: <-0.135064, 0.978453, -0.156165> + BasisY: < 0.963903, 0.093252, -0.249389> + BasisZ: <-0.229453, -0.184212, -0.955729> + Transl: <-0.188150, -0.055591, 1.451722> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 3540966775393318185 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.transform +Node Type: TransformData + Matrix: + BasisX: <-0.200593, 0.193760, -0.960322> + BasisY: < 0.966208, -0.122847, -0.226609> + BasisZ: <-0.161881, -0.973327, -0.162570> + Transl: <-0.081969, 0.115722, -0.010486> + +Node Name: r_upArm +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm +Node Type: BoneData + WorldTransform: + BasisX: <-0.053400, -0.300900, -0.952159> + BasisY: <-0.998561, 0.020802, 0.049429> + BasisZ: < 0.004934, 0.953429, -0.301578> + Transl: < 0.197240, 0.053625, 1.473123> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.neck.head.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 6631764228102045526 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.neck.head.transform +Node Type: TransformData + Matrix: + BasisX: < 0.997232, -0.023058, 0.070690> + BasisY: < 0.031617, 0.991969, -0.122462> + BasisZ: <-0.067299, 0.124358, 0.989953> + Transl: <-0.000000, 0.094124, 0.016902> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 6458355176395918122 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.transform +Node Type: TransformData + Matrix: + BasisX: < 0.985987, -0.115634, -0.120241> + BasisY: <-0.144991, -0.950487, -0.274866> + BasisZ: <-0.082504, 0.288449, -0.953934> + Transl: <-0.011672, 0.115971, -0.042453> + +Node Name: l_upArmRoll +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_upArmRoll +Node Type: BoneData + WorldTransform: + BasisX: <-0.135064, 0.978453, -0.156165> + BasisY: < 0.963903, 0.093252, -0.249389> + BasisZ: <-0.229453, -0.184212, -0.955729> + Transl: <-0.217290, -0.078986, 1.330344> + +Node Name: l_loArm +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm +Node Type: BoneData + WorldTransform: + BasisX: < 0.224836, 0.963610, 0.144585> + BasisY: < 0.971745, -0.210802, -0.106181> + BasisZ: <-0.071838, 0.164373, -0.983779> + Transl: <-0.246431, -0.102381, 1.208967> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 11191005262967074716 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.transform +Node Type: TransformData + Matrix: + BasisX: < 0.128486, 0.305927, 0.943345> + BasisY: <-0.403212, 0.885172, -0.232143> + BasisZ: <-0.906042, -0.350541, 0.237086> + Transl: < 0.011672, -0.115971, 0.042450> + +Node Name: r_upArmRoll +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_upArmRoll +Node Type: BoneData + WorldTransform: + BasisX: <-0.053400, -0.300900, -0.952159> + BasisY: <-0.998561, 0.020802, 0.049429> + BasisZ: < 0.004934, 0.953429, -0.301578> + Transl: < 0.197861, 0.174716, 1.434821> + +Node Name: r_loArm +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm +Node Type: BoneData + WorldTransform: + BasisX: <-0.043387, 0.259999, -0.964634> + BasisY: <-0.998451, 0.022380, 0.050941> + BasisZ: < 0.034833, 0.965349, 0.258625> + Transl: < 0.198492, 0.295797, 1.396522> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_upArmRoll.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 15529789169672670472 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_upArmRoll.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.127000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 1758140872588918890 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.transform +Node Type: TransformData + Matrix: + BasisX: < 0.889900, 0.270521, -0.367282> + BasisY: <-0.320926, 0.943491, -0.082657> + BasisZ: < 0.324166, 0.191427, 0.926430> + Transl: < 0.000000, 0.000000, 0.254000> + +Node Name: l_loArmRoll +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_loArmRoll +Node Type: BoneData + WorldTransform: + BasisX: < 0.224836, 0.963610, 0.144585> + BasisY: < 0.971745, -0.210802, -0.106181> + BasisZ: <-0.071838, 0.164373, -0.983779> + Transl: <-0.256363, -0.079655, 1.072955> + +Node Name: l_hand +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand +Node Type: BoneData + WorldTransform: + BasisX: < 0.514369, 0.855813, 0.054857> + BasisY: < 0.853026, -0.517172, 0.069855> + BasisZ: < 0.088153, 0.010863, -0.996047> + Transl: <-0.266295, -0.056930, 0.936943> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_upArmRoll.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 2211195713271562660 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_upArmRoll.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000005, 0.127006> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 589767551061579979 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.transform +Node Type: TransformData + Matrix: + BasisX: < 0.842568, 0.001053, 0.538588> + BasisY: <-0.001920, 0.999998, 0.001049> + BasisZ: <-0.538586, -0.001918, 0.842568> + Transl: < 0.000000, 0.000001, 0.254001> + +Node Name: r_loArmRoll +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_loArmRoll +Node Type: BoneData + WorldTransform: + BasisX: <-0.043387, 0.259999, -0.964634> + BasisY: <-0.998451, 0.022380, 0.050941> + BasisZ: < 0.034833, 0.965349, 0.258625> + Transl: < 0.203311, 0.429257, 1.432278> + +Node Name: r_hand +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand +Node Type: BoneData + WorldTransform: + BasisX: < 0.047812, -0.119954, -0.991627> + BasisY: <-0.998511, -0.031839, -0.044292> + BasisZ: <-0.026260, 0.992269, -0.121297> + Transl: < 0.208123, 0.562725, 1.468034> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_loArmRoll.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 42494702923975860 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_loArmRoll.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, -0.000000, 0.138254> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 1504211847179885169 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.transform +Node Type: TransformData + Matrix: + BasisX: < 0.948250, 0.313603, 0.049754> + BasisY: <-0.296461, 0.930527, -0.215011> + BasisZ: <-0.113726, 0.189134, 0.975343> + Transl: < 0.000000, 0.000000, 0.276509> + +Node Name: l_thumb1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1 +Node Type: BoneData + WorldTransform: + BasisX: < 0.966532, -0.061678, 0.249022> + BasisY: < 0.256526, 0.220065, -0.941151> + BasisZ: < 0.003247, 0.973533, 0.228522> + Transl: <-0.239031, -0.017519, 0.902424> + +Node Name: l_index1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1 +Node Type: BoneData + WorldTransform: + BasisX: < 0.361563, 0.929834, 0.068408> + BasisY: < 0.167767, 0.007290, -0.985800> + BasisZ: <-0.917129, 0.367906, -0.153359> + Transl: <-0.255363, -0.027723, 0.841391> + +Node Name: l_mid1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1 +Node Type: BoneData + WorldTransform: + BasisX: < 0.329257, 0.944038, -0.019538> + BasisY: < 0.134573, -0.067396, -0.988609> + BasisZ: <-0.934601, 0.322877, -0.149233> + Transl: <-0.261361, -0.046572, 0.840283> + +Node Name: l_metacarpal +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal +Node Type: BoneData + WorldTransform: + BasisX: < 0.521514, 0.839841, -0.150633> + BasisY: <-0.019236, -0.164924, -0.986118> + BasisZ: <-0.853026, 0.517172, -0.069855> + Transl: <-0.269306, -0.070683, 0.893712> + +Node Name: l_handProp +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_handProp +Node Type: BoneData + WorldTransform: + BasisX: < 0.514369, 0.855813, 0.054857> + BasisY: < 0.088153, 0.010863, -0.996047> + BasisZ: <-0.853026, 0.517172, -0.069855> + Transl: <-0.247306, -0.062325, 0.878373> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_loArmRoll.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 10681596377770080497 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_loArmRoll.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: <-0.000001, -0.000004, 0.138251> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 3698507119532421339 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.transform +Node Type: TransformData + Matrix: + BasisX: < 0.923295, -0.100937, -0.370591> + BasisY: < 0.077771, 0.993995, -0.076972> + BasisZ: < 0.376136, 0.042247, 0.925601> + Transl: < 0.000000, 0.000000, 0.276509> + +Node Name: r_thumb1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1 +Node Type: BoneData + WorldTransform: + BasisX: < 0.005202, 0.326409, 0.945214> + BasisY: < 0.985042, -0.164476, 0.051377> + BasisZ: < 0.172235, 0.930809, -0.322382> + Transl: < 0.204493, 0.605135, 1.508976> + +Node Name: r_index1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1 +Node Type: BoneData + WorldTransform: + BasisX: <-0.080293, -0.430850, -0.898844> + BasisY: < 0.599061, -0.741585, 0.301956> + BasisZ: <-0.796667, -0.514218, 0.317650> + Transl: < 0.216812, 0.661876, 1.482050> + +Node Name: r_mid1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1 +Node Type: BoneData + WorldTransform: + BasisX: <-0.026042, -0.464356, -0.885265> + BasisY: < 0.970667, -0.223480, 0.088669> + BasisZ: <-0.239013, -0.856989, 0.456555> + Transl: < 0.213176, 0.659786, 1.462687> + +Node Name: r_metacarpal +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal +Node Type: BoneData + WorldTransform: + BasisX: <-0.193971, -0.324462, -0.925797> + BasisY: < 0.264669, -0.926035, 0.269092> + BasisZ: <-0.944630, -0.192833, 0.265499> + Transl: < 0.206234, 0.603106, 1.447237> + +Node Name: r_handProp +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_handProp +Node Type: BoneData + WorldTransform: + BasisX: < 0.055796, -0.000585, -0.998442> + BasisY: <-0.998185, -0.022714, -0.055768> + BasisZ: <-0.022646, 0.999742, -0.001852> + Transl: < 0.180957, 0.701814, 1.494520> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 14343621999852722213 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1.transform +Node Type: TransformData + Matrix: + BasisX: < 0.458030, 0.873770, -0.163505> + BasisY: < 0.268655, 0.039268, 0.962436> + BasisZ: < 0.847368, -0.484751, -0.216757> + Transl: < 0.045859, 0.000463, 0.037215> + +Node Name: l_thumb2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1.l_thumb2 +Node Type: BoneData + WorldTransform: + BasisX: < 0.966532, -0.061678, 0.249022> + BasisY: < 0.237349, -0.153444, -0.959229> + BasisZ: < 0.097374, 0.986230, -0.133669> + Transl: <-0.228837, -0.008774, 0.865024> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 11739678721039917067 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1.transform +Node Type: TransformData + Matrix: + BasisX: < 0.985494, -0.167682, -0.026164> + BasisY: < 0.038455, 0.070476, 0.996772> + BasisZ: <-0.165297, -0.983319, 0.075902> + Transl: < 0.025377, -0.012455, 0.096456> + +Node Name: l_index2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1.l_index2 +Node Type: BoneData + WorldTransform: + BasisX: < 0.361563, 0.929834, 0.068408> + BasisY: < 0.490118, -0.127139, -0.862334> + BasisZ: <-0.793130, 0.345316, -0.501698> + Transl: <-0.247117, -0.027364, 0.792942> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 13442540339145227410 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1.transform +Node Type: TransformData + Matrix: + BasisX: < 0.976208, -0.208730, 0.058741> + BasisY: <-0.042691, 0.080590, 0.995833> + BasisZ: <-0.212594, -0.974647, 0.069762> + Transl: < 0.006100, -0.007900, 0.096826> + +Node Name: l_mid2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1.l_mid2 +Node Type: BoneData + WorldTransform: + BasisX: < 0.329257, 0.944038, -0.019538> + BasisY: < 0.465563, -0.180309, -0.866452> + BasisZ: <-0.821487, 0.276189, -0.498877> + Transl: <-0.255124, -0.049696, 0.794467> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 974575578739462707 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.transform +Node Type: TransformData + Matrix: + BasisX: < 0.978734, -0.000000, 0.205134> + BasisY: <-0.205134, -0.000000, 0.978734> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: <-0.015690, 0.001524, 0.042645> + +Node Name: l_ring1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1 +Node Type: BoneData + WorldTransform: + BasisX: < 0.355971, 0.932976, -0.053286> + BasisY: < 0.135966, -0.108122, -0.984796> + BasisZ: <-0.924553, 0.343314, -0.165341> + Transl: <-0.267971, -0.066789, 0.843287> + +Node Name: l_pinky1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1 +Node Type: BoneData + WorldTransform: + BasisX: < 0.316973, 0.929755, -0.187305> + BasisY: < 0.269800, -0.277722, -0.921997> + BasisZ: <-0.909250, 0.241713, -0.338878> + Transl: <-0.274493, -0.085610, 0.850043> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_handProp.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 12174887080937692924 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_handProp.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.001937, 0.014896, 0.059955> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 13049257571504175902 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1.transform +Node Type: TransformData + Matrix: + BasisX: <-0.976206, -0.057453, 0.209097> + BasisY: < 0.015879, -0.980614, -0.195303> + BasisZ: < 0.216264, -0.187336, 0.958194> + Transl: <-0.045860, 0.000461, 0.037212> + +Node Name: r_thumb2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1.r_thumb2 +Node Type: BoneData + WorldTransform: + BasisX: < 0.005202, 0.326409, 0.945214> + BasisY: < 0.528044, -0.803596, 0.274598> + BasisZ: < 0.849202, 0.497686, -0.176538> + Transl: < 0.165349, 0.611670, 1.506935> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 5016935426297207355 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.transform + +Node Type: TransformData + Matrix: + BasisX: < 0.939162, 0.133704, -0.316383> + BasisY: <-0.181830, -0.587932, -0.788209> + BasisZ: <-0.291398, 0.797784, -0.527852> + Transl: <-0.025377, -0.012454, 0.096457> + +Node Name: r_index2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.r_index2 +Node Type: BoneData + WorldTransform: + BasisX: <-0.102387, -0.418082, -0.902621> + BasisY: < 0.928150, 0.286271, -0.237880> + BasisZ: < 0.357847, -0.862123, 0.358732> + Transl: < 0.187367, 0.698324, 1.467209> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 18436012888435130283 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.transform +Node Type: TransformData + Matrix: + BasisX: < 0.932310, 0.079999, -0.352702> + BasisY: <-0.014710, -0.966034, -0.257996> + BasisZ: <-0.361362, 0.245721, -0.899466> + Transl: <-0.006099, -0.007899, 0.096827> + +Node Name: r_mid2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.r_mid2 +Node Type: BoneData + WorldTransform: + BasisX: <-0.026042, -0.464356, -0.885265> + BasisY: < 0.573273, 0.718543, -0.393768> + BasisZ: < 0.818951, -0.517753, 0.247490> + Transl: < 0.168188, 0.670142, 1.458578> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 5132817095621228682 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.transform +Node Type: TransformData + Matrix: + BasisX: < 0.947692, 0.245019, -0.204563> + BasisY: <-0.143103, -0.246709, -0.958465> + BasisZ: <-0.285310, 0.937604, -0.198741> + Transl: < 0.015689, 0.001521, 0.042641> + +Node Name: r_ring1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1 +Node Type: BoneData + WorldTransform: + BasisX: <-0.065609, -0.462609, -0.884132> + BasisY: < 0.993795, -0.110043, -0.016168> + BasisZ: <-0.089813, -0.879707, 0.466958> + Transl: < 0.199642, 0.653138, 1.443571> + +Node Name: r_pinky1 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1 +Node Type: BoneData + WorldTransform: + BasisX: <-0.110035, -0.467205, -0.877275> + BasisY: < 0.990191, 0.024933, -0.137476> + BasisZ: < 0.086103, -0.883797, 0.459878> + Transl: < 0.192242, 0.642502, 1.427003> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_handProp.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 13043566764728405902 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_handProp.transform +Node Type: TransformData + Matrix: + BasisX: < 0.992821, -0.011471, 0.119062> + BasisY: < 0.010301, 0.999892, 0.010438> + BasisZ: <-0.119169, -0.009136, 0.992832> + Transl: <-0.044247, 0.021524, 0.135515> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1.l_thumb2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 8680475892235074248 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1.l_thumb2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.929898, -0.367817> + BasisZ: < 0.000000, 0.367817, 0.929898> + Transl: <-0.000000, 0.039739, 0.000000> + +Node Name: l_thumb3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1.l_thumb2.l_thumb3 +Node Type: BoneData + WorldTransform: + BasisX: < 0.966532, -0.061678, 0.249022> + BasisY: < 0.237349, -0.153444, -0.959229> + BasisZ: < 0.097374, 0.986230, -0.133669> + Transl: <-0.221823, -0.013308, 0.836676> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1.l_index2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 9992946931765780388 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1.l_index2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.931387, -0.364030> + BasisZ: < 0.000000, 0.364030, 0.931387> + Transl: <-0.000000, 0.049147, 0.000000> + +Node Name: l_index3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1.l_index2.l_index3 +Node Type: BoneData + WorldTransform: + BasisX: < 0.361563, 0.929834, 0.068408> + BasisY: < 0.745213, -0.244121, -0.620534> + BasisZ: <-0.560294, 0.275341, -0.781190> + Transl: <-0.231135, -0.031510, 0.764821> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1.l_mid2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 5852602857894555578 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1.l_mid2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.931387, -0.364030> + BasisZ: < 0.000000, 0.364030, 0.931387> + Transl: <-0.000000, 0.046344, 0.000000> + +Node Name: l_mid3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1.l_mid2.l_mid3 +Node Type: BoneData + WorldTransform: + BasisX: < 0.329257, 0.944038, -0.019538> + BasisY: < 0.732665, -0.268479, -0.625396> + BasisZ: <-0.595644, 0.191601, -0.780062> + Transl: <-0.239947, -0.055573, 0.766221> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 1267179882835617369 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1.transform +Node Type: TransformData + Matrix: + BasisX: < 0.977223, -0.108172, 0.182579> + BasisY: < 0.128445, 0.986342, -0.103107> + BasisZ: <-0.168932, 0.124210, 0.977770> + Transl: < 0.011562, 0.049057, 0.004398> + +Node Name: l_ring2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1.l_ring2 +Node Type: BoneData + WorldTransform: + BasisX: < 0.355971, 0.932976, -0.053286> + BasisY: < 0.512261, -0.242505, -0.823881> + BasisZ: <-0.781584, 0.265982, -0.564252> + Transl: <-0.262502, -0.071138, 0.803673> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 6387671957824055658 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1.transform +Node Type: TransformData + Matrix: + BasisX: < 0.974367, 0.025268, 0.223541> + BasisY: < 0.046345, 0.949812, -0.309370> + BasisZ: <-0.220139, 0.311800, 0.924294> + Transl: <-0.008664, 0.045625, -0.000244> + +Node Name: l_pinky2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1.l_pinky2 +Node Type: BoneData + WorldTransform: + BasisX: < 0.316973, 0.929755, -0.187305> + BasisY: < 0.608398, -0.350828, -0.711879> + BasisZ: <-0.727585, 0.111690, -0.676864> + Transl: <-0.265311, -0.095062, 0.818663> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1.r_thumb2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 16242899712758786600 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1.r_thumb2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.666425, -0.745572> + BasisZ: < 0.000000, 0.745572, 0.666425> + Transl: < 0.000000, -0.039738, -0.000001> + +Node Name: r_thumb3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1.r_thumb2.r_thumb3 +Node Type: BoneData + WorldTransform: + BasisX: < 0.005202, 0.326409, 0.945214> + BasisY: < 0.067094, -0.943211, 0.325348> + BasisZ: < 0.997733, 0.061726, -0.026807> + Transl: < 0.149746, 0.635423, 1.498819> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.r_index2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 5372640529794878290 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.r_index2.transform +Node Type: TransformData + Matrix: + BasisX: < 0.999667, -0.023844, 0.009837> + BasisY: < 0.015953, 0.271895, -0.962195> + BasisZ: < 0.020268, 0.962031, 0.272185> + Transl: < 0.000001, -0.049150, 0.000002> + +Node Name: r_index3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.r_index2.r_index3 +Node Type: BoneData + WorldTransform: + BasisX: <-0.102387, -0.418082, -0.902621> + BasisY: < 0.098677, 0.898645, -0.427434> + BasisZ: < 0.989838, -0.132831, -0.050755> + Transl: < 0.157099, 0.688988, 1.474966> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.r_mid2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 12073124542497485563 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.r_mid2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.360962, -0.932581> + BasisZ: < 0.000000, 0.932581, 0.360962> + Transl: < 0.000001, -0.046347, 0.000002> + +Node Name: r_mid3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.r_mid2.r_mid3 +Node Type: BoneData + WorldTransform: + BasisX: <-0.026042, -0.464356, -0.885265> + BasisY: <-0.563987, 0.737995, -0.370516> + BasisZ: < 0.825373, 0.489629, -0.281110> + Transl: < 0.149501, 0.646717, 1.471415> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 8154488711982939451 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1.transform +Node Type: TransformData + Matrix: + BasisX: < 0.981351, 0.173114, -0.083554> + BasisY: <-0.142094, 0.360579, -0.921841> + BasisZ: <-0.129456, 0.916523, 0.378454> + Transl: <-0.011561, -0.049063, -0.004394> + +Node Name: r_ring2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1.r_ring2 +Node Type: BoneData + WorldTransform: + BasisX: <-0.065609, -0.462609, -0.884132> + BasisY: < 0.321723, 0.828917, -0.457593> + BasisZ: < 0.944558, -0.314468, 0.094448> + Transl: < 0.159665, 0.657564, 1.444222> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 9369437217348288734 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1.transform +Node Type: TransformData + Matrix: + BasisX: < 0.985112, 0.167457, -0.038881> + BasisY: <-0.072884, 0.201990, -0.976672> + BasisZ: <-0.155697, 0.964965, 0.211187> + Transl: < 0.008664, -0.045630, 0.000249> + +Node Name: r_pinky2 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1.r_pinky2 +Node Type: BoneData + WorldTransform: + BasisX: <-0.110035, -0.467205, -0.877275> + BasisY: < 0.256704, 0.839331, -0.479194> + BasisZ: < 0.960206, -0.277928, 0.027578> + Transl: < 0.158539, 0.641652, 1.431683> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1.l_thumb2.l_thumb3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 4676225233777059829 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_thumb1.l_thumb2.l_thumb3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: <-0.000000, 0.029553, 0.000000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1.l_index2.l_index3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 6008238647628856750 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_index1.l_index2.l_index3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.931387, -0.364030> + BasisZ: < 0.000000, 0.364030, 0.931387> + Transl: <-0.000000, 0.032610, 0.000000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1.l_mid2.l_mid3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 15208282716784707655 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_mid1.l_mid2.l_mid3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.931387, -0.364030> + BasisZ: < 0.000000, 0.364030, 0.931387> + Transl: <-0.000000, 0.032599, 0.000000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1.l_ring2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 1878731310508703159 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1.l_ring2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.907225, -0.420646> + BasisZ: < 0.000000, 0.420646, 0.907225> + Transl: < 0.000000, 0.040226, -0.000000> + +Node Name: l_ring3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1.l_ring2.l_ring3 +Node Type: BoneData + WorldTransform: + BasisX: < 0.355971, 0.932976, -0.053286> + BasisY: < 0.793506, -0.331890, -0.510095> + BasisZ: <-0.493592, 0.139297, -0.858465> + Transl: <-0.245519, -0.079178, 0.776359> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1.l_pinky2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 13581491544008149678 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1.l_pinky2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.917929, -0.396745> + BasisZ: < 0.000000, 0.396745, 0.917929> + Transl: < 0.000000, 0.034035, 0.000000> + +Node Name: l_pinky3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1.l_pinky2.l_pinky3 +Node Type: BoneData + WorldTransform: + BasisX: < 0.316973, 0.929755, -0.187305> + BasisY: < 0.847132, -0.366347, -0.384912> + BasisZ: <-0.426492, -0.036665, -0.903747> + Transl: <-0.250848, -0.103402, 0.801741> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1.r_thumb2.r_thumb3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 10072340935457210394 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_thumb1.r_thumb2.r_thumb3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.882729, -0.469883> + BasisZ: < 0.000000, 0.469883, 0.882729> + Transl: < 0.000001, -0.029555, 0.000004> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.r_index2.r_index3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 9406467531174726159 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.r_index2.r_index3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.450521, -0.892766> + BasisZ: < 0.000000, 0.892766, 0.450521> + Transl: < 0.000000, -0.032611, 0.000000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.r_mid2.r_mid3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 10286873651678877093 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.r_mid2.r_mid3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.352861, -0.935676> + BasisZ: < 0.000000, 0.935676, 0.352861> + Transl: < 0.000000, -0.032600, 0.000001> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1.r_ring2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 440935841590130722 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1.r_ring2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.235909, -0.971775> + BasisZ: < 0.000000, 0.971775, 0.235909> + Transl: < 0.000000, -0.040227, 0.000001> + +Node Name: r_ring3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1.r_ring2.r_ring3 +Node Type: BoneData + WorldTransform: + BasisX: <-0.065609, -0.462609, -0.884132> + BasisY: <-0.830725, 0.516186, -0.208441> + BasisZ: < 0.552803, 0.720795, -0.418167> + Transl: < 0.148998, 0.630083, 1.459393> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1.r_pinky2.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 14713526539244402950 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1.r_pinky2.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.340991, -0.940067> + BasisZ: < 0.000000, 0.940067, 0.340991> + Transl: < 0.000000, -0.034037, 0.000001> + +Node Name: r_pinky3 +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1.r_pinky2.r_pinky3 +Node Type: BoneData + WorldTransform: + BasisX: <-0.110035, -0.467205, -0.877275> + BasisY: <-0.875654, 0.463151, -0.136825> + BasisZ: < 0.470236, 0.753134, -0.460072> + Transl: < 0.152437, 0.621701, 1.443073> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1.l_ring2.l_ring3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 10370667742752731647 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_ring1.l_ring2.l_ring3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.907225, -0.420646> + BasisZ: < 0.000000, 0.420646, 0.907225> + Transl: <-0.000000, 0.033153, 0.000000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1.l_pinky2.l_pinky3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 8840292647679362561 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.l_pinky1.l_pinky2.l_pinky3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.917929, -0.396745> + BasisZ: < 0.000000, 0.396745, 0.917929> + Transl: < 0.000000, 0.023771, 0.000000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1.r_ring2.r_ring3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 16473969543279624642 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_ring1.r_ring2.r_ring3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.255993, -0.966679> + BasisZ: < 0.000000, 0.966679, 0.255993> + Transl: < 0.000000, -0.033153, -0.000000> + +Node Name: animation +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1.r_pinky2.r_pinky3.animation +Node Type: AnimationData + KeyFrames: Count 195. Hash: 756580873850673075 + TimeStepBetweenFrames: 0.033333 + +Node Name: transform +Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_metacarpal.r_pinky1.r_pinky2.r_pinky3.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 0.229519, -0.973304> + BasisZ: < 0.000000, 0.973304, 0.229519> + Transl: < 0.000000, -0.023770, 0.000000> diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.fbx b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.fbx new file mode 100644 index 0000000000..995d2f4ea2 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3384e88cd0f47ab8ec81eb75101b02ff9675c76dc070c726a9f3f39f1b2b2df +size 4994448 diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/single_mesh_multiple_materials.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/single_mesh_multiple_materials.dbgsg index 293a936de0..e1a3d618ad 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/single_mesh_multiple_materials.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/single_mesh_multiple_materials.dbgsg @@ -1,16 +1,34 @@ ProductName: single_mesh_multiple_materials.dbgsg debugSceneGraphVersion: 1 single_mesh_multiple_materials -Node Name: Torus -Node Path: RootNode.Torus +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Torus_1 +Node Path: RootNode.Torus.Torus_1 Node Type: MeshData Positions: Count 2304. Hash: 12560656679477605282 Normals: Count 2304. Hash: 14915939258818888021 FaceList: Count 1152. Hash: 3035560221708475304 FaceMaterialIds: Count 1152. Hash: 2033667258170256242 -Node Name: Torus_optimized -Node Path: RootNode.Torus_optimized +Node Name: Torus_2 +Node Path: RootNode.Torus.Torus_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Torus_1_optimized +Node Path: RootNode.Torus.Torus_1_optimized Node Type: MeshData Positions: Count 2304. Hash: 12560656679477605282 Normals: Count 2304. Hash: 14915939258818888021 @@ -18,7 +36,7 @@ Node Type: MeshData FaceMaterialIds: Count 1152. Hash: 2033667258170256242 Node Name: transform -Node Path: RootNode.Torus.transform +Node Path: RootNode.Torus.Torus_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -27,13 +45,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UV0 -Node Path: RootNode.Torus.UV0 +Node Path: RootNode.Torus.Torus_1.UV0 Node Type: MeshVertexUVData UVs: Count 2304. Hash: 6069930558565069665 UVCustomName: UV0 Node Name: OrangeMaterial -Node Path: RootNode.Torus.OrangeMaterial +Node Path: RootNode.Torus.Torus_1.OrangeMaterial Node Type: MaterialData MaterialName: OrangeMaterial UniqueId: 10937477720113828524 @@ -63,7 +81,7 @@ Node Type: MaterialData BaseColorTexture: Node Name: SecondTextureMaterial -Node Path: RootNode.Torus.SecondTextureMaterial +Node Path: RootNode.Torus.Torus_1.SecondTextureMaterial Node Type: MaterialData MaterialName: SecondTextureMaterial UniqueId: 16601413836225607467 @@ -93,7 +111,7 @@ Node Type: MaterialData BaseColorTexture: OneMeshMultipleMaterials/FBXSecondTestTexture.png Node Name: FirstTextureMaterial -Node Path: RootNode.Torus.FirstTextureMaterial +Node Path: RootNode.Torus.Torus_1.FirstTextureMaterial Node Type: MaterialData MaterialName: FirstTextureMaterial UniqueId: 2580020563915538382 @@ -122,40 +140,21 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: OneMeshMultipleMaterials/FBXTestTexture.png -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Torus.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Torus.Torus_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 2304. Hash: 17641066831235827929 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Torus.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Torus.Torus_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 2304. Hash: 6274616552656695154 - TangentSpace: 1 - -Node Name: UV0 -Node Path: RootNode.Torus_optimized.UV0 -Node Type: MeshVertexUVData - UVs: Count 2304. Hash: 6069930558565069665 - UVCustomName: UV0 - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Torus_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 2304. Hash: 17641066831235827929 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Torus_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 2304. Hash: 6274616552656695154 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Torus_optimized.transform +Node Path: RootNode.Torus.Torus_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -163,8 +162,14 @@ Node Type: TransformData BasisZ: < 0.000000, -100.000000, -0.000016> Transl: < 0.000000, 0.000000, 0.000000> +Node Name: UV0 +Node Path: RootNode.Torus.Torus_2.UV0 +Node Type: MeshVertexUVData + UVs: Count 2304. Hash: 6069930558565069665 + UVCustomName: UV0 + Node Name: OrangeMaterial -Node Path: RootNode.Torus_optimized.OrangeMaterial +Node Path: RootNode.Torus.Torus_2.OrangeMaterial Node Type: MaterialData MaterialName: OrangeMaterial UniqueId: 10937477720113828524 @@ -194,7 +199,7 @@ Node Type: MaterialData BaseColorTexture: Node Name: SecondTextureMaterial -Node Path: RootNode.Torus_optimized.SecondTextureMaterial +Node Path: RootNode.Torus.Torus_2.SecondTextureMaterial Node Type: MaterialData MaterialName: SecondTextureMaterial UniqueId: 16601413836225607467 @@ -224,7 +229,7 @@ Node Type: MaterialData BaseColorTexture: OneMeshMultipleMaterials/FBXSecondTestTexture.png Node Name: FirstTextureMaterial -Node Path: RootNode.Torus_optimized.FirstTextureMaterial +Node Path: RootNode.Torus.Torus_2.FirstTextureMaterial Node Type: MaterialData MaterialName: FirstTextureMaterial UniqueId: 2580020563915538382 @@ -253,3 +258,120 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: OneMeshMultipleMaterials/FBXTestTexture.png +Node Name: UV0 +Node Path: RootNode.Torus.Torus_1_optimized.UV0 +Node Type: MeshVertexUVData + UVs: Count 2304. Hash: 6069930558565069665 + UVCustomName: UV0 + +Node Name: TangentSet_0 +Node Path: RootNode.Torus.Torus_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 2304. Hash: 17641066831235827929 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.Torus.Torus_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 2304. Hash: 6274616552656695154 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.Torus.Torus_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: OrangeMaterial +Node Path: RootNode.Torus.Torus_1_optimized.OrangeMaterial +Node Type: MaterialData + MaterialName: OrangeMaterial + UniqueId: 10937477720113828524 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.113346, 0.000000> + SpecularColor: < 0.800000, 0.113346, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: SecondTextureMaterial +Node Path: RootNode.Torus.Torus_1_optimized.SecondTextureMaterial +Node Type: MaterialData + MaterialName: SecondTextureMaterial + UniqueId: 16601413836225607467 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.800000, 0.800000, 0.800000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: OneMeshMultipleMaterials/FBXSecondTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: OneMeshMultipleMaterials/FBXSecondTestTexture.png + +Node Name: FirstTextureMaterial +Node Path: RootNode.Torus.Torus_1_optimized.FirstTextureMaterial +Node Type: MaterialData + MaterialName: FirstTextureMaterial + UniqueId: 2580020563915538382 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.800000, 0.800000, 0.800000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: OneMeshMultipleMaterials/FBXTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: OneMeshMultipleMaterials/FBXTestTexture.png diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/onemeshonematerial.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/onemeshonematerial.dbgsg index 658e8c8994..ffe6a8ce71 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/onemeshonematerial.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/onemeshonematerial.dbgsg @@ -1,16 +1,34 @@ ProductName: OneMeshOneMaterial.dbgsg debugSceneGraphVersion: 1 OneMeshOneMaterial -Node Name: Cube -Node Path: RootNode.Cube +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1 +Node Path: RootNode.Cube.Cube_1 Node Type: MeshData Positions: Count 24. Hash: 8661923109306356285 Normals: Count 24. Hash: 5807525742165000561 FaceList: Count 12. Hash: 9888799799190757436 FaceMaterialIds: Count 12. Hash: 7110546404675862471 -Node Name: Cube_optimized -Node Path: RootNode.Cube_optimized +Node Name: Cube_2 +Node Path: RootNode.Cube.Cube_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1_optimized +Node Path: RootNode.Cube.Cube_1_optimized Node Type: MeshData Positions: Count 24. Hash: 8661923109306356285 Normals: Count 24. Hash: 5807525742165000561 @@ -18,7 +36,7 @@ Node Type: MeshData FaceMaterialIds: Count 12. Hash: 7110546404675862471 Node Name: transform -Node Path: RootNode.Cube.transform +Node Path: RootNode.Cube.Cube_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -27,13 +45,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cube.UVMap +Node Path: RootNode.Cube.Cube_1.UVMap Node Type: MeshVertexUVData UVs: Count 24. Hash: 1622169145591646736 UVCustomName: UVMap Node Name: CubeMaterial -Node Path: RootNode.Cube.CubeMaterial +Node Path: RootNode.Cube.Cube_1.CubeMaterial Node Type: MaterialData MaterialName: CubeMaterial UniqueId: 973942033197978066 @@ -62,40 +80,21 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: OneMeshOneMaterial/FBXTestTexture.png -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 - -Node Name: UVMap -Node Path: RootNode.Cube_optimized.UVMap -Node Type: MeshVertexUVData - UVs: Count 24. Hash: 1622169145591646736 - UVCustomName: UVMap - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cube_optimized.transform +Node Path: RootNode.Cube.Cube_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -103,8 +102,72 @@ Node Type: TransformData BasisZ: < 0.000000, -100.000000, -0.000016> Transl: < 0.000000, 0.000000, 0.000000> +Node Name: UVMap +Node Path: RootNode.Cube.Cube_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + Node Name: CubeMaterial -Node Path: RootNode.Cube_optimized.CubeMaterial +Node Path: RootNode.Cube.Cube_2.CubeMaterial +Node Type: MaterialData + MaterialName: CubeMaterial + UniqueId: 973942033197978066 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.800000, 0.800000, 0.800000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 36.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: OneMeshOneMaterial/FBXTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: OneMeshOneMaterial/FBXTestTexture.png + +Node Name: UVMap +Node Path: RootNode.Cube.Cube_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 24. Hash: 13438447437797057049 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 24. Hash: 11372562338897179017 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.Cube.Cube_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: CubeMaterial +Node Path: RootNode.Cube.Cube_1_optimized.CubeMaterial Node Type: MaterialData MaterialName: CubeMaterial UniqueId: 973942033197978066 diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/lodtest.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/lodtest.dbgsg index 1c39ca312a..90bcb94795 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/lodtest.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/lodtest.dbgsg @@ -1,64 +1,109 @@ ProductName: lodtest.dbgsg debugSceneGraphVersion: 1 lodtest -Node Name: lodtest -Node Path: RootNode.lodtest +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: lodtest_1 +Node Path: RootNode.lodtest.lodtest_1 Node Type: MeshData Positions: Count 24. Hash: 8661923109306356285 Normals: Count 24. Hash: 5807525742165000561 FaceList: Count 12. Hash: 9888799799190757436 FaceMaterialIds: Count 12. Hash: 7110546404675862471 -Node Name: lodtest_lod3 -Node Path: RootNode.lodtest_lod3 +Node Name: lodtest_2 +Node Path: RootNode.lodtest.lodtest_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: lodtest_1_optimized +Node Path: RootNode.lodtest.lodtest_1_optimized +Node Type: MeshData + Positions: Count 24. Hash: 8661923109306356285 + Normals: Count 24. Hash: 5807525742165000561 + FaceList: Count 12. Hash: 9888799799190757436 + FaceMaterialIds: Count 12. Hash: 7110546404675862471 + +Node Name: lodtest_lod3_1 +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1 Node Type: MeshData Positions: Count 1984. Hash: 6600975913707260286 Normals: Count 1984. Hash: 2708036977889843831 FaceList: Count 960. Hash: 10390417165025722786 FaceMaterialIds: Count 960. Hash: 12510609185544665964 -Node Name: lodtest_lod2 -Node Path: RootNode.lodtest_lod2 +Node Name: lodtest_lod3_2 +Node Path: RootNode.lodtest_lod3.lodtest_lod3_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 2.298166, 0.000000> + +Node Name: lodtest_lod3_1_optimized +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1_optimized +Node Type: MeshData + Positions: Count 1984. Hash: 6600975913707260286 + Normals: Count 1984. Hash: 2708036977889843831 + FaceList: Count 960. Hash: 10390417165025722786 + FaceMaterialIds: Count 960. Hash: 12510609185544665964 + +Node Name: lodtest_lod2_1 +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1 Node Type: MeshData Positions: Count 240. Hash: 219362421205407416 Normals: Count 240. Hash: 11195242321181199939 FaceList: Count 80. Hash: 11130917988116538993 FaceMaterialIds: Count 80. Hash: 4190892684086530065 -Node Name: lodtest_lod1 -Node Path: RootNode.lodtest_lod1 +Node Name: lodtest_lod2_2 +Node Path: RootNode.lodtest_lod2.lodtest_lod2_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, -0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, -2.211498, 0.000000> + +Node Name: lodtest_lod2_1_optimized +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1_optimized +Node Type: MeshData + Positions: Count 240. Hash: 219362421205407416 + Normals: Count 240. Hash: 11195242321181199939 + FaceList: Count 80. Hash: 11130917988116538993 + FaceMaterialIds: Count 80. Hash: 4190892684086530065 + +Node Name: lodtest_lod1_1 +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1 Node Type: MeshData Positions: Count 192. Hash: 1283526254311745349 Normals: Count 192. Hash: 1873340970602844856 FaceList: Count 124. Hash: 3728991722746136013 FaceMaterialIds: Count 124. Hash: 2372486708814455910 -Node Name: lodtest_optimized -Node Path: RootNode.lodtest_optimized -Node Type: MeshData - Positions: Count 24. Hash: 8661923109306356285 - Normals: Count 24. Hash: 5807525742165000561 - FaceList: Count 12. Hash: 9888799799190757436 - FaceMaterialIds: Count 12. Hash: 7110546404675862471 +Node Name: lodtest_lod1_2 +Node Path: RootNode.lodtest_lod1.lodtest_lod1_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 2.410331, 0.000000, 0.000000> -Node Name: lodtest_lod3_optimized -Node Path: RootNode.lodtest_lod3_optimized -Node Type: MeshData - Positions: Count 1984. Hash: 6600975913707260286 - Normals: Count 1984. Hash: 2708036977889843831 - FaceList: Count 960. Hash: 10390417165025722786 - FaceMaterialIds: Count 960. Hash: 12510609185544665964 - -Node Name: lodtest_lod2_optimized -Node Path: RootNode.lodtest_lod2_optimized -Node Type: MeshData - Positions: Count 240. Hash: 219362421205407416 - Normals: Count 240. Hash: 11195242321181199939 - FaceList: Count 80. Hash: 11130917988116538993 - FaceMaterialIds: Count 80. Hash: 4190892684086530065 - -Node Name: lodtest_lod1_optimized -Node Path: RootNode.lodtest_lod1_optimized +Node Name: lodtest_lod1_1_optimized +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1_optimized Node Type: MeshData Positions: Count 192. Hash: 7921557352486854444 Normals: Count 192. Hash: 1873340970602844856 @@ -66,7 +111,7 @@ Node Type: MeshData FaceMaterialIds: Count 124. Hash: 2372486708814455910 Node Name: transform -Node Path: RootNode.lodtest.transform +Node Path: RootNode.lodtest.lodtest_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -75,13 +120,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.lodtest.UVMap +Node Path: RootNode.lodtest.lodtest_1.UVMap Node Type: MeshVertexUVData UVs: Count 24. Hash: 1622169145591646736 UVCustomName: UVMap Node Name: Material -Node Path: RootNode.lodtest.Material +Node Path: RootNode.lodtest.lodtest_1.Material Node Type: MaterialData MaterialName: Material UniqueId: 11127505492038345244 @@ -110,21 +155,124 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.lodtest.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.lodtest.lodtest_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.lodtest.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.lodtest.lodtest_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.lodtest_lod3.transform +Node Path: RootNode.lodtest.lodtest_2.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: UVMap +Node Path: RootNode.lodtest.lodtest_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: Material +Node Path: RootNode.lodtest.lodtest_2.Material +Node Type: MaterialData + MaterialName: Material + UniqueId: 11127505492038345244 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.800000, 0.800000, 0.800000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 36.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: UVMap +Node Path: RootNode.lodtest.lodtest_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.lodtest.lodtest_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 24. Hash: 13438447437797057049 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.lodtest.lodtest_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 24. Hash: 11372562338897179017 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.lodtest.lodtest_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Material +Node Path: RootNode.lodtest.lodtest_1_optimized.Material +Node Type: MaterialData + MaterialName: Material + UniqueId: 11127505492038345244 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.800000, 0.800000, 0.800000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 36.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: transform +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -133,13 +281,13 @@ Node Type: TransformData Transl: < 0.000000, 2.298166, 0.000000> Node Name: UVMap -Node Path: RootNode.lodtest_lod3.UVMap +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1.UVMap Node Type: MeshVertexUVData UVs: Count 1984. Hash: 14119273880200542497 UVCustomName: UVMap Node Name: DefaultMaterial -Node Path: RootNode.lodtest_lod3.DefaultMaterial +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1.DefaultMaterial Node Type: MaterialData MaterialName: DefaultMaterial UniqueId: 3809502407269006983 @@ -168,21 +316,124 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod3.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 1984. Hash: 5664494957869921957 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod3.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 1984. Hash: 5048878728906162461 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.lodtest_lod2.transform +Node Path: RootNode.lodtest_lod3.lodtest_lod3_2.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 2.298166, 0.000000> + +Node Name: UVMap +Node Path: RootNode.lodtest_lod3.lodtest_lod3_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 1984. Hash: 14119273880200542497 + UVCustomName: UVMap + +Node Name: DefaultMaterial +Node Path: RootNode.lodtest_lod3.lodtest_lod3_2.DefaultMaterial +Node Type: MaterialData + MaterialName: DefaultMaterial + UniqueId: 3809502407269006983 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 0.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: UVMap +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 1984. Hash: 14119273880200542497 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1984. Hash: 5664494957869921957 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1984. Hash: 5048878728906162461 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 2.298166, 0.000000> + +Node Name: DefaultMaterial +Node Path: RootNode.lodtest_lod3.lodtest_lod3_1_optimized.DefaultMaterial +Node Type: MaterialData + MaterialName: DefaultMaterial + UniqueId: 3809502407269006983 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 0.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: transform +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, -0.000000, 0.000000> @@ -191,13 +442,13 @@ Node Type: TransformData Transl: < 0.000000, -2.211498, 0.000000> Node Name: UVMap -Node Path: RootNode.lodtest_lod2.UVMap +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1.UVMap Node Type: MeshVertexUVData UVs: Count 240. Hash: 13702273589593616598 UVCustomName: UVMap Node Name: DefaultMaterial -Node Path: RootNode.lodtest_lod2.DefaultMaterial +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1.DefaultMaterial Node Type: MaterialData MaterialName: DefaultMaterial UniqueId: 3809502407269006983 @@ -226,21 +477,124 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod2.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 240. Hash: 1390901212717410749 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod2.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 240. Hash: 1379238632949267281 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.lodtest_lod1.transform +Node Path: RootNode.lodtest_lod2.lodtest_lod2_2.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, -0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, -2.211498, 0.000000> + +Node Name: UVMap +Node Path: RootNode.lodtest_lod2.lodtest_lod2_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 240. Hash: 13702273589593616598 + UVCustomName: UVMap + +Node Name: DefaultMaterial +Node Path: RootNode.lodtest_lod2.lodtest_lod2_2.DefaultMaterial +Node Type: MaterialData + MaterialName: DefaultMaterial + UniqueId: 3809502407269006983 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 0.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: UVMap +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 240. Hash: 13702273589593616598 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 240. Hash: 1390901212717410749 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 240. Hash: 1379238632949267281 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, -0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, -2.211498, 0.000000> + +Node Name: DefaultMaterial +Node Path: RootNode.lodtest_lod2.lodtest_lod2_1_optimized.DefaultMaterial +Node Type: MaterialData + MaterialName: DefaultMaterial + UniqueId: 3809502407269006983 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 0.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: transform +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -249,13 +603,13 @@ Node Type: TransformData Transl: < 2.410331, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.lodtest_lod1.UVMap +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1.UVMap Node Type: MeshVertexUVData UVs: Count 192. Hash: 27253578623892681 UVCustomName: UVMap Node Name: DefaultMaterial -Node Path: RootNode.lodtest_lod1.DefaultMaterial +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1.DefaultMaterial Node Type: MaterialData MaterialName: DefaultMaterial UniqueId: 3809502407269006983 @@ -284,107 +638,36 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod1.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 192. Hash: 11165448242141781141 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod1.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 192. Hash: 7987814487334449536 - TangentSpace: 1 - -Node Name: UVMap -Node Path: RootNode.lodtest_optimized.UVMap -Node Type: MeshVertexUVData - UVs: Count 24. Hash: 1622169145591646736 - UVCustomName: UVMap - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.lodtest_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.lodtest_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.lodtest_optimized.transform +Node Path: RootNode.lodtest_lod1.lodtest_lod1_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> BasisY: < 0.000000, -0.000016, 100.000000> BasisZ: < 0.000000, -100.000000, -0.000016> - Transl: < 0.000000, 0.000000, 0.000000> - -Node Name: Material -Node Path: RootNode.lodtest_optimized.Material -Node Type: MaterialData - MaterialName: Material - UniqueId: 11127505492038345244 - IsNoDraw: false - DiffuseColor: < 0.800000, 0.800000, 0.800000> - SpecularColor: < 0.800000, 0.800000, 0.800000> - EmissiveColor: < 0.000000, 0.000000, 0.000000> - Opacity: 1.000000 - Shininess: 36.000000 - UseColorMap: Not set - BaseColor: Not set - UseMetallicMap: Not set - MetallicFactor: Not set - UseRoughnessMap: Not set - RoughnessFactor: Not set - UseEmissiveMap: Not set - EmissiveIntensity: Not set - UseAOMap: Not set - DiffuseTexture: - SpecularTexture: - BumpTexture: - NormalTexture: - MetallicTexture: - RoughnessTexture: - AmbientOcclusionTexture: - EmissiveTexture: - BaseColorTexture: + Transl: < 2.410331, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.lodtest_lod3_optimized.UVMap +Node Path: RootNode.lodtest_lod1.lodtest_lod1_2.UVMap Node Type: MeshVertexUVData - UVs: Count 1984. Hash: 14119273880200542497 + UVs: Count 192. Hash: 27253578623892681 UVCustomName: UVMap -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod3_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 1984. Hash: 5664494957869921957 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod3_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 1984. Hash: 5048878728906162461 - TangentSpace: 1 - -Node Name: transform -Node Path: RootNode.lodtest_lod3_optimized.transform -Node Type: TransformData - Matrix: - BasisX: < 100.000000, 0.000000, 0.000000> - BasisY: < 0.000000, -0.000016, 100.000000> - BasisZ: < 0.000000, -100.000000, -0.000016> - Transl: < 0.000000, 2.298166, 0.000000> - Node Name: DefaultMaterial -Node Path: RootNode.lodtest_lod3_optimized.DefaultMaterial +Node Path: RootNode.lodtest_lod1.lodtest_lod1_2.DefaultMaterial Node Type: MaterialData MaterialName: DefaultMaterial UniqueId: 3809502407269006983 @@ -414,84 +697,26 @@ Node Type: MaterialData BaseColorTexture: Node Name: UVMap -Node Path: RootNode.lodtest_lod2_optimized.UVMap -Node Type: MeshVertexUVData - UVs: Count 240. Hash: 13702273589593616598 - UVCustomName: UVMap - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod2_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 240. Hash: 1390901212717410749 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod2_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 240. Hash: 1379238632949267281 - TangentSpace: 1 - -Node Name: transform -Node Path: RootNode.lodtest_lod2_optimized.transform -Node Type: TransformData - Matrix: - BasisX: < 100.000000, -0.000000, 0.000000> - BasisY: < 0.000000, -0.000016, 100.000000> - BasisZ: < 0.000000, -100.000000, -0.000016> - Transl: < 0.000000, -2.211498, 0.000000> - -Node Name: DefaultMaterial -Node Path: RootNode.lodtest_lod2_optimized.DefaultMaterial -Node Type: MaterialData - MaterialName: DefaultMaterial - UniqueId: 3809502407269006983 - IsNoDraw: false - DiffuseColor: < 0.800000, 0.800000, 0.800000> - SpecularColor: < 0.000000, 0.000000, 0.000000> - EmissiveColor: < 0.000000, 0.000000, 0.000000> - Opacity: 1.000000 - Shininess: 0.000000 - UseColorMap: Not set - BaseColor: Not set - UseMetallicMap: Not set - MetallicFactor: Not set - UseRoughnessMap: Not set - RoughnessFactor: Not set - UseEmissiveMap: Not set - EmissiveIntensity: Not set - UseAOMap: Not set - DiffuseTexture: - SpecularTexture: - BumpTexture: - NormalTexture: - MetallicTexture: - RoughnessTexture: - AmbientOcclusionTexture: - EmissiveTexture: - BaseColorTexture: - -Node Name: UVMap -Node Path: RootNode.lodtest_lod1_optimized.UVMap +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1_optimized.UVMap Node Type: MeshVertexUVData UVs: Count 192. Hash: 13790301632763350589 UVCustomName: UVMap -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod1_optimized.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1_optimized.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 192. Hash: 7293001660047850407 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.lodtest_lod1_optimized.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1_optimized.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 192. Hash: 2874689498270494796 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.lodtest_lod1_optimized.transform +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1_optimized.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -500,7 +725,7 @@ Node Type: TransformData Transl: < 2.410331, 0.000000, 0.000000> Node Name: DefaultMaterial -Node Path: RootNode.lodtest_lod1_optimized.DefaultMaterial +Node Path: RootNode.lodtest_lod1.lodtest_lod1_1_optimized.DefaultMaterial Node Type: MaterialData MaterialName: DefaultMaterial UniqueId: 3809502407269006983 @@ -528,4 +753,3 @@ Node Type: MaterialData AmbientOcclusionTexture: EmissiveTexture: BaseColorTexture: - diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/physicstest.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/physicstest.dbgsg index f620b6c35b..9b7ec58b7a 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/physicstest.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/physicstest.dbgsg @@ -1,32 +1,59 @@ ProductName: physicstest.dbgsg debugSceneGraphVersion: 1 physicstest -Node Name: Cone -Node Path: RootNode.Cone +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cone_1 +Node Path: RootNode.Cone.Cone_1 Node Type: MeshData Positions: Count 128. Hash: 7714223793259938211 Normals: Count 128. Hash: 2352668179264002707 FaceList: Count 62. Hash: 14563017593520122982 FaceMaterialIds: Count 62. Hash: 12234218120113875284 -Node Name: Cube_phys -Node Path: RootNode.Cube_phys -Node Type: MeshData - Positions: Count 24. Hash: 3478903613105670818 - Normals: Count 24. Hash: 7251512570672401149 - FaceList: Count 12. Hash: 9888799799190757436 - FaceMaterialIds: Count 12. Hash: 7110546404675862471 +Node Name: Cone_2 +Node Path: RootNode.Cone.Cone_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> -Node Name: Cone_optimized -Node Path: RootNode.Cone_optimized +Node Name: Cone_1_optimized +Node Path: RootNode.Cone.Cone_1_optimized Node Type: MeshData Positions: Count 128. Hash: 10174710861731544050 Normals: Count 128. Hash: 2352668179264002707 FaceList: Count 62. Hash: 11332459830831720586 FaceMaterialIds: Count 62. Hash: 12234218120113875284 +Node Name: Cube_phys_1 +Node Path: RootNode.Cube_phys.Cube_phys_1 +Node Type: MeshData + Positions: Count 24. Hash: 3478903613105670818 + Normals: Count 24. Hash: 7251512570672401149 + FaceList: Count 12. Hash: 9888799799190757436 + FaceMaterialIds: Count 12. Hash: 7110546404675862471 + +Node Name: Cube_phys_2 +Node Path: RootNode.Cube_phys.Cube_phys_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + Node Name: transform -Node Path: RootNode.Cone.transform +Node Path: RootNode.Cone.Cone_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -35,13 +62,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cone.UVMap +Node Path: RootNode.Cone.Cone_1.UVMap Node Type: MeshVertexUVData UVs: Count 128. Hash: 10171083346831193808 UVCustomName: UVMap Node Name: DefaultMaterial -Node Path: RootNode.Cone.DefaultMaterial +Node Path: RootNode.Cone.Cone_1.DefaultMaterial Node Type: MaterialData MaterialName: DefaultMaterial UniqueId: 3809502407269006983 @@ -70,21 +97,21 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cone.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cone.Cone_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 128. Hash: 14351734474754285313 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cone.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cone.Cone_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 128. Hash: 15997251922861304891 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cube_phys.transform +Node Path: RootNode.Cone.Cone_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -93,13 +120,116 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cube_phys.UVMap +Node Path: RootNode.Cone.Cone_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 128. Hash: 10171083346831193808 + UVCustomName: UVMap + +Node Name: DefaultMaterial +Node Path: RootNode.Cone.Cone_2.DefaultMaterial +Node Type: MaterialData + MaterialName: DefaultMaterial + UniqueId: 3809502407269006983 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 0.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: UVMap +Node Path: RootNode.Cone.Cone_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 128. Hash: 7873368003484215433 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.Cone.Cone_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 128. Hash: 12937806066914201637 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.Cone.Cone_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 128. Hash: 873786942732834087 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.Cone.Cone_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: DefaultMaterial +Node Path: RootNode.Cone.Cone_1_optimized.DefaultMaterial +Node Type: MaterialData + MaterialName: DefaultMaterial + UniqueId: 3809502407269006983 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 0.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: transform +Node Path: RootNode.Cube_phys.Cube_phys_1.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: UVMap +Node Path: RootNode.Cube_phys.Cube_phys_1.UVMap Node Type: MeshVertexUVData UVs: Count 24. Hash: 13623018071435219250 UVCustomName: UVMap Node Name: DefaultMaterial -Node Path: RootNode.Cube_phys.DefaultMaterial +Node Path: RootNode.Cube_phys.Cube_phys_1.DefaultMaterial Node Type: MaterialData MaterialName: DefaultMaterial UniqueId: 3809502407269006983 @@ -128,40 +258,21 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube_phys.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cube_phys.Cube_phys_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 24. Hash: 11965897353301448436 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube_phys.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cube_phys.Cube_phys_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 24. Hash: 17515781720544086759 - TangentSpace: 1 - -Node Name: UVMap -Node Path: RootNode.Cone_optimized.UVMap -Node Type: MeshVertexUVData - UVs: Count 128. Hash: 7873368003484215433 - UVCustomName: UVMap - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cone_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 128. Hash: 12937806066914201637 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cone_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 128. Hash: 873786942732834087 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cone_optimized.transform +Node Path: RootNode.Cube_phys.Cube_phys_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -169,8 +280,14 @@ Node Type: TransformData BasisZ: < 0.000000, -100.000000, -0.000016> Transl: < 0.000000, 0.000000, 0.000000> +Node Name: UVMap +Node Path: RootNode.Cube_phys.Cube_phys_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 13623018071435219250 + UVCustomName: UVMap + Node Name: DefaultMaterial -Node Path: RootNode.Cone_optimized.DefaultMaterial +Node Path: RootNode.Cube_phys.Cube_phys_2.DefaultMaterial Node Type: MaterialData MaterialName: DefaultMaterial UniqueId: 3809502407269006983 @@ -198,4 +315,3 @@ Node Type: MaterialData AmbientOcclusionTexture: EmissiveTexture: BaseColorTexture: - diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/multiple_mesh_linked_materials.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/multiple_mesh_linked_materials.dbgsg index 50f9285712..c0544ea3a9 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/multiple_mesh_linked_materials.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/multiple_mesh_linked_materials.dbgsg @@ -1,32 +1,59 @@ ProductName: multiple_mesh_linked_materials.dbgsg debugSceneGraphVersion: 1 multiple_mesh_linked_materials -Node Name: Cube -Node Path: RootNode.Cube +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1 +Node Path: RootNode.Cube.Cube_1 Node Type: MeshData Positions: Count 24. Hash: 8661923109306356285 Normals: Count 24. Hash: 5807525742165000561 FaceList: Count 12. Hash: 9888799799190757436 FaceMaterialIds: Count 12. Hash: 7113802799051126666 -Node Name: Cone -Node Path: RootNode.Cone +Node Name: Cube_2 +Node Path: RootNode.Cube.Cube_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1_optimized +Node Path: RootNode.Cube.Cube_1_optimized +Node Type: MeshData + Positions: Count 24. Hash: 8661923109306356285 + Normals: Count 24. Hash: 5807525742165000561 + FaceList: Count 12. Hash: 9888799799190757436 + FaceMaterialIds: Count 12. Hash: 7113802799051126666 + +Node Name: Cone_1 +Node Path: RootNode.Cone.Cone_1 Node Type: MeshData Positions: Count 128. Hash: 12506421592104186200 Normals: Count 128. Hash: 367461522682321485 FaceList: Count 62. Hash: 13208951979626973193 FaceMaterialIds: Count 62. Hash: 15454348664434923102 -Node Name: Cube_optimized -Node Path: RootNode.Cube_optimized -Node Type: MeshData - Positions: Count 24. Hash: 8661923109306356285 - Normals: Count 24. Hash: 5807525742165000561 - FaceList: Count 12. Hash: 9888799799190757436 - FaceMaterialIds: Count 12. Hash: 7113802799051126666 +Node Name: Cone_2 +Node Path: RootNode.Cone.Cone_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 2.000000> -Node Name: Cone_optimized -Node Path: RootNode.Cone_optimized +Node Name: Cone_1_optimized +Node Path: RootNode.Cone.Cone_1_optimized Node Type: MeshData Positions: Count 128. Hash: 14946490408303214595 Normals: Count 128. Hash: 367461522682321485 @@ -34,7 +61,7 @@ Node Type: MeshData FaceMaterialIds: Count 62. Hash: 15454348664434923102 Node Name: transform -Node Path: RootNode.Cube.transform +Node Path: RootNode.Cube.Cube_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -43,13 +70,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UV0 -Node Path: RootNode.Cube.UV0 +Node Path: RootNode.Cube.Cube_1.UV0 Node Type: MeshVertexUVData UVs: Count 24. Hash: 1622169145591646736 UVCustomName: UV0 Node Name: SharedBlack -Node Path: RootNode.Cube.SharedBlack +Node Path: RootNode.Cube.Cube_1.SharedBlack Node Type: MaterialData MaterialName: SharedBlack UniqueId: 5248829540156873090 @@ -79,7 +106,7 @@ Node Type: MaterialData BaseColorTexture: Node Name: SharedOrange -Node Path: RootNode.Cube.SharedOrange +Node Path: RootNode.Cube.Cube_1.SharedOrange Node Type: MaterialData MaterialName: SharedOrange UniqueId: 9470651048605569128 @@ -108,21 +135,184 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cone.transform +Node Path: RootNode.Cube.Cube_2.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: UV0 +Node Path: RootNode.Cube.Cube_2.UV0 +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UV0 + +Node Name: SharedBlack +Node Path: RootNode.Cube.Cube_2.SharedBlack +Node Type: MaterialData + MaterialName: SharedBlack + UniqueId: 5248829540156873090 + IsNoDraw: false + DiffuseColor: < 0.000000, 0.000000, 0.000000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: SharedOrange +Node Path: RootNode.Cube.Cube_2.SharedOrange +Node Type: MaterialData + MaterialName: SharedOrange + UniqueId: 9470651048605569128 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.139382, 0.014429> + SpecularColor: < 0.800000, 0.139382, 0.014429> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: UV0 +Node Path: RootNode.Cube.Cube_1_optimized.UV0 +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UV0 + +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 24. Hash: 13438447437797057049 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 24. Hash: 11372562338897179017 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.Cube.Cube_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: SharedBlack +Node Path: RootNode.Cube.Cube_1_optimized.SharedBlack +Node Type: MaterialData + MaterialName: SharedBlack + UniqueId: 5248829540156873090 + IsNoDraw: false + DiffuseColor: < 0.000000, 0.000000, 0.000000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: SharedOrange +Node Path: RootNode.Cube.Cube_1_optimized.SharedOrange +Node Type: MaterialData + MaterialName: SharedOrange + UniqueId: 9470651048605569128 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.139382, 0.014429> + SpecularColor: < 0.800000, 0.139382, 0.014429> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + +Node Name: transform +Node Path: RootNode.Cone.Cone_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -131,13 +321,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 2.000000> Node Name: UV0 -Node Path: RootNode.Cone.UV0 +Node Path: RootNode.Cone.Cone_1.UV0 Node Type: MeshVertexUVData UVs: Count 128. Hash: 10291654057525777310 UVCustomName: UV0 Node Name: SharedOrange -Node Path: RootNode.Cone.SharedOrange +Node Path: RootNode.Cone.Cone_1.SharedOrange Node Type: MaterialData MaterialName: SharedOrange UniqueId: 9470651048605569128 @@ -167,7 +357,7 @@ Node Type: MaterialData BaseColorTexture: Node Name: SharedBlack -Node Path: RootNode.Cone.SharedBlack +Node Path: RootNode.Cone.Cone_1.SharedBlack Node Type: MaterialData MaterialName: SharedBlack UniqueId: 5248829540156873090 @@ -196,79 +386,36 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cone.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cone.Cone_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 128. Hash: 12695232913942738512 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cone.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cone.Cone_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 128. Hash: 9034210764777745751 - TangentSpace: 1 - -Node Name: UV0 -Node Path: RootNode.Cube_optimized.UV0 -Node Type: MeshVertexUVData - UVs: Count 24. Hash: 1622169145591646736 - UVCustomName: UV0 - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cube_optimized.transform +Node Path: RootNode.Cone.Cone_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> BasisY: < 0.000000, -0.000016, 100.000000> BasisZ: < 0.000000, -100.000000, -0.000016> - Transl: < 0.000000, 0.000000, 0.000000> + Transl: < 0.000000, 0.000000, 2.000000> -Node Name: SharedBlack -Node Path: RootNode.Cube_optimized.SharedBlack -Node Type: MaterialData - MaterialName: SharedBlack - UniqueId: 5248829540156873090 - IsNoDraw: false - DiffuseColor: < 0.000000, 0.000000, 0.000000> - SpecularColor: < 0.000000, 0.000000, 0.000000> - EmissiveColor: < 0.000000, 0.000000, 0.000000> - Opacity: 1.000000 - Shininess: 25.000000 - UseColorMap: Not set - BaseColor: Not set - UseMetallicMap: Not set - MetallicFactor: Not set - UseRoughnessMap: Not set - RoughnessFactor: Not set - UseEmissiveMap: Not set - EmissiveIntensity: Not set - UseAOMap: Not set - DiffuseTexture: - SpecularTexture: - BumpTexture: - NormalTexture: - MetallicTexture: - RoughnessTexture: - AmbientOcclusionTexture: - EmissiveTexture: - BaseColorTexture: +Node Name: UV0 +Node Path: RootNode.Cone.Cone_2.UV0 +Node Type: MeshVertexUVData + UVs: Count 128. Hash: 10291654057525777310 + UVCustomName: UV0 Node Name: SharedOrange -Node Path: RootNode.Cube_optimized.SharedOrange +Node Path: RootNode.Cone.Cone_2.SharedOrange Node Type: MaterialData MaterialName: SharedOrange UniqueId: 9470651048605569128 @@ -297,27 +444,57 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: +Node Name: SharedBlack +Node Path: RootNode.Cone.Cone_2.SharedBlack +Node Type: MaterialData + MaterialName: SharedBlack + UniqueId: 5248829540156873090 + IsNoDraw: false + DiffuseColor: < 0.000000, 0.000000, 0.000000> + SpecularColor: < 0.000000, 0.000000, 0.000000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: + Node Name: UV0 -Node Path: RootNode.Cone_optimized.UV0 +Node Path: RootNode.Cone.Cone_1_optimized.UV0 Node Type: MeshVertexUVData UVs: Count 128. Hash: 7173974213247584731 UVCustomName: UV0 -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cone_optimized.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cone.Cone_1_optimized.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 128. Hash: 10740776669168782230 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cone_optimized.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cone.Cone_1_optimized.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 128. Hash: 6990068477421150065 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cone_optimized.transform +Node Path: RootNode.Cone.Cone_1_optimized.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -326,7 +503,7 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 2.000000> Node Name: SharedOrange -Node Path: RootNode.Cone_optimized.SharedOrange +Node Path: RootNode.Cone.Cone_1_optimized.SharedOrange Node Type: MaterialData MaterialName: SharedOrange UniqueId: 9470651048605569128 @@ -356,7 +533,7 @@ Node Type: MaterialData BaseColorTexture: Node Name: SharedBlack -Node Path: RootNode.Cone_optimized.SharedBlack +Node Path: RootNode.Cone.Cone_1_optimized.SharedBlack Node Type: MaterialData MaterialName: SharedBlack UniqueId: 5248829540156873090 @@ -384,4 +561,3 @@ Node Type: MaterialData AmbientOcclusionTexture: EmissiveTexture: BaseColorTexture: - diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/multiple_mesh_one_material.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/multiple_mesh_one_material.dbgsg index d341bcdb70..48135760f2 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/multiple_mesh_one_material.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/multiple_mesh_one_material.dbgsg @@ -1,32 +1,59 @@ ProductName: multiple_mesh_one_material.dbgsg debugSceneGraphVersion: 1 multiple_mesh_one_material -Node Name: Cube -Node Path: RootNode.Cube +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1 +Node Path: RootNode.Cube.Cube_1 Node Type: MeshData Positions: Count 24. Hash: 8661923109306356285 Normals: Count 24. Hash: 5807525742165000561 FaceList: Count 12. Hash: 9888799799190757436 FaceMaterialIds: Count 12. Hash: 7110546404675862471 -Node Name: Cylinder -Node Path: RootNode.Cylinder +Node Name: Cube_2 +Node Path: RootNode.Cube.Cube_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1_optimized +Node Path: RootNode.Cube.Cube_1_optimized +Node Type: MeshData + Positions: Count 24. Hash: 8661923109306356285 + Normals: Count 24. Hash: 5807525742165000561 + FaceList: Count 12. Hash: 9888799799190757436 + FaceMaterialIds: Count 12. Hash: 7110546404675862471 + +Node Name: Cylinder_1 +Node Path: RootNode.Cylinder.Cylinder_1 Node Type: MeshData Positions: Count 192. Hash: 1283526254311745349 Normals: Count 192. Hash: 1873340970602844856 FaceList: Count 124. Hash: 3728991722746136013 FaceMaterialIds: Count 124. Hash: 2372486708814455910 -Node Name: Cube_optimized -Node Path: RootNode.Cube_optimized -Node Type: MeshData - Positions: Count 24. Hash: 8661923109306356285 - Normals: Count 24. Hash: 5807525742165000561 - FaceList: Count 12. Hash: 9888799799190757436 - FaceMaterialIds: Count 12. Hash: 7110546404675862471 +Node Name: Cylinder_2 +Node Path: RootNode.Cylinder.Cylinder_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: <-4.388482, 0.000000, 0.000000> -Node Name: Cylinder_optimized -Node Path: RootNode.Cylinder_optimized +Node Name: Cylinder_1_optimized +Node Path: RootNode.Cylinder.Cylinder_1_optimized Node Type: MeshData Positions: Count 192. Hash: 7921557352486854444 Normals: Count 192. Hash: 1873340970602844856 @@ -34,7 +61,7 @@ Node Type: MeshData FaceMaterialIds: Count 124. Hash: 2372486708814455910 Node Name: transform -Node Path: RootNode.Cube.transform +Node Path: RootNode.Cube.Cube_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -43,13 +70,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cube.UVMap +Node Path: RootNode.Cube.Cube_1.UVMap Node Type: MeshVertexUVData UVs: Count 24. Hash: 1622169145591646736 UVCustomName: UVMap Node Name: SingleMaterial -Node Path: RootNode.Cube.SingleMaterial +Node Path: RootNode.Cube.Cube_1.SingleMaterial Node Type: MaterialData MaterialName: SingleMaterial UniqueId: 14432700632681398127 @@ -78,21 +105,124 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: TwoMeshOneMaterial/FBXTestTexture.png -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cylinder.transform +Node Path: RootNode.Cube.Cube_2.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: UVMap +Node Path: RootNode.Cube.Cube_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: SingleMaterial +Node Path: RootNode.Cube.Cube_2.SingleMaterial +Node Type: MaterialData + MaterialName: SingleMaterial + UniqueId: 14432700632681398127 + IsNoDraw: false + DiffuseColor: < 0.814049, 0.814049, 0.814049> + SpecularColor: < 0.814049, 0.814049, 0.814049> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: TwoMeshOneMaterial/FBXTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: TwoMeshOneMaterial/FBXTestTexture.png + +Node Name: UVMap +Node Path: RootNode.Cube.Cube_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 24. Hash: 13438447437797057049 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 24. Hash: 11372562338897179017 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.Cube.Cube_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: SingleMaterial +Node Path: RootNode.Cube.Cube_1_optimized.SingleMaterial +Node Type: MaterialData + MaterialName: SingleMaterial + UniqueId: 14432700632681398127 + IsNoDraw: false + DiffuseColor: < 0.814049, 0.814049, 0.814049> + SpecularColor: < 0.814049, 0.814049, 0.814049> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: TwoMeshOneMaterial/FBXTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: TwoMeshOneMaterial/FBXTestTexture.png + +Node Name: transform +Node Path: RootNode.Cylinder.Cylinder_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -101,13 +231,13 @@ Node Type: TransformData Transl: <-4.388482, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cylinder.UVMap +Node Path: RootNode.Cylinder.Cylinder_1.UVMap Node Type: MeshVertexUVData UVs: Count 192. Hash: 27253578623892681 UVCustomName: UVMap Node Name: SingleMaterial -Node Path: RootNode.Cylinder.SingleMaterial +Node Path: RootNode.Cylinder.Cylinder_1.SingleMaterial Node Type: MaterialData MaterialName: SingleMaterial UniqueId: 14432700632681398127 @@ -136,49 +266,36 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: TwoMeshOneMaterial/FBXTestTexture.png -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cylinder.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 192. Hash: 11165448242141781141 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cylinder.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 192. Hash: 7987814487334449536 - TangentSpace: 1 - -Node Name: UVMap -Node Path: RootNode.Cube_optimized.UVMap -Node Type: MeshVertexUVData - UVs: Count 24. Hash: 1622169145591646736 - UVCustomName: UVMap - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cube_optimized.transform +Node Path: RootNode.Cylinder.Cylinder_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> BasisY: < 0.000000, -0.000016, 100.000000> BasisZ: < 0.000000, -100.000000, -0.000016> - Transl: < 0.000000, 0.000000, 0.000000> + Transl: <-4.388482, 0.000000, 0.000000> + +Node Name: UVMap +Node Path: RootNode.Cylinder.Cylinder_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 27253578623892681 + UVCustomName: UVMap Node Name: SingleMaterial -Node Path: RootNode.Cube_optimized.SingleMaterial +Node Path: RootNode.Cylinder.Cylinder_2.SingleMaterial Node Type: MaterialData MaterialName: SingleMaterial UniqueId: 14432700632681398127 @@ -208,26 +325,26 @@ Node Type: MaterialData BaseColorTexture: TwoMeshOneMaterial/FBXTestTexture.png Node Name: UVMap -Node Path: RootNode.Cylinder_optimized.UVMap +Node Path: RootNode.Cylinder.Cylinder_1_optimized.UVMap Node Type: MeshVertexUVData UVs: Count 192. Hash: 13790301632763350589 UVCustomName: UVMap -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cylinder_optimized.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1_optimized.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 192. Hash: 7293001660047850407 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cylinder_optimized.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1_optimized.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 192. Hash: 2874689498270494796 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cylinder_optimized.transform +Node Path: RootNode.Cylinder.Cylinder_1_optimized.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -236,7 +353,7 @@ Node Type: TransformData Transl: <-4.388482, 0.000000, 0.000000> Node Name: SingleMaterial -Node Path: RootNode.Cylinder_optimized.SingleMaterial +Node Path: RootNode.Cylinder.Cylinder_1_optimized.SingleMaterial Node Type: MaterialData MaterialName: SingleMaterial UniqueId: 14432700632681398127 @@ -264,4 +381,3 @@ Node Type: MaterialData AmbientOcclusionTexture: EmissiveTexture: BaseColorTexture: TwoMeshOneMaterial/FBXTestTexture.png - diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material.dbgsg index b2d8df6f4a..c5587a2778 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material.dbgsg @@ -1,32 +1,59 @@ ProductName: multiple_mesh_multiple_material.dbgsg debugSceneGraphVersion: 1 multiple_mesh_multiple_material -Node Name: Cube -Node Path: RootNode.Cube +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1 +Node Path: RootNode.Cube.Cube_1 Node Type: MeshData Positions: Count 24. Hash: 8661923109306356285 Normals: Count 24. Hash: 5807525742165000561 FaceList: Count 12. Hash: 9888799799190757436 FaceMaterialIds: Count 12. Hash: 7110546404675862471 -Node Name: Cylinder -Node Path: RootNode.Cylinder +Node Name: Cube_2 +Node Path: RootNode.Cube.Cube_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1_optimized +Node Path: RootNode.Cube.Cube_1_optimized +Node Type: MeshData + Positions: Count 24. Hash: 8661923109306356285 + Normals: Count 24. Hash: 5807525742165000561 + FaceList: Count 12. Hash: 9888799799190757436 + FaceMaterialIds: Count 12. Hash: 7110546404675862471 + +Node Name: Cylinder_1 +Node Path: RootNode.Cylinder.Cylinder_1 Node Type: MeshData Positions: Count 192. Hash: 1283526254311745349 Normals: Count 192. Hash: 1873340970602844856 FaceList: Count 124. Hash: 3728991722746136013 FaceMaterialIds: Count 124. Hash: 2372486708814455910 -Node Name: Cube_optimized -Node Path: RootNode.Cube_optimized -Node Type: MeshData - Positions: Count 24. Hash: 8661923109306356285 - Normals: Count 24. Hash: 5807525742165000561 - FaceList: Count 12. Hash: 9888799799190757436 - FaceMaterialIds: Count 12. Hash: 7110546404675862471 +Node Name: Cylinder_2 +Node Path: RootNode.Cylinder.Cylinder_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: <-4.388482, 0.000000, 0.000000> -Node Name: Cylinder_optimized -Node Path: RootNode.Cylinder_optimized +Node Name: Cylinder_1_optimized +Node Path: RootNode.Cylinder.Cylinder_1_optimized Node Type: MeshData Positions: Count 192. Hash: 7921557352486854444 Normals: Count 192. Hash: 1873340970602844856 @@ -34,7 +61,7 @@ Node Type: MeshData FaceMaterialIds: Count 124. Hash: 2372486708814455910 Node Name: transform -Node Path: RootNode.Cube.transform +Node Path: RootNode.Cube.Cube_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -43,13 +70,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cube.UVMap +Node Path: RootNode.Cube.Cube_1.UVMap Node Type: MeshVertexUVData UVs: Count 24. Hash: 1622169145591646736 UVCustomName: UVMap Node Name: SingleMaterial -Node Path: RootNode.Cube.SingleMaterial +Node Path: RootNode.Cube.Cube_1.SingleMaterial Node Type: MaterialData MaterialName: SingleMaterial UniqueId: 14432700632681398127 @@ -78,21 +105,124 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: TwoMeshTwoMaterial/FBXTestTexture.png -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cylinder.transform +Node Path: RootNode.Cube.Cube_2.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: UVMap +Node Path: RootNode.Cube.Cube_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: SingleMaterial +Node Path: RootNode.Cube.Cube_2.SingleMaterial +Node Type: MaterialData + MaterialName: SingleMaterial + UniqueId: 14432700632681398127 + IsNoDraw: false + DiffuseColor: < 0.814049, 0.814049, 0.814049> + SpecularColor: < 0.814049, 0.814049, 0.814049> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: TwoMeshTwoMaterial/FBXTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: TwoMeshTwoMaterial/FBXTestTexture.png + +Node Name: UVMap +Node Path: RootNode.Cube.Cube_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 24. Hash: 13438447437797057049 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 24. Hash: 11372562338897179017 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.Cube.Cube_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: SingleMaterial +Node Path: RootNode.Cube.Cube_1_optimized.SingleMaterial +Node Type: MaterialData + MaterialName: SingleMaterial + UniqueId: 14432700632681398127 + IsNoDraw: false + DiffuseColor: < 0.814049, 0.814049, 0.814049> + SpecularColor: < 0.814049, 0.814049, 0.814049> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: TwoMeshTwoMaterial/FBXTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: TwoMeshTwoMaterial/FBXTestTexture.png + +Node Name: transform +Node Path: RootNode.Cylinder.Cylinder_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -101,13 +231,13 @@ Node Type: TransformData Transl: <-4.388482, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cylinder.UVMap +Node Path: RootNode.Cylinder.Cylinder_1.UVMap Node Type: MeshVertexUVData UVs: Count 192. Hash: 27253578623892681 UVCustomName: UVMap Node Name: SecondMaterial -Node Path: RootNode.Cylinder.SecondMaterial +Node Path: RootNode.Cylinder.Cylinder_1.SecondMaterial Node Type: MaterialData MaterialName: SecondMaterial UniqueId: 5229255358802505087 @@ -136,55 +266,42 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: TwoMeshTwoMaterial/FBXSecondTestTexture.png -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cylinder.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 192. Hash: 11165448242141781141 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cylinder.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 192. Hash: 7987814487334449536 - TangentSpace: 1 - -Node Name: UVMap -Node Path: RootNode.Cube_optimized.UVMap -Node Type: MeshVertexUVData - UVs: Count 24. Hash: 1622169145591646736 - UVCustomName: UVMap - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cube_optimized.transform +Node Path: RootNode.Cylinder.Cylinder_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> BasisY: < 0.000000, -0.000016, 100.000000> BasisZ: < 0.000000, -100.000000, -0.000016> - Transl: < 0.000000, 0.000000, 0.000000> + Transl: <-4.388482, 0.000000, 0.000000> -Node Name: SingleMaterial -Node Path: RootNode.Cube_optimized.SingleMaterial +Node Name: UVMap +Node Path: RootNode.Cylinder.Cylinder_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 27253578623892681 + UVCustomName: UVMap + +Node Name: SecondMaterial +Node Path: RootNode.Cylinder.Cylinder_2.SecondMaterial Node Type: MaterialData - MaterialName: SingleMaterial - UniqueId: 14432700632681398127 + MaterialName: SecondMaterial + UniqueId: 5229255358802505087 IsNoDraw: false - DiffuseColor: < 0.814049, 0.814049, 0.814049> - SpecularColor: < 0.814049, 0.814049, 0.814049> + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.800000, 0.800000, 0.800000> EmissiveColor: < 0.000000, 0.000000, 0.000000> Opacity: 1.000000 Shininess: 25.000000 @@ -197,7 +314,7 @@ Node Type: MaterialData UseEmissiveMap: Not set EmissiveIntensity: Not set UseAOMap: Not set - DiffuseTexture: TwoMeshTwoMaterial/FBXTestTexture.png + DiffuseTexture: TwoMeshTwoMaterial/FBXSecondTestTexture.png SpecularTexture: BumpTexture: NormalTexture: @@ -205,29 +322,29 @@ Node Type: MaterialData RoughnessTexture: AmbientOcclusionTexture: EmissiveTexture: - BaseColorTexture: TwoMeshTwoMaterial/FBXTestTexture.png + BaseColorTexture: TwoMeshTwoMaterial/FBXSecondTestTexture.png Node Name: UVMap -Node Path: RootNode.Cylinder_optimized.UVMap +Node Path: RootNode.Cylinder.Cylinder_1_optimized.UVMap Node Type: MeshVertexUVData UVs: Count 192. Hash: 13790301632763350589 UVCustomName: UVMap -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cylinder_optimized.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1_optimized.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 192. Hash: 7293001660047850407 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cylinder_optimized.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1_optimized.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 192. Hash: 2874689498270494796 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cylinder_optimized.transform +Node Path: RootNode.Cylinder.Cylinder_1_optimized.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -236,7 +353,7 @@ Node Type: TransformData Transl: <-4.388482, 0.000000, 0.000000> Node Name: SecondMaterial -Node Path: RootNode.Cylinder_optimized.SecondMaterial +Node Path: RootNode.Cylinder.Cylinder_1_optimized.SecondMaterial Node Type: MaterialData MaterialName: SecondMaterial UniqueId: 5229255358802505087 @@ -264,4 +381,3 @@ Node Type: MaterialData AmbientOcclusionTexture: EmissiveTexture: BaseColorTexture: TwoMeshTwoMaterial/FBXSecondTestTexture.png - diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material_override.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material_override.dbgsg index 6c6b6d552d..8426a68d3b 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material_override.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material_override.dbgsg @@ -1,32 +1,59 @@ ProductName: multiple_mesh_multiple_material.dbgsg debugSceneGraphVersion: 1 multiple_mesh_multiple_material -Node Name: Cube -Node Path: RootNode.Cube +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1 +Node Path: RootNode.Cube.Cube_1 Node Type: MeshData Positions: Count 24. Hash: 8661923109306356285 Normals: Count 24. Hash: 5807525742165000561 FaceList: Count 12. Hash: 9888799799190757436 FaceMaterialIds: Count 12. Hash: 7110546404675862471 -Node Name: Cylinder -Node Path: RootNode.Cylinder +Node Name: Cube_2 +Node Path: RootNode.Cube.Cube_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1_optimized +Node Path: RootNode.Cube.Cube_1_optimized +Node Type: MeshData + Positions: Count 24. Hash: 8661923109306356285 + Normals: Count 24. Hash: 5807525742165000561 + FaceList: Count 12. Hash: 9888799799190757436 + FaceMaterialIds: Count 12. Hash: 7110546404675862471 + +Node Name: Cylinder_1 +Node Path: RootNode.Cylinder.Cylinder_1 Node Type: MeshData Positions: Count 192. Hash: 1283526254311745349 Normals: Count 192. Hash: 1873340970602844856 FaceList: Count 124. Hash: 3728991722746136013 FaceMaterialIds: Count 124. Hash: 2372486708814455910 -Node Name: Cube_optimized -Node Path: RootNode.Cube_optimized -Node Type: MeshData - Positions: Count 24. Hash: 8661923109306356285 - Normals: Count 24. Hash: 5807525742165000561 - FaceList: Count 12. Hash: 9888799799190757436 - FaceMaterialIds: Count 12. Hash: 7110546404675862471 +Node Name: Cylinder_2 +Node Path: RootNode.Cylinder.Cylinder_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: <-4.388482, 0.000000, 0.000000> Node Name: transform -Node Path: RootNode.Cube.transform +Node Path: RootNode.Cube.Cube_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -35,13 +62,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cube.UVMap +Node Path: RootNode.Cube.Cube_1.UVMap Node Type: MeshVertexUVData UVs: Count 24. Hash: 1622169145591646736 UVCustomName: UVMap Node Name: SingleMaterial -Node Path: RootNode.Cube.SingleMaterial +Node Path: RootNode.Cube.Cube_1.SingleMaterial Node Type: MaterialData MaterialName: SingleMaterial UniqueId: 14432700632681398127 @@ -70,21 +97,124 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: TwoMeshTwoMaterial/FBXTestTexture.png -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cylinder.transform +Node Path: RootNode.Cube.Cube_2.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: UVMap +Node Path: RootNode.Cube.Cube_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: SingleMaterial +Node Path: RootNode.Cube.Cube_2.SingleMaterial +Node Type: MaterialData + MaterialName: SingleMaterial + UniqueId: 14432700632681398127 + IsNoDraw: false + DiffuseColor: < 0.814049, 0.814049, 0.814049> + SpecularColor: < 0.814049, 0.814049, 0.814049> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: TwoMeshTwoMaterial/FBXTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: TwoMeshTwoMaterial/FBXTestTexture.png + +Node Name: UVMap +Node Path: RootNode.Cube.Cube_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 24. Hash: 1622169145591646736 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 24. Hash: 13438447437797057049 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 24. Hash: 11372562338897179017 + GenerationMethod: 1 + +Node Name: transform +Node Path: RootNode.Cube.Cube_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: SingleMaterial +Node Path: RootNode.Cube.Cube_1_optimized.SingleMaterial +Node Type: MaterialData + MaterialName: SingleMaterial + UniqueId: 14432700632681398127 + IsNoDraw: false + DiffuseColor: < 0.814049, 0.814049, 0.814049> + SpecularColor: < 0.814049, 0.814049, 0.814049> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 25.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: TwoMeshTwoMaterial/FBXTestTexture.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: TwoMeshTwoMaterial/FBXTestTexture.png + +Node Name: transform +Node Path: RootNode.Cylinder.Cylinder_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -93,13 +223,13 @@ Node Type: TransformData Transl: <-4.388482, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cylinder.UVMap +Node Path: RootNode.Cylinder.Cylinder_1.UVMap Node Type: MeshVertexUVData UVs: Count 192. Hash: 27253578623892681 UVCustomName: UVMap Node Name: SecondMaterial -Node Path: RootNode.Cylinder.SecondMaterial +Node Path: RootNode.Cylinder.Cylinder_1.SecondMaterial Node Type: MaterialData MaterialName: SecondMaterial UniqueId: 5229255358802505087 @@ -128,55 +258,42 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: TwoMeshTwoMaterial/FBXSecondTestTexture.png -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cylinder.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 192. Hash: 11165448242141781141 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cylinder.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cylinder.Cylinder_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 192. Hash: 7987814487334449536 - TangentSpace: 1 - -Node Name: UVMap -Node Path: RootNode.Cube_optimized.UVMap -Node Type: MeshVertexUVData - UVs: Count 24. Hash: 1622169145591646736 - UVCustomName: UVMap - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 24. Hash: 13438447437797057049 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 24. Hash: 11372562338897179017 - TangentSpace: 1 + GenerationMethod: 1 Node Name: transform -Node Path: RootNode.Cube_optimized.transform +Node Path: RootNode.Cylinder.Cylinder_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> BasisY: < 0.000000, -0.000016, 100.000000> BasisZ: < 0.000000, -100.000000, -0.000016> - Transl: < 0.000000, 0.000000, 0.000000> + Transl: <-4.388482, 0.000000, 0.000000> -Node Name: SingleMaterial -Node Path: RootNode.Cube_optimized.SingleMaterial +Node Name: UVMap +Node Path: RootNode.Cylinder.Cylinder_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 27253578623892681 + UVCustomName: UVMap + +Node Name: SecondMaterial +Node Path: RootNode.Cylinder.Cylinder_2.SecondMaterial Node Type: MaterialData - MaterialName: SingleMaterial - UniqueId: 14432700632681398127 + MaterialName: SecondMaterial + UniqueId: 5229255358802505087 IsNoDraw: false - DiffuseColor: < 0.814049, 0.814049, 0.814049> - SpecularColor: < 0.814049, 0.814049, 0.814049> + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.800000, 0.800000, 0.800000> EmissiveColor: < 0.000000, 0.000000, 0.000000> Opacity: 1.000000 Shininess: 25.000000 @@ -189,7 +306,7 @@ Node Type: MaterialData UseEmissiveMap: Not set EmissiveIntensity: Not set UseAOMap: Not set - DiffuseTexture: TwoMeshTwoMaterial/FBXTestTexture.png + DiffuseTexture: TwoMeshTwoMaterial/FBXSecondTestTexture.png SpecularTexture: BumpTexture: NormalTexture: @@ -197,5 +314,4 @@ Node Type: MaterialData RoughnessTexture: AmbientOcclusionTexture: EmissiveTexture: - BaseColorTexture: TwoMeshTwoMaterial/FBXTestTexture.png - + BaseColorTexture: TwoMeshTwoMaterial/FBXSecondTestTexture.png diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/vertexcolor.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/vertexcolor.dbgsg index 990e8ff628..8ccccfca4b 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/vertexcolor.dbgsg +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/vertexcolor.dbgsg @@ -1,30 +1,48 @@ ProductName: vertexcolor.dbgsg debugSceneGraphVersion: 1 vertexcolor -Node Name: Cube -Node Path: RootNode.Cube +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1 +Node Path: RootNode.Cube.Cube_1 Node Type: MeshData Positions: Count 24576. Hash: 7031773714680283213 Normals: Count 24576. Hash: 8968157737282745201 FaceList: Count 12288. Hash: 13183441914179219962 FaceMaterialIds: Count 12288. Hash: 12545154121625736090 -Node Name: Cube_optimized -Node Path: RootNode.Cube_optimized +Node Name: Cube_2 +Node Path: RootNode.Cube.Cube_2 +Node Type: BoneData + WorldTransform: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Cube_1_optimized +Node Path: RootNode.Cube.Cube_1_optimized Node Type: MeshData - Positions: Count 24576. Hash: 7031773714680283213 - Normals: Count 24576. Hash: 8968157737282745201 - FaceList: Count 12288. Hash: 13183441914179219962 + Positions: Count 6376. Hash: 10806296444120211070 + Normals: Count 6376. Hash: 3814626075063770280 + FaceList: Count 12288. Hash: 15242182080304859208 FaceMaterialIds: Count 12288. Hash: 12545154121625736090 Node Name: Col0 -Node Path: RootNode.Cube.Col0 +Node Path: RootNode.Cube.Cube_1.Col0 Node Type: MeshVertexColorData Colors: Count 24576. Hash: 17169952715183318502 ColorsCustomName: Node Name: transform -Node Path: RootNode.Cube.transform +Node Path: RootNode.Cube.Cube_1.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -33,13 +51,13 @@ Node Type: TransformData Transl: < 0.000000, 0.000000, 0.000000> Node Name: UVMap -Node Path: RootNode.Cube.UVMap +Node Path: RootNode.Cube.Cube_1.UVMap Node Type: MeshVertexUVData UVs: Count 24576. Hash: 4554678369329207802 UVCustomName: UVMap Node Name: Material -Node Path: RootNode.Cube.Material +Node Path: RootNode.Cube.Cube_1.Material Node Type: MaterialData MaterialName: Material UniqueId: 11127505492038345244 @@ -68,46 +86,27 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube.TangentSet_MikkT_0 +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1.TangentSet_0 Node Type: MeshVertexTangentData Tangents: Count 24576. Hash: 13321090379606717973 - TangentSpace: 1 + GenerationMethod: 1 SetIndex: 0 -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube.BitangentSet_MikkT_0 +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1.BitangentSet_0 Node Type: MeshVertexBitangentData Bitangents: Count 24576. Hash: 17217515414004886507 - TangentSpace: 1 - -Node Name: UVMap -Node Path: RootNode.Cube_optimized.UVMap -Node Type: MeshVertexUVData - UVs: Count 24576. Hash: 4554678369329207802 - UVCustomName: UVMap - -Node Name: TangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.TangentSet_MikkT_0 -Node Type: MeshVertexTangentData - Tangents: Count 24576. Hash: 13321090379606717973 - TangentSpace: 1 - SetIndex: 0 - -Node Name: BitangentSet_MikkT_0 -Node Path: RootNode.Cube_optimized.BitangentSet_MikkT_0 -Node Type: MeshVertexBitangentData - Bitangents: Count 24576. Hash: 17217515414004886507 - TangentSpace: 1 + GenerationMethod: 1 Node Name: Col0 -Node Path: RootNode.Cube_optimized.Col0 +Node Path: RootNode.Cube.Cube_2.Col0 Node Type: MeshVertexColorData Colors: Count 24576. Hash: 17169952715183318502 ColorsCustomName: Node Name: transform -Node Path: RootNode.Cube_optimized.transform +Node Path: RootNode.Cube.Cube_2.transform Node Type: TransformData Matrix: BasisX: < 100.000000, 0.000000, 0.000000> @@ -115,8 +114,14 @@ Node Type: TransformData BasisZ: < 0.000000, -100.000000, -0.000016> Transl: < 0.000000, 0.000000, 0.000000> +Node Name: UVMap +Node Path: RootNode.Cube.Cube_2.UVMap +Node Type: MeshVertexUVData + UVs: Count 24576. Hash: 4554678369329207802 + UVCustomName: UVMap + Node Name: Material -Node Path: RootNode.Cube_optimized.Material +Node Path: RootNode.Cube.Cube_2.Material Node Type: MaterialData MaterialName: Material UniqueId: 11127505492038345244 @@ -145,3 +150,66 @@ Node Type: MaterialData EmissiveTexture: BaseColorTexture: +Node Name: UVMap +Node Path: RootNode.Cube.Cube_1_optimized.UVMap +Node Type: MeshVertexUVData + UVs: Count 6376. Hash: 12957930967905951851 + UVCustomName: UVMap + +Node Name: TangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 6376. Hash: 7712841033379094373 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.Cube.Cube_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 6376. Hash: 12547048737213169362 + GenerationMethod: 1 + +Node Name: Col0 +Node Path: RootNode.Cube.Cube_1_optimized.Col0 +Node Type: MeshVertexColorData + Colors: Count 6376. Hash: 8761962599807935159 + ColorsCustomName: + +Node Name: transform +Node Path: RootNode.Cube.Cube_1_optimized.transform +Node Type: TransformData + Matrix: + BasisX: < 100.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000016, 100.000000> + BasisZ: < 0.000000, -100.000000, -0.000016> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Material +Node Path: RootNode.Cube.Cube_1_optimized.Material +Node Type: MaterialData + MaterialName: Material + UniqueId: 11127505492038345244 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.800000, 0.800000, 0.800000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 36.000000 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py index 5cb61da68e..71f200074c 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py @@ -67,7 +67,7 @@ blackbox_fbx_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=0, + warning_count=1, products = [ asset_db_utils.DBProduct( product_name='onemeshonematerial/onemeshonematerial.dbgsg', @@ -99,7 +99,7 @@ blackbox_fbx_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=9, + warning_count=22, products = [ asset_db_utils.DBProduct( product_name='softnaminglod/lodtest.dbgsg', @@ -131,7 +131,7 @@ blackbox_fbx_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=6, + warning_count=14, products = [ asset_db_utils.DBProduct( product_name='softnamingphysics/physicstest.dbgsg', @@ -165,7 +165,7 @@ blackbox_fbx_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=0, + warning_count=2, products = [ asset_db_utils.DBProduct( product_name='twomeshonematerial/multiple_mesh_one_material.dbgsg', @@ -197,7 +197,7 @@ blackbox_fbx_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=0, + warning_count=2, products= [ asset_db_utils.DBProduct( product_name='twomeshlinkedmaterials/multiple_mesh_linked_materials.dbgsg', @@ -230,7 +230,7 @@ blackbox_fbx_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=0, + warning_count=1, products = [ asset_db_utils.DBProduct( product_name='onemeshmultiplematerials/single_mesh_multiple_materials.dbgsg', @@ -260,7 +260,7 @@ blackbox_fbx_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=0, + warning_count=1, products=[ asset_db_utils.DBProduct( product_name='vertexcolor/vertexcolor.dbgsg', @@ -275,6 +275,38 @@ blackbox_fbx_tests = [ id="35796285", marks=pytest.mark.test_case_id("C35796285"), ), + pytest.param( + BlackboxAssetTest( + test_name= "MotionTest_RunAP_SuccessWithMatchingProducts", + asset_folder= "Motion", + scene_debug_file="Jack_Idle_Aim_ZUp.dbgsg", + assets = [ + asset_db_utils.DBSourceAsset( + source_file_name = "Jack_Idle_Aim_ZUp.fbx", + uuid = b"eda904ae0e145f8b973d57fc5809918b", + jobs = [ + asset_db_utils.DBJob( + job_key= "Scene compilation", + builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", + status=4, + error_count=0, + warning_count=0, + products = [ + asset_db_utils.DBProduct( + product_name='motion/jack_idle_aim_zup.dbgsg', + sub_id=-517610290, + asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'), + asset_db_utils.DBProduct( + product_name='motion/jack_idle_aim_zup.motion', + sub_id=186392073, + asset_type=b'00494b8e75784ba28b28272e90680787') + ] + ), + ] + ) + ] + ), + ), ] @@ -296,7 +328,7 @@ blackbox_fbx_special_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=0, + warning_count=2, products = [ asset_db_utils.DBProduct( product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg', @@ -317,7 +349,7 @@ blackbox_fbx_special_tests = [ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, - warning_count=0, + warning_count=2, products = [ asset_db_utils.DBProduct( product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg', @@ -387,7 +419,6 @@ class TestsFBX_AllPlatforms(object): self.run_fbx_test(workspace, ap_setup_fixture, asset_processor, project, blackbox_param, True) - def populateAssetInfo(self, workspace, project, assets): # Check that each given source asset resulted in the expected jobs and products. @@ -398,7 +429,6 @@ class TestsFBX_AllPlatforms(object): product.product_name = job.platform + "/" \ + product.product_name - def run_fbx_test(self, workspace, ap_setup_fixture, asset_processor, project, blackbox_params: BlackboxAssetTest, overrideAsset = False): """ From 7ddcdffed7df6bc4c4dbeb47593dfe14bb8ba27d Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Wed, 20 Oct 2021 11:15:24 -0700 Subject: [PATCH 056/152] Move Qt Toast Notifications from GraphCanvas into Framework Move the existing Qt Toast Notification QWidgets, EBuses and logic from the GraphCanvas gem into AzQtComponents and AzToolsFramework so they can be re-used. Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Components}/ToastNotification.cpp | 51 ++--- .../Components}/ToastNotification.h | 36 ++-- .../Components}/ToastNotification.ui | 9 +- .../ToastNotificationConfiguration.cpp | 18 ++ .../ToastNotificationConfiguration.h | 46 +++++ .../AzQtComponents/azqtcomponents_files.cmake | 5 + .../UI/Notifications/ToastBus.h | 91 +++++++++ .../Notifications/ToastNotificationsView.cpp | 180 ++++++++++++++++++ .../UI/Notifications/ToastNotificationsView.h | 65 +++++++ .../aztoolsframework_files.cmake | 3 + .../Connections/ConnectionComponent.cpp | 8 +- .../Connections/ConnectionComponent.h | 3 +- .../Slots/Data/DataSlotLayoutComponent.cpp | 6 +- .../Slots/Data/DataSlotLayoutComponent.h | 3 +- .../GraphCanvas/Components/ToastBus.h | 29 --- .../GraphCanvas/Components/ViewBus.h | 9 +- .../GraphCanvas/Editor/EditorTypes.h | 92 --------- .../GraphCanvasGraphicsView.cpp | 144 ++------------ .../GraphCanvasGraphicsView.h | 23 +-- .../Code/graphcanvas_staticlib_files.cmake | 4 - .../Code/Editor/Components/EditorGraph.cpp | 24 +-- .../ScriptCanvas/Components/EditorGraph.h | 8 +- .../GraphValidationDockWidget.cpp | 20 +- .../GraphValidationDockWidget.h | 6 +- .../Code/Editor/View/Windows/MainWindow.h | 12 +- 25 files changed, 514 insertions(+), 381 deletions(-) rename {Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification => Code/Framework/AzQtComponents/AzQtComponents/Components}/ToastNotification.cpp (77%) rename {Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification => Code/Framework/AzQtComponents/AzQtComponents/Components}/ToastNotification.h (82%) rename {Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification => Code/Framework/AzQtComponents/AzQtComponents/Components}/ToastNotification.ui (93%) create mode 100644 Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.cpp create mode 100644 Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastBus.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h delete mode 100644 Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/ToastBus.h diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp similarity index 77% rename from Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.cpp rename to Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp index 41c2d8cecd..670607900f 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp @@ -5,24 +5,19 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include - #include +#include +#include -#include -#include +#include +#include +#include +#include -#include - -namespace GraphCanvas +namespace AzQtComponents { - ////////////////////// - // ToastNotification - ////////////////////// - ToastNotification::ToastNotification(QWidget* parent, const ToastConfiguration& toastConfiguration) : QDialog(parent, Qt::FramelessWindowHint) - , m_toastId(AZ::Entity::MakeId()) , m_closeOnClick(true) , m_ui(new Ui::ToastNotification()) , m_fadeAnimation(nullptr) @@ -36,35 +31,35 @@ namespace GraphCanvas QIcon toastIcon; - switch (toastConfiguration.GetToastType()) + switch (toastConfiguration.m_toastType) { case ToastType::Error: - toastIcon = QIcon(":/GraphCanvasEditorResources/toast_error_icon.png"); + toastIcon = QIcon(":/stylesheet/img/logging/error.svg"); break; case ToastType::Warning: - toastIcon = QIcon(":/GraphCanvasEditorResources/toast_warning_icon.png"); + toastIcon = QIcon(":/stylesheet/img/logging/warning-yellow.svg"); break; case ToastType::Information: - toastIcon = QIcon(":/GraphCanvasEditorResources/toast_information_icon.png"); + toastIcon = QIcon(":/stylesheet/img/logging/information.svg"); break; case ToastType::Custom: - toastIcon = QIcon(toastConfiguration.GetCustomToastImage().c_str()); + toastIcon = QIcon(toastConfiguration.m_customIconImage); default: break; } m_ui->iconLabel->setPixmap(toastIcon.pixmap(64, 64)); - m_ui->titleLabel->setText(toastConfiguration.GetTitleLabel().c_str()); - m_ui->mainLabel->setText(toastConfiguration.GetDescriptionLabel().c_str()); + m_ui->titleLabel->setText(toastConfiguration.m_title); + m_ui->mainLabel->setText(toastConfiguration.m_description); - m_lifeSpan.setInterval(aznumeric_cast(toastConfiguration.GetDuration().count())); - m_closeOnClick = toastConfiguration.GetCloseOnClick(); + m_lifeSpan.setInterval(aznumeric_cast(toastConfiguration.m_duration.count())); + m_closeOnClick = toastConfiguration.m_closeOnClick; m_ui->closeButton->setVisible(m_closeOnClick); QObject::connect(m_ui->closeButton, &QToolButton::clicked, this, &ToastNotification::accept); - m_fadeDuration = toastConfiguration.GetFadeDuration(); + m_fadeDuration = toastConfiguration.m_fadeDuration; QObject::connect(&m_lifeSpan, &QTimer::timeout, this, &ToastNotification::FadeOut); } @@ -73,11 +68,6 @@ namespace GraphCanvas { } - ToastId ToastNotification::GetToastId() const - { - return m_toastId; - } - void ToastNotification::ShowToastAtCursor() { QPoint globalCursorPos = QCursor::pos(); @@ -131,8 +121,6 @@ namespace GraphCanvas { StartTimer(); } - - emit ToastNotificationShown(); } void ToastNotification::hideEvent(QHideEvent* hideEvent) @@ -147,7 +135,6 @@ namespace GraphCanvas delete m_fadeAnimation; } - ToastNotificationBus::Event(GetToastId(), &ToastNotifications::OnToastDismissed); emit ToastNotificationHidden(); } @@ -155,7 +142,7 @@ namespace GraphCanvas { if (m_closeOnClick) { - ToastNotificationBus::Event(GetToastId(), &ToastNotifications::OnToastInteraction); + emit ToastNotificationInteraction(); accept(); } } @@ -205,5 +192,5 @@ namespace GraphCanvas accept(); } } -#include +#include "Components/moc_ToastNotification.cpp" } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h similarity index 82% rename from Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.h rename to Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h index 40debea0fe..eaf8a0751d 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h @@ -8,17 +8,13 @@ #pragma once #if !defined(Q_MOC_RUN) +#include +#include +#include #include #include #include -#include #include -#include -#include -#include -#include - -#include #endif namespace Ui @@ -26,20 +22,20 @@ namespace Ui class ToastNotification; } -namespace GraphCanvas +QT_FORWARD_DECLARE_CLASS(QPropertyAnimation) + +namespace AzQtComponents { - class ToastNotification + class AZ_QT_COMPONENTS_API ToastNotification : public QDialog { Q_OBJECT public: AZ_CLASS_ALLOCATOR(ToastNotification, AZ::SystemAllocator, 0); - ToastNotification(QWidget* parent, const ToastConfiguration& configuration); + ToastNotification(QWidget* parent, const ToastConfiguration& toastConfiguration); virtual ~ToastNotification(); - ToastId GetToastId() const; - // Shows the toast notification relative to the current cursor. void ShowToastAtCursor(); @@ -53,30 +49,26 @@ namespace GraphCanvas // QDialog void showEvent(QShowEvent* showEvent) override; void hideEvent(QHideEvent* hideEvent) override; - void mousePressEvent(QMouseEvent* mouseEvent) override; - bool eventFilter(QObject* object, QEvent* event) override; - //// public slots: - void StartTimer(); void FadeOut(); signals: - void ToastNotificationShown(); void ToastNotificationHidden(); + void ToastNotificationInteraction(); private: - QPropertyAnimation* m_fadeAnimation; - AZStd::chrono::milliseconds m_fadeDuration; - - ToastId m_toastId; bool m_closeOnClick; QTimer m_lifeSpan; + + AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING + AZStd::chrono::milliseconds m_fadeDuration; AZStd::unique_ptr m_ui; + AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING }; -} +} // namespace AzQtComponents diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.ui b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.ui similarity index 93% rename from Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.ui rename to Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.ui index 2c82f042b8..107281bdc2 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.ui +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.ui @@ -101,7 +101,7 @@ - :/GraphCanvasEditorResources/toast_information_icon.png + :/stylesheet/img/logging/information.svg true @@ -203,8 +203,8 @@ ... - - :/GraphCanvasEditorResources/lineedit_clear.png:/GraphCanvasEditorResources/lineedit_clear.png + + :/stylesheet/img/close_x.svg:/stylesheet/img/close_x.svg @@ -230,8 +230,7 @@ - - + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.cpp new file mode 100644 index 0000000000..41e3ea836c --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#include + +namespace AzQtComponents +{ + ToastConfiguration::ToastConfiguration(ToastType toastType, const QString& title, const QString& description) + : m_toastType(toastType) + , m_title(title) + , m_description(description) + { + } +} // namespace AzQtComponents diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h new file mode 100644 index 0000000000..05999017e4 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#include +#endif + +namespace AzQtComponents +{ + enum class ToastType + { + Information, + Warning, + Error, + Custom + }; + + class AZ_QT_COMPONENTS_API ToastConfiguration + { + public: + AZ_CLASS_ALLOCATOR(ToastConfiguration, AZ::SystemAllocator, 0); + ToastConfiguration(ToastType toastType, const QString& title, const QString& description); + + bool m_closeOnClick = true; + + ToastType m_toastType = ToastType::Information; + + QString m_title; + QString m_description; + QString m_customIconImage; + + AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING + AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds(5000); + AZStd::chrono::milliseconds m_fadeDuration = AZStd::chrono::milliseconds(250); + AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING + }; +} // namespace AzQtComponents diff --git a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake index 4c343b852c..4ed87ee358 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake @@ -49,6 +49,11 @@ set(FILES Components/Titlebar.h Components/TitleBarOverdrawHandler.cpp Components/TitleBarOverdrawHandler.h + Components/ToastNotification.cpp + Components/ToastNotification.h + Components/ToastNotificationConfiguration.h + Components/ToastNotificationConfiguration.cpp + Components/ToastNotification.ui Components/ToolButtonComboBox.cpp Components/ToolButtonComboBox.h Components/ToolButtonLineEdit.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastBus.h new file mode 100644 index 0000000000..8b6b4d8ebf --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastBus.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include + +#include +#endif + +namespace AzToolsFramework +{ + typedef AZ::EntityId ToastId; + + /** + * An EBus for receiving notifications when a user interacts with or dismisses + * a toast notification. + */ + class ToastNotifications + : public AZ::EBusTraits + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = ToastId; + + virtual void OnToastInteraction() {} + virtual void OnToastDismissed() {} + }; + + using ToastNotificationBus = AZ::EBus; + + typedef AZ::u32 ToastRequestBusId; + + /** + * An EBus used to hide or show toast notifications. Generally, these request are handled by a + * ToastNotificationsView that has been created with a specific ToastRequestBusId + * e.g. AZ_CRC("ExampleToastNotificationView") + */ + class ToastRequests + : public AZ::EBusTraits + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = ToastRequestBusId; // bus is addressed by CRC of the view name + + /** + * Hide a toast notification widget. + * + * @param toastId The toast notification's ToastId + */ + virtual void HideToastNotification(const ToastId& toastId) = 0; + + /** + * Show a toast notification with the specified toast configuration. When handled by a ToastNotificationsView, + * notifications are queued and presented to the user in sequence. + * + * @param toastConfiguration The toast configuration + * @return a ToastId + */ + virtual ToastId ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) = 0; + + /** + * Show a toast notification with the specified toast configuration at the current moust cursor location. + * + * @param toastConfiguration The toast configuration + * @return a ToastId + */ + virtual ToastId ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) = 0; + + /** + * Show a toast notification with the specified toast configuration at the specified location. + * + * @param screenPosition The screen position + * @param anchorPoint The anchorPoint for the toast notification widget + * @param toastConfiguration The toast configuration + * @return a ToastId + */ + virtual ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration&) = 0; + }; + + using ToastRequestBus = AZ::EBus; +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp new file mode 100644 index 0000000000..0ef0bf9a7b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include + +namespace AzToolsFramework +{ + ToastNotificationsView::ToastNotificationsView(QWidget* parent, ToastRequestBusId busId) + : QWidget(parent) + { + ToastRequestBus::Handler::BusConnect(busId); + } + + ToastNotificationsView::~ToastNotificationsView() + { + ToastRequestBus::Handler::BusDisconnect(); + } + + void ToastNotificationsView::OnHide() + { + QWidget::hide(); + + if (m_activeNotification.IsValid()) + { + auto notificationIter = m_notifications.find(m_activeNotification); + if (notificationIter != m_notifications.end()) + { + notificationIter->second->hide(); + } + } + } + + void ToastNotificationsView::UpdateToastPosition() + { + if (m_activeNotification.IsValid()) + { + auto notificationIter = m_notifications.find(m_activeNotification); + if (notificationIter != m_notifications.end()) + { + notificationIter->second->UpdatePosition(GetGlobalPoint(), m_anchorPoint); + } + } + } + + void ToastNotificationsView::OnShow() + { + QWidget::show(); + + if (m_activeNotification.IsValid() || !m_queuedNotifications.empty()) + { + DisplayQueuedNotification(); + } + } + + ToastId ToastNotificationsView::ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) + { + ToastId toastId = CreateToastNotification(toastConfiguration); + m_queuedNotifications.emplace_back(toastId); + + if (!m_activeNotification.IsValid()) + { + DisplayQueuedNotification(); + } + + return toastId; + } + + ToastId ToastNotificationsView::ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) + { + ToastId toastId = CreateToastNotification(toastConfiguration); + m_notifications[toastId]->ShowToastAtCursor(); + return toastId; + } + + ToastId ToastNotificationsView::ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration& toastConfiguration) + { + ToastId toastId = CreateToastNotification(toastConfiguration); + m_notifications[toastId]->ShowToastAtPoint(screenPosition, anchorPoint); + return toastId; + } + + void ToastNotificationsView::HideToastNotification(const ToastId& toastId) + { + auto notificationIter = m_notifications.find(toastId); + if (notificationIter != m_notifications.end()) + { + auto queuedIter = AZStd::find(m_queuedNotifications.begin(), m_queuedNotifications.end(), toastId); + if (queuedIter != m_queuedNotifications.end()) + { + m_queuedNotifications.erase(queuedIter); + } + + notificationIter->second->reject(); + } + } + + ToastId ToastNotificationsView::CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) + { + AzQtComponents::ToastNotification* notification = aznew AzQtComponents::ToastNotification(parentWidget(), toastConfiguration); + ToastId toastId = AZ::Entity::MakeId(); + m_notifications[toastId] = notification; + + QObject::connect( + m_notifications[toastId], &AzQtComponents::ToastNotification::ToastNotificationHidden, + [toastId]() + { + ToastNotificationBus::Event(toastId, &ToastNotificationBus::Events::OnToastDismissed); + }); + + QObject::connect( + m_notifications[toastId], &AzQtComponents::ToastNotification::ToastNotificationInteraction, + [toastId]() + { + ToastNotificationBus::Event(toastId, &ToastNotificationBus::Events::OnToastInteraction); + }); + + return toastId; + } + + QPoint ToastNotificationsView::GetGlobalPoint() + { + QPoint relativePoint = m_offset; + + AZ_Assert(parentWidget(), "ToastNotificationsView has invalid parent QWidget"); + if (m_anchorPoint.x() == 1.0) + { + relativePoint.setX(parentWidget()->width() - m_offset.x()); + } + if (m_anchorPoint.y() == 1.0) + { + relativePoint.setY(parentWidget()->height() - m_offset.y()); + } + + return parentWidget()->mapToGlobal(relativePoint); + } + + void ToastNotificationsView::DisplayQueuedNotification() + { + AZ_Assert(parentWidget(), "ToastNotificationsView has invalid parent QWidget"); + if (m_queuedNotifications.empty() || !parentWidget()->isVisible() || !isVisible()) + { + return; + } + + ToastId toastId = m_queuedNotifications.front(); + m_queuedNotifications.erase(m_queuedNotifications.begin()); + + auto notificationIter = m_notifications.find(toastId); + if (notificationIter != m_notifications.end()) + { + m_activeNotification = toastId; + + notificationIter->second->ShowToastAtPoint(GetGlobalPoint(), m_anchorPoint); + + QObject::connect( + notificationIter->second, &AzQtComponents::ToastNotification::ToastNotificationHidden, + [&]() + { + m_activeNotification.SetInvalid(); + DisplayQueuedNotification(); + } + ); + } + + // If we didn't actually show something, recurse to avoid things getting stuck in the queue. + if (!m_activeNotification.IsValid()) + { + DisplayQueuedNotification(); + } + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h new file mode 100644 index 0000000000..c4220331d8 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include + +#include +#include +#include +#endif + +namespace AzQtComponents +{ + class ToastNotification; +} + +namespace AzToolsFramework +{ + /** + * \brief A QWidget that displays and manages a queue of toast notifications. + * + * This view must be updated by its parent when the parent widget is show, hidden, moved + * or resized because toast notifications are displayed on top of the parent and are not part + * of the layout, so they must be manually moved. + */ + class ToastNotificationsView final + : public QWidget + , protected ToastRequestBus::Handler + { + Q_OBJECT + public: + ToastNotificationsView(QWidget* parent, ToastRequestBusId busId); + ~ToastNotificationsView() override; + + void HideToastNotification(const ToastId& toastId) override; + + ToastId ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) override; + ToastId ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) override; + ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration&) override; + + void OnHide(); + void OnShow(); + void UpdateToastPosition(); + + private: + ToastId CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration); + void DisplayQueuedNotification(); + QPoint GetGlobalPoint(); + + ToastId m_activeNotification; + AZStd::unordered_map m_notifications; + AZStd::vector m_queuedNotifications; + + QPoint m_offset = QPoint(10, 10); + QPointF m_anchorPoint = QPointF(1, 0); + }; +} // AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 5db65f89f4..b57eedcd81 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -759,6 +759,9 @@ set(FILES UI/Prefab/PrefabUiHandler.cpp UI/Prefab/PrefabViewportFocusPathHandler.h UI/Prefab/PrefabViewportFocusPathHandler.cpp + UI/Notifications/ToastNotificationsView.cpp + UI/Notifications/ToastNotificationsView.h + UI/Notifications/ToastBus.h PythonTerminal/ScriptHelpDialog.cpp PythonTerminal/ScriptHelpDialog.h PythonTerminal/ScriptHelpDialog.ui diff --git a/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionComponent.cpp b/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionComponent.cpp index 690026e477..d61e8bda29 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionComponent.cpp +++ b/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionComponent.cpp @@ -13,6 +13,8 @@ #include +#include + #include #include @@ -1117,10 +1119,8 @@ namespace GraphCanvas QPointF globalConnectionPoint = ConversionUtils::AZToQPoint(globalConnectionVector); QPointF anchorPoint(0.0f, 0.0f); - - ToastConfiguration toastConfiguration(ToastType::Error, "Unable to connect to slot", m_validationResult.m_failureReason); - - toastConfiguration.SetCloseOnClick(false); + AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Error, "Unable to connect to slot", m_validationResult.m_failureReason.c_str()); + toastConfiguration.m_closeOnClick = false; m_toastId = viewHandler->ShowToastAtPoint(globalConnectionPoint.toPoint(), anchorPoint, toastConfiguration); } diff --git a/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionComponent.h b/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionComponent.h index dc35772b87..b447cee394 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionComponent.h @@ -19,6 +19,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include @@ -210,7 +211,7 @@ namespace GraphCanvas ConnectionValidationTooltip m_validationResult; Endpoint m_endpointTooltip; - ToastId m_toastId; + AzToolsFramework::ToastId m_toastId; //! The Id of the graph this connection belongs to. GraphId m_graphId; diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotLayoutComponent.cpp b/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotLayoutComponent.cpp index 95c6ecff11..78a94296ff 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotLayoutComponent.cpp +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotLayoutComponent.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -157,8 +159,8 @@ namespace GraphCanvas anchorPoint = QPointF(1.0f, 0.5f); } - ToastConfiguration toastConfiguration(ToastType::Error, "Unable to drop onto to slot", error); - toastConfiguration.SetCloseOnClick(false); + AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Error, "Unable to drop onto to slot", error.c_str()); + toastConfiguration.m_closeOnClick = false; m_toastId = viewHandler->ShowToastAtPoint(globalConnectionPoint.toPoint(), anchorPoint, toastConfiguration); } diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotLayoutComponent.h index 3c49b36833..61fd6f31db 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotLayoutComponent.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -58,7 +59,7 @@ namespace GraphCanvas SlotId m_slotId; ViewId m_viewId; - ToastId m_toastId; + AzToolsFramework::ToastId m_toastId; }; class DoubleClickSceneEventFilter diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/ToastBus.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/ToastBus.h deleted file mode 100644 index b775c5f068..0000000000 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/ToastBus.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -#pragma once - -#include - -#include - -namespace GraphCanvas -{ - class ToastNotifications - : public AZ::EBusTraits - { - public: - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; - using BusIdType = ToastId; - - virtual void OnToastInteraction() {} - virtual void OnToastDismissed() {} - }; - - using ToastNotificationBus = AZ::EBus; -} diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/ViewBus.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/ViewBus.h index 590b3812a8..05bf650dc1 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/ViewBus.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/ViewBus.h @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -139,11 +140,11 @@ namespace GraphCanvas virtual void RefreshView() = 0; //! Toast Notifications - virtual void HideToastNotification(const ToastId& toastId) = 0; + virtual void HideToastNotification(const AzToolsFramework::ToastId& toastId) = 0; - virtual ToastId ShowToastNotification(const ToastConfiguration& toastConfiguration) = 0; - virtual ToastId ShowToastAtCursor(const ToastConfiguration& toastConfiguration) = 0; - virtual ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const ToastConfiguration&) = 0; + virtual AzToolsFramework::ToastId ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) = 0; + virtual AzToolsFramework::ToastId ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) = 0; + virtual AzToolsFramework::ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration&) = 0; virtual bool IsShowing() const = 0; }; diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Editor/EditorTypes.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Editor/EditorTypes.h index e0722836b1..792e02bd4b 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Editor/EditorTypes.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Editor/EditorTypes.h @@ -29,8 +29,6 @@ namespace GraphCanvas typedef AZ::EntityId GraphicsEffectId; - typedef AZ::EntityId ToastId; - typedef AZ::Uuid PersistentGraphMemberId; typedef AZ::Crc32 ExtenderId; @@ -81,14 +79,6 @@ namespace GraphCanvas Target }; - enum class ToastType - { - Information, - Warning, - Error, - Custom - }; - enum class ListingType { Unknown, @@ -116,88 +106,6 @@ namespace GraphCanvas private: }; - class ToastConfiguration - { - public: - ToastConfiguration(ToastType toastType, const AZStd::string& titleLabel, const AZStd::string& descriptionLabel) - : m_toastType(toastType) - , m_titleLabel(titleLabel) - , m_descriptionLabel(descriptionLabel) - { - } - - ~ToastConfiguration() = default; - - ToastType GetToastType() const - { - return m_toastType; - } - - const AZStd::string& GetTitleLabel() const - { - return m_titleLabel; - } - - const AZStd::string& GetDescriptionLabel() const - { - return m_descriptionLabel; - } - - void SetCustomToastImage(const AZStd::string& toastImage) - { - AZ_Error("GraphCanvas", m_toastType == ToastType::Custom, "Setting a custom image on a non-custom Toast notification"); - m_customToastImage = toastImage; - } - - const AZStd::string& GetCustomToastImage() const - { - return m_customToastImage; - } - - void SetDuration(AZStd::chrono::milliseconds duration) - { - m_duration = duration; - } - - AZStd::chrono::milliseconds GetDuration() const - { - return m_duration; - } - - void SetCloseOnClick(bool closeOnClick) - { - m_closeOnClick = closeOnClick; - } - - bool GetCloseOnClick() const - { - return m_closeOnClick; - } - - void SetFadeDuration(AZStd::chrono::milliseconds fadeDuration) - { - m_fadeDuration = fadeDuration; - } - - AZStd::chrono::milliseconds GetFadeDuration() const - { - return m_fadeDuration; - } - - private: - - AZStd::chrono::milliseconds m_fadeDuration = AZStd::chrono::milliseconds(250); - - AZStd::chrono::milliseconds m_duration; - bool m_closeOnClick; - - AZStd::string m_customToastImage; - - ToastType m_toastType; - AZStd::string m_titleLabel; - AZStd::string m_descriptionLabel; - }; - struct ConnectionValidationTooltip { bool operator()() const diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.cpp index fa7e051035..a8b0f7dabf 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.cpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -46,6 +47,7 @@ namespace GraphCanvas , m_queuedFocus(nullptr) { m_viewId = AZ::Entity::MakeId(); + m_notificationsView = AZStd::make_unique(this, AZ_CRC(m_viewId.ToString())); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -603,8 +605,8 @@ namespace GraphCanvas const int maxSize = 17500; if (windowSize.width() > maxSize || windowSize.height() > maxSize) { - ToastConfiguration toastConfiguration(ToastType::Information, "Screenshot", "Screenshot attempted to capture an area too large. Some down-ressing may occur."); - ShowToastNotification(toastConfiguration); + AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Information, "Screenshot", "Screenshot attempted to capture an area too large. Some down-ressing may occur."); + m_notificationsView->ShowToastNotification(toastConfiguration); if (windowSize.width() > maxSize) { @@ -825,64 +827,24 @@ namespace GraphCanvas invalidateScene(GetViewableAreaInSceneCoordinates()); } - void GraphCanvasGraphicsView::HideToastNotification(const ToastId& toastId) + void GraphCanvasGraphicsView::HideToastNotification(const AzToolsFramework::ToastId& toastId) { - auto notificationIter = m_notifications.find(toastId); - - if (notificationIter != m_notifications.end()) - { - auto queuedIter = AZStd::find(m_queuedNotifications.begin(), m_queuedNotifications.end(), toastId); - - if (queuedIter != m_queuedNotifications.end()) - { - m_queuedNotifications.erase(queuedIter); - } - - notificationIter->second->reject(); - } + m_notificationsView->HideToastNotification(toastId); } - ToastId GraphCanvasGraphicsView::ShowToastNotification(const ToastConfiguration& toastConfiguration) + AzToolsFramework::ToastId GraphCanvasGraphicsView::ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) { - ToastNotification* notification = aznew ToastNotification(this, toastConfiguration); - - ToastId toastId = notification->GetToastId(); - m_notifications[toastId] = notification; - - m_queuedNotifications.emplace_back(toastId); - - if (!m_activeNotification.IsValid()) - { - DisplayQueuedNotification(); - } - - return toastId; + return m_notificationsView->ShowToastNotification(toastConfiguration); } - ToastId GraphCanvasGraphicsView::ShowToastAtCursor(const ToastConfiguration& toastConfiguration) + AzToolsFramework::ToastId GraphCanvasGraphicsView::ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) { - ToastNotification* notification = aznew ToastNotification(this, toastConfiguration); - - notification->ShowToastAtCursor(); - - ToastId toastId = notification->GetToastId(); - - m_notifications[toastId] = notification; - - return toastId; + return m_notificationsView->ShowToastAtCursor(toastConfiguration); } - ToastId GraphCanvasGraphicsView::ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const ToastConfiguration& toastConfiguration) + AzToolsFramework::ToastId GraphCanvasGraphicsView::ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration& toastConfiguration) { - ToastNotification* notification = aznew ToastNotification(this, toastConfiguration); - - notification->ShowToastAtPoint(screenPosition, anchorPoint); - - ToastId toastId = notification->GetToastId(); - - m_notifications[toastId] = notification; - - return toastId; + return m_notificationsView->ShowToastAtPoint(screenPosition, anchorPoint, toastConfiguration); } bool GraphCanvasGraphicsView::IsShowing() const @@ -1245,14 +1207,14 @@ namespace GraphCanvas CalculateInternalRectangle(); - UpdateToastPosition(); + m_notificationsView->UpdateToastPosition(); } void GraphCanvasGraphicsView::moveEvent(QMoveEvent* event) { QGraphicsView::moveEvent(event); - UpdateToastPosition(); + m_notificationsView->UpdateToastPosition(); } void GraphCanvasGraphicsView::scrollContentsBy(int dx, int dy) @@ -1266,25 +1228,14 @@ namespace GraphCanvas { QGraphicsView::showEvent(showEvent); - if (m_activeNotification.IsValid()) - { - DisplayQueuedNotification(); - } + m_notificationsView->OnShow(); } void GraphCanvasGraphicsView::hideEvent(QHideEvent* hideEvent) { QGraphicsView::hideEvent(hideEvent); - if (m_activeNotification.IsValid()) - { - auto notificationIter = m_notifications.find(m_activeNotification); - - if (notificationIter != m_notifications.end()) - { - notificationIter->second->hide(); - } - } + m_notificationsView->OnHide(); } void GraphCanvasGraphicsView::OnSettingsChanged() @@ -1378,25 +1329,6 @@ namespace GraphCanvas BookmarkManagerRequestBus::Event(sceneId, &BookmarkManagerRequests::ActivateShortcut, bookmarkShortcut); } - void GraphCanvasGraphicsView::UpdateToastPosition() - { - if (m_activeNotification.IsValid()) - { - auto notificationIter = m_notifications.find(m_activeNotification); - - if (notificationIter != m_notifications.end()) - { - // Want this to be roughly in the top right corner of the graphics view. - QPoint globalPoint = mapToGlobal(QPoint(width() - 10, 10)); - - // Anchor point will be top right - QPointF anchorPoint = QPointF(1, 0); - - notificationIter->second->UpdatePosition(globalPoint, anchorPoint); - } - } - } - void GraphCanvasGraphicsView::CenterOnSceneMembers(const AZStd::vector& memberIds) { QRectF boundingRect; @@ -1642,48 +1574,4 @@ namespace GraphCanvas AZ::TickBus::Handler::BusDisconnect(); } } - - void GraphCanvasGraphicsView::OnNotificationHidden() - { - m_activeNotification.SetInvalid(); - - if (!m_queuedNotifications.empty()) - { - DisplayQueuedNotification(); - } - } - - void GraphCanvasGraphicsView::DisplayQueuedNotification() - { - if (m_queuedNotifications.empty() && isVisible()) - { - return; - } - - ToastId toastId = m_queuedNotifications.front(); - m_queuedNotifications.erase(m_queuedNotifications.begin()); - - auto notificationIter = m_notifications.find(toastId); - - if (notificationIter != m_notifications.end()) - { - m_activeNotification = toastId; - - // Want this to be roughly in the top right corner of the graphics view. - QPoint globalPoint = mapToGlobal(QPoint(width() - 10, 10)); - - // Anchor point will be top right - QPointF anchorPoint = QPointF(1, 0); - - notificationIter->second->ShowToastAtPoint(globalPoint, anchorPoint); - - QObject::connect(notificationIter->second, &ToastNotification::ToastNotificationHidden, this, &GraphCanvasGraphicsView::OnNotificationHidden); - } - - // If we didn't actually show something, recurse to avoid things getting stuck in the queue. - if (!m_activeNotification.IsValid()) - { - DisplayQueuedNotification(); - } - } } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h index 70b3e16166..f7b34b598c 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h @@ -27,11 +27,11 @@ AZ_POP_DISABLE_WARNING #include #include +#include #include #include #include -#include namespace GraphCanvas { @@ -123,11 +123,11 @@ namespace GraphCanvas void RefreshView() override; - void HideToastNotification(const ToastId& toastId) override; + void HideToastNotification(const AzToolsFramework::ToastId& toastId) override; - ToastId ShowToastNotification(const ToastConfiguration& toastConfiguration) override; - ToastId ShowToastAtCursor(const ToastConfiguration& toastConfiguration) override; - ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const ToastConfiguration& toastConfiguration) override; + AzToolsFramework::ToastId ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) override; + AzToolsFramework::ToastId ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) override; + AzToolsFramework::ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration& toastConfiguration) override; bool IsShowing() const override; //// @@ -186,8 +186,6 @@ namespace GraphCanvas private: - void UpdateToastPosition(); - void CenterOnSceneMembers(const AZStd::vector& memberIds); void ConnectBoundsSignals(); @@ -206,9 +204,6 @@ namespace GraphCanvas void ManageTickState(); - void OnNotificationHidden(); - void DisplayQueuedNotification(); - GraphCanvasGraphicsView(const GraphCanvasGraphicsView&) = delete; ViewId m_viewId; @@ -242,13 +237,7 @@ namespace GraphCanvas AZStd::unique_ptr m_queuedFocus; - // These will display sequentially in a reserved part of the UI - ToastId m_activeNotification; - AZStd::vector< ToastId > m_queuedNotifications; - - // There could be more then the queued list in terms of general notifications - // As some systems might want to re-use the systems for their own needs. - AZStd::unordered_map< ToastId, ToastNotification* > m_notifications; + AZStd::unique_ptr m_notificationsView; bool m_isEditing; diff --git a/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake b/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake index bedc6329d5..b62ff303a6 100644 --- a/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake +++ b/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake @@ -30,7 +30,6 @@ set(FILES StaticLib/GraphCanvas/Components/PersistentIdBus.h StaticLib/GraphCanvas/Components/SceneBus.h StaticLib/GraphCanvas/Components/StyleBus.h - StaticLib/GraphCanvas/Components/ToastBus.h StaticLib/GraphCanvas/Components/ViewBus.h StaticLib/GraphCanvas/Components/VisualBus.h StaticLib/GraphCanvas/GraphicsItems/AnimatedPulse.cpp @@ -222,9 +221,6 @@ set(FILES StaticLib/GraphCanvas/Widgets/StyledItemDelegates/GenericComboBoxDelegate.h StaticLib/GraphCanvas/Widgets/StyledItemDelegates/IconDecoratedNameDelegate.cpp StaticLib/GraphCanvas/Widgets/StyledItemDelegates/IconDecoratedNameDelegate.h - StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.cpp - StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.h - StaticLib/GraphCanvas/Widgets/ToastNotification/ToastNotification.ui StaticLib/GraphCanvas/Utils/ColorUtils.h StaticLib/GraphCanvas/Utils/ConversionUtils.h StaticLib/GraphCanvas/Utils/GraphUtils.cpp diff --git a/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp b/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp index f003618c03..78325e9a90 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp @@ -19,6 +19,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include #include @@ -3310,7 +3311,7 @@ namespace ScriptCanvasEditor void Graph::OnToastInteraction() { - const GraphCanvas::ToastId* toastId = GraphCanvas::ToastNotificationBus::GetCurrentBusId(); + const AzToolsFramework::ToastId* toastId = AzToolsFramework::ToastNotificationBus::GetCurrentBusId(); if (toastId) { @@ -3335,7 +3336,7 @@ namespace ScriptCanvasEditor void Graph::OnToastDismissed() { - const GraphCanvas::ToastId* toastId = GraphCanvas::ToastNotificationBus::GetCurrentBusId(); + const AzToolsFramework::ToastId* toastId = AzToolsFramework::ToastNotificationBus::GetCurrentBusId(); if (toastId) { @@ -3358,25 +3359,21 @@ namespace ScriptCanvasEditor void Graph::ReportError(const ScriptCanvas::Node& node, const AZStd::string& errorSource, const AZStd::string& errorMessage) { - GraphCanvas::ToastConfiguration toastConfiguration(GraphCanvas::ToastType::Error, errorSource, errorMessage); - - toastConfiguration.SetCloseOnClick(true); - toastConfiguration.SetDuration(AZStd::chrono::milliseconds(5000)); + AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Error, errorSource.c_str(), errorMessage.c_str()); GraphCanvas::ViewId viewId; GraphCanvas::SceneRequestBus::EventResult(viewId, GetGraphCanvasGraphId(), &GraphCanvas::SceneRequests::GetViewId); - GraphCanvas::ToastId toastId; - + AzToolsFramework::ToastId toastId; GraphCanvas::ViewRequestBus::EventResult(toastId, viewId, &GraphCanvas::ViewRequests::ShowToastNotification, toastConfiguration); - GraphCanvas::ToastNotificationBus::MultiHandler::BusConnect(toastId); + AzToolsFramework::ToastNotificationBus::MultiHandler::BusConnect(toastId); m_toastNodeIds[toastId] = node.GetEntityId(); } - void Graph::UnregisterToast(const GraphCanvas::ToastId& toastId) + void Graph::UnregisterToast(const AzToolsFramework::ToastId& toastId) { - GraphCanvas::ToastNotificationBus::MultiHandler::BusDisconnect(toastId); + AzToolsFramework::ToastNotificationBus::MultiHandler::BusDisconnect(toastId); m_toastNodeIds.erase(toastId); } @@ -3408,10 +3405,7 @@ namespace ScriptCanvasEditor m_updateStrings.clear(); - GraphCanvas::ToastConfiguration toastConfiguration(GraphCanvas::ToastType::Information, "Nodes Updates", displayString); - toastConfiguration.SetCloseOnClick(true); - toastConfiguration.SetDuration(AZStd::chrono::milliseconds(5000)); - + AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Information, "Nodes Updates", displayString.c_str()); GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::ShowToastNotification, toastConfiguration); } } diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h index 6f73e84493..9913fa838b 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -27,7 +28,6 @@ #include #include #include -#include #include #include @@ -52,7 +52,7 @@ namespace ScriptCanvasEditor , private GraphCanvas::GraphModelRequestBus::Handler , private GraphCanvas::SceneNotificationBus::Handler , private GraphItemCommandNotificationBus::Handler - , private GraphCanvas::ToastNotificationBus::MultiHandler + , private AzToolsFramework::ToastNotificationBus::MultiHandler , private GeneralEditorNotificationBus::Handler , private AZ::SystemTickBus::Handler { @@ -327,7 +327,7 @@ namespace ScriptCanvasEditor protected: void PostRestore(const UndoData& restoredData) override; - void UnregisterToast(const GraphCanvas::ToastId& toastId); + void UnregisterToast(const AzToolsFramework::ToastId& toastId); Graph(const Graph&) = delete; @@ -350,7 +350,7 @@ namespace ScriptCanvasEditor void HandleQueuedUpdates(); bool IsNodeVersionConverting(const AZ::EntityId& graphCanvasNodeId) const; - AZStd::unordered_map< GraphCanvas::ToastId, AZ::EntityId > m_toastNodeIds; + AZStd::unordered_map< AzToolsFramework::ToastId, AZ::EntityId > m_toastNodeIds; // Function Definition Node Extension void HandleFunctionDefinitionExtension(ScriptCanvas::Node* node, GraphCanvas::SlotId graphCanvasSlotId, const GraphCanvas::NodeId& nodeId); diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.cpp index ded0130001..33de6c0e2c 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -1434,31 +1435,28 @@ namespace ScriptCanvasEditor GraphCanvas::ViewId viewId; GraphCanvas::SceneRequestBus::EventResult(viewId, m_graphCanvasId, &GraphCanvas::SceneRequests::GetViewId); - GraphCanvas::ToastType toastType; + AzQtComponents::ToastType toastType; AZStd::string titleLabel = "Validation Issue"; AZStd::string description = ""; if (m_model->GetValidationResults().HasErrors()) { - toastType = GraphCanvas::ToastType::Error; + toastType = AzQtComponents::ToastType::Error; description = AZStd::string::format("%i validation error(s) were found.", m_model->GetValidationResults().ErrorCount()); } else { - toastType = GraphCanvas::ToastType::Warning; + toastType = AzQtComponents::ToastType::Warning; description = AZStd::string::format("%i validation warning(s) were found.", m_model->GetValidationResults().WarningCount()); } - GraphCanvas::ToastConfiguration toastConfiguration(toastType, titleLabel, description); + AzQtComponents::ToastConfiguration toastConfiguration(toastType, titleLabel.c_str(), description.c_str()); - toastConfiguration.SetCloseOnClick(true); - toastConfiguration.SetDuration(AZStd::chrono::milliseconds(5000)); - - GraphCanvas::ToastId validationToastId; + AzToolsFramework::ToastId validationToastId; GraphCanvas::ViewRequestBus::EventResult(validationToastId, viewId, &GraphCanvas::ViewRequests::ShowToastNotification, toastConfiguration); - GraphCanvas::ToastNotificationBus::MultiHandler::BusConnect(validationToastId); + AzToolsFramework::ToastNotificationBus::MultiHandler::BusConnect(validationToastId); } @@ -1469,11 +1467,11 @@ namespace ScriptCanvasEditor void ValidationData::OnToastDismissed() { - const GraphCanvas::ToastId* toastId = GraphCanvas::ToastNotificationBus::GetCurrentBusId(); + const AzToolsFramework::ToastId* toastId = AzToolsFramework::ToastNotificationBus::GetCurrentBusId(); if (toastId) { - GraphCanvas::ToastNotificationBus::MultiHandler::BusDisconnect((*toastId)); + AzToolsFramework::ToastNotificationBus::MultiHandler::BusDisconnect((*toastId)); } } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.h index 107cfe7c10..66eecadaf8 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.h @@ -15,10 +15,10 @@ #include #include +#include #include #include -#include #include #include @@ -183,7 +183,7 @@ namespace ScriptCanvasEditor //! Owns the model for each currently opened graph class ValidationData : private ScriptCanvas::StatusRequestBus::Handler - , private GraphCanvas::ToastNotificationBus::MultiHandler + , private AzToolsFramework::ToastNotificationBus::MultiHandler { public: AZ_CLASS_ALLOCATOR(ValidationData, AZ::SystemAllocator, 0); @@ -227,7 +227,7 @@ namespace ScriptCanvasEditor : public AzQtComponents::StyledDockWidget , public GraphCanvas::AssetEditorNotificationBus::Handler , public GraphCanvas::SceneNotificationBus::Handler - , public GraphCanvas::ToastNotificationBus::Handler + , public AzToolsFramework::ToastNotificationBus::Handler { Q_OBJECT public: diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h index 2c84412e28..7672f0a199 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -124,7 +125,7 @@ namespace ScriptCanvasEditor public: OnSaveToast(AZStd::string_view tabName, AZ::EntityId graphCanvasGraphId, bool saveSuccessful) { - GraphCanvas::ToastType toastType = GraphCanvas::ToastType::Information; + AzQtComponents::ToastType toastType = AzQtComponents::ToastType::Information; AZStd::string titleLabel = "Notification"; AZStd::string description; @@ -136,15 +137,12 @@ namespace ScriptCanvasEditor else { description = AZStd::string::format("Failed to save %s", tabName.data()); - toastType = GraphCanvas::ToastType::Error; + toastType = AzQtComponents::ToastType::Error; } - GraphCanvas::ToastConfiguration toastConfiguration(toastType, titleLabel, description); + AzQtComponents::ToastConfiguration toastConfiguration(toastType, titleLabel.c_str(), description.c_str()); - toastConfiguration.SetCloseOnClick(true); - toastConfiguration.SetDuration(AZStd::chrono::milliseconds(5000)); - - GraphCanvas::ToastId validationToastId; + AzToolsFramework::ToastId validationToastId; GraphCanvas::ViewId viewId; GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId); From 1c409e0a2324183b5e498a3c61cc7c40f5d22e1c Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Wed, 20 Oct 2021 11:18:27 -0700 Subject: [PATCH 057/152] Change default folders for user content (#4333) old vs. new defaults: %USERHOME%/.o3de/Projects-> %USERHOME%/O3DE/Projects %USERHOME%/.o3de/Gems-> %USERHOME%/O3DE/Gems %USERHOME%/.o3de/Templates-> %USERHOME%/O3DE/Templates Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- scripts/o3de/o3de/manifest.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py index d9cbe92829..fc778591c4 100644 --- a/scripts/o3de/o3de/manifest.py +++ b/scripts/o3de/o3de/manifest.py @@ -41,6 +41,11 @@ def get_o3de_folder() -> pathlib.Path: o3de_folder.mkdir(parents=True, exist_ok=True) return o3de_folder +def get_o3de_user_folder() -> pathlib.Path: + o3de_user_folder = get_home_folder() / 'O3DE' + o3de_user_folder.mkdir(parents=True, exist_ok=True) + return o3de_user_folder + def get_o3de_registry_folder() -> pathlib.Path: registry_folder = get_o3de_folder() / 'Registry' @@ -67,19 +72,19 @@ def get_o3de_engines_folder() -> pathlib.Path: def get_o3de_projects_folder() -> pathlib.Path: - projects_folder = get_o3de_folder() / 'Projects' + projects_folder = get_o3de_user_folder() / 'Projects' projects_folder.mkdir(parents=True, exist_ok=True) return projects_folder def get_o3de_gems_folder() -> pathlib.Path: - gems_folder = get_o3de_folder() / 'Gems' + gems_folder = get_o3de_user_folder() / 'Gems' gems_folder.mkdir(parents=True, exist_ok=True) return gems_folder def get_o3de_templates_folder() -> pathlib.Path: - templates_folder = get_o3de_folder() / 'Templates' + templates_folder = get_o3de_user_folder() / 'Templates' templates_folder.mkdir(parents=True, exist_ok=True) return templates_folder From 77ec88f86e5eff5969f397a15b1da1520b14af2b Mon Sep 17 00:00:00 2001 From: Junbo Liang <68558268+junbo75@users.noreply.github.com> Date: Wed, 20 Oct 2021 11:28:28 -0700 Subject: [PATCH 058/152] Improve the stability of metrics gem tests by removing local file operations (#4761) Signed-off-by: Junbo Liang --- .../Code/Include/Private/MetricsManager.h | 13 +++++----- .../AWSMetrics/Code/Tests/AWSMetricsGemMock.h | 5 ---- .../Code/Tests/MetricsManagerTest.cpp | 25 ++++++++++++++----- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h b/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h index 2cd5ba97a8..3bb06acd75 100644 --- a/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h +++ b/Gems/AWSMetrics/Code/Include/Private/MetricsManager.h @@ -31,7 +31,7 @@ namespace AWSMetrics static const unsigned int DesiredMaxWorkers = 2; MetricsManager(); - ~MetricsManager(); + virtual ~MetricsManager(); //! Initializing the metrics manager //! @return Whether the operation is successful. @@ -93,6 +93,12 @@ namespace AWSMetrics //! @return Total number of requests for sending metrics events. int GetNumTotalRequests() const; + protected: + //! Send metrics to a local file. + //! @param metricsQueue metricsQueue Metrics queue that stores the metrics. + //! @return Outcome of the operation. + virtual AZ::Outcome SendMetricsToFile(AZStd::shared_ptr metricsQueue); + private: //! Job management void SetupJobContext(); @@ -112,11 +118,6 @@ namespace AWSMetrics //! @param metricsQueue Metrics events to send. void SendMetricsToServiceApiAsync(const MetricsQueue& metricsQueue); - //! Send metrics to a local file. - //! @param metricsQueue metricsQueue Metrics queue that stores the metrics. - //! @return Outcome of the operation. - AZ::Outcome SendMetricsToFile(AZStd::shared_ptr metricsQueue); - //! Push metrics events to the front of the queue for retry. //! @param metricsEventsForRetry Metrics events for retry. void PushMetricsForRetry(MetricsQueue& metricsEventsForRetry); diff --git a/Gems/AWSMetrics/Code/Tests/AWSMetricsGemMock.h b/Gems/AWSMetrics/Code/Tests/AWSMetricsGemMock.h index b77f39c93c..4f40fd6ff1 100644 --- a/Gems/AWSMetrics/Code/Tests/AWSMetricsGemMock.h +++ b/Gems/AWSMetrics/Code/Tests/AWSMetricsGemMock.h @@ -139,11 +139,6 @@ namespace AWSMetrics return true; } - bool RemoveDirectory(const AZStd::string& directory) - { - return AZ::IO::SystemFile::DeleteDir(directory.c_str()); - } - AZ::IO::FileIOBase* m_priorFileIO = nullptr; AZ::IO::FileIOBase* m_localFileIO = nullptr; diff --git a/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp b/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp index 3f87a1079d..9fcebec524 100644 --- a/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp +++ b/Gems/AWSMetrics/Code/Tests/MetricsManagerTest.cpp @@ -75,6 +75,23 @@ namespace AZ namespace AWSMetrics { + class MetricsManagerMock + : public MetricsManager + { + private: + AZ::Outcome SendMetricsToFile(AZStd::shared_ptr metricsQueue) override + { + if (AZ::IO::FileIOBase::GetInstance()) + { + return AZ::Success(); + } + else + { + return AZ::Failure(AZStd::string{ "Invalid File IO" }); + } + } + }; + class AWSMetricsNotificationBusMock : protected AWSMetricsNotificationBus::Handler { @@ -134,13 +151,11 @@ namespace AWSMetrics AWSMetricsGemAllocatorFixture::SetUp(); AWSMetricsRequestBus::Handler::BusConnect(); - m_metricsManager = AZStd::make_unique(); + m_metricsManager = AZStd::make_unique(); AZStd::string configFilePath = CreateClientConfigFile(true, (double) TestMetricsEventSizeInBytes / MbToBytes * 2, DefaultFlushPeriodInSeconds, 0); m_settingsRegistry->MergeSettingsFile(configFilePath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}); m_metricsManager->Init(); - RemoveFile(m_metricsManager->GetMetricsFilePath()); - ReplaceLocalFileIOWithMockIO(); } @@ -149,8 +164,6 @@ namespace AWSMetrics RevertMockIOToLocalFileIO(); RemoveFile(GetDefaultTestFilePath()); - RemoveFile(m_metricsManager->GetMetricsFilePath()); - RemoveDirectory(m_metricsManager->GetMetricsFileDirectory()); m_metricsManager.reset(); @@ -233,7 +246,7 @@ namespace AWSMetrics } } - AZStd::unique_ptr m_metricsManager; + AZStd::unique_ptr m_metricsManager; AWSMetricsNotificationBusMock m_notifications; AZ::IO::FileIOBase* m_fileIOMock; From af2790659812238907762dc3e782de0bc963508c Mon Sep 17 00:00:00 2001 From: AMZN-AlexOteiza Date: Wed, 20 Oct 2021 20:00:53 +0100 Subject: [PATCH 059/152] Moved atom flaky tests to sandbox --- .../Gem/PythonTests/Atom/TestSuite_Main.py | 70 ------------------ .../Gem/PythonTests/Atom/TestSuite_Sandbox.py | 72 +++++++++++++++++++ 2 files changed, 72 insertions(+), 70 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index 3403c938a8..6cc48984ab 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -253,73 +253,3 @@ class TestAtomEditorComponentsMain(object): ) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("launcher_platform", ['windows_generic']) -@pytest.mark.system -class TestMaterialEditorBasicTests(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project): - def delete_files(): - file_system.delete( - [ - os.path.join(workspace.paths.project(), "Materials", "test_material.material"), - os.path.join(workspace.paths.project(), "Materials", "test_material_1.material"), - os.path.join(workspace.paths.project(), "Materials", "test_material_2.material"), - ], - True, - True, - ) - # Cleanup our newly created materials - delete_files() - - def teardown(): - # Cleanup our newly created materials - delete_files() - - request.addfinalizer(teardown) - - @pytest.mark.parametrize("exe_file_name", ["MaterialEditor"]) - @pytest.mark.test_case_id("C34448113") # Creating a New Asset. - @pytest.mark.test_case_id("C34448114") # Opening an Existing Asset. - @pytest.mark.test_case_id("C34448115") # Closing Selected Material. - @pytest.mark.test_case_id("C34448116") # Closing All Materials. - @pytest.mark.test_case_id("C34448117") # Closing all but Selected Material. - @pytest.mark.test_case_id("C34448118") # Saving Material. - @pytest.mark.test_case_id("C34448119") # Saving as a New Material. - @pytest.mark.test_case_id("C34448120") # Saving as a Child Material. - @pytest.mark.test_case_id("C34448121") # Saving all Open Materials. - def test_MaterialEditorBasicTests( - self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name): - - expected_lines = [ - "Material opened: True", - "Test asset doesn't exist initially: True", - "New asset created: True", - "New Material opened: True", - "Material closed: True", - "All documents closed: True", - "Close All Except Selected worked as expected: True", - "Actual Document saved with changes: True", - "Document saved as copy is saved with changes: True", - "Document saved as child is saved with changes: True", - "Save All worked as expected: True", - ] - unexpected_lines = [ - # "Trace::Assert", - # "Trace::Error", - "Traceback (most recent call last):" - ] - - hydra.launch_and_validate_results( - request, - TEST_DIRECTORY, - generic_launcher, - "hydra_AtomMaterialEditor_BasicTests.py", - run_python="--runpython", - timeout=120, - expected_lines=expected_lines, - unexpected_lines=unexpected_lines, - halt_on_unexpected=True, - null_renderer=True, - log_file_name="MaterialEditor.log", - ) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py index 58e5d00ff2..9bb7f9c50e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py @@ -70,3 +70,75 @@ class TestAtomEditorComponentsSandbox(object): null_renderer=True, cfg_args=cfg_args, ) + +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ['windows_generic']) +@pytest.mark.system +class TestMaterialEditorBasicTests(object): + @pytest.fixture(autouse=True) + def setup_teardown(self, request, workspace, project): + def delete_files(): + file_system.delete( + [ + os.path.join(workspace.paths.project(), "Materials", "test_material.material"), + os.path.join(workspace.paths.project(), "Materials", "test_material_1.material"), + os.path.join(workspace.paths.project(), "Materials", "test_material_2.material"), + ], + True, + True, + ) + # Cleanup our newly created materials + delete_files() + + def teardown(): + # Cleanup our newly created materials + delete_files() + + request.addfinalizer(teardown) + + @pytest.mark.parametrize("exe_file_name", ["MaterialEditor"]) + @pytest.mark.test_case_id("C34448113") # Creating a New Asset. + @pytest.mark.test_case_id("C34448114") # Opening an Existing Asset. + @pytest.mark.test_case_id("C34448115") # Closing Selected Material. + @pytest.mark.test_case_id("C34448116") # Closing All Materials. + @pytest.mark.test_case_id("C34448117") # Closing all but Selected Material. + @pytest.mark.test_case_id("C34448118") # Saving Material. + @pytest.mark.test_case_id("C34448119") # Saving as a New Material. + @pytest.mark.test_case_id("C34448120") # Saving as a Child Material. + @pytest.mark.test_case_id("C34448121") # Saving all Open Materials. + def test_MaterialEditorBasicTests( + self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name): + + expected_lines = [ + "Material opened: True", + "Test asset doesn't exist initially: True", + "New asset created: True", + "New Material opened: True", + "Material closed: True", + "All documents closed: True", + "Close All Except Selected worked as expected: True", + "Actual Document saved with changes: True", + "Document saved as copy is saved with changes: True", + "Document saved as child is saved with changes: True", + "Save All worked as expected: True", + ] + unexpected_lines = [ + # "Trace::Assert", + # "Trace::Error", + "Traceback (most recent call last):" + ] + + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + generic_launcher, + "hydra_AtomMaterialEditor_BasicTests.py", + run_python="--runpython", + timeout=120, + expected_lines=expected_lines, + unexpected_lines=unexpected_lines, + halt_on_unexpected=True, + null_renderer=True, + log_file_name="MaterialEditor.log", + ) + From 24b0dab30e7c3ad34decf3495c945c725182ddb8 Mon Sep 17 00:00:00 2001 From: srikappa-amzn Date: Wed, 20 Oct 2021 12:02:46 -0700 Subject: [PATCH 060/152] Make safe asset folder comparison for prefab creation to be case insensitive Signed-off-by: srikappa-amzn --- .../UI/Prefab/PrefabIntegrationManager.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 526912cf29..0bb596d0f6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -800,16 +800,14 @@ namespace AzToolsFramework } else { - QString cleanSaveAs(QDir::cleanPath(prefabPath)); + AZ::IO::FixedMaxPath lexicallyNormalPath = AZ::IO::PathView(prefabPath.toUtf8().constData()).LexicallyNormal(); bool isPathSafeForAssets = false; - for (AZStd::string assetSafeFolder : assetSafeFolders) + for (AZ::IO::Path assetSafeFolder : assetSafeFolders) { - QString cleanAssetSafeFolder(QDir::cleanPath(assetSafeFolder.c_str())); - // Compare using clean paths so slash direction does not matter. - // Note that this comparison is case sensitive because some file systems - // Open 3D Engine supports are case sensitive. - if (cleanSaveAs.startsWith(cleanAssetSafeFolder)) + // Check if the prefabPath is relative to the safe asset directory. + // The Path classes are being used to make this check case insensitive. + if (lexicallyNormalPath.IsRelativeTo(assetSafeFolder)) { isPathSafeForAssets = true; break; From 714f5357b2ebacf1db8540e2adf9f49e4037ce7d Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Wed, 20 Oct 2021 14:45:32 -0500 Subject: [PATCH 061/152] Add an error message to AP when the project path is invalid (#4801) * Add an error message to AP when bad project path Produce a log error or a dialog box error when the project path for AP does not have a project.json and is invalid. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fix a failing unit test - AssetProcessorMessages Adding a check for 'project.json' caused BeforeRun() in a test fixture to fail. Teardown of the fixture was also broken if the test failed to fully startup the application manager, so added null checks there. Added an assert to the fixture's Setup to check the status of BeforeRun(). Added additional settings registry setup to the fixture to make sure the project path and branch token are configured before BeforeRun() is called. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../tests/AssetProcessorMessagesTests.cpp | 42 ++++++-- .../native/utilities/ApplicationManager.cpp | 9 +- .../utilities/GUIApplicationManager.cpp | 96 +------------------ 3 files changed, 43 insertions(+), 104 deletions(-) diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp index 63536a160b..516f6beb3c 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -98,10 +100,26 @@ namespace AssetProcessorMessagesTests int argC = 0; m_batchApplicationManager = AZStd::make_unique(&argC, nullptr, nullptr); - m_batchApplicationManager->BeforeRun(); - // Override Game Name to be "AutomatedTesting" - AssetUtilities::ComputeProjectName("AutomatedTesting", true); + auto registry = AZ::SettingsRegistry::Get(); + EXPECT_NE(registry, nullptr); + constexpr AZ::SettingsRegistryInterface::FixedValueString bootstrapKey{ + AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey + }; + constexpr AZ::SettingsRegistryInterface::FixedValueString projectPathKey{ bootstrapKey + "/project_path" }; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + + // Force the branch token into settings registry before starting the application manager. + // This avoids writing the asset_processor.setreg file which can cause fileIO errors. + const AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath(); + constexpr AZ::SettingsRegistryInterface::FixedValueString branchTokenKey{ bootstrapKey + "/assetProcessor_branch_token" }; + AZStd::string token; + AZ::StringFunc::AssetPath::CalculateBranchToken(enginePath.c_str(), token); + registry->Set(branchTokenKey, token.c_str()); + + auto status = m_batchApplicationManager->BeforeRun(); + ASSERT_EQ(status, ApplicationManager::BeforeRunStatus::Status_Success); m_batchApplicationManager->m_platformConfiguration = new PlatformConfiguration(); m_batchApplicationManager->InitAssetProcessorManager(); @@ -159,21 +177,25 @@ namespace AssetProcessorMessagesTests ASSERT_TRUE(result); }); - - } void TearDown() override { - QEventLoop eventLoop; + if (m_batchApplicationManager->m_connectionManager) + { + QEventLoop eventLoop; - QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ReadyToQuit, &eventLoop, &QEventLoop::quit); + QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ReadyToQuit, &eventLoop, &QEventLoop::quit); - m_batchApplicationManager->m_connectionManager->QuitRequested(); + m_batchApplicationManager->m_connectionManager->QuitRequested(); - eventLoop.exec(); + eventLoop.exec(); + } - m_assetSystemComponent->Deactivate(); + if (m_assetSystemComponent) + { + m_assetSystemComponent->Deactivate(); + } m_batchApplicationManager->Destroy(); } diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp b/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp index c237f4801e..a024ce6c7a 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp @@ -505,6 +505,14 @@ bool ApplicationManager::StartAZFramework() AzFramework::Application::Descriptor appDescriptor; AZ::ComponentApplication::StartupParameters params; + QDir projectPath{ AssetUtilities::ComputeProjectPath() }; + if (!projectPath.exists("project.json")) + { + AZStd::string errorMsg = AZStd::string::format("Path '%s' is not a valid project path.", projectPath.path().toUtf8().constData()); + AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBus::Events::OnErrorMessage, errorMsg.c_str()); + return false; + } + QString projectName = AssetUtilities::ComputeProjectName(); // Prevent loading of gems in the Create method of the ComponentApplication @@ -520,7 +528,6 @@ bool ApplicationManager::StartAZFramework() //Registering all the Components m_frameworkApp.RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor()); - Reflect(); const AzFramework::CommandLine* commandLine = nullptr; diff --git a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp index 40d3bd3caa..c3ff22a39e 100644 --- a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp @@ -95,6 +95,8 @@ GUIApplicationManager::~GUIApplicationManager() ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun() { + AssetProcessor::MessageInfoBus::Handler::BusConnect(); + ApplicationManager::BeforeRunStatus status = ApplicationManagerBase::BeforeRun(); if (status != ApplicationManager::BeforeRunStatus::Status_Success) { @@ -109,7 +111,6 @@ ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun() #if defined(EXTERNAL_CRASH_REPORTING) CrashHandler::ToolsCrashHandler::InitCrashHandler("AssetProcessor", projectAssetRoot.absolutePath().toStdString()); #endif - AssetProcessor::MessageInfoBus::Handler::BusConnect(); // we have to monitor both the cache folder and the database file and restart AP if either of them gets deleted // It is important to note that we are monitoring the parent folder and not the actual cache folder itself since @@ -436,98 +437,7 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message) connection = Qt::QueuedConnection; } - if (m_isCurrentlyLoadingGems) - { - // if something goes wrong during gem initialization, this is a special case and we need to be extra helpful. - const char* userSettingsFile = "_WAF_/user_settings.options"; - const char* defaultSettingsFile = "_WAF_/default_settings.json"; - - QDir engineRoot; - AssetUtilities::ComputeEngineRoot(engineRoot); - - QString settingsPath = engineRoot.absoluteFilePath(userSettingsFile); - QString friendlyErrorMessage; - bool usingDefaults = false; - - if (QFile::exists(settingsPath)) - { - QSettings loader(settingsPath, QSettings::IniFormat); - QVariant settingValue = loader.value("Game Projects/enabled_game_projects"); - QStringList compiledProjects = settingValue.toStringList(); - - if (compiledProjects.isEmpty()) - { - QByteArray byteArray; - QFile jsonFile; - jsonFile.setFileName(engineRoot.absoluteFilePath(defaultSettingsFile)); - jsonFile.open(QIODevice::ReadOnly | QIODevice::Text); - byteArray = jsonFile.readAll(); - jsonFile.close(); - - QJsonObject settingsObject = QJsonDocument::fromJson(byteArray).object(); - QJsonArray projectsArray = settingsObject["Game Projects"].toArray(); - - if (!projectsArray.isEmpty()) - { - auto projectObject = projectsArray[0].toObject(); - QString projects = projectObject["default_value"].toString(); - - if (!projects.isEmpty()) - { - compiledProjects = projects.split(','); - usingDefaults = true; - } - } - } - - for (int i = 0; i < compiledProjects.size(); ++i) - { - compiledProjects[i] = compiledProjects[i].trimmed(); - } - - QString enabledProject = AssetUtilities::ComputeProjectName(); - - if (!compiledProjects.contains(enabledProject)) - { - QString projectSourceLine; - - if (usingDefaults) - { - projectSourceLine = QString("The currently compiled projects according to the defaults in %1 are '%2'").arg(defaultSettingsFile); - } - else - { - projectSourceLine = QString("The currently compiled projects according to %1 are '%2'").arg(userSettingsFile); - } - - projectSourceLine = projectSourceLine.arg(compiledProjects.join(", ")); - friendlyErrorMessage = QString("An error occurred while loading gems.\n" - "The enabled game project is not in the list of compiled projects.\n" - "Please configure the enabled project to be compiled and rebuild or change the enabled project.\n" - "The currently enabled game project (from bootstrap.cfg or /%4 command-line parameter) is '%1'.\n" - "%2\n" - "Full error text:\n" - "%3" - ).arg(enabledProject).arg(projectSourceLine).arg(message).arg(AssetUtilities::ProjectPathOverrideParameter); - } - } - - if (friendlyErrorMessage.isEmpty()) - { - friendlyErrorMessage = QString("An error occurred while loading gems.\n" - "This can happen when new gems are added to a project, but those gems need to be built in order to function.\n" - "This can also happen when switching to a different project, one which uses gems which are not yet built.\n" - "To continue, please build the current project before attempting to run Asset Processor again.\n\n" - "Full error text:\n" - "%1").arg(message); - } - QMetaObject::invokeMethod(this, "ShowMessageBox", connection, Q_ARG(QString, QString("Error")), Q_ARG(QString, friendlyErrorMessage), Q_ARG(bool, true)); - } - else - { - QMetaObject::invokeMethod(this, "ShowMessageBox", connection, Q_ARG(QString, QString("Error")), Q_ARG(QString, QString(message)), Q_ARG(bool, true)); - } - + QMetaObject::invokeMethod(this, "ShowMessageBox", connection, Q_ARG(QString, QString("Error")), Q_ARG(QString, QString(message)), Q_ARG(bool, true)); return true; } From 60c286dafa5be8cb188a117d7455f25325318374 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 20 Oct 2021 12:56:30 -0700 Subject: [PATCH 062/152] LYN-7483 + LYN-7052 | Correctly initialize and refresh Prefab Focus Mode handler. (#4718) * Initialize the PrefabFocusHandler on context reset, to also cover the case of a new level being created on the welcome screen. Relax checks/restrictions on refreshes to cover cases where an instance is reused by the Prefab EOS. Refresh the breadcrumbs when a container is renamed and when a change is propagated to the instances to ensure the correct names are displayed. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Rename m_isInitialized to m_initialized in PrefabFocusHandler Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Use find_if to detect when a container entity in the focus path has been renamed. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Renaming and commenting variables in PrefabFocusHandler. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Undo minor naming change Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Replace lazy initialization and have the UI side initialize the Editor calls in PrefabFocusHandler. This should prevent issues with focus mode trying to access these interfaces in non-editor applications. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../PrefabEditorEntityOwnershipService.cpp | 2 +- .../Prefab/PrefabFocusHandler.cpp | 138 +++++++++++------- .../Prefab/PrefabFocusHandler.h | 24 ++- .../Prefab/PrefabFocusInterface.h | 5 + .../UI/Prefab/PrefabIntegrationManager.cpp | 5 + 5 files changed, 113 insertions(+), 61 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index 7db507e751..67b5d99011 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -53,7 +53,7 @@ namespace AzToolsFramework AZ_Assert(m_loaderInterface != nullptr, "Couldn't get prefab loader interface, it's a requirement for PrefabEntityOwnership system to work"); - m_rootInstance = AZStd::unique_ptr(m_prefabSystemComponent->CreatePrefab({}, {}, "NewLevel.prefab")); + m_rootInstance = AZStd::unique_ptr(m_prefabSystemComponent->CreatePrefab({}, {}, "newLevel.prefab")); m_sliceOwnershipService.BusConnect(m_entityContextId); m_sliceOwnershipService.m_shouldAssertForLegacySlicesUsage = m_shouldAssertForLegacySlicesUsage; m_editorSliceOwnershipService.BusConnect(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index a79b9eb73d..09b5745a90 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -28,7 +28,9 @@ namespace AzToolsFramework::Prefab "Instance Entity Mapper Interface could not be found. " "Check that it is being correctly initialized."); + EditorEntityInfoNotificationBus::Handler::BusConnect(); EditorEntityContextNotificationBus::Handler::BusConnect(); + PrefabPublicNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); AZ::Interface::Register(this); } @@ -37,10 +39,12 @@ namespace AzToolsFramework::Prefab { AZ::Interface::Unregister(this); AZ::Interface::Unregister(this); + PrefabPublicNotificationBus::Handler::BusDisconnect(); EditorEntityContextNotificationBus::Handler::BusDisconnect(); + EditorEntityInfoNotificationBus::Handler::BusDisconnect(); } - void PrefabFocusHandler::Initialize() + void PrefabFocusHandler::InitializeEditorInterfaces() { m_containerEntityInterface = AZ::Interface::Get(); AZ_Assert( @@ -55,13 +59,6 @@ namespace AzToolsFramework::Prefab "Prefab - PrefabFocusHandler - " "Focus Mode Interface could not be found. " "Check that it is being correctly initialized."); - - m_instanceEntityMapperInterface = AZ::Interface::Get(); - AZ_Assert( - m_instanceEntityMapperInterface, - "Prefab - PrefabFocusHandler - " - "Instance Entity Mapper Interface could not be found. " - "Check that it is being correctly initialized."); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) @@ -90,12 +87,12 @@ namespace AzToolsFramework::Prefab PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) { - if (index < 0 || index >= m_instanceFocusVector.size()) + if (index < 0 || index >= m_instanceFocusHierarchy.size()) { return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); } - InstanceOptionalReference focusedInstance = m_instanceFocusVector[index]; + InstanceOptionalReference focusedInstance = m_instanceFocusHierarchy[index]; FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId()); @@ -134,42 +131,38 @@ namespace AzToolsFramework::Prefab return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on.")); } - if (!m_isInitialized) + // Close all container entities in the old path. + CloseInstanceContainers(m_instanceFocusHierarchy); + + m_focusedInstance = focusedInstance; + m_focusedTemplateId = focusedInstance->get().GetTemplateId(); + + AZ::EntityId containerEntityId; + + if (focusedInstance->get().GetParentInstance() != AZStd::nullopt) { - Initialize(); + containerEntityId = focusedInstance->get().GetContainerEntityId(); } - - if (!m_focusedInstance.has_value() || &m_focusedInstance->get() != &focusedInstance->get()) + else { - // Close all container entities in the old path - CloseInstanceContainers(m_instanceFocusVector); - - m_focusedInstance = focusedInstance; - m_focusedTemplateId = focusedInstance->get().GetTemplateId(); - - AZ::EntityId containerEntityId; - - if (focusedInstance->get().GetParentInstance() != AZStd::nullopt) - { - containerEntityId = focusedInstance->get().GetContainerEntityId(); - } - else - { - containerEntityId = AZ::EntityId(); - } + containerEntityId = AZ::EntityId(); + } - // Focus on the descendants of the container entity + // Focus on the descendants of the container entity in the Editor, if the interface is initialized. + if (m_focusModeInterface) + { m_focusModeInterface->SetFocusRoot(containerEntityId); - - // Refresh path variables - RefreshInstanceFocusList(); - - // Open all container entities in the new path - OpenInstanceContainers(m_instanceFocusVector); - - PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); } + // Refresh path variables. + RefreshInstanceFocusList(); + RefreshInstanceFocusPath(); + + // Open all container entities in the new path. + OpenInstanceContainers(m_instanceFocusHierarchy); + + PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); + return AZ::Success(); } @@ -220,49 +213,80 @@ namespace AzToolsFramework::Prefab const int PrefabFocusHandler::GetPrefabFocusPathLength([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { - return aznumeric_cast(m_instanceFocusVector.size()); + return aznumeric_cast(m_instanceFocusHierarchy.size()); } - void PrefabFocusHandler::OnEntityStreamLoadSuccess() + void PrefabFocusHandler::OnContextReset() { - if (!m_isInitialized) - { - Initialize(); - } - // Clear the old focus vector - m_instanceFocusVector.clear(); + m_instanceFocusHierarchy.clear(); // Focus on the root prefab (AZ::EntityId() will default to it) FocusOnPrefabInstanceOwningEntityId(AZ::EntityId()); } + void PrefabFocusHandler::OnEntityInfoUpdatedName(AZ::EntityId entityId, [[maybe_unused]]const AZStd::string& name) + { + // Determine if the entityId is the container for any of the instances in the vector + auto result = AZStd::find_if( + m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(), + [entityId](const InstanceOptionalReference& instance) + { + return (instance->get().GetContainerEntityId() == entityId); + } + ); + + if (result != m_instanceFocusHierarchy.end()) + { + // Refresh the path and notify changes. + RefreshInstanceFocusPath(); + PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); + } + } + + void PrefabFocusHandler::OnPrefabInstancePropagationEnd() + { + // Refresh the path and notify changes in case propagation updated any container names. + RefreshInstanceFocusPath(); + PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); + } + void PrefabFocusHandler::RefreshInstanceFocusList() { - m_instanceFocusVector.clear(); - m_instanceFocusPath.clear(); + m_instanceFocusHierarchy.clear(); AZStd::list instanceFocusList; - // Use a support list to easily push front while traversing the prefab hierarchy InstanceOptionalReference currentInstance = m_focusedInstance; while (currentInstance.has_value()) { - instanceFocusList.push_front(currentInstance); + m_instanceFocusHierarchy.emplace_back(currentInstance); currentInstance = currentInstance->get().GetParentInstance(); } - // Populate internals using the support list - for (auto& instance : instanceFocusList) + // Invert the vector, since we need the top instance to be at index 0 + AZStd::reverse(m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end()); + } + + void PrefabFocusHandler::RefreshInstanceFocusPath() + { + m_instanceFocusPath.clear(); + + for (const InstanceOptionalReference& instance : m_instanceFocusHierarchy) { m_instanceFocusPath.Append(instance->get().GetContainerEntity()->get().GetName()); - m_instanceFocusVector.emplace_back(instance); } } void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector& instances) const { + // If this is called outside the Editor, this interface won't be initialized. + if (!m_containerEntityInterface) + { + return; + } + for (const InstanceOptionalReference& instance : instances) { if (instance.has_value()) @@ -274,6 +298,12 @@ namespace AzToolsFramework::Prefab void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector& instances) const { + // If this is called outside the Editor, this interface won't be initialized. + if (!m_containerEntityInterface) + { + return; + } + for (const InstanceOptionalReference& instance : instances) { if (instance.has_value()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 80b7a6859c..6a71365d8e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -11,9 +11,11 @@ #include #include +#include #include #include #include +#include #include namespace AzToolsFramework @@ -30,7 +32,9 @@ namespace AzToolsFramework::Prefab class PrefabFocusHandler final : private PrefabFocusInterface , private PrefabFocusPublicInterface + , private PrefabPublicNotificationBus::Handler , private EditorEntityContextNotificationBus::Handler + , private EditorEntityInfoNotificationBus::Handler { public: AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0); @@ -38,9 +42,8 @@ namespace AzToolsFramework::Prefab PrefabFocusHandler(); ~PrefabFocusHandler(); - void Initialize(); - // PrefabFocusInterface overrides ... + void InitializeEditorInterfaces() override; PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override; TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override; InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override; @@ -54,25 +57,34 @@ namespace AzToolsFramework::Prefab const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override; // EditorEntityContextNotificationBus overrides ... - void OnEntityStreamLoadSuccess() override; + void OnContextReset() override; + + // EditorEntityInfoNotificationBus overrides ... + void OnEntityInfoUpdatedName(AZ::EntityId entityId, const AZStd::string& name) override; + + // PrefabPublicNotifications overrides ... + void OnPrefabInstancePropagationEnd(); private: PrefabFocusOperationResult FocusOnPrefabInstance(InstanceOptionalReference focusedInstance); void RefreshInstanceFocusList(); + void RefreshInstanceFocusPath(); void OpenInstanceContainers(const AZStd::vector& instances) const; void CloseInstanceContainers(const AZStd::vector& instances) const; + //! The instance the editor is currently focusing on. InstanceOptionalReference m_focusedInstance; + //! The templateId of the focused instance. TemplateId m_focusedTemplateId; - AZStd::vector m_instanceFocusVector; + //! The list of instances going from the root (index 0) to the focused instance. + AZStd::vector m_instanceFocusHierarchy; + //! A path containing the names of the containers in the instance focus hierarchy, separated with a /. AZ::IO::Path m_instanceFocusPath; ContainerEntityInterface* m_containerEntityInterface = nullptr; FocusModeInterface* m_focusModeInterface = nullptr; InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr; - - bool m_isInitialized = false; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 25c83b89bc..287cbbaf96 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -26,6 +26,11 @@ namespace AzToolsFramework::Prefab public: AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}"); + //! Initializes the editor interfaces for Prefab Focus mode. + //! If this is not called on initialization, the Prefab Focus Mode functions will still work + //! but won't trigger the Editor APIs to visualize focus mode on the UI. + virtual void InitializeEditorInterfaces() = 0; + //! Set the focused prefab instance to the owning instance of the entityId provided. //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. virtual PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 526912cf29..bef08466ae 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -135,6 +136,10 @@ namespace AzToolsFramework return; } + // Initialize Editor functionality for the Prefab Focus Handler + auto prefabFocusInterface = AZ::Interface::Get(); + prefabFocusInterface->InitializeEditorInterfaces(); + EditorContextMenuBus::Handler::BusConnect(); EditorEventsBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); From c1948bf94ee057bc63a3ae10a8269391c3f30b0e Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Wed, 20 Oct 2021 15:04:21 -0500 Subject: [PATCH 063/152] Treat invalid p4 configuration as a warning for the scene settings save action so the processing popup will get the job results. Signed-off-by: Chris Galvan --- .../Plugins/EditorCommon/SaveUtilities/AsyncSaveRunner.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Editor/Plugins/EditorCommon/SaveUtilities/AsyncSaveRunner.cpp b/Code/Editor/Plugins/EditorCommon/SaveUtilities/AsyncSaveRunner.cpp index 0b514513ca..d05074c483 100644 --- a/Code/Editor/Plugins/EditorCommon/SaveUtilities/AsyncSaveRunner.cpp +++ b/Code/Editor/Plugins/EditorCommon/SaveUtilities/AsyncSaveRunner.cpp @@ -76,6 +76,7 @@ namespace AZ else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_ProviderIsDown) { message = "Failed to put entries/dependencies into source control as the provider is not available.\n"; + reportAsWarning = true; } else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_CertificateInvalid) { From 8e797982a5fe5792b39ec6e80c52cdc905796799 Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Wed, 20 Oct 2021 13:11:12 -0700 Subject: [PATCH 064/152] [LYN-7530] Fix matchmaking request type typo and add more matchmaking notifications (#4774) * [LYN-7530] Fix matchmaking request type typo and add more matchmaking notifications Signed-off-by: onecent1101 --- .../Matchmaking/IMatchmakingRequests.h | 29 ++++- .../Matchmaking/MatchmakingNotifications.h | 44 +++----- ...s.h => AWSGameLiftMatchmakingRequestBus.h} | 67 +---------- .../Include/Request/AWSGameLiftRequestBus.h | 51 +++++++++ .../Request/AWSGameLiftSessionRequestBus.h | 38 +++++++ .../AWSGameLiftClientLocalTicketTracker.cpp | 16 ++- .../AWSGameLiftClientLocalTicketTracker.h | 2 +- .../Source/AWSGameLiftClientManager.cpp | 9 ++ .../Source/AWSGameLiftClientManager.h | 30 ++++- .../AWSGameLiftClientSystemComponent.cpp | 35 +++--- .../Request/IAWSGameLiftInternalRequests.h | 2 +- ...WSGameLiftClientLocalTicketTrackerTest.cpp | 104 ++++++++++-------- .../Tests/AWSGameLiftClientManagerTest.cpp | 6 + .../Tests/AWSGameLiftClientMocks.h | 39 +++++-- .../awsgamelift_client_files.cmake | 4 +- ...quests.h => AWSGameLiftServerRequestBus.h} | 4 +- .../Source/AWSGameLiftServerManager.h | 2 +- .../awsgamelift_server_files.cmake | 2 +- 18 files changed, 303 insertions(+), 181 deletions(-) rename Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/{IAWSGameLiftRequests.h => AWSGameLiftMatchmakingRequestBus.h} (53%) create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftRequestBus.h create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftSessionRequestBus.h rename Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/{IAWSGameLiftServerRequests.h => AWSGameLiftServerRequestBus.h} (97%) diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h index 22b65f8340..c657e0edc7 100644 --- a/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h +++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h @@ -43,7 +43,7 @@ namespace AzFramework class IMatchmakingAsyncRequests { public: - AZ_RTTI(ISessionAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}"); + AZ_RTTI(IMatchmakingAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}"); IMatchmakingAsyncRequests() = default; virtual ~IMatchmakingAsyncRequests() = default; @@ -60,4 +60,31 @@ namespace AzFramework // @param stopMatchmakingRequest The request of StopMatchmaking operation virtual void StopMatchmakingAsync(const StopMatchmakingRequest& stopMatchmakingRequest) = 0; }; + + //! MatchmakingAsyncRequestNotifications + //! The notifications correspond to matchmaking async requests + class MatchmakingAsyncRequestNotifications + : public AZ::EBusTraits + { + public: + // Safeguard handler for multi-threaded use case + using MutexType = AZStd::recursive_mutex; + + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + + // OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes + virtual void OnAcceptMatchAsyncComplete() = 0; + + // OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes + // @param matchmakingTicketId The unique identifier for the matchmaking ticket + virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0; + + // OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes + virtual void OnStopMatchmakingAsyncComplete() = 0; + }; + using MatchmakingAsyncRequestNotificationBus = AZ::EBus; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h index ad61971a11..aa19b94b4a 100644 --- a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h +++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h @@ -13,36 +13,10 @@ namespace AzFramework { - //! MatchmakingAsyncRequestNotifications - //! The notifications correspond to matchmaking async requests - class MatchmakingAsyncRequestNotifications - : public AZ::EBusTraits - { - public: - // Safeguard handler for multi-threaded use case - using MutexType = AZStd::recursive_mutex; - - ////////////////////////////////////////////////////////////////////////// - // EBusTraits overrides - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - ////////////////////////////////////////////////////////////////////////// - - // OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes - virtual void OnAcceptMatchAsyncComplete() = 0; - - // OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes - // @param matchmakingTicketId The unique identifier for the matchmaking ticket - virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0; - - // OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes - virtual void OnStopMatchmakingAsyncComplete() = 0; - }; - using MatchmakingAsyncRequestNotificationBus = AZ::EBus; - //! MatchmakingNotifications //! The matchmaking notifications to listen for performing required operations - class MatchAcceptanceNotifications + //! based on matchmaking ticket event + class MatchmakingNotifications : public AZ::EBusTraits { public: @@ -55,8 +29,18 @@ namespace AzFramework static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; ////////////////////////////////////////////////////////////////////////// - // OnMatchAcceptance is fired when DescribeMatchmaking ticket status is REQUIRES_ACCEPTANCE + // OnMatchAcceptance is fired when match is found and pending on acceptance + // Use this notification to accept found match virtual void OnMatchAcceptance() = 0; + + // OnMatchComplete is fired when match is complete + virtual void OnMatchComplete() = 0; + + // OnMatchError is fired when match is processed with error + virtual void OnMatchError() = 0; + + // OnMatchFailure is fired when match is failed to complete + virtual void OnMatchFailure() = 0; }; - using MatchAcceptanceNotificationBus = AZ::EBus; + using MatchmakingNotificationBus = AZ::EBus; } // namespace AzFramework diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftMatchmakingRequestBus.h similarity index 53% rename from Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h rename to Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftMatchmakingRequestBus.h index c14ef559b2..8ab215d741 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftMatchmakingRequestBus.h @@ -5,75 +5,16 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - + #pragma once #include -#include #include #include -#include namespace AWSGameLift { - //! IAWSGameLiftRequests - //! GameLift Gem interfaces to configure client manager - class IAWSGameLiftRequests - { - public: - AZ_RTTI(IAWSGameLiftRequests, "{494167AD-1185-4AF3-8BF9-C8C37FC9C199}"); - - IAWSGameLiftRequests() = default; - virtual ~IAWSGameLiftRequests() = default; - - //! ConfigureGameLiftClient - //! Configure GameLift client to interact with Amazon GameLift service - //! @param region Specifies the AWS region to use - //! @return True if client configuration succeeds, false otherwise - virtual bool ConfigureGameLiftClient(const AZStd::string& region) = 0; - - //! CreatePlayerId - //! Create a new, random ID number for every player in every new game session. - //! @param includeBrackets Whether includes brackets in player id - //! @param includeDashes Whether includes dashes in player id - //! @return The player id to use in game session - virtual AZStd::string CreatePlayerId(bool includeBrackets, bool includeDashes) = 0; - }; - - // IAWSGameLiftRequests EBus wrapper for scripting - class AWSGameLiftRequests - : public AZ::EBusTraits - { - public: - using MutexType = AZStd::recursive_mutex; - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - }; - using AWSGameLiftRequestBus = AZ::EBus; - - // ISessionAsyncRequests EBus wrapper for scripting - class AWSGameLiftSessionAsyncRequests - : public AZ::EBusTraits - { - public: - using MutexType = AZStd::recursive_mutex; - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - }; - using AWSGameLiftSessionAsyncRequestBus = AZ::EBus; - - // ISessionRequests EBus wrapper for scripting - class AWSGameLiftSessionRequests - : public AZ::EBusTraits - { - public: - using MutexType = AZStd::recursive_mutex; - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - }; - using AWSGameLiftSessionRequestBus = AZ::EBus; - - // IMatchmakingAsyncRequests EBus wrapper for scripting + // IMatchmakingAsyncRequests EBus wrapper class AWSGameLiftMatchmakingAsyncRequests : public AZ::EBusTraits { @@ -84,7 +25,7 @@ namespace AWSGameLift }; using AWSGameLiftMatchmakingAsyncRequestBus = AZ::EBus; - // IMatchmakingRequests EBus wrapper for scripting + // IMatchmakingRequests EBus wrapper class AWSGameLiftMatchmakingRequests : public AZ::EBusTraits { @@ -121,7 +62,7 @@ namespace AWSGameLift virtual void StopPolling() = 0; }; - // IAWSGameLiftMatchmakingEventRequests EBus wrapper for scripting + // IAWSGameLiftMatchmakingEventRequests EBus wrapper class AWSGameLiftMatchmakingEventRequests : public AZ::EBusTraits { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftRequestBus.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftRequestBus.h new file mode 100644 index 0000000000..e1951d1e61 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftRequestBus.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AWSGameLift +{ + //! IAWSGameLiftRequests + //! GameLift Gem interfaces to configure GameLift client and other help functions, + //! like creating random GameLift player id + class IAWSGameLiftRequests + { + public: + AZ_RTTI(IAWSGameLiftRequests, "{494167AD-1185-4AF3-8BF9-C8C37FC9C199}"); + + IAWSGameLiftRequests() = default; + virtual ~IAWSGameLiftRequests() = default; + + //! ConfigureGameLiftClient + //! Configure GameLift client to interact with Amazon GameLift service + //! @param region Specifies the AWS region to use + //! @return True if client configuration succeeds, false otherwise + virtual bool ConfigureGameLiftClient(const AZStd::string& region) = 0; + + //! CreatePlayerId + //! Create a new, random ID number for every player in every new game session. + //! @param includeBrackets Whether includes brackets in player id + //! @param includeDashes Whether includes dashes in player id + //! @return The player id to use in game session + virtual AZStd::string CreatePlayerId(bool includeBrackets, bool includeDashes) = 0; + }; + + // IAWSGameLiftRequests EBus wrapper + class AWSGameLiftRequests + : public AZ::EBusTraits + { + public: + using MutexType = AZStd::recursive_mutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + }; + using AWSGameLiftRequestBus = AZ::EBus; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftSessionRequestBus.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftSessionRequestBus.h new file mode 100644 index 0000000000..c99509ca3f --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftSessionRequestBus.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace AWSGameLift +{ + // ISessionAsyncRequests EBus wrapper + class AWSGameLiftSessionAsyncRequests + : public AZ::EBusTraits + { + public: + using MutexType = AZStd::recursive_mutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + }; + using AWSGameLiftSessionAsyncRequestBus = AZ::EBus; + + // ISessionRequests EBus wrapper + class AWSGameLiftSessionRequests + : public AZ::EBusTraits + { + public: + using MutexType = AZStd::recursive_mutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + }; + using AWSGameLiftSessionRequestBus = AZ::EBus; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp index cc2f84cb35..2e978402cd 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp @@ -97,6 +97,7 @@ namespace AWSGameLift AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, "Matchmaking ticket %s is complete.", ticket.GetTicketId().c_str()); RequestPlayerJoinMatch(ticket, playerId); + AzFramework::MatchmakingNotificationBus::Broadcast(&AzFramework::MatchmakingNotifications::OnMatchComplete); m_status = TicketTrackerStatus::Idle; return; } @@ -104,25 +105,28 @@ namespace AWSGameLift ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::FAILED || ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::CANCELLED) { - AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, "Matchmaking ticket %s is not complete, %s", - ticket.GetTicketId().c_str(), ticket.GetStatusReason().c_str()); + AZ_Warning(AWSGameLiftClientLocalTicketTrackerName, false, "Matchmaking ticket %s is not complete, %s", + ticket.GetTicketId().c_str(), ticket.GetStatusMessage().c_str()); + AzFramework::MatchmakingNotificationBus::Broadcast(&AzFramework::MatchmakingNotifications::OnMatchFailure); m_status = TicketTrackerStatus::Idle; return; } else if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::REQUIRES_ACCEPTANCE) { - // broadcast acceptance requires to player - AzFramework::MatchAcceptanceNotificationBus::Broadcast(&AzFramework::MatchAcceptanceNotifications::OnMatchAcceptance); + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, "Matchmaking ticket %s is pending on acceptance, %s.", + ticket.GetTicketId().c_str(), ticket.GetStatusMessage().c_str()); + AzFramework::MatchmakingNotificationBus::Broadcast(&AzFramework::MatchmakingNotifications::OnMatchAcceptance); } else { AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, "Matchmaking ticket %s is processing, %s.", - ticket.GetTicketId().c_str(), ticket.GetStatusReason().c_str()); + ticket.GetTicketId().c_str(), ticket.GetStatusMessage().c_str()); } } else { AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, "Unable to find expected ticket with id %s", ticketId.c_str()); + AzFramework::MatchmakingNotificationBus::Broadcast(&AzFramework::MatchmakingNotifications::OnMatchError); } } else @@ -130,11 +134,13 @@ namespace AWSGameLift AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, AWSGameLiftErrorMessageTemplate, describeMatchmakingOutcome.GetError().GetExceptionName().c_str(), describeMatchmakingOutcome.GetError().GetMessage().c_str()); + AzFramework::MatchmakingNotificationBus::Broadcast(&AzFramework::MatchmakingNotifications::OnMatchError); } } else { AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, AWSGameLiftClientMissingErrorMessage); + AzFramework::MatchmakingNotificationBus::Broadcast(&AzFramework::MatchmakingNotifications::OnMatchError); } m_waitEvent.try_acquire_for(AZStd::chrono::milliseconds(m_pollingPeriodInMS)); } diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h index 04bdd71c85..9fd7f76e1d 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h @@ -12,7 +12,7 @@ #include #include -#include +#include #include diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp index 224f16481e..4ee2d31ebf 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -75,7 +76,15 @@ namespace AWSGameLift bool AWSGameLiftClientManager::ConfigureGameLiftClient(const AZStd::string& region) { AZ::Interface::Get()->SetGameLiftClient(nullptr); + Aws::Client::ClientConfiguration clientConfig; + AWSCore::AwsApiJobConfig* defaultConfig = nullptr; + AWSCore::AWSCoreRequestBus::BroadcastResult(defaultConfig, &AWSCore::AWSCoreRequests::GetDefaultConfig); + if (defaultConfig) + { + clientConfig = defaultConfig->GetClientConfiguration(); + } + // Set up client endpoint or region AZStd::string localEndpoint = ""; #if defined(AWSGAMELIFT_DEV) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h index 1f32b69f75..8a0c91c36d 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h @@ -8,10 +8,13 @@ #pragma once +#include #include #include -#include +#include +#include +#include namespace AWSGameLift { @@ -23,22 +26,37 @@ namespace AWSGameLift struct AWSGameLiftStartMatchmakingRequest; struct AWSGameLiftStopMatchmakingRequest; - // MatchAcceptanceNotificationBus EBus handler for scripting - class AWSGameLiftMatchAcceptanceNotificationBusHandler - : public AzFramework::MatchAcceptanceNotificationBus::Handler + // MatchmakingNotificationBus EBus handler for scripting + class AWSGameLiftMatchmakingNotificationBusHandler + : public AzFramework::MatchmakingNotificationBus::Handler , public AZ::BehaviorEBusHandler { public: AZ_EBUS_BEHAVIOR_BINDER( - AWSGameLiftMatchAcceptanceNotificationBusHandler, + AWSGameLiftMatchmakingNotificationBusHandler, "{CBE057D3-F5CE-46D3-B02D-8A6A1446B169}", AZ::SystemAllocator, - OnMatchAcceptance); + OnMatchAcceptance, OnMatchComplete, OnMatchError, OnMatchFailure); void OnMatchAcceptance() override { Call(FN_OnMatchAcceptance); } + + void OnMatchComplete() override + { + Call(FN_OnMatchComplete); + } + + void OnMatchError() override + { + Call(FN_OnMatchError); + } + + void OnMatchFailure() override + { + Call(FN_OnMatchFailure); + } }; // MatchmakingAsyncRequestNotificationBus EBus handler for scripting diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp index 981c1599c9..d256c6c33c 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp @@ -65,13 +65,6 @@ namespace AWSGameLift ->Event("CreatePlayerId", &AWSGameLiftRequestBus::Events::CreatePlayerId, { { { "IncludeBrackets", "" }, { "IncludeDashes", "" } } }); - - behaviorContext->EBus("AWSGameLiftMatchmakingEventRequestBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") - ->Event("StartPolling", &AWSGameLiftMatchmakingEventRequestBus::Events::StartPolling, - { { { "TicketId", "" }, - { "PlayerId", "" } } }) - ->Event("StopPolling", &AWSGameLiftMatchmakingEventRequestBus::Events::StopPolling); } } @@ -128,7 +121,7 @@ namespace AWSGameLift if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->EBus("AWSGameLiftMatchmakingAsyncRequestBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift/Matchmaking") ->Event("AcceptMatchAsync", &AWSGameLiftMatchmakingAsyncRequestBus::Events::AcceptMatchAsync, { { { "AcceptMatchRequest", "" } } }) ->Event("StartMatchmakingAsync", &AWSGameLiftMatchmakingAsyncRequestBus::Events::StartMatchmakingAsync, @@ -137,20 +130,28 @@ namespace AWSGameLift { { { "StopMatchmakingRequest", "" } } }); behaviorContext->EBus("AWSGameLiftMatchmakingAsyncRequestNotificationBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift/Matchmaking") ->Handler(); behaviorContext->EBus("AWSGameLiftMatchmakingRequestBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") - ->Event("AcceptMatch", &AWSGameLiftMatchmakingRequestBus::Events::AcceptMatch, { { { "AcceptMatchRequest", "" } } }) + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift/Matchmaking") + ->Event("AcceptMatch", &AWSGameLiftMatchmakingRequestBus::Events::AcceptMatch, + { { { "AcceptMatchRequest", "" } } }) ->Event("StartMatchmaking", &AWSGameLiftMatchmakingRequestBus::Events::StartMatchmaking, { { { "StartMatchmakingRequest", "" } } }) ->Event("StopMatchmaking", &AWSGameLiftMatchmakingRequestBus::Events::StopMatchmaking, { { { "StopMatchmakingRequest", "" } } }); - behaviorContext->EBus("AWSGameLiftMatchAcceptanceNotificationBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") - ->Handler(); + behaviorContext->EBus("AWSGameLiftMatchmakingEventRequestBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift/Matchmaking") + ->Event("StartPolling", &AWSGameLiftMatchmakingEventRequestBus::Events::StartPolling, + { { { "TicketId", "" }, + { "PlayerId", "" } } }) + ->Event("StopPolling", &AWSGameLiftMatchmakingEventRequestBus::Events::StopPolling); + + behaviorContext->EBus("AWSGameLiftMatchmakingNotificationBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift/Matchmaking") + ->Handler(); } } @@ -166,7 +167,7 @@ namespace AWSGameLift if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->EBus("AWSGameLiftSessionAsyncRequestBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift/Session") ->Event("CreateSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::CreateSessionAsync, { { { "CreateSessionRequest", "" } } }) ->Event("JoinSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::JoinSessionAsync, { { { "JoinSessionRequest", "" } } }) @@ -175,11 +176,11 @@ namespace AWSGameLift ->Event("LeaveSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::LeaveSessionAsync); behaviorContext->EBus("AWSGameLiftSessionAsyncRequestNotificationBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift/Session") ->Handler(); behaviorContext->EBus("AWSGameLiftSessionRequestBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift/Session") ->Event("CreateSession", &AWSGameLiftSessionRequestBus::Events::CreateSession, { { { "CreateSessionRequest", "" } } }) ->Event("JoinSession", &AWSGameLiftSessionRequestBus::Events::JoinSession, { { { "JoinSessionRequest", "" } } }) ->Event("SearchSessions", &AWSGameLiftSessionRequestBus::Events::SearchSessions, { { { "SearchSessionsRequest", "" } } }) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h index dbac9a798a..7e17f085b3 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h @@ -20,7 +20,7 @@ namespace Aws namespace AWSGameLift { - //! IAWSGameLiftRequests + //! IAWSGameLiftInternalRequests //! GameLift Gem internal interface which is used to fetch gem global GameLift client class IAWSGameLiftInternalRequests { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp index 4dc4dd85f6..be72de555e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp @@ -82,27 +82,12 @@ protected: m_gameliftClientMockPtr.reset(); } - void WaitForProcessFinish(uint64_t expectedNum) + void WaitForProcessFinish(AZStd::function processFinishCondition) { int processingTime = 0; while (processingTime < TEST_WAIT_MAXIMUM_TIME_MS) { - if (::UnitTest::TestRunner::Instance().m_numAssertsFailed == expectedNum) - { - AZ_TEST_STOP_TRACE_SUPPRESSION(expectedNum); - return; - } - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(TEST_WAIT_BUFFER_TIME_MS)); - processingTime += TEST_WAIT_BUFFER_TIME_MS; - } - } - - void WaitForProcessFinish() - { - int processingTime = 0; - while (processingTime < TEST_WAIT_MAXIMUM_TIME_MS) - { - if (m_gameliftClientTicketTracker->IsTrackerIdle()) + if (processFinishCondition()) { return; } @@ -119,19 +104,27 @@ public: TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithoutClientSetup_GetExpectedErrors) { AZ::Interface::Get()->SetGameLiftClient(nullptr); + + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1); ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); } TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_MultipleCallsWithoutClientSetup_GetExpectedErrors) { AZ::Interface::Get()->SetGameLiftClient(nullptr); + + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1); ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -144,9 +137,12 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButWithFailedOu .Times(1) .WillOnce(::testing::Return(outcome)); + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1); ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -161,9 +157,12 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithMoreThanOne .Times(1) .WillOnce(::testing::Return(outcome)); + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1); ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -189,13 +188,15 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithCompleteSta .Times(1) .WillOnce(::testing::Return(outcome)); - SessionHandlingClientRequestsMock handlerMock; - EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + SessionHandlingClientRequestsMock sessionHandlerMock; + EXPECT_CALL(sessionHandlerMock, RequestPlayerJoinSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(true)); + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(); + WaitForProcessFinish([this](){ return m_gameliftClientTicketTracker->IsTrackerIdle(); }); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchComplete == 1); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -217,9 +218,12 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButNoPlayerSess .Times(1) .WillOnce(::testing::Return(outcome)); + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([this](){ return m_gameliftClientTicketTracker->IsTrackerIdle(); }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchComplete == 1); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -245,14 +249,17 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButFailedToJoin .Times(1) .WillOnce(::testing::Return(outcome)); - SessionHandlingClientRequestsMock handlerMock; - EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + SessionHandlingClientRequestsMock sessionHandlerMock; + EXPECT_CALL(sessionHandlerMock, RequestPlayerJoinSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(false)); + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([this](){ return m_gameliftClientTicketTracker->IsTrackerIdle(); }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchComplete == 1); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -269,9 +276,10 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketTimeOu .Times(1) .WillOnce(::testing::Return(outcome)); - AZ_TEST_START_TRACE_SUPPRESSION; + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([this](){ return m_gameliftClientTicketTracker->IsTrackerIdle(); }); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchFailure == 1); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -288,9 +296,10 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketFailed .Times(1) .WillOnce(::testing::Return(outcome)); - AZ_TEST_START_TRACE_SUPPRESSION; + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([this](){ return m_gameliftClientTicketTracker->IsTrackerIdle(); }); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchFailure == 1); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -307,9 +316,10 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketCancel .Times(1) .WillOnce(::testing::Return(outcome)); - AZ_TEST_START_TRACE_SUPPRESSION; + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(1); + WaitForProcessFinish([this](){ return m_gameliftClientTicketTracker->IsTrackerIdle(); }); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchFailure == 1); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -342,13 +352,15 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallAndTicketComple .WillOnce(::testing::Return(outcome1)) .WillOnce(::testing::Return(outcome2)); - SessionHandlingClientRequestsMock handlerMock; - EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + SessionHandlingClientRequestsMock sessionHandlerMock; + EXPECT_CALL(sessionHandlerMock, RequestPlayerJoinSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(true)); + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(); + WaitForProcessFinish([this](){ return m_gameliftClientTicketTracker->IsTrackerIdle(); }); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchComplete == 1); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } @@ -365,7 +377,9 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_RequiresAcceptanceA connectionInfo.SetIpAddress("DummyIpAddress"); connectionInfo.SetPort(123); connectionInfo.AddMatchedPlayerSessions( - Aws::GameLift::Model::MatchedPlayerSession().WithPlayerId("player1").WithPlayerSessionId("playersession1")); + Aws::GameLift::Model::MatchedPlayerSession() + .WithPlayerId("player1") + .WithPlayerSessionId("playersession1")); Aws::GameLift::Model::MatchmakingTicket ticket2; ticket2.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); @@ -379,13 +393,15 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_RequiresAcceptanceA .WillOnce(::testing::Return(outcome1)) .WillOnce(::testing::Return(outcome2)); - MatchAcceptanceNotificationsHandlerMock handlerMock1; - EXPECT_CALL(handlerMock1, OnMatchAcceptance()).Times(1); - - SessionHandlingClientRequestsMock handlerMock2; - EXPECT_CALL(handlerMock2, RequestPlayerJoinSession(::testing::_)).Times(1).WillOnce(::testing::Return(true)); + SessionHandlingClientRequestsMock sessionHandlerMock; + EXPECT_CALL(sessionHandlerMock, RequestPlayerJoinSession(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(true)); + MatchmakingNotificationsHandlerMock matchmakingHandlerMock; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish(); + WaitForProcessFinish([this](){ return m_gameliftClientTicketTracker->IsTrackerIdle(); }); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchAcceptance == 1); + ASSERT_TRUE(matchmakingHandlerMock.m_numMatchComplete == 1); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp index 1c3e8726fd..6ce0cb8f64 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp @@ -266,6 +266,8 @@ const char* const AWSGameLiftClientManagerTest::DummyPlayerId = "dummyPlayerId"; TEST_F(AWSGameLiftClientManagerTest, ConfigureGameLiftClient_CallWithoutRegion_GetFalseAsResult) { + AWSCoreRequestsHandlerMock coreHandlerMock; + EXPECT_CALL(coreHandlerMock, GetDefaultConfig()).Times(1).WillOnce(nullptr); AZ_TEST_START_TRACE_SUPPRESSION; auto result = m_gameliftClientManager->ConfigureGameLiftClient(""); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message @@ -274,6 +276,8 @@ TEST_F(AWSGameLiftClientManagerTest, ConfigureGameLiftClient_CallWithoutRegion_G TEST_F(AWSGameLiftClientManagerTest, ConfigureGameLiftClient_CallWithoutCredential_GetFalseAsResult) { + AWSCoreRequestsHandlerMock coreHandlerMock; + EXPECT_CALL(coreHandlerMock, GetDefaultConfig()).Times(1).WillOnce(nullptr); AWSResourceMappingRequestsHandlerMock handlerMock; EXPECT_CALL(handlerMock, GetDefaultRegion()).Times(1).WillOnce(::testing::Return("us-west-2")); AZ_TEST_START_TRACE_SUPPRESSION; @@ -284,6 +288,8 @@ TEST_F(AWSGameLiftClientManagerTest, ConfigureGameLiftClient_CallWithoutCredenti TEST_F(AWSGameLiftClientManagerTest, ConfigureGameLiftClient_CallWithRegionAndCredential_GetTrueAsResult) { + AWSCoreRequestsHandlerMock coreHandlerMock; + EXPECT_CALL(coreHandlerMock, GetDefaultConfig()).Times(1).WillOnce(nullptr); AWSCredentialRequestsHandlerMock handlerMock; EXPECT_CALL(handlerMock, GetCredentialsProvider()) .Times(1) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h index 01afec5c3f..d685f61d30 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h @@ -9,9 +9,9 @@ #pragma once #include -#include #include #include +#include #include #include @@ -76,21 +76,44 @@ public: MOCK_METHOD0(OnStopMatchmakingAsyncComplete, void()); }; -class MatchAcceptanceNotificationsHandlerMock - : public AzFramework::MatchAcceptanceNotificationBus::Handler +class MatchmakingNotificationsHandlerMock + : public AzFramework::MatchmakingNotificationBus::Handler { public: - MatchAcceptanceNotificationsHandlerMock() + MatchmakingNotificationsHandlerMock() { - AzFramework::MatchAcceptanceNotificationBus::Handler::BusConnect(); + AzFramework::MatchmakingNotificationBus::Handler::BusConnect(); } - ~MatchAcceptanceNotificationsHandlerMock() + ~MatchmakingNotificationsHandlerMock() { - AzFramework::MatchAcceptanceNotificationBus::Handler::BusDisconnect(); + AzFramework::MatchmakingNotificationBus::Handler::BusDisconnect(); } - MOCK_METHOD0(OnMatchAcceptance, void()); + void OnMatchAcceptance() override + { + ++m_numMatchAcceptance; + } + + void OnMatchComplete() override + { + ++m_numMatchComplete; + } + + void OnMatchError() override + { + ++m_numMatchError; + } + + void OnMatchFailure() override + { + ++m_numMatchFailure; + } + + int m_numMatchAcceptance = 0; + int m_numMatchComplete = 0; + int m_numMatchError = 0; + int m_numMatchFailure = 0; }; class SessionAsyncRequestNotificationsHandlerMock diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake index 629d1596cf..fe22e0c65c 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake @@ -17,7 +17,9 @@ set(FILES Include/Request/AWSGameLiftSearchSessionsRequest.h Include/Request/AWSGameLiftStartMatchmakingRequest.h Include/Request/AWSGameLiftStopMatchmakingRequest.h - Include/Request/IAWSGameLiftRequests.h + Include/Request/AWSGameLiftRequestBus.h + Include/Request/AWSGameLiftSessionRequestBus.h + Include/Request/AWSGameLiftMatchmakingRequestBus.h Source/Activity/AWSGameLiftActivityUtils.cpp Source/Activity/AWSGameLiftActivityUtils.h Source/Activity/AWSGameLiftAcceptMatchActivity.cpp diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/AWSGameLiftServerRequestBus.h similarity index 97% rename from Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h rename to Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/AWSGameLiftServerRequestBus.h index 777086e633..27096b9fe8 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/AWSGameLiftServerRequestBus.h @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - + #pragma once #include @@ -45,7 +45,7 @@ namespace AWSGameLift virtual bool StopMatchBackfill(const AZStd::string& ticketId) = 0; }; - // IAWSGameLiftServerRequests EBus wrapper for scripting + // IAWSGameLiftServerRequests EBus wrapper class AWSGameLiftServerRequests : public AZ::EBusTraits { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h index ee21751fe9..6e7ce4e005 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h @@ -20,7 +20,7 @@ #include #include -#include +#include namespace AWSGameLift { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake index 9039c9943e..70dfd38fc3 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake @@ -10,7 +10,7 @@ set(FILES ../AWSGameLiftCommon/Include/AWSGameLiftPlayer.h ../AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp ../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h - Include/Request/IAWSGameLiftServerRequests.h + Include/Request/AWSGameLiftServerRequestBus.h Source/AWSGameLiftServerManager.cpp Source/AWSGameLiftServerManager.h Source/AWSGameLiftServerSystemComponent.cpp From 9e8e08a8155e575fd42b2838cb3179227d813360 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Wed, 20 Oct 2021 14:10:11 -0700 Subject: [PATCH 065/152] Throwing up Depth of Field WIP so Galib can investigate AP crash with shader processing Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- .../Common/Assets/Passes/NewDepthOfField.pass | 143 +++++++++++++ .../Passes/NewDepthOfFieldComposite.pass | 37 ++++ .../Passes/NewDepthOfFieldDownsample.pass | 72 +++++++ .../Passes/NewDepthOfFieldFilterLarge.pass | 62 ++++++ .../Passes/NewDepthOfFieldFilterSmall.pass | 62 ++++++ .../Assets/Passes/NewDepthOfFieldTile3x3.pass | 57 +++++ .../Passes/NewDepthOfFieldTileReduce.pass | 63 ++++++ .../Assets/Passes/PassTemplates.azasset | 28 +++ .../Assets/Passes/PostProcessParent.pass | 29 ++- .../Shaders/PostProcessing/DepthOfField.azsli | 8 + .../NewDepthOfFieldCommon.azsli | 29 +++ .../NewDepthOfFieldComposite.azsl | 80 +++++++ .../NewDepthOfFieldComposite.shader | 31 +++ .../NewDepthOfFieldDownsample.azsl | 100 +++++++++ .../NewDepthOfFieldDownsample.shader | 22 ++ .../NewDepthOfFieldFilterLarge.azsl | 50 +++++ .../NewDepthOfFieldFilterLarge.shader | 22 ++ .../NewDepthOfFieldFilterSmall.azsl | 74 +++++++ .../NewDepthOfFieldFilterSmall.shader | 22 ++ .../NewDepthOfFieldTile3x3.azsl | 66 ++++++ .../NewDepthOfFieldTile3x3.shader | 22 ++ .../NewDepthOfFieldTileReduce.azsl | 83 ++++++++ .../NewDepthOfFieldTileReduce.shader | 14 ++ .../atom_feature_common_asset_files.cmake | 73 +++++-- .../Code/Source/CommonSystemComponent.cpp | 9 + .../PostProcessing/NewDepthOfFieldPasses.cpp | 196 ++++++++++++++++++ .../PostProcessing/NewDepthOfFieldPasses.h | 117 +++++++++++ .../Code/Source/PostProcessing/TaaPass.cpp | 2 +- .../Code/atom_feature_common_files.cmake | 2 + .../Atom/RHI.Reflect/ShaderInputNameIndex.h | 4 + .../RHI.Reflect/ShaderInputNameIndex.cpp | 6 + .../RPI/Assets/ShaderLib/Atom/RPI/Math.azsli | 40 ++++ .../Atom/RPI.Public/Pass/PassAttachment.h | 8 +- .../Include/Atom/RPI.Public/Pass/RenderPass.h | 2 +- .../RPI.Reflect/Pass/PassAttachmentReflect.h | 7 + .../Source/RPI.Public/Pass/PassAttachment.cpp | 1 + .../Source/RPI.Public/Pass/RenderPass.cpp | 42 +++- .../Pass/PassAttachmentReflect.cpp | 3 +- 38 files changed, 1657 insertions(+), 31 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldComposite.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldDownsample.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldFilterLarge.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldFilterSmall.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldTile3x3.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldTileReduce.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.shader create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.shader create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader create mode 100644 Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass new file mode 100644 index 0000000000..e7c6a23c6c --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass @@ -0,0 +1,143 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "NewDepthOfFieldTemplate", + "PassClass": "NewDepthOfFieldParentPass", + "Slots": [ + { + "Name": "Depth", + "SlotType": "Input" + }, + { + "Name": "LightingBuffer", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + } + ], + "PassRequests": [ + { + "Name": "Downsample", + "TemplateName": "NewDepthOfFieldDownsampleTemplate", + + "Connections": [ + { + "LocalSlot": "ColorInput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "LightingBuffer" + } + }, + { + "LocalSlot": "DepthInput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Depth" + } + } + ] + }, + { + "Name": "TileReduce", + "TemplateName": "NewDepthOfFieldTileReduceTemplate", + + "Connections": [ + { + "LocalSlot": "ColorAndCocInput", + "AttachmentRef": { + "Pass": "Downsample", + "Attachment": "OutputColorAndCoC" + } + } + ] + }, + { + "Name": "Tile3x3", + "TemplateName": "NewDepthOfFieldTile3x3Template", + + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "TileReduce", + "Attachment": "MinMaxCoC" + } + } + ] + }, + { + "Name": "LargeFilter", + "TemplateName": "NewDepthOfFieldFilterLargeTemplate", + + "Connections": [ + { + "LocalSlot": "ColorAndCoc", + "AttachmentRef": { + "Pass": "Downsample", + "Attachment": "OutputColorAndCoC" + } + }, + { + "LocalSlot": "CocTile", + "AttachmentRef": { + "Pass": "Tile3x3", + "Attachment": "Output" + } + } + ] + }, + { + "Name": "SmallFilter", + "TemplateName": "NewDepthOfFieldFilterSmallTemplate", + + "Connections": [ + { + "LocalSlot": "ColorAndCoc", + "AttachmentRef": { + "Pass": "LargeFilter", + "Attachment": "OutputColorAndCoc" + } + }, + { + "LocalSlot": "CocTile", + "AttachmentRef": { + "Pass": "Tile3x3", + "Attachment": "Output" + } + } + ] + }, + { + "Name": "Composite", + "TemplateName": "NewDepthOfFieldCompositeTemplate", + + "Connections": [ + { + "LocalSlot": "Depth", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Depth" + } + }, + { + "LocalSlot": "HalfResColorAndCoC", + "AttachmentRef": { + "Pass": "SmallFilter", + "Attachment": "OutputColorAndCoc" + } + }, + { + "LocalSlot": "ColorInputOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "LightingBuffer" + } + } + ] + } + ] + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldComposite.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldComposite.pass new file mode 100644 index 0000000000..522ae78fa5 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldComposite.pass @@ -0,0 +1,37 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "NewDepthOfFieldCompositeTemplate", + "PassClass": "FullScreenTriangle", + "Slots": [ + { + "Name": "Depth", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderImageDimensionsConstant": "m_fullResDimensions" + }, + { + "Name": "HalfResColorAndCoC", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderImageDimensionsConstant": "m_halfResDimensions" + }, + { + "Name": "ColorInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/NewDepthOfFieldComposite.shader" + }, + "PipelineViewTag": "MainCamera" + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldDownsample.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldDownsample.pass new file mode 100644 index 0000000000..2f1787c6cd --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldDownsample.pass @@ -0,0 +1,72 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "NewDepthOfFieldDownsampleTemplate", + "PassClass": "FullScreenTriangle", + "Slots": [ + { + "Name": "ColorInput", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderImageDimensionsConstant": "m_inputDimensions" + }, + { + "Name": "DepthInput", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "AspectFlags": [ + "Depth" + ] + } + }, + { + "Name": "OutputColorAndCoC", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "ShaderImageDimensionsConstant": "m_outputDimensions", + "LoadStoreAction": { + "LoadAction": "Clear" + } + } + ], + "ImageAttachments": [ + { + "Name": "OutputAttachment", + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "ColorInput" + }, + "Multipliers": { + "WidthMultiplier": 0.5, + "HeightMultiplier": 0.5 + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT" + } + } + ], + "Connections": [ + { + "LocalSlot": "OutputColorAndCoC", + "AttachmentRef": { + "Pass": "This", + "Attachment": "OutputAttachment" + } + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/NewDepthOfFieldDownsample.shader" + }, + "PipelineViewTag": "MainCamera" + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldFilterLarge.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldFilterLarge.pass new file mode 100644 index 0000000000..7b40009cec --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldFilterLarge.pass @@ -0,0 +1,62 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "NewDepthOfFieldFilterLargeTemplate", + "PassClass": "NewDepthOfFieldFilterPass", + "Slots": [ + { + "Name": "ColorAndCoc", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderImageDimensionsConstant": "m_textureDimensions" + }, + { + "Name": "CocTile", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "OutputColorAndCoc", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "LoadAction": "Clear" + } + } + ], + "ImageAttachments": [ + { + "Name": "OutputAttachment", + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "ColorAndCoc" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT" + } + } + ], + "Connections": [ + { + "LocalSlot": "OutputColorAndCoc", + "AttachmentRef": { + "Pass": "This", + "Attachment": "OutputAttachment" + } + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader" + }, + "PipelineViewTag": "MainCamera" + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldFilterSmall.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldFilterSmall.pass new file mode 100644 index 0000000000..35a658a3b1 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldFilterSmall.pass @@ -0,0 +1,62 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "NewDepthOfFieldFilterSmallTemplate", + "PassClass": "NewDepthOfFieldFilterPass", + "Slots": [ + { + "Name": "ColorAndCoc", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderImageDimensionsConstant": "m_textureDimensions" + }, + { + "Name": "CocTile", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "OutputColorAndCoc", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "LoadAction": "Clear" + } + } + ], + "ImageAttachments": [ + { + "Name": "OutputAttachment", + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "ColorAndCoc" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT" + } + } + ], + "Connections": [ + { + "LocalSlot": "OutputColorAndCoc", + "AttachmentRef": { + "Pass": "This", + "Attachment": "OutputAttachment" + } + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader" + }, + "PipelineViewTag": "MainCamera" + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldTile3x3.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldTile3x3.pass new file mode 100644 index 0000000000..93ae645c83 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldTile3x3.pass @@ -0,0 +1,57 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "NewDepthOfFieldTile3x3Template", + "PassClass": "FullScreenTriangle", + "Slots": [ + { + "Name": "Input", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderImageDimensionsConstant": "m_textureDimensions" + }, + { + "Name": "Output", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "LoadAction": "Clear" + } + } + ], + "ImageAttachments": [ + { + "Name": "OutputAttachment", + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "Input" + } + }, + "ImageDescriptor": { + "Format": "R16G16_SNORM" + } + } + ], + "Connections": [ + { + "LocalSlot": "Output", + "AttachmentRef": { + "Pass": "This", + "Attachment": "OutputAttachment" + } + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader" + }, + "PipelineViewTag": "MainCamera" + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldTileReduce.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldTileReduce.pass new file mode 100644 index 0000000000..2bcf0ef32d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfFieldTileReduce.pass @@ -0,0 +1,63 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "NewDepthOfFieldTileReduceTemplate", + "PassClass": "NewDepthOfFieldTileReducePass", + "Slots": [ + { + "Name": "ColorAndCocInput", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderImageDimensionsConstant": "m_inputDimensions" + }, + { + "Name": "MinMaxCoC", + "SlotType": "Output", + "ScopeAttachmentUsage": "Shader", + "ShaderImageDimensionsConstant": "m_outputDimensions", + "LoadStoreAction": { + "LoadAction": "Clear" + } + } + ], + "ImageAttachments": [ + { + "Name": "MinMaxCoCAttachment", + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "ColorAndCocInput" + }, + "Multipliers": { + // 1/16 = 0.0625 + "WidthMultiplier": 0.0625, + "HeightMultiplier": 0.0625 + } + }, + "ImageDescriptor": { + "Format": "R16G16_SNORM" + } + } + ], + "Connections": [ + { + "LocalSlot": "MinMaxCoC", + "AttachmentRef": { + "Pass": "This", + "Attachment": "MinMaxCoCAttachment" + } + } + ], + "PassData": { + "$type": "ComputePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader" + }, + "PipelineViewTag": "MainCamera" + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset index 76285b00c7..355e930237 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset +++ b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset @@ -185,6 +185,34 @@ "Path": "Passes/DepthOfFieldWriteFocusDepthFromGpu.pass" }, { + "Name": "NewDepthOfFieldTemplate", + "Path": "Passes/NewDepthOfField.pass" + }, + { + "Name": "NewDepthOfFieldDownsampleTemplate", + "Path": "Passes/NewDepthOfFieldDownsample.pass" + }, + { + "Name": "NewDepthOfFieldTileReduceTemplate", + "Path": "Passes/NewDepthOfFieldTileReduce.pass" + }, + { + "Name": "NewDepthOfFieldTile3x3Template", + "Path": "Passes/NewDepthOfFieldTile3x3.pass" + }, + { + "Name": "NewDepthOfFieldFilterLargeTemplate", + "Path": "Passes/NewDepthOfFieldFilterLarge.pass" + }, + { + "Name": "NewDepthOfFieldFilterSmallTemplate", + "Path": "Passes/NewDepthOfFieldFilterSmall.pass" + }, + { + "Name": "NewDepthOfFieldCompositeTemplate", + "Path": "Passes/NewDepthOfFieldComposite.pass" + }, + { "Name": "EsmShadowmapsTemplate", "Path": "Passes/EsmShadowmaps.pass" }, diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass index fb27770da3..dc1c0f1664 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass @@ -112,20 +112,41 @@ } ] }, + //{ + // "Name": "DepthOfFieldPass", + // "TemplateName": "DepthOfFieldTemplate", + // "Enabled": true, + // "Connections": [ + // { + // "LocalSlot": "DoFColorInput", + // "AttachmentRef": { + // "Pass": "TaaPass", + // "Attachment": "OutputColor" + // } + // }, + // { + // "LocalSlot": "DoFDepthInput", + // "AttachmentRef": { + // "Pass": "Parent", + // "Attachment": "Depth" + // } + // } + // ] + //}, { "Name": "DepthOfFieldPass", - "TemplateName": "DepthOfFieldTemplate", + "TemplateName": "NewDepthOfFieldTemplate", "Enabled": true, "Connections": [ { - "LocalSlot": "DoFColorInput", + "LocalSlot": "LightingBuffer", "AttachmentRef": { "Pass": "TaaPass", "Attachment": "OutputColor" } }, { - "LocalSlot": "DoFDepthInput", + "LocalSlot": "Depth", "AttachmentRef": { "Pass": "Parent", "Attachment": "Depth" @@ -142,7 +163,7 @@ "LocalSlot": "InputOutput", "AttachmentRef": { "Pass": "DepthOfFieldPass", - "Attachment": "DoFOutput" + "Attachment": "LightingBuffer" } } ] diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfField.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfField.azsli index bc94b4a793..f4c396253f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfField.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfField.azsli @@ -14,6 +14,14 @@ inline float InvertDepth(float depth) return 1.0f - depth; } +inline float4 InvertDepth(float4 depth) +{ + // Convert depth from [1.0 - 0.0] to [0.0 - 1.0]. + // Set the front(near side) to 0.0 and the back(far side) to 1.0. + + return float4(1, 1, 1, 1) - depth; +} + inline float ConvertDofFactor(float depth, float far, float near, float focusDistance) { // dofFactor : The value Calculated from depth. diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli new file mode 100644 index 0000000000..fc98cee055 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli @@ -0,0 +1,29 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + + +#include + +#define COC_EPSILON 0.0001 + +#define SAMPLES_LOOP_1 8 +#define SAMPLES_LOOP_2 16 +#define SAMPLES_LOOP_3 24 + +// Must match the struct in NewDepthOfFieldPasses.cpp +struct NewDepthOfFieldConstants +{ + float4 samplePositions[60]; // XY are sample positions (normalized so max lenght is 1) + // Z is the length of XY (0 - 1) + // W is unused + + +}; + + + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl new file mode 100644 index 0000000000..8a766c71d9 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl @@ -0,0 +1,80 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include "NewDepthOfFieldCommon.azsli" +#include "DepthOfField.azsli" + +#include + +#define COC_EPSILON 0.0001 + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_depth; + Texture2D m_halfResColorAndCoc; + + // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + float4 m_fullResDimensions; + float4 m_halfResDimensions; + + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +} + +PSOutput MainPS(VSOutput IN) +{ + // Sampling positions + float2 fullResPixelPos = IN.m_position.xy; + float2 halfResPixelPos = fullResPixelPos * 0.5f; + float2 fullResUV = fullResPixelPos * PassSrg::m_fullResDimensions.zw; + float2 halfResUV = halfResPixelPos * PassSrg::m_halfResDimensions.zw; + + // Full res CoC (Circle of Confusion) + float depth = PassSrg::m_depth.Sample(PassSrg::LinearSampler, fullResUV); + float far = ViewSrg::m_dof.m_cameraParameters.x; + float near = ViewSrg::m_dof.m_cameraParameters.y; + float focusDistance = ViewSrg::m_dof.m_cameraParameters.z; + float coc = ConvertDofFactor(InvertDepth(depth), far, near, focusDistance); + + // Calculate Alpha + float cocRadius = abs(coc) * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; + float maxPixelDist = max(PassSrg::m_halfResDimensions.z, PassSrg::m_halfResDimensions.w); + float alpha = saturate(0.25f * cocRadius / maxPixelDist); + + // Sample half res color and CoC + float4 colorAndCoC = PassSrg::m_halfResColorAndCoc.Sample(PassSrg::LinearSampler, halfResUV); + + + // Make out of focus foreground increasingly blue + float multiplier = saturate(1.0f + coc); + colorAndCoC.r *= multiplier; + colorAndCoC.g *= multiplier; + + // Make out of focus background increasingly red + multiplier = saturate(1.0f - coc); + colorAndCoC.b *= multiplier; + colorAndCoC.g *= multiplier; + + // Output + PSOutput OUT; + OUT.m_color.rgb = colorAndCoC.rgb; + OUT.m_color.a = alpha; + + return OUT; +} + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.shader new file mode 100644 index 0000000000..f17837b330 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.shader @@ -0,0 +1,31 @@ +{ + "Source" : "NewDepthOfFieldComposite", + + "DepthStencilState" : + { + "Depth" : { "Enable" : false }, + "Stencil" : { "Enable" : false } + }, + + "BlendState" : { + "Enable" : true, + "BlendSource" : "AlphaSource", + "BlendDest" : "AlphaSourceInverse", + "BlendOp" : "Add" + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl new file mode 100644 index 0000000000..c8070178f0 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl @@ -0,0 +1,100 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include "NewDepthOfFieldCommon.azsli" +#include "DepthOfField.azsli" + +#include + +#define COC_EPSILON 0.0001 + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_colorTexture; + Texture2D m_depth; + + // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + float4 m_inputDimensions; + float4 m_outputDimensions; + + Sampler PointSampler + { + MinFilter = Point; + MagFilter = Point; + MipFilter = Point; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +} + +PSOutput MainPS(VSOutput IN) +{ + // Sampling positions + float2 outputPixelPos = IN.m_position.xy; + float2 inputPixelPos = outputPixelPos * 2.0f; + float2 inputUV = inputPixelPos * PassSrg::m_inputDimensions.zw; + + // Gather Depth + float4 depthGather = PassSrg::m_depth.Gather(PassSrg::PointSampler, inputUV); + depthGather = InvertDepth(depthGather); + + // Calculate CoC (Circle of Confusion) + float far = ViewSrg::m_dof.m_cameraParameters.x; + float near = ViewSrg::m_dof.m_cameraParameters.y; + float focusDistance = ViewSrg::m_dof.m_cameraParameters.z; + + float4 cocGather; + cocGather.x = ConvertDofFactor(depthGather.x, far, near, focusDistance); + cocGather.y = ConvertDofFactor(depthGather.y, far, near, focusDistance); + cocGather.z = ConvertDofFactor(depthGather.z, far, near, focusDistance); + cocGather.w = ConvertDofFactor(depthGather.w, far, near, focusDistance); + + // Clamp CoC + cocGather = clamp(cocGather, -1.0f, 1.0f); + + // Weigh samles by CoC to avoid in foces pixels bleeding into bokeh effect + float4 weights = abs(cocGather) + COC_EPSILON; + weights = weights / (weights.x + weights.y + weights.z + weights.w); + + PSOutput OUT; + + // Red + float4 redGather = PassSrg::m_colorTexture.GatherRed(PassSrg::PointSampler, inputUV); + OUT.m_color.r = dot(redGather, weights); + + // Green + float4 greenGather = PassSrg::m_colorTexture.GatherGreen(PassSrg::PointSampler, inputUV); + OUT.m_color.g = dot(greenGather, weights); + + // Blue + float4 blueGather = PassSrg::m_colorTexture.GatherBlue(PassSrg::PointSampler, inputUV); + OUT.m_color.b = dot(blueGather, weights); + + // CoC - Take the CoC with the maximum absolute value (note CoC can be negative) to get the fullest bokeh effect + // The above weighting by CoC mitigates bokeh bleeding from taking the max CoC + float coc = cocGather.x; + coc = abs(coc) < abs(cocGather.y) ? cocGather.y : coc; + coc = abs(coc) < abs(cocGather.z) ? cocGather.z : coc; + coc = abs(coc) < abs(cocGather.w) ? cocGather.w : coc; + + // CoC weighting #2: we use linear sampling when we compute the CoC blur + // This can lead to in focus pixels bleeding into the bokeh blur + // Pre-multiply the color values by the CoC to avoid this type of bleeding + // Because we then re-multiply by the CoC value after the linear sampling, we want to avoid coc values of 0 + // See http://advances.realtimerendering.com/s2013/Sousa_Graphics_Gems_CryENGINE3.pptx + coc = abs(coc) > COC_EPSILON ? coc : -COC_EPSILON; + OUT.m_color.rgb *= abs(coc); + + OUT.m_color.a = coc; + + return OUT; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.shader new file mode 100644 index 0000000000..00aa89e6d8 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.shader @@ -0,0 +1,22 @@ +{ + "Source" : "NewDepthOfFieldDownsample", + + "DepthStencilState" : { + "Depth" : { "Enable" : false } + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl new file mode 100644 index 0000000000..e566db7880 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl @@ -0,0 +1,50 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include "DepthOfField.azsli" +#include "NewDepthOfFieldCommon.azsli" + +#include + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_colorAndCoc; + Texture2D m_minMaxCocTile; + + float4 m_textureDimensions; + + NewDepthOfFieldConstants m_dofConstants; + + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +} + +PSOutput MainPS(VSOutput IN) +{ + PSOutput OUT = (PSOutput)0; + + float4 centerSample = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, IN.m_texCoord); + float3 centerColor = centerSample.rgb; + float centerCoc = centerSample.a; + + centerColor /= centerCoc; + + OUT.m_color.rgb = centerColor; + OUT.m_color.a = centerCoc; + return OUT; +} + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader new file mode 100644 index 0000000000..8f6faedb76 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader @@ -0,0 +1,22 @@ +{ + "Source" : "NewDepthOfFieldFilterLarge", + + "DepthStencilState" : { + "Depth" : { "Enable" : false } + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl new file mode 100644 index 0000000000..dadaf57506 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl @@ -0,0 +1,74 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include "DepthOfField.azsli" +#include "NewDepthOfFieldCommon.azsli" + +#include + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_colorAndCoc; + Texture2D m_minMaxCocTile; + + float4 m_textureDimensions; + + NewDepthOfFieldConstants m_dofConstants; + + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +} + +float2 GetOffsetUV(uint index, float radiusMultiplier) +{ + return PassSrg::m_dofConstants.samplePositions[index] * radiusMultiplier * PassSrg::m_textureDimensions.zw; +} + +PSOutput MainPS(VSOutput IN) +{ + // UV of pixel being shaded + float2 pixelUV = IN.m_texCoord.xy; + + // Sample pixel being shaded + float4 centerSample = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV).rgba; + float centerCoc = centerSample.a; + + // Color and weight accumulation + float3 color = centerSample.rgb; + float totalWeight = 1; + + // Sampling radius + float cocRadius = abs(centerCoc) * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; + cocRadius *= 0.5f; // This is the small filter, half the sampling radius + + for(uint i = 0; i < SAMPLES_LOOP_1; ++i) + { + float2 offsetUV = GetOffsetUV(i, cocRadius); + float4 sample = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offsetUV).rgba; + float cocDiff = abs(sample.a - centerCoC); + float weight = saturate( 2 - (20 * cocDiff)); + color += sample.rgb * weight; + totalWeight += weight; + } + + // Output + PSOutput OUT; + OUT.m_color.rgb = color / totalWeight; + OUT.m_color.a = centerCoc; + return OUT; +} + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader new file mode 100644 index 0000000000..d05e09be8f --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader @@ -0,0 +1,22 @@ +{ + "Source" : "NewDepthOfFieldFilterSmall", + + "DepthStencilState" : { + "Depth" : { "Enable" : false } + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl new file mode 100644 index 0000000000..7238b391aa --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl @@ -0,0 +1,66 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include "NewDepthOfFieldCommon.azsli" + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_minMaxSource; + + // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + float4 m_textureDimensions; + + Sampler PointSampler + { + MinFilter = Point; + MagFilter = Point; + MipFilter = Point; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +} + +struct PSOutput +{ + float2 m_color : SV_Target0; +}; + +PSOutput MainPS(VSOutput IN) +{ + // We want the min/max in a 3x3 region. Start sampling up left. + float2 startPixelPos = IN.m_position.xy - float2(1, 1); + + float2 stepSize = PassSrg::m_textureDimensions.zw; + float2 startUV = startPixelPos * stepSize; + + float cocMin = 1.0f; + float cocMax = -1.0f; + + // Gather min/max in 3x3 region + [unroll] + for(float Y = 0.0f; Y < 3.0f; Y += 1.0f) + { + [unroll] + for(float X = 0.0f; X < 3.0f; X += 1.0f) + { + float2 sampleUV = mad(float2(X, Y), stepSize, startUV); + float2 minMax = PassSrg::m_minMaxSource.SampleLevel(PassSrg::PointSampler, sampleUV, 0).xy; + + cocMin = min(cocMin, minMax.x); + cocMax = max(cocMax, minMax.y); + } + } + + PSOutput output; + output.m_color.x = cocMin; + output.m_color.y = cocMax; + return output; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader new file mode 100644 index 0000000000..f7d9cc160e --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader @@ -0,0 +1,22 @@ +{ + "Source" : "NewDepthOfFieldTile3x3", + + "DepthStencilState" : { + "Depth" : { "Enable" : false } + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl new file mode 100644 index 0000000000..2705e1fdfd --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl @@ -0,0 +1,83 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include "NewDepthOfFieldCommon.azsli" + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + // For this shader we're only interested in the CoC values, which are in the alpha channel + Texture2D m_colorAndCoC; + + // Tiled min/max CoC values + RWTexture2D m_minMaxCoC; + + // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + float4 m_inputDimensions; + float4 m_outputDimensions; + + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +} + +groupshared uint LDS_MIN_COC[8]; +groupshared uint LDS_MAX_COC[8]; + +[numthreads(8, 8, 1)] +void MainCS(uint3 group_thread_id : SV_GroupThreadID, uint3 group_id : SV_GroupID, uint3 dispatch_id: SV_DispatchThreadID, uint linear_id : SV_GroupIndex) +{ + // Initialize groupshared mem for atomic min/max operations + if(group_thread_id.y == 0) + { + LDS_MIN_COC[group_thread_id.x] = 0xFFFFFFFF; + LDS_MAX_COC[group_thread_id.x] = 0; + } + + // We use gather to get 2x2 values at once, so thread samples are spaced 2 pixels apart (+1 so the sample position is in between the four pixels) + float2 samplePos = float2(dispatch_id.xy) * 2 + float2(1, 1); + float2 sampleUV = samplePos * PassSrg::m_inputDimensions.zw; + + // Gather CoC + float4 cocGather = PassSrg::m_colorAndCoC.GatherAlpha(PassSrg::LinearSampler, sampleUV); + float cocMin = min4(cocGather); + float cocMax = max4(cocGather); + + // For atomic min/max to work with uints, floating point values should be positive + // Map from [-1, 1] range to [0, 2] and cast as uint + InterlockedMin( LDS_MIN_COC[group_thread_id.x], asuint(cocMin + 1) ); + InterlockedMax( LDS_MAX_COC[group_thread_id.x], asuint(cocMax + 1) ); + + // Sync LDS + GroupMemoryBarrierWithGroupSync(); + + if(group_thread_id.y != 0) + { + return; + } + + InterlockedMin( LDS_MIN_COC[0], LDS_MIN_COC[group_thread_id.x] ); + InterlockedMax( LDS_MAX_COC[0], LDS_MAX_COC[group_thread_id.x] ); + + if(group_thread_id.x == 0) + { + // Unpack uints + cocMin = asfloat(LDS_MIN_COC[0]) - 1; + cocMax = asfloat(LDS_MAX_COC[0]) - 1; + + // Output min/max coc of 16x16 region + PassSrg::m_minMaxCoC[group_id.xy] = float2(cocMin, cocMax); + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader new file mode 100644 index 0000000000..f2da5305e5 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader @@ -0,0 +1,14 @@ +{ + "Source" : "NewDepthOfFieldTileReduce", + + "ProgramSettings" : + { + "EntryPoints": + [ + { + "name" : "MainCS", + "type" : "Compute" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 60614993b5..d1f94afe3c 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -1,9 +1,8 @@ # -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT # set(FILES @@ -86,7 +85,6 @@ set(FILES Passes/CascadedShadowmaps.pass Passes/CheckerboardResolveColor.pass Passes/CheckerboardResolveDepth.pass - Passes/HDRColorGrading.pass Passes/ContrastAdaptiveSharpening.pass Passes/ConvertToAcescg.pass Passes/DebugOverlayParent.pass @@ -144,6 +142,7 @@ set(FILES Passes/ForwardSubsurfaceMSAA.pass Passes/FullscreenCopy.pass Passes/FullscreenOutputOnly.pass + Passes/HDRColorGrading.pass Passes/ImGui.pass Passes/KawaseShadowBlur.pass Passes/LightAdaptationParent.pass @@ -160,8 +159,6 @@ set(FILES Passes/LuminanceHistogramGenerator.pass Passes/MainPipeline.pass Passes/MainPipelineRenderToTexture.pass - Passes/ThumbnailPipeline.pass - Passes/ThumbnailPipelineRenderToTexture.pass Passes/MeshMotionVector.pass Passes/ModulateTexture.pass Passes/MorphTarget.pass @@ -169,6 +166,13 @@ set(FILES Passes/MSAAResolveColor.pass Passes/MSAAResolveCustom.pass Passes/MSAAResolveDepth.pass + Passes/NewDepthOfField.pass + Passes/NewDepthOfFieldComposite.pass + Passes/NewDepthOfFieldDownsample.pass + Passes/NewDepthOfFieldFilterLarge.pass + Passes/NewDepthOfFieldFilterSmall.pass + Passes/NewDepthOfFieldTile3x3.pass + Passes/NewDepthOfFieldTileReduce.pass Passes/OpaqueParent.pass Passes/PostProcessParent.pass Passes/ProjectedShadowmaps.pass @@ -204,11 +208,17 @@ set(FILES Passes/SsaoParent.pass Passes/SubsurfaceScattering.pass Passes/Taa.pass + Passes/ThumbnailPipeline.pass + Passes/ThumbnailPipelineRenderToTexture.pass Passes/Transparent.pass Passes/TransparentParent.pass Passes/UI.pass Passes/UIParent.pass + Scripts/material_find_overrides_demo.lua Scripts/material_property_overrides_demo.lua + ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli + ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli + ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli ShaderLib/Atom/Features/BlendUtility.azsli ShaderLib/Atom/Features/IndirectRendering.azsli ShaderLib/Atom/Features/MatrixUtility.azsli @@ -217,6 +227,8 @@ set(FILES ShaderLib/Atom/Features/SphericalHarmonicsUtility.azsli ShaderLib/Atom/Features/SrgSemantics.azsli ShaderLib/Atom/Features/ColorManagement/TransformColor.azsli + ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli + ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCgToAcesCc.azsli ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCg_To_LinearSrgb.azsli ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/Aces_To_AcesCg.azsli ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/CalculateLuminance_AcesCg.azsli @@ -224,8 +236,6 @@ set(FILES ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/LinearSrgb_To_AcesCg.azsli ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/LinearSrgb_To_Srgb.azsli ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/Srgb_To_LinearSrgb.azsli - ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli - ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCgToAcesCc.azsli ShaderLib/Atom/Features/CoreLights/PhotometricValue.azsli ShaderLib/Atom/Features/Decals/DecalTextureUtil.azsli ShaderLib/Atom/Features/LightCulling/LightCullingShared.azsli @@ -282,18 +292,23 @@ set(FILES ShaderLib/Atom/Features/PostProcessing/GlyphData.azsli ShaderLib/Atom/Features/PostProcessing/GlyphRender.azsli ShaderLib/Atom/Features/PostProcessing/PostProcessUtil.azsli + ShaderLib/Atom/Features/PostProcessing/Shapers.azsli + ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli + ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli + ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli + ShaderLib/Atom/Features/RayTracing/RayTracingSrgs.azsl + ShaderLib/Atom/Features/RayTracing/RayTracingSrgs.shader ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli ShaderLib/Atom/Features/Shadow/BicubicPcfFilters.azsli ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli + ShaderLib/Atom/Features/Shadow/ReceiverPlaneDepthBias.azsli ShaderLib/Atom/Features/Shadow/Shadow.azsli ShaderLib/Atom/Features/Shadow/ShadowmapAtlasLib.azsli + ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli ShaderLib/Atom/Features/Vertex/VertexHelper.azsli - ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli - ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli - ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli ShaderResourceGroups/SceneSrg.azsli ShaderResourceGroups/SceneSrgAll.azsli ShaderResourceGroups/ViewSrg.azsli @@ -304,6 +319,8 @@ set(FILES ShaderResourceGroups/PostProcessing/SceneSrg.azsli ShaderResourceGroups/PostProcessing/ViewSrg.azsli ShaderResourceGroups/SkyBox/SceneSrg.azsli + Shaders/ForwardPassSrg.azsl + Shaders/ForwardPassSrg.shader Shaders/AuxGeom/AuxGeomObject.azsl Shaders/AuxGeom/AuxGeomObject.shader Shaders/AuxGeom/AuxGeomObjectLit.azsl @@ -318,14 +335,20 @@ set(FILES Shaders/Checkerboard/CheckerboardColorResolveCS.shader Shaders/Depth/DepthPass.azsl Shaders/Depth/DepthPass.shader + Shaders/Depth/DepthPassCommon.azsli + Shaders/Depth/DepthPassSkin.azsl + Shaders/Depth/DepthPassSkin.shader Shaders/Depth/DepthPassTransparentMax.shader Shaders/Depth/DepthPassTransparentMin.shader Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl Shaders/DiffuseGlobalIllumination/DiffuseComposite.shader + Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.azsl Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.azsl Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.shader + Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen_nomsaa.azsl Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.shader + Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.azsl Shaders/ImGui/ImGui.azsl Shaders/ImGui/ImGui.shader Shaders/LightCulling/LightCulling.azsl @@ -345,6 +368,9 @@ set(FILES Shaders/MotionVector/CameraMotionVector.shader Shaders/MotionVector/MeshMotionVector.azsl Shaders/MotionVector/MeshMotionVector.shader + Shaders/MotionVector/MeshMotionVectorCommon.azsli + Shaders/MotionVector/MeshMotionVectorSkin.azsl + Shaders/MotionVector/MeshMotionVectorSkin.shader Shaders/PostProcessing/AcesOutputTransformLut.azsl Shaders/PostProcessing/AcesOutputTransformLut.shader Shaders/PostProcessing/ApplyShaperLookupTable.azsl @@ -359,6 +385,8 @@ set(FILES Shaders/PostProcessing/BloomCompositeCS.shader Shaders/PostProcessing/BloomDownsampleCS.azsl Shaders/PostProcessing/BloomDownsampleCS.shader + Shaders/PostProcessing/ContrastAdaptiveSharpening.azsl + Shaders/PostProcessing/ContrastAdaptiveSharpening.shader Shaders/PostProcessing/ConvertToAcescg.azsl Shaders/PostProcessing/ConvertToAcescg.shader Shaders/PostProcessing/DepthDownsample.azsl @@ -415,6 +443,18 @@ set(FILES Shaders/PostProcessing/MSAAResolveCustom.shader Shaders/PostProcessing/MSAAResolveDepth.azsl Shaders/PostProcessing/MSAAResolveDepth.shader + Shaders/PostProcessing/NewDepthOfFieldComposite.azsl + Shaders/PostProcessing/NewDepthOfFieldComposite.shader + Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl + Shaders/PostProcessing/NewDepthOfFieldDownsample.shader + Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl + Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader + Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl + Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader + Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl + Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader + Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl + Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader Shaders/PostProcessing/OutputTransform.azsl Shaders/PostProcessing/OutputTransform.shader Shaders/PostProcessing/ScreenSpaceSubsurfaceScatteringCS.azsl @@ -431,13 +471,17 @@ set(FILES Shaders/PostProcessing/SMAAUtils.azsli Shaders/PostProcessing/SsaoCompute.azsl Shaders/PostProcessing/SsaoCompute.shader + Shaders/PostProcessing/Taa.azsl + Shaders/PostProcessing/Taa.shader Shaders/PostProcessing/UniformColor.azsl Shaders/PostProcessing/UniformColor.shader Shaders/Reflections/ReflectionCommon.azsli Shaders/Reflections/ReflectionComposite.azsl Shaders/Reflections/ReflectionComposite.shader + Shaders/Reflections/ReflectionComposite_nomsaa.azsl Shaders/Reflections/ReflectionGlobalFullscreen.azsl Shaders/Reflections/ReflectionGlobalFullscreen.shader + Shaders/Reflections/ReflectionGlobalFullscreen_nomsaa.azsl Shaders/Reflections/ReflectionProbeBlendWeight.azsl Shaders/Reflections/ReflectionProbeBlendWeight.shader Shaders/Reflections/ReflectionProbeRenderCommon.azsli @@ -466,6 +510,9 @@ set(FILES Shaders/Shadow/KawaseShadowBlur.shader Shaders/Shadow/Shadowmap.azsl Shaders/Shadow/Shadowmap.shader + Shaders/Shadow/ShadowmapCommon.azsli + Shaders/Shadow/ShadowmapSkin.azsl + Shaders/Shadow/ShadowmapSkin.shader Shaders/SkinnedMesh/LinearSkinningCS.azsl Shaders/SkinnedMesh/LinearSkinningCS.shader Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 22c5f37ff8..a7891b7907 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,14 @@ namespace AZ passSystem->AddPassCreator(Name("DepthOfFieldReadBackFocusDepthPass"), &DepthOfFieldReadBackFocusDepthPass::Create); passSystem->AddPassCreator(Name("DepthOfFieldWriteFocusDepthFromGpuPass"), &DepthOfFieldWriteFocusDepthFromGpuPass::Create); + passSystem->AddPassCreator(Name("NewDepthOfFieldParentPass"), &NewDepthOfFieldParentPass::Create); + passSystem->AddPassCreator(Name("NewDepthOfFieldTileReducePass"), &NewDepthOfFieldTileReducePass::Create); + passSystem->AddPassCreator(Name("NewDepthOfFieldFilterPass"), &NewDepthOfFieldFilterPass::Create); + passSystem->AddPassCreator(Name("NewDepthOfFieldCompositePass"), &NewDepthOfFieldCompositePass::Create); + + + + // Add FastDepthAwareBlur passes passSystem->AddPassCreator(Name("FastDepthAwareBlurHorPass"), &FastDepthAwareBlurHorPass::Create); passSystem->AddPassCreator(Name("FastDepthAwareBlurVerPass"), &FastDepthAwareBlurVerPass::Create); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp new file mode 100644 index 0000000000..7b9e46f6b1 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + + // Must match the struct in NewDepthOfFieldCommon.azsli + struct NewDepthOfFieldConstants + { + static constexpr uint32_t numberOfLoops = 3; + static constexpr float loopCounts[] = { 8.0f, 16.0f, 24.0f }; + + AZStd::array m_samplePositions; // XY are sample positions (normalized so max lenght is 1) + // Z is the length of XY (0 - 1) + // W is unused + }; + + + // --- Depth of Field Parent Pass --- + + RPI::Ptr NewDepthOfFieldParentPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew NewDepthOfFieldParentPass(descriptor); + return AZStd::move(pass); + } + + NewDepthOfFieldParentPass::NewDepthOfFieldParentPass(const RPI::PassDescriptor& descriptor) + : RPI::ParentPass(descriptor) + { } + + bool NewDepthOfFieldParentPass::IsEnabled() const + { + if (!ParentPass::IsEnabled()) + { + return false; + } + RPI::Scene* scene = GetScene(); + if (!scene) + { + return false; + } + PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor(); + AZ::RPI::ViewPtr view = GetRenderPipeline()->GetDefaultView(); + if (!fp) + { + return false; + } + PostProcessSettings* postProcessSettings = fp->GetLevelSettingsFromView(view); + if (!postProcessSettings) + { + return false; + } + DepthOfFieldSettings* dofSettings = postProcessSettings->GetDepthOfFieldSettings(); + return (dofSettings != nullptr) && dofSettings->GetEnabled(); + } + + void NewDepthOfFieldParentPass::FrameBeginInternal(FramePrepareParams params) + { + RPI::Scene* scene = GetScene(); + PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor(); + AZ::RPI::ViewPtr view = GetRenderPipeline()->GetDefaultView(); + if (fp) + { + PostProcessSettings* postProcessSettings = fp->GetLevelSettingsFromView(view); + if (postProcessSettings) + { + DepthOfFieldSettings* dofSettings = postProcessSettings->GetDepthOfFieldSettings(); + if (dofSettings) + { + dofSettings->SetValuesToViewSrg(view->GetShaderResourceGroup()); + } + } + } + + ParentPass::FrameBeginInternal(params); + } + + // --- Tile Reduce Pass --- + + RPI::Ptr NewDepthOfFieldTileReducePass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew NewDepthOfFieldTileReducePass(descriptor); + return AZStd::move(pass); + } + + NewDepthOfFieldTileReducePass::NewDepthOfFieldTileReducePass(const RPI::PassDescriptor& descriptor) + : RPI::ComputePass(descriptor) + { + // Though this is a fullscreen pass, the algorithm used makes each thread output 3 blurred pixels, so + // it's not a 1-to-1 ratio and requires custom calculation of target thread counts + m_isFullscreenPass = false; + } + + void NewDepthOfFieldTileReducePass::FrameBeginInternal(FramePrepareParams params) + { + AZ_Assert(GetOutputCount() > 0, "NewDepthOfFieldTileReducePass: No output bindings!"); + RPI::PassAttachment* outputAttachment = GetOutputBinding(0).m_attachment.get(); + + AZ_Assert(outputAttachment != nullptr, "NewDepthOfFieldTileReducePass: Output binding has no attachment!"); + RHI::Size outputSize = outputAttachment->m_descriptor.m_image.m_size; + + // The algorithm outputs the min/max CoC values from a 16x16 region using 8x8 threads + u32 targetThreadCountX = outputSize.m_width * 8; + u32 targetThreadCountY = outputSize.m_height * 8; + SetTargetThreadCounts(targetThreadCountX, targetThreadCountY, 1); + + RPI::ComputePass::FrameBeginInternal(params); + } + + + + // --- Filter Pass --- + + RPI::Ptr NewDepthOfFieldFilterPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew NewDepthOfFieldFilterPass(descriptor); + return AZStd::move(pass); + } + + NewDepthOfFieldFilterPass::NewDepthOfFieldFilterPass(const RPI::PassDescriptor& descriptor) + : RPI::FullscreenTrianglePass(descriptor) + { } + + void NewDepthOfFieldFilterPass::FrameBeginInternal(FramePrepareParams params) + { + NewDepthOfFieldConstants dofConstants; + + uint32_t sampleIndex = 0; + + for (uint32_t loop = 0; loop < NewDepthOfFieldConstants::numberOfLoops; ++loop) + { + float radius = (loop + 1.0f) / float(NewDepthOfFieldConstants::numberOfLoops); + float loopCount = NewDepthOfFieldConstants::loopCounts[loop]; + + for (float i = 0.0f; i < loopCount; ++i) + { + float angle = Constants::TwoPi * i / loopCount; + Vector2 pos = Vector2::CreateFromAngle(angle); + pos = pos * radius; + + dofConstants.m_samplePositions[sampleIndex][0] = pos.GetX(); + dofConstants.m_samplePositions[sampleIndex][1] = pos.GetY(); + dofConstants.m_samplePositions[sampleIndex][2] = radius; + dofConstants.m_samplePositions[sampleIndex][3] = 0.0f; + ++sampleIndex; + } + + } + + m_shaderResourceGroup->SetConstant(m_constantsIndex, dofConstants); + + // TODO HERE + RPI::FullscreenTrianglePass::FrameBeginInternal(params); + } + + + + // --- Composite Pass --- + + RPI::Ptr NewDepthOfFieldCompositePass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew NewDepthOfFieldCompositePass(descriptor); + return AZStd::move(pass); + } + + NewDepthOfFieldCompositePass::NewDepthOfFieldCompositePass(const RPI::PassDescriptor& descriptor) + : RPI::ComputePass(descriptor) + { } + + void NewDepthOfFieldCompositePass::FrameBeginInternal(FramePrepareParams params) + { + // TODO HERE + RPI::ComputePass::FrameBeginInternal(params); + } + + + + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h new file mode 100644 index 0000000000..f7a0ee95ca --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include + +#include + +namespace AZ +{ + namespace Render + { + //! + class NewDepthOfFieldParentPass final + : public RPI::ParentPass + { + AZ_RPI_PASS(NewDepthOfFieldParentPass); + + public: + AZ_RTTI(AZ::Render::NewDepthOfFieldParentPass, "{71F4998B-447C-4BAC-A5BE-2D2850FABB57}", AZ::RPI::ParentPass); + AZ_CLASS_ALLOCATOR(NewDepthOfFieldParentPass, SystemAllocator, 0); + virtual ~NewDepthOfFieldParentPass() = default; + + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + bool IsEnabled() const override; + + protected: + // Behavior functions override... + void FrameBeginInternal(FramePrepareParams params) override; + + private: + NewDepthOfFieldParentPass(const RPI::PassDescriptor& descriptor); + }; + + + + //! + class NewDepthOfFieldTileReducePass final + : public RPI::ComputePass + { + AZ_RPI_PASS(NewDepthOfFieldTileReducePass); + + public: + AZ_RTTI(AZ::Render::NewDepthOfFieldTileReducePass, "{2E072695-0847-43A6-9BE4-D6D85CFFBA41}", AZ::RPI::ComputePass); + AZ_CLASS_ALLOCATOR(NewDepthOfFieldTileReducePass, SystemAllocator, 0); + virtual ~NewDepthOfFieldTileReducePass() = default; + + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + // Behavior functions override... + void FrameBeginInternal(FramePrepareParams params) override; + + private: + NewDepthOfFieldTileReducePass(const RPI::PassDescriptor& descriptor); + }; + + + + //! + class NewDepthOfFieldFilterPass final + : public RPI::FullscreenTrianglePass + { + AZ_RPI_PASS(NewDepthOfFieldFilterPass); + + public: + AZ_RTTI(AZ::Render::NewDepthOfFieldFilterPass, "{F8A98E53-1A50-4178-A6EB-2BD0148C038B}", AZ::RPI::FullscreenTrianglePass); + AZ_CLASS_ALLOCATOR(NewDepthOfFieldFilterPass, SystemAllocator, 0); + virtual ~NewDepthOfFieldFilterPass() = default; + + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + // Behavior functions override... + void FrameBeginInternal(FramePrepareParams params) override; + + private: + NewDepthOfFieldFilterPass(const RPI::PassDescriptor& descriptor); + + // SRG binding indices... + AZ::RHI::ShaderInputNameIndex m_constantsIndex = "m_dofConstants"; + }; + + + + //! + class NewDepthOfFieldCompositePass final + : public RPI::ComputePass + { + AZ_RPI_PASS(NewDepthOfFieldCompositePass); + + public: + AZ_RTTI(AZ::Render::NewDepthOfFieldCompositePass, "{63270A3A-EAE5-4C0C-98AA-43CA55279613}", AZ::RPI::ComputePass); + AZ_CLASS_ALLOCATOR(NewDepthOfFieldCompositePass, SystemAllocator, 0); + virtual ~NewDepthOfFieldCompositePass() = default; + + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + // Behavior functions override... + void FrameBeginInternal(FramePrepareParams params) override; + + private: + NewDepthOfFieldCompositePass(const RPI::PassDescriptor& descriptor); + }; + + + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp index 779f02cba0..643d9bb96d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp @@ -168,7 +168,7 @@ namespace AZ::Render // The ImageViewDescriptor must be specified to make sure the frame graph compiler doesn't treat this as a transient image. RHI::ImageViewDescriptor viewDesc = RHI::ImageViewDescriptor::Create(imageDesc.m_format, 0, 0); viewDesc.m_aspectFlags = RHI::ImageAspectFlags::Color; - viewDesc.m_overrideBindFlags = RHI::ImageBindFlags::ShaderReadWrite; + //viewDesc.m_overrideBindFlags = RHI::ImageBindFlags::ShaderReadWrite; // The full path name is needed for the attachment image so it's not deduplicated from accumulation images in different pipelines. AZStd::string imageName = RPI::ConcatPassString(GetPathName(), attachment->m_path); diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 18cdc273d2..23a67ef5db 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -240,6 +240,8 @@ set(FILES Source/PostProcessing/LookModificationTransformPass.h Source/PostProcessing/LuminanceHistogramGeneratorPass.h Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp + Source/PostProcessing/NewDepthOfFieldPasses.cpp + Source/PostProcessing/NewDepthOfFieldPasses.h Source/PostProcessing/PostProcessingShaderOptionBase.cpp Source/PostProcessing/PostProcessingShaderOptionBase.h Source/PostProcessing/SMAABasePass.cpp diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/ShaderInputNameIndex.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/ShaderInputNameIndex.h index 03062afd52..5e9b1a9c77 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/ShaderInputNameIndex.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/ShaderInputNameIndex.h @@ -67,6 +67,10 @@ namespace AZ bool IsInitialized() const; void AssetInialized() const; + // Retrieves the underlying name. Should only be used for debug purposes like printing the name when we fail to bind to the SRG. + // All regular functionality should go through the above functions. + const Name& GetNameForDebug() const; + private: enum class IndexType : u32 diff --git a/Gems/Atom/RHI/Code/Source/RHI.Reflect/ShaderInputNameIndex.cpp b/Gems/Atom/RHI/Code/Source/RHI.Reflect/ShaderInputNameIndex.cpp index 2eb23895f8..b7ed2dbfe7 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Reflect/ShaderInputNameIndex.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Reflect/ShaderInputNameIndex.cpp @@ -227,5 +227,11 @@ namespace AZ } + const Name& ShaderInputNameIndex::GetNameForDebug() const + { + AZ_Assert(HasName(), "GetNameForDebug() called on ShaderInputNameIndex that doesn't have a name set. Please initialize it with a name.", m_name.GetCStr()); + return m_name; + } + } } diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli index 80f8a6cc36..6ffdb6e815 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli @@ -31,6 +31,8 @@ void swap(inout float a, inout float b) b = c; } +// ---------- Power ----------- + float Pow2(float x) { return x * x; @@ -48,6 +50,44 @@ float Pow5(float x) { return x * Pow4(x); } + +// ---------- Min & Max ----------- + +float min3(float a, float b, float c) +{ + return min(min(a, b), c); +} +float min4(float a, float b, float c, float d) +{ + return min(min3(a, b, c), d); +} +float max3(float a, float b, float c) +{ + return max(max(a, b), c); +} +float max4(float a, float b, float c, float d) +{ + return max(max3(a, b, c), d); +} + +float min3(float3 abc) +{ + return min3(abc.x, abc.y, abc.z); +} +float min4(float4 abcd) +{ + return min4(abcd.x, abcd.y, abcd.z, abcd.w); +} +float max3(float3 abc) +{ + return max3(abc.x, abc.y, abc.z); +} +float max4(float4 abcd) +{ + return max4(abcd.x, abcd.y, abcd.z, abcd.w); +} + + // ---------- Intersection ----------- // a simple ray sphere intersection function, didn't take limited precision diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h index babcf752e9..d60a5a8064 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h @@ -10,6 +10,8 @@ #include #include +#include + #include namespace AZ @@ -145,7 +147,11 @@ namespace AZ //! Name of the SRG member this binds to (see PassSlot::m_shaderInputName for more details) Name m_shaderInputName = Name("AutoBind"); - + + //! Name index of the SRG constant to which, if specified, we automatically calculate + //! and bind the image dimensions (if this binding is of type image) + RHI::ShaderInputNameIndex m_shaderImageDimensionsNameIndex; + //! Whether binding is an input, output or inputOutput PassSlotType m_slotType = PassSlotType::Uninitialized; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h index cbff297021..5fb90d044e 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h @@ -119,7 +119,7 @@ namespace AZ private: // Helper function that binds a single attachment to the pass shader resource group - void BindAttachment(const RHI::FrameGraphCompileContext& context, const PassAttachmentBinding& binding, int16_t& imageIndex, int16_t& bufferIndex); + void BindAttachment(const RHI::FrameGraphCompileContext& context, PassAttachmentBinding& binding, int16_t& imageIndex, int16_t& bufferIndex); // Helper function to get the query by the scope index and query type RHI::Ptr GetQuery(ScopeQueryType queryType); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Pass/PassAttachmentReflect.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Pass/PassAttachmentReflect.h index 1131f2ff70..0de3676abe 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Pass/PassAttachmentReflect.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Pass/PassAttachmentReflect.h @@ -87,6 +87,13 @@ namespace AZ //! The keyword "NoBind" means the slot will not bind it's attachment to the SRG Name m_shaderInputName = Name("AutoBind"); + //! Name of the shader resource group constant (must be float4) to which the pass can automatically bind the following: + //! X component = image width + //! Y component = image height + //! Z component = 1 / image width + //! W component = 1 / image height + Name m_shaderImageDimensionsName; + //! This is to specify an array index if the shader input is an array. //! e.g. Texture2DMS m_color[4]; uint16_t m_shaderInputArrayIndex = 0; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp index e0831381e7..3b4f10a968 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp @@ -187,6 +187,7 @@ namespace AZ { m_name = slot.m_name; m_shaderInputName = slot.m_shaderInputName; + m_shaderImageDimensionsNameIndex = slot.m_shaderImageDimensionsName; m_shaderInputArrayIndex = slot.m_shaderInputArrayIndex; m_slotType = slot.m_slotType; m_scopeAttachmentUsage = slot.m_scopeAttachmentUsage; 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 318ea7d11f..dad511def1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -272,15 +273,8 @@ namespace AZ } } - void RenderPass::BindAttachment(const RHI::FrameGraphCompileContext& context, const PassAttachmentBinding& binding, int16_t& imageIndex, int16_t& bufferIndex) + void RenderPass::BindAttachment(const RHI::FrameGraphCompileContext& context, PassAttachmentBinding& binding, int16_t& imageIndex, int16_t& bufferIndex) { - if (binding.m_shaderInputIndex == PassAttachmentBinding::ShaderInputNoBind || - binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::RenderTarget || - binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::DepthStencil) - { - return; - } - PassAttachment* attachment = binding.m_attachment.get(); if (attachment) { @@ -293,11 +287,39 @@ namespace AZ inputIndex = imageIndex; } const RHI::ImageView* imageView = context.GetImageView(attachment->GetAttachmentId(), binding.m_attachmentUsageIndex); - m_shaderResourceGroup->SetImageView(RHI::ShaderInputImageIndex(inputIndex), imageView, arrayIndex); - ++imageIndex; + + if (binding.m_shaderImageDimensionsNameIndex.HasName()) + { + RHI::Size size = attachment->m_descriptor.m_image.m_size; + + AZ::Vector4 imageDimensions; + imageDimensions.SetX(float(size.m_width)); + imageDimensions.SetY(float(size.m_height)); + imageDimensions.SetZ(1.0f / float(size.m_width)); + imageDimensions.SetW(1.0f / float(size.m_height)); + + bool success = m_shaderResourceGroup->SetConstant(binding.m_shaderImageDimensionsNameIndex, imageDimensions); + AZ_Assert(success, "Pass [%s] Could not find float4 constant [%s] in Shader Resource Group [%s]", + GetPathName().GetCStr(), + binding.m_shaderImageDimensionsNameIndex.GetNameForDebug().GetCStr(), + m_shaderResourceGroup->GetDatabaseName()); + } + + if (binding.m_shaderInputIndex != PassAttachmentBinding::ShaderInputNoBind && + binding.m_scopeAttachmentUsage != RHI::ScopeAttachmentUsage::RenderTarget && + binding.m_scopeAttachmentUsage != RHI::ScopeAttachmentUsage::DepthStencil) + { + m_shaderResourceGroup->SetImageView(RHI::ShaderInputImageIndex(inputIndex), imageView, arrayIndex); + ++imageIndex; + } } else if (attachment->GetAttachmentType() == RHI::AttachmentType::Buffer) { + if (binding.m_shaderInputIndex == PassAttachmentBinding::ShaderInputNoBind) + { + return; + } + if (inputIndex == PassAttachmentBinding::ShaderInputAutoBind) { inputIndex = bufferIndex; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Pass/PassAttachmentReflect.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Pass/PassAttachmentReflect.cpp index 39be08d299..602793ba19 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Pass/PassAttachmentReflect.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Pass/PassAttachmentReflect.cpp @@ -56,9 +56,10 @@ namespace AZ ; serializeContext->Class() - ->Version(1) + ->Version(2) ->Field("Name", &PassSlot::m_name) ->Field("ShaderInputName", &PassSlot::m_shaderInputName) + ->Field("ShaderImageDimensionsConstant", &PassSlot::m_shaderImageDimensionsName) ->Field("ShaderInputArrayIndex", &PassSlot::m_shaderInputArrayIndex) ->Field("SlotType", &PassSlot::m_slotType) ->Field("ScopeAttachmentUsage", &PassSlot::m_scopeAttachmentUsage) From 0c7e732527b4da943cfa9c86b7ad6e22c2114fe2 Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Wed, 20 Oct 2021 14:20:49 -0700 Subject: [PATCH 066/152] removing return type Union which is not supported Signed-off-by: Scott Murray --- .../Atom/atom_utils/atom_constants.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index 86fa3bb7e1..344a0dbd29 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -37,7 +37,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def bloom(property: str = 'name') -> Union[str, List[str]]: + def bloom(property: str = 'name') -> str: """ Bloom component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. @@ -78,7 +78,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def deferred_fog(property: str = 'name') -> Union[str, List[str]]: + def deferred_fog(property: str = 'name') -> str: """ Deferred Fog component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. @@ -93,7 +93,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def depth_of_field(property: str = 'name') -> Union[str, List[str]]: + def depth_of_field(property: str = 'name') -> str: """ Depth of Field component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. @@ -111,7 +111,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def diffuse_probe(property: str = 'name') -> Union[str, List[str]]: + def diffuse_probe(property: str = 'name') -> str: """ Diffuse Probe Grid component properties. Requires one of 'shapes'. - 'shapes' a list of supported shapes as component names. @@ -164,7 +164,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def exposure_control(property: str = 'name') -> Union[str, List[str]]: + def exposure_control(property: str = 'name') -> str: """ Exposure Control component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. @@ -207,7 +207,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def hdr_color_grading(property: str = 'name') -> Union[str, List[str]]: + def hdr_color_grading(property: str = 'name') -> str: """ HDR Color Grading component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. @@ -248,7 +248,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def look_modification(property: str = 'name') -> Union[str, List[str]]: + def look_modification(property: str = 'name') -> str: """ Look Modification component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. @@ -263,7 +263,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def material(property: str = 'name') -> Union[str, List[str]]: + def material(property: str = 'name') -> str: """ Material component properties. Requires one of Actor OR Mesh component. - 'requires' a list of component names as strings required by this component. @@ -329,7 +329,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def postfx_gradient(property: str = 'name') -> Union[str, List[str]]: + def postfx_gradient(property: str = 'name') -> str: """ PostFX Gradient Weight Modifier component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. @@ -344,7 +344,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def postfx_radius(property: str = 'name') -> Union[str, List[str]]: + def postfx_radius(property: str = 'name') -> str: """ PostFX Radius Weight Modifier component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. @@ -359,7 +359,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def postfx_shape(property: str = 'name') -> Union[str, List[str]]: + def postfx_shape(property: str = 'name') -> str: """ PostFX Shape Weight Modifier component properties. Requires PostFX Layer and one of 'shapes' listed. - 'requires' a list of component names as strings required by this component. @@ -377,7 +377,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def reflection_probe(property: str = 'name') -> Union[str, List[str]]: + def reflection_probe(property: str = 'name') -> str: """ Reflection Probe component properties. Requires one of 'shapes' listed. - 'shapes' a list of supported shapes as component names. @@ -393,7 +393,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def ssao(property: str = 'name') -> Union[str, List[str]]: + def ssao(property: str = 'name') -> str: """ SSAO component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. From 6566ff1fcb9b7e96a42706baf62a2aa7815087f0 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 20 Oct 2021 16:22:34 -0500 Subject: [PATCH 067/152] Fixing material system component mac release build Signed-off-by: Guthrie Adams --- .../Code/Source/Material/EditorMaterialSystemComponent.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp index 746b1ff7e5..e53010d747 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp @@ -194,6 +194,8 @@ namespace AZ AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath), propertyOverrides), [entityId, materialAssignmentId]() { + AZ_UNUSED(entityId); + AZ_UNUSED(materialAssignmentId); AZ_Warning( "EditorMaterialSystemComponent", false, "RenderMaterialPreview capture failed for entity %s slot %s.", entityId.ToString().c_str(), materialAssignmentId.ToString().c_str()); From 27a535eaf472d7c891504af963b1c4460d59086a Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Wed, 20 Oct 2021 14:34:30 -0700 Subject: [PATCH 068/152] Linux Fixes for Launching the Material Editor (#4808) - Prevent P4 thread to run if we cannot detect the P4 command to begin with - Add a trait to disable calling the parent ComponentApplication::Destroy(), instead calling _exit() to skip the module unloading on exit Signed-off-by: Steve Pham --- .../SourceControl/PerforceComponent.cpp | 23 +++++++++++++++---- .../SourceControl/PerforceComponent.h | 2 ++ .../AtomToolsFramework/Code/CMakeLists.txt | 4 ++++ .../Application/AtomToolsApplication.cpp | 4 ++++ .../Linux/AtomToolsFramework_Traits_Linux.h | 15 ++++++++++++ .../AtomToolsFramework_Traits_Platform.h | 10 ++++++++ .../Platform/Linux/platform_linux_files.cmake | 12 ++++++++++ .../Mac/AtomToolsFramework_Traits_Mac.h | 15 ++++++++++++ .../Mac/AtomToolsFramework_Traits_Platform.h | 10 ++++++++ .../Platform/Mac/platform_mac_files.cmake | 12 ++++++++++ .../AtomToolsFramework_Traits_Platform.h | 10 ++++++++ .../AtomToolsFramework_Traits_Windows.h | 15 ++++++++++++ .../Windows/platform_windows_files.cmake | 12 ++++++++++ 13 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/AtomToolsFramework_Traits_Linux.h create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/AtomToolsFramework_Traits_Platform.h create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/platform_linux_files.cmake create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/AtomToolsFramework_Traits_Mac.h create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/AtomToolsFramework_Traits_Platform.h create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/platform_mac_files.cmake create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/AtomToolsFramework_Traits_Platform.h create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/AtomToolsFramework_Traits_Windows.h create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/platform_windows_files.cmake diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.cpp index 3bd12b1ad3..acb2cd4b1c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.cpp @@ -20,6 +20,8 @@ #include #include +#include + namespace AzToolsFramework { namespace @@ -75,9 +77,17 @@ namespace AzToolsFramework m_resolveKey = true; m_testTrust = false; + // set up signals before we start thread. m_shutdownThreadSignal = false; - m_WorkerThread = AZStd::thread(AZStd::bind(&PerforceComponent::ThreadWorker, this)); + + // Check to see if the 'p4' command is available at the command line + int p4VersionExitCode = QProcess::execute("p4", QStringList{ "-V" }); + m_p4ApplicationDetected = (p4VersionExitCode == 0); + if (m_p4ApplicationDetected) + { + m_WorkerThread = AZStd::thread(AZStd::bind(&PerforceComponent::ThreadWorker, this)); + } SourceControlConnectionRequestBus::Handler::BusConnect(); SourceControlCommandBus::Handler::BusConnect(); @@ -88,10 +98,13 @@ namespace AzToolsFramework SourceControlCommandBus::Handler::BusDisconnect(); SourceControlConnectionRequestBus::Handler::BusDisconnect(); - m_shutdownThreadSignal = true; // tell the thread to die. - m_WorkerSemaphore.release(1); // wake up the thread so that it sees the signal - m_WorkerThread.join(); // wait for the thread to finish. - m_WorkerThread = AZStd::thread(); + if (m_p4ApplicationDetected) + { + m_shutdownThreadSignal = true; // tell the thread to die. + m_WorkerSemaphore.release(1); // wake up the thread so that it sees the signal + m_WorkerThread.join(); // wait for the thread to finish. + m_WorkerThread = AZStd::thread(); + } SetConnection(nullptr); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.h index 858d9c0103..e5ccf7ad29 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.h @@ -260,5 +260,7 @@ namespace AzToolsFramework AZStd::atomic_bool m_validConnection; SourceControlState m_connectionState; + + bool m_p4ApplicationDetected { false }; }; } // namespace AzToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt b/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt index ea5b65be7c..e64c9b80e7 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt @@ -10,6 +10,8 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS) return() endif() +ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) + ly_add_target( NAME AtomToolsFramework.Static STATIC NAMESPACE Gem @@ -18,9 +20,11 @@ ly_add_target( AUTORCC FILES_CMAKE atomtoolsframework_files.cmake + ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake INCLUDE_DIRECTORIES PRIVATE Source + ${pal_source_dir} PUBLIC Include BUILD_DEPENDENCIES diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 3fae2ee7a3..58b07d8351 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -216,7 +216,11 @@ namespace AtomToolsFramework AtomToolsMainWindowNotificationBus::Handler::BusDisconnect(); AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor); +#if AZ_TRAIT_ATOMTOOLSFRAMEWORK_SKIP_APP_DESTROY + _exit(0); +#else Base::Destroy(); +#endif } AZStd::vector AtomToolsApplication::GetCriticalAssetFilters() const diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/AtomToolsFramework_Traits_Linux.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/AtomToolsFramework_Traits_Linux.h new file mode 100644 index 0000000000..2f4c787fbe --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/AtomToolsFramework_Traits_Linux.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +// On some platforms, there is an issue with environment variables that are removed before some objects are deallocated (during the process of +// ComponentApplication::Destroy). Until all of the shutdown issues are solved, the following trait will skip the parent ::Destroy() and exit +// the application as soon as possible if set to true. +// (Tracked by GHI - 4806) +#define AZ_TRAIT_ATOMTOOLSFRAMEWORK_SKIP_APP_DESTROY true + diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/AtomToolsFramework_Traits_Platform.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/AtomToolsFramework_Traits_Platform.h new file mode 100644 index 0000000000..8101a49a5a --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/AtomToolsFramework_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/platform_linux_files.cmake new file mode 100644 index 0000000000..957ef8663e --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -0,0 +1,12 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + AtomToolsFramework_Traits_Platform.h + AtomToolsFramework_Traits_Linux.h +) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/AtomToolsFramework_Traits_Mac.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/AtomToolsFramework_Traits_Mac.h new file mode 100644 index 0000000000..3ca246c797 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/AtomToolsFramework_Traits_Mac.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +// On some platforms, there is an issue with environment variables that are removed before some objects are deallocated (during the process of +// ComponentApplication::Destroy). Until all of the shutdown issues are solved, the following trait will skip the parent ::Destroy() and exit +// the application as soon as possible if set to true. +// (Tracked by GHI - 4806) +#define AZ_TRAIT_ATOMTOOLSFRAMEWORK_SKIP_APP_DESTROY false + diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/AtomToolsFramework_Traits_Platform.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/AtomToolsFramework_Traits_Platform.h new file mode 100644 index 0000000000..6d8c8d7e32 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/AtomToolsFramework_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/platform_mac_files.cmake new file mode 100644 index 0000000000..13cc6886ef --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -0,0 +1,12 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + AtomToolsFramework_Traits_Platform.h + AtomToolsFramework_Traits_Mac.h +) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/AtomToolsFramework_Traits_Platform.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/AtomToolsFramework_Traits_Platform.h new file mode 100644 index 0000000000..ac82be7874 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/AtomToolsFramework_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/AtomToolsFramework_Traits_Windows.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/AtomToolsFramework_Traits_Windows.h new file mode 100644 index 0000000000..3ca246c797 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/AtomToolsFramework_Traits_Windows.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +// On some platforms, there is an issue with environment variables that are removed before some objects are deallocated (during the process of +// ComponentApplication::Destroy). Until all of the shutdown issues are solved, the following trait will skip the parent ::Destroy() and exit +// the application as soon as possible if set to true. +// (Tracked by GHI - 4806) +#define AZ_TRAIT_ATOMTOOLSFRAMEWORK_SKIP_APP_DESTROY false + diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/platform_windows_files.cmake new file mode 100644 index 0000000000..e2a2113bf9 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -0,0 +1,12 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + AtomToolsFramework_Traits_Platform.h + AtomToolsFramework_Traits_Windows.h +) From c2ec18dc0ead9e4b5ef39be84308cdf2064174d6 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 20 Oct 2021 14:41:35 -0700 Subject: [PATCH 069/152] Remove prefab WIP checks to make focus mode the default (and only) prefab editing workflow in the editor. (#4840) Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../UI/Prefab/PrefabIntegrationManager.cpp | 19 ++++--------------- .../UI/Prefab/PrefabUiHandler.cpp | 11 ++--------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index bef08466ae..65f7b0cdfb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -255,7 +255,7 @@ namespace AzToolsFramework if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity)) { // Edit Prefab - if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity)) + if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity)) { QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); @@ -1159,25 +1159,14 @@ namespace AzToolsFramework { s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId()); - bool prefabWipFeaturesEnabled = false; - AzFramework::ApplicationRequests::Bus::BroadcastResult( - prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); - - if (prefabWipFeaturesEnabled) - { - // Register entity as a container - s_containerEntityInterface->RegisterEntityAsContainer(entityId); - } + // Register entity as a container + s_containerEntityInterface->RegisterEntityAsContainer(entityId); } } void PrefabIntegrationManager::OnPrefabComponentDeactivate(AZ::EntityId entityId) { - bool prefabWipFeaturesEnabled = false; - AzFramework::ApplicationRequests::Bus::BroadcastResult( - prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); - - if (prefabWipFeaturesEnabled && !s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId)) + if (!s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId)) { // Unregister entity as a container s_containerEntityInterface->UnregisterEntityAsContainer(entityId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index ccad85e32b..7c2c65d204 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -316,14 +316,7 @@ namespace AzToolsFramework void PrefabUiHandler::OnDoubleClick(AZ::EntityId entityId) const { - bool prefabWipFeaturesEnabled = false; - AzFramework::ApplicationRequests::Bus::BroadcastResult( - prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); - - if (prefabWipFeaturesEnabled) - { - // Focus on this prefab - m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); - } + // Focus on this prefab + m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); } } From 9aa9ed8c8e160c06f6447ea568e49ca05c0374af Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Wed, 20 Oct 2021 14:55:08 -0700 Subject: [PATCH 070/152] Changing some class names and other download UI feedback. Signed-off-by: AMZN-Phil --- ...dController.cpp => DownloadController.cpp} | 30 +++++++++---------- ...nloadController.h => DownloadController.h} | 10 +++---- ...tDownloadWorker.cpp => DownloadWorker.cpp} | 10 +++---- ...bjectDownloadWorker.h => DownloadWorker.h} | 10 ++----- .../GemCatalog/GemCatalogHeaderWidget.cpp | 10 +++---- .../GemCatalog/GemCatalogHeaderWidget.h | 12 ++++---- .../Source/GemCatalog/GemCatalogScreen.cpp | 4 +-- .../Source/GemCatalog/GemCatalogScreen.h | 4 +-- .../Source/UpdateProjectCtrl.cpp | 2 +- .../project_manager_files.cmake | 8 ++--- 10 files changed, 48 insertions(+), 52 deletions(-) rename Code/Tools/ProjectManager/Source/{O3DEObjectDownloadController.cpp => DownloadController.cpp} (59%) rename Code/Tools/ProjectManager/Source/{O3DEObjectDownloadController.h => DownloadController.h} (83%) rename Code/Tools/ProjectManager/Source/{O3DEObjectDownloadWorker.cpp => DownloadWorker.cpp} (78%) rename Code/Tools/ProjectManager/Source/{O3DEObjectDownloadWorker.h => DownloadWorker.h} (84%) diff --git a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.cpp b/Code/Tools/ProjectManager/Source/DownloadController.cpp similarity index 59% rename from Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.cpp rename to Code/Tools/ProjectManager/Source/DownloadController.cpp index f3be0fd8e4..e06763ed4d 100644 --- a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadController.cpp @@ -6,8 +6,8 @@ * */ -#include -#include +#include +#include #include #include @@ -17,29 +17,29 @@ namespace O3DE::ProjectManager { - O3DEObjectDownloadController::O3DEObjectDownloadController(QWidget* parent) + DownloadController::DownloadController(QWidget* parent) : QObject() , m_lastProgress(0) , m_parent(parent) { - m_worker = new O3DEObjectDownloadWorker(); + m_worker = new DownloadWorker(); m_worker->moveToThread(&m_workerThread); - connect(&m_workerThread, &QThread::started, m_worker, &O3DEObjectDownloadWorker::StartDownload); - connect(m_worker, &O3DEObjectDownloadWorker::Done, this, &O3DEObjectDownloadController::HandleResults); - connect(m_worker, &O3DEObjectDownloadWorker::UpdateProgress, this, &O3DEObjectDownloadController::UpdateUIProgress); - connect(this, &O3DEObjectDownloadController::StartGemDownload, m_worker, &O3DEObjectDownloadWorker::StartDownload); + connect(&m_workerThread, &QThread::started, m_worker, &DownloadWorker::StartDownload); + connect(m_worker, &DownloadWorker::Done, this, &DownloadController::HandleResults); + connect(m_worker, &DownloadWorker::UpdateProgress, this, &DownloadController::UpdateUIProgress); + connect(this, &DownloadController::StartGemDownload, m_worker, &DownloadWorker::StartDownload); } - O3DEObjectDownloadController::~O3DEObjectDownloadController() + DownloadController::~DownloadController() { - connect(&m_workerThread, &QThread::finished, m_worker, &O3DEObjectDownloadWorker::deleteLater); + connect(&m_workerThread, &QThread::finished, m_worker, &DownloadController::deleteLater); m_workerThread.requestInterruption(); m_workerThread.quit(); m_workerThread.wait(); } - void O3DEObjectDownloadController::AddGemDownload(const QString& gemName) + void DownloadController::AddGemDownload(const QString& gemName) { m_gemNames.push_back(gemName); if (m_gemNames.size() == 1) @@ -49,18 +49,18 @@ namespace O3DE::ProjectManager } } - void O3DEObjectDownloadController::Start() + void DownloadController::Start() { } - void O3DEObjectDownloadController::UpdateUIProgress(int progress) + void DownloadController::UpdateUIProgress(int progress) { m_lastProgress = progress; emit GemDownloadProgress(progress); } - void O3DEObjectDownloadController::HandleResults(const QString& result) + void DownloadController::HandleResults(const QString& result) { bool succeeded = true; @@ -84,7 +84,7 @@ namespace O3DE::ProjectManager } } - void O3DEObjectDownloadController::HandleCancel() + void DownloadController::HandleCancel() { m_workerThread.quit(); emit Done(false); diff --git a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.h b/Code/Tools/ProjectManager/Source/DownloadController.h similarity index 83% rename from Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.h rename to Code/Tools/ProjectManager/Source/DownloadController.h index 7cd9220f9f..a769533c72 100644 --- a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadController.h +++ b/Code/Tools/ProjectManager/Source/DownloadController.h @@ -17,15 +17,15 @@ QT_FORWARD_DECLARE_CLASS(QProcess) namespace O3DE::ProjectManager { - QT_FORWARD_DECLARE_CLASS(O3DEObjectDownloadWorker) + QT_FORWARD_DECLARE_CLASS(DownloadWorker) - class O3DEObjectDownloadController : public QObject + class DownloadController : public QObject { Q_OBJECT public: - explicit O3DEObjectDownloadController(QWidget* parent = nullptr); - ~O3DEObjectDownloadController(); + explicit DownloadController(QWidget* parent = nullptr); + ~DownloadController(); void AddGemDownload(const QString& m_gemName); @@ -55,7 +55,7 @@ namespace O3DE::ProjectManager void GemDownloadProgress(int percentage); private: - O3DEObjectDownloadWorker* m_worker; + DownloadWorker* m_worker; QThread m_workerThread; QWidget* m_parent; AZStd::vector m_gemNames; diff --git a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.cpp b/Code/Tools/ProjectManager/Source/DownloadWorker.cpp similarity index 78% rename from Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.cpp rename to Code/Tools/ProjectManager/Source/DownloadWorker.cpp index 3462600c40..954eae432e 100644 --- a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadWorker.cpp @@ -6,8 +6,8 @@ * */ -#include -#include +#include +#include #include #include @@ -17,12 +17,12 @@ namespace O3DE::ProjectManager { - O3DEObjectDownloadWorker::O3DEObjectDownloadWorker() + DownloadWorker::DownloadWorker() : QObject() { } - void O3DEObjectDownloadWorker::StartDownload() + void DownloadWorker::StartDownload() { auto gemDownloadProgress = [=](int downloadProgress) { @@ -40,7 +40,7 @@ namespace O3DE::ProjectManager } } - void O3DEObjectDownloadWorker::SetGemToDownload(const QString& gemName, bool downloadNow) + void DownloadWorker::SetGemToDownload(const QString& gemName, bool downloadNow) { m_gemName = gemName; if (downloadNow) diff --git a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.h b/Code/Tools/ProjectManager/Source/DownloadWorker.h similarity index 84% rename from Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.h rename to Code/Tools/ProjectManager/Source/DownloadWorker.h index 4fff71634f..1bd51bca66 100644 --- a/Code/Tools/ProjectManager/Source/O3DEObjectDownloadWorker.h +++ b/Code/Tools/ProjectManager/Source/DownloadWorker.h @@ -9,17 +9,13 @@ #if !defined(Q_MOC_RUN) #include - -#include -#include -#include #endif QT_FORWARD_DECLARE_CLASS(QProcess) namespace O3DE::ProjectManager { - class O3DEObjectDownloadWorker : public QObject + class DownloadWorker : public QObject { // QProcess::waitForFinished uses -1 to indicate that the process should not timeout static constexpr int MaxBuildTimeMSecs = -1; @@ -29,8 +25,8 @@ namespace O3DE::ProjectManager Q_OBJECT public: - explicit O3DEObjectDownloadWorker(); - ~O3DEObjectDownloadWorker() = default; + explicit DownloadWorker(); + ~DownloadWorker() = default; public slots: void StartDownload(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 7d25ae180b..9396234a68 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -17,7 +17,7 @@ namespace O3DE::ProjectManager { - CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, O3DEObjectDownloadController* downloadController, QWidget* parent) + CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent) : QWidget(parent) , m_gemModel(gemModel) , m_downloadController(downloadController) @@ -252,8 +252,8 @@ namespace O3DE::ProjectManager update(0); // update the list to remove the gem that has finished }; // connect to download controller data changed - connect(m_downloadController, &O3DEObjectDownloadController::GemDownloadProgress, this, update); - connect(m_downloadController, &O3DEObjectDownloadController::Done, this, downloadEnded); + connect(m_downloadController, &DownloadController::GemDownloadProgress, this, update); + connect(m_downloadController, &DownloadController::Done, this, downloadEnded); update(0); } @@ -268,7 +268,7 @@ namespace O3DE::ProjectManager return gemNames; } - CartButton::CartButton(GemModel* gemModel, O3DEObjectDownloadController* downloadController, QWidget* parent) + CartButton::CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent) : QWidget(parent) , m_gemModel(gemModel) , m_downloadController(downloadController) @@ -374,7 +374,7 @@ namespace O3DE::ProjectManager } } - GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, O3DEObjectDownloadController* downloadController, QWidget* parent) + GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, DownloadController* downloadController, QWidget* parent) : QFrame(parent) { QHBoxLayout* hLayout = new QHBoxLayout(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 3d977e2b15..8e0eaa13ba 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include #endif namespace O3DE::ProjectManager @@ -32,7 +32,7 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - CartOverlayWidget(GemModel* gemModel, O3DEObjectDownloadController* downloadController, QWidget* parent = nullptr); + CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); private: QStringList ConvertFromModelIndices(const QVector& gems) const; @@ -43,7 +43,7 @@ namespace O3DE::ProjectManager QVBoxLayout* m_layout = nullptr; GemModel* m_gemModel = nullptr; - O3DEObjectDownloadController* m_downloadController = nullptr; + DownloadController* m_downloadController = nullptr; inline constexpr static int s_width = 240; }; @@ -54,7 +54,7 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - CartButton(GemModel* gemModel, O3DEObjectDownloadController* downloadController, QWidget* parent = nullptr); + CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); ~CartButton(); void ShowOverlay(); @@ -67,7 +67,7 @@ namespace O3DE::ProjectManager QLabel* m_countLabel = nullptr; QPushButton* m_dropDownButton = nullptr; CartOverlayWidget* m_cartOverlay = nullptr; - O3DEObjectDownloadController* m_downloadController = nullptr; + DownloadController* m_downloadController = nullptr; inline constexpr static int s_iconSize = 24; inline constexpr static int s_arrowDownIconSize = 8; @@ -79,7 +79,7 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, O3DEObjectDownloadController* downloadController, QWidget* parent = nullptr); + explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, DownloadController* downloadController, QWidget* parent = nullptr); ~GemCatalogHeaderWidget() = default; void ReinitForProject(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 5a4462d14b..08df6f255f 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -33,7 +33,7 @@ namespace O3DE::ProjectManager vLayout->setSpacing(0); setLayout(vLayout); - m_downloadController = new O3DEObjectDownloadController(); + m_downloadController = new DownloadController(); m_downloadController->Start(); m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index ad603138f0..361e34b214 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -39,7 +39,7 @@ namespace O3DE::ProjectManager EnableDisableGemsResult EnableDisableGemsForProject(const QString& projectPath); GemModel* GetGemModel() const { return m_gemModel; } - O3DEObjectDownloadController* GetDownloadController() const { return m_downloadController; } + DownloadController* GetDownloadController() const { return m_downloadController; } private: void FillModel(const QString& projectPath); @@ -51,6 +51,6 @@ namespace O3DE::ProjectManager GemSortFilterProxyModel* m_proxModel = nullptr; QVBoxLayout* m_filterWidgetLayout = nullptr; GemFilterWidget* m_filterWidget = nullptr; - O3DEObjectDownloadController* m_downloadController = nullptr; + DownloadController* m_downloadController = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 40f114b82b..fd2ebf340f 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -38,7 +38,7 @@ namespace O3DE::ProjectManager vLayout->addWidget(m_header); m_updateSettingsScreen = new UpdateProjectSettingsScreen(); - m_gemCatalogScreen = new GemCatalogScreen(nullptr); + m_gemCatalogScreen = new GemCatalogScreen(); m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index d53cf6c8c1..e2e35717f6 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -29,10 +29,10 @@ set(FILES Source/FormImageBrowseEditWidget.cpp Source/GemsSubWidget.h Source/GemsSubWidget.cpp - Source/O3DEObjectDownloadController.h - Source/O3DEObjectDownloadController.cpp - Source/O3DEObjectDownloadWorker.h - Source/O3DEObjectDownloadWorker.cpp + Source/DownloadController.h + Source/DownloadController.cpp + Source/DownloadWorker.h + Source/DownloadWorker.cpp Source/PathValidator.h Source/PathValidator.cpp Source/ProjectManagerWindow.h From a0b1dec929e336b43aadaabb99d0e95ccabf7bfc Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:31:01 -0700 Subject: [PATCH 071/152] Added support for material version updates in MaterialSourceData. This is necessary for tools like Material Editor and Asset Processor to work with the latest property names. - Added MaterialSourceData::ApplyVersionUpdates() for updating the properties. This should be called by tools after loading the MaterialSourceData. (But can be omitted if a tool wants to read the data exactly as it appears in the .material file). - Updated MaterialTypeSourceData::FindProperty to support applying version update renames, including a ApplyPropertyRenames utility function, which are necessary for MaterialSourceData to be able to find the necessary property definitons while loading. - Added a new context struct to JsonMaterialPropertyValueSerializer for passing down the material type version number, to help with applying property renames. - Renamed the .material file format "propertyLayoutVersion" to "materialTypeVersion" which is more accurate. This shouldn't hurt existing data as this field wasn't actually used for anything before. - Updated Material Editor to again store the material type version number in .material files. MaterialSourceDataTests updates... - Updated to include both a .materialtype file and a MaterialTypeAsset for the test material type. Both are used by the MaterialTypeSourceData class. - The default test material type now includes some version update steps; these are only used for version update tests and won't impact the other test functions. - Updated the path for storing temp files to disk, to just be in a "temp" folder in the exe path. (Originally they were saved to the gem folder near MaterialSourceDataTests.cpp, but at some point someone changed it to be under the exe folder, so there's no reason to use the full gem path anymore). MaterialTypeSourceDataTests updates... - Moved some code that was accidentally added to LoadAllFieldsUsingOldFormat but should have been in LoadAndStoreJson_AllFields. - Added test cases for unsupported version update operations Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Utils/Utils.cpp | 2 +- .../MaterialPropertyValueSerializer.h | 7 + .../RPI.Edit/Material/MaterialSourceData.h | 6 +- .../Material/MaterialTypeSourceData.h | 13 +- .../Atom/RPI.Reflect/Material/MaterialAsset.h | 3 +- .../RPI.Builders/Material/MaterialBuilder.cpp | 5 + .../MaterialPropertyValueSerializer.cpp | 4 +- .../RPI.Edit/Material/MaterialSourceData.cpp | 39 +++ .../Material/MaterialSourceDataSerializer.cpp | 9 +- .../Material/MaterialTypeSourceData.cpp | 73 +++++- .../RPI/Code/Tests/Common/AssetSystemStub.cpp | 10 +- .../Material/MaterialSourceDataTests.cpp | 244 +++++++++++++++--- .../Material/MaterialTypeSourceDataTests.cpp | 177 ++++++++++++- .../Code/Source/Document/MaterialDocument.cpp | 9 + 14 files changed, 535 insertions(+), 66 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp index 2031d14d08..e6bfd78806 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp @@ -120,7 +120,7 @@ namespace AZ::Utils AZ::Outcome WriteFile(AZStd::string_view content, AZStd::string_view filePath) { AZ::IO::FixedMaxPath filePathFixed = filePath; // Because FileIOStream requires a null-terminated string - AZ::IO::FileIOStream stream(filePathFixed.c_str(), AZ::IO::OpenMode::ModeWrite); + AZ::IO::FileIOStream stream(filePathFixed.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath); bool success = false; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyValueSerializer.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyValueSerializer.h index befbb7c990..29371618cf 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyValueSerializer.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialPropertyValueSerializer.h @@ -24,6 +24,13 @@ namespace AZ AZ_RTTI(AZ::RPI::JsonMaterialPropertyValueSerializer, "{A52B1ED8-C849-4269-9AA7-9D0814D2EC59}", BaseJsonSerializer); AZ_CLASS_ALLOCATOR_DECL; + //! A LoadContext object must be passed down to the serializer via JsonDeserializerContext::GetMetadata().Add(...) + struct LoadContext + { + AZ_TYPE_INFO(JsonMaterialPropertyValueSerializer::LoadContext, "{5E0A891A-27F6-4AD7-88A5-B9EA50F88B45}"); + uint32_t m_materialTypeVersion; //!< The version number from the .materialtype file + }; + JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, JsonDeserializerContext& context) override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index 02607a7954..deeace0d41 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -50,7 +50,7 @@ namespace AZ AZStd::string m_parentMaterial; //!< The immediate parent of this material - uint32_t m_propertyLayoutVersion = 0; //!< The version of the property layout, defined in the material type, which was used to configure this material + uint32_t m_materialTypeVersion = 0; //!< The version of the material type that was used to configure this material struct Property { @@ -64,6 +64,10 @@ namespace AZ PropertyGroupMap m_properties; + //! Checks the material type version and potentially applies a series of property changes (most common are simple property renames) + //! based on the MaterialTypeAsset's version update procedure. + bool ApplyVersionUpdates(); + //! Creates a MaterialAsset from the MaterialSourceData content. //! @param assetId ID for the MaterialAsset //! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for resolving file-relative paths. diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h index 44ac926ecf..99eb15db24 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace AZ { @@ -179,7 +180,12 @@ namespace AZ const GroupDefinition* FindGroup(AZStd::string_view groupName) const; - const PropertyDefinition* FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName) const; + //! Searches for a specific property. + //! Note this function can find properties using old versions of the property name; in that case, + //! the name in the returned PropertyDefinition* will not match the @propertyName that was searched for. + //! @param materialTypeVersion indicates the version number of the property name being passed in. Only renames above this version number will be applied. + //! @return the requested property, or null if it could not be found + const PropertyDefinition* FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName, uint32_t materialTypeVersion = 0) const; //! Construct a complete list of group definitions, including implicit groups, arranged in the same order as the source data //! Groups with the same name will be consolidated into a single entry @@ -205,6 +211,11 @@ namespace AZ bool ConvertPropertyValueToSourceDataFormat(const PropertyDefinition& propertyDefinition, MaterialPropertyValue& propertyValue) const; Outcome> CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath = "", bool elevateWarnings = true) const; + + //! Possibly renames @propertyId based on the material version update steps. + //! @param materialTypeVersion indicates the version number of the property name being passed in. Only renames above this version number will be applied. + //! @return true if the property was renamed + bool ApplyPropertyRenames(MaterialPropertyId& propertyId, uint32_t materialTypeVersion = 0) const; }; //! The wrapper class for derived material functors. diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h index e14ff30f87..2a1de6debd 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h @@ -122,7 +122,8 @@ namespace AZ //! from m_materialTypeAsset. void RealignPropertyValuesAndNames(); - //! Renames properties in m_propertyNames based on the MaterialTypeAsset's version update. Note that only version upgrades are supported. + //! Checks the material type version and potentially applies a series of property changes (most common are simple property renames) + //! based on the MaterialTypeAsset's version update procedure. void ApplyVersionUpdates(); //! Called by asset creators to assign the asset to a ready state. diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index 86ea8ab688..0680c5c428 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -287,6 +287,11 @@ namespace AZ return {}; } + if (!material.GetValue().ApplyVersionUpdates()) + { + return {}; + } + auto materialAssetOutcome = material.GetValue().CreateMaterialAsset(Uuid::CreateRandom(), materialSourceFilePath, true); if (!materialAssetOutcome.IsSuccess()) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp index 3b2d36451a..5e04365ffb 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp @@ -62,6 +62,8 @@ namespace AZ return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Catastrophic, "Material type reference not found."); } + const JsonMaterialPropertyValueSerializer::LoadContext* loadContext = context.GetMetadata().Find(); + // Construct the full property name (groupName.propertyName) by parsing it from the JSON path string. size_t startPropertyName = context.GetPath().Get().rfind('/'); size_t startGroupName = context.GetPath().Get().rfind('/', startPropertyName-1); @@ -70,7 +72,7 @@ namespace AZ JSR::ResultCode result(JSR::Tasks::ReadField); - auto propertyDefinition = materialType->FindProperty(groupName, propertyName); + auto propertyDefinition = materialType->FindProperty(groupName, propertyName, loadContext->m_materialTypeVersion); if (!propertyDefinition) { AZStd::string message = AZStd::string::format("Property '%.*s.%.*s' not found in material type.", AZ_STRING_ARG(groupName), AZ_STRING_ARG(propertyName)); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index f697f33a3f..e43095f639 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -72,6 +72,45 @@ namespace AZ materialAssetCreator.SetPropertyValue(propertyId, entry.second); } } + + bool MaterialSourceData::ApplyVersionUpdates() + { + auto materialTypeSourceDataOutcome = MaterialUtils::LoadMaterialTypeSourceData(m_materialType); + if (!materialTypeSourceDataOutcome.IsSuccess()) + { + return false; + } + + MaterialTypeSourceData materialTypeSourceData = materialTypeSourceDataOutcome.TakeValue(); + + // Note that the only kind of property update currently supported is rename... + + for (auto& groupPair : m_properties) + { + PropertyMap& propertyMap = groupPair.second; + + PropertyMap newPropertyMap; + + for (auto& propertyPair : propertyMap) + { + MaterialPropertyId propertyId{groupPair.first, propertyPair.first}; + if (materialTypeSourceData.ApplyPropertyRenames(propertyId, m_materialTypeVersion)) + { + newPropertyMap[propertyId.GetPropertyName().GetStringView()] = propertyPair.second; + } + else + { + newPropertyMap[propertyPair.first] = propertyPair.second; + } + } + + propertyMap = newPropertyMap; + } + + m_materialTypeVersion = materialTypeSourceData.m_version; + + return true; + } Outcome > MaterialSourceData::CreateMaterialAsset(Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp index 5e4af07aae..cdd434e894 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -47,7 +48,7 @@ namespace AZ result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_description, azrtti_typeid(), inputValue, "description", context)); result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_materialType, azrtti_typeid(), inputValue, "materialType", context)); result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_parentMaterial, azrtti_typeid(), inputValue, "parentMaterial", context)); - result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_propertyLayoutVersion, azrtti_typeid(), inputValue, "propertyLayoutVersion", context)); + result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_materialTypeVersion, azrtti_typeid(), inputValue, "materialTypeVersion", context)); if (materialSourceData->m_materialType.empty()) { @@ -118,6 +119,10 @@ namespace AZ context.GetMetadata().Add(AZStd::move(materialTypeData)); + JsonMaterialPropertyValueSerializer::LoadContext materialPropertyValueLoadContext; + materialPropertyValueLoadContext.m_materialTypeVersion = materialSourceData->m_materialTypeVersion; + context.GetMetadata().Add(materialPropertyValueLoadContext); + result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_properties, azrtti_typeid(), inputValue, "properties", context)); if (result.GetProcessing() == JsonSerializationResult::Processing::Completed) @@ -148,7 +153,7 @@ namespace AZ resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "description", &materialSourceData->m_description, nullptr, azrtti_typeid(), context)); resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "materialType", &materialSourceData->m_materialType, nullptr, azrtti_typeid(), context)); resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "parentMaterial", &materialSourceData->m_parentMaterial, nullptr, azrtti_typeid(), context)); - resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "propertyLayoutVersion", &materialSourceData->m_propertyLayoutVersion, nullptr, azrtti_typeid(), context)); + resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "materialTypeVersion", &materialSourceData->m_materialTypeVersion, nullptr, azrtti_typeid(), context)); resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "properties", &materialSourceData->m_properties, nullptr, azrtti_typeid(), context)); return context.Report(resultCode, "Processed material."); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 5e2ceacafc..ea2e499972 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -129,7 +129,38 @@ namespace AZ return nullptr; } - const MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName) const + bool MaterialTypeSourceData::ApplyPropertyRenames(MaterialPropertyId& propertyId, uint32_t materialTypeVersion) const + { + bool renamed = false; + + for (const VersionUpdateDefinition& versionUpdate : m_versionUpdates) + { + if (materialTypeVersion >= versionUpdate.m_toVersion) + { + continue; + } + + for (const VersionUpdatesRenameOperationDefinition& action : versionUpdate.m_actions) + { + if (action.m_operation == "rename") + { + if (action.m_renameFrom == propertyId.GetFullName().GetStringView()) + { + propertyId = MaterialPropertyId::Parse(action.m_renameTo); + renamed = true; + } + } + else + { + AZ_Warning("Material source data", false, "Unsupported material version update operation '%s'", action.m_operation.c_str()); + } + } + } + + return renamed; + } + + const MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName, uint32_t materialTypeVersion) const { auto groupIter = m_propertyLayout.m_properties.find(groupName); if (groupIter == m_propertyLayout.m_properties.end()) @@ -145,6 +176,27 @@ namespace AZ } } + // Property has not been found, try looking for renames in the version history + + MaterialPropertyId propertyId = MaterialPropertyId{groupName, propertyName}; + ApplyPropertyRenames(propertyId, materialTypeVersion); + + // Do the search again with the new names + + groupIter = m_propertyLayout.m_properties.find(propertyId.GetGroupName().GetStringView()); + if (groupIter == m_propertyLayout.m_properties.end()) + { + return nullptr; + } + + for (const PropertyDefinition& property : groupIter->second) + { + if (property.m_name == propertyId.GetPropertyName().GetStringView()) + { + return &property; + } + } + return nullptr; } @@ -302,17 +354,24 @@ namespace AZ // Set materialtype version and add each version update object into MaterialTypeAsset. materialTypeAssetCreator.SetVersion(m_version); { - const AZ::Name rename = AZ::Name{ "rename" }; - const AZ::Name from = AZ::Name{ "from" }; - const AZ::Name to = AZ::Name{ "to" }; + const AZ::Name rename = AZ::Name{ "rename" }; + const AZ::Name from = AZ::Name{ "from" }; + const AZ::Name to = AZ::Name{ "to" }; for (const auto& versionUpdate : m_versionUpdates) { MaterialVersionUpdate materialVersionUpdate; for (const auto& action : versionUpdate.m_actions) { - materialVersionUpdate.AddAction(MaterialVersionUpdate::Action(rename, { - { from, AZ::Name{ action.m_renameFrom } }, - { to, AZ::Name{ action.m_renameTo } } })); + if (action.m_operation == rename.GetStringView()) + { + materialVersionUpdate.AddAction(MaterialVersionUpdate::Action(rename, { + { from, AZ::Name{ action.m_renameFrom } }, + { to, AZ::Name{ action.m_renameTo } } })); + } + else + { + materialTypeAssetCreator.ReportWarning("Unsupported material version update operation '%s'", action.m_operation.c_str()); + } } materialTypeAssetCreator.AddVersionUpdate(versionUpdate.m_toVersion, materialVersionUpdate); } diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp index a5511edea1..31a46e9a6f 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp @@ -7,6 +7,7 @@ */ #include +#include namespace UnitTest { @@ -36,12 +37,17 @@ namespace UnitTest // Because GetSourceInfoBySourcePath should always return 0 for the sub-id, since it's about the source file not product file. sourceInfo.m_assetInfo.m_assetId.m_subId = 0; - m_sourceInfoMap.emplace(sourcePath, sourceInfo); + AZStd::string normalizedSourcePath = sourcePath; + AzFramework::StringFunc::Path::Normalize(normalizedSourcePath); + m_sourceInfoMap.emplace(normalizedSourcePath, sourceInfo); } bool AssetSystemStub::GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) { - auto iter = m_sourceInfoMap.find(sourcePath); + AZStd::string normalizedSourcePath = sourcePath; + AzFramework::StringFunc::Path::Normalize(normalizedSourcePath); + + auto iter = m_sourceInfoMap.find(normalizedSourcePath); if (iter != m_sourceInfoMap.end()) { diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp index dd3b3b2711..a6ce6504fb 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -60,22 +61,72 @@ namespace UnitTest localFileIO->SetAlias("@exefolder@", rootPath); m_testMaterialSrgLayout = CreateCommonTestMaterialSrgLayout(); - m_testShaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout); + m_assetSystemStub.RegisterSourceInfo("@exefolder@/Temp/test.shader", m_testShaderAsset.GetId()); - MaterialTypeAssetCreator materialTypeCreator; - materialTypeCreator.Begin(Uuid::CreateRandom()); - materialTypeCreator.AddShader(m_testShaderAsset); - AddCommonTestMaterialProperties(materialTypeCreator, "general."); - materialTypeCreator.End(m_testMaterialTypeAsset); + // The MaterialSourceData relies on both MaterialTypeSourceData and MaterialTypeAsset. We have to make sure the + // .materialtype file is present on disk, and that the MaterialTypeAsset is available through the asset database stub... + + const char* materialTypeJson = R"( + { + "version": 10, + "propertyLayout": { + "properties": { + "general": [ + {"name": "MyBool", "type": "bool"}, + {"name": "MyInt", "type": "Int"}, + {"name": "MyUInt", "type": "UInt"}, + {"name": "MyFloat", "type": "Float"}, + {"name": "MyFloat2", "type": "Vector2"}, + {"name": "MyFloat3", "type": "Vector3"}, + {"name": "MyFloat4", "type": "Vector4"}, + {"name": "MyColor", "type": "Color"}, + {"name": "MyImage", "type": "Image"}, + {"name": "MyEnum", "type": "Enum", "enumValues": ["Enum0", "Enum1", "Enum2"], "defaultValue": "Enum0"} + ] + } + }, + "shaders": [ + { + "file": "@exefolder@/Temp/test.shader" + } + ], + "versionUpdates": [ + { + "toVersion": 2, + "actions": [ + {"op": "rename", "from": "general.testColorNameA", "to": "general.testColorNameB"} + ] + }, + { + "toVersion": 4, + "actions": [ + {"op": "rename", "from": "general.testColorNameB", "to": "general.testColorNameC"} + ] + }, + { + "toVersion": 10, + "actions": [ + {"op": "rename", "from": "general.testColorNameC", "to": "general.MyColor"} + ] + } + ] + } + )"; + + AZ::Utils::WriteFile(materialTypeJson, "@exefolder@/Temp/test.materialtype"); + + MaterialTypeSourceData materialTypeSourceData; + LoadTestDataFromJson(materialTypeSourceData, materialTypeJson); + m_testMaterialTypeAsset = materialTypeSourceData.CreateMaterialTypeAsset(Uuid::CreateRandom()).TakeValue(); // Since this test doesn't actually instantiate a Material, it won't need to instantiate this ImageAsset, so all we // need is an asset reference with a valid ID. m_testImageAsset = Data::Asset{ Data::AssetId{Uuid::CreateRandom(), StreamingImageAsset::GetImageAssetSubId()}, azrtti_typeid() }; // Register the test assets with the AssetSystemStub so CreateMaterialAsset() can use AssetUtils. - m_assetSystemStub.RegisterSourceInfo("test.materialtype", m_testMaterialTypeAsset.GetId()); - m_assetSystemStub.RegisterSourceInfo("test.streamingimage", m_testImageAsset.GetId()); + m_assetSystemStub.RegisterSourceInfo("@exefolder@/Temp/test.materialtype", m_testMaterialTypeAsset.GetId()); + m_assetSystemStub.RegisterSourceInfo("@exefolder@/Temp/test.streamingimage", m_testImageAsset.GetId()); } void TearDown() override @@ -88,12 +139,12 @@ namespace UnitTest RPITestFixture::TearDown(); } }; - + void AddPropertyGroup(MaterialSourceData& material, AZStd::string_view groupName) { material.m_properties.insert(groupName); } - + void AddProperty(MaterialSourceData& material, AZStd::string_view groupName, AZStd::string_view propertyName, const MaterialPropertyValue& anyValue) { material.m_properties[groupName][propertyName].m_value = anyValue; @@ -103,7 +154,7 @@ namespace UnitTest { MaterialSourceData sourceData; - sourceData.m_materialType = "test.materialtype"; + sourceData.m_materialType = "@exefolder@/Temp/test.materialtype"; AddPropertyGroup(sourceData, "general"); AddProperty(sourceData, "general", "MyBool", true); AddProperty(sourceData, "general", "MyInt", -10); @@ -113,7 +164,7 @@ namespace UnitTest AddProperty(sourceData, "general", "MyFloat2", AZ::Vector2(2.1f, 2.2f)); AddProperty(sourceData, "general", "MyFloat3", AZ::Vector3(3.1f, 3.2f, 3.3f)); AddProperty(sourceData, "general", "MyFloat4", AZ::Vector4(4.1f, 4.2f, 4.3f, 4.4f)); - AddProperty(sourceData, "general", "MyImage", AZStd::string("test.streamingimage")); + AddProperty(sourceData, "general", "MyImage", AZStd::string("@exefolder@/Temp/test.streamingimage")); AddProperty(sourceData, "general", "MyEnum", AZStd::string("Enum1")); auto materialAssetOutcome = sourceData.CreateMaterialAsset(Uuid::CreateRandom(), "", true); @@ -139,7 +190,7 @@ namespace UnitTest EXPECT_STREQ(a.m_materialType.data(), b.m_materialType.data()); EXPECT_STREQ(a.m_description.data(), b.m_description.data()); EXPECT_STREQ(a.m_parentMaterial.data(), b.m_parentMaterial.data()); - EXPECT_EQ(a.m_propertyLayoutVersion, b.m_propertyLayoutVersion); + EXPECT_EQ(a.m_materialTypeVersion, b.m_materialTypeVersion); EXPECT_EQ(a.m_properties.size(), b.m_properties.size()); for (auto& groupA : a.m_properties) @@ -170,7 +221,7 @@ namespace UnitTest auto& propertyA = propertyIterA.second; auto& propertyB = propertyIterB->second; - + bool typesMatch = propertyA.m_value.GetTypeId() == propertyB.m_value.GetTypeId(); EXPECT_TRUE(typesMatch); if (typesMatch) @@ -229,8 +280,8 @@ namespace UnitTest " } \n" "} \n"; - const char* materialTypeFilePath = "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/roundTripTest.materialtype"; - + const char* materialTypeFilePath = "@exefolder@/Temp/roundTripTest.materialtype"; + AZ::IO::FileIOStream file; EXPECT_TRUE(file.Open(materialTypeFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath)); file.Write(strlen(materialTypeJson), materialTypeJson); @@ -240,7 +291,7 @@ namespace UnitTest sourceDataOriginal.m_materialType = materialTypeFilePath; sourceDataOriginal.m_parentMaterial = materialTypeFilePath; sourceDataOriginal.m_description = "This is a description"; - sourceDataOriginal.m_propertyLayoutVersion = 7; + sourceDataOriginal.m_materialTypeVersion = 7; AddPropertyGroup(sourceDataOriginal, "groupA"); AddProperty(sourceDataOriginal, "groupA", "MyBool", true); AddProperty(sourceDataOriginal, "groupA", "MyInt", -10); @@ -252,14 +303,14 @@ namespace UnitTest AddPropertyGroup(sourceDataOriginal, "groupC"); AddProperty(sourceDataOriginal, "groupC", "MyFloat4", AZ::Vector4(4.1f, 4.2f, 4.3f, 4.4f)); AddProperty(sourceDataOriginal, "groupC", "MyColor", AZ::Color{0.1f, 0.2f, 0.3f, 0.4f}); - AddProperty(sourceDataOriginal, "groupC", "MyImage", AZStd::string("test.streamingimage")); + AddProperty(sourceDataOriginal, "groupC", "MyImage", AZStd::string("@exefolder@/Temp/test.streamingimage")); AZStd::string sourceDataSerialized; JsonTestResult storeResult = StoreTestDataToJson(sourceDataOriginal, sourceDataSerialized); MaterialSourceData sourceDataCopy; JsonTestResult loadResult = LoadTestDataFromJson(sourceDataCopy, sourceDataSerialized); - + CheckEqual(sourceDataOriginal, sourceDataCopy); } @@ -277,10 +328,10 @@ namespace UnitTest ] } } - } + } )"; - const char* materialTypeFilePath = "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype"; + const char* materialTypeFilePath = "@exefolder@/Temp/simpleMaterialType.materialtype"; AZ::IO::FileIOStream file; EXPECT_TRUE(file.Open(materialTypeFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath)); @@ -296,7 +347,7 @@ namespace UnitTest "testColor": [0.1,0.2,0.3] } }, - "materialType": "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype" + "materialType": "@exefolder@/Temp/simpleMaterialType.materialtype" } )"; @@ -330,7 +381,7 @@ namespace UnitTest { const AZStd::string inputJson = R"( { - "propertyLayoutVersion": 1, + "materialTypeVersion": 1, "properties": { "baseColor": { "color": [1.0,1.0,1.0] @@ -354,7 +405,7 @@ namespace UnitTest const AZStd::string inputJson = R"( { "materialType": "DoesNotExist.materialtype", - "propertyLayoutVersion": 1, + "materialTypeVersion": 1, "properties": { "baseColor": { "color": [1.0,1.0,1.0] @@ -387,10 +438,10 @@ namespace UnitTest ] } } - } + } )"; - const char* materialTypeFilePath = "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype"; + const char* materialTypeFilePath = "@exefolder@/Temp/simpleMaterialType.materialtype"; AZ::IO::FileIOStream file; EXPECT_TRUE(file.Open(materialTypeFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath)); @@ -399,8 +450,8 @@ namespace UnitTest const AZStd::string inputJson = R"( { - "materialType": "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype", - "propertyLayoutVersion": 1, + "materialType": "@exefolder@/Temp/simpleMaterialType.materialtype", + "materialTypeVersion": 1, "properties": { "general": { "testColor": [1.0,1.0,1.0] @@ -433,10 +484,10 @@ namespace UnitTest ] } } - } + } )"; - const char* materialTypeFilePath = "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype"; + const char* materialTypeFilePath = "@exefolder@/Temp/simpleMaterialType.materialtype"; AZ::IO::FileIOStream file; EXPECT_TRUE(file.Open(materialTypeFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath)); @@ -445,8 +496,8 @@ namespace UnitTest const AZStd::string inputJson = R"( { - "materialType": "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype", - "propertyLayoutVersion": 1, + "materialType": "@exefolder@/Temp/simpleMaterialType.materialtype", + "materialTypeVersion": 1, "properties": { "general": { "doesNotExist": [1.0,1.0,1.0] @@ -467,20 +518,20 @@ namespace UnitTest TEST_F(MaterialSourceDataTests, CreateMaterialAsset_MultiLevelDataInheritance) { MaterialSourceData sourceDataLevel1; - sourceDataLevel1.m_materialType = "test.materialtype"; + sourceDataLevel1.m_materialType = "@exefolder@/Temp/test.materialtype"; AddPropertyGroup(sourceDataLevel1, "general"); AddProperty(sourceDataLevel1, "general", "MyFloat", 1.5f); AddProperty(sourceDataLevel1, "general", "MyColor", AZ::Color{0.1f, 0.2f, 0.3f, 0.4f}); MaterialSourceData sourceDataLevel2; - sourceDataLevel2.m_materialType = "test.materialtype"; + sourceDataLevel2.m_materialType = "@exefolder@/Temp/test.materialtype"; sourceDataLevel2.m_parentMaterial = "level1.material"; AddPropertyGroup(sourceDataLevel2, "general"); AddProperty(sourceDataLevel2, "general", "MyColor", AZ::Color{0.15f, 0.25f, 0.35f, 0.45f}); AddProperty(sourceDataLevel2, "general", "MyFloat2", AZ::Vector2{4.1f, 4.2f}); MaterialSourceData sourceDataLevel3; - sourceDataLevel3.m_materialType = "test.materialtype"; + sourceDataLevel3.m_materialType = "@exefolder@/Temp/test.materialtype"; sourceDataLevel3.m_parentMaterial = "level2.material"; AddPropertyGroup(sourceDataLevel3, "general"); AddProperty(sourceDataLevel3, "general", "MyFloat", 3.5f); @@ -497,7 +548,7 @@ namespace UnitTest auto materialAssetLevel3 = sourceDataLevel3.CreateMaterialAsset(Uuid::CreateRandom(), "", true); EXPECT_TRUE(materialAssetLevel3.IsSuccess()); - + auto layout = m_testMaterialTypeAsset->GetMaterialPropertiesLayout(); MaterialPropertyIndex myFloat = layout->FindPropertyIndex(Name("general.MyFloat")); MaterialPropertyIndex myFloat2 = layout->FindPropertyIndex(Name("general.MyFloat2")); @@ -535,14 +586,14 @@ namespace UnitTest m_assetSystemStub.RegisterSourceInfo("otherBase.materialtype", otherMaterialType.GetId()); MaterialSourceData sourceDataLevel1; - sourceDataLevel1.m_materialType = "test.materialtype"; + sourceDataLevel1.m_materialType = "@exefolder@/Temp/test.materialtype"; MaterialSourceData sourceDataLevel2; - sourceDataLevel2.m_materialType = "test.materialtype"; + sourceDataLevel2.m_materialType = "@exefolder@/Temp/test.materialtype"; sourceDataLevel2.m_parentMaterial = "level1.material"; MaterialSourceData sourceDataLevel3; - sourceDataLevel3.m_materialType = "otherBase.materialtype"; + sourceDataLevel3.m_materialType = "@exefolder@/Temp/otherBase.materialtype"; sourceDataLevel3.m_parentMaterial = "level2.material"; auto materialAssetLevel1 = sourceDataLevel1.CreateMaterialAsset(Uuid::CreateRandom(), "", true); @@ -570,7 +621,7 @@ namespace UnitTest { MaterialSourceData sourceData; - sourceData.m_materialType = "test.materialtype"; + sourceData.m_materialType = "@exefolder@/Temp/test.materialtype"; AddPropertyGroup(sourceData, "general"); @@ -587,7 +638,7 @@ namespace UnitTest { MaterialSourceData sourceData; - sourceData.m_materialType = "test.materialtype"; + sourceData.m_materialType = "@exefolder@/Temp/test.materialtype"; AddPropertyGroup(sourceData, "general"); @@ -629,7 +680,7 @@ namespace UnitTest expectWarning([](MaterialSourceData& materialSourceData) { - AddProperty(materialSourceData, "general", "DoesNotExist", AZStd::string("test.streamingimage")); + AddProperty(materialSourceData, "general", "DoesNotExist", AZStd::string("@exefolder@/Temp/test.streamingimage")); }); // Missing image reference @@ -638,6 +689,115 @@ namespace UnitTest AddProperty(materialSourceData, "general", "MyImage", AZStd::string("doesNotExist.streamingimage")); }, 3); // Expect a 3rd error because AssetUtils reports its own assertion failure } + + + TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionUpdate) + { + const AZStd::string inputJson = R"( + { + "materialType": "@exefolder@/Temp/test.materialtype", + "materialTypeVersion": 1, + "properties": { + "general": { + "testColorNameA": [0.1, 0.2, 0.3] + } + } + } + )"; + + MaterialSourceData material; + JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson); + + EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask()); + EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing()); + + // Initially, the loaded material data will match the .material file exactly. This gives us the accurate representation of + // what's actually saved on disk. + + EXPECT_NE(material.m_properties["general"].find("testColorNameA"), material.m_properties["general"].end()); + EXPECT_EQ(material.m_properties["general"].find("testColorNameB"), material.m_properties["general"].end()); + EXPECT_EQ(material.m_properties["general"].find("testColorNameC"), material.m_properties["general"].end()); + EXPECT_EQ(material.m_properties["general"].find("MyColor"), material.m_properties["general"].end()); + + AZ::Color testColor = material.m_properties["general"]["testColorNameA"].m_value.GetValue(); + EXPECT_TRUE(AZ::Color(0.1f, 0.2f, 0.3f, 1.0f).IsClose(testColor, 0.01)); + + EXPECT_EQ(1, material.m_materialTypeVersion); + + // Then we force the material data to update to the latest material type version specification + material.ApplyVersionUpdates(); + + // Now the material data should match the latest material type. + // Look for the property under the latest name in the material type, not the name used in the .material file. + + EXPECT_EQ(material.m_properties["general"].find("testColorNameA"), material.m_properties["general"].end()); + EXPECT_EQ(material.m_properties["general"].find("testColorNameB"), material.m_properties["general"].end()); + EXPECT_EQ(material.m_properties["general"].find("testColorNameC"), material.m_properties["general"].end()); + EXPECT_NE(material.m_properties["general"].find("MyColor"), material.m_properties["general"].end()); + + testColor = material.m_properties["general"]["MyColor"].m_value.GetValue(); + EXPECT_TRUE(AZ::Color(0.1f, 0.2f, 0.3f, 1.0f).IsClose(testColor, 0.01)); + + EXPECT_EQ(10, material.m_materialTypeVersion); + } + + TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionPartialUpdate) + { + // This case is similar to Load_MaterialTypeVersionUpdate but we start at a later + // version so only some of the version updates are applied. + + const AZStd::string inputJson = R"( + { + "materialType": "@exefolder@/Temp/test.materialtype", + "materialTypeVersion": 3, + "properties": { + "general": { + "testColorNameB": [0.1, 0.2, 0.3] + } + } + } + )"; + + MaterialSourceData material; + JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson); + + EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask()); + EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing()); + + material.ApplyVersionUpdates(); + + AZ::Color testColor = material.m_properties["general"]["MyColor"].m_value.GetValue(); + EXPECT_TRUE(AZ::Color(0.1f, 0.2f, 0.3f, 1.0f).IsClose(testColor, 0.01)); + + EXPECT_EQ(10, material.m_materialTypeVersion); + } + + TEST_F(MaterialSourceDataTests, Load_Error_MaterialTypeVersionUpdateWithMismatchedVersion) + { + const AZStd::string inputJson = R"( + { + "materialType": "@exefolder@/Temp/test.materialtype", + "materialTypeVersion": 3, // At this version, the property should be testColorNameB not testColorNameA + "properties": { + "general": { + "testColorNameA": [0.1, 0.2, 0.3] + } + } + } + )"; + + MaterialSourceData material; + JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson); + + loadResult.ContainsMessage("/properties/general/testColorNameA", "Property 'general.testColorNameA' not found in material type."); + + EXPECT_FALSE(material.m_properties["general"]["testColorNameA"].m_value.IsValid()); + + material.ApplyVersionUpdates(); + + EXPECT_FALSE(material.m_properties["general"]["MyColor"].m_value.IsValid()); + } + } diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp index 778fca5402..709d6b84ec 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -1070,7 +1071,12 @@ namespace UnitTest EXPECT_EQ(material.m_description, "This is a general description about the material"); - EXPECT_EQ(material.m_propertyLayout.m_version, 2); + EXPECT_EQ(material.m_version, 2); + EXPECT_EQ(material.m_versionUpdates.size(), 1); + EXPECT_EQ(material.m_versionUpdates[0].m_toVersion, 2); + EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_operation, "rename"); + EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_renameFrom, "groupA.fooPrev"); + EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_renameTo, "groupA.foo"); EXPECT_EQ(material.m_propertyLayout.m_groups.size(), 2); EXPECT_TRUE(material.FindGroup("groupA") != nullptr); @@ -1215,12 +1221,7 @@ namespace UnitTest JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson); EXPECT_EQ(material.m_description, "This is a general description about the material"); - EXPECT_EQ(material.m_version, 2); - EXPECT_EQ(material.m_versionUpdates.size(), 1); - EXPECT_EQ(material.m_versionUpdates[0].m_toVersion, 2); - EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_operation, "rename"); - EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_renameFrom, "groupA.fooPrev"); - EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_renameTo, "groupA.foo"); + EXPECT_EQ(material.m_propertyLayout.m_groups.size(), 2); EXPECT_TRUE(material.FindGroup("groupA") != nullptr); EXPECT_TRUE(material.FindGroup("groupB") != nullptr); @@ -1277,7 +1278,6 @@ namespace UnitTest { "description": "", "propertyLayout": { - "version": 2, "groups": [ { "name": "general", @@ -1316,4 +1316,165 @@ namespace UnitTest CheckPropertyValue>(materialTypeAsset, Name{ "general.absolute" }, m_testImageAsset2); CheckPropertyValue>(materialTypeAsset, Name{ "general.relative" }, m_testImageAsset2); } + + + TEST_F(MaterialTypeSourceDataTests, FindPropertyUsingOldName) + { + const AZStd::string inputJson = R"( + { + "version": 10, + "versionUpdates": [ + { + "toVersion": 2, + "actions": [ + { "op": "rename", "from": "general.fooA", "to": "general.fooB" } + ] + }, + { + "toVersion": 4, + "actions": [ + { "op": "rename", "from": "general.barA", "to": "general.barB" } + ] + }, + { + "toVersion": 6, + "actions": [ + { "op": "rename", "from": "general.fooB", "to": "general.fooC" }, + { "op": "rename", "from": "general.barB", "to": "general.barC" } + ] + }, + { + "toVersion": 7, + "actions": [ + { "op": "rename", "from": "general.bazA", "to": "otherGroup.bazB" } + ] + } + ], + "propertyLayout": { + "properties": { + "general": [ + { + "name": "fooC", + "type": "Bool" + }, + { + "name": "barC", + "type": "Float" + } + ], + "otherGroup": [ + { + "name": "dontMindMe", + "type": "Bool" + }, + { + "name": "bazB", + "type": "Float" + } + ] + } + } + } + )"; + + MaterialTypeSourceData materialType; + JsonTestResult loadResult = LoadTestDataFromJson(materialType, inputJson); + + EXPECT_EQ(materialType.m_version, 10); + + // First find the properties using their correct current names + const MaterialTypeSourceData::PropertyDefinition* foo = materialType.FindProperty("general", "fooC"); + const MaterialTypeSourceData::PropertyDefinition* bar = materialType.FindProperty("general", "barC"); + const MaterialTypeSourceData::PropertyDefinition* baz = materialType.FindProperty("otherGroup", "bazB"); + + EXPECT_TRUE(foo); + EXPECT_TRUE(bar); + EXPECT_TRUE(baz); + EXPECT_EQ(foo->m_name, "fooC"); + EXPECT_EQ(bar->m_name, "barC"); + EXPECT_EQ(baz->m_name, "bazB"); + + // Now try doing the property lookup using old versions of the name and make sure the same property can be found + + EXPECT_EQ(foo, materialType.FindProperty("general", "fooA")); + EXPECT_EQ(foo, materialType.FindProperty("general", "fooB")); + EXPECT_EQ(bar, materialType.FindProperty("general", "barA")); + EXPECT_EQ(bar, materialType.FindProperty("general", "barB")); + EXPECT_EQ(baz, materialType.FindProperty("general", "bazA")); + + EXPECT_EQ(nullptr, materialType.FindProperty("general", "fooX")); + EXPECT_EQ(nullptr, materialType.FindProperty("general", "barX")); + EXPECT_EQ(nullptr, materialType.FindProperty("general", "bazX")); + EXPECT_EQ(nullptr, materialType.FindProperty("general", "bazB")); + EXPECT_EQ(nullptr, materialType.FindProperty("otherGroup", "bazA")); + } + + TEST_F(MaterialTypeSourceDataTests, FindPropertyUsingOldName_Error_UnsupportedVersionUpdate) + { + const AZStd::string inputJson = R"( + { + "version": 10, + "versionUpdates": [ + { + "toVersion": 2, + "actions": [ + { "op": "notRename", "from": "general.fooA", "to": "general.fooB" } + ] + } + ], + "propertyLayout": { + "properties": { + "general": [ + { + "name": "fooB", + "type": "Bool" + } + ] + } + } + } + )"; + + MaterialTypeSourceData materialType; + JsonTestResult loadResult = LoadTestDataFromJson(materialType, inputJson); + + ErrorMessageFinder errorMessageFinder; + errorMessageFinder.AddExpectedErrorMessage("Unsupported material version update operation 'notRename'"); + + + const MaterialTypeSourceData::PropertyDefinition* foo = materialType.FindProperty("general", "fooA"); + + EXPECT_EQ(nullptr, foo); + + errorMessageFinder.CheckExpectedErrorsFound(); + } + + TEST_F(MaterialTypeSourceDataTests, CreateMaterialTypeAsset_Error_UnsupportedVersionUpdate) + { + MaterialTypeSourceData sourceData; + + MaterialTypeSourceData::PropertyDefinition propertySource; + propertySource.m_name = "a"; + propertySource.m_dataType = MaterialPropertyDataType::Int; + propertySource.m_value = 0; + sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource); + + sourceData.m_version = 2; + + MaterialTypeSourceData::VersionUpdateDefinition versionUpdate; + versionUpdate.m_toVersion = 2; + MaterialTypeSourceData::VersionUpdatesRenameOperationDefinition updateAction; + updateAction.m_operation = "operationNotKnown"; + versionUpdate.m_actions.push_back(updateAction); + sourceData.m_versionUpdates.push_back(versionUpdate); + + ErrorMessageFinder errorMessageFinder; + errorMessageFinder.AddExpectedErrorMessage("Unsupported material version update operation 'operationNotKnown'"); + errorMessageFinder.AddIgnoredErrorMessage("Failed to build MaterialTypeAsset", true); + + auto materialTypeOutcome = sourceData.CreateMaterialTypeAsset(Uuid::CreateRandom()); + EXPECT_FALSE(materialTypeOutcome.IsSuccess()); + + errorMessageFinder.CheckExpectedErrorsFound(); + } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 2aca8ce1f2..51164a70f4 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -232,6 +232,9 @@ namespace MaterialEditor MaterialSourceData sourceData; sourceData.m_materialType = m_materialSourceData.m_materialType; sourceData.m_parentMaterial = m_materialSourceData.m_parentMaterial; + + AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null."); + sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion(); // Force save data to store forward slashes AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/"); @@ -303,6 +306,9 @@ namespace MaterialEditor MaterialSourceData sourceData; sourceData.m_materialType = m_materialSourceData.m_materialType; sourceData.m_parentMaterial = m_materialSourceData.m_parentMaterial; + + AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null."); + sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion(); // Force save data to store forward slashes AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/"); @@ -372,6 +378,9 @@ namespace MaterialEditor // create source data from properties MaterialSourceData sourceData; sourceData.m_materialType = m_materialSourceData.m_materialType; + + AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null."); + sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion(); // Only assign a parent path if the source was a .material if (AzFramework::StringFunc::Path::IsExtension(m_relativePath.c_str(), MaterialSourceData::Extension)) From 836d018de75a88b2f44d444b7284b9c9640d47a4 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:53:22 -0700 Subject: [PATCH 072/152] Simplified MaterialVersionUpdate code to focus just on renaming, since that's the only operation we currently support. We shouldn't add more complexity until additional operations need to be supported. Also rearranged some logic to simplify the code that loops over rename actions, avoiding making unnecessary additional maps. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Material/MaterialVersionUpdate.h | 18 ++++---- .../Material/MaterialTypeSourceData.cpp | 10 ++--- .../Material/MaterialTypeAssetCreator.cpp | 17 +++----- .../Material/MaterialVersionUpdate.cpp | 41 ++++--------------- .../Tests/Material/MaterialAssetTests.cpp | 8 ++-- .../Tests/Material/MaterialTypeAssetTests.cpp | 16 +++++--- 6 files changed, 42 insertions(+), 68 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h index ee15ebfdcf..fd5a57fa8d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h @@ -27,16 +27,14 @@ namespace AZ static void Reflect(ReflectContext* context); - struct Action + // At this time, the only supported operation is rename. If/when we add more actions in the future, + // we'll need to improve this, possibly with some virtual interface or union data. + struct RenamePropertyAction { - AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate::Action, "{A1FBEB19-EA05-40F0-9700-57D048DF572B}"); + AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate::RenameAction, "{A1FBEB19-EA05-40F0-9700-57D048DF572B}"); - AZ::Name m_operation; - AZStd::unordered_map m_argsMap; - - Action() = default; - Action(const AZ::Name& operation, const AZStd::initializer_list>& args); - void AddArg(const AZ::Name& key, const AZ::Name& argument); + AZ::Name m_fromPropertyId; + AZ::Name m_toPropertyId; }; explicit MaterialVersionUpdate() = default; @@ -47,9 +45,9 @@ namespace AZ void ApplyVersionUpdates(MaterialAsset& materialAsset) const; - using Actions = AZStd::vector; + using Actions = AZStd::vector; const Actions& GetActions() const; - void AddAction(const Action& action); + void AddAction(const RenamePropertyAction& action); private: uint32_t m_toVersion; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index ea2e499972..31b34da092 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -355,8 +355,7 @@ namespace AZ materialTypeAssetCreator.SetVersion(m_version); { const AZ::Name rename = AZ::Name{ "rename" }; - const AZ::Name from = AZ::Name{ "from" }; - const AZ::Name to = AZ::Name{ "to" }; + for (const auto& versionUpdate : m_versionUpdates) { MaterialVersionUpdate materialVersionUpdate; @@ -364,9 +363,10 @@ namespace AZ { if (action.m_operation == rename.GetStringView()) { - materialVersionUpdate.AddAction(MaterialVersionUpdate::Action(rename, { - { from, AZ::Name{ action.m_renameFrom } }, - { to, AZ::Name{ action.m_renameTo } } })); + materialVersionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction{ + AZ::Name{ action.m_renameFrom }, + AZ::Name{ action.m_renameTo } + }); } else { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp index 79336692ec..3839976004 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp @@ -105,27 +105,20 @@ namespace AZ AZStd::vector renamedPropertyNames; const auto& materialVersionUpdate = m_asset->GetMaterialVersionUpdate(m_asset->m_version); for (const auto& action : materialVersionUpdate.GetActions()) - { - const auto it = action.m_argsMap.find(AZ::Name{ "to" }); - if (it != action.m_argsMap.end()) - { - renamedPropertyNames.push_back(it->second); - } - } - - for (const auto& propertyName : renamedPropertyNames) - { - const auto propertyIndex = m_asset->m_materialPropertiesLayout->FindPropertyIndex(AZ::Name{ propertyName }); + { + const auto propertyIndex = m_asset->m_materialPropertiesLayout->FindPropertyIndex(AZ::Name{ action.m_toPropertyId }); if (!propertyIndex.IsValid()) { ReportError(AZStd::string::format( "Renamed property '%s' not found in material property layout. Check that the property name has been " "upgraded to the correct version", - propertyName.GetCStr()) + action.m_toPropertyId.GetCStr()) .c_str()); return false; } + } + return true; } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp index faa571e403..de85d4230e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp @@ -18,10 +18,10 @@ namespace AZ { if (auto* serializeContext = azrtti_cast(context)) { - serializeContext->Class() + serializeContext->Class() ->Version(1) - ->Field("ArgsMap", &Action::m_argsMap) - ->Field("Operation", &Action::m_operation) + ->Field("From", &MaterialVersionUpdate::RenamePropertyAction::m_fromPropertyId) + ->Field("To", &MaterialVersionUpdate::RenamePropertyAction::m_toPropertyId) ; serializeContext->RegisterGenericType(); @@ -53,23 +53,14 @@ namespace AZ void MaterialVersionUpdate::ApplyVersionUpdates(MaterialAsset& materialAsset) const { - // collect all renames within this version update - AZStd::unordered_map renameToFrom; - for (const auto& action : m_actions) - { - const AZ::Name from = action.m_argsMap.find(AZ::Name{ "from" })->second; - const AZ::Name to = action.m_argsMap.find(AZ::Name{ "to" })->second; - - renameToFrom[from] = to; - } - - // apply rename actions for (auto& propertyName : materialAsset.m_propertyNames) { - const auto toFromIterator = renameToFrom.find(propertyName); - if (toFromIterator != renameToFrom.end()) + for (const auto& action : m_actions) { - propertyName = AZ::Name{ toFromIterator->second }; + if (propertyName == action.m_fromPropertyId) + { + propertyName = action.m_toPropertyId; + } } } } @@ -79,23 +70,9 @@ namespace AZ return m_actions; } - void MaterialVersionUpdate::AddAction(const Action& action) + void MaterialVersionUpdate::AddAction(const RenamePropertyAction& action) { m_actions.push_back(action); } - - MaterialVersionUpdate::Action::Action(const AZ::Name& operation, const AZStd::initializer_list>& args) - : m_operation(operation) - { - for (const auto& arg : args) - { - AddArg(arg.first, arg.second); - } - } - - void MaterialVersionUpdate::Action::AddArg(const AZ::Name& key, const AZ::Name& argument) - { - m_argsMap[key] = argument; - } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp index 3191797ded..df45e81ddf 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp @@ -238,9 +238,11 @@ namespace UnitTest // Prepare material type asset version 2 with the update actions MaterialVersionUpdate versionUpdate(2); - versionUpdate.AddAction(MaterialVersionUpdate::Action(AZ::Name{ "rename" }, { - { Name{ "from" }, Name{ "MyBool" } }, - { Name{ "to" }, Name{ "MyBoolNext" } } })); + versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction( + { + Name{ "MyBool" }, + Name{ "MyBoolNext" } + })); Data::Asset testMaterialTypeAssetV2; materialTypeCreator.Begin(Uuid::CreateRandom()); diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp index 8341a8e382..7dd2734ee4 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp @@ -156,9 +156,11 @@ namespace UnitTest // Version updates MaterialVersionUpdate versionUpdate(2); - versionUpdate.AddAction(MaterialVersionUpdate::Action(AZ::Name{ "rename" }, { - { Name{ "from" }, Name{ "EnableSpecialPassPrev" } }, - { Name{ "to" }, Name{ "EnableSpecialPass" } } })); + versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction( + { + Name{ "EnableSpecialPassPrev" }, + Name{ "EnableSpecialPass" } + })); materialTypeCreator.SetVersion(versionUpdate.GetVersion()); materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); @@ -510,9 +512,11 @@ namespace UnitTest // Invalid version updates MaterialVersionUpdate versionUpdate(2); - versionUpdate.AddAction(MaterialVersionUpdate::Action(AZ::Name{ "rename" }, { - { Name{ "from" }, Name{ "EnableSpecialPassPrev" } }, - { Name{ "to" }, Name{ "InvalidPropertyName" } } })); + versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction( + { + Name{ "EnableSpecialPassPrev" }, + Name{ "InvalidPropertyName" } + })); materialTypeCreator.SetVersion(versionUpdate.GetVersion()); materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); materialTypeCreator.AddShader(m_testShaderAsset); From 961c41217983f6bab96552a3731cbc637c2d6150 Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Wed, 20 Oct 2021 16:39:11 -0700 Subject: [PATCH 073/152] Fix adding local repos due to JSON not being able to serialize Windows paths Signed-off-by: AMZN-Phil --- scripts/o3de/o3de/register.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index c3d201f23c..3c4d69a3c0 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -480,9 +480,9 @@ def register_repo(json_data: dict, while repo_uri in json_data['repos']: json_data['repos'].remove(repo_uri) else: - repo_uri = pathlib.Path(repo_uri).resolve() - while repo_uri.as_posix() in json_data['repos']: - json_data['repos'].remove(repo_uri.as_posix()) + repo_uri = pathlib.Path(repo_uri).resolve().as_posix() + while repo_uri in json_data['repos']: + json_data['repos'].remove(repo_uri) if remove: logger.warn(f'Removing repo uri {repo_uri}.') From 74d74050f2cd2f1c54fdfa2a9303c29d300adf34 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Wed, 20 Oct 2021 18:54:27 -0700 Subject: [PATCH 074/152] Fix unused variable error in release linux builds (#4846) Signed-off-by: Steve Pham --- .../Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp index af5b3af3d8..79a6333612 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp @@ -308,7 +308,7 @@ namespace AzFramework event.data.data32[2] = 0; event.data.data32[3] = 1; event.data.data32[4] = 0; - xcb_void_cookie_t xcbCheckResult = xcb_send_event( + [[maybe_unused]] xcb_void_cookie_t xcbCheckResult = xcb_send_event( m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char*)&event); AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set _NET_WM_STATE_FULLSCREEN"); @@ -333,7 +333,7 @@ namespace AzFramework event.data.data32[2] = 0; event.data.data32[3] = 0; event.data.data32[4] = 0; - xcb_void_cookie_t xcbCheckResult = xcb_send_event( + [[maybe_unused]] xcb_void_cookie_t xcbCheckResult = xcb_send_event( m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char*)&event); AZ_Assert( From 1633ced656f6cec6096103d29a46e7f6d4a444d7 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Wed, 20 Oct 2021 23:36:53 -0700 Subject: [PATCH 075/152] Reports warnings when a material version auto update is applied, notifying the user they should update their source data. Also improved the MaterialAssetTests UpgradeMaterialAsset() to focus on testing the inputs and outputs of the class rather than the private internal data. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../RPI.Edit/Material/MaterialSourceData.h | 1 + .../Material/MaterialVersionUpdate.h | 4 +- .../RPI.Edit/Material/MaterialSourceData.cpp | 20 ++++++- .../RPI.Reflect/Material/MaterialAsset.cpp | 27 ++++++++- .../Material/MaterialVersionUpdate.cpp | 7 ++- .../Tests/Material/MaterialAssetTests.cpp | 58 +++++++++++++------ .../Material/MaterialSourceDataTests.cpp | 8 +++ 7 files changed, 103 insertions(+), 22 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index deeace0d41..9557d51849 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -66,6 +66,7 @@ namespace AZ //! Checks the material type version and potentially applies a series of property changes (most common are simple property renames) //! based on the MaterialTypeAsset's version update procedure. + //! @return true if any changes were applied bool ApplyVersionUpdates(); //! Creates a MaterialAsset from the MaterialSourceData content. diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h index fd5a57fa8d..7aede97301 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h @@ -43,7 +43,9 @@ namespace AZ uint32_t GetVersion() const; void SetVersion(uint32_t toVersion); - void ApplyVersionUpdates(MaterialAsset& materialAsset) const; + //! Apply version updates to the given material asset. + //! @return true if any changes were made + bool ApplyVersionUpdates(MaterialAsset& materialAsset) const; using Actions = AZStd::vector; const Actions& GetActions() const; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index e43095f639..c3a63fa883 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -80,9 +80,16 @@ namespace AZ { return false; } - + MaterialTypeSourceData materialTypeSourceData = materialTypeSourceDataOutcome.TakeValue(); + if (m_materialTypeVersion == materialTypeSourceData.m_version) + { + return false; + } + + bool changesWereApplied = false; + // Note that the only kind of property update currently supported is rename... for (auto& groupPair : m_properties) @@ -97,6 +104,7 @@ namespace AZ if (materialTypeSourceData.ApplyPropertyRenames(propertyId, m_materialTypeVersion)) { newPropertyMap[propertyId.GetPropertyName().GetStringView()] = propertyPair.second; + changesWereApplied = true; } else { @@ -107,8 +115,16 @@ namespace AZ propertyMap = newPropertyMap; } - m_materialTypeVersion = materialTypeSourceData.m_version; + if (changesWereApplied) + { + AZ_Warning("MaterialSourceData", false, + "This material is based on version %u of '%s', but the material type is now at version %u. " + "Automatic updates are available. Consider updating the .material source file.", + m_materialTypeVersion, m_materialType.c_str(), materialTypeSourceData.m_version); + } + m_materialTypeVersion = materialTypeSourceData.m_version; + return true; } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index ac740107f2..aa188bead6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -105,11 +105,18 @@ namespace AZ AZStd::array_view MaterialAsset::GetPropertyValues() const { + // If property names are included, they are used to re-arrange the property value list to align with the + // MaterialPropertiesLayout. This realignment would be necessary if the material type is updated with + // a new property layout, and a corresponding material is not reprocessed by the AP and continues using the + // old property layout. if (!m_propertyNames.empty()) { const uint32_t materialTypeVersion = m_materialTypeAsset->GetVersion(); if (m_materialTypeVersion < materialTypeVersion) { + // It is possible that the material type has had some properties renamed. If that's the case, and this material + // is still referencing the old property layout, we need to apply any auto updates to rename those properties + // before using them to realign the property values. const_cast(this)->ApplyVersionUpdates(); } @@ -196,11 +203,29 @@ namespace AZ void MaterialAsset::ApplyVersionUpdates() { + if (m_materialTypeVersion == m_materialTypeAsset->GetVersion()) + { + return; + } + + bool changesWereApplied = false; + for (int i = 0; i < aznumeric_cast(m_materialTypeAsset->GetVersion() - m_materialTypeVersion); ++i) { const auto& versionUpdate = m_materialTypeAsset->GetMaterialVersionUpdate(m_materialTypeVersion + i + 1); - versionUpdate.ApplyVersionUpdates(*this); + if (versionUpdate.ApplyVersionUpdates(*this)) + { + changesWereApplied = true; + } + } + + if (changesWereApplied) + { + AZ_Warning("MaterialAsset", false, + "This material is based on version %u of '%s', but the material type is now at version %u. " + "Automatic updates are available. Consider updating the .material source file.", + m_materialTypeVersion, m_materialTypeAsset.ToString().c_str(), m_materialTypeAsset->GetVersion()); } m_materialTypeVersion = m_materialTypeAsset->GetVersion(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp index de85d4230e..48ad095eba 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp @@ -51,8 +51,10 @@ namespace AZ m_toVersion = toVersion; } - void MaterialVersionUpdate::ApplyVersionUpdates(MaterialAsset& materialAsset) const + bool MaterialVersionUpdate::ApplyVersionUpdates(MaterialAsset& materialAsset) const { + bool changesWereApplied = false; + for (auto& propertyName : materialAsset.m_propertyNames) { for (const auto& action : m_actions) @@ -60,9 +62,12 @@ namespace AZ if (propertyName == action.m_fromPropertyId) { propertyName = action.m_toPropertyId; + changesWereApplied = true; } } } + + return changesWereApplied; } const AZ::RPI::MaterialVersionUpdate::Actions& MaterialVersionUpdate::GetActions() const diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp index df45e81ddf..d376ca06b7 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -64,15 +65,9 @@ namespace UnitTest RPITestFixture::TearDown(); } - void UpgradeAndValidateMaterialAsset(Data::Asset materialAsset, Data::Asset upgradedMaterialTypeAsset) + void ReplaceMaterialType(Data::Asset materialAsset, Data::Asset upgradedMaterialTypeAsset) { - // Set materialTypeAsset to the upgraded version. - EXPECT_EQ(1, materialAsset->m_materialTypeVersion); materialAsset->m_materialTypeAsset = upgradedMaterialTypeAsset; - materialAsset->ApplyVersionUpdates(); - - EXPECT_EQ(2, materialAsset->m_materialTypeVersion); - EXPECT_EQ(AZ::Name{ "MyBoolNext" }, materialAsset->m_propertyNames[0]); } }; @@ -215,6 +210,10 @@ namespace UnitTest TEST_F(MaterialAssetTests, UpgradeMaterialAsset) { + // Here we test the main way that a material asset upgrade would be applied at runtime: A material type is updated to + // both rename a property *and* change the order in which properties appear in the layout. In this case, the new name + // must be identified and then that new name is used to find the appropriate index in the property layout. + auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); @@ -223,16 +222,20 @@ namespace UnitTest MaterialTypeAssetCreator materialTypeCreator; materialTypeCreator.Begin(Uuid::CreateRandom()); materialTypeCreator.AddShader(shaderAsset); - AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyBool" }, MaterialPropertyDataType::Bool, Name{ "m_bool" }); - materialTypeCreator.SetPropertyValue(Name{ "MyBool" }, true); + AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyInt" }, MaterialPropertyDataType::Int, Name{ "m_int" }); + AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyUInt" }, MaterialPropertyDataType::UInt, Name{ "m_uint" }); + AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyFloat" }, MaterialPropertyDataType::Float, Name{ "m_float" }); EXPECT_TRUE(materialTypeCreator.End(testMaterialTypeAssetV1)); // Construct the material asset with materialTypeAsset version 1 Data::AssetId assetId(Uuid::CreateRandom()); MaterialAssetCreator creator; - creator.Begin(assetId, *testMaterialTypeAssetV1); - creator.SetPropertyValue(Name{ "MyBool" }, true); + const bool includePropertyNames = true; + creator.Begin(assetId, *testMaterialTypeAssetV1, includePropertyNames); + creator.SetPropertyValue(Name{ "MyInt" }, 7); + creator.SetPropertyValue(Name{ "MyUInt" }, 8u); + creator.SetPropertyValue(Name{ "MyFloat" }, 9.0f); Data::Asset materialAsset; EXPECT_TRUE(creator.End(materialAsset)); @@ -240,8 +243,8 @@ namespace UnitTest MaterialVersionUpdate versionUpdate(2); versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction( { - Name{ "MyBool" }, - Name{ "MyBoolNext" } + Name{ "MyInt" }, + Name{ "MyIntRenamed" } })); Data::Asset testMaterialTypeAssetV2; @@ -249,12 +252,33 @@ namespace UnitTest materialTypeCreator.SetVersion(versionUpdate.GetVersion()); materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); materialTypeCreator.AddShader(shaderAsset); - AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyBoolNext" }, MaterialPropertyDataType::Bool, Name{ "m_bool" }); - materialTypeCreator.SetPropertyValue(Name{ "MyBoolNext" }, true); + // Now we add the properties in a different order from before, and use the new name for MyInt. + AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyUInt" }, MaterialPropertyDataType::UInt, Name{ "m_uint" }); + AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyFloat" }, MaterialPropertyDataType::Float, Name{ "m_float" }); + AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyIntRenamed" }, MaterialPropertyDataType::Int, Name{ "m_int" }); EXPECT_TRUE(materialTypeCreator.End(testMaterialTypeAssetV2)); - // Upgrade the material asset with the materialTypeAsset v2 and verify - UpgradeAndValidateMaterialAsset(materialAsset, testMaterialTypeAssetV2); + // This is our way of faking the idea that an old version of the MaterialAsset could be loaded with a new version of the MaterialTypeAsset. + ReplaceMaterialType(materialAsset, testMaterialTypeAssetV2); + + // This can find errors and warnings, we are looking for a warning when the version update is applied + ErrorMessageFinder warningFinder; + warningFinder.AddExpectedErrorMessage("Automatic updates are available. Consider updating the .material source file"); + + // Even though this material was created using the old version of the material type, it's property values should get automatically + // updated to align with the new property layout in the latest MaterialTypeAsset. + MaterialPropertyIndex myIntIndex = materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(Name{"MyIntRenamed"}); + EXPECT_EQ(2, myIntIndex.GetIndex()); + EXPECT_EQ(7, materialAsset->GetPropertyValues()[myIntIndex.GetIndex()].GetValue()); + + warningFinder.CheckExpectedErrorsFound(); + + // Since the MaterialAsset has already been updated, and the warning reported once, we should not see the "consider updating" + // warning reported again on subsequent property accesses. + warningFinder.Reset(); + myIntIndex = materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(Name{"MyIntRenamed"}); + EXPECT_EQ(2, myIntIndex.GetIndex()); + EXPECT_EQ(7, materialAsset->GetPropertyValues()[myIntIndex.GetIndex()].GetValue()); } TEST_F(MaterialAssetTests, Error_NoBegin) diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp index a6ce6504fb..6a6947e7c7 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -725,7 +726,10 @@ namespace UnitTest EXPECT_EQ(1, material.m_materialTypeVersion); // Then we force the material data to update to the latest material type version specification + ErrorMessageFinder warningFinder; // Note this finds errors and warnings, and we're looking for a warning. + warningFinder.AddExpectedErrorMessage("Automatic updates are available. Consider updating the .material source file"); material.ApplyVersionUpdates(); + warningFinder.CheckExpectedErrorsFound(); // Now the material data should match the latest material type. // Look for the property under the latest name in the material type, not the name used in the .material file. @@ -739,6 +743,10 @@ namespace UnitTest EXPECT_TRUE(AZ::Color(0.1f, 0.2f, 0.3f, 1.0f).IsClose(testColor, 0.01)); EXPECT_EQ(10, material.m_materialTypeVersion); + + // Calling ApplyVersionUpdates() again should not report the warning again, since the material has already been updated. + warningFinder.Reset(); + material.ApplyVersionUpdates(); } TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionPartialUpdate) From 46061eb30212ffbf2793a97f73ac96690f32ecaf Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 21 Oct 2021 00:07:36 -0700 Subject: [PATCH 076/152] Simplified the code around MaterialAsset::ApplyVersionUpdates() Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../RPI.Reflect/Material/MaterialTypeAsset.h | 8 +- .../Material/MaterialTypeAssetCreator.h | 2 +- .../Material/MaterialVersionUpdate.h | 1 - .../Material/MaterialTypeSourceData.cpp | 4 +- .../RPI.Reflect/Material/MaterialAsset.cpp | 12 +-- .../Material/MaterialTypeAsset.cpp | 8 +- .../Material/MaterialTypeAssetCreator.cpp | 38 ++++++--- .../Material/MaterialVersionUpdate.cpp | 2 - .../Tests/Material/MaterialAssetTests.cpp | 2 +- .../Tests/Material/MaterialTypeAssetTests.cpp | 78 ++++++++++++++++++- 10 files changed, 119 insertions(+), 36 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h index d0f0609c02..9bc0e018d3 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h @@ -126,10 +126,8 @@ namespace AZ //! Returns the version of the MaterialTypeAsset. uint32_t GetVersion() const; - //! Returns the toVersion update containing the actions to perform. If a toVersion - //! is not defined in m_materialVersionUpdatesMap, a material toVersion with the specified - //! version but empty actions will be returned. - MaterialVersionUpdate GetMaterialVersionUpdate(uint32_t toVersion) const; + + const AZStd::vector& GetMaterialVersionUpdateList() const { return m_materialVersionUpdates; } private: bool PostLoadInit() override; @@ -175,7 +173,7 @@ namespace AZ uint32_t m_version = 1; //! Contains actions to perform for each material update version. - MaterialVersionUpdateMap m_materialVersionUpdateMap; + AZStd::vector m_materialVersionUpdates; }; class MaterialTypeAssetHandler : public AssetHandler diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h index 64327ecd03..5e5f94da6d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h @@ -41,7 +41,7 @@ namespace AZ //! Sets the version of the MaterialTypeAsset void SetVersion(uint32_t version); //! Adds a version update object into the MaterialTypeAsset - void AddVersionUpdate(uint32_t toVersion, const MaterialVersionUpdate& materialVersionUpdate); + void AddVersionUpdate(const MaterialVersionUpdate& materialVersionUpdate); //! Indicates that this MaterialType will own the specified shader option. //! Material-owned shader options can be connected to material properties (either directly or through functors). diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h index 7aede97301..0acd1d4432 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h @@ -56,6 +56,5 @@ namespace AZ Actions m_actions; }; - using MaterialVersionUpdateMap = AZStd::map; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 31b34da092..8c8bad8c0c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -358,7 +358,7 @@ namespace AZ for (const auto& versionUpdate : m_versionUpdates) { - MaterialVersionUpdate materialVersionUpdate; + MaterialVersionUpdate materialVersionUpdate{versionUpdate.m_toVersion}; for (const auto& action : versionUpdate.m_actions) { if (action.m_operation == rename.GetStringView()) @@ -373,7 +373,7 @@ namespace AZ materialTypeAssetCreator.ReportWarning("Unsupported material version update operation '%s'", action.m_operation.c_str()); } } - materialTypeAssetCreator.AddVersionUpdate(versionUpdate.m_toVersion, materialVersionUpdate); + materialTypeAssetCreator.AddVersionUpdate(materialVersionUpdate); } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index aa188bead6..2b7c1c58c7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -210,13 +210,15 @@ namespace AZ bool changesWereApplied = false; - for (int i = 0; i < aznumeric_cast(m_materialTypeAsset->GetVersion() - m_materialTypeVersion); ++i) + for (const MaterialVersionUpdate& versionUpdate : m_materialTypeAsset->GetMaterialVersionUpdateList()) { - const auto& versionUpdate = m_materialTypeAsset->GetMaterialVersionUpdate(m_materialTypeVersion + i + 1); - - if (versionUpdate.ApplyVersionUpdates(*this)) + if (m_materialTypeVersion < versionUpdate.GetVersion()) { - changesWereApplied = true; + if (versionUpdate.ApplyVersionUpdates(*this)) + { + changesWereApplied = true; + m_materialTypeVersion = versionUpdate.GetVersion(); + } } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp index a876028599..522ba74119 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp @@ -47,7 +47,7 @@ namespace AZ serializeContext->Class() ->Version(5) // Material version update ->Field("Version", &MaterialTypeAsset::m_version) - ->Field("VersionUpdates", &MaterialTypeAsset::m_materialVersionUpdateMap) + ->Field("VersionUpdates", &MaterialTypeAsset::m_materialVersionUpdates) ->Field("ShaderCollection", &MaterialTypeAsset::m_shaderCollection) ->Field("MaterialFunctors", &MaterialTypeAsset::m_materialFunctors) ->Field("MaterialSrgShaderIndex", &MaterialTypeAsset::m_materialSrgShaderIndex) @@ -169,12 +169,6 @@ namespace AZ return m_version; } - MaterialVersionUpdate MaterialTypeAsset::GetMaterialVersionUpdate(uint32_t toVersion) const - { - const auto it = m_materialVersionUpdateMap.find(toVersion); - return it != m_materialVersionUpdateMap.end() ? it->second : MaterialVersionUpdate(toVersion); - } - void MaterialTypeAsset::SetReady() { m_status = AssetStatus::Ready; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp index 3839976004..46086dfecc 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp @@ -102,18 +102,38 @@ namespace AZ bool MaterialTypeAssetCreator::ValidateMaterialVersion() { - AZStd::vector renamedPropertyNames; - const auto& materialVersionUpdate = m_asset->GetMaterialVersionUpdate(m_asset->m_version); - for (const auto& action : materialVersionUpdate.GetActions()) + if (m_asset->m_materialVersionUpdates.empty()) + { + return true; + } + + uint32_t prevVersion = 0; + for(const MaterialVersionUpdate& versionUpdate : m_asset->m_materialVersionUpdates) + { + if (versionUpdate.GetVersion() <= prevVersion) + { + ReportError("Version updates are not sequential. See version update '%u'.", versionUpdate.GetVersion()); + return false; + } + + if (versionUpdate.GetVersion() > m_asset->m_version) + { + ReportError("Version updates go beyond the current material type version. See version update '%u'.", versionUpdate.GetVersion()); + return false; + } + + prevVersion = versionUpdate.GetVersion(); + } + + const auto& lastMaterialVersionUpdate = m_asset->m_materialVersionUpdates.back(); + for (const auto& action : lastMaterialVersionUpdate.GetActions()) { const auto propertyIndex = m_asset->m_materialPropertiesLayout->FindPropertyIndex(AZ::Name{ action.m_toPropertyId }); if (!propertyIndex.IsValid()) { - ReportError(AZStd::string::format( - "Renamed property '%s' not found in material property layout. Check that the property name has been " + ReportError("Renamed property '%s' not found in material property layout. Check that the property name has been " "upgraded to the correct version", - action.m_toPropertyId.GetCStr()) - .c_str()); + action.m_toPropertyId.GetCStr()); return false; } @@ -150,9 +170,9 @@ namespace AZ m_asset->m_version = version; } - void MaterialTypeAssetCreator::AddVersionUpdate(uint32_t toVersion, const MaterialVersionUpdate& materialVersionUpdate) + void MaterialTypeAssetCreator::AddVersionUpdate(const MaterialVersionUpdate& materialVersionUpdate) { - m_asset->m_materialVersionUpdateMap[toVersion] = materialVersionUpdate; + m_asset->m_materialVersionUpdates.push_back(materialVersionUpdate); } void MaterialTypeAssetCreator::ClaimShaderOptionOwnership(const Name& shaderOptionName) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp index 48ad095eba..bf503f3c67 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp @@ -31,8 +31,6 @@ namespace AZ ->Field("ToVersion", &MaterialVersionUpdate::m_toVersion) ->Field("Actions", &MaterialVersionUpdate::m_actions) ; - - serializeContext->RegisterGenericType(); } } diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp index d376ca06b7..cf8b55d581 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp @@ -250,7 +250,7 @@ namespace UnitTest Data::Asset testMaterialTypeAssetV2; materialTypeCreator.Begin(Uuid::CreateRandom()); materialTypeCreator.SetVersion(versionUpdate.GetVersion()); - materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); + materialTypeCreator.AddVersionUpdate(versionUpdate); materialTypeCreator.AddShader(shaderAsset); // Now we add the properties in a different order from before, and use the new name for MyInt. AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyUInt" }, MaterialPropertyDataType::UInt, Name{ "m_uint" }); diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp index 7dd2734ee4..81b723ec04 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp @@ -162,7 +162,7 @@ namespace UnitTest Name{ "EnableSpecialPass" } })); materialTypeCreator.SetVersion(versionUpdate.GetVersion()); - materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); + materialTypeCreator.AddVersionUpdate(versionUpdate); // Built-in shader @@ -501,7 +501,7 @@ namespace UnitTest }); } - TEST_F(MaterialTypeAssetTests, Error_InvalidMaterialVersionUpdate) + TEST_F(MaterialTypeAssetTests, Error_InvalidMaterialVersionUpdate_WrongName) { Data::Asset materialTypeAsset; @@ -518,7 +518,7 @@ namespace UnitTest Name{ "InvalidPropertyName" } })); materialTypeCreator.SetVersion(versionUpdate.GetVersion()); - materialTypeCreator.AddVersionUpdate(versionUpdate.GetVersion(), versionUpdate); + materialTypeCreator.AddVersionUpdate(versionUpdate); materialTypeCreator.AddShader(m_testShaderAsset); materialTypeCreator.BeginMaterialProperty(Name{ "EnableSpecialPass" }, MaterialPropertyDataType::Bool); @@ -529,6 +529,78 @@ namespace UnitTest AZ_TEST_STOP_ASSERTTEST(1); EXPECT_EQ(1, materialTypeCreator.GetErrorCount()); } + + TEST_F(MaterialTypeAssetTests, Error_InvalidMaterialVersionUpdate_WrongOrder) + { + MaterialTypeAssetCreator materialTypeCreator; + materialTypeCreator.Begin(Uuid::CreateRandom()); + + materialTypeCreator.SetVersion(4); + materialTypeCreator.AddShader(m_testShaderAsset); + materialTypeCreator.BeginMaterialProperty(Name{ "d" }, MaterialPropertyDataType::Bool); + materialTypeCreator.EndMaterialProperty(); + + ErrorMessageFinder errorMessageFinder; + errorMessageFinder.AddExpectedErrorMessage("Version updates are not sequential. See version update '3'"); + + { + MaterialVersionUpdate versionUpdate(2); + versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "a" },Name{ "b" }})); + materialTypeCreator.AddVersionUpdate(versionUpdate); + } + + { + MaterialVersionUpdate versionUpdate(4); + versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "b" },Name{ "c" }})); + materialTypeCreator.AddVersionUpdate(versionUpdate); + } + + { + MaterialVersionUpdate versionUpdate(3); + versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "c" },Name{ "d" }})); + materialTypeCreator.AddVersionUpdate(versionUpdate); + } + + Data::Asset materialTypeAsset; + EXPECT_FALSE(materialTypeCreator.End(materialTypeAsset)); + + errorMessageFinder.CheckExpectedErrorsFound(); + + EXPECT_EQ(1, materialTypeCreator.GetErrorCount()); + } + + TEST_F(MaterialTypeAssetTests, Error_InvalidMaterialVersionUpdate_GoesTooFar) + { + MaterialTypeAssetCreator materialTypeCreator; + materialTypeCreator.Begin(Uuid::CreateRandom()); + + materialTypeCreator.SetVersion(3); + materialTypeCreator.AddShader(m_testShaderAsset); + materialTypeCreator.BeginMaterialProperty(Name{ "d" }, MaterialPropertyDataType::Bool); + materialTypeCreator.EndMaterialProperty(); + + ErrorMessageFinder errorMessageFinder; + errorMessageFinder.AddExpectedErrorMessage("Version updates go beyond the current material type version. See version update '4'"); + + { + MaterialVersionUpdate versionUpdate(2); + versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "a" },Name{ "b" }})); + materialTypeCreator.AddVersionUpdate(versionUpdate); + } + + { + MaterialVersionUpdate versionUpdate(4); + versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "b" },Name{ "c" }})); + materialTypeCreator.AddVersionUpdate(versionUpdate); + } + + Data::Asset materialTypeAsset; + EXPECT_FALSE(materialTypeCreator.End(materialTypeAsset)); + + errorMessageFinder.CheckExpectedErrorsFound(); + + EXPECT_EQ(1, materialTypeCreator.GetErrorCount()); + } TEST_F(MaterialTypeAssetTests, MaterialTypeWithNoSRGOrProperties) { From d04d9883bc3c3f5b63ea1bb99725ee715969d002 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Thu, 21 Oct 2021 00:42:48 -0700 Subject: [PATCH 077/152] New Depth Of Field working quite well with a small amount of artefacts Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- .../Common/Assets/Passes/NewDepthOfField.pass | 16 ++- .../NewDepthOfFieldCommon.azsli | 2 + .../NewDepthOfFieldComposite.azsl | 23 ++-- .../NewDepthOfFieldDownsample.azsl | 2 +- .../NewDepthOfFieldFilterLarge.azsl | 101 +++++++++++++++-- ...epthOfFieldFilterLarge.azsl~RF1bf9782e.TMP | 105 ++++++++++++++++++ .../NewDepthOfFieldFilterSmall.azsl | 36 ++++-- 7 files changed, 253 insertions(+), 32 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl~RF1bf9782e.TMP diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass index e7c6a23c6c..483ce0e49a 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass @@ -67,6 +67,20 @@ } ] }, + { + "Name": "Tile5x5", + "TemplateName": "NewDepthOfFieldTile3x3Template", + + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "Tile3x3", + "Attachment": "Output" + } + } + ] + }, { "Name": "LargeFilter", "TemplateName": "NewDepthOfFieldFilterLargeTemplate", @@ -82,7 +96,7 @@ { "LocalSlot": "CocTile", "AttachmentRef": { - "Pass": "Tile3x3", + "Pass": "Tile5x5", "Attachment": "Output" } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli index fc98cee055..a789537819 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli @@ -15,6 +15,8 @@ #define SAMPLES_LOOP_2 16 #define SAMPLES_LOOP_3 24 +#define SAMPLES_LOOP_TOTAL 48 + // Must match the struct in NewDepthOfFieldPasses.cpp struct NewDepthOfFieldConstants { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl index 8a766c71d9..14bc0d3b18 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl @@ -54,22 +54,25 @@ PSOutput MainPS(VSOutput IN) // Calculate Alpha float cocRadius = abs(coc) * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; float maxPixelDist = max(PassSrg::m_halfResDimensions.z, PassSrg::m_halfResDimensions.w); - float alpha = saturate(0.25f * cocRadius / maxPixelDist); + float alpha = saturate(cocRadius / maxPixelDist); // Sample half res color and CoC float4 colorAndCoC = PassSrg::m_halfResColorAndCoc.Sample(PassSrg::LinearSampler, halfResUV); - // Make out of focus foreground increasingly blue - float multiplier = saturate(1.0f + coc); - colorAndCoC.r *= multiplier; - colorAndCoC.g *= multiplier; + if(false) + { + // Make out of focus foreground increasingly blue + float multiplier = saturate(1.0f + coc); + colorAndCoC.r *= multiplier; + colorAndCoC.g *= multiplier; + + // Make out of focus background increasingly red + multiplier = saturate(1.0f - coc); + colorAndCoC.b *= multiplier; + colorAndCoC.g *= multiplier; + } - // Make out of focus background increasingly red - multiplier = saturate(1.0f - coc); - colorAndCoC.b *= multiplier; - colorAndCoC.g *= multiplier; - // Output PSOutput OUT; OUT.m_color.rgb = colorAndCoC.rgb; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl index c8070178f0..d9972b8400 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl @@ -93,8 +93,8 @@ PSOutput MainPS(VSOutput IN) // See http://advances.realtimerendering.com/s2013/Sousa_Graphics_Gems_CryENGINE3.pptx coc = abs(coc) > COC_EPSILON ? coc : -COC_EPSILON; OUT.m_color.rgb *= abs(coc); - OUT.m_color.a = coc; + return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl index e566db7880..497d73444b 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl @@ -31,20 +31,103 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + Sampler PointSampler + { + MinFilter = Point; + MagFilter = Point; + MipFilter = Point; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +} + +float3 GetOffsetUV(uint index, float2 offsetMultiplier) +{ + float3 offset = PassSrg::m_dofConstants.samplePositions[index].xyz; + offset.xy *= offsetMultiplier; + return offset; +} + +float CaclulateWeight(float offsetRadius, float samplingRadius, float sampleCoc, float centerCoc) +{ + // The maximum distance for which samples are valid is the min of the sample CoC and the center CoC + float maxRadius = abs(min(sampleCoc, centerCoc)); + + // radius = samplingRadius * offsetRadius; + // falloff = maxRadius - radius; + // weight = 1 + (4 * falloff) + float falloff = mad(-samplingRadius, offsetRadius, maxRadius); + float weight = saturate(mad(4, falloff, 1)); + return weight; } PSOutput MainPS(VSOutput IN) { + // Get center sample + float2 pixelUV = IN.m_texCoord; + float4 color = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV).rgba; + float centerCoc = color.a; + + // Get tile min and max + int2 tile = int2(IN.m_position.xy) / 16; + float minCoc = PassSrg::m_minMaxCocTile[tile].x; + + // Aspect ratio = texture.x / texture.y = dimensions.x * dimensions.w + float aspectRatio = PassSrg::m_textureDimensions.x * PassSrg::m_textureDimensions.w; + + // Sampling radius + float cocRadius = max( abs(centerCoc), -minCoc); + float screenRadius = cocRadius * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; + float2 offsetMultiplier = float2(screenRadius / aspectRatio, screenRadius); + + + float4 backgroundColor = float4(0, 0, 0, 0); + + { + float backgroundMin = min(0, centerCoc); + color.rgb /= abs(color.a); + color.a = 1; + + for(uint i = 0; i < SAMPLES_LOOP_TOTAL; ++i) + { + // Calculate sample offset + float3 offset = GetOffsetUV(i, offsetMultiplier); + + // Get sample + float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV + offset.xy).rgba; + sampleColorCoc.rgb /= abs(sampleColorCoc.a); + + // Calculate weight for sample + float weight = CaclulateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); + sampleColorCoc.rgb *= weight; + + bool isBackground = (sampleColorCoc.a < backgroundMin); + + // We accumulate weight in alpha channel of color and backgroundColor + sampleColorCoc.a = weight; + + // Accumulate + backgroundColor += isBackground * sampleColorCoc; + color += !isBackground * sampleColorCoc; + } + } + + float backgroundRatio = backgroundColor.a / float(SAMPLES_LOOP_TOTAL); + float alpha = backgroundRatio > abs(centerCoc) ? -backgroundRatio : centerCoc; + + color.rgb /= max(color.a, COC_EPSILON); + backgroundColor.rgb /= max(backgroundColor.a, COC_EPSILON); + + color = color * saturate(1 - backgroundRatio) + backgroundColor * backgroundRatio; + //color.rgb += backgroundColor.rgb; + + + PSOutput OUT = (PSOutput)0; - - float4 centerSample = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, IN.m_texCoord); - float3 centerColor = centerSample.rgb; - float centerCoc = centerSample.a; - - centerColor /= centerCoc; - - OUT.m_color.rgb = centerColor; - OUT.m_color.a = centerCoc; + OUT.m_color.rgb = color.rgb; + OUT.m_color.a = alpha; return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl~RF1bf9782e.TMP b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl~RF1bf9782e.TMP new file mode 100644 index 0000000000..7c0f32381b --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl~RF1bf9782e.TMP @@ -0,0 +1,105 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include "DepthOfField.azsli" +#include "NewDepthOfFieldCommon.azsli" + +#include + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_colorAndCoc; + Texture2D m_minMaxCocTile; + + float4 m_textureDimensions; + + NewDepthOfFieldConstants m_dofConstants; + + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +} + +float2 GetOffsetUV(uint index, float2 offsetMultiplier) +{ + return PassSrg::m_dofConstants.samplePositions[index].xy * offsetMultiplier; +} + +PSOutput MainPS(VSOutput IN) +{ + // Get center sample + float2 pixelUV = IN.m_texCoord; + float4 color = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV).rgba; + float centerCoc = color.a; + + // Get tile min and max + int2 tile = int2(IN.m_position.xy) / 16; + float minCoc = PassSrg::m_minMaxCocTile[tile].x; + + // Aspect ratio = texture.x / texture.y = dimensions.x * dimensions.w + float aspectRatio = PassSrg::m_textureDimensions.x * PassSrg::m_textureDimensions.w; + + // Sampling radius + float cocRadius = max( abs(centerCoc), -minCoc); + cocRadius *= ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; + float2 offsetMultiplier = float2(cocRadius / aspectRatio, cocRadius); + + + + float4 negColor = float4(0, 0, 0, 0); + + { + float negMin = min(0, centerCoc); + color.rgb /= abs(color.a); + color.a = 1; + + for(uint i = 0; i < SAMPLES_LOOP_TOTAL; ++i) + { + // Calculate sample offset + float2 offsetUV = GetOffsetUV(i, offsetMultiplier); + + // Get sample + float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offsetUV).rgba; + + // Calculate weight and adjust sample + float cocDiff = sampleColorCoc.a - centerCoc; + float weight = saturate( 2 - (20 * cocDiff)); + sampleColorCoc.rgb *= (weight / abs(sampleColorCoc.a)); + // + // bool isNeg = (sampleColorCoc.a < negMin); + // sampleColorCoc.a = weight; + // + // // Accumulate + // negColor += isNeg * sampleColorCoc; + // color += !isNeg * sampleColorCoc; + } + } + + float negRatio = negColor.a / float(SAMPLES_LOOP_TOTAL); + float alpha = negRatio > abs(centerCoc) ? -negRatio : centerCoc; + + color = color * saturate(1 - negRatio) + negColor; + color.rgb /= max(color.a, COC_EPSILON); + color.rgb += negColor.rgb; + + //float alpha = 1.0f; + + PSOutput OUT = (PSOutput)0; + OUT.m_color.rgb = color.rgb; + OUT.m_color.a = alpha; + return OUT; +} + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl index dadaf57506..440701ded9 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl @@ -33,9 +33,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass }; } -float2 GetOffsetUV(uint index, float radiusMultiplier) +float2 GetOffsetUV(uint index, float2 offsetMultiplier) { - return PassSrg::m_dofConstants.samplePositions[index] * radiusMultiplier * PassSrg::m_textureDimensions.zw; + return PassSrg::m_dofConstants.samplePositions[index].xy * offsetMultiplier; } PSOutput MainPS(VSOutput IN) @@ -47,27 +47,41 @@ PSOutput MainPS(VSOutput IN) float4 centerSample = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV).rgba; float centerCoc = centerSample.a; - // Color and weight accumulation - float3 color = centerSample.rgb; - float totalWeight = 1; + // Aspect ratio = texture.x / texture.y = dimensions.x * dimensions.w + float aspectRatio = PassSrg::m_textureDimensions.x * PassSrg::m_textureDimensions.w; // Sampling radius float cocRadius = abs(centerCoc) * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; cocRadius *= 0.5f; // This is the small filter, half the sampling radius + float2 offsetMultiplier = float2(cocRadius / aspectRatio, cocRadius); - for(uint i = 0; i < SAMPLES_LOOP_1; ++i) + // Color and weight accumulation + float3 color = centerSample.rgb; + float totalWeight = 1; + + for(uint i = 0; i < 8; ++i) { - float2 offsetUV = GetOffsetUV(i, cocRadius); - float4 sample = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offsetUV).rgba; - float cocDiff = abs(sample.a - centerCoC); + // Calculate sample offset + float2 offsetUV = GetOffsetUV(i, offsetMultiplier); + + // Get sample + float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offsetUV).rgba; + + // Calculate weight + float cocDiff = sampleColorCoc.a - centerCoc; float weight = saturate( 2 - (20 * cocDiff)); - color += sample.rgb * weight; + + // Accumulate sample and weight + color += sampleColorCoc.rgb * weight; totalWeight += weight; } + // Normalize accumulated sample + color /= totalWeight; + // Output PSOutput OUT; - OUT.m_color.rgb = color / totalWeight; + OUT.m_color.rgb = color; OUT.m_color.a = centerCoc; return OUT; } From 60148ccc3975bf8bcd1f18fe3298d04a530ef135 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Thu, 21 Oct 2021 01:23:43 -0700 Subject: [PATCH 078/152] New Depth of Field - Minor improvements, optimizations, and now works with auto focus Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- .../Common/Assets/Passes/NewDepthOfField.pass | 13 +++++++++ .../NewDepthOfFieldFilterLarge.azsl | 28 +++++++++++++++---- .../NewDepthOfFieldFilterSmall.azsl | 15 ++++++++-- .../PostProcessing/NewDepthOfFieldPasses.cpp | 8 ++++++ 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass index 483ce0e49a..70cebab5a1 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/NewDepthOfField.pass @@ -18,6 +18,19 @@ } ], "PassRequests": [ + { + "Name": "AutoFocus", + "TemplateName": "DepthOfFieldReadBackFocusDepthTemplate", + "Connections": [ + { + "LocalSlot": "DepthInput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Depth" + } + } + ] + }, { "Name": "Downsample", "TemplateName": "NewDepthOfFieldDownsampleTemplate", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl index 497d73444b..6ab47b14ea 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl @@ -85,6 +85,24 @@ PSOutput MainPS(VSOutput IN) float4 backgroundColor = float4(0, 0, 0, 0); + if(minCoc >= 0) + { + for(uint i = 0; i < SAMPLES_LOOP_TOTAL; ++i) + { + // Calculate sample offset + float3 offset = GetOffsetUV(i, offsetMultiplier); + + // Get sample + float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offset.xy).rgba; + + // Calculate weight for sample + float weight = CaclulateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); + + // Accumulate + color += weight * sampleColorCoc; + } + } + else { float backgroundMin = min(0, centerCoc); color.rgb /= abs(color.a); @@ -112,17 +130,15 @@ PSOutput MainPS(VSOutput IN) backgroundColor += isBackground * sampleColorCoc; color += !isBackground * sampleColorCoc; } + + backgroundColor.rgb /= max(backgroundColor.a, COC_EPSILON); } - float backgroundRatio = backgroundColor.a / float(SAMPLES_LOOP_TOTAL); + float backgroundRatio = saturate( backgroundColor.a / float(SAMPLES_LOOP_TOTAL) ); float alpha = backgroundRatio > abs(centerCoc) ? -backgroundRatio : centerCoc; color.rgb /= max(color.a, COC_EPSILON); - backgroundColor.rgb /= max(backgroundColor.a, COC_EPSILON); - - color = color * saturate(1 - backgroundRatio) + backgroundColor * backgroundRatio; - //color.rgb += backgroundColor.rgb; - + color = lerp(color, backgroundColor, backgroundRatio); PSOutput OUT = (PSOutput)0; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl index 440701ded9..05227cfee7 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl @@ -31,6 +31,16 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + Sampler PointSampler + { + MinFilter = Point; + MagFilter = Point; + MipFilter = Point; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; } float2 GetOffsetUV(uint index, float2 offsetMultiplier) @@ -44,7 +54,7 @@ PSOutput MainPS(VSOutput IN) float2 pixelUV = IN.m_texCoord.xy; // Sample pixel being shaded - float4 centerSample = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV).rgba; + float4 centerSample = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV).rgba; float centerCoc = centerSample.a; // Aspect ratio = texture.x / texture.y = dimensions.x * dimensions.w @@ -65,7 +75,7 @@ PSOutput MainPS(VSOutput IN) float2 offsetUV = GetOffsetUV(i, offsetMultiplier); // Get sample - float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offsetUV).rgba; + float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV + offsetUV).rgba; // Calculate weight float cocDiff = sampleColorCoc.a - centerCoc; @@ -85,4 +95,3 @@ PSOutput MainPS(VSOutput IN) OUT.m_color.a = centerCoc; return OUT; } - diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp index 7b9e46f6b1..b69484297a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp @@ -149,6 +149,14 @@ namespace AZ float radius = (loop + 1.0f) / float(NewDepthOfFieldConstants::numberOfLoops); float loopCount = NewDepthOfFieldConstants::loopCounts[loop]; + float angleOffset = 0; + + // Every other loop slightly rotate sample ring so they don't line up + if (loop & 1) + { + angleOffset = Constants::TwoPi * 0.5f / loopCount; + } + for (float i = 0.0f; i < loopCount; ++i) { float angle = Constants::TwoPi * i / loopCount; From dfcf88265b0736e78e66817e481825a6f1bae153 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 21 Oct 2021 01:28:08 -0700 Subject: [PATCH 079/152] Fixed a MaterialBuilder issue where material version updates were incorrectly reporting failure in some cases. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Atom/RPI.Edit/Material/MaterialSourceData.h | 10 ++++++++-- .../Source/RPI.Builders/Material/MaterialBuilder.cpp | 2 +- .../Source/RPI.Edit/Material/MaterialSourceData.cpp | 11 ++++++----- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index 9557d51849..a8bf790ad8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -64,10 +64,16 @@ namespace AZ PropertyGroupMap m_properties; + enum class ApplyVersionUpdatesResult + { + Failed, + NoUpdates, + UpdatesApplied + }; + //! Checks the material type version and potentially applies a series of property changes (most common are simple property renames) //! based on the MaterialTypeAsset's version update procedure. - //! @return true if any changes were applied - bool ApplyVersionUpdates(); + ApplyVersionUpdatesResult ApplyVersionUpdates(AZStd::string_view materialSourceFilePath); //! Creates a MaterialAsset from the MaterialSourceData content. //! @param assetId ID for the MaterialAsset diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index 0680c5c428..02fd11bdff 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -287,7 +287,7 @@ namespace AZ return {}; } - if (!material.GetValue().ApplyVersionUpdates()) + if (MaterialSourceData::ApplyVersionUpdatesResult::Failed == material.GetValue().ApplyVersionUpdates(materialSourceFilePath)) { return {}; } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index c3a63fa883..9d44963942 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -73,19 +73,20 @@ namespace AZ } } - bool MaterialSourceData::ApplyVersionUpdates() + MaterialSourceData::ApplyVersionUpdatesResult MaterialSourceData::ApplyVersionUpdates(AZStd::string_view materialSourceFilePath) { - auto materialTypeSourceDataOutcome = MaterialUtils::LoadMaterialTypeSourceData(m_materialType); + AZStd::string materialTypeFullPath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType); + auto materialTypeSourceDataOutcome = MaterialUtils::LoadMaterialTypeSourceData(materialTypeFullPath); if (!materialTypeSourceDataOutcome.IsSuccess()) { - return false; + return ApplyVersionUpdatesResult::Failed; } MaterialTypeSourceData materialTypeSourceData = materialTypeSourceDataOutcome.TakeValue(); if (m_materialTypeVersion == materialTypeSourceData.m_version) { - return false; + return ApplyVersionUpdatesResult::NoUpdates; } bool changesWereApplied = false; @@ -125,7 +126,7 @@ namespace AZ m_materialTypeVersion = materialTypeSourceData.m_version; - return true; + return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates; } Outcome > MaterialSourceData::CreateMaterialAsset(Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const From 6642850c02d17dc8accfb6636200e2c596d698a9 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Thu, 21 Oct 2021 04:44:26 -0700 Subject: [PATCH 080/152] Cleaned up new Depth of Field, ready for PR Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- .../NewDepthOfFieldCommon.azsli | 4 - .../NewDepthOfFieldComposite.azsl | 76 ++++++++++++++----- .../NewDepthOfFieldFilterLarge.azsl | 44 ++++++++--- .../NewDepthOfFieldFilterSmall.azsl | 65 +++++++++++----- .../NewDepthOfFieldTile3x3.azsl | 7 +- .../NewDepthOfFieldTileReduce.azsl | 3 + .../atom_feature_common_asset_files.cmake | 1 + .../Code/Source/CommonSystemComponent.cpp | 4 - .../PostProcessing/NewDepthOfFieldPasses.cpp | 44 ++--------- .../PostProcessing/NewDepthOfFieldPasses.h | 35 ++------- 10 files changed, 155 insertions(+), 128 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli index a789537819..cd0f334f29 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldCommon.azsli @@ -23,9 +23,5 @@ struct NewDepthOfFieldConstants float4 samplePositions[60]; // XY are sample positions (normalized so max lenght is 1) // Z is the length of XY (0 - 1) // W is unused - - }; - - diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl index 14bc0d3b18..b15095535d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl @@ -51,33 +51,69 @@ PSOutput MainPS(VSOutput IN) float focusDistance = ViewSrg::m_dof.m_cameraParameters.z; float coc = ConvertDofFactor(InvertDepth(depth), far, near, focusDistance); - // Calculate Alpha - float cocRadius = abs(coc) * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; - float maxPixelDist = max(PassSrg::m_halfResDimensions.z, PassSrg::m_halfResDimensions.w); - float alpha = saturate(cocRadius / maxPixelDist); + // --- Weights based on CoC similarity --- + + // Gather CoCs + float4 cocGather = PassSrg::m_halfResColorAndCoc.GatherAlpha(PassSrg::LinearSampler, halfResUV); + + // Calculate differences + float4 diff = saturate(cocGather - coc); - // Sample half res color and CoC - float4 colorAndCoC = PassSrg::m_halfResColorAndCoc.Sample(PassSrg::LinearSampler, halfResUV); + // Slide differences such that small difference (i.e. most similar CoC) will become 0 + // (which then gets inverted in the next step) + float minDiff = min4(diff); + diff -= minDiff; + + // Invert the differences with a slope multiplier of 2 + float4 cocDiffWeights = saturate(1 - (2 * diff)); + + // --- Weights based on pixel proximity --- + + // Based on which pixel we're shading, we'll be closer/further to half res pixels + // Here are the pre-caculated weights, arranged to match the Gather pattern + // + // W Z + // X Y + // + // Note: These weights come down to the same contributions as if we did a linear sample + int2 pixel = int2(fullResPixelPos); + float4 weights = (pixel.x & 1) + ? ( (pixel.y & 1) ? float4(0.1875f, 0.0625f, 0.1875f, 0.5625f) + : float4(0.5625f, 0.1875f, 0.0625f, 0.1875f) ) + : ( (pixel.y & 1) ? float4(0.0625f, 0.1875f, 0.5625f, 0.1875f) + : float4(0.1875f, 0.5625f, 0.1875f, 0.0625f) ); + + // Combine and normalize weights + weights *= cocDiffWeights; + weights /= (weights.x + weights.y + weights.z + weights.w); + + // --- Color --- + + // For each color channel, do a gather and multiply the samples by the weights calculated above + float3 color; + float4 red = PassSrg::m_halfResColorAndCoc.GatherRed(PassSrg::LinearSampler, halfResUV); + color.r = dot(red, weights); + float4 blue = PassSrg::m_halfResColorAndCoc.GatherBlue(PassSrg::LinearSampler, halfResUV); + color.b = dot(blue, weights); + float4 green = PassSrg::m_halfResColorAndCoc.GatherGreen(PassSrg::LinearSampler, halfResUV); + color.g = dot(green, weights); - if(false) - { - // Make out of focus foreground increasingly blue - float multiplier = saturate(1.0f + coc); - colorAndCoC.r *= multiplier; - colorAndCoC.g *= multiplier; + // --- Alpha --- - // Make out of focus background increasingly red - multiplier = saturate(1.0f - coc); - colorAndCoC.b *= multiplier; - colorAndCoC.g *= multiplier; - } + // Calculate alpha such that we fully take the half res texture value if the CoC of the full + // resolution pixel is greater than the size of a half resolution pixel + float cocRadius = abs(coc) * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; + float alpha = saturate(cocRadius / PassSrg::m_halfResDimensions.w); + + // We may have objects in focus (CoC = 0) but that receive contribution from background bokeh + // (which have a negative CoC that we calculated in the large filter). Take the max here. + float minCoc = min4(cocGather); + alpha = max(alpha, -minCoc); - // Output PSOutput OUT; - OUT.m_color.rgb = colorAndCoC.rgb; + OUT.m_color.rgb = color.rgb; OUT.m_color.a = alpha; - return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl index 6ab47b14ea..ff955493d6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl @@ -18,6 +18,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_colorAndCoc; Texture2D m_minMaxCocTile; + // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height float4 m_textureDimensions; NewDepthOfFieldConstants m_dofConstants; @@ -43,10 +44,10 @@ ShaderResourceGroup PassSrg : SRG_PerPass }; } -float3 GetOffsetUV(uint index, float2 offsetMultiplier) +float3 GetOffset(uint index, float2 offsetUVMultiplier) { float3 offset = PassSrg::m_dofConstants.samplePositions[index].xyz; - offset.xy *= offsetMultiplier; + offset.xy *= offsetUVMultiplier; return offset; } @@ -55,14 +56,19 @@ float CaclulateWeight(float offsetRadius, float samplingRadius, float sampleCoc, // The maximum distance for which samples are valid is the min of the sample CoC and the center CoC float maxRadius = abs(min(sampleCoc, centerCoc)); + // Easy human readable calculations: // radius = samplingRadius * offsetRadius; // falloff = maxRadius - radius; - // weight = 1 + (4 * falloff) + // weight = 1 + (4 * falloff) + // + // The same thing in mad form: float falloff = mad(-samplingRadius, offsetRadius, maxRadius); - float weight = saturate(mad(4, falloff, 1)); - return weight; + return saturate(mad(4, falloff, 1)); } +// This shader blurs by sampling 48 pixels in a circle around the center pixel +// See http://advances.realtimerendering.com/s2013/Sousa_Graphics_Gems_CryENGINE3.pptx +// for a detailed explanation. PSOutput MainPS(VSOutput IN) { // Get center sample @@ -70,10 +76,12 @@ PSOutput MainPS(VSOutput IN) float4 color = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV).rgba; float centerCoc = color.a; - // Get tile min and max + // Get tile min CoC int2 tile = int2(IN.m_position.xy) / 16; float minCoc = PassSrg::m_minMaxCocTile[tile].x; + // Aspect ratio is needed because sample offsets are calculated in a perfect circle, but + // UV space is stretched due to normalized device coordinates. Correct this with aspect ratio // Aspect ratio = texture.x / texture.y = dimensions.x * dimensions.w float aspectRatio = PassSrg::m_textureDimensions.x * PassSrg::m_textureDimensions.w; @@ -82,15 +90,20 @@ PSOutput MainPS(VSOutput IN) float screenRadius = cocRadius * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; float2 offsetMultiplier = float2(screenRadius / aspectRatio, screenRadius); - + // Background samples are samples behind the current pixel. Because of how depth of field works, + // these pixels can contribute to the center pixel even if they are out of range of the center pixel's CoC + // We accumulate them seperately and calculate a new estimated alpha value based on the ratio of samples + // that were background pixels. float4 backgroundColor = float4(0, 0, 0, 0); + // If there are only positive CoCs in our region, we don't need to consider background blur + // Do the faster and nicer approach if(minCoc >= 0) { for(uint i = 0; i < SAMPLES_LOOP_TOTAL; ++i) { // Calculate sample offset - float3 offset = GetOffsetUV(i, offsetMultiplier); + float3 offset = GetOffset(i, offsetMultiplier); // Get sample float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offset.xy).rgba; @@ -102,18 +115,22 @@ PSOutput MainPS(VSOutput IN) color += weight * sampleColorCoc; } } - else + else // Some CoCs in the region are negative, need to consider possible background bokeh contribution { + // Distance behind which samples are considered background samples float backgroundMin = min(0, centerCoc); + + // Linear sampling colors pre-multiplied with CoC yields artefacts when combined with this background technique + // We therefore do point sampling and unpack the original color value per sample color.rgb /= abs(color.a); color.a = 1; for(uint i = 0; i < SAMPLES_LOOP_TOTAL; ++i) { // Calculate sample offset - float3 offset = GetOffsetUV(i, offsetMultiplier); + float3 offset = GetOffset(i, offsetMultiplier); - // Get sample + // Get sample + unpack float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV + offset.xy).rgba; sampleColorCoc.rgb /= abs(sampleColorCoc.a); @@ -131,16 +148,19 @@ PSOutput MainPS(VSOutput IN) color += !isBackground * sampleColorCoc; } + // Average background samples backgroundColor.rgb /= max(backgroundColor.a, COC_EPSILON); } + // Calculate background ratio. If greater than the current CoC, replace the current CoC with + // background ratio. This is so background bokeh effects will still render on in-focus objects float backgroundRatio = saturate( backgroundColor.a / float(SAMPLES_LOOP_TOTAL) ); float alpha = backgroundRatio > abs(centerCoc) ? -backgroundRatio : centerCoc; + // Average accumulated color samples and combine with background samples color.rgb /= max(color.a, COC_EPSILON); color = lerp(color, backgroundColor, backgroundRatio); - PSOutput OUT = (PSOutput)0; OUT.m_color.rgb = color.rgb; OUT.m_color.a = alpha; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl index 05227cfee7..04bc095a93 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl @@ -18,6 +18,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_colorAndCoc; Texture2D m_minMaxCocTile; + // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height float4 m_textureDimensions; NewDepthOfFieldConstants m_dofConstants; @@ -43,55 +44,77 @@ ShaderResourceGroup PassSrg : SRG_PerPass }; } -float2 GetOffsetUV(uint index, float2 offsetMultiplier) +float3 GetOffset(uint index, float2 offsetUVMultiplier) { - return PassSrg::m_dofConstants.samplePositions[index].xy * offsetMultiplier; + float3 offset = PassSrg::m_dofConstants.samplePositions[index].xyz; + offset.xy *= offsetUVMultiplier; + return offset; } +float CaclulateWeight(float offsetRadius, float samplingRadius, float sampleCoc, float centerCoc) +{ + // The maximum distance for which samples are valid is the min of the sample CoC and the center CoC + float maxRadius = abs(min(sampleCoc, centerCoc)); + + // Easy human readable calculations: + // radius = samplingRadius * offsetRadius; + // falloff = maxRadius - radius; + // weight = 1 + (4 * falloff) + // + // The same thing in mad form: + float falloff = mad(-samplingRadius, offsetRadius, maxRadius); + return saturate(mad(4, falloff, 1)); +} + +// This shader attempts to fill the gaps left by the large filter by sampling 8 pixels around the center pixel +// See http://advances.realtimerendering.com/s2013/Sousa_Graphics_Gems_CryENGINE3.pptx +// for a detailed overview of the technique PSOutput MainPS(VSOutput IN) { - // UV of pixel being shaded + // Get center sample float2 pixelUV = IN.m_texCoord.xy; + float4 color = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV).rgba; + float centerCoc = color.a; - // Sample pixel being shaded - float4 centerSample = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV).rgba; - float centerCoc = centerSample.a; + // Get tile min CoC + int2 tile = int2(IN.m_position.xy) / 16; + float minCoc = PassSrg::m_minMaxCocTile[tile].x; + // Aspect ratio is needed because sample offsets are calculated in a perfect circle, but + // UV space is stretched due to normalized device coordinates. Correct this with aspect ratio // Aspect ratio = texture.x / texture.y = dimensions.x * dimensions.w float aspectRatio = PassSrg::m_textureDimensions.x * PassSrg::m_textureDimensions.w; // Sampling radius - float cocRadius = abs(centerCoc) * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; - cocRadius *= 0.5f; // This is the small filter, half the sampling radius - float2 offsetMultiplier = float2(cocRadius / aspectRatio, cocRadius); + float cocRadius = max( abs(centerCoc), -minCoc) * 0.5f; // Small filter pass so half the radius + float screenRadius = cocRadius * ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; + float2 offsetMultiplier = float2(screenRadius / aspectRatio, screenRadius); - // Color and weight accumulation - float3 color = centerSample.rgb; + // Weight accumulation. Start with 1 for center pixel. float totalWeight = 1; - for(uint i = 0; i < 8; ++i) + for(uint i = 0; i < SAMPLES_LOOP_1; ++i) { // Calculate sample offset - float2 offsetUV = GetOffsetUV(i, offsetMultiplier); + float3 offset = GetOffset(i, offsetMultiplier); // Get sample - float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV + offsetUV).rgba; + float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV + offset.xy).rgba; // Calculate weight - float cocDiff = sampleColorCoc.a - centerCoc; - float weight = saturate( 2 - (20 * cocDiff)); + float weight = CaclulateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); // Accumulate sample and weight - color += sampleColorCoc.rgb * weight; + color.rgb += sampleColorCoc.rgb * weight; totalWeight += weight; } - // Normalize accumulated sample - color /= totalWeight; - // Output + // Normalize accumulated sample + color.rgb /= totalWeight; + PSOutput OUT; - OUT.m_color.rgb = color; + OUT.m_color.rgb = color.rgb; OUT.m_color.a = centerCoc; return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl index 7238b391aa..e7ce99aae9 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl @@ -33,13 +33,14 @@ struct PSOutput float2 m_color : SV_Target0; }; +// Expands the min and max tiles so each tile contains the min and max of it's 3x3 neighborhood PSOutput MainPS(VSOutput IN) { // We want the min/max in a 3x3 region. Start sampling up left. float2 startPixelPos = IN.m_position.xy - float2(1, 1); - float2 stepSize = PassSrg::m_textureDimensions.zw; - float2 startUV = startPixelPos * stepSize; + float2 pixelSizeInUV = PassSrg::m_textureDimensions.zw; + float2 startUV = startPixelPos * pixelSizeInUV; float cocMin = 1.0f; float cocMax = -1.0f; @@ -51,7 +52,7 @@ PSOutput MainPS(VSOutput IN) [unroll] for(float X = 0.0f; X < 3.0f; X += 1.0f) { - float2 sampleUV = mad(float2(X, Y), stepSize, startUV); + float2 sampleUV = mad(float2(X, Y), pixelSizeInUV, startUV); float2 minMax = PassSrg::m_minMaxSource.SampleLevel(PassSrg::PointSampler, sampleUV, 0).xy; cocMin = min(cocMin, minMax.x); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl index 2705e1fdfd..740ebabaff 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl @@ -36,6 +36,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass groupshared uint LDS_MIN_COC[8]; groupshared uint LDS_MAX_COC[8]; +// Calculates the min and max CoC (Circle of Confusion) for 16x16 pixel tiles [numthreads(8, 8, 1)] void MainCS(uint3 group_thread_id : SV_GroupThreadID, uint3 group_id : SV_GroupID, uint3 dispatch_id: SV_DispatchThreadID, uint linear_id : SV_GroupIndex) { @@ -68,9 +69,11 @@ void MainCS(uint3 group_thread_id : SV_GroupThreadID, uint3 group_id : SV_GroupI return; } + // Min the mins and max the maxs InterlockedMin( LDS_MIN_COC[0], LDS_MIN_COC[group_thread_id.x] ); InterlockedMax( LDS_MAX_COC[0], LDS_MAX_COC[group_thread_id.x] ); + // Each group write to just one pixel. If we're the last thread in the group, write out if(group_thread_id.x == 0) { // Unpack uints diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index d1f94afe3c..0423741dda 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -443,6 +443,7 @@ set(FILES Shaders/PostProcessing/MSAAResolveCustom.shader Shaders/PostProcessing/MSAAResolveDepth.azsl Shaders/PostProcessing/MSAAResolveDepth.shader + Shaders/PostProcessing/NewDepthOfFieldCommon.azsli Shaders/PostProcessing/NewDepthOfFieldComposite.azsl Shaders/PostProcessing/NewDepthOfFieldComposite.shader Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index a7891b7907..c117cadbde 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -252,10 +252,6 @@ namespace AZ passSystem->AddPassCreator(Name("NewDepthOfFieldParentPass"), &NewDepthOfFieldParentPass::Create); passSystem->AddPassCreator(Name("NewDepthOfFieldTileReducePass"), &NewDepthOfFieldTileReducePass::Create); passSystem->AddPassCreator(Name("NewDepthOfFieldFilterPass"), &NewDepthOfFieldFilterPass::Create); - passSystem->AddPassCreator(Name("NewDepthOfFieldCompositePass"), &NewDepthOfFieldCompositePass::Create); - - - // Add FastDepthAwareBlur passes passSystem->AddPassCreator(Name("FastDepthAwareBlurHorPass"), &FastDepthAwareBlurHorPass::Create); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp index b69484297a..69ee2a6751 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.cpp @@ -32,7 +32,6 @@ namespace AZ // W is unused }; - // --- Depth of Field Parent Pass --- RPI::Ptr NewDepthOfFieldParentPass::Create(const RPI::PassDescriptor& descriptor) @@ -103,8 +102,8 @@ namespace AZ NewDepthOfFieldTileReducePass::NewDepthOfFieldTileReducePass(const RPI::PassDescriptor& descriptor) : RPI::ComputePass(descriptor) { - // Though this is a fullscreen pass, the algorithm used makes each thread output 3 blurred pixels, so - // it's not a 1-to-1 ratio and requires custom calculation of target thread counts + // Though this is a fullscreen pass, the shader computes 16x16 tiles with groups of 8x8 threads, + // each thread outputting to a single pixel in the tiled min/max texture m_isFullscreenPass = false; } @@ -124,8 +123,6 @@ namespace AZ RPI::ComputePass::FrameBeginInternal(params); } - - // --- Filter Pass --- RPI::Ptr NewDepthOfFieldFilterPass::Create(const RPI::PassDescriptor& descriptor) @@ -144,22 +141,19 @@ namespace AZ uint32_t sampleIndex = 0; + // Calculate all the offset positions for (uint32_t loop = 0; loop < NewDepthOfFieldConstants::numberOfLoops; ++loop) { float radius = (loop + 1.0f) / float(NewDepthOfFieldConstants::numberOfLoops); float loopCount = NewDepthOfFieldConstants::loopCounts[loop]; - float angleOffset = 0; + float angleStep = Constants::TwoPi / loopCount; // Every other loop slightly rotate sample ring so they don't line up - if (loop & 1) - { - angleOffset = Constants::TwoPi * 0.5f / loopCount; - } + float angle = (loop & 1) ? (angleStep * 0.5f) : 0; for (float i = 0.0f; i < loopCount; ++i) { - float angle = Constants::TwoPi * i / loopCount; Vector2 pos = Vector2::CreateFromAngle(angle); pos = pos * radius; @@ -167,38 +161,16 @@ namespace AZ dofConstants.m_samplePositions[sampleIndex][1] = pos.GetY(); dofConstants.m_samplePositions[sampleIndex][2] = radius; dofConstants.m_samplePositions[sampleIndex][3] = 0.0f; - ++sampleIndex; - } + ++sampleIndex; + angle += angleStep; + } } m_shaderResourceGroup->SetConstant(m_constantsIndex, dofConstants); - // TODO HERE RPI::FullscreenTrianglePass::FrameBeginInternal(params); } - - - // --- Composite Pass --- - - RPI::Ptr NewDepthOfFieldCompositePass::Create(const RPI::PassDescriptor& descriptor) - { - RPI::Ptr pass = aznew NewDepthOfFieldCompositePass(descriptor); - return AZStd::move(pass); - } - - NewDepthOfFieldCompositePass::NewDepthOfFieldCompositePass(const RPI::PassDescriptor& descriptor) - : RPI::ComputePass(descriptor) - { } - - void NewDepthOfFieldCompositePass::FrameBeginInternal(FramePrepareParams params) - { - // TODO HERE - RPI::ComputePass::FrameBeginInternal(params); - } - - - } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h index f7a0ee95ca..d529623c0a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h @@ -17,7 +17,9 @@ namespace AZ { namespace Render { - //! + //! Parent pass for the new depth of field technique + //! Main updates the view srg via the depth of field settings + //! And enables/disables all depth of field passes based on component activation class NewDepthOfFieldParentPass final : public RPI::ParentPass { @@ -41,8 +43,7 @@ namespace AZ }; - - //! + //! Need a class for the tile reduce pass because it dispatches a non-trivial number of threads class NewDepthOfFieldTileReducePass final : public RPI::ComputePass { @@ -64,8 +65,9 @@ namespace AZ }; - - //! + //! Filter pass used to render the bokeh blur effect on downsampled image buffer + //! This class is used for both the large filter and the small filter + //! It's main purpose is calculating the sample positions and setting srg constants class NewDepthOfFieldFilterPass final : public RPI::FullscreenTrianglePass { @@ -90,28 +92,5 @@ namespace AZ }; - - //! - class NewDepthOfFieldCompositePass final - : public RPI::ComputePass - { - AZ_RPI_PASS(NewDepthOfFieldCompositePass); - - public: - AZ_RTTI(AZ::Render::NewDepthOfFieldCompositePass, "{63270A3A-EAE5-4C0C-98AA-43CA55279613}", AZ::RPI::ComputePass); - AZ_CLASS_ALLOCATOR(NewDepthOfFieldCompositePass, SystemAllocator, 0); - virtual ~NewDepthOfFieldCompositePass() = default; - - static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); - - protected: - // Behavior functions override... - void FrameBeginInternal(FramePrepareParams params) override; - - private: - NewDepthOfFieldCompositePass(const RPI::PassDescriptor& descriptor); - }; - - } // namespace Render } // namespace AZ From 6e7d2e6dd62bbb26114e20637a1704076553f1fd Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Thu, 21 Oct 2021 04:48:45 -0700 Subject: [PATCH 081/152] removing commented line in TAA pass Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp index 643d9bb96d..574d959c07 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp @@ -168,7 +168,6 @@ namespace AZ::Render // The ImageViewDescriptor must be specified to make sure the frame graph compiler doesn't treat this as a transient image. RHI::ImageViewDescriptor viewDesc = RHI::ImageViewDescriptor::Create(imageDesc.m_format, 0, 0); viewDesc.m_aspectFlags = RHI::ImageAspectFlags::Color; - //viewDesc.m_overrideBindFlags = RHI::ImageBindFlags::ShaderReadWrite; // The full path name is needed for the attachment image so it's not deduplicated from accumulation images in different pipelines. AZStd::string imageName = RPI::ConcatPassString(GetPathName(), attachment->m_path); From 58a518ce692f18a1378d6a4d02f252ce56d4ec69 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Thu, 21 Oct 2021 04:52:46 -0700 Subject: [PATCH 082/152] removing accidental file Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- ...epthOfFieldFilterLarge.azsl~RF1bf9782e.TMP | 105 ------------------ 1 file changed, 105 deletions(-) delete mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl~RF1bf9782e.TMP diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl~RF1bf9782e.TMP b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl~RF1bf9782e.TMP deleted file mode 100644 index 7c0f32381b..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl~RF1bf9782e.TMP +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include "DepthOfField.azsli" -#include "NewDepthOfFieldCommon.azsli" - -#include - -ShaderResourceGroup PassSrg : SRG_PerPass -{ - Texture2D m_colorAndCoc; - Texture2D m_minMaxCocTile; - - float4 m_textureDimensions; - - NewDepthOfFieldConstants m_dofConstants; - - Sampler LinearSampler - { - MinFilter = Linear; - MagFilter = Linear; - MipFilter = Linear; - AddressU = Clamp; - AddressV = Clamp; - AddressW = Clamp; - }; -} - -float2 GetOffsetUV(uint index, float2 offsetMultiplier) -{ - return PassSrg::m_dofConstants.samplePositions[index].xy * offsetMultiplier; -} - -PSOutput MainPS(VSOutput IN) -{ - // Get center sample - float2 pixelUV = IN.m_texCoord; - float4 color = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV).rgba; - float centerCoc = color.a; - - // Get tile min and max - int2 tile = int2(IN.m_position.xy) / 16; - float minCoc = PassSrg::m_minMaxCocTile[tile].x; - - // Aspect ratio = texture.x / texture.y = dimensions.x * dimensions.w - float aspectRatio = PassSrg::m_textureDimensions.x * PassSrg::m_textureDimensions.w; - - // Sampling radius - float cocRadius = max( abs(centerCoc), -minCoc); - cocRadius *= ViewSrg::m_dof.m_cocToScreenRatio * 0.5f; - float2 offsetMultiplier = float2(cocRadius / aspectRatio, cocRadius); - - - - float4 negColor = float4(0, 0, 0, 0); - - { - float negMin = min(0, centerCoc); - color.rgb /= abs(color.a); - color.a = 1; - - for(uint i = 0; i < SAMPLES_LOOP_TOTAL; ++i) - { - // Calculate sample offset - float2 offsetUV = GetOffsetUV(i, offsetMultiplier); - - // Get sample - float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offsetUV).rgba; - - // Calculate weight and adjust sample - float cocDiff = sampleColorCoc.a - centerCoc; - float weight = saturate( 2 - (20 * cocDiff)); - sampleColorCoc.rgb *= (weight / abs(sampleColorCoc.a)); - // - // bool isNeg = (sampleColorCoc.a < negMin); - // sampleColorCoc.a = weight; - // - // // Accumulate - // negColor += isNeg * sampleColorCoc; - // color += !isNeg * sampleColorCoc; - } - } - - float negRatio = negColor.a / float(SAMPLES_LOOP_TOTAL); - float alpha = negRatio > abs(centerCoc) ? -negRatio : centerCoc; - - color = color * saturate(1 - negRatio) + negColor; - color.rgb /= max(color.a, COC_EPSILON); - color.rgb += negColor.rgb; - - //float alpha = 1.0f; - - PSOutput OUT = (PSOutput)0; - OUT.m_color.rgb = color.rgb; - OUT.m_color.a = alpha; - return OUT; -} - From d78aa5bf905231ed954066a22b69fa1ec9772f34 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Thu, 21 Oct 2021 10:55:32 -0500 Subject: [PATCH 083/152] Removed legacy ColorGradientCtrl. Signed-off-by: Chris Galvan --- Code/Editor/Controls/ColorGradientCtrl.cpp | 923 ------------------ Code/Editor/Controls/ColorGradientCtrl.h | 167 ---- .../ReflectedPropertyControl/PropertyCtrl.cpp | 1 - .../PropertyMiscCtrl.cpp | 27 - .../PropertyMiscCtrl.h | 14 - Code/Editor/editor_lib_files.cmake | 2 - 6 files changed, 1134 deletions(-) delete mode 100644 Code/Editor/Controls/ColorGradientCtrl.cpp delete mode 100644 Code/Editor/Controls/ColorGradientCtrl.h diff --git a/Code/Editor/Controls/ColorGradientCtrl.cpp b/Code/Editor/Controls/ColorGradientCtrl.cpp deleted file mode 100644 index 446e5810c5..0000000000 --- a/Code/Editor/Controls/ColorGradientCtrl.cpp +++ /dev/null @@ -1,923 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#include "EditorDefs.h" - -#include "ColorGradientCtrl.h" - -// Qt -#include -#include - -// AzQtComponents -#include - - -#define MIN_TIME_EPSILON 0.01f - -////////////////////////////////////////////////////////////////////////// -CColorGradientCtrl::CColorGradientCtrl(QWidget* parent) - : QWidget(parent) -{ - m_nActiveKey = -1; - m_nHitKeyIndex = -1; - m_nKeyDrawRadius = 3; - m_bTracking = false; - m_pSpline = nullptr; - m_fMinTime = -1; - m_fMaxTime = 1; - m_fMinValue = -1; - m_fMaxValue = 1; - m_fTooltipScaleX = 1; - m_fTooltipScaleY = 1; - m_bNoTimeMarker = true; - m_bLockFirstLastKey = false; - m_bNoZoom = true; - - ClearSelection(); - - m_bSelectedKeys.reserve(0); - - m_fTimeMarker = -10; - - m_grid.zoom.x = 100; - - setMouseTracking(true); -} - -CColorGradientCtrl::~CColorGradientCtrl() -{ -} - - -///////////////////////////////////////////////////////////////////////////// -// QColorGradientCtrl message handlers - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::resizeEvent(QResizeEvent* event) -{ - QWidget::resizeEvent(event); - - QRect rc(QPoint(0, 0), event->size()); - m_rcGradient = rc; - m_rcGradient.setHeight(m_rcGradient.height() - 11); - //m_rcGradient.DeflateRect(4,4); - - m_grid.rect = m_rcGradient; - if (m_bNoZoom) - { - m_grid.zoom.x = static_cast(m_grid.rect.width()); - } - - m_rcKeys = rc; - m_rcKeys.setTop(m_rcKeys.bottom() - 10); -} - - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::SetZoom(float fZoom) -{ - m_grid.zoom.x = fZoom; -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::SetOrigin(float fOffset) -{ - m_grid.origin.x = fOffset; -} - -////////////////////////////////////////////////////////////////////////// -QPoint CColorGradientCtrl::KeyToPoint(int nKey) -{ - if (nKey >= 0) - { - return TimeToPoint(m_pSpline->GetKeyTime(nKey)); - } - return QPoint(0, 0); -} - -////////////////////////////////////////////////////////////////////////// -QPoint CColorGradientCtrl::TimeToPoint(float time) -{ - return QPoint(m_grid.WorldToClient(Vec2(time, 0)).x(), m_rcGradient.height() / 2); -} - -////////////////////////////////////////////////////////////////////////// -AZ::Color CColorGradientCtrl::TimeToColor(float time) -{ - ISplineInterpolator::ValueType val; - m_pSpline->Interpolate(time, val); - const AZ::Color col = ValueToColor(val); - return col; -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::PointToTimeValue(QPoint point, float& time, ISplineInterpolator::ValueType& val) -{ - time = XOfsToTime(point.x()); - ColorToValue(TimeToColor(time), val); -} - -////////////////////////////////////////////////////////////////////////// -float CColorGradientCtrl::XOfsToTime(int x) -{ - return m_grid.ClientToWorld(QPoint(x, 0)).x; -} - -////////////////////////////////////////////////////////////////////////// -QPoint CColorGradientCtrl::XOfsToPoint(int x) -{ - return TimeToPoint(XOfsToTime(x)); -} - -////////////////////////////////////////////////////////////////////////// -AZ::Color CColorGradientCtrl::XOfsToColor(int x) -{ - return TimeToColor(XOfsToTime(x)); -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::paintEvent(QPaintEvent* e) -{ - QPainter painter(this); - - QRect rcClient = rect(); - - if (m_pSpline) - { - m_bSelectedKeys.resize(m_pSpline->GetKeyCount()); - } - { - if (!isEnabled()) - { - painter.setBrush(palette().button()); - painter.drawRect(rcClient); - return; - } - - ////////////////////////////////////////////////////////////////////////// - // Fill keys backgound. - ////////////////////////////////////////////////////////////////////////// - QRect rcKeys = m_rcKeys.intersected(e->rect()); - painter.setBrush(palette().button()); - painter.drawRect(rcKeys); - ////////////////////////////////////////////////////////////////////////// - - //Draw Keys and Curve - if (m_pSpline) - { - DrawGradient(e, &painter); - DrawKeys(e, &painter); - } - } -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::DrawGradient(QPaintEvent* e, QPainter* painter) -{ - //Draw Curve - // create and select a thick, white pen - painter->setPen(QPen(QColor(128, 255, 128), 1, Qt::SolidLine)); - - const QRect rcClip = e->rect().intersected(m_rcGradient); - const int right = rcClip.left() + rcClip.width(); - for (int x = rcClip.left(); x < right; x++) - { - const AZ::Color col = XOfsToColor(x); - QPen pen(QColor(col.GetR8(), col.GetG8(), col.GetR8(), col.GetA8()), 1, Qt::SolidLine); - painter->setPen(pen); - painter->drawLine(x, m_rcGradient.top(), x, m_rcGradient.top() + m_rcGradient.height()); - } -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::DrawKeys(QPaintEvent* e, QPainter* painter) -{ - if (!m_pSpline) - { - return; - } - - // create and select a white pen - painter->setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine)); - - QRect rcClip = e->rect(); - - m_bSelectedKeys.resize(m_pSpline->GetKeyCount()); - - for (int i = 0; i < m_pSpline->GetKeyCount(); i++) - { - float time = m_pSpline->GetKeyTime(i); - QPoint pt = TimeToPoint(time); - - if (pt.x() < rcClip.left() - 8 || pt.x() > rcClip.left() + rcClip.width() + 8) - { - continue; - } - - const AZ::Color clr = TimeToColor(time); - QBrush brush(QColor(clr.GetR8(), clr.GetG8(), clr.GetB8(), clr.GetA8())); - painter->setBrush(brush); - - // Find the midpoints of the top, right, left, and bottom - // of the client area. They will be the vertices of our polygon. - QPoint pts[3]; - pts[0].rx() = pt.x(); - pts[0].ry() = m_rcKeys.top() + 1; - pts[1].rx() = pt.x() - 5; - pts[1].ry() = m_rcKeys.top() + 8; - pts[2].rx() = pt.x() + 5; - pts[2].ry() = m_rcKeys.top() + 8; - painter->drawPolygon(pts, 3); - - if (m_bSelectedKeys[i]) - { - QPen pen(QColor(200, 0, 0), 1, Qt::SolidLine); - QPen oldPen = painter->pen(); - painter->setPen(pen); - painter->drawPolygon(pts, 3); - painter->setPen(oldPen); - } - } - - if (!m_bNoTimeMarker) - { - QPen timePen(QColor(255, 0, 255), 1, Qt::SolidLine); - painter->setPen(timePen); - QPoint pt = TimeToPoint(m_fTimeMarker); - painter->drawLine(pt.x(), m_rcGradient.top() + 1, pt.x(), m_rcGradient.bottom() - 1); - } -} - -void CColorGradientCtrl::UpdateTooltip(QPoint pos) -{ - if (m_nHitKeyIndex >= 0) - { - float time = m_pSpline->GetKeyTime(m_nHitKeyIndex); - ISplineInterpolator::ValueType val; - m_pSpline->GetKeyValue(m_nHitKeyIndex, val); - - AZ::Color col = TimeToColor(time); - int cont_s = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_IN_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2; - int cont_d = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_OUT_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2; - - QString tipText(tr("%1 : %2,%3,%4 [%5,%6]").arg(time * m_fTooltipScaleX, 0, 'f', 2).arg(col.GetR8()).arg(col.GetG8()).arg(col.GetB8()).arg(cont_s).arg(cont_d)); - const QPoint globalPos = mapToGlobal(pos); - QToolTip::showText(mapToGlobal(pos), tipText, this, QRect(globalPos, QSize(1, 1))); - } -} - -///////////////////////////////////////////////////////////////////////////// -//Mouse Message Handlers -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::mousePressEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) - { - OnLButtonDown(event); - } - else if (event->button() == Qt::RightButton) - { - OnRButtonDown(event); - } -} - -void CColorGradientCtrl::OnLButtonDown([[maybe_unused]] QMouseEvent* event) -{ - if (m_bTracking) - { - return; - } - if (!m_pSpline) - { - return; - } - - setFocus(); - - switch (m_hitCode) - { - case HIT_KEY: - StartTracking(); - SetActiveKey(m_nHitKeyIndex); - break; - - /* - case HIT_SPLINE: - { - // Cycle the spline slope of the nearest key. - int flags = m_pSpline->GetKeyFlags(m_nHitKeyIndex); - if (m_nHitKeyDist < 0) - // Toggle left side. - flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_IN_SHIFT; - if (m_nHitKeyDist > 0) - // Toggle right side. - flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_OUT_SHIFT; - m_pSpline->SetKeyFlags(m_nHitKeyIndex, flags); - m_pSpline->Update(); - - SetActiveKey(-1); - SendNotifyEvent( CLRGRDN_CHANGE ); - if (m_updateCallback) - m_updateCallback(this); - break; - } - */ - - case HIT_NOTHING: - SetActiveKey(-1); - break; - } - update(); -} - - - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::OnRButtonDown([[maybe_unused]] QMouseEvent* event) -{ -} - - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::mouseDoubleClickEvent(QMouseEvent* event) -{ - if (!m_pSpline) - { - return; - } - - if (event->button() != Qt::LeftButton) - { - return; - } - - switch (m_hitCode) - { - case HIT_SPLINE: - { - int iIndex = InsertKey(event->pos()); - SetActiveKey(iIndex); - EditKey(iIndex); - - update(); - } - break; - case HIT_KEY: - { - EditKey(m_nHitKeyIndex); - } - break; - } -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::mouseMoveEvent(QMouseEvent* event) -{ - if (!m_pSpline) - { - return; - } - - if (!m_bTracking) - { - switch (HitTest(event->pos())) - { - case HIT_SPLINE: - { - setCursor(CMFCUtils::LoadCursor(IDC_ARRWHITE)); - } break; - case HIT_KEY: - { - setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCK)); - } break; - default: - break; - } - } - - if (m_bTracking) - { - TrackKey(event->pos()); - } - - if (m_bTracking || m_nHitKeyIndex >= 0) - { - UpdateTooltip(event->pos()); - } - else - { - QToolTip::hideText(); - } -} - -void CColorGradientCtrl::mouseReleaseEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) - { - OnLButtonUp(event); - } - else if (event->button() == Qt::RightButton) - { - OnRButtonUp(event); - } -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::OnLButtonUp(QMouseEvent* event) -{ - if (!m_pSpline) - { - return; - } - - if (m_bTracking) - { - StopTracking(event->pos()); - } -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::OnRButtonUp([[maybe_unused]] QMouseEvent* event) -{ - if (!m_pSpline) - { - return; - } -} - -///////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::SetActiveKey(int nIndex) -{ - ClearSelection(); - - //Activate New Key - if (nIndex >= 0) - { - m_bSelectedKeys[nIndex] = true; - } - m_nActiveKey = nIndex; - update(); - - SendNotifyEvent(CLRGRDN_ACTIVE_KEY_CHANGE); -} - -///////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::SetSpline(ISplineInterpolator* pSpline, bool bRedraw) -{ - if (pSpline != m_pSpline) - { - //if (pSpline && pSpline->GetNumDimensions() != 3) - //return; - m_pSpline = pSpline; - m_nActiveKey = -1; - } - - ClearSelection(); - - if (bRedraw) - { - update(); - } -} - -////////////////////////////////////////////////////////////////////////// -ISplineInterpolator* CColorGradientCtrl::GetSpline() -{ - return m_pSpline; -} - -///////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::keyPressEvent(QKeyEvent* event) -{ - bool bProcessed = false; - - if (m_nActiveKey != -1 && m_pSpline) - { - switch (event->key()) - { - case Qt::Key_Delete: - { - RemoveKey(m_nActiveKey); - bProcessed = true; - } break; - case Qt::Key_Up: - { - CUndo undo("Move Spline Key"); - QPoint point = KeyToPoint(m_nActiveKey); - point.rx() -= 1; - SendNotifyEvent(CLRGRDN_BEFORE_CHANGE); - TrackKey(point); - bProcessed = true; - } break; - case Qt::Key_Down: - { - CUndo undo("Move Spline Key"); - QPoint point = KeyToPoint(m_nActiveKey); - point.rx() += 1; - SendNotifyEvent(CLRGRDN_BEFORE_CHANGE); - TrackKey(point); - bProcessed = true; - } break; - case Qt::Key_Left: - { - CUndo undo("Move Spline Key"); - QPoint point = KeyToPoint(m_nActiveKey); - point.rx() -= 1; - SendNotifyEvent(CLRGRDN_BEFORE_CHANGE); - TrackKey(point); - bProcessed = true; - } break; - case Qt::Key_Right: - { - CUndo undo("Move Spline Key"); - QPoint point = KeyToPoint(m_nActiveKey); - point.rx() += 1; - SendNotifyEvent(CLRGRDN_BEFORE_CHANGE); - TrackKey(point); - bProcessed = true; - } break; - - default: - break; //do nothing - } - - update(); - } - - event->setAccepted(bProcessed); -} - -////////////////////////////////////////////////////////////////////////////// -CColorGradientCtrl::EHitCode CColorGradientCtrl::HitTest(QPoint point) -{ - if (!m_pSpline) - { - return HIT_NOTHING; - } - - ISplineInterpolator::ValueType val; - float time; - PointToTimeValue(point, time, val); - - QRect rc = rect(); - - m_nHitKeyIndex = -1; - - if (rc.contains(point)) - { - m_nHitKeyDist = 0xFFFF; - m_hitCode = HIT_SPLINE; - - for (int i = 0; i < m_pSpline->GetKeyCount(); i++) - { - QPoint splinePt = TimeToPoint(m_pSpline->GetKeyTime(i)); - if (abs(point.x() - splinePt.x()) < abs(m_nHitKeyDist)) - { - m_nHitKeyIndex = i; - m_nHitKeyDist = point.x() - splinePt.x(); - } - } - if (abs(m_nHitKeyDist) < 4) - { - m_hitCode = HIT_KEY; - } - } - else - { - m_hitCode = HIT_NOTHING; - } - - return m_hitCode; -} - -/////////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::StartTracking() -{ - m_bTracking = true; - - GetIEditor()->BeginUndo(); - SendNotifyEvent(CLRGRDN_BEFORE_CHANGE); - - setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCKCROSS)); -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::TrackKey(QPoint point) -{ - if (point.x() < m_rcGradient.left() || point.y() > m_rcGradient.right()) - { - return; - } - - int nKey = m_nHitKeyIndex; - - if (nKey >= 0) - { - ISplineInterpolator::ValueType val; - float time; - PointToTimeValue(point, time, val); - - // Clamp to min/max time. - if (time < m_fMinTime || time > m_fMaxTime) - { - return; - } - - int i; - for (i = 0; i < m_pSpline->GetKeyCount(); i++) - { - // Switch to next key. - if ((m_pSpline->GetKeyTime(i) < time && i > nKey) || - (m_pSpline->GetKeyTime(i) > time && i < nKey)) - { - m_pSpline->SetKeyTime(nKey, time); - m_pSpline->Update(); - SetActiveKey(i); - m_nHitKeyIndex = i; - return; - } - } - - if (!m_bLockFirstLastKey || (nKey != 0 && nKey != m_pSpline->GetKeyCount() - 1)) - { - m_pSpline->SetKeyTime(nKey, time); - m_pSpline->Update(); - } - - SendNotifyEvent(CLRGRDN_CHANGE); - if (m_updateCallback) - { - m_updateCallback(this); - } - - update(); - } -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::StopTracking(QPoint point) -{ - if (!m_bTracking) - { - return; - } - - GetIEditor()->AcceptUndo("Spline Move"); - - if (m_nHitKeyIndex >= 0) - { - QRect rc = rect(); - rc = rc.marginsAdded(QMargins(100, 100, 100, 100)); - if (!rc.contains(point)) - { - RemoveKey(m_nHitKeyIndex); - } - } - - m_bTracking = false; -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::EditKey(int nKey) -{ - if (!m_pSpline) - { - return; - } - - if (nKey < 0 || nKey >= m_pSpline->GetKeyCount()) - { - return; - } - - SetActiveKey(nKey); - - ISplineInterpolator::ValueType val; - m_pSpline->GetKeyValue(nKey, val); - - SendNotifyEvent(CLRGRDN_BEFORE_CHANGE); - - AzQtComponents::ColorPicker dlg(AzQtComponents::ColorPicker::Configuration::RGB); - dlg.setCurrentColor(ValueToColor(val)); - dlg.setSelectedColor(ValueToColor(val)); - connect(&dlg, &AzQtComponents::ColorPicker::currentColorChanged, this, &CColorGradientCtrl::OnKeyColorChanged); - if (dlg.exec() == QDialog::Accepted) - { - CUndo undo("Modify Gradient Color"); - OnKeyColorChanged(dlg.selectedColor()); - } - else - { - OnKeyColorChanged(ValueToColor(val)); - } -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::OnKeyColorChanged(const AZ::Color& color) -{ - int nKey = m_nActiveKey; - if (!m_pSpline) - { - return; - } - if (nKey < 0 || nKey >= m_pSpline->GetKeyCount()) - { - return; - } - - ISplineInterpolator::ValueType val; - ColorToValue(color, val); - m_pSpline->SetKeyValue(nKey, val); - update(); - - if (m_bLockFirstLastKey) - { - if (nKey == 0) - { - m_pSpline->SetKeyValue(m_pSpline->GetKeyCount() - 1, val); - } - else if (nKey == m_pSpline->GetKeyCount() - 1) - { - m_pSpline->SetKeyValue(0, val); - } - } - m_pSpline->Update(); - SendNotifyEvent(CLRGRDN_CHANGE); - if (m_updateCallback) - { - m_updateCallback(this); - } - - GetIEditor()->UpdateViews(eRedrawViewports); -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::RemoveKey(int nKey) -{ - if (!m_pSpline) - { - return; - } - if (m_bLockFirstLastKey) - { - if (nKey == 0 || nKey == m_pSpline->GetKeyCount() - 1) - { - return; - } - } - - CUndo undo("Remove Spline Key"); - - SendNotifyEvent(CLRGRDN_BEFORE_CHANGE); - m_nActiveKey = -1; - m_nHitKeyIndex = -1; - if (m_pSpline) - { - m_pSpline->RemoveKey(nKey); - m_pSpline->Update(); - } - SendNotifyEvent(CLRGRDN_CHANGE); - if (m_updateCallback) - { - m_updateCallback(this); - } - - update(); -} - -////////////////////////////////////////////////////////////////////////// -int CColorGradientCtrl::InsertKey(QPoint point) -{ - CUndo undo("Spline Insert Key"); - - ISplineInterpolator::ValueType val; - - float time; - PointToTimeValue(point, time, val); - - if (time < m_fMinTime || time > m_fMaxTime) - { - return -1; - } - - int i; - for (i = 0; i < m_pSpline->GetKeyCount(); i++) - { - // Skip if any key already have time that is very close. - if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON) - { - return i; - } - } - - SendNotifyEvent(CLRGRDN_BEFORE_CHANGE); - - m_pSpline->InsertKey(time, val); - m_pSpline->Interpolate(time, val); - ClearSelection(); - update(); - - SendNotifyEvent(CLRGRDN_CHANGE); - if (m_updateCallback) - { - m_updateCallback(this); - } - - for (i = 0; i < m_pSpline->GetKeyCount(); i++) - { - // Find key with added time. - if (m_pSpline->GetKeyTime(i) == time) - { - return i; - } - } - - return -1; -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::ClearSelection() -{ - m_nActiveKey = -1; - if (m_pSpline) - { - m_bSelectedKeys.resize(m_pSpline->GetKeyCount()); - } - for (int i = 0; i < (int)m_bSelectedKeys.size(); i++) - { - m_bSelectedKeys[i] = false; - } -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::SetTimeMarker(float fTime) -{ - if (!m_pSpline) - { - return; - } - - { - QPoint pt = TimeToPoint(m_fTimeMarker); - QRect rc = QRect(pt.x(), m_rcGradient.top(), 0, m_rcGradient.bottom() - m_rcGradient.top()).normalized(); - rc += QMargins(1, 0, 1, 0); - update(rc); - } - { - QPoint pt = TimeToPoint(fTime); - QRect rc = QRect(pt.x(), m_rcGradient.top(), 0, m_rcGradient.bottom() - m_rcGradient.top()).normalized(); - rc += QMargins(1, 0, 1, 0); - update(rc); - } - m_fTimeMarker = fTime; -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::SendNotifyEvent(int nEvent) -{ - switch (nEvent) - { - case CLRGRDN_BEFORE_CHANGE: - emit beforeChange(); - break; - case CLRGRDN_CHANGE: - emit change(); - break; - case CLRGRDN_ACTIVE_KEY_CHANGE: - emit activeKeyChange(); - break; - } -} - -////////////////////////////////////////////////////////////////////////// -AZ::Color CColorGradientCtrl::ValueToColor(ISplineInterpolator::ValueType val) -{ - const AZ::Color color(val[0], val[1], val[2], 1.0); - return color.LinearToGamma(); -} - -////////////////////////////////////////////////////////////////////////// -void CColorGradientCtrl::ColorToValue(const AZ::Color& col, ISplineInterpolator::ValueType& val) -{ - const AZ::Color colLin = col.GammaToLinear(); - val[0] = colLin.GetR(); - val[1] = colLin.GetG(); - val[2] = colLin.GetB(); - val[3] = 0; -} - -void CColorGradientCtrl::SetNoTimeMarker(bool noTimeMarker) -{ - m_bNoTimeMarker = noTimeMarker; - update(); -} - - -#include diff --git a/Code/Editor/Controls/ColorGradientCtrl.h b/Code/Editor/Controls/ColorGradientCtrl.h deleted file mode 100644 index bb1a83b0c1..0000000000 --- a/Code/Editor/Controls/ColorGradientCtrl.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#ifndef CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H -#define CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H -#pragma once - -#if !defined(Q_MOC_RUN) -#include -#include -#include "Controls/WndGridHelper.h" -#endif - -namespace AZ -{ - class Color; -} - -// Notify event sent when spline is being modified. -#define CLRGRDN_CHANGE (0x0001) -// Notify event sent just before when spline is modified. -#define CLRGRDN_BEFORE_CHANGE (0x0002) -// Notify event sent when the active key changes -#define CLRGRDN_ACTIVE_KEY_CHANGE (0x0003) - -////////////////////////////////////////////////////////////////////////// -// Spline control. -////////////////////////////////////////////////////////////////////////// -class CColorGradientCtrl - : public QWidget -{ - Q_OBJECT -public: - CColorGradientCtrl(QWidget* parent = nullptr); - virtual ~CColorGradientCtrl(); - - //Key functions - int GetActiveKey() { return m_nActiveKey; }; - void SetActiveKey(int nIndex); - int InsertKey(QPoint point); - - // Turns on/off zooming and scroll support. - void SetNoZoom([[maybe_unused]] bool bNoZoom) { m_bNoZoom = false; }; - - void SetTimeRange(float tmin, float tmax) { m_fMinTime = tmin; m_fMaxTime = tmax; } - void SetValueRange(float tmin, float tmax) { m_fMinValue = tmin; m_fMaxValue = tmax; } - void SetTooltipValueScale(float x, float y) { m_fTooltipScaleX = x; m_fTooltipScaleY = y; }; - // Lock value of first and last key to be the same. - void LockFirstAndLastKeys(bool bLock) { m_bLockFirstLastKey = bLock; } - - void SetSpline(ISplineInterpolator* pSpline, bool bRedraw = false); - ISplineInterpolator* GetSpline(); - - void SetTimeMarker(float fTime); - - // Zoom in pixels per time unit. - void SetZoom(float fZoom); - void SetOrigin(float fOffset); - - typedef AZStd::function UpdateCallback; - void SetUpdateCallback(const UpdateCallback& cb) { m_updateCallback = cb; }; - - void SetNoTimeMarker(bool noTimeMarker); - -signals: - void change(); - void beforeChange(); - void activeKeyChange(); - -protected: - enum EHitCode - { - HIT_NOTHING, - HIT_KEY, - HIT_SPLINE, - }; - - void paintEvent(QPaintEvent* e) override; - void resizeEvent(QResizeEvent* event) override; - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void OnLButtonDown(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* event) override; - void OnLButtonUp(QMouseEvent* event); - void OnRButtonUp(QMouseEvent* event); - void mouseDoubleClickEvent(QMouseEvent* event) override; - void OnRButtonDown(QMouseEvent* event); - void keyPressEvent(QKeyEvent* event) override; - - // Drawing functions - void DrawGradient(QPaintEvent* e, QPainter* painter); - void DrawKeys(QPaintEvent* e, QPainter* painter); - void UpdateTooltip(QPoint pos); - - EHitCode HitTest(QPoint point); - - //Tracking support helper functions - void StartTracking(); - void TrackKey(QPoint point); - void StopTracking(QPoint point); - void RemoveKey(int nKey); - void EditKey(int nKey); - - QPoint KeyToPoint(int nKey); - QPoint TimeToPoint(float time); - void PointToTimeValue(QPoint point, float& time, ISplineInterpolator::ValueType& val); - float XOfsToTime(int x); - QPoint XOfsToPoint(int x); - - AZ::Color XOfsToColor(int x); - AZ::Color TimeToColor(float time); - - void ClearSelection(); - - void SendNotifyEvent(int nEvent); - - AZ::Color ValueToColor(ISplineInterpolator::ValueType val); - void ColorToValue(const AZ::Color& col, ISplineInterpolator::ValueType& val); - - -private: - void OnKeyColorChanged(const AZ::Color& color); - -private: - ISplineInterpolator* m_pSpline; - - bool m_bNoZoom; - - QRect m_rcClipRect; - QRect m_rcGradient; - QRect m_rcKeys; - - QPoint m_hitPoint; - EHitCode m_hitCode; - int m_nHitKeyIndex; - int m_nHitKeyDist; - QPoint m_curvePoint; - - float m_fTimeMarker; - - int m_nActiveKey; - int m_nKeyDrawRadius; - - bool m_bTracking; - - float m_fMinTime, m_fMaxTime; - float m_fMinValue, m_fMaxValue; - float m_fTooltipScaleX, m_fTooltipScaleY; - - bool m_bLockFirstLastKey; - - bool m_bNoTimeMarker; - - std::vector m_bSelectedKeys; - - UpdateCallback m_updateCallback; - - CWndGridHelper m_grid; -}; - -#endif // CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyCtrl.cpp b/Code/Editor/Controls/ReflectedPropertyControl/PropertyCtrl.cpp index c228fbda09..68c4acb95e 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyCtrl.cpp +++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyCtrl.cpp @@ -27,7 +27,6 @@ void RegisterReflectedVarHandlers() EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew LocalStringPropertyHandler()); EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew LightAnimationPropertyHandler()); EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew UserPopupWidgetHandler()); - EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew ColorCurveHandler()); EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew FloatCurveHandler()); EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew MotionPropertyWidgetHandler()); } diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.cpp b/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.cpp index b9f7e12e95..2278f55d95 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.cpp +++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.cpp @@ -170,30 +170,3 @@ bool FloatCurveHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, CSpline GUI->SetSpline(reinterpret_cast(instance.m_spline)); return false; } - - -QWidget* ColorCurveHandler::CreateGUI(QWidget *pParent) -{ - CColorGradientCtrl* gradientCtrl = new CColorGradientCtrl(pParent); - //connect(gradientCtrl, &CColorGradientCtrl::change, [gradientCtrl]() - //{ - // EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, gradientCtrl); - //}); - gradientCtrl->SetTimeRange(0, 1); - gradientCtrl->setFixedHeight(36); - return gradientCtrl; - -} - -void ColorCurveHandler::ConsumeAttribute(CColorGradientCtrl*, AZ::u32, AzToolsFramework::PropertyAttributeReader*, const char*) -{} - -void ColorCurveHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, [[maybe_unused]] CColorGradientCtrl* GUI, [[maybe_unused]] property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node) -{} - -bool ColorCurveHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, CColorGradientCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node) -{ - GUI->SetSpline(reinterpret_cast(instance.m_spline)); - return false; -} - diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h b/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h index e63a974c91..5ec24b679d 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h +++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h @@ -16,7 +16,6 @@ #include #include "ReflectedVar.h" #include "Util/VariablePropertyType.h" -#include "Controls/ColorGradientCtrl.h" #include "Controls/SplineCtrl.h" #include #endif @@ -82,17 +81,4 @@ public: void OnSplineChange(CSplineCtrl*); }; -class ColorCurveHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarSpline, CColorGradientCtrl> -{ -public: - AZ_CLASS_ALLOCATOR(ColorCurveHandler, AZ::SystemAllocator, 0); - bool IsDefaultHandler() const override { return false; } - QWidget* CreateGUI(QWidget *pParent) override; - - AZ::u32 GetHandlerName(void) const override { return AZ_CRC("ePropertyColorCurve", 0xa30da4ec); } - - void ConsumeAttribute(CColorGradientCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override; - void WriteGUIValuesIntoProperty(size_t index, CColorGradientCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override; - bool ReadValuesIntoGUI(size_t index, CColorGradientCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override; -}; #endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H diff --git a/Code/Editor/editor_lib_files.cmake b/Code/Editor/editor_lib_files.cmake index 031ad26d76..47b69765ba 100644 --- a/Code/Editor/editor_lib_files.cmake +++ b/Code/Editor/editor_lib_files.cmake @@ -330,8 +330,6 @@ set(FILES Commands/CommandManager.h Controls/BitmapToolTip.cpp Controls/BitmapToolTip.h - Controls/ColorGradientCtrl.cpp - Controls/ColorGradientCtrl.h Controls/ConsoleSCB.cpp Controls/ConsoleSCB.h Controls/ConsoleSCB.ui From a33ab6712549c11256d5ecba4a9c004860add9c4 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Thu, 21 Oct 2021 08:58:14 -0700 Subject: [PATCH 084/152] bugfix: handle moving files for inode-watch for AssetProcessor (#4656) (#4809) Signed-off-by: Michael Pollind --- .../Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp b/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp index ea458ba826..2d3e671999 100644 --- a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp +++ b/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp @@ -72,7 +72,7 @@ struct FolderRootWatch::PlatformImplementation // Add the folder to watch and track it int watchHandle = inotify_add_watch(m_iNotifyHandle, cleanPath.toUtf8().constData(), - IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY); + IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE); if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout)) { @@ -95,7 +95,7 @@ struct FolderRootWatch::PlatformImplementation int watchHandle = inotify_add_watch(m_iNotifyHandle, dirName.toUtf8().constData(), - IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY); + IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE); if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout)) { From 7a14a21377537a47cb392f3f888892b726c2b36f Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 21 Oct 2021 10:59:42 -0500 Subject: [PATCH 085/152] Fixed create-project/create-gem to use relative paths directly (#4811) * Fixed create-project/create-gem to use relative paths directly It was previously making relative paths, relative to the default_projects_folder and default_gems_folders Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed uppercase of "Templates" param in get_registered call. That method looks up the restricted template directory location based on the lowercase key of "templates" Updated argparse doc to fix typos or clarify which directories a path is relative to. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- scripts/o3de/o3de/engine_template.py | 86 ++++++++++++---------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/scripts/o3de/o3de/engine_template.py b/scripts/o3de/o3de/engine_template.py index cba7539aaa..e93f972301 100755 --- a/scripts/o3de/o3de/engine_template.py +++ b/scripts/o3de/o3de/engine_template.py @@ -407,15 +407,12 @@ def create_template(source_path: pathlib.Path, source_name = os.path.basename(source_path) sanitized_source_name = utils.sanitize_identifier_for_cpp(source_name) - # if no template path, error + # if no template path, use default_templates_folder path if not template_path: - logger.info(f'Template path empty. Using source name {source_name}') - template_path = pathlib.Path(source_name) - if not template_path.is_absolute(): default_templates_folder = manifest.get_registered(default_folder='templates') - template_path = default_templates_folder / template_path - logger.info(f'Template path not a full path. Using default templates folder {template_path}') - if not force and template_path.is_dir(): + template_path = default_templates_folder / source_name + logger.info(f'Template path empty. Using default templates folder {template_path}') + if not force and template_path.is_dir() and len(list(template_path.iterdir())): logger.error(f'Template path {template_path} already exists.') return 1 @@ -1105,7 +1102,7 @@ def create_from_template(destination_path: pathlib.Path, logger.error(f'Could not find the template {template_name}=>{template_path}') return 1 - # the template.json should be in the template_path, make sure it's there a nd valid + # the template.json should be in the template_path, make sure it is valid template_json = template_path / 'template.json' if not validation.valid_o3de_template_json(template_json): logger.error(f'Template json {template_path} is invalid.') @@ -1254,7 +1251,7 @@ def create_from_template(destination_path: pathlib.Path, # destination restricted path elif destination_restricted_path: if os.path.isabs(destination_restricted_path): - restricted_default_path = manifest.get_registered(default='restricted') + restricted_default_path = manifest.get_registered(default_folder='restricted') new_destination_restricted_path = restricted_default_path / destination_restricted_path logger.info(f'{destination_restricted_path} is not a full path, making it relative' f' to default restricted path = {new_destination_restricted_path}') @@ -1346,7 +1343,7 @@ def create_project(project_path: pathlib.Path, Template instantiation specialization that makes all default assumptions for a Project template instantiation, reducing the effort needed in instancing a project :param project_path: the project path, can be absolute or relative to default projects path - :param project_name: the project name, defaults to project_path basename if not provided + :param project_name: the project name, defaults to project_path basename if not provided :param template_path: the path to the template you want to instance, can be absolute or relative to default templates path :param template_name: the name the registered template you want to instance, defaults to DefaultProject, resolves template_path :param project_restricted_path: path to the projects restricted folder, can be absolute or relative to the restricted='projects' @@ -1523,13 +1520,9 @@ def create_project(project_path: pathlib.Path, if not project_path: logger.error('Project path cannot be empty.') return 1 - if not os.path.isabs(project_path): - default_projects_folder = manifest.get_registered(default_folder='projects') - new_project_path = default_projects_folder / project_path - logger.info(f'Project Path {project_path} is not a full path, we must assume its relative' - f' to default projects path = {new_project_path}') - project_path = new_project_path - if not force and os.path.isdir(project_path) and len(os.listdir(project_path)) > 0: + + project_path = project_path.resolve() + if not force and project_path.is_dir() and len(list(project_path.iterdir())): logger.error(f'Project path {project_path} already exists and is not empty.') return 1 elif not os.path.isdir(project_path): @@ -1904,14 +1897,10 @@ def create_gem(gem_path: pathlib.Path, if not gem_path: logger.error('Gem path cannot be empty.') return 1 - if not os.path.isabs(gem_path): - default_gems_folder = manifest.get_registered(default_folder='gems') - new_gem_path = default_gems_folder / gem_path - logger.info(f'Gem Path {gem_path} is not a full path, we must assume its relative' - f' to default gems path = {new_gem_path}') - gem_path = new_gem_path - if not force and os.path.isdir(gem_path): - logger.error(f'Gem path {gem_path} already exists.') + + gem_path = gem_path.resolve() + if not force and gem_path.is_dir() and len(list(gem_path.iterdir())): + logger.error(f'Gem path {gem_path} already exists and is not empty.') return 1 else: os.makedirs(gem_path, exist_ok=force) @@ -1936,16 +1925,18 @@ def create_gem(gem_path: pathlib.Path, # gem restricted path elif gem_restricted_path: if not os.path.isabs(gem_restricted_path): - default_gems_restricted_folder = manifest.get_registered(restricted_name='gems') - new_gem_restricted_path = default_gems_restricted_folder /gem_restricted_path - logger.info(f'Gem restricted path {gem_restricted_path} is not a full path, we must assume its' - f' relative to default gems restricted path = {new_gem_restricted_path}') - gem_restricted_path = new_gem_restricted_path - elif template_restricted_path: + gem_restricted_default_path = manifest.get_registered(restricted_name='gems') + if gem_restricted_default_path: + new_gem_restricted_path = gem_restricted_default_path / gem_restricted_path + logger.info(f'Gem restricted path {gem_restricted_path} is not a full path, we must assume its' + f' relative to default gems restricted path = {new_gem_restricted_path}') + gem_restricted_path = new_gem_restricted_path + else: gem_restricted_default_path = manifest.get_registered(restricted_name='gems') - logger.info(f'--gem-restricted-path is not specified, using default gem restricted path / gem name' - f' = {gem_restricted_default_path}') - gem_restricted_path = gem_restricted_default_path + if gem_restricted_default_path: + logger.info(f'--gem-restricted-path is not specified, using default / ' + f' = {gem_restricted_default_path}') + gem_restricted_path = gem_restricted_default_path / gem_name # gem restricted relative if not gem_restricted_platform_relative_path: @@ -1964,7 +1955,7 @@ def create_gem(gem_path: pathlib.Path, replacements.append(("${NameUpper}", gem_name.upper())) replacements.append(("${NameLower}", gem_name.lower())) replacements.append(("${SanitizedCppName}", sanitized_cpp_name)) - + # module id is a uuid with { and - if module_id: @@ -2244,14 +2235,14 @@ def add_args(subparsers) -> None: create_from_template_subparser = subparsers.add_parser('create-from-template') create_from_template_subparser.add_argument('-dp', '--destination-path', type=pathlib.Path, required=True, help='The path to where you want the template instantiated,' - ' can be absolute or dev root relative.' + ' can be absolute or relative to the current working directory.' 'Ex. C:/o3de/Test' 'Test = ') group = create_from_template_subparser.add_mutually_exclusive_group(required=True) group.add_argument('-tp', '--template-path', type=pathlib.Path, required=False, help='The path to the template you want to instantiate, can be absolute' - ' or dev root/Templates relative.' + ' or relative to the current working directory.' 'Ex. C:/o3de/Template/TestTemplate' 'TestTemplate = ') group.add_argument('-tn', '--template-name', type=str, required=False, @@ -2327,7 +2318,7 @@ def add_args(subparsers) -> None: create_project_subparser = subparsers.add_parser('create-project') create_project_subparser.add_argument('-pp', '--project-path', type=pathlib.Path, required=True, help='The location of the project you wish to create from the template,' - ' can be an absolute path or dev root relative.' + ' can be an absolute path or relative to the current working directory.' ' Ex. C:/o3de/TestProject' ' TestProject = if --project-name not provided') create_project_subparser.add_argument('-pn', '--project-name', type=str, required=False, @@ -2349,8 +2340,8 @@ def add_args(subparsers) -> None: group = create_project_subparser.add_mutually_exclusive_group(required=False) group.add_argument('-prp', '--project-restricted-path', type=pathlib.Path, required=False, default=None, - help='path to the projects restricted folder, can be absolute or relative' - ' to the restricted="projects"') + help='path to the projects restricted folder, can be absolute or relative to' + ' the default restricted projects directory') group.add_argument('-prn', '--project-restricted-name', type=str, required=False, default=None, help='The name of the registered projects restricted path. If supplied this will resolve' @@ -2360,7 +2351,7 @@ def add_args(subparsers) -> None: group.add_argument('-trp', '--template-restricted-path', type=pathlib.Path, required=False, default=None, help='The templates restricted path can be absolute or relative to' - ' restricted="templates"') + 'the default restricted templates directory') group.add_argument('-trn', '--template-restricted-name', type=str, required=False, default=None, help='The name of the registered templates restricted path. If supplied this will resolve' @@ -2423,7 +2414,7 @@ def add_args(subparsers) -> None: # creation of a gem from a template (like create from template but makes gem assumptions) create_gem_subparser = subparsers.add_parser('create-gem') create_gem_subparser.add_argument('-gp', '--gem-path', type=pathlib.Path, required=True, - help='The gem path, can be absolute or relative to default gems path') + help='The gem path, can be absolute or relative to the current working directory') create_gem_subparser.add_argument('-gn', '--gem-name', type=str, help='The name to use when substituting the ${Name} placeholder for the gem,' ' must be alphanumeric, ' @@ -2444,19 +2435,18 @@ def add_args(subparsers) -> None: group = create_gem_subparser.add_mutually_exclusive_group(required=False) group.add_argument('-grp', '--gem-restricted-path', type=pathlib.Path, required=False, default=None, - help='The path to the gem restricted to write to folder if any, can be' - 'absolute or dev root relative, default is dev root/restricted.') + help='The gem restricted path, can be absolute or relative to' + ' the default restricted gems directory') group.add_argument('-grn', '--gem-restricted-name', type=str, required=False, default=None, - help='The path to the gem restricted to write to folder if any, can be' - 'absolute or dev root relative, default is dev root/restricted. If supplied' - ' this will resolve the --gem-restricted-path.') + help='The name of the gem to look up the gem restricted path if any.' + 'If supplied this will resolve the --gem-restricted-path.') group = create_gem_subparser.add_mutually_exclusive_group(required=False) group.add_argument('-trp', '--template-restricted-path', type=pathlib.Path, required=False, default=None, help='The templates restricted path, can be absolute or relative to' - ' the restricted="templates"') + ' the default restricted templates directory') group.add_argument('-trn', '--template-restricted-name', type=str, required=False, default=None, help='The name of the registered templates restricted path. If supplied' From ab86c9961e48c03b3180dac323ba118f6dfe6fe9 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Thu, 21 Oct 2021 09:09:56 -0700 Subject: [PATCH 086/152] [Linux] Fix deadlock when running `LaunchProcess()` from a thread (#4833) `LaunchProcess()` on Linux works by calling `fork` then `execvpe`. `fork` is used to copy a running process, generating a new child process. The new child starts running from the location where the parent was running, from whatever thread from the parent called `fork`. The child process only gets one thread, however. If a different thread in the parent process had locked a mutex, that mutex is also locked in the child process. Since that separate thread is not present in the child, the mutex remains locked in the child, with no way to unlock it. So it is important that as little work as possible happens between the call to `fork` and to `execvpe`. Previously, this code was trying to report an error that may have occurred from calling `execvpe`. It was doing that by calling `AZ_TracePrintf`. That function does lots of things, including trying to make an EBus call, which looks up a variable in the `AZ::Environment` instance, which has a global mutex. If there was some other thread that had that mutex locked when the `fork` call was made, the subprocess would deadlock, and the parent process would also deadlock waiting for the child to finish. This solves that issue by removing the call to `AZ_TracePrintf` from the subprocess code path. Instead, the parent process sets up a pipe for the child process to write to in case the call to `execvpe` fails (the self-pipe trick). The parent then reads from that pipe. If it reads no data, `execvpe` worked and there's no error. If it does read data, the data to be read is the errno from the failed `execvpe` call made by the child. The parent can then use `strerror()` to report the error. Fixes #4702. Signed-off-by: Chris Burel --- .../AzFramework/Process/ProcessWatcher.cpp | 4 +- .../Process/ProcessWatcher_Linux.cpp | 95 ++++++++++++++----- 2 files changed, 73 insertions(+), 26 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Process/ProcessWatcher.cpp b/Code/Framework/AzFramework/AzFramework/Process/ProcessWatcher.cpp index 3ba7ed7223..798b1fe99f 100644 --- a/Code/Framework/AzFramework/AzFramework/Process/ProcessWatcher.cpp +++ b/Code/Framework/AzFramework/AzFramework/Process/ProcessWatcher.cpp @@ -22,7 +22,7 @@ namespace AzFramework AZStd::scoped_ptr pWatcher(LaunchProcess(processLaunchInfo, communicationType)); if (!pWatcher) { - AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: Unable to launch process '%s %s'", processLaunchInfo.m_processExecutableString.c_str(), processLaunchInfo.m_commandlineParameters.c_str()); + AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: Unable to launch process '%s %s'\n", processLaunchInfo.m_processExecutableString.c_str(), processLaunchInfo.m_commandlineParameters.c_str()); return false; } else @@ -31,7 +31,7 @@ namespace AzFramework ProcessCommunicator* pCommunicator = pWatcher->GetCommunicator(); if (!pCommunicator || !pCommunicator->IsValid()) { - AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: No communicator for watcher's process (%s %s)!", processLaunchInfo.m_processExecutableString.c_str(), processLaunchInfo.m_commandlineParameters.c_str()); + AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: No communicator for watcher's process (%s %s)!\n", processLaunchInfo.m_processExecutableString.c_str(), processLaunchInfo.m_commandlineParameters.c_str()); return false; } else diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Process/ProcessWatcher_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Process/ProcessWatcher_Linux.cpp index d2cd681012..51a8545443 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Process/ProcessWatcher_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Process/ProcessWatcher_Linux.cpp @@ -91,31 +91,42 @@ namespace AzFramework return processId == 0; } - /*! Executes a command in the child process after the fork operation has been executed. - * This function will never return. If the execvp command fails this will call _exit with - * the errno value as the return value since continuing execution after a execvp command - * is invalid (it will be running the parent's code and in its address space and will - * cause many issues). + /*! Executes a command in the child process after the fork operation + * has been executed. This function will never return. If the execvpe + * command fails this will call _exit since continuing execution after + * a execvpe command is invalid (it will be running the parent's code + * and in its address space and will cause many issues). + * + * This function runs after a `fork()` call. `fork()` creates a copy of + * the current process, including the current state of the process's + * memory, at the time the call is made. However, it only creates a + * copy of the one thread that called `fork()`. This means that if any + * mutexes are locked by other threads at the time of `fork()`, those + * mutexes will remain locked in the child process, with no way to + * unlock them. So this function needs to ensure that it does as little + * work as possible. * * \param commandAndArgs - Array of strings that has the command to execute in index 0 with any args for the command following. Last element must be a null pointer. - * \param envionrmentVariables - Array of strings that contains environment variables that command should use. Last element must be a null pointer. + * \param environmentVariables - Array of strings that contains environment variables that command should use. Last element must be a null pointer. * \param processLaunchInfo - struct containing information about luanching the command * \param startupInfo - struct containing information needed to startup the command + * \param errorPipe - a pipe file descriptor used to communicate a failed execvpe call's error code to the parent process */ - void ExecuteCommandAsChild(char** commandAndArgs, char** environmentVariables, const ProcessLauncher::ProcessLaunchInfo& processLaunchInfo, StartupInfo& startupInfo) + [[noreturn]] static void ExecuteCommandAsChild(char** commandAndArgs, char** environmentVariables, const ProcessLauncher::ProcessLaunchInfo& processLaunchInfo, StartupInfo& startupInfo, const AZStd::array& errorPipe) { + close(errorPipe[0]); + if (!processLaunchInfo.m_workingDirectory.empty()) { int res = chdir(processLaunchInfo.m_workingDirectory.c_str()); if (res != 0) { - std::cerr << strerror(errno) << std::endl; - AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: Unable to change the launched process' directory to '%s'.", processLaunchInfo.m_workingDirectory.c_str()); + write(errorPipe[1], &errno, sizeof(int)); // We *have* to _exit as we are the child process and simply // returning at this point would mean we would start running // the code from our parent process and that will just wreck // havoc. - _exit(errno); + _exit(0); } } @@ -135,15 +146,17 @@ namespace AzFramework startupInfo.SetupHandlesForChildProcess(); - execve(commandAndArgs[0], commandAndArgs, environmentVariables); + execvpe(commandAndArgs[0], commandAndArgs, environmentVariables); + const int errval = errno; - // If we get here then execve failed to run the requested program and + // If we get here then execvpe failed to run the requested program and // we have an error. In this case we need to exit the child process - // to stop it from continuing to run as a clone of the parent - AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: Unable to launch process %s : errno = %s ", commandAndArgs[0], strerror(errno)); - std::cerr << strerror(errno) << std::endl; + // to stop it from continuing to run as a clone of the parent. + // Communicate the error code back to the parent via a pipe for the + // parent to read. + write(errorPipe[1], &errval, sizeof(errval)); - _exit(errno); + _exit(0); } } @@ -212,9 +225,8 @@ namespace AzFramework AZStd::string outputString; bool inQuotes = false; - for (size_t pos = 0; pos < processLaunchInfo.m_commandlineParameters.size(); ++pos) + for (const char currentChar : processLaunchInfo.m_commandlineParameters) { - char currentChar = processLaunchInfo.m_commandlineParameters[pos]; if (currentChar == '"') { inQuotes = !inQuotes; @@ -231,7 +243,7 @@ namespace AzFramework outputString.push_back(currentChar); } } - + if (!outputString.empty()) { commandTokens.push_back(outputString); @@ -249,10 +261,10 @@ namespace AzFramework return false; } - // Because of the way execve is defined we need to copy the strings from + // Because of the way execvpe is defined we need to copy the strings from // AZ::string (using c_str() returns a const char*) into a non-const char* - // Need to add one more as exec requires the array's last element to be a null pointer + // Need to add one more as execvpe requires the array's last element to be a null pointer char** commandAndArgs = new char*[commandTokens.size() + 1]; for (int i = 0; i < commandTokens.size(); ++i) { @@ -275,7 +287,7 @@ namespace AzFramework azstrcat(environmentVariable.get(), envVarString.size() + 1, envVarString.c_str()); environmentVariablesVector.emplace_back(environmentVariable.get()); } - // Adding one more as exec expects the array to have a nullptr as the last element + // Adding one more as execvpe expects the array to have a nullptr as the last element environmentVariablesVector.emplace_back(nullptr); environmentVariables = environmentVariablesVector.data(); } @@ -288,15 +300,50 @@ namespace AzFramework AZ_Assert(environmentVariables, "Environment variables for current process not available\n"); } + // Set up a pipe to communicate the error code from the subprocess's execvpe call + AZStd::array childErrorPipeFds{}; + pipe(childErrorPipeFds.data()); + + // This configures the write end of the pipe to close on calls to `exec` + fcntl(childErrorPipeFds[1], F_SETFD, fcntl(childErrorPipeFds[1], F_GETFD) | FD_CLOEXEC); + pid_t child_pid = fork(); if (IsIdChildProcess(child_pid)) { - ExecuteCommandAsChild(commandAndArgs, environmentVariables, processLaunchInfo, processData.m_startupInfo); + ExecuteCommandAsChild(commandAndArgs, environmentVariables, processLaunchInfo, processData.m_startupInfo, childErrorPipeFds); } - processData.m_childProcessId = child_pid; // Close these handles as they are only to be used by the child process processData.m_startupInfo.CloseAllHandles(); + close(childErrorPipeFds[1]); + + { + int errorCodeFromChild = 0; + int count = 0; + // Read from the error pipe. + // * If the child's call to execvpe succeeded, then the pipe will + // be closed due to setting FD_CLOEXEC on the write end of the + // pipe. `read()` will return 0. + // * If the child's call to execvpe failed, the child will have + // written the error code to the pipe. `read()` will return >0, and + // the data to be read is the error code from execvpe. + while ((count = read(childErrorPipeFds[0], &errorCodeFromChild, sizeof(errorCodeFromChild))) == -1) + { + if (errno != EAGAIN && errno != EINTR) + { + break; + } + } + if (count) + { + AZ_TracePrintf("Process Watcher", "ProcessLauncher::LaunchProcess: Unable to launch process %s : errno = %s\n", commandAndArgs[0], strerror(errorCodeFromChild)); + processData.m_childProcessIsDone = true; + child_pid = -1; + } + } + close(childErrorPipeFds[0]); + + processData.m_childProcessId = child_pid; for (int i = 0; i < commandTokens.size(); i++) { From 4ed73a0b9136b130bc707937207b3e497d01e825 Mon Sep 17 00:00:00 2001 From: srikappa-amzn Date: Thu, 21 Oct 2021 09:11:11 -0700 Subject: [PATCH 087/152] Use case insensitive folder path comparison for creating slices Signed-off-by: srikappa-amzn --- .../AzToolsFramework/Slice/SliceUtilities.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp index aca667b9aa..69d4a95b59 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp @@ -2648,16 +2648,14 @@ namespace AzToolsFramework } else { - QString cleanSaveAs(QDir::cleanPath(slicePath)); + AZ::IO::FixedMaxPath lexicallyNormalPath = AZ::IO::PathView(slicePath.toUtf8().constData()).LexicallyNormal(); bool isPathSafeForAssets = false; - for (AZStd::string assetSafeFolder : assetSafeFolders) + for (AZ::IO::Path assetSafeFolder : assetSafeFolders) { - QString cleanAssetSafeFolder(QDir::cleanPath(assetSafeFolder.c_str())); - // Compare using clean paths so slash direction does not matter. - // Note that this comparison is case sensitive because some file systems - // Open 3D Engine supports are case sensitive. - if (cleanSaveAs.startsWith(cleanAssetSafeFolder)) + // Check if the slice path is relative to the safe asset directory. + // The Path classes are being used to make this check case insensitive. + if (lexicallyNormalPath.IsRelativeTo(assetSafeFolder)) { isPathSafeForAssets = true; break; From c871224daee0e5ef6296047593fd390c93353efd Mon Sep 17 00:00:00 2001 From: SJ Date: Thu, 21 Oct 2021 09:13:43 -0700 Subject: [PATCH 088/152] Support for importing Json files (#4609) * Initial support for importing Json files within other Json files Signed-off-by: amzn-sj * Add some test cases for testing/iterating on the Json import work. Fix MacOS AzTestRunner module loading bug. Signed-off-by: amzn-sj * The import resolver can take the allocator as a parameter to Load/StoreImports() instead of storing a copy. Signed-off-by: amzn-sj * Fix assert Signed-off-by: amzn-sj * Some rework of the JsonImport feature. Base test cases pass. More complex test cases need to be added. Signed-off-by: amzn-sj * 1. Add test case for testing nested imports. 2. Initialize rapidjson value to fix assert. 3. Fix bug found in merge patch creation. Signed-off-by: amzn-sj * 1. Update the Resolver class member functions to return proper result codes. 2. Add the wrapper functions for resolving/restoring imports to the JsonSerialization class. 3. Add new test case. Signed-off-by: amzn-sj * Add test cases for import + patches. Fix bug found when patching import. Rename test cases. Signed-off-by: amzn-sj * 1. Add ApplyPatch() function to BaseJsonImporter. 2. Move patch logic out of ResolveImport() and into ApplyPatch() 3. Get rid of the custom RestoreImport implementation in the tests since it was the same as the base version. 4. Add test case for patching nested imports. 5. Update merge patch outcome reporting logic to work for nested object patches. Signed-off-by: amzn-sj * 1. Add a CreatePatch() function to BaseJsonImporter to match the ApplyPatch() function. 2. Reorganize some responsibilities between RestoreImports(), RestoreImport() and CreatePatch() to make ResolveImports() and RestoreImports() more symmetrical. Signed-off-by: amzn-sj * Combine result code in code path where we add empty object to path Signed-off-by: amzn-sj * Add test case for inserting a new import into an existing object. Signed-off-by: amzn-sj * Use == instead of Compare() for comparing file paths. Signed-off-by: amzn-sj * Address some PR feedback. Signed-off-by: amzn-sj * Address additional PR feedback Signed-off-by: amzn-sj * Add missing includes to fix non-unity build Signed-off-by: amzn-sj * Fix build error. Address additional feedback. Signed-off-by: amzn-sj --- .../Serialization/Json/JsonImporter.cpp | 254 +++++++++++ .../AzCore/Serialization/Json/JsonImporter.h | 108 +++++ .../AzCore/Serialization/Json/JsonMerger.cpp | 19 +- .../Serialization/Json/JsonSerialization.cpp | 60 ++- .../Serialization/Json/JsonSerialization.h | 27 +- .../Json/JsonSerializationResult.cpp | 3 + .../Json/JsonSerializationResult.h | 3 +- .../AzCore/AzCore/azcore_files.cmake | 2 + .../Json/TestCases_Importing.cpp | 413 ++++++++++++++++++ .../AzCore/Tests/azcoretests_files.cmake | 1 + .../AzTest/Platform/Mac/Platform_Mac.cpp | 12 +- 11 files changed, 886 insertions(+), 16 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/Serialization/Json/JsonImporter.cpp create mode 100644 Code/Framework/AzCore/AzCore/Serialization/Json/JsonImporter.h create mode 100644 Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Importing.cpp diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonImporter.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonImporter.cpp new file mode 100644 index 0000000000..2c856fb4b0 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonImporter.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AZ +{ + JsonSerializationResult::ResultCode JsonImportResolver::ResolveNestedImports(rapidjson::Value& jsonDoc, + rapidjson::Document::AllocatorType& allocator, ImportPathStack& importPathStack, + JsonImportSettings& settings, const AZ::IO::FixedMaxPath& importPath, StackedString& element) + { + using namespace JsonSerializationResult; + + for (auto& path : importPathStack) + { + if (importPath == path) + { + return settings.m_reporting( + AZStd::string::format("'%s' was already imported in this chain. This indicates a cyclic dependency.", importPath.c_str()), + ResultCode(Tasks::Import, Outcomes::Catastrophic), element); + } + } + + importPathStack.push_back(importPath); + AZ::StackedString importElement(AZ::StackedString::Format::JsonPointer); + JsonImportSettings nestedImportSettings; + nestedImportSettings.m_importer = settings.m_importer; + nestedImportSettings.m_reporting = settings.m_reporting; + nestedImportSettings.m_resolveFlags = ImportTracking::Dependencies; + ResultCode result = ResolveImports(jsonDoc, allocator, importPathStack, nestedImportSettings, importElement); + importPathStack.pop_back(); + + if (result.GetOutcome() == Outcomes::Catastrophic) + { + return result; + } + + return ResultCode(Tasks::Import, Outcomes::Success); + } + + JsonSerializationResult::ResultCode JsonImportResolver::ResolveImports(rapidjson::Value& jsonDoc, + rapidjson::Document::AllocatorType& allocator, ImportPathStack& importPathStack, + JsonImportSettings& settings, StackedString& element) + { + using namespace JsonSerializationResult; + + if (jsonDoc.IsObject()) + { + for (auto& field : jsonDoc.GetObject()) + { + if(strncmp(field.name.GetString(), JsonSerialization::ImportDirectiveIdentifier, field.name.GetStringLength()) == 0) + { + const rapidjson::Value& importDirective = field.value; + AZ::IO::FixedMaxPath importAbsPath = importPathStack.back(); + importAbsPath.RemoveFilename(); + AZStd::string importName; + if (importDirective.IsObject()) + { + auto filenameField = importDirective.FindMember("filename"); + if (filenameField != importDirective.MemberEnd()) + { + importName = AZStd::string(filenameField->value.GetString(), filenameField->value.GetStringLength()); + } + } + else + { + importName = AZStd::string(importDirective.GetString(), importDirective.GetStringLength()); + } + importAbsPath.Append(importName); + + rapidjson::Value patch; + ResultCode resolveResult = settings.m_importer->ResolveImport(&jsonDoc, patch, importDirective, importAbsPath, allocator); + if (resolveResult.GetOutcome() == Outcomes::Catastrophic) + { + return resolveResult; + } + + if ((settings.m_resolveFlags & ImportTracking::Imports) == ImportTracking::Imports) + { + rapidjson::Pointer path(element.Get().data(), element.Get().size()); + settings.m_importer->AddImportDirective(path, importName); + } + if ((settings.m_resolveFlags & ImportTracking::Dependencies) == ImportTracking::Dependencies) + { + settings.m_importer->AddImportedFile(importAbsPath.String()); + } + + ResultCode result = ResolveNestedImports(jsonDoc, allocator, importPathStack, settings, importAbsPath, element); + if (result.GetOutcome() == Outcomes::Catastrophic) + { + return result; + } + settings.m_importer->ApplyPatch(jsonDoc, patch, allocator); + } + else if (field.value.IsObject() || field.value.IsArray()) + { + ScopedStackedString entryName(element, AZStd::string_view(field.name.GetString(), field.name.GetStringLength())); + ResultCode result = ResolveImports(field.value, allocator, importPathStack, settings, element); + if (result.GetOutcome() == Outcomes::Catastrophic) + { + return result; + } + } + } + } + else if(jsonDoc.IsArray()) + { + int index = 0; + for (rapidjson::Value::ValueIterator elem = jsonDoc.Begin(); elem != jsonDoc.End(); ++elem, ++index) + { + if (!elem->IsObject() && !elem->IsArray()) + { + continue; + } + ScopedStackedString entryName(element, index); + ResultCode result = ResolveImports(*elem, allocator, importPathStack, settings, element); + if (result.GetOutcome() == Outcomes::Catastrophic) + { + return result; + } + } + } + + return ResultCode(Tasks::Import, Outcomes::Success); + } + + JsonSerializationResult::ResultCode JsonImportResolver::RestoreImports(rapidjson::Value& jsonDoc, + rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings) + { + using namespace JsonSerializationResult; + + if (jsonDoc.IsObject() || jsonDoc.IsArray()) + { + const BaseJsonImporter::ImportDirectivesList& importDirectives = settings.m_importer->GetImportDirectives(); + for (auto& import : importDirectives) + { + rapidjson::Pointer importPtr = import.first; + rapidjson::Value* currentValue = importPtr.Get(jsonDoc); + + rapidjson::Value importedValue(rapidjson::kObjectType); + importedValue.AddMember(rapidjson::StringRef(JsonSerialization::ImportDirectiveIdentifier), rapidjson::StringRef(import.second.c_str()), allocator); + ResultCode resolveResult = JsonSerialization::ResolveImports(importedValue, allocator, settings); + if (resolveResult.GetOutcome() == Outcomes::Catastrophic) + { + return resolveResult; + } + + rapidjson::Value patch; + settings.m_importer->CreatePatch(patch, importedValue, *currentValue, allocator); + settings.m_importer->RestoreImport(currentValue, patch, allocator, import.second); + } + } + + return ResultCode(Tasks::Import, Outcomes::Success); + } + + JsonSerializationResult::ResultCode BaseJsonImporter::ResolveImport(rapidjson::Value* importPtr, + rapidjson::Value& patch, const rapidjson::Value& importDirective, + const AZ::IO::FixedMaxPath& importedFilePath, rapidjson::Document::AllocatorType& allocator) + { + using namespace JsonSerializationResult; + + auto importedObject = JsonSerializationUtils::ReadJsonFile(importedFilePath.Native()); + if (importedObject.IsSuccess()) + { + rapidjson::Value& importedDoc = importedObject.GetValue(); + + if (importDirective.IsObject()) + { + auto patchField = importDirective.FindMember("patch"); + if (patchField != importDirective.MemberEnd()) + { + patch.CopyFrom(patchField->value, allocator); + } + } + + importPtr->CopyFrom(importedDoc, allocator); + } + else + { + return ResultCode(Tasks::Import, Outcomes::Catastrophic); + } + + return ResultCode(Tasks::Import, Outcomes::Success); + } + + JsonSerializationResult::ResultCode BaseJsonImporter::RestoreImport(rapidjson::Value* importPtr, + rapidjson::Value& patch, rapidjson::Document::AllocatorType& allocator, const AZStd::string& importFilename) + { + using namespace JsonSerializationResult; + + importPtr->SetObject(); + if ((patch.IsObject() && patch.MemberCount() > 0) || (patch.IsArray() && !patch.Empty())) + { + rapidjson::Value importDirective(rapidjson::kObjectType); + importDirective.AddMember(rapidjson::StringRef("filename"), rapidjson::StringRef(importFilename.c_str()), allocator); + importDirective.AddMember(rapidjson::StringRef("patch"), patch, allocator); + importPtr->AddMember(rapidjson::StringRef(JsonSerialization::ImportDirectiveIdentifier), importDirective, allocator); + } + else + { + importPtr->AddMember(rapidjson::StringRef(JsonSerialization::ImportDirectiveIdentifier), rapidjson::StringRef(importFilename.c_str()), allocator); + } + + return ResultCode(Tasks::Import, Outcomes::Success); + } + + JsonSerializationResult::ResultCode BaseJsonImporter::ApplyPatch(rapidjson::Value& target, + const rapidjson::Value& patch, rapidjson::Document::AllocatorType& allocator) + { + using namespace JsonSerializationResult; + + if ((patch.IsObject() && patch.MemberCount() > 0) || (patch.IsArray() && !patch.Empty())) + { + return AZ::JsonSerialization::ApplyPatch(target, allocator, patch, JsonMergeApproach::JsonMergePatch); + } + + return ResultCode(Tasks::Import, Outcomes::Success); + } + + JsonSerializationResult::ResultCode BaseJsonImporter::CreatePatch(rapidjson::Value& patch, + const rapidjson::Value& source, const rapidjson::Value& target, + rapidjson::Document::AllocatorType& allocator) + { + return JsonSerialization::CreatePatch(patch, allocator, source, target, JsonMergeApproach::JsonMergePatch); + } + + void BaseJsonImporter::AddImportDirective(const rapidjson::Pointer& jsonPtr, AZStd::string importFile) + { + m_importDirectives.emplace_back(jsonPtr, AZStd::move(importFile)); + } + + void BaseJsonImporter::AddImportedFile(AZStd::string importedFile) + { + m_importedFiles.insert(AZStd::move(importedFile)); + } + + const BaseJsonImporter::ImportDirectivesList& BaseJsonImporter::GetImportDirectives() + { + return m_importDirectives; + } + + const BaseJsonImporter::ImportedFilesList& BaseJsonImporter::GetImportedFiles() + { + return m_importedFiles; + } +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonImporter.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonImporter.h new file mode 100644 index 0000000000..ef53e265d8 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonImporter.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + struct JsonImportSettings; + + class BaseJsonImporter + { + public: + AZ_RTTI(BaseJsonImporter, "{7B225807-7B43-430F-8B11-C794DCF5ACA5}"); + + using ImportDirectivesList = AZStd::vector>; + using ImportedFilesList = AZStd::unordered_set; + + virtual JsonSerializationResult::ResultCode ResolveImport(rapidjson::Value* importPtr, + rapidjson::Value& patch, const rapidjson::Value& importDirective, + const AZ::IO::FixedMaxPath& importedFilePath, rapidjson::Document::AllocatorType& allocator); + + virtual JsonSerializationResult::ResultCode RestoreImport(rapidjson::Value* importPtr, + rapidjson::Value& patch, rapidjson::Document::AllocatorType& allocator, + const AZStd::string& importFilename); + + virtual JsonSerializationResult::ResultCode ApplyPatch(rapidjson::Value& target, + const rapidjson::Value& patch, rapidjson::Document::AllocatorType& allocator); + + virtual JsonSerializationResult::ResultCode CreatePatch(rapidjson::Value& patch, + const rapidjson::Value& source, const rapidjson::Value& target, + rapidjson::Document::AllocatorType& allocator); + + void AddImportDirective(const rapidjson::Pointer& jsonPtr, AZStd::string importFile); + const ImportDirectivesList& GetImportDirectives(); + + void AddImportedFile(AZStd::string importedFile); + const ImportedFilesList& GetImportedFiles(); + + virtual ~BaseJsonImporter() = default; + + protected: + + ImportDirectivesList m_importDirectives; + ImportedFilesList m_importedFiles; + }; + + enum class ImportTracking : AZ::u8 + { + None = 0, + Dependencies = (1<<0), + Imports = (1<<1), + All = (Dependencies | Imports) + }; + AZ_DEFINE_ENUM_BITWISE_OPERATORS(ImportTracking); + + class JsonImportResolver final + { + public: + + using ImportPathStack = AZStd::vector; + + JsonImportResolver() = delete; + JsonImportResolver& operator=(const JsonImportResolver& rhs) = delete; + JsonImportResolver& operator=(JsonImportResolver&& rhs) = delete; + JsonImportResolver(const JsonImportResolver& rhs) = delete; + JsonImportResolver(JsonImportResolver&& rhs) = delete; + ~JsonImportResolver() = delete; + + static JsonSerializationResult::ResultCode ResolveImports(rapidjson::Value& jsonDoc, + rapidjson::Document::AllocatorType& allocator, ImportPathStack& importPathStack, + JsonImportSettings& settings, StackedString& element); + + static JsonSerializationResult::ResultCode RestoreImports(rapidjson::Value& jsonDoc, + rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings); + + private: + + static JsonSerializationResult::ResultCode ResolveNestedImports(rapidjson::Value& jsonDoc, + rapidjson::Document::AllocatorType& allocator, ImportPathStack& importPathStack, + JsonImportSettings& settings, const AZ::IO::FixedMaxPath& importPath, StackedString& element); + }; + + + struct JsonImportSettings final + { + JsonSerializationResult::JsonIssueCallback m_reporting; + + BaseJsonImporter* m_importer = nullptr; + + ImportTracking m_resolveFlags = ImportTracking::All; + + AZ::IO::FixedMaxPath m_loadedJsonPath; + }; +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonMerger.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonMerger.cpp index 45549b8078..a9b2d2fefa 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonMerger.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonMerger.cpp @@ -706,7 +706,7 @@ namespace AZ rapidjson::Value(rapidjson::kNullType), field.value, element, settings); } - if (result.GetOutcome() == Outcomes::Success) + if (result.GetOutcome() == Outcomes::Success || result.GetOutcome() == Outcomes::PartialDefaults) { rapidjson::Value name; name.CopyFrom(field.name, allocator, true); @@ -717,6 +717,10 @@ namespace AZ { return result; } + else + { + resultCode.Combine(result); + } } // Do an extra pass to find all the fields that are removed. @@ -751,7 +755,7 @@ namespace AZ rapidjson::Value value; ResultCode result = CreateMergePatchInternal(value, allocator, rapidjson::Value(rapidjson::kNullType), field.value, element, settings); - if (result.GetOutcome() == Outcomes::Success) + if (result.GetOutcome() == Outcomes::Success || result.GetOutcome() == Outcomes::PartialDefaults) { rapidjson::Value name; name.CopyFrom(field.name, allocator, true); @@ -762,11 +766,20 @@ namespace AZ { return result; } + else + { + resultCode.Combine(result); + } + } + + if (target.MemberCount() == 0) + { + resultCode.Combine(settings.m_reporting("Added empty object to JSON Merge Patch.", + ResultCode(Tasks::CreatePatch, Outcomes::Success), element)); } } patch = AZStd::move(resultValue); - resultCode.Combine(ResultCode(Tasks::CreatePatch, Outcomes::Success)); return resultCode; } else diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp index bc07f684f6..db76e46f2b 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,11 +20,6 @@ namespace AZ { - const char* JsonSerialization::TypeIdFieldIdentifier = "$type"; - const char* JsonSerialization::DefaultStringIdentifier = "{}"; - const char* JsonSerialization::KeyFieldIdentifier = "Key"; - const char* JsonSerialization::ValueFieldIdentifier = "Value"; - namespace JsonSerializationInternal { template @@ -394,6 +390,60 @@ namespace AZ } } + JsonSerializationResult::ResultCode JsonSerialization::ResolveImports( + rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings) + { + using namespace JsonSerializationResult; + + if (settings.m_importer == nullptr) + { + AZ_Assert(false, "Importer object needs to be provided"); + return ResultCode(Tasks::Import, Outcomes::Catastrophic); + } + + AZStd::string scratchBuffer; + auto issueReportingCallback = [&scratchBuffer](AZStd::string_view message, ResultCode result, AZStd::string_view target) -> ResultCode + { + return JsonSerialization::DefaultIssueReporter(scratchBuffer, message, result, target); + }; + if (!settings.m_reporting) + { + settings.m_reporting = issueReportingCallback; + } + + JsonImportResolver::ImportPathStack importPathStack; + importPathStack.push_back(settings.m_loadedJsonPath); + StackedString element(StackedString::Format::JsonPointer); + + return JsonImportResolver::ResolveImports(jsonDoc, allocator, importPathStack, settings, element); + } + + JsonSerializationResult::ResultCode JsonSerialization::RestoreImports( + rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings) + { + using namespace JsonSerializationResult; + + if (settings.m_importer == nullptr) + { + AZ_Assert(false, "Importer object needs to be provided"); + return ResultCode(Tasks::Import, Outcomes::Catastrophic); + } + + AZStd::string scratchBuffer; + auto issueReportingCallback = [&scratchBuffer](AZStd::string_view message, ResultCode result, AZStd::string_view target) -> ResultCode + { + return JsonSerialization::DefaultIssueReporter(scratchBuffer, message, result, target); + }; + if (!settings.m_reporting) + { + settings.m_reporting = issueReportingCallback; + } + + settings.m_resolveFlags = ImportTracking::None; + + return JsonImportResolver::RestoreImports(jsonDoc, allocator, settings); + } + JsonSerializationResult::ResultCode JsonSerialization::DefaultIssueReporter(AZStd::string& scratchBuffer, AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view path) { diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.h index c85847ac78..d961953a1d 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.h @@ -18,6 +18,8 @@ namespace AZ { class BaseJsonSerializer; + + struct JsonImportSettings; enum class JsonMergeApproach { @@ -51,10 +53,11 @@ namespace AZ class JsonSerialization final { public: - static const char* TypeIdFieldIdentifier; - static const char* DefaultStringIdentifier; - static const char* KeyFieldIdentifier; - static const char* ValueFieldIdentifier; + static constexpr const char* TypeIdFieldIdentifier = "$type"; + static constexpr const char* DefaultStringIdentifier = "{}"; + static constexpr const char* KeyFieldIdentifier = "Key"; + static constexpr const char* ValueFieldIdentifier = "Value"; + static constexpr const char* ImportDirectiveIdentifier = "$import"; //! Merges two json values together by applying "patch" to "target" using the selected merge algorithm. //! This version of ApplyPatch is destructive to "target". If the patch can't be correctly applied it will @@ -284,6 +287,22 @@ namespace AZ //! @return An enum containing less, equal or greater. In case of an error, the value for the enum will "error". static JsonSerializerCompareResult Compare(const rapidjson::Value& lhs, const rapidjson::Value& rhs); + //! Resolves all import directives, including nested imports, in the given document. An importer object needs to be passed + //! in through the settings. + //! @param jsonDoc The json document in which to resolve imports. + //! @param allocator The allocator associated with the json document. + //! @param settings Additional settings that control the way the imports are resolved. + static JsonSerializationResult::ResultCode ResolveImports( + rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings); + + //! Restores all import directives that were present in the json document. The same importer object that was + //! passed into ResolveImports through the settings needs to be passed here through settings as well. + //! @param jsonDoc The json document in which to restore imports. + //! @param allocator The allocator associated with the json document. + //! @param settings Additional settings that control the way the imports are restored. + static JsonSerializationResult::ResultCode RestoreImports( + rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings); + private: JsonSerialization() = delete; ~JsonSerialization() = delete; diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializationResult.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializationResult.cpp index 7e84aced7b..822c1c43d5 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializationResult.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializationResult.cpp @@ -69,6 +69,9 @@ namespace AZ case Tasks::CreatePatch: target.append("a create patch operation "); break; + case Tasks::Import: + target.append("an import operation"); + break; default: target.append("an unknown operation "); break; diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializationResult.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializationResult.h index 8590971a1c..204c40b8ca 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializationResult.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializationResult.h @@ -32,7 +32,8 @@ namespace AZ ReadField, //!< Task to read a field from JSON to a value. WriteValue, //!< Task to write a value to a JSON field. Merge, //!< Task to merge two JSON values/documents together. - CreatePatch //!< Task to create a patch to transform one value/document to another. + CreatePatch, //!< Task to create a patch to transform one value/document to another. + Import //!< Task to import a JSON document. }; //! Describes how the task was processed. diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 4d95ddf098..41229429f2 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -522,6 +522,8 @@ set(FILES Serialization/Json/IntSerializer.cpp Serialization/Json/JsonDeserializer.h Serialization/Json/JsonDeserializer.cpp + Serialization/Json/JsonImporter.cpp + Serialization/Json/JsonImporter.h Serialization/Json/JsonMerger.h Serialization/Json/JsonMerger.cpp Serialization/Json/JsonSerialization.h diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Importing.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Importing.cpp new file mode 100644 index 0000000000..5121efa5e3 --- /dev/null +++ b/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Importing.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace JsonSerializationTests +{ + class JsonImportingTests; + + class JsonImporterCustom + : public AZ::BaseJsonImporter + { + public: + AZ_RTTI(JsonImporterCustom, "{003F5896-71E0-4A50-A14F-08C319B06AD0}"); + + + AZ::JsonSerializationResult::ResultCode ResolveImport(rapidjson::Value* importPtr, + rapidjson::Value& patch, const rapidjson::Value& importDirective, + const AZ::IO::FixedMaxPath& importedFilePath, rapidjson::Document::AllocatorType& allocator) override; + + JsonImporterCustom(JsonImportingTests* tests) + { + testClass = tests; + } + + private: + JsonImportingTests* testClass; + }; + + class JsonImportingTests + : public BaseJsonSerializerFixture + { + public: + void SetUp() override + { + BaseJsonSerializerFixture::SetUp(); + } + + void TearDown() override + { + BaseJsonSerializerFixture::TearDown(); + } + + void GetTestDocument(const AZStd::string& docName, rapidjson::Document& out) + { + const char *objectJson = R"({ + "field_1" : "value_1", + "field_2" : "value_2", + "field_3" : "value_3" + })"; + + const char *arrayJson = R"([ + { "element_1" : "value_1" }, + { "element_2" : "value_2" }, + { "element_3" : "value_3" } + ])"; + + const char *nestedImportJson = R"({ + "desc" : "Nested Import", + "obj" : {"$import" : "object.json"} + })"; + + const char *nestedImportCycle1Json = R"({ + "desc" : "Nested Import Cycle 1", + "obj" : {"$import" : "nested_import_c2.json"} + })"; + + const char *nestedImportCycle2Json = R"({ + "desc" : "Nested Import Cycle 2", + "obj" : {"$import" : "nested_import_c1.json"} + })"; + + if (docName.compare("object.json") == 0) + { + out.Parse(objectJson); + ASSERT_FALSE(out.HasParseError()); + } + else if (docName.compare("array.json") == 0) + { + out.Parse(arrayJson); + ASSERT_FALSE(out.HasParseError()); + } + else if (docName.compare("nested_import.json") == 0) + { + out.Parse(nestedImportJson); + ASSERT_FALSE(out.HasParseError()); + } + else if (docName.compare("nested_import_c1.json") == 0) + { + out.Parse(nestedImportCycle1Json); + ASSERT_FALSE(out.HasParseError()); + } + else if (docName.compare("nested_import_c2.json") == 0) + { + out.Parse(nestedImportCycle2Json); + ASSERT_FALSE(out.HasParseError()); + } + } + + protected: + void TestImportLoadStore(const char* input, const char* expectedImportedValue) + { + m_jsonDocument->Parse(input); + ASSERT_FALSE(m_jsonDocument->HasParseError()); + + JsonImporterCustom* importerObj = new JsonImporterCustom(this); + + rapidjson::Document expectedOutcome; + expectedOutcome.Parse(expectedImportedValue); + ASSERT_FALSE(expectedOutcome.HasParseError()); + + TestResolveImports(importerObj); + + Expect_DocStrEq(m_jsonDocument->GetObject(), expectedOutcome.GetObject()); + + rapidjson::Document originalInput; + originalInput.Parse(input); + ASSERT_FALSE(originalInput.HasParseError()); + + TestRestoreImports(importerObj); + + Expect_DocStrEq(m_jsonDocument->GetObject(), originalInput.GetObject()); + + m_jsonDocument->SetObject(); + delete importerObj; + } + + void TestImportCycle(const char* input) + { + m_jsonDocument->Parse(input); + ASSERT_FALSE(m_jsonDocument->HasParseError()); + + JsonImporterCustom* importerObj = new JsonImporterCustom(this); + + AZ::JsonSerializationResult::ResultCode result = TestResolveImports(importerObj); + + EXPECT_EQ(result.GetOutcome(), AZ::JsonSerializationResult::Outcomes::Catastrophic); + + m_jsonDocument->SetObject(); + delete importerObj; + } + + void TestInsertNewImport(const char* input, const char* expectedRestoredValue) + { + m_jsonDocument->Parse(input); + ASSERT_FALSE(m_jsonDocument->HasParseError()); + + JsonImporterCustom* importerObj = new JsonImporterCustom(this); + + TestResolveImports(importerObj); + + importerObj->AddImportDirective(rapidjson::Pointer("/object_2"), "object.json"); + + rapidjson::Document expectedOutput; + expectedOutput.Parse(expectedRestoredValue); + ASSERT_FALSE(expectedOutput.HasParseError()); + + TestRestoreImports(importerObj); + + Expect_DocStrEq(m_jsonDocument->GetObject(), expectedOutput.GetObject()); + + m_jsonDocument->SetObject(); + delete importerObj; + } + + AZ::JsonSerializationResult::ResultCode TestResolveImports(JsonImporterCustom* importerObj) + { + AZ::JsonImportSettings settings; + settings.m_importer = importerObj; + + return AZ::JsonSerialization::ResolveImports(m_jsonDocument->GetObject(), m_jsonDocument->GetAllocator(), settings); + } + + AZ::JsonSerializationResult::ResultCode TestRestoreImports(JsonImporterCustom* importerObj) + { + AZ::JsonImportSettings settings; + settings.m_importer = importerObj; + + return AZ::JsonSerialization::RestoreImports(m_jsonDocument->GetObject(), m_jsonDocument->GetAllocator(), settings); + } + }; + + AZ::JsonSerializationResult::ResultCode JsonImporterCustom::ResolveImport(rapidjson::Value* importPtr, + rapidjson::Value& patch, const rapidjson::Value& importDirective, const AZ::IO::FixedMaxPath& importedFilePath, + rapidjson::Document::AllocatorType& allocator) + { + AZ::JsonSerializationResult::ResultCode resultCode(AZ::JsonSerializationResult::Tasks::Import); + + rapidjson::Document importedDoc; + testClass->GetTestDocument(importedFilePath.String(), importedDoc); + + if (importDirective.IsObject()) + { + auto patchField = importDirective.FindMember("patch"); + if (patchField != importDirective.MemberEnd()) + { + patch.CopyFrom(patchField->value, allocator); + } + } + + importPtr->CopyFrom(importedDoc, allocator); + + return resultCode; + } + + // Test Cases + + TEST_F(JsonImportingTests, ImportSimpleObjectTest) + { + const char* inputFile = R"( + { + "name" : "simple_object_import", + "object": {"$import" : "object.json"} + } + )"; + + const char* expectedOutput = R"( + { + "name" : "simple_object_import", + "object": { + "field_1" : "value_1", + "field_2" : "value_2", + "field_3" : "value_3" + } + } + )"; + + TestImportLoadStore(inputFile, expectedOutput); + } + + TEST_F(JsonImportingTests, ImportSimpleObjectPatchTest) + { + const char* inputFile = R"( + { + "name" : "simple_object_import", + "object": { + "$import" : { + "filename" : "object.json", + "patch" : { "field_2" : "patched_value" } + } + } + } + )"; + + const char* expectedOutput = R"( + { + "name" : "simple_object_import", + "object": { + "field_1" : "value_1", + "field_2" : "patched_value", + "field_3" : "value_3" + } + } + )"; + + TestImportLoadStore(inputFile, expectedOutput); + } + + TEST_F(JsonImportingTests, ImportSimpleArrayTest) + { + const char* inputFile = R"( + { + "name" : "simple_array_import", + "object": {"$import" : "array.json"} + } + )"; + + const char* expectedOutput = R"( + { + "name" : "simple_array_import", + "object": [ + { "element_1" : "value_1" }, + { "element_2" : "value_2" }, + { "element_3" : "value_3" } + ] + } + )"; + + TestImportLoadStore(inputFile, expectedOutput); + } + + TEST_F(JsonImportingTests, ImportSimpleArrayPatchTest) + { + const char* inputFile = R"( + { + "name" : "simple_array_import", + "object": { + "$import" : { + "filename" : "array.json", + "patch" : [ { "element_1" : "patched_value" } ] + } + } + } + )"; + + const char* expectedOutput = R"( + { + "name" : "simple_array_import", + "object": [ + { "element_1" : "patched_value" } + ] + } + )"; + + TestImportLoadStore(inputFile, expectedOutput); + } + + TEST_F(JsonImportingTests, NestedImportTest) + { + const char* inputFile = R"( + { + "name" : "nested_import", + "object": {"$import" : "nested_import.json"} + } + )"; + + const char* expectedOutput = R"( + { + "name" : "nested_import", + "object": { + "desc" : "Nested Import", + "obj" : { + "field_1" : "value_1", + "field_2" : "value_2", + "field_3" : "value_3" + } + } + } + )"; + + TestImportLoadStore(inputFile, expectedOutput); + } + + TEST_F(JsonImportingTests, NestedImportPatchTest) + { + const char* inputFile = R"( + { + "name" : "nested_import", + "object": { + "$import" : { + "filename" : "nested_import.json", + "patch" : { "obj" : { "field_3" : "patched_value" } } + } + } + } + )"; + + const char* expectedOutput = R"( + { + "name" : "nested_import", + "object": { + "desc" : "Nested Import", + "obj" : { + "field_1" : "value_1", + "field_2" : "value_2", + "field_3" : "patched_value" + } + } + } + )"; + + TestImportLoadStore(inputFile, expectedOutput); + } + + TEST_F(JsonImportingTests, NestedImportCycleTest) + { + const char* inputFile = R"( + { + "name" : "nested_import_cycle", + "object": {"$import" : "nested_import_c1.json"} + } + )"; + + TestImportCycle(inputFile); + } + + TEST_F(JsonImportingTests, InsertNewImportTest) + { + const char* inputFile = R"( + { + "name" : "simple_object_import", + "object_1": {"$import" : "object.json"}, + "object_2": { + "field_1" : "other_value", + "field_2" : "value_2", + "field_3" : "value_3" + } + } + )"; + + const char* expectedOutput = R"( + { + "name" : "simple_object_import", + "object_1": {"$import" : "object.json"}, + "object_2": { + "$import" : { + "filename" : "object.json", + "patch" : { "field_1" : "other_value" } + } + } + } + )"; + + TestInsertNewImport(inputFile, expectedOutput); + } +} diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index cc6000209f..d39595c45e 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -121,6 +121,7 @@ set(FILES Serialization/Json/TestCases_Classes.cpp Serialization/Json/TestCases_Compare.cpp Serialization/Json/TestCases_Enum.cpp + Serialization/Json/TestCases_Importing.cpp Serialization/Json/TestCases_Patching.cpp Serialization/Json/TestCases_Pointers.h Serialization/Json/TestCases_Pointers.cpp diff --git a/Code/Framework/AzTest/AzTest/Platform/Mac/Platform_Mac.cpp b/Code/Framework/AzTest/AzTest/Platform/Mac/Platform_Mac.cpp index 864bc3f52a..7581986950 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Mac/Platform_Mac.cpp +++ b/Code/Framework/AzTest/AzTest/Platform/Mac/Platform_Mac.cpp @@ -7,6 +7,7 @@ */ #include #include +#include #include #include @@ -20,11 +21,16 @@ public: explicit ModuleHandle(const std::string& lib) : m_libHandle(nullptr) { - std::string libext = lib; - if (!AZ::Test::EndsWith(libext, ".dylib")) + AZ::IO::FixedMaxPath libext = AZStd::string_view{ lib.c_str(), lib.size() }; + if (!libext.Stem().Native().starts_with(AZ_TRAIT_OS_DYNAMIC_LIBRARY_PREFIX)) { - libext += ".dylib"; + libext = AZ_TRAIT_OS_DYNAMIC_LIBRARY_PREFIX + libext.Native(); } + if (libext.Extension() != AZ_TRAIT_OS_DYNAMIC_LIBRARY_EXTENSION) + { + libext.Native() += AZ_TRAIT_OS_DYNAMIC_LIBRARY_EXTENSION; + } + m_libHandle = dlopen(libext.c_str(), RTLD_NOW); const char* error = dlerror(); if (error) From 3a6cc2498b0551c6948d103b3566d0e50dc7229f Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:16:48 -0500 Subject: [PATCH 089/152] Skipping GraphUpdate test due to LC node issue in Debug configuration (#4847) * Skipping GraphUpdate test due to LC node issue in Debug configuration Signed-off-by: jckand-amzn * Reverting test type for GraphUpdates test to EditorSharedTest Signed-off-by: jckand-amzn * Updating xfail reason for GraphUpdates test with GitHub issue link Signed-off-by: jckand-amzn --- .../largeworlds/landscape_canvas/TestSuite_Main_Optimized.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py index 0461ff2647..1c3652cae9 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py @@ -9,6 +9,7 @@ import os import pytest import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite @@ -79,6 +80,8 @@ class TestAutomation(EditorTestSuite): class test_LandscapeCanvas_GradientNodes_EntityRemovedOnNodeDelete(EditorSharedTest): from .EditorScripts import GradientNodes_EntityRemovedOnNodeDelete as test_module + @pytest.mark.skipif("debug" == os.path.basename(internal_plugin.build_directory), + reason="https://github.com/o3de/o3de/issues/4872") class test_LandscapeCanvas_GraphUpdates_UpdateComponents(EditorSharedTest): from .EditorScripts import GraphUpdates_UpdateComponents as test_module From 2809c3b7ed6f2b4968cb3bea692d1277377d8b3a Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Thu, 21 Oct 2021 09:35:03 -0700 Subject: [PATCH 090/152] Removing unused variables and defines and some renaming Signed-off-by: AMZN-Phil --- Code/Tools/ProjectManager/Source/DownloadController.cpp | 3 --- Code/Tools/ProjectManager/Source/DownloadController.h | 9 ++++++++- Code/Tools/ProjectManager/Source/DownloadWorker.cpp | 4 ---- Code/Tools/ProjectManager/Source/DownloadWorker.h | 5 ----- .../Source/GemCatalog/GemCatalogHeaderWidget.cpp | 8 ++++---- 5 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/DownloadController.cpp b/Code/Tools/ProjectManager/Source/DownloadController.cpp index e06763ed4d..4942f4efb0 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadController.cpp @@ -8,11 +8,8 @@ #include #include -#include #include -#include -#include namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/DownloadController.h b/Code/Tools/ProjectManager/Source/DownloadController.h index a769533c72..2a377f1ce8 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.h +++ b/Code/Tools/ProjectManager/Source/DownloadController.h @@ -41,7 +41,14 @@ namespace O3DE::ProjectManager const QString& GetCurrentDownloadingGem() const { - return m_gemNames[0]; + if (!m_gemNames.empty()) + { + return m_gemNames[0]; + } + else + { + return QString(); + } } public slots: void Start(); diff --git a/Code/Tools/ProjectManager/Source/DownloadWorker.cpp b/Code/Tools/ProjectManager/Source/DownloadWorker.cpp index 954eae432e..9bda1b34cc 100644 --- a/Code/Tools/ProjectManager/Source/DownloadWorker.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadWorker.cpp @@ -10,10 +10,6 @@ #include #include -#include -#include -#include - namespace O3DE::ProjectManager { diff --git a/Code/Tools/ProjectManager/Source/DownloadWorker.h b/Code/Tools/ProjectManager/Source/DownloadWorker.h index 1bd51bca66..316a730a78 100644 --- a/Code/Tools/ProjectManager/Source/DownloadWorker.h +++ b/Code/Tools/ProjectManager/Source/DownloadWorker.h @@ -17,8 +17,6 @@ namespace O3DE::ProjectManager { class DownloadWorker : public QObject { - // QProcess::waitForFinished uses -1 to indicate that the process should not timeout - static constexpr int MaxBuildTimeMSecs = -1; // Download was cancelled inline static const QString DownloadCancelled = QObject::tr("Download Cancelled."); @@ -38,9 +36,6 @@ namespace O3DE::ProjectManager private: - QProcess* m_configProjectProcess = nullptr; - QProcess* m_buildProjectProcess = nullptr; - QString m_gemName; int m_downloadProgress; }; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 9396234a68..79ff7624b7 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -164,11 +164,11 @@ namespace O3DE::ProjectManager layout->setAlignment(Qt::AlignTop); widget->setLayout(layout); - QLabel* label = new QLabel(); - label->setObjectName("GemCatalogCartOverlaySectionLabel"); - layout->addWidget(label); + QLabel* titleLabel = new QLabel(); + titleLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(titleLabel); - label->setText(tr("Gems to be installed")); + titleLabel->setText(tr("Gems to be installed")); // Create header section QWidget* downloadingGemsWidget = new QWidget(); From a032c59eab5acfb081ef13471d063f3206978299 Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Thu, 21 Oct 2021 09:39:15 -0700 Subject: [PATCH 091/152] Removing unused function Signed-off-by: AMZN-Phil --- Code/Tools/ProjectManager/Source/DownloadController.cpp | 5 ----- Code/Tools/ProjectManager/Source/DownloadController.h | 1 - .../ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp | 1 - 3 files changed, 7 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/DownloadController.cpp b/Code/Tools/ProjectManager/Source/DownloadController.cpp index 4942f4efb0..fa3fdb10d1 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadController.cpp @@ -46,11 +46,6 @@ namespace O3DE::ProjectManager } } - void DownloadController::Start() - { - - } - void DownloadController::UpdateUIProgress(int progress) { m_lastProgress = progress; diff --git a/Code/Tools/ProjectManager/Source/DownloadController.h b/Code/Tools/ProjectManager/Source/DownloadController.h index 2a377f1ce8..385ceb4772 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.h +++ b/Code/Tools/ProjectManager/Source/DownloadController.h @@ -51,7 +51,6 @@ namespace O3DE::ProjectManager } } public slots: - void Start(); void UpdateUIProgress(int progress); void HandleResults(const QString& result); void HandleCancel(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 08df6f255f..58f3b48c81 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -34,7 +34,6 @@ namespace O3DE::ProjectManager setLayout(vLayout); m_downloadController = new DownloadController(); - m_downloadController->Start(); m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController); vLayout->addWidget(m_headerWidget); From bc9f6be4cf368e9ceb9672fafce9788df9a1b8ad Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Thu, 21 Oct 2021 10:11:18 -0700 Subject: [PATCH 092/152] Change repo_uri passed into register repo to always be a str Signed-off-by: AMZN-Phil --- scripts/o3de/o3de/register.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index 3c4d69a3c0..0de161177c 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -467,7 +467,7 @@ def register_restricted_path(json_data: dict, def register_repo(json_data: dict, - repo_uri: str or pathlib.Path, + repo_uri: str, remove: bool = False) -> int: if not repo_uri: logger.error(f'Repo URI cannot be empty.') @@ -566,7 +566,7 @@ def register(engine_path: pathlib.Path = None, external_subdir_path: pathlib.Path = None, template_path: pathlib.Path = None, restricted_path: pathlib.Path = None, - repo_uri: str or pathlib.Path = None, + repo_uri: str = None, default_engines_folder: pathlib.Path = None, default_projects_folder: pathlib.Path = None, default_gems_folder: pathlib.Path = None, @@ -641,7 +641,7 @@ def register(engine_path: pathlib.Path = None, return 1 result = result or register_restricted_path(json_data, restricted_path, remove, project_path, engine_path) - if isinstance(repo_uri, str) or isinstance(repo_uri, pathlib.PurePath): + if isinstance(repo_uri, str): if not repo_uri: logger.error(f'Repo URI cannot be empty.') return 1 From e008ba857912eec2093d74b0eaf45a91ae44507e Mon Sep 17 00:00:00 2001 From: Scott Romero <24445312+AMZN-ScottR@users.noreply.github.com> Date: Thu, 21 Oct 2021 10:22:24 -0700 Subject: [PATCH 093/152] [development] Fixed Profiler ImGui module name when building monolithic (#4848) Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Gems/Profiler/Code/Source/ProfilerImGuiModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Profiler/Code/Source/ProfilerImGuiModule.cpp b/Gems/Profiler/Code/Source/ProfilerImGuiModule.cpp index 38cfee2c26..f7a3d4d69d 100644 --- a/Gems/Profiler/Code/Source/ProfilerImGuiModule.cpp +++ b/Gems/Profiler/Code/Source/ProfilerImGuiModule.cpp @@ -46,4 +46,4 @@ namespace Profiler }; }// namespace Profiler -AZ_DECLARE_MODULE_CLASS(Gem_Profiler, Profiler::ProfilerImGuiModule) +AZ_DECLARE_MODULE_CLASS(Gem_ProfilerImGui, Profiler::ProfilerImGuiModule) From f83b6594c9680d54fa82f9ed9ea1cbce03638ba6 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:05:47 -0700 Subject: [PATCH 094/152] Updated MaterialTypeSourceData to force users to move the "version" indicator to the new location at the top level of the json document. (It's a simple enough change to make manually, and making .materialtype is an uncommon workflow, so not worth doing this automatically). Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../ReflectionProbeVisualization.materialtype | 2 +- .../Special/ShadowCatcher.materialtype | 2 +- .../Materials/Types/EnhancedPBR.materialtype | 2 +- .../Assets/Materials/Types/Skin.materialtype | 2 +- .../Types/StandardMultilayerPBR.materialtype | 2 +- .../Materials/Types/StandardPBR.materialtype | 4 +-- .../RPI.Edit/Material/MaterialSourceData.h | 3 ++- .../Material/MaterialTypeSourceData.h | 3 +++ .../RPI.Builders/Material/MaterialBuilder.cpp | 2 +- .../Material/MaterialSourceDataSerializer.cpp | 4 +-- .../Material/MaterialTypeSourceData.cpp | 10 +++++++ .../Material/MaterialTypeSourceDataTests.cpp | 26 +++++++++++++++++++ .../Materials/Types/AutoBrick.materialtype | 2 +- .../Materials/Types/MinimalPBR.materialtype | 2 +- .../Materials/Terrain/PbrTerrain.materialtype | 2 +- .../Terrain/TerrainMacroMaterial.materialtype | 2 +- 16 files changed, 55 insertions(+), 15 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype index 17209771e5..30062205a8 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype @@ -1,7 +1,7 @@ { "description": "Base material for the reflection probe visualization model.", + "version": 1, "propertyLayout": { - "version": 1, "properties": { "general": [ { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.materialtype index 74246f85db..d05f03c9a9 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.materialtype @@ -1,7 +1,7 @@ { "description": "Base material for the reflection probe visualization model.", + "version": 1, "propertyLayout": { - "version": 1, "properties": { "settings": [ { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index bed3b69c4c..f4bcfb2673 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -1,7 +1,7 @@ { "description": "Material Type with properties used to define Enhanced PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model, with advanced features like subsurface scattering, transmission, and anisotropy.", + "version": 3, "propertyLayout": { - "version": 3, "groups": [ { "name": "baseColor", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index e4da9c6022..e2a05aa916 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -1,7 +1,7 @@ { "description": "Material Type tailored for rendering skin, with support for blended wrinkle maps that work with animated vertex blend shapes.", + "version": 3, "propertyLayout": { - "version": 3, "groups": [ { "name": "baseColor", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index 5fa0dcb217..107b525ae4 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -1,7 +1,7 @@ { "description": "Similar to StandardPBR but supports multiple layers blended together.", + "version": 3, "propertyLayout": { - "version": 3, "groups": [ { "name": "blend", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index 6eb82b85ae..84b4c29fcb 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -1,7 +1,7 @@ { "description": "Material Type with properties used to define Standard PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model.", + "version": 3, "propertyLayout": { - "version": 3, "groups": [ { "name": "baseColor", @@ -152,7 +152,7 @@ ], "baseColor": [ { - "name": "color", + "name": "colorX", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index a8bf790ad8..a67477f061 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -73,7 +73,8 @@ namespace AZ //! Checks the material type version and potentially applies a series of property changes (most common are simple property renames) //! based on the MaterialTypeAsset's version update procedure. - ApplyVersionUpdatesResult ApplyVersionUpdates(AZStd::string_view materialSourceFilePath); + //! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for resolving file-relative paths. + ApplyVersionUpdatesResult ApplyVersionUpdates(AZStd::string_view materialSourceFilePath = ""); //! Creates a MaterialAsset from the MaterialSourceData content. //! @param assetId ID for the MaterialAsset diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h index 99eb15db24..1234b15f95 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h @@ -147,6 +147,9 @@ namespace AZ struct PropertyLayout { AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::PropertyLayout, "{AE53CF3F-5C3B-44F5-B2FB-306F0EB06393}"); + + //! This field is unused, and has been replaced by MaterialTypeSourceData::m_version below. It is kept for legacy file compatibility to suppress warnings and errors. + uint32_t m_versionOld = 0; //! List of groups that will contain the available properties AZStd::vector m_groups; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index 02fd11bdff..e9cebf29c7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -47,7 +47,7 @@ namespace AZ { AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor; materialBuilderDescriptor.m_name = JobKey; - materialBuilderDescriptor.m_version = 109; // Changed "id" to "name" in serialization + materialBuilderDescriptor.m_version = 110; // Material version auto update feature materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_busId = azrtti_typeid(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp index cdd434e894..2a504fc345 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp @@ -46,8 +46,8 @@ namespace AZ } result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_description, azrtti_typeid(), inputValue, "description", context)); - result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_materialType, azrtti_typeid(), inputValue, "materialType", context)); result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_parentMaterial, azrtti_typeid(), inputValue, "parentMaterial", context)); + result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_materialType, azrtti_typeid(), inputValue, "materialType", context)); result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_materialTypeVersion, azrtti_typeid(), inputValue, "materialTypeVersion", context)); if (materialSourceData->m_materialType.empty()) @@ -151,8 +151,8 @@ namespace AZ JSR::ResultCode resultCode(JSR::Tasks::ReadField); resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "description", &materialSourceData->m_description, nullptr, azrtti_typeid(), context)); - resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "materialType", &materialSourceData->m_materialType, nullptr, azrtti_typeid(), context)); resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "parentMaterial", &materialSourceData->m_parentMaterial, nullptr, azrtti_typeid(), context)); + resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "materialType", &materialSourceData->m_materialType, nullptr, azrtti_typeid(), context)); resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "materialTypeVersion", &materialSourceData->m_materialTypeVersion, nullptr, azrtti_typeid(), context)); resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "properties", &materialSourceData->m_properties, nullptr, azrtti_typeid(), context)); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 8c8bad8c0c..74250647c3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -86,6 +86,7 @@ namespace AZ serializeContext->Class() ->Version(2) // Material Version Update + ->Field("version", &PropertyLayout::m_versionOld) ->Field("groups", &PropertyLayout::m_groups) ->Field("properties", &PropertyLayout::m_properties) ; @@ -351,6 +352,15 @@ namespace AZ materialTypeAssetCreator.SetElevateWarnings(elevateWarnings); materialTypeAssetCreator.Begin(assetId); + if (m_propertyLayout.m_versionOld != 0) + { + materialTypeAssetCreator.ReportError( + "The field '/propertyLayout/version' is deprecated and moved to '/version'. " + "Please edit this material type source file and move the '\"version\": %u' setting up one level.", + m_propertyLayout.m_versionOld); + return Failure(); + } + // Set materialtype version and add each version update object into MaterialTypeAsset. materialTypeAssetCreator.SetVersion(m_version); { diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp index 709d6b84ec..b811e630d0 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp @@ -1477,4 +1477,30 @@ namespace UnitTest errorMessageFinder.CheckExpectedErrorsFound(); } + + TEST_F(MaterialTypeSourceDataTests, CreateMaterialTypeAsset_Error_VersionInWrongLocation) + { + // The version field used to be under the propertyLayout section, but it has been moved up to the top level. + // If any users have their own custom .materialtype with an older format that has the version in the wrong place + // then we will report an error with instructions to move it to the correct location. + + ErrorMessageFinder errorMessageFinder; + errorMessageFinder.AddExpectedErrorMessage("The field '/propertyLayout/version' is deprecated and moved to '/version'. Please edit this material type source file and move the '\"version\": 4' setting up one level"); + + const AZStd::string inputJson = R"( + { + "propertyLayout": { + "version": 4 + } + } + )"; + + MaterialTypeSourceData materialType; + JsonTestResult loadResult = LoadTestDataFromJson(materialType, inputJson); + + auto materialTypeOutcome = materialType.CreateMaterialTypeAsset(Uuid::CreateRandom()); + EXPECT_FALSE(materialTypeOutcome.IsSuccess()); + + errorMessageFinder.CheckExpectedErrorsFound(); + } } diff --git a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick.materialtype b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick.materialtype index 00f11663f7..cf0bffa058 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick.materialtype +++ b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick.materialtype @@ -1,7 +1,7 @@ { "description": "This is an example of a custom material type using Atom's PBR shading model: procedurally generated brick or tile.", + "version": 3, "propertyLayout": { - "version": 3, "groups": [ { "name": "shape", diff --git a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR.materialtype b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR.materialtype index 81ebd63c28..5d99737576 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR.materialtype +++ b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR.materialtype @@ -1,7 +1,7 @@ { "description": "Base Material with properties used to define Standard PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model.", + "version": 3, "propertyLayout": { - "version": 3, "groups": [ { "name": "settings", diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype index ec04412fe6..01b862c4f8 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype @@ -1,7 +1,7 @@ { "description": "A material for rendering terrain with a physically-based rendering (PBR) material shading model.", + "version": 1, "propertyLayout": { - "version": 1, "groups": [ { "id": "settings", diff --git a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype index 3cdab8da10..17769ffb92 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype @@ -1,7 +1,7 @@ { "description": "A material for providing terrain with low-fidelity color and normals. This material will get blended with surface detail materials.", + "version": 1, "propertyLayout": { - "version": 1, "groups": [ { "name": "baseColor", From 64bbc700fa6be64c6ad572c8b8573e4d882ca69f Mon Sep 17 00:00:00 2001 From: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> Date: Thu, 21 Oct 2021 14:29:53 -0400 Subject: [PATCH 095/152] Hair - ShortCut rendering technique and pipeline with Marschner lighting model (#4871) - This rendering technique reduces the memory required per pipeline by 750MB compared to the PPLL technique!! - Using 3 screen buffers representing the closest 3 hair gragments depths. - Going through second geometry pass that disqualify any fragment further than the third depth, the technique blends the three closes shaders hair fragments. - Supports the Marschner lighting model (big change from the original TressFX 4.1 implementation) - The current technique is almost twice the performance of the PPLL but not quite as advacned when come to final visual quality. Remarks: Unlike the PPLL, this technique is still lacking some of the advanced features added to the PPLL such as 1. Back lobe (TT) conseal by depth comparison 2. Thickness dependency in light transfer (mainly TT) 3. Allowing TT transfer for thin separated hair strands (might be supported by default with no distinction) Signed-off-by: Adi-Amazon Signed-off-by: Adi-Amazon Signed-off-by: Adi-Amazon Signed-off-by: Adi-Amazon Co-authored-by: Adi-Amazon --- .../Passes/AtomTressFX_MainPipeline.pass | 6 +- .../Passes/AtomTressFX_PassTemplates.azasset | 23 ++ .../Assets/Passes/HairParentShortCutPass.pass | 391 ++++++++++++++++++ .../HairShortCutGeometryDepthAlpha.pass | 101 +++++ .../Passes/HairShortCutGeometryShading.pass | 155 +++++++ .../Passes/HairShortCutResolveColor.pass | 45 ++ .../Passes/HairShortCutResolveDepth.pass | 37 ++ .../{HairSrgs.azsli => HairComputeSrgs.azsli} | 2 +- .../Assets/Shaders/HairFullScreenUtils.azsli | 60 +++ .../Assets/Shaders/HairLighting.azsli | 17 +- .../Shaders/HairLightingEquations.azsli | 10 +- .../Assets/Shaders/HairRenderingFillPPLL.azsl | 42 +- .../Shaders/HairRenderingResolvePPLL.azsl | 23 +- .../HairShortCutGeometryDepthAlpha.azsl | 131 ++++++ .../HairShortCutGeometryDepthAlpha.shader | 45 ++ .../Shaders/HairShortCutGeometryShading.azsl | 176 ++++++++ .../HairShortCutGeometryShading.shader | 45 ++ .../Shaders/HairShortCutResolveColor.azsl | 63 +++ .../Shaders/HairShortCutResolveColor.shader | 41 ++ .../Shaders/HairShortCutResolveDepth.azsl | 65 +++ .../Shaders/HairShortCutResolveDepth.shader | 37 ++ .../Assets/Shaders/HairSimulationCompute.azsl | 2 +- ....azsli => HairSimulationComputeSrgs.azsli} | 4 +- .../Assets/Shaders/HairStrands.azsli | 37 +- .../Assets/Shaders/HairUtilities.azsli | 37 -- .../Code/Components/HairSystemComponent.cpp | 6 + .../Code/Passes/HairGeometryRasterPass.cpp | 13 +- .../Code/Passes/HairGeometryRasterPass.h | 3 +- .../Code/Passes/HairPPLLResolvePass.cpp | 33 +- .../Code/Passes/HairPPLLResolvePass.h | 13 +- .../Code/Passes/HairParentPass.cpp | 1 - .../HairShortCutGeometryDepthAlphaPass.cpp | 50 +++ .../HairShortCutGeometryDepthAlphaPass.h | 49 +++ .../HairShortCutGeometryShadingPass.cpp | 111 +++++ .../Passes/HairShortCutGeometryShadingPass.h | 69 ++++ .../Code/Rendering/HairFeatureProcessor.cpp | 171 ++++++-- .../Code/Rendering/HairFeatureProcessor.h | 28 +- .../Code/Rendering/HairRenderObject.cpp | 51 ++- .../Code/Rendering/HairRenderObject.h | 31 +- Gems/AtomTressFX/Hair_files.cmake | 56 ++- 40 files changed, 2055 insertions(+), 225 deletions(-) create mode 100644 Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass create mode 100644 Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryDepthAlpha.pass create mode 100644 Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass create mode 100644 Gems/AtomTressFX/Assets/Passes/HairShortCutResolveColor.pass create mode 100644 Gems/AtomTressFX/Assets/Passes/HairShortCutResolveDepth.pass rename Gems/AtomTressFX/Assets/Shaders/{HairSrgs.azsli => HairComputeSrgs.azsli} (99%) create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairFullScreenUtils.azsli create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryDepthAlpha.azsl create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryDepthAlpha.shader create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.shader create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveColor.azsl create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveColor.shader create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveDepth.azsl create mode 100644 Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveDepth.shader rename Gems/AtomTressFX/Assets/Shaders/{HairSimulationSrgs.azsli => HairSimulationComputeSrgs.azsli} (99%) create mode 100644 Gems/AtomTressFX/Code/Passes/HairShortCutGeometryDepthAlphaPass.cpp create mode 100644 Gems/AtomTressFX/Code/Passes/HairShortCutGeometryDepthAlphaPass.h create mode 100644 Gems/AtomTressFX/Code/Passes/HairShortCutGeometryShadingPass.cpp create mode 100644 Gems/AtomTressFX/Code/Passes/HairShortCutGeometryShadingPass.h diff --git a/Gems/AtomTressFX/Assets/Passes/AtomTressFX_MainPipeline.pass b/Gems/AtomTressFX/Assets/Passes/AtomTressFX_MainPipeline.pass index 84bb85c3e1..9bfa746bf1 100644 --- a/Gems/AtomTressFX/Assets/Passes/AtomTressFX_MainPipeline.pass +++ b/Gems/AtomTressFX/Assets/Passes/AtomTressFX_MainPipeline.pass @@ -212,7 +212,11 @@ // instead of regular Depth as DepthStencil. Specifically, HairResolvePPLL.pass and the associated // .azsl file will need to be updated. "Name": "HairParentPass", - "TemplateName": "HairParentPassTemplate", + // Note: The following two lines represent the choice of rendering pipeline for the hair. + // You can either choose to use PPLL or ShortCut and accordingly change the flag + // 'm_usePPLLRenderTechnique' in the class 'HairFeatureProcessor.cpp' +// "TemplateName": "HairParentPassTemplate", + "TemplateName": "HairParentShortCutPassTemplate", "Enabled": true, "Connections": [ // Critical to keep DepthLinear as input - used to set the size of the Head PPLL image buffer. diff --git a/Gems/AtomTressFX/Assets/Passes/AtomTressFX_PassTemplates.azasset b/Gems/AtomTressFX/Assets/Passes/AtomTressFX_PassTemplates.azasset index a287664286..1580a4e1a9 100644 --- a/Gems/AtomTressFX/Assets/Passes/AtomTressFX_PassTemplates.azasset +++ b/Gems/AtomTressFX/Assets/Passes/AtomTressFX_PassTemplates.azasset @@ -8,6 +8,11 @@ "Name": "HairParentPassTemplate", "Path": "Passes/HairParentPass.pass" }, + { + "Name": "HairParentShortCutPassTemplate", + "Path": "Passes/HairParentShortCutPass.pass" + }, + { "Name": "HairGlobalShapeConstraintsComputePassTemplate", "Path": "Passes/HairGlobalShapeConstraintsCompute.pass" @@ -32,6 +37,7 @@ "Name": "HairUpdateFollowHairComputePassTemplate", "Path": "Passes/HairUpdateFollowHairCompute.pass" }, + { "Name": "HairPPLLRasterPassTemplate", "Path": "Passes/HairFillPPLL.pass" @@ -39,6 +45,23 @@ { "Name": "HairPPLLResolvePassTemplate", "Path": "Passes/HairResolvePPLL.pass" + }, + + { + "Name": "HairShortCutGeometryDepthAlphaPassTemplate", + "Path": "Passes/HairShortCutGeometryDepthAlpha.pass" + }, + { + "Name": "HairShortCutResolveDepthPassTemplate", + "Path": "Passes/HairShortCutResolveDepth.pass" + }, + { + "Name": "HairShortCutGeometryShadingPassTemplate", + "Path": "Passes/HairShortCutGeometryShading.pass" + }, + { + "Name": "HairShortCutResolveColorPassTemplate", + "Path": "Passes/HairShortCutResolveColor.pass" } ] } diff --git a/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass b/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass new file mode 100644 index 0000000000..7322611e2a --- /dev/null +++ b/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass @@ -0,0 +1,391 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "HairParentShortCutPassTemplate", + "PassClass": "ParentPass", + "Slots": [ + { + "Name": "RenderTargetInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + }, + { // used for copy from MSAA to regular RT + "Name": "RenderTargetInputOnly", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + // This is the depth stencil buffer that is to be used by the fill pass + // to early reject pixels by depth and in the resolve pass to write the + // the hair depth + { + "Name": "Depth", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil" + }, + // Keep DepthLinear as input - used to set the size of the Head PPLL image buffer. + // If DepthLinear is not availbale - connect to another viewport (non MSAA) image. + { + "Name": "DepthLinearInput", + "SlotType": "InputOutput" + }, + { + "Name": "DepthLinear", + "SlotType": "Output" + }, + + // Lights & Shadows resources + { + "Name": "DirectionalShadowmap", + "SlotType": "Input" + }, + { + "Name": "DirectionalESM", + "SlotType": "Input" + }, + { + "Name": "ProjectedShadowmap", + "SlotType": "Input" + }, + { + "Name": "ProjectedESM", + "SlotType": "Input" + }, + { + "Name": "TileLightData", + "SlotType": "Input" + }, + { + "Name": "LightListRemapped", + "SlotType": "Input" + } + ], + "Connections": [ + { + "LocalSlot": "DepthLinear", + "AttachmentRef": { + "Pass": "DepthToDepthLinearPass", + "Attachment": "Output" + } + } + ], + "PassRequests": [ + { + "Name": "HairGlobalShapeConstraintsComputePass", + "TemplateName": "HairGlobalShapeConstraintsComputePassTemplate", + "Enabled": true + }, + { + "Name": "HairCalculateStrandLevelDataComputePass", + "TemplateName": "HairCalculateStrandLevelDataComputePassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "SkinnedHairSharedBuffer", + "AttachmentRef": { + "Pass": "HairGlobalShapeConstraintsComputePass", + "Attachment": "SkinnedHairSharedBuffer" + } + } + ] + }, + { + "Name": "HairVelocityShockPropagationComputePass", + "TemplateName": "HairVelocityShockPropagationComputePassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "SkinnedHairSharedBuffer", + "AttachmentRef": { + "Pass": "HairCalculateStrandLevelDataComputePass", + "Attachment": "SkinnedHairSharedBuffer" + } + } + ] + }, + { + "Name": "HairLocalShapeConstraintsComputePass", + "TemplateName": "HairLocalShapeConstraintsComputePassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "SkinnedHairSharedBuffer", + "AttachmentRef": { + "Pass": "HairVelocityShockPropagationComputePass", + "Attachment": "SkinnedHairSharedBuffer" + } + } + ] + }, + { + "Name": "HairLengthConstraintsWindAndCollisionComputePass", + "TemplateName": "HairLengthConstraintsWindAndCollisionComputePassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "SkinnedHairSharedBuffer", + "AttachmentRef": { + "Pass": "HairLocalShapeConstraintsComputePass", + "Attachment": "SkinnedHairSharedBuffer" + } + } + ] + }, + { + "Name": "HairUpdateFollowHairComputePass", + "TemplateName": "HairUpdateFollowHairComputePassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "SkinnedHairSharedBuffer", + "AttachmentRef": { + "Pass": "HairLengthConstraintsWindAndCollisionComputePass", + "Attachment": "SkinnedHairSharedBuffer" + } + } + ] + }, + + // Render Target Copy from MS to Regular + { + "Name": "RenderTargetCopyPass", + "TemplateName": "FullscreenCopyTemplate", + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "RenderTargetInputOnly" + } + }, + { + "LocalSlot": "Output", + "AttachmentRef": { + "Pass": "This", + "Attachment": "Output" + } + } + ], + "ImageAttachments": [ + { + "Name": "Output", + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "Input" + } + }, + "FormatSource": { + "Pass": "This", + "Attachment": "Input" + }, + "GenerateFullMipChain": false + } + ] + }, + + // Rendering Passes + { + "Name": "HairShortCutGeometryDepthAlphaPass", + "TemplateName": "HairShortCutGeometryDepthAlphaPassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "SkinnedHairSharedBuffer", + "AttachmentRef": { + "Pass": "HairUpdateFollowHairComputePass", + "Attachment": "SkinnedHairSharedBuffer" + } + }, + { + "LocalSlot": "Depth", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Depth" + } + }, + { + "LocalSlot": "InverseAlphaRTOutput", + "AttachmentRef": { + "Pass": "This", + "Attachment": "InverseAlphaRTOutput" + } + }, + { + "LocalSlot": "HairDepthsTextureArray", + "AttachmentRef": { + "Pass": "This", + "Attachment": "HairDepthsTextureArray" + } + } + ] + }, + + { + "Name": "HairShortCutResolveDepthPass", + "TemplateName": "HairShortCutResolveDepthPassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "Depth", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Depth" + } + }, + { + "LocalSlot": "HairDepthsTextureArray", + "AttachmentRef": { + "Pass": "HairShortCutGeometryDepthAlphaPass", + "Attachment": "HairDepthsTextureArray" + } + } + ] + }, + + { + "Name": "HairShortCutGeometryShadingPass", + "TemplateName": "HairShortCutGeometryShadingPassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "HairColorRenderTarget", + "AttachmentRef": { + "Pass": "This", + "Attachment": "HairColorRenderTarget" + } + }, + { // The final render target - this is MSAA mode RT - would it be cheaper to + // use non-MSAA and then copy? + "LocalSlot": "RenderTargetInputOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "RenderTargetInputOutput" + } + }, + { + "LocalSlot": "DepthLinear", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "DepthLinearInput" + } + }, + { + "LocalSlot": "Depth", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Depth" + } + }, + { + "LocalSlot": "SkinnedHairSharedBuffer", + "AttachmentRef": { + "Pass": "HairUpdateFollowHairComputePass", + "Attachment": "SkinnedHairSharedBuffer" + } + }, + + // Shadows resources + { + "LocalSlot": "DirectionalShadowmap", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "DirectionalShadowmap" + } + }, + { + "LocalSlot": "DirectionalESM", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "DirectionalESM" + } + }, + { + "LocalSlot": "ProjectedShadowmap", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "ProjectedShadowmap" + } + }, + { + "LocalSlot": "ProjectedESM", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "ProjectedESM" + } + }, + + // Lights Resources + { + "LocalSlot": "TileLightData", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "TileLightData" + } + }, + { + "LocalSlot": "LightListRemapped", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "LightListRemapped" + } + } + ] + }, + + { + "Name": "HairShortCutResolveColorPass", + "TemplateName": "HairShortCutResolveColorPassTemplate", + "Enabled": true, + "Connections": [ + { // The final render target - this is MSAA mode RT - would it be cheaper to + // use non-MSAA and then copy? + "LocalSlot": "RenderTargetInputOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "RenderTargetInputOutput" + } + }, + { + "LocalSlot": "AccumulatedInverseAlpha", + "AttachmentRef": { + "Pass": "HairShortCutGeometryDepthAlphaPass", + "Attachment": "InverseAlphaRTOutput" + } + }, + { + "LocalSlot": "HairColorTexture", + "AttachmentRef": { + "Pass": "HairShortCutGeometryShadingPass", + "Attachment": "HairColorRenderTarget" + } + } + ] + }, + + { + // This pass copies the updated depth buffer (now contains hair depth) to linear depth texture + // for downstream passes to use. This can be optimized even further by writing into the stencil + // buffer pixels that were touched by HairPPLLResolvePass hence preventing depth update unless + // it is hair. + "Name": "DepthToDepthLinearPass", + "TemplateName": "DepthToLinearTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "HairShortCutResolveDepthPass", + "Attachment": "Depth" + } + } + ] + } + + ] + } + } +} + diff --git a/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryDepthAlpha.pass b/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryDepthAlpha.pass new file mode 100644 index 0000000000..559224faa6 --- /dev/null +++ b/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryDepthAlpha.pass @@ -0,0 +1,101 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "HairShortCutGeometryDepthAlphaPassTemplate", + "PassClass": "HairShortCutGeometryDepthAlphaPass", + "Slots": [ + { + "Name": "SkinnedHairSharedBuffer", + "ShaderInputName": "m_skinnedHairSharedBuffer", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + { // DepthStencil for early disqualifying the pixel based on depth. No write. + "Name": "Depth", + "SlotType": "Input", + "ScopeAttachmentUsage": "DepthStencil" + }, + { + // the regular render target is blended using inverse alpha to reduce the + // incoming color contribution based on the hair thickness and alpha. + "Name": "InverseAlphaRTOutput", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "LoadAction": "Clear", + "ClearValue": { + "Value": [ 1.0, 1.0, 1.0, 1.0 ] + }, + "StoreAction": "Store" + } + }, + { + "Name": "HairDepthsTextureArray", + "SlotType": "Output", + "ScopeAttachmentUsage": "Shader", + "ShaderInputName": "m_RWFragmentDepthsTexture", + "LoadStoreAction": { + "LoadAction": "Clear", + "ClearValue": { // reverse depth order: closer --> 1.0 + "Value": [ 0.0, 0.0, 0.0, 0.0 ] + }, + "StoreAction": "Store" + } + } + ], + "ImageAttachments": [ + { + // This buffer is used as the render target and should be at non-MSAA screen resolution + // to make sure no overwork is done. + "Name": "InverseAlphaRTOutput", + "SizeSource": { + "Source": { + "Pass": "Parent", + "Attachment": "DepthLinear" + } + }, + "ImageDescriptor": { + "Format": "R32_FLOAT", + "SharedQueueMask": "Graphics", + "BindFlags": [ + "Color", + "ShaderRead" + ] + } + }, + { + "Name": "HairDepthsTextureArray", + "SizeSource": { + "Source": { + "Pass": "Parent", + "Attachment": "DepthLinear" + } + }, + "ImageDescriptor": { + "Format": "R32_UINT", + "ArraySize": "3", + "SharedQueueMask": "Graphics", + "BindFlags": [ + "ShaderReadWrite", + "ShaderWrite", + "ShaderRead" + ] + } + } + ], + "PassData": { + "$type": "RasterPassData", + "DrawListTag": "HairGeometryDepthAlphaDrawList", + "PipelineViewTag": "MainCamera", + "PassSrgShaderAsset": { + // Looking for it in the Shaders directory relative to the Assets directory + "FilePath": "Shaders/HairShortCutGeometryDepthAlpha.shader" + } + } + } + } +} + diff --git a/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass b/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass new file mode 100644 index 0000000000..5940f8c549 --- /dev/null +++ b/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass @@ -0,0 +1,155 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "HairShortCutGeometryShadingPassTemplate", + "PassClass": "HairShortCutGeometryShadingPass", + "Slots": [ + + { // Temporary color buffer to store the gathered shaded hair color - MSAA + "Name": "HairColorRenderTarget", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "LoadAction": "Clear", + "ClearValue": { // reverse depth order: closer --> 1.0 + "Value": [ 0.0, 0.0, 0.0, 0.0 ] + }, + "StoreAction": "Store" + } + }, + + { + // This RT is MSAA - is it cheaper to avoid doing this work and only do a copy at a separate pass? + "Name": "RenderTargetInputOutput", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + { // Used to get the transform from screen space to world space. + "Name": "DepthLinear", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + { // For comparing the depth to early disqualify but not to write + "Name": "Depth", + "SlotType": "Input", + "ScopeAttachmentUsage": "DepthStencil" + }, + { + "Name": "SkinnedHairSharedBuffer", + "ShaderInputName": "m_skinnedHairSharedBuffer", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + + //------------- Shadowing Resources ------------- + { + "Name": "DirectionalShadowmap", + "ShaderInputName": "m_directionalLightShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "DirectionalESM", + "ShaderInputName": "m_directionalLightExponentialShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "ProjectedShadowmap", + "ShaderInputName": "m_projectedShadowmaps", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "ProjectedESM", + "ShaderInputName": "m_projectedExponentialShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + + //------------- Lighting Resources ------------- + { + "Name": "BRDFTextureInput", + "ShaderInputName": "m_brdfMap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "TileLightData", + "SlotType": "Input", + "ShaderInputName": "m_tileLightData", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "LightListRemapped", + "SlotType": "Input", + "ShaderInputName": "m_lightListRemapped", + "ScopeAttachmentUsage": "Shader" + } + + ], + "ImageAttachments": [ + { + // The shader hair color render target - important to have at a non-MSAA mode + // so that no overwork is done on sampling. + "Name": "HairColorRenderTarget", + "SizeSource": { + "Source": { + "Pass": "Parent", + "Attachment": "RenderTargetInputOutput" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT", + "SharedQueueMask": "Graphics", + "BindFlags": [ + "Color", + "ShaderRead" + ] + } + }, + { + "Name": "BRDFTexture", + "Lifetime": "Imported", + "AssetRef": { + "FilePath": "Textures/BRDFTexture.attimage" + } + } + ], + "Connections": [ + { + "LocalSlot": "BRDFTextureInput", + "AttachmentRef": { + "Pass": "This", + "Attachment": "BRDFTexture" + } + } + ], + "PassData": { + "$type": "RasterPassData", + "DrawListTag": "HairGeometryShadingDrawList", + "PipelineViewTag": "MainCamera", + "PassSrgShaderAsset": { + // Looking for it in the Shaders directory relative to the Assets directory + "FilePath": "Shaders/HairShortCutGeometryShading.shader" + } + } + } + } +} + diff --git a/Gems/AtomTressFX/Assets/Passes/HairShortCutResolveColor.pass b/Gems/AtomTressFX/Assets/Passes/HairShortCutResolveColor.pass new file mode 100644 index 0000000000..efbf993307 --- /dev/null +++ b/Gems/AtomTressFX/Assets/Passes/HairShortCutResolveColor.pass @@ -0,0 +1,45 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "HairShortCutResolveColorPassTemplate", + "PassClass": "FullScreenTriangle", + "Slots": [ + { + // This RT is MSAA - is it cheaper to avoid doing this work and only do a copy at a separate pass? + "Name": "RenderTargetInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "LoadAction": "Load", + "StoreAction": "Store" + } + }, + { + "Name": "HairColorTexture", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderInputName": "m_hairColorTexture" + }, + { + "Name": "AccumulatedInverseAlpha", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderInputName": "m_accumInvAlpha" + } + ], + "Connections": [ + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + // Looking for it in the Shaders directory relative to the Assets directory + "FilePath": "Shaders/HairShortCutResolveColor.shader" + } + } + } + } +} + diff --git a/Gems/AtomTressFX/Assets/Passes/HairShortCutResolveDepth.pass b/Gems/AtomTressFX/Assets/Passes/HairShortCutResolveDepth.pass new file mode 100644 index 0000000000..021d711f1f --- /dev/null +++ b/Gems/AtomTressFX/Assets/Passes/HairShortCutResolveDepth.pass @@ -0,0 +1,37 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "HairShortCutResolveDepthPassTemplate", + "PassClass": "FullScreenTriangle", + "Slots": [ + //------ General Input/Output resources and Render Target ------ + { + "Name": "Depth", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil", + "LoadStoreAction": { + "LoadAction": "Load", + "StoreAction": "Store" + } + }, + { // This holds the K nearset depths. The furthest depth will be taken to be written in the depth buffer. + "Name": "HairDepthsTextureArray", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderInputName": "m_fragmentDepthsTexture" + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + // Looking for it in the Shaders directory relative to the Assets directory + "FilePath": "Shaders/HairShortCutResolveDepth.shader" + } + } + } + } +} + diff --git a/Gems/AtomTressFX/Assets/Shaders/HairSrgs.azsli b/Gems/AtomTressFX/Assets/Shaders/HairComputeSrgs.azsli similarity index 99% rename from Gems/AtomTressFX/Assets/Shaders/HairSrgs.azsli rename to Gems/AtomTressFX/Assets/Shaders/HairComputeSrgs.azsli index cb0bdc2c6c..a4568588d5 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairSrgs.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairComputeSrgs.azsli @@ -29,7 +29,7 @@ // THE SOFTWARE. // //------------------------------------------------------------------------------ -// File: HairSRGs.azsli +// File: HairComputeSrgs.azsli // // Declarations of SRGs used by the hair shaders. //------------------------------------------------------------------------------ diff --git a/Gems/AtomTressFX/Assets/Shaders/HairFullScreenUtils.azsli b/Gems/AtomTressFX/Assets/Shaders/HairFullScreenUtils.azsli new file mode 100644 index 0000000000..7e57e8102c --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairFullScreenUtils.azsli @@ -0,0 +1,60 @@ +/* +* Modifications Copyright (c) Contributors to the Open 3D Engine Project. +* For complete copyright and license terms please see the LICENSE at the root of this distribution. +* +* SPDX-License-Identifier: (Apache-2.0 OR MIT) AND MIT +* +*/ + +#include +#include +#include + +//============================================================================== +// Generate a fullscreen triangle from pipeline provided vertex id +VSOutput FullScreenVS(VSInput input) +{ + VSOutput OUT; + + float4 posTex = GetVertexPositionAndTexCoords(input.m_vertexID); + + OUT.m_texCoord = float2(posTex.z, posTex.w); // [To Do] - test sign of Y based on original code + OUT.m_position = float4(posTex.xy, 0.0, 1.0); + + return OUT; +} + +//============================================================================== +// Given the depth buffer depth of the current pixel and the fragment XY position, +// reconstruct the NDC. +// screenCoords - from 0.. dimension of the screen of the current pixel +// screenTexture - screen buffer texture representing the same resolution we work in +// sDepth - the depth buffer depth at the fragment location +// NDC - Normalized Device Coordinates = warped screen space ( -1.1, -1..1, 0..1 ) +float3 ScreenPosToNDC( Texture2D screenTexture, float2 screenCoords, float depth ) +{ + uint2 dimensions; + screenTexture.GetDimensions(dimensions.x, dimensions.y); + float2 UV = saturate(screenCoords / dimensions.xy); + + float x = UV.x * 2.0f - 1.0f; + float y = (1.0f - UV.y) * 2.0f - 1.0f; + float3 NDC = float3(x, y, depth); + + return NDC; +} + +// Given the depth buffer depth of the current pixel and the fragment XY position, +// reconstruct the world space position +float3 ScreenPosToWorldPos( + Texture2D screenTexture, float2 screenCoords, float depth, + inout float3 screenPosNDC ) +{ + screenPosNDC = ScreenPosToNDC(screenTexture, screenCoords, depth); + float4 projectedPos = float4(screenPosNDC, 1.0f); // warped projected space [0..1] + float4 positionVS = mul(ViewSrg::m_projectionMatrixInverse, projectedPos); + positionVS /= positionVS.w; // notice the normalization factor - crucial! + float4 positionWS = mul(ViewSrg::m_viewMatrixInverse, positionVS); + + return positionWS.xyz; +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairLighting.azsli b/Gems/AtomTressFX/Assets/Shaders/HairLighting.azsli index dcdcf43243..7beba248f7 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairLighting.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairLighting.azsli @@ -230,7 +230,7 @@ float3 CalculateLighting( return lightingData.diffuseLighting + lightingData.specularLighting; } -float3 TressFXShading(float2 pixelCoord, float depth, float3 vTangentCoverage, float3 baseColor, float thickness, int shaderParamIndex) +float3 TressFXShading(float2 pixelCoord, float depth, float3 tangent, float3 baseColor, float thickness, int shaderParamIndex) { float3 vNDC; // normalized device / screen coordinates: [-1..1, -1..1, 0..1] float3 vPositionWS = ScreenPosToWorldPos(PassSrg::m_linearDepth, pixelCoord, depth, vNDC); @@ -241,9 +241,6 @@ float3 TressFXShading(float2 pixelCoord, float depth, float3 vTangentCoverage, f float3 vViewDirWS = g_vEye - vPositionWS; - // Need to expand the tangent that was compressed to store in the buffer - float3 vTangent = normalize(vTangentCoverage.xyz * 2.f - 1.f); - //---- TressFX original lighting params setting ---- HairShadeParams params; params.m_color = baseColor; @@ -266,11 +263,19 @@ float3 TressFXShading(float2 pixelCoord, float depth, float3 vTangentCoverage, f if (o_hairLightingModel == HairLightingModel::Kajiya) { // This option should be removed and the Kajiya-Kay model should be operated from within // the Atom lighting loop. - accumulatedLight = SimplifiedHairLighting(vTangent, vPositionWS, vViewDirWS, params, vNDC); + accumulatedLight = SimplifiedHairLighting(tangent, vPositionWS, vViewDirWS, params, vNDC); } else { - accumulatedLight = CalculateLighting(screenCoords, vPositionWS, vViewDirWS, vTangent, thickness, params); + accumulatedLight = CalculateLighting(screenCoords, vPositionWS, vViewDirWS, tangent, thickness, params); } return accumulatedLight; } + +float3 TressFXShadingFullScreen(float2 pixelCoord, float depth, float3 compressedTangent, float3 baseColor, float thickness, int shaderParamIndex) +{ + // The tangent that was compressed to store in the PPLL structure + float3 tangent = normalize(compressedTangent.xyz * 2.f - 1.f); + + return TressFXShading(pixelCoord, depth, tangent, baseColor, thickness, shaderParamIndex); +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairLightingEquations.azsli b/Gems/AtomTressFX/Assets/Shaders/HairLightingEquations.azsli index 83ebc04521..4e4245f765 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairLightingEquations.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairLightingEquations.azsli @@ -66,7 +66,7 @@ option bool o_enableAzimuthCoeff = true; float M_R(Surface surface, float Lh, float sinLiPlusSinLr) { float a = 1.0f * surface.cuticleTilt; // Tilt is translate as the mean offset - float b = 0.5 * surface.roughnessA2; // Roughness is used as the standard deviation + float b = 0.5f * surface.roughnessA2; // Roughness is used as the standard deviation // return GaussianNormalized(sinLiPlusSinLr, a, b); // reference return GaussianNormalized(Lh, a, b); @@ -74,8 +74,8 @@ float M_R(Surface surface, float Lh, float sinLiPlusSinLr) float M_TT(Surface surface, float Lh, float sinLiPlusSinLr) { - float a = 1.0 * surface.cuticleTilt; - float b = 0.5 * surface.roughnessA2; + float a = 1.0f * surface.cuticleTilt; + float b = 0.5f * surface.roughnessA2; // return GaussianNormalized(sinLiPlusSinLr, a, b); // reference return GaussianNormalized(Lh, a, b); @@ -83,8 +83,8 @@ float M_TT(Surface surface, float Lh, float sinLiPlusSinLr) float M_TRT(Surface surface, float Lh, float sinLiPlusSinLr) { - float a = 1.5 * surface.cuticleTilt; - float b = 1.0 * surface.roughnessA2; + float a = 1.5f * surface.cuticleTilt; + float b = 1.0f * surface.roughnessA2; // return GaussianNormalized(sinLiPlusSinLr, a, b); // reference return GaussianNormalized(Lh, a, b); diff --git a/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl b/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl index 82c677faad..02777435f5 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl +++ b/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl @@ -40,7 +40,8 @@ //! that can change between passes due to the application of skinning, simulation //! and physics affect and is then read by the rendering shaders. ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback -{ //! This shared buffer needs to match the SharedBuffer structure +{ + //! This shared buffer needs to match the SharedBuffer structure //! shared between all draw calls / dispatches for the hair skinning StructuredBuffer m_skinnedHairSharedBuffer; @@ -101,39 +102,8 @@ ShaderResourceGroup HairDynamicDataSrg : SRG_PerObject // space 1 - per instance #define g_GuideHairVertexTangents HairDynamicDataSrg::m_hairVertexTangents //============================================================================== -#include +#include // VS resides here //============================================================================== -//! Hair input structure to Pixel shaders -struct PS_INPUT_HAIR -{ - float4 Position : SV_POSITION; - float4 Tangent : Tangent; - float4 p0p1 : TEXCOORD0; - float4 StrandColor : TEXCOORD1; -}; - -//! Hair Render VS -PS_INPUT_HAIR RenderHairVS(uint vertexId : SV_VertexID) -{ -// uint2 scrSize; -// PassSrg::m_linearDepth.GetDimensions(scrSize.x, scrSize.y); -// TressFXVertex tressfxVert = GetExpandedTressFXVert(vertexId, g_vEye.xyz, float2(scrSize), g_mVP); - - // [To Do] Hair: the above code should replace the existing but requires modifications to - // the function GetExpandedTressFXVert. - // Note that in Atom g_vViewport is aspect ratio and NOT size. - TressFXVertex tressfxVert = GetExpandedTressFXVert(vertexId, g_vEye.xyz, g_vViewport.zw, g_mVP); - - - PS_INPUT_HAIR Output; - - Output.Position = tressfxVert.Position; - Output.Tangent = tressfxVert.Tangent; - Output.p0p1 = tressfxVert.p0p1; - Output.StrandColor = tressfxVert.StrandColor; - - return Output; -} // Allocate a new fragment location in fragment color, depth, and link buffers int AllocateFragment(int2 vScreenAddress) @@ -202,9 +172,9 @@ void PPLLFillPS(PS_INPUT_HAIR input) ////////////////////////////////////////////////////////////////////// // [To Do] Hair: anti aliasing via coverage requires work and is disabled for now - float3 vNDC = ScreenPosToNDC(PassSrg::m_linearDepth, input.Position.xy, input.Position.z); - uint2 dimensions; - PassSrg::m_linearDepth.GetDimensions(dimensions.x, dimensions.y); +// float3 vNDC = ScreenPosToNDC(PassSrg::m_linearDepth, input.Position.xy, input.Position.z); +// uint2 dimensions; +// PassSrg::m_linearDepth.GetDimensions(dimensions.x, dimensions.y); // float coverage = ComputeCoverage(input.p0p1.xy, input.p0p1.zw, vNDC.xy, float2(dimensions.x, dimensions.y)); float coverage = 1.0; ///////////////////////////////////////////////////////////////////// diff --git a/Gems/AtomTressFX/Assets/Shaders/HairRenderingResolvePPLL.azsl b/Gems/AtomTressFX/Assets/Shaders/HairRenderingResolvePPLL.azsl index 82c75280f6..3d72f1e21e 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairRenderingResolvePPLL.azsl +++ b/Gems/AtomTressFX/Assets/Shaders/HairRenderingResolvePPLL.azsl @@ -58,7 +58,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback // in the OIT process. // It can also be used to avoid the HW blend done at the end of the pixel // shader stage but HW blend might be cheaper than additional PS blend. - Texture2D m_frameBuffer; // The merged MSAA output + Texture2D m_frameBuffer; // The merged non-MSAA input // Linear depth is used for getting the screen to world transform Texture2D m_linearDepth; @@ -93,26 +93,11 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback #define HairParams PassSrg::m_hairParams //============================================================================== +#include // provides the Vertex Shader #include -#include -#include - -// Generates a fullscreen triangle from pipeline provided vertex id -VSOutput FullScreenVS(VSInput input) -{ - VSOutput OUT; - - float4 posTex = GetVertexPositionAndTexCoords(input.m_vertexID); - - OUT.m_texCoord = float2(posTex.z, posTex.w); - OUT.m_position = float4(posTex.x, posTex.y, 0.0, 1.0); - - return OUT; -} ////////////////////////////////////////////////////////////// // Bind data for PPLLResolvePS - #define NODE_DATA(x) LinkedListNodes[x].data #define NODE_NEXT(x) LinkedListNodes[x].uNext #define NODE_DEPTH(x) LinkedListNodes[x].depth @@ -298,7 +283,7 @@ float4 GatherLinkedList(float2 vfScreenAddress, float2 screenUV, inout float out uint shadeParamIndex; // So we know what settings to shade with float3 vColor = UnpackUintIntoFloat3Byte(color, shadeParamIndex); - float3 fragmentColor = TressFXShading(vfScreenAddress, fDepth, vTangent, vColor, fcolor.w, shadeParamIndex); + float3 fragmentColor = TressFXShadingFullScreen(vfScreenAddress, fDepth, vTangent, vColor, fcolor.w, shadeParamIndex); // Blend in the fragment color fcolor.xyz = fcolor.xyz * (1.f - alpha) + fragmentColor * alpha; @@ -355,7 +340,7 @@ float4 GetClosestFragment(float2 vfScreenAddress, float2 screenUV, inout float c float alpha = 1.0; uint shadeParamIndex; // the material index float3 vColor = UnpackUintIntoFloat3Byte(curColor, shadeParamIndex); - float3 fragmentColor = TressFXShading(vfScreenAddress, curDepth, vTangent, vColor, fcolor.w, shadeParamIndex); + float3 fragmentColor = TressFXShadingFullScreen(vfScreenAddress, curDepth, vTangent, vColor, fcolor.w, shadeParamIndex); // Blend in the fragment color fcolor.xyz = fcolor.xyz * (1.f - alpha) + (fragmentColor * alpha); diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryDepthAlpha.azsl b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryDepthAlpha.azsl new file mode 100644 index 0000000000..23c926406f --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryDepthAlpha.azsl @@ -0,0 +1,131 @@ +/* +* Modifications Copyright (c) Contributors to the Open 3D Engine Project. +* For complete copyright and license terms please see the LICENSE at the root of this distribution. +* +* SPDX-License-Identifier: (Apache-2.0 OR MIT) AND MIT +* +*/ + +// +// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include +#include + +//!------------------------------ SRG Structure -------------------------------- +//! Per pass SRG that holds the dynamic shared read-write buffer shared +//! across all dispatches and draw calls. It is used for all the dynamic buffers +//! that can change between passes due to the application of skinning, simulation +//! and physics affect. +//! Once the compute pases are done, it is read by the rendering shaders. +ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback +{ + //! This shared buffer needs to match the SharedBuffer structure + //! shared between all draw calls / dispatches for the hair skinning + StructuredBuffer m_skinnedHairSharedBuffer; + + //! Based on [[vk::binding(0, 3)]] RWTexture2DArray RWFragmentDepthsTexture : register(u0, space3); + RWTexture2DArray m_RWFragmentDepthsTexture; +} +//============================================================================== + +//!------------------------------ SRG Structure -------------------------------- +//! Per instance/draw SRG representing dynamic read-write set of buffers +//! that are unique per instance and are shared and changed between passes due +//! to the application of skinning, simulation and physics affect. +//! It is then also read by the rendering shaders. +//! This Srg is NOT shared by the passes since it requires having barriers between +//! both passes and draw calls, instead, all buffers are allocated from a single +//! shared buffer (through BufferViews) and that buffer is then shared between +//! the passes via the PerPass Srg frequency. +ShaderResourceGroup HairDynamicDataSrg : SRG_PerObject // space 1 - per instance / object +{ + Buffer m_hairVertexPositions; + Buffer m_hairVertexTangents; + + //! Per hair object offset to the start location of each buffer within + //! 'm_skinnedHairSharedBuffer'. The offset is in bytes! + uint m_positionBufferOffset; + uint m_tangentBufferOffset; +}; +//------------------------------------------------------------------------------ +// Allow for the code to run with minimal changes - skinning / simulation compute passes +// Usage of per-instance buffer +#define g_GuideHairVertexPositions HairDynamicDataSrg::m_hairVertexPositions +#define g_GuideHairVertexTangents HairDynamicDataSrg::m_hairVertexTangents +//------------------------------------------------------------------------------ + +#include // VS resides here + +//!============================================================================= +//! Geometry Depth Alpha - First Pass of ShortCut Render +//! It is a Geometry pass that stores the K=3 front fragment depths, and accumulates +//! product of 1-alpha multiplications (fade out) of the input render target. +//! +//! Short explanation: in the original AMD implementation 1-alpha is multiplied +//! repeatedly with the incoming render target (back buffer) hence blending out +//! the existing back buffer color based on the density and transparency of the hair. +//! This implies that later on the hair color should be added based on the inverse +//! of this operation. +//!============================================================================= +[earlydepthstencil] +float HairShortCutDepthsAlphaPS(PS_INPUT_HAIR input) : SV_Target +{ + ////////////////////////////////////////////////////////////////////// + // [To Do] Hair: anti aliasing via coverage requires work and is disabled for now +// float3 vNDC = ScreenPosToNDC(PassSrg::m_linearDepth, input.Position.xy, input.Position.z); +// uint2 dimensions; +// PassSrg::m_linearDepth.GetDimensions(dimensions.x, dimensions.y); +// float coverage = ComputeCoverage(input.p0p1.xy, input.p0p1.zw, vNDC.xy, float2(dimensions.x, dimensions.y)); + float coverage = 1.0; + ///////////////////////////////////////////////////////////////////// + + float alpha = coverage * MatBaseColor.a; + + if (alpha < SHORTCUT_MIN_ALPHA) + return 1.0; + + int2 vScreenAddress = int2(input.Position.xy); + uint uDepth = asuint(input.Position.z); + uint uDepth0Prev, uDepth1Prev, uDepth2Prev; + + // Min of depth 0 and input depth - in Atom the Z order is reverse + // Original value is uDepth0Prev + InterlockedMax(PassSrg::m_RWFragmentDepthsTexture[uint3(vScreenAddress, 0)], uDepth, uDepth0Prev); + + // Min of depth 1 and greater of the last compare - in Atom the Z order is reverse + // If fragment opaque, always use input depth (don't need greater depths) + uDepth = (alpha > 0.98) ? uDepth : max(uDepth, uDepth0Prev); + + InterlockedMax(PassSrg::m_RWFragmentDepthsTexture[uint3(vScreenAddress, 1)], uDepth, uDepth1Prev); + + // Min of depth 2 and greater of the last compare - in Atom the Z order is reverse + // If fragment opaque, always use input depth (don't need greater depths) + uDepth = (alpha > 0.98) ? uDepth : max(uDepth, uDepth1Prev); + + InterlockedMax(PassSrg::m_RWFragmentDepthsTexture[uint3(vScreenAddress, 2)], uDepth, uDepth2Prev); + + // Accumulate the alpha multiplication from all hair components by multiplying the inverse and + // therefore going down towards 0. At the end product, the inverse will be taken as the hair + // alpha and the remainder will be used to blend the back buffer. + return 1.0 - alpha; +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryDepthAlpha.shader b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryDepthAlpha.shader new file mode 100644 index 0000000000..7cd1f44510 --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryDepthAlpha.shader @@ -0,0 +1,45 @@ +{ + "Source" : "HairShortCutGeometryDepthAlpha.azsl", + "DrawList" : "HairGeometryDepthAlphaDrawList", + + "DepthStencilState" : + { + "Depth" : + { + "Enable" : true, + "WriteMask" : "Zero", // Avoid writing the depth + "CompareFunc" : "GreaterEqual" + // Originally in TressFX this is LessEqual - Atom is using reverse sort + }, + "Stencil" : + { + "Enable" : false + } + }, + + "BlendState" : + { + "Enable" : true, + "BlendSource" : "Zero", + "BlendDest" : "ColorSource", + "BlendOp" : "Add", + "BlendAlphaSource" : "Zero", + "BlendAlphaDest" : "AlphaSource", + "BlendAlphaOp" : "Add" + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "RenderHairVS", + "type": "Vertex" + }, + { + "name": "HairShortCutDepthsAlphaPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl new file mode 100644 index 0000000000..c2a2958dfe --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl @@ -0,0 +1,176 @@ +/* +* Modifications Copyright (c) Contributors to the Open 3D Engine Project. +* For complete copyright and license terms please see the LICENSE at the root of this distribution. +* +* SPDX-License-Identifier: (Apache-2.0 OR MIT) AND MIT +* +*/ + +//------------------------------------------------------------------------------ +// Shader code related to lighting and shadowing for TressFX +//------------------------------------------------------------------------------ +// +// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include +#include + +#define AMD_TRESSFX_MAX_HAIR_GROUP_RENDER 16 + +//!------------------------------ SRG Structure -------------------------------- +//! Per pass SRG that holds the dynamic shared read-write buffer shared +//! across all dispatches and draw calls. It is used for all the dynamic buffers +//! that can change between passes due to the application of skinning, simulation +//! and physics affect. +//! Once the compute pases are done, it is read by the rendering shaders. +ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback +{ + //! This shared buffer needs to match the SharedBuffer structure + //! shared between all draw calls / dispatches for the hair skinning + StructuredBuffer m_skinnedHairSharedBuffer; + + //! Per hair object material array used by the PPLL resolve pass + //! Originally in TressFXRendering.hlsl this is space 0 + HairObjectShadeParams m_hairParams[AMD_TRESSFX_MAX_HAIR_GROUP_RENDER]; + + // Linear depth is used for getting the screen to world transform + Texture2D m_linearDepth; + + //------------------------------ + // Lighting Data + //------------------------------ + Sampler LinearSampler + { // Required by LightingData.azsli + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; + + Texture2DArray m_directionalLightShadowmap; + Texture2DArray m_directionalLightExponentialShadowmap; + Texture2DArray m_projectedShadowmaps; + Texture2DArray m_projectedExponentialShadowmap; + Texture2D m_brdfMap; + Texture2D m_tileLightData; + StructuredBuffer m_lightListRemapped; +} + +//------------------------------------------------------------------------------ +//! The hair objects' material array buffer used by the rendering resolve pass +#define HairParams PassSrg::m_hairParams +//============================================================================== + +//!------------------------------ SRG Structure -------------------------------- +//! Per instance/draw SRG representing dynamic read-write set of buffers +//! that are unique per instance and are shared and changed between passes due +//! to the application of skinning, simulation and physics affect. +//! It is then also read by the rendering shaders. +//! This Srg is NOT shared by the passes since it requires having barriers between +//! both passes and draw calls, instead, all buffers are allocated from a single +//! shared buffer (through BufferViews) and that buffer is then shared between +//! the passes via the PerPass Srg frequency. +ShaderResourceGroup HairDynamicDataSrg : SRG_PerObject // space 1 - per instance / object +{ + Buffer m_hairVertexPositions; + Buffer m_hairVertexTangents; + + //! Per hair object offset to the start location of each buffer within + //! 'm_skinnedHairSharedBuffer'. The offset is in bytes! + uint m_positionBufferOffset; + uint m_tangentBufferOffset; +}; +//------------------------------------------------------------------------------ +// Allow for the code to run with minimal changes - skinning / simulation compute passes +// Usage of per-instance buffer +#define g_GuideHairVertexPositions HairDynamicDataSrg::m_hairVertexPositions +#define g_GuideHairVertexTangents HairDynamicDataSrg::m_hairVertexTangents +//------------------------------------------------------------------------------ + +#include // VS resides here +#include // Required for world coordinates calculation +#include + +//!============================================================================= +//! Geometry Shading - Third Pass of ShortCut Render +//! Geometry pass that shades pixels that passes the early depth test. Due to this, it +//! is limited to the stored K near fragments due to previous depth write pass that +//! wrote the furthest depth of the K stored depths. +//! Colors are accumulated in the render target for a weighted average in final pass. +//! [To Do] - in the original short cut, the alpha is taken from the depth alpha pass +//!============================================================================= +[earlydepthstencil] +float4 HairShortCutGeometryColorPS(PS_INPUT_HAIR input) : SV_Target +{ + // Strand Color read in is either the BaseMatColor, or BaseMatColor modulated with a color read from texture + // on vertex shader for base color along with modulation by the tip color + float4 strandColor = float4(input.StrandColor.rgb, MatBaseColor.a); + + // If we are supporting strand UV texturing, further blend in the texture color/alpha + // Do this while computing NDC and coverage to hide latency from texture lookup + if (EnableStrandUV) + { + // Grab the uv in case we need it + float2 uv = float2(input.Tangent.w, input.StrandColor.w); + + // Apply StrandUVTiling + float2 strandUV = float2(uv.x, (uv.y * StrandUVTilingFactor) - floor(uv.y * StrandUVTilingFactor)); + + strandColor.rgb *= StrandAlbedoTexture.Sample(LinearWrapSampler, strandUV).rgb; + } + + ////////////////////////////////////////////////////////////////////// + // [To Do] Hair: anti aliasing via coverage requires work and is disabled for now +// float3 vNDC = ScreenPosToNDC(PassSrg::m_linearDepth, input.Position.xy, input.Position.z); +// uint2 dimensions; +// PassSrg::m_linearDepth.GetDimensions(dimensions.x, dimensions.y); +// float2 screenCoords = saturate(pixelCoord / dimensions.xy); +// float coverage = ComputeCoverage(input.p0p1.xy, input.p0p1.zw, vNDC.xy, float2(dimensions.x, dimensions.y)); +// original: float coverage = ComputeCoverage(input.p0p1.xy, input.p0p1.zw, vNDC.xy, g_vViewport.zw - g_vViewport.xy); + float coverage = 1.0; + ///////////////////////////////////////////////////////////////////// + + float alpha = coverage; + + // Update the alpha to have proper value (accounting for coverage, base alpha, and strand alpha) + alpha *= strandColor.w; + + // Early out + if (alpha < SHORTCUT_MIN_ALPHA) + { + return float4(0, 0, 0, 0); + } + + float2 pixelCoord = input.Position.xy; + float depth = input.Position.z; + // [To Do] - the thickness will need to be corrected somehow since this technique doesn't + // keeps track of the accumulated alpha / thickness + float thickness = alpha; + float3 shadedFragment = TressFXShading(pixelCoord, depth, input.Tangent.xyz, strandColor.rgb, thickness, RenderParamsIndex); + + // Color channel: Pre-multiply with alpha to create non-normalized weighted sum. + // Alpha Channel: Sum up all the hair alphas - this will be used to normalize the color + // per fragment at the next pass. + return float4(shadedFragment * alpha, alpha); +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.shader b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.shader new file mode 100644 index 0000000000..40e56006cd --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.shader @@ -0,0 +1,45 @@ +{ + "Source" : "HairShortCutGeometryShading.azsl", + "DrawList" : "HairGeometryShadingDrawList", + + "DepthStencilState" : + { + "Depth" : + { + "Enable" : true, + "WriteMask" : "Zero", // Avoid writing the depth + "CompareFunc" : "GreaterEqual" + // Originally in TressFX this is LessEqual - Atom is using reverse sort + }, + "Stencil" : + { + "Enable" : false + } + }, + + "BlendState" : + { + "Enable" : true, + "BlendSource" : "One", + "BlendDest" : "One", + "BlendOp" : "Add", + "BlendAlphaSource" : "One", + "BlendAlphaDest" : "One", + "BlendAlphaOp" : "Add" + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "RenderHairVS", + "type": "Vertex" + }, + { + "name": "HairShortCutGeometryColorPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveColor.azsl b/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveColor.azsl new file mode 100644 index 0000000000..f25c9dd711 --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveColor.azsl @@ -0,0 +1,63 @@ +/* +* Modifications Copyright (c) Contributors to the Open 3D Engine Project. +* For complete copyright and license terms please see the LICENSE at the root of this distribution. +* +* SPDX-License-Identifier: (Apache-2.0 OR MIT) AND MIT +* +*/ + +#include +#include + +//!------------------------------ SRG Structure -------------------------------- +//! Per pass SRG that holds the dynamic shared read-write buffer shared +//! across all dispatches and draw calls. It is used for all the dynamic buffers +//! that can change between passes due to the application of skinning, simulation +//! and physics affect. +//! Once the compute pases are done, it is read by the rendering shaders. +ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback +{ + // oiriginally: [[vk::binding(0, 0)]] Texture2D HaiColorTexture : register(t0, space0); + // oiriginally: [[vk::binding(1, 0)]] Texture2D AccumInvAlpha : register(t1, space0); + Texture2D m_hairColorTexture; + Texture2D m_accumInvAlpha; +} +//------------------------------------------------------------------------------ + +#include // provides the Vertex Shader + +//!============================================================================= +//! HairColorPS - Fourth Pass of ShortCut Render +//! Full-screen pass that finalizes the weighted average, and blends using the +//! accumulated 1-alpha product. +//!============================================================================= +[earlydepthstencil] +float4 HairShortCutResolveColorPS(VSOutput input) : SV_Target +{ + int2 vScreenAddress = int2(input.m_position.xy); + + float fInvAlpha = PassSrg::m_accumInvAlpha[vScreenAddress]; + float fAlpha = 1.0 - fInvAlpha; + + if (fAlpha < SHORTCUT_MIN_ALPHA) + { + // next we discard of non-hair pixels to avoid manipulating them depending + // on the alpha blend state - this is the safer and faster approach as there + // is no hair in these pixels + discard; + } + + float4 finalColor; + float weightSum = PassSrg::m_hairColorTexture[vScreenAddress].w; + + // Normalize the sum of the shaded fragment from the previous pass and + // then multiply it by the alpha blend of the hairs done in the depth-alpha pass. + finalColor.xyz = PassSrg::m_hairColorTexture[vScreenAddress] * fAlpha / weightSum; + + // The alpha is set to the inverse alpha of the hair so that the original + // background will be blended using this factor emulating single step alpha blend + // over the sum of all hair fragment blends. + finalColor.w = fInvAlpha; + + return finalColor; +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveColor.shader b/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveColor.shader new file mode 100644 index 0000000000..6703c63f18 --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveColor.shader @@ -0,0 +1,41 @@ +{ + "Source" : "HairShortCutResolveColor.azsl", + + "DepthStencilState" : + { + "Depth" : + { + "Enable" : false // Avoid comparing depth + }, + "Stencil" : + { + "Enable" : false + } + }, + + "BlendState" : + { + "Enable" : true, + "BlendSource" : "One", + "BlendDest" : "AlphaSource", + "BlendOp" : "Add", + "BlendAlphaSource" : "Zero", + "BlendAlphaDest" : "Zero", + "BlendAlphaOp" : "Add" + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "FullScreenVS", + "type": "Vertex" + }, + { + "name": "HairShortCutResolveColorPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveDepth.azsl b/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveDepth.azsl new file mode 100644 index 0000000000..862b7c9015 --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveDepth.azsl @@ -0,0 +1,65 @@ +/* +* Modifications Copyright (c) Contributors to the Open 3D Engine Project. +* For complete copyright and license terms please see the LICENSE at the root of this distribution. +* +* SPDX-License-Identifier: (Apache-2.0 OR MIT) AND MIT +* +*/ + +// +// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include + +//!------------------------------ SRG Structure -------------------------------- +//! Per pass SRG that holds the dynamic shared read-write buffer shared +//! across all dispatches and draw calls. It is used for all the dynamic buffers +//! that can change between passes due to the application of skinning, simulation +//! and physics affect. +//! Once the compute pases are done, it is read by the rendering shaders. +ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback +{ + // Originally: [[vk::binding(0, 0)]] Texture2DArray FragmentDepthsTexture : register(t0, space0); + Texture2DArray m_fragmentDepthsTexture; +} +//------------------------------------------------------------------------------ + +#include // provides the Vertex Shader + +//!============================================================================= +//! Resolve Depth - Second Pass of ShortCut +//! Full-screen pass that writes the farthest of the stored K near depths so it +//! could be used for depth culling during the following geometry shading pass. +//!============================================================================= +float HairShortCutResolveDepthPS(VSOutput input) : SV_Depth +{ + // Blend the layers of fragments from back to front + int2 vScreenAddress = int2(input.m_position.xy); + + // Write farthest depth value for culling in the next pass. + // It may be the initial value of 1.0 if there were not enough fragments to write all depths, but then culling not important. + const int farthestDepthIndex = 2; + uint uDepth = PassSrg::m_fragmentDepthsTexture[uint3(vScreenAddress, farthestDepthIndex)]; + + // The following line is writing the depth into the actual depth buffer + return asfloat(uDepth); +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveDepth.shader b/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveDepth.shader new file mode 100644 index 0000000000..5b518c3bfc --- /dev/null +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutResolveDepth.shader @@ -0,0 +1,37 @@ +{ + "Source" : "HairShortCutResolveDepth.azsl", + + "DepthStencilState" : + { + "Depth" : + { + "Enable" : true, // test the written depth and accept/discard based on the depth buffer + "CompareFunc" : "GreaterEqual" + // Originally in TressFX this is LessEqual - Atom is using reverse sort + }, + "Stencil" : + { + "Enable" : false + } + }, + + "BlendState" : + { + "Enable" : false + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "FullScreenVS", + "type": "Vertex" + }, + { + "name": "HairShortCutResolveDepthPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairSimulationCompute.azsl b/Gems/AtomTressFX/Assets/Shaders/HairSimulationCompute.azsl index 496aa5c7da..ed6ec5de27 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairSimulationCompute.azsl +++ b/Gems/AtomTressFX/Assets/Shaders/HairSimulationCompute.azsl @@ -31,7 +31,7 @@ // THE SOFTWARE. // //-------------------------------------------------------------------------------------- -#include +#include #include //-------------------------------------------------------------------------------------- diff --git a/Gems/AtomTressFX/Assets/Shaders/HairSimulationSrgs.azsli b/Gems/AtomTressFX/Assets/Shaders/HairSimulationComputeSrgs.azsli similarity index 99% rename from Gems/AtomTressFX/Assets/Shaders/HairSimulationSrgs.azsli rename to Gems/AtomTressFX/Assets/Shaders/HairSimulationComputeSrgs.azsli index f95dac92cf..3ded3ab2af 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairSimulationSrgs.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairSimulationComputeSrgs.azsli @@ -29,13 +29,13 @@ // THE SOFTWARE. // //------------------------------------------------------------------------------ -// File: HairSRGs.azsli +// File: HairSimulationComputeSrgs.azsli // // Declarations of SRGs used by the hair shaders. //------------------------------------------------------------------------------ #pragma once -#include +#include //!----------------------------------------------------------------------------- //! diff --git a/Gems/AtomTressFX/Assets/Shaders/HairStrands.azsli b/Gems/AtomTressFX/Assets/Shaders/HairStrands.azsli index b8c4ea9eb4..abbbc8c0de 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairStrands.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairStrands.azsli @@ -66,12 +66,22 @@ float3 GetSharedTangent(int tangentIndex) ); } +//! Hair vertex geometry output - input structure for the Pixel shaders struct TressFXVertex { float4 Position; - float4 Tangent; + float4 Tangent; // xyz = Tangent, w = Strand U float4 p0p1; - float4 StrandColor; + float4 StrandColor; // xyz = Strand Color, w = Strand V +}; + +//! Matching structure to carry out as VS output / PS input +struct PS_INPUT_HAIR +{ + float4 Position : SV_POSITION; + float4 Tangent : Tangent; + float4 p0p1 : TEXCOORD0; + float4 StrandColor : TEXCOORD1; }; float3 GetStrandColor(int index, float fractionOfStrand) @@ -178,5 +188,26 @@ TressFXVertex GetExpandedTressFXShadowVert(uint vertexId, float3 eye, float2 win return Output; } -// EndHLSL +//!============================================================================= +//! Hair Render VS - Used by all geometry hair shaders +//!============================================================================= +PS_INPUT_HAIR RenderHairVS(uint vertexId : SV_VertexID) +{ + PS_INPUT_HAIR vsOutput; + // uint2 scrSize; + // PassSrg::m_linearDepth.GetDimensions(scrSize.x, scrSize.y); + // TressFXVertex tressfxVert = GetExpandedTressFXVert(vertexId, g_vEye.xyz, float2(scrSize), g_mVP); + + // [To Do] Hair: the above code should replace the existing but requires modifications to + // the function GetExpandedTressFXVert. + // Note that in Atom g_vViewport is aspect ratio and NOT size. + TressFXVertex tressfxVert = GetExpandedTressFXVert(vertexId, g_vEye.xyz, g_vViewport.zw, g_mVP); + + vsOutput.Position = tressfxVert.Position; + vsOutput.Tangent = tressfxVert.Tangent; + vsOutput.p0p1 = tressfxVert.p0p1; + vsOutput.StrandColor = tressfxVert.StrandColor; + + return vsOutput; +} diff --git a/Gems/AtomTressFX/Assets/Shaders/HairUtilities.azsli b/Gems/AtomTressFX/Assets/Shaders/HairUtilities.azsli index 1d50d3c040..f91ba1ff3b 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairUtilities.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairUtilities.azsli @@ -34,9 +34,6 @@ #pragma once -#include - - #define SHORTCUT_MIN_ALPHA 0.02 #define TRESSFX_FLOAT_EPSILON 1e-7 @@ -52,40 +49,6 @@ float4 MatrixMult(float4x4 m, float4 v) return mul(m, v); } -// Given the depth buffer depth of the current pixel and the fragment XY position, -// reconstruct the NDC. -// screenCoords - from 0.. dimension of the screen of the current pixel -// screenTexture - screen buffer texture representing the same resolution we work in -// sDepth - the depth buffer depth at the fragment location -// NDC - Normalized Device Coordinates = warped screen space ( -1.1, -1..1, 0..1 ) -float3 ScreenPosToNDC( Texture2D screenTexture, float2 screenCoords, float depth ) -{ - uint2 dimensions; - screenTexture.GetDimensions(dimensions.x, dimensions.y); - float2 UV = saturate(screenCoords / dimensions.xy); - - float x = UV.x * 2.0f - 1.0f; - float y = (1.0f - UV.y) * 2.0f - 1.0f; - float3 NDC = float3(x, y, depth); - - return NDC; -} - -// Given the depth buffer depth of the current pixel and the fragment XY position, -// reconstruct the world space position -float3 ScreenPosToWorldPos( - Texture2D screenTexture, float2 screenCoords, float depth, - inout float3 screenPosNDC ) -{ - screenPosNDC = ScreenPosToNDC(PassSrg::m_linearDepth, screenCoords, depth); - float4 projectedPos = float4(screenPosNDC, 1.0f); // warped projected space [0..1] - float4 positionVS = mul(ViewSrg::m_projectionMatrixInverse, projectedPos); - positionVS /= positionVS.w; // notice the normalization factor - crucial! - float4 positionWS = mul(ViewSrg::m_viewMatrixInverse, positionVS); - - return positionWS.xyz; -} - // Pack a float4 into an uint uint PackFloat4IntoUint(float4 vValue) { diff --git a/Gems/AtomTressFX/Code/Components/HairSystemComponent.cpp b/Gems/AtomTressFX/Code/Components/HairSystemComponent.cpp index 66ac3d2e09..2ae8b9d97d 100644 --- a/Gems/AtomTressFX/Code/Components/HairSystemComponent.cpp +++ b/Gems/AtomTressFX/Code/Components/HairSystemComponent.cpp @@ -80,8 +80,14 @@ namespace AZ // Load the AtomTressFX pass classes passSystem->AddPassCreator(Name("HairSkinningComputePass"), &HairSkinningComputePass::Create); + + // Load the PPLL render method passes passSystem->AddPassCreator(Name("HairPPLLRasterPass"), &HairPPLLRasterPass::Create); passSystem->AddPassCreator(Name("HairPPLLResolvePass"), &HairPPLLResolvePass::Create); + + // Load the ShortCut render method passes + passSystem->AddPassCreator(Name("HairShortCutGeometryDepthAlphaPass"), &HairShortCutGeometryDepthAlphaPass::Create); + passSystem->AddPassCreator(Name("HairShortCutGeometryShadingPass"), &HairShortCutGeometryShadingPass::Create); } void HairSystemComponent::Deactivate() diff --git a/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp index 7af5903cf3..b9c0d2e379 100644 --- a/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp +++ b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp @@ -165,6 +165,15 @@ namespace AZ return true; } + Data::Instance HairGeometryRasterPass::GetShader() + { + if (!m_initialized || !m_shader) + { + AZ_Error("Hair Gem", LoadShaderAndPipelineState(), "HairGeometryRasterPass could not initialize pipeline or shader"); + } + return m_shader; + } + void HairGeometryRasterPass::SchedulePacketBuild(HairRenderObject* hairObject) { m_newRenderObjects.insert(hairObject); @@ -188,7 +197,7 @@ namespace AZ // The PerPass is gathered through the RasterPass::m_shaderResourceGroup AZStd::lock_guard lock(m_mutex); - return hairObject->BuildPPLLDrawPacket(drawRequest); + return hairObject->BuildDrawPacket(m_shader.get(), drawRequest); } bool HairGeometryRasterPass::AddDrawPackets(AZStd::list>& hairRenderObjects) @@ -205,7 +214,7 @@ namespace AZ for (auto& renderObject : hairRenderObjects) { - const RHI::DrawPacket* drawPacket = renderObject->GetFillDrawPacket(); + const RHI::DrawPacket* drawPacket = renderObject->GetGeometrylDrawPacket(m_shader.get()); if (!drawPacket) { // might not be an error - the object might have just been added and the DrawPacket is // scheduled to be built when the render frame begins diff --git a/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h index a226d2c294..d3ba22039d 100644 --- a/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h +++ b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h @@ -51,7 +51,7 @@ namespace AZ //! The following will be called when an object was added or shader has been compiled void SchedulePacketBuild(HairRenderObject* hairObject); - Data::Instance GetShader() { return m_shader; } + Data::Instance GetShader(); void SetFeatureProcessor(HairFeatureProcessor* featureProcessor) { @@ -76,7 +76,6 @@ namespace AZ // Pass behavior overrides void InitializeInternal() override; -// void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; // Scope producer functions... diff --git a/Gems/AtomTressFX/Code/Passes/HairPPLLResolvePass.cpp b/Gems/AtomTressFX/Code/Passes/HairPPLLResolvePass.cpp index fa643a5488..de66d91e0b 100644 --- a/Gems/AtomTressFX/Code/Passes/HairPPLLResolvePass.cpp +++ b/Gems/AtomTressFX/Code/Passes/HairPPLLResolvePass.cpp @@ -30,6 +30,17 @@ namespace AZ HairPPLLResolvePass::HairPPLLResolvePass(const RPI::PassDescriptor& descriptor) : RPI::FullscreenTrianglePass(descriptor) { + o_enableShadows = AZ::Name("o_enableShadows"); + o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights"); + o_enablePunctualLights = AZ::Name("o_enablePunctualLights"); + o_enableAreaLights = AZ::Name("o_enableAreaLights"); + o_enableIBL = AZ::Name("o_enableIBL"); + o_hairLightingModel = AZ::Name("o_hairLightingModel"); + o_enableMarschner_R = AZ::Name("o_enableMarschner_R"); + o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT"); + o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT"); + o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff"); + o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff"); } void HairPPLLResolvePass::UpdateGlobalShaderOptions() @@ -38,17 +49,17 @@ namespace AZ m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings); - shaderOption.SetValue(AZ::Name("o_enableShadows"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows }); - shaderOption.SetValue(AZ::Name("o_enableDirectionalLights"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights }); - shaderOption.SetValue(AZ::Name("o_enablePunctualLights"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights }); - shaderOption.SetValue(AZ::Name("o_enableAreaLights"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights }); - shaderOption.SetValue(AZ::Name("o_enableIBL"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL }); - shaderOption.SetValue(AZ::Name("o_hairLightingModel"), AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) }); - shaderOption.SetValue(AZ::Name("o_enableMarschner_R"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R }); - shaderOption.SetValue(AZ::Name("o_enableMarschner_TRT"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT }); - shaderOption.SetValue(AZ::Name("o_enableMarschner_TT"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT }); - shaderOption.SetValue(AZ::Name("o_enableLongtitudeCoeff"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff }); - shaderOption.SetValue(AZ::Name("o_enableAzimuthCoeff"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff }); + shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows }); + shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights }); + shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights }); + shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights }); + shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL }); + shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) }); + shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R }); + shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT }); + shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT }); + shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff }); + shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff }); m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue(); } diff --git a/Gems/AtomTressFX/Code/Passes/HairPPLLResolvePass.h b/Gems/AtomTressFX/Code/Passes/HairPPLLResolvePass.h index bf1a3f768e..036659b9a5 100644 --- a/Gems/AtomTressFX/Code/Passes/HairPPLLResolvePass.h +++ b/Gems/AtomTressFX/Code/Passes/HairPPLLResolvePass.h @@ -58,9 +58,20 @@ namespace AZ void CompileResources(const RHI::FrameGraphCompileContext& context) override; private: + AZ::Name o_enableShadows; + AZ::Name o_enableDirectionalLights; + AZ::Name o_enablePunctualLights; + AZ::Name o_enableAreaLights; + AZ::Name o_enableIBL; + AZ::Name o_hairLightingModel; + AZ::Name o_enableMarschner_R; + AZ::Name o_enableMarschner_TRT; + AZ::Name o_enableMarschner_TT; + AZ::Name o_enableLongtitudeCoeff; + AZ::Name o_enableAzimuthCoeff; + HairPPLLResolvePass(const RPI::PassDescriptor& descriptor); - private: void UpdateGlobalShaderOptions(); HairGlobalSettings m_hairGlobalSettings; diff --git a/Gems/AtomTressFX/Code/Passes/HairParentPass.cpp b/Gems/AtomTressFX/Code/Passes/HairParentPass.cpp index 24cd6465dd..1b0b034dba 100644 --- a/Gems/AtomTressFX/Code/Passes/HairParentPass.cpp +++ b/Gems/AtomTressFX/Code/Passes/HairParentPass.cpp @@ -9,7 +9,6 @@ #include #include #include -#include namespace AZ { diff --git a/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryDepthAlphaPass.cpp b/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryDepthAlphaPass.cpp new file mode 100644 index 0000000000..5e402e33f6 --- /dev/null +++ b/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryDepthAlphaPass.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + namespace Hair + { + + HairShortCutGeometryDepthAlphaPass::HairShortCutGeometryDepthAlphaPass(const RPI::PassDescriptor& descriptor) + : HairGeometryRasterPass(descriptor) + { + SetShaderPath("Shaders/hairshortcutgeometrydepthalpha.azshader"); + } + + RPI::Ptr HairShortCutGeometryDepthAlphaPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew HairShortCutGeometryDepthAlphaPass(descriptor); + return pass; + } + + void HairShortCutGeometryDepthAlphaPass::BuildInternal() + { + RasterPass::BuildInternal(); // change this to call parent if the method exists + + if (!AcquireFeatureProcessor()) + { + return; + } + + LoadShaderAndPipelineState(); + } + + } // namespace Hair + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryDepthAlphaPass.h b/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryDepthAlphaPass.h new file mode 100644 index 0000000000..da6e28fe31 --- /dev/null +++ b/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryDepthAlphaPass.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include + +namespace AZ +{ + namespace RHI + { + struct DrawItem; + } + + namespace Render + { + namespace Hair + { + //! This geometry pass uses the following Srgs: + //! - PerPassSrg shared by all hair passes for the shared dynamic buffer + //! - PerMaterialSrg - used solely by this pass to alter the vertices and apply the visual + //! hair properties to each fragment. + //! - HairDynamicDataSrg (PerObjectSrg) - shared buffers views for this hair object only. + //! - PerViewSrg and PerSceneSrg - as per the data from Atom. + class HairShortCutGeometryDepthAlphaPass + : public HairGeometryRasterPass + { + AZ_RPI_PASS(HairShortCutGeometryDepthAlphaPass); + + public: + AZ_RTTI(HairShortCutGeometryDepthAlphaPass, "{F09A0411-B1FF-4085-98E7-6B8B0E1B2C3D}", HairGeometryRasterPass); + AZ_CLASS_ALLOCATOR(HairShortCutGeometryDepthAlphaPass, SystemAllocator, 0); + + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + explicit HairShortCutGeometryDepthAlphaPass(const RPI::PassDescriptor& descriptor); + + // Pass behavior overrides + void BuildInternal() override; + }; + + } // namespace Hair + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryShadingPass.cpp b/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryShadingPass.cpp new file mode 100644 index 0000000000..eda7f9f83a --- /dev/null +++ b/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryShadingPass.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + namespace Hair + { + + HairShortCutGeometryShadingPass::HairShortCutGeometryShadingPass(const RPI::PassDescriptor& descriptor) + : HairGeometryRasterPass(descriptor) + { + o_enableShadows = AZ::Name("o_enableShadows"); + o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights"); + o_enablePunctualLights = AZ::Name("o_enablePunctualLights"); + o_enableAreaLights = AZ::Name("o_enableAreaLights"); + o_enableIBL = AZ::Name("o_enableIBL"); + o_hairLightingModel = AZ::Name("o_hairLightingModel"); + o_enableMarschner_R = AZ::Name("o_enableMarschner_R"); + o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT"); + o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT"); + o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff"); + o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff"); + + SetShaderPath("Shaders/hairshortcutgeometryshading.azshader"); + } + + RPI::Ptr HairShortCutGeometryShadingPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew HairShortCutGeometryShadingPass(descriptor); + return pass; + } + + void HairShortCutGeometryShadingPass::UpdateGlobalShaderOptions() + { + RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup(); + + m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings); + + shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows }); + shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights }); + shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights }); + shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights }); + shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL }); + shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) }); + shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R }); + shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT }); + shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT }); + shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff }); + shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff }); + + m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue(); + } + + void HairShortCutGeometryShadingPass::CompileResources(const RHI::FrameGraphCompileContext& context) + { + if (!m_shaderResourceGroup || !AcquireFeatureProcessor()) + { + AZ_Error("Hair Gem", m_shaderResourceGroup, "HairShortCutGeometryShadingPass: missing Srg or no feature processor yet"); + return; // no error message due to FP - initialization not complete yet, wait for the next frame + } + + UpdateGlobalShaderOptions(); + + if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry()) + { + m_shaderResourceGroup->SetShaderVariantKeyFallbackValue(m_shaderOptions); + } + + // Update the material array constant buffer within the per pass srg + SrgBufferDescriptor descriptor = SrgBufferDescriptor( + RPI::CommonBufferPoolType::Constant, RHI::Format::Unknown, + sizeof(AMD::TressFXShadeParams), 1, + Name{ "HairMaterialsArray" }, Name{ "m_hairParams" }, 0, 0 + ); + + m_featureProcessor->GetMaterialsArray().UpdateGPUData(m_shaderResourceGroup, descriptor); + + // Compilation of remaining srgs will be done by the parent class + RPI::RasterPass::CompileResources(context); + } + + void HairShortCutGeometryShadingPass::BuildInternal() + { + RasterPass::BuildInternal(); // change this to call parent if the method exists + + if (!AcquireFeatureProcessor()) + { + return; + } + + LoadShaderAndPipelineState(); + } + + } // namespace Hair + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryShadingPass.h b/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryShadingPass.h new file mode 100644 index 0000000000..d728803473 --- /dev/null +++ b/Gems/AtomTressFX/Code/Passes/HairShortCutGeometryShadingPass.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include + +namespace AZ +{ + namespace RHI + { + struct DrawItem; + } + + namespace Render + { + namespace Hair + { + //! This geometry pass uses the following Srgs: + //! - PerPassSrg shared by all hair passes for the shared dynamic buffer + //! - PerMaterialSrg - used solely by this pass to alter the vertices and apply the visual + //! hair properties to each fragment. + //! - HairDynamicDataSrg (PerObjectSrg) - shared buffers views for this hair object only. + //! - PerViewSrg and PerSceneSrg - as per the data from Atom. + class HairShortCutGeometryShadingPass + : public HairGeometryRasterPass + { + AZ_RPI_PASS(HairShortCutGeometryShadingPass); + + public: + AZ_RTTI(HairShortCutGeometryShadingPass, "{11BA673D-0788-4B25-978D-9737BF4E48FE}", HairGeometryRasterPass); + AZ_CLASS_ALLOCATOR(HairShortCutGeometryShadingPass, SystemAllocator, 0); + + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + + protected: + AZ::Name o_enableShadows; + AZ::Name o_enableDirectionalLights; + AZ::Name o_enablePunctualLights; + AZ::Name o_enableAreaLights; + AZ::Name o_enableIBL; + AZ::Name o_hairLightingModel; + AZ::Name o_enableMarschner_R; + AZ::Name o_enableMarschner_TRT; + AZ::Name o_enableMarschner_TT; + AZ::Name o_enableLongtitudeCoeff; + AZ::Name o_enableAzimuthCoeff; + + explicit HairShortCutGeometryShadingPass(const RPI::PassDescriptor& descriptor); + + void UpdateGlobalShaderOptions(); + + // Pass behavior overrides + void BuildInternal() override; + + HairGlobalSettings m_hairGlobalSettings; + AZ::RPI::ShaderVariantKey m_shaderOptions; + }; + + } // namespace Hair + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index 7a317dc09c..b7c6bbce13 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -47,11 +47,11 @@ namespace AZ HairFeatureProcessor::HairFeatureProcessor() { + m_usePPLLRenderTechnique = false; // Use the ShortCut rendering technique + HairParentPassName = Name{ "HairParentPass" }; - HairPPLLRasterPassName = Name{ "HairPPLLRasterPass" }; - HairPPLLResolvePassName = Name{ "HairPPLLResolvePass" }; - + // Hair Skinning and Simulation Compute passes GlobalShapeConstraintsPassName = Name{ "HairGlobalShapeConstraintsComputePass" }; CalculateStrandDataPassName = Name{ "HairCalculateStrandLevelDataComputePass" }; VelocityShockPropagationPassName = Name{ "HairVelocityShockPropagationComputePass" }; @@ -59,12 +59,21 @@ namespace AZ LengthConstriantsWindAndCollisionPassName = Name{ "HairLengthConstraintsWindAndCollisionComputePass" }; UpdateFollowHairPassName = Name{ "HairUpdateFollowHairComputePass" }; + // PPLL render technique pases + HairPPLLRasterPassName = Name{ "HairPPLLRasterPass" }; + HairPPLLResolvePassName = Name{ "HairPPLLResolvePass" }; + + // ShortCut render technique pases + HairShortCutGeometryDepthAlphaPassName = Name{ "HairShortCutGeometryDepthAlphaPass" }; + HairShortCutResolveDepthPassName = Name{ "HairShortCutResolveDepthPass" }; + HairShortCutGeometryShadingPassName = Name{ "HairShortCutGeometryShadingPass" }; + HairShortCutResolveColorPassName = Name{ "HairShortCutResolveColorPass" }; + ++s_instanceCount; if (!CreatePerPassResources()) { // this might not be an error - if the pass system is still empty / minimal - // and these passes are not part of the minimal pipeline, they will not - // be created. + // and these passes are not part of the minimal pipeline, they will not be created. AZ_Error("Hair Gem", false, "Failed to create the hair shared buffer resource"); } } @@ -127,25 +136,33 @@ namespace AZ m_hairRenderObjects.push_back(renderObject); + // Adding the object will schedule Srgs binding and the DrawItem build for the geometry passes. BuildDispatchAndDrawItems(renderObject); EnablePasses(true); } - void HairFeatureProcessor::EnablePasses(bool enable) + void HairFeatureProcessor::EnablePasses([[maybe_unused]] bool enable) { + return; + + // [To Do] - This part should be enabled (remove the return) to reduce overhead + // when Hair is disabled / doesn't exist in the scene. + // Currently it might break features such as fog that depend on the output and for some + // reason doesn't quite work for ShortCut. + // The current overhead is minimal (< 0.1 msec) and this Gem is disabled by default. +/* if (!m_initialized) { return; } - for (auto& [passName, pass] : m_computePasses) + RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName); + if (desiredPass) { - pass->SetEnabled(enable); + desiredPass->SetEnabled(enable); } - - m_hairPPLLRasterPass->SetEnabled(enable); - m_hairPPLLResolvePass->SetEnabled(enable); +*/ } bool HairFeatureProcessor::RemoveHairRenderObject(Data::Instance renderObject) @@ -214,7 +231,8 @@ namespace AZ } if (m_forceRebuildRenderData) - { + { // In the case of a force build, schedule Srgs binding and the DrawItem build for + // the geometry passes of all existing hair objects. for (auto& hairRenderObject : m_hairRenderObjects) { BuildDispatchAndDrawItems(hairRenderObject); @@ -276,17 +294,32 @@ namespace AZ pass->AddDispatchItems(m_hairRenderObjects); } - // Add all hair objects to the Render / Raster Pass - m_hairPPLLRasterPass->AddDrawPackets(m_hairRenderObjects); + if (m_usePPLLRenderTechnique) + { + // Add all hair objects to the Render / Raster Pass + m_hairPPLLRasterPass->AddDrawPackets(m_hairRenderObjects); + } + else + { + m_hairShortCutGeometryDepthAlphaPass->AddDrawPackets(m_hairRenderObjects); + m_hairShortCutGeometryShadingPass->AddDrawPackets(m_hairRenderObjects); + } } void HairFeatureProcessor::ClearPasses() { m_initialized = false; // Avoid simulation or render m_computePasses.clear(); + + // PPLL geometry and resolve full screen passes m_hairPPLLRasterPass = nullptr; m_hairPPLLResolvePass = nullptr; + // ShortCut passes - Special handling of geometry passes only, and using the regular + // full screen pass for the resolve + m_hairShortCutGeometryDepthAlphaPass = nullptr; + m_hairShortCutGeometryShadingPass = nullptr; + // Mark for all passes to evacuate their render data and recreate it. m_forceRebuildRenderData = true; m_forceClearRenderData = true; @@ -338,6 +371,12 @@ namespace AZ ClearPasses(); + if (!m_renderPipeline) + { + AZ_Error("Hair Gem", false, "HairFeatureProcessor does NOT have render pipeline set yet"); + return false; + } + // Compute Passes - populate the passes map bool resultSuccess = InitComputePass(GlobalShapeConstraintsPassName); resultSuccess &= InitComputePass(CalculateStrandDataPassName); @@ -347,8 +386,15 @@ namespace AZ resultSuccess &= InitComputePass(UpdateFollowHairPassName); // Rendering Passes - resultSuccess &= InitPPLLFillPass(); - resultSuccess &= InitPPLLResolvePass(); + if (m_usePPLLRenderTechnique) + { + resultSuccess &= InitPPLLFillPass(); + resultSuccess &= InitPPLLResolvePass(); + } + else + { + resultSuccess &= InitShortCutRenderPasses(); + } m_initialized = resultSuccess; @@ -388,7 +434,8 @@ namespace AZ } } - // PPLL nodes buffer + // PPLL nodes buffer - created only if the PPLL technique is used + if (m_usePPLLRenderTechnique) { descriptor = SrgBufferDescriptor( RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown, @@ -425,11 +472,6 @@ namespace AZ bool HairFeatureProcessor::InitComputePass(const Name& passName, bool allowIterations) { m_computePasses[passName] = nullptr; - if (!m_renderPipeline) - { - AZ_Error("Hair Gem", false, "%s does NOT have render pipeline set yet", passName.GetCStr()); - return false; - } RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(passName); if (desiredPass) @@ -452,11 +494,6 @@ namespace AZ bool HairFeatureProcessor::InitPPLLFillPass() { m_hairPPLLRasterPass = nullptr; // reset it to null, just in case it fails to load the assets properly - if (!m_renderPipeline) - { - AZ_Error("Hair Gem", false, "Hair Fill Pass does NOT have render pipeline set yet"); - return false; - } RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLRasterPassName); if (desiredPass) @@ -466,7 +503,7 @@ namespace AZ } else { - AZ_Error("Hair Gem", false, "HairPPLLRasterPass does not have any valid passes. Check your game project's .pass assets."); + AZ_Error("Hair Gem", false, "HairPPLLRasterPass cannot be found. Check your game project's .pass assets."); return false; } return true; @@ -475,11 +512,6 @@ namespace AZ bool HairFeatureProcessor::InitPPLLResolvePass() { m_hairPPLLResolvePass = nullptr; // reset it to null, just in case it fails to load the assets properly - if (!m_renderPipeline) - { - AZ_Error("Hair Gem", false, "Hair Fill Pass does NOT have render pipeline set yet"); - return false; - } RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLResolvePassName); if (desiredPass) @@ -489,12 +521,46 @@ namespace AZ } else { - AZ_Error("Hair Gem", false, "HairPPLLResolvePassTemplate does not have valid passes. Check your game project's .pass assets."); + AZ_Error("Hair Gem", false, "HairPPLLResolvePass cannot be found. Check your game project's .pass assets."); return false; } return true; } + //! Set the two short cut geometry pases and assign them the FP. The other two full screen passes + //! are generic full screen passes and don't need any interaction with the FP. + bool HairFeatureProcessor::InitShortCutRenderPasses() + { + m_hairShortCutGeometryDepthAlphaPass = nullptr; + m_hairShortCutGeometryShadingPass = nullptr; + + m_hairShortCutGeometryDepthAlphaPass = static_cast( + m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryDepthAlphaPassName).get()); + if (m_hairShortCutGeometryDepthAlphaPass) + { + m_hairShortCutGeometryDepthAlphaPass->SetFeatureProcessor(this); + } + else + { + AZ_Error("Hair Gem", false, "HairShortCutResolveDepthPass cannot be found. Check your game project's .pass assets."); + return false; + } + + m_hairShortCutGeometryShadingPass = static_cast( + m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryShadingPassName).get()); + if (m_hairShortCutGeometryShadingPass) + { + m_hairShortCutGeometryShadingPass->SetFeatureProcessor(this); + } + else + { + AZ_Error("Hair Gem", false, "HairShortCutGeometryShadingPass cannot be found. Check your game project's .pass assets."); + return false; + } + + return true; + } + void HairFeatureProcessor::BuildDispatchAndDrawItems(Data::Instance renderObject) { HairRenderObject* renderObjectPtr = renderObject.get(); @@ -513,9 +579,18 @@ namespace AZ m_computePasses[UpdateFollowHairPassName]->BuildDispatchItem( renderObjectPtr, DispatchLevel::DISPATCHLEVEL_VERTEX); - // Render / Raster pass - adding the object will schedule Srgs binding - // and DrawItem build. - m_hairPPLLRasterPass->SchedulePacketBuild(renderObjectPtr); + // Schedule Srgs binding and the DrawItem build. + // Since this does not bind the PerPass srg but prepare the rest of the Srgs + // such as the dynamic srg, it should only be done once per object per frame. + if (m_usePPLLRenderTechnique) + { + m_hairPPLLRasterPass->SchedulePacketBuild(renderObjectPtr); + } + else + { + m_hairShortCutGeometryDepthAlphaPass->SchedulePacketBuild(renderObjectPtr); + m_hairShortCutGeometryShadingPass->SchedulePacketBuild(renderObjectPtr); + } } Data::Instance HairFeatureProcessor::GetHairSkinningComputegPass() @@ -527,14 +602,28 @@ namespace AZ return m_computePasses[GlobalShapeConstraintsPassName]; } - Data::Instance HairFeatureProcessor::GetHairPPLLRasterPass() + Data::Instance HairFeatureProcessor::GetGeometryRasterShader() { - if (!m_hairPPLLRasterPass) + if (m_usePPLLRenderTechnique) { - Init(m_renderPipeline); + if (!m_hairPPLLRasterPass && !Init(m_renderPipeline)) + { + AZ_Error("Hair Gem", false, + "GetGeometryRasterShader - m_hairPPLLRasterPass was not created"); + return nullptr; + } + return m_hairPPLLRasterPass->GetShader(); } - return m_hairPPLLRasterPass; + + if (!m_hairShortCutGeometryDepthAlphaPass && !Init(m_renderPipeline)) + { + AZ_Error("Hair Gem", false, + "GetGeometryRasterShader - m_hairShortCutGeometryDepthAlphaPass was not created"); + return nullptr; + } + return m_hairShortCutGeometryDepthAlphaPass->GetShader(); } + } // namespace Hair } // namespace Render } // namespace AZ diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h index c56dc5c921..46660a6623 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h @@ -17,14 +17,19 @@ #include #include +#include // Hair specific #include #include + #include #include +#include +#include + #include #include #include @@ -73,9 +78,7 @@ namespace AZ { Name HairParentPassName; - Name HairPPLLRasterPassName; - Name HairPPLLResolvePassName; - + // Compute passes Name GlobalShapeConstraintsPassName; Name CalculateStrandDataPassName; Name VelocityShockPropagationPassName; @@ -83,6 +86,16 @@ namespace AZ Name LengthConstriantsWindAndCollisionPassName; Name UpdateFollowHairPassName; + // PPLL render passes + Name HairPPLLRasterPassName; + Name HairPPLLResolvePassName; + + // ShortCut render passes + Name HairShortCutGeometryDepthAlphaPassName; + Name HairShortCutResolveDepthPassName; + Name HairShortCutGeometryShadingPassName; + Name HairShortCutResolveColorPassName; + public: AZ_RTTI(AZ::Render::Hair::HairFeatureProcessor, "{5F9DDA81-B43F-4E30-9E56-C7C3DC517A4C}", RPI::FeatureProcessor); @@ -117,6 +130,7 @@ namespace AZ Data::Instance GetHairSkinningComputegPass(); Data::Instance GetHairPPLLRasterPass(); + Data::Instance GetGeometryRasterShader(); //! Update the hair objects materials array. void FillHairMaterialsArray(std::vector& renderSettings); @@ -144,6 +158,7 @@ namespace AZ bool InitPPLLFillPass(); bool InitPPLLResolvePass(); + bool InitShortCutRenderPasses(); bool InitComputePass(const Name& passName, bool allowIterations = false); void BuildDispatchAndDrawItems(Data::Instance renderObject); @@ -168,10 +183,14 @@ namespace AZ //! Simulation Compute Passes AZStd::unordered_map > m_computePasses; - // Render Passes + // PPLL Render Passes Data::Instance m_hairPPLLRasterPass = nullptr; Data::Instance m_hairPPLLResolvePass = nullptr; + // ShortCut Render Passes - special case for the geometry render passes + Data::Instance m_hairShortCutGeometryDepthAlphaPass = nullptr; + Data::Instance m_hairShortCutGeometryShadingPass = nullptr; + //-------------------------------------------------------------- // Per Pass Resources //-------------------------------------------------------------- @@ -196,6 +215,7 @@ namespace AZ bool m_forceClearRenderData = false; bool m_initialized = false; bool m_isEnabled = true; + bool m_usePPLLRenderTechnique = true; static uint32_t s_instanceCount; HairGlobalSettings m_hairGlobalSettings; diff --git a/Gems/AtomTressFX/Code/Rendering/HairRenderObject.cpp b/Gems/AtomTressFX/Code/Rendering/HairRenderObject.cpp index baf986daf6..4d615e31ae 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairRenderObject.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairRenderObject.cpp @@ -993,7 +993,7 @@ namespace AZ //------------------------------------- // Dynamic buffers, data and Srg creation - shared between passes and changed on the GPU if (!m_dynamicHairData.CreateDynamicGPUResources( - m_skinningShader, m_PPLLFillShader, + m_skinningShader, m_geometryRasterShader, m_NumTotalVertices, m_NumTotalStrands)) { AZ_Error("Hair Gem", false, "Hair - Error creating dynamic resources [%s]", assetName ); @@ -1028,7 +1028,7 @@ namespace AZ // Rendering setup bool renderResourcesSuccess; - renderResourcesSuccess = CreateRenderingGPUResources(m_PPLLFillShader, *asset, assetName); + renderResourcesSuccess = CreateRenderingGPUResources(m_geometryRasterShader, *asset, assetName); renderResourcesSuccess &= PopulateDrawStrandsBindSet(renderSettings); renderResourcesSuccess &= UploadRenderingGPUResources(*asset); @@ -1057,17 +1057,10 @@ namespace AZ } { - Data::Instance rasterPass = m_featureProcessor->GetHairPPLLRasterPass(); - if (!rasterPass.get()) + m_geometryRasterShader = m_featureProcessor->GetGeometryRasterShader(); + if (!m_geometryRasterShader) { - AZ_Error("Hair Gem", false, "Failed to get PPLL raster fill Pass."); - return false; - } - - m_PPLLFillShader = rasterPass->GetShader(); - if (!m_PPLLFillShader) - { - AZ_Error("Hair Gem", false, "Failed to get hair raster fill shader from raster pass"); + AZ_Error("Hair Gem", false, "Failed to get hair geometry raster shader"); return false; } } @@ -1116,7 +1109,7 @@ namespace AZ return updatedCB; } - bool HairRenderObject::BuildPPLLDrawPacket(RHI::DrawPacketBuilder::DrawRequest& drawRequest) + bool HairRenderObject::BuildDrawPacket(RPI::Shader* geometryShader, RHI::DrawPacketBuilder::DrawRequest& drawRequest) { RHI::DrawPacketBuilder drawPacketBuilder; RHI::DrawIndexed drawIndexed; @@ -1159,21 +1152,38 @@ namespace AZ drawPacketBuilder.AddShaderResourceGroup(simSrg->GetRHIShaderResourceGroup()); drawPacketBuilder.AddDrawItem(drawRequest); - if (m_fillDrawPacket) - { - delete m_fillDrawPacket; - } - m_fillDrawPacket = drawPacketBuilder.End(); - - if (!m_fillDrawPacket) + const RHI::DrawPacket* drawPacket = drawPacketBuilder.End(); + if (!drawPacket) { AZ_Error("Hair Gem", false, "Failed to build the hair DrawPacket."); return false; } + // Insert the newly created draw packet to the map based on its shader + auto iter = m_geometryDrawPackets.find(geometryShader); + if (iter != m_geometryDrawPackets.end()) + { + delete iter->second; + iter->second = drawPacket; + } + else + { + m_geometryDrawPackets[geometryShader] = drawPacket; + } + return true; } + const RHI::DrawPacket* HairRenderObject::GetGeometrylDrawPacket(RPI::Shader* geometryShader) + { + auto iter = m_geometryDrawPackets.find(geometryShader); + if (iter == m_geometryDrawPackets.end()) + { + return nullptr; + } + return iter->second; + } + const RHI::DispatchItem* HairRenderObject::GetDispatchItem(RPI::Shader* computeShader) { auto dispatchIter = m_dispatchItems.find(computeShader); @@ -1210,4 +1220,3 @@ namespace AZ } // namespace Hair } // namespace Render } // namespace AZ - diff --git a/Gems/AtomTressFX/Code/Rendering/HairRenderObject.h b/Gems/AtomTressFX/Code/Rendering/HairRenderObject.h index fa4095eed0..817ebd89fa 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairRenderObject.h +++ b/Gems/AtomTressFX/Code/Rendering/HairRenderObject.h @@ -179,14 +179,9 @@ namespace AZ AMD::TressFXSimulationSettings* simSettings, AMD::TressFXRenderingSettings* renderSettings ); - //! Creates and fill the draw item associated with the PPLL render of the - //! current hair object - const RHI::DrawPacket* GetFillDrawPacket() - { - return m_fillDrawPacket; - } + bool BuildDrawPacket(RPI::Shader* geometryShader, RHI::DrawPacketBuilder::DrawRequest& drawRequest); - bool BuildPPLLDrawPacket(RHI::DrawPacketBuilder::DrawRequest& drawRequest); + const RHI::DrawPacket* GetGeometrylDrawPacket(RPI::Shader* geometryShader); //! Creates and fill the dispatch item associated with the compute shader bool BuildDispatchItem(RPI::Shader* computeShader, DispatchLevel dispatchLevel); @@ -302,17 +297,20 @@ namespace AZ //! responsible for the various stages and passes' updates HairFeatureProcessor* m_featureProcessor = nullptr; - //! The dispatch item used for the skinning - HairDispatchItem m_skinningDispatchItem; - - //! Compute dispatch items map per the existing passes - AZStd::map> m_dispatchItems; - //! Skinning compute shader used for creation of the compute Srgs and dispatch item Data::Instance m_skinningShader = nullptr; - //! PPLL fill shader used for creation of the raster Srgs and draw item - Data::Instance m_PPLLFillShader = nullptr; + //! Compute dispatch items map per the existing passes + AZStd::unordered_map> m_dispatchItems; + + //! Geometry raster shader used for creation of the raster Srgs. + //! Since the Srgs for geometry raster are the same across the shaders we keep + //! only a single shader - if this to change in the future, several shaders and sets + //! of dynamic Srgs should be created. + Data::Instance m_geometryRasterShader = nullptr; + + //! DrawPacket for the multi object geometry raster pass. + AZStd::unordered_map m_geometryDrawPackets; float m_frameDeltaTime = 0.02; @@ -378,9 +376,6 @@ namespace AZ //! Index buffer for the render pass via draw calls - naming was kept Data::Instance m_indexBuffer; RHI::IndexBufferView m_indexBufferView; - - //! DrawPacket for the multi object raster fill pass. - const RHI::DrawPacket* m_fillDrawPacket = nullptr; //------------------------------------------------------------------- AZStd::mutex m_mutex; diff --git a/Gems/AtomTressFX/Hair_files.cmake b/Gems/AtomTressFX/Hair_files.cmake index 000abeb559..180685d499 100644 --- a/Gems/AtomTressFX/Hair_files.cmake +++ b/Gems/AtomTressFX/Hair_files.cmake @@ -67,6 +67,13 @@ set(FILES # Base class of all geometry raster passes Code/Passes/HairGeometryRasterPass.h Code/Passes/HairGeometryRasterPass.cpp + + # ShortCut rendering technique - pass classes + Code/Passes/HairShortCutGeometryDepthAlphaPass.h + Code/Passes/HairShortCutGeometryDepthAlphaPass.cpp + Code/Passes/HairShortCutGeometryShadingPass.h + Code/Passes/HairShortCutGeometryShadingPass.cpp + # PPLL rendering technique - geometry raster pass Code/Passes/HairPPLLRasterPass.h Code/Passes/HairPPLLRasterPass.cpp @@ -84,30 +91,37 @@ set(FILES Code/Assets/HairAsset.cpp #) #set(shaders_sources - # Srgs and Utility files - Assets/Shaders/HairSrgs.azsli - Assets/Shaders/HairSimulationSrgs.azsli + # Geometry and Full Screen azsl utility files Assets/Shaders/HairRenderingSrgs.azsli - Assets/Shaders/HairSimulationCommon.azsli Assets/Shaders/HairStrands.azsli Assets/Shaders/HairUtilities.azsli + Assets/Shaders/HairFullScreenUtils.azsli Assets/Shaders/HairLighting.azsli Assets/Shaders/HairLightingEquations.azsli Assets/Shaders/HairLightTypes.azsli Assets/Shaders/HairSurface.azsli - # Simulation Compute shaders - Assets/Shaders/HairSimulationCompute.azsl - - # Collision shaders - to be included soon -# Assets/Shaders/HairCollisionPrepareSDF.azsl -# Assets/Shaders/HairCollisionWithSDF.azsl + # ShortCut technique shaders (using multiple RTs instead of PPLL for GPU memory reduction) + Assets/Shaders/HairShortCutGeometryDepthAlpha.azsl + Assets/Shaders/HairShortCutResolveDepth.azsl + Assets/Shaders/HairShortCutGeometryShading.azsl + Assets/Shaders/HairShortCutResolveColor.azsl - # Rendering shaders + # Rendering azsl files Assets/Shaders/HairRenderingFillPPLL.azsl Assets/Shaders/HairRenderingResolvePPLL.azsl - # Simulation .shader files + # Simulation Compute azsl files + Assets/Shaders/HairComputeSrgs.azsli + Assets/Shaders/HairSimulationComputeSrgs.azsli + Assets/Shaders/HairSimulationCommon.azsli + Assets/Shaders/HairSimulationCompute.azsl + + # Collision azsl files - to be included soon +# Assets/Shaders/HairCollisionPrepareSDF.azsl +# Assets/Shaders/HairCollisionWithSDF.azsl + + # Simulation Compute .shader files Assets/Shaders/HairGlobalShapeConstraintsCompute.shader Assets/Shaders/HairCalculateStrandLevelDataCompute.shader Assets/Shaders/HairVelocityShockPropagationCompute.shader @@ -115,9 +129,15 @@ set(FILES Assets/Shaders/HairLengthConstraintsWindAndCollisionCompute.shader Assets/Shaders/HairUpdateFollowHairCompute.shader - # Rendering .shader file + # PPLL Render .shader file Assets/Shaders/HairRenderingFillPPLL.shader Assets/Shaders/HairRenderingResolvePPLL.shader + + # ShortCut Render .shader file + Assets/Shaders/HairShortCutGeometryDepthAlpha.shader + Assets/Shaders/HairShortCutResolveDepth.shader + Assets/Shaders/HairShortCutGeometryShading.shader + Assets/Shaders/HairShortCutResolveColor.shader # Colisions .shader files - to be included soon # Assets/Shaders/HairCollisionInitializeSDF.shader @@ -127,15 +147,25 @@ set(FILES #) # #set(atom_hair_passes + # Compute simulation and skinning passes Assets/Passes/HairParentPass.pass + Assets/Passes/HairParentShortCutPass.pass Assets/Passes/HairGlobalShapeConstraintsCompute.pass Assets/Passes/HairCalculateStrandLevelDataCompute.pass Assets/Passes/HairVelocityShockPropagationCompute.pass Assets/Passes/HairLocalShapeConstraintsCompute.pass Assets/Passes/HairLengthConstraintsWindAndCollisionCompute.pass Assets/Passes/HairUpdateFollowHairCompute.pass + + # PPLL render passes Assets/Passes/HairFillPPLL.pass Assets/Passes/HairResolvePPLL.pass + + # Shortcut render passes + Assets/Passes/HairShortCutGeometryDepthAlpha.pass + Assets/Passes/HairShortCutResolveDepth.pass + Assets/Passes/HairShortCutGeometryShading.pass + Assets/Passes/HairShortCutResolveColor.pass ) set(SKIP_UNITY_BUILD_INCLUSION_FILES From 33490ed6dfe03431057567698703cfc8fab4946b Mon Sep 17 00:00:00 2001 From: srikappa-amzn Date: Thu, 21 Oct 2021 11:42:41 -0700 Subject: [PATCH 096/152] Avoided a string copy by using path view Signed-off-by: srikappa-amzn --- .../AzToolsFramework/Slice/SliceUtilities.cpp | 5 +++-- .../AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp index 69d4a95b59..c13f7dd848 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp @@ -2651,11 +2651,12 @@ namespace AzToolsFramework AZ::IO::FixedMaxPath lexicallyNormalPath = AZ::IO::PathView(slicePath.toUtf8().constData()).LexicallyNormal(); bool isPathSafeForAssets = false; - for (AZ::IO::Path assetSafeFolder : assetSafeFolders) + for (const AZStd::string& assetSafeFolder : assetSafeFolders) { + AZ::IO::PathView assetSafeFolderView(assetSafeFolder); // Check if the slice path is relative to the safe asset directory. // The Path classes are being used to make this check case insensitive. - if (lexicallyNormalPath.IsRelativeTo(assetSafeFolder)) + if (lexicallyNormalPath.IsRelativeTo(assetSafeFolderView)) { isPathSafeForAssets = true; break; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 0bb596d0f6..7a50961910 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -803,11 +803,12 @@ namespace AzToolsFramework AZ::IO::FixedMaxPath lexicallyNormalPath = AZ::IO::PathView(prefabPath.toUtf8().constData()).LexicallyNormal(); bool isPathSafeForAssets = false; - for (AZ::IO::Path assetSafeFolder : assetSafeFolders) + for (const AZStd::string& assetSafeFolder : assetSafeFolders) { + AZ::IO::PathView assetSafeFolderView(assetSafeFolder); // Check if the prefabPath is relative to the safe asset directory. // The Path classes are being used to make this check case insensitive. - if (lexicallyNormalPath.IsRelativeTo(assetSafeFolder)) + if (lexicallyNormalPath.IsRelativeTo(assetSafeFolderView)) { isPathSafeForAssets = true; break; From a1edb2c4ac0e8b56e150be02277a091c27c6fffc Mon Sep 17 00:00:00 2001 From: sweeneys Date: Thu, 21 Oct 2021 11:51:47 -0700 Subject: [PATCH 097/152] Update platform defaults to return a launcher instead of a string Signed-off-by: sweeneys --- .../launchers/launcher_helper.py | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py index d37623f352..9a75bbe3ee 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py @@ -9,14 +9,15 @@ Main launchers module, provides a facade for creating launchers. import logging -import ly_test_tools._internal.managers.workspace import ly_test_tools +import ly_test_tools._internal.managers.workspace as workspace_manager +import ly_test_tools.launchers.platforms.base as base_launcher log = logging.getLogger(__name__) def create_launcher(workspace, launcher_platform=ly_test_tools.HOST_OS_PLATFORM, args=None): - # type: (ly_test_tools.managers.workspace.WorkspaceManager, str, List[str]) -> Launcher + # type: (workspace_manager.AbstractWorkspaceManager, str, list[str]) -> base_launcher.Launcher """ Create a launcher compatible with the specified workspace, if no specific launcher is found return a generic one. @@ -25,12 +26,16 @@ def create_launcher(workspace, launcher_platform=ly_test_tools.HOST_OS_PLATFORM, :param args: List of arguments to pass to the launcher's 'args' argument during construction :return: Launcher instance """ - launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_PLATFORM) + launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform) + if not launcher_class: + log.warning(f"Using default launcher for '{ly_test_tools.HOST_OS_PLATFORM}' " + f"as no option is available for '{launcher_platform}'") + launcher_class = ly_test_tools.LAUNCHERS.get(ly_test_tools.HOST_OS_PLATFORM) return launcher_class(workspace, args) def create_dedicated_launcher(workspace, launcher_platform=ly_test_tools.HOST_OS_DEDICATED_SERVER, args=None): - # type: (ly_test_tools.managers.workspace.WorkspaceManager, str, List[str]) -> Launcher + # type: (workspace_manager.AbstractWorkspaceManager, str, list[str]) -> base_launcher.Launcher """ Create a dedicated launcher compatible with the specified workspace. Dedicated Launcher is only supported on the Linux and Windows Platform @@ -41,11 +46,15 @@ def create_dedicated_launcher(workspace, launcher_platform=ly_test_tools.HOST_OS :return: Launcher instance """ launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_DEDICATED_SERVER) + if not launcher_class: + log.warning(f"Using default dedicated launcher for '{ly_test_tools.HOST_OS_PLATFORM}' " + f"as no option is available for '{launcher_platform}'") + launcher_class = ly_test_tools.LAUNCHERS.get(ly_test_tools.HOST_OS_DEDICATED_SERVER) return launcher_class(workspace, args) def create_editor(workspace, launcher_platform=ly_test_tools.HOST_OS_EDITOR, args=None): - # type: (ly_test_tools.managers.workspace.WorkspaceManager, str, List[str]) -> Launcher + # type: (workspace_manager.AbstractWorkspaceManager, str, list[str]) -> base_launcher.Launcher """ Create an Editor compatible with the specified workspace. Editor is only officially supported on the Windows Platform. @@ -56,11 +65,15 @@ def create_editor(workspace, launcher_platform=ly_test_tools.HOST_OS_EDITOR, arg :return: Editor instance """ launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_EDITOR) + if not launcher_class: + log.warning(f"Using default editor launcher for '{ly_test_tools.HOST_OS_PLATFORM}' " + f"as no option is available for '{launcher_platform}'") + launcher_class = ly_test_tools.LAUNCHERS.get(ly_test_tools.HOST_OS_EDITOR) return launcher_class(workspace, args) def create_generic_launcher(workspace, launcher_platform, exe_file_name, args=None): - # type: (ly_test_tools.managers.workspace.WorkspaceManager, str, str, List[str]) -> Launcher + # type: (workspace_manager.AbstractWorkspaceManager, str, str, list[str]) -> base_launcher.Launcher """ Create a generic launcher compatible with the specified workspace. Allows custom .exe files to serve as the launcher instead of ones listed in the ly_test_tools.LAUNCHERS constant @@ -72,4 +85,8 @@ def create_generic_launcher(workspace, launcher_platform, exe_file_name, args=No :return: Launcher instance. """ launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_GENERIC_EXECUTABLE) + if not launcher_class: + log.warning(f"Using default generic executable launcher for '{ly_test_tools.HOST_OS_PLATFORM}' " + f"as no option is available for '{launcher_platform}'") + launcher_class = ly_test_tools.LAUNCHERS.get(ly_test_tools.HOST_OS_GENERIC_EXECUTABLE) return launcher_class(workspace, exe_file_name, args) From 7eaf54e48dd272ed2bd34bf03f4f69dbf7c1c213 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:52:50 -0700 Subject: [PATCH 098/152] Adding 5361st maybe_unused to the code base to pass AR Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp | 1 + 1 file changed, 1 insertion(+) 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 dad511def1..b8115dff10 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -298,6 +298,7 @@ namespace AZ imageDimensions.SetZ(1.0f / float(size.m_width)); imageDimensions.SetW(1.0f / float(size.m_height)); + [[maybe_unused]] bool success = m_shaderResourceGroup->SetConstant(binding.m_shaderImageDimensionsNameIndex, imageDimensions); AZ_Assert(success, "Pass [%s] Could not find float4 constant [%s] in Shader Resource Group [%s]", GetPathName().GetCStr(), From 68b585102e5c8b64254683c78e734576b20910de Mon Sep 17 00:00:00 2001 From: hershey5045 <43485729+hershey5045@users.noreply.github.com> Date: Thu, 21 Oct 2021 12:06:46 -0700 Subject: [PATCH 099/152] Re-order some color grading operations. Increase values for SMH to account for HDR colors. (#4856) Signed-off-by: rbarrand Co-authored-by: rbarrand --- .../PostProcessing/HDRColorGradingCommon.azsl | 5 ++--- .../ColorGrading/EditorHDRColorGradingComponent.cpp | 12 ++++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl index 099a6394d4..a380a0a547 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl @@ -138,7 +138,7 @@ float3 ColorGrade(float3 frameColor) PassSrg::m_colorFilterMultiply, PassSrg::m_colorFilterIntensity), PassSrg::m_colorAdjustmentWeight); frameColor = max(frameColor, 0.0); frameColor = lerp(frameColor, ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPreSaturation), PassSrg::m_colorAdjustmentWeight); - + frameColor = max(frameColor, 0.0); frameColor = ColorGradeSplitTone(frameColor, PassSrg::m_splitToneBalance, PassSrg::m_splitToneWeight, PassSrg::m_splitToneShadowsColor, PassSrg::m_splitToneHighlightsColor); frameColor = ColorGradeChannelMixer(frameColor, PassSrg::m_channelMixingRed, PassSrg::m_channelMixingGreen, PassSrg::m_channelMixingBlue); @@ -147,8 +147,7 @@ float3 ColorGrade(float3 frameColor) PassSrg::m_smhHighlightsStart, PassSrg::m_smhHighlightsEnd, PassSrg::m_smhWeight, PassSrg::m_smhShadowsColor, PassSrg::m_smhMidtonesColor, PassSrg::m_smhHighlightsColor); - - frameColor = lerp(frameColor, ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPostSaturation), PassSrg::m_finalAdjustmentWeight); frameColor = lerp(frameColor, ColorGradeHueShift(frameColor, PassSrg::m_colorGradingHueShift), PassSrg::m_finalAdjustmentWeight); + frameColor = lerp(frameColor, ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPostSaturation), PassSrg::m_finalAdjustmentWeight); return max(frameColor.rgb, 0.0); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp index 2d8f83a32a..e32b26f0c5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp @@ -131,16 +131,20 @@ namespace AZ ->Attribute(Edit::Attributes::Max, 1.0f) ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhShadowsStart, "Shadows Start", "SMH Shadows Start Value") ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 1.0f) + ->Attribute(Edit::Attributes::Max, 16.0f) + ->Attribute(Edit::Attributes::SoftMax, 2.0f) ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhShadowsEnd, "Shadows End", "SMH Shadows End Value") ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 1.0f) + ->Attribute(Edit::Attributes::Max, 16.0f) + ->Attribute(Edit::Attributes::SoftMax, 2.0f) ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhHighlightsStart, "Highlights Start", "SMH Highlights Start Value") ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 1.0f) + ->Attribute(Edit::Attributes::Max, 16.0f) + ->Attribute(Edit::Attributes::SoftMax, 2.0f) ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhHighlightsEnd, "Highlights End", "SMH Highlights End Value") ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 1.0f) + ->Attribute(Edit::Attributes::Max, 16.0f) + ->Attribute(Edit::Attributes::SoftMax, 2.0f) ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhShadowsColor, "Shadows Color", "SMH Shadows Color") ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhMidtonesColor, "Midtones Color", "SMH Midtones Color") ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhHighlightsColor, "Highlights Color", "SMH Highlights Color") From ea325d356c736a7cf5544a1b18b42252720c91ae Mon Sep 17 00:00:00 2001 From: sweeneys Date: Thu, 21 Oct 2021 12:05:22 -0700 Subject: [PATCH 100/152] updated defaults in all launchers, with additional unit test Signed-off-by: sweeneys --- .../ly_test_tools/launchers/launcher_helper.py | 12 ++++++------ Tools/LyTestTools/tests/unit/test_launcher_base.py | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py index 9a75bbe3ee..ac4ad621da 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py @@ -45,9 +45,9 @@ def create_dedicated_launcher(workspace, launcher_platform=ly_test_tools.HOST_OS :param args: List of arguments to pass to the launcher's 'args' argument during construction :return: Launcher instance """ - launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_DEDICATED_SERVER) + launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform) if not launcher_class: - log.warning(f"Using default dedicated launcher for '{ly_test_tools.HOST_OS_PLATFORM}' " + log.warning(f"Using default dedicated launcher for '{ly_test_tools.HOST_OS_DEDICATED_SERVER}' " f"as no option is available for '{launcher_platform}'") launcher_class = ly_test_tools.LAUNCHERS.get(ly_test_tools.HOST_OS_DEDICATED_SERVER) return launcher_class(workspace, args) @@ -64,9 +64,9 @@ def create_editor(workspace, launcher_platform=ly_test_tools.HOST_OS_EDITOR, arg :param args: List of arguments to pass to the launcher's 'args' argument during construction :return: Editor instance """ - launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_EDITOR) + launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform) if not launcher_class: - log.warning(f"Using default editor launcher for '{ly_test_tools.HOST_OS_PLATFORM}' " + log.warning(f"Using default editor launcher for '{ly_test_tools.HOST_OS_EDITOR}' " f"as no option is available for '{launcher_platform}'") launcher_class = ly_test_tools.LAUNCHERS.get(ly_test_tools.HOST_OS_EDITOR) return launcher_class(workspace, args) @@ -84,9 +84,9 @@ def create_generic_launcher(workspace, launcher_platform, exe_file_name, args=No :param args: List of arguments to pass to the launcher's 'args' argument during construction :return: Launcher instance. """ - launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_GENERIC_EXECUTABLE) + launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform) if not launcher_class: - log.warning(f"Using default generic executable launcher for '{ly_test_tools.HOST_OS_PLATFORM}' " + log.warning(f"Using default generic executable launcher for '{ly_test_tools.HOST_OS_GENERIC_EXECUTABLE}' " f"as no option is available for '{launcher_platform}'") launcher_class = ly_test_tools.LAUNCHERS.get(ly_test_tools.HOST_OS_GENERIC_EXECUTABLE) return launcher_class(workspace, exe_file_name, args) diff --git a/Tools/LyTestTools/tests/unit/test_launcher_base.py b/Tools/LyTestTools/tests/unit/test_launcher_base.py index 1ab9129a7d..5264e1bc28 100755 --- a/Tools/LyTestTools/tests/unit/test_launcher_base.py +++ b/Tools/LyTestTools/tests/unit/test_launcher_base.py @@ -221,3 +221,10 @@ class TestLauncherBuilder(object): under_test = ly_test_tools.launchers.launcher_helper.create_editor( dummy_workspace, ly_test_tools.HOST_OS_GENERIC_EXECUTABLE) assert isinstance(under_test, ly_test_tools.launchers.Launcher) + + def test_CreateEditor_InvalidPlatform_ValidLauncherStillReturned(self): + dummy_workspace = mock.MagicMock() + dummy_workspace.paths.build_directory.return_value = 'dummy' + under_test = ly_test_tools.launchers.launcher_helper.create_editor( + dummy_workspace, 'does not exist') + assert isinstance(under_test, ly_test_tools.launchers.Launcher) From 731680294138c193c69d0f1f654f4478df5f74d3 Mon Sep 17 00:00:00 2001 From: chiyenteng <82238204+chiyenteng@users.noreply.github.com> Date: Thu, 21 Oct 2021 12:08:00 -0700 Subject: [PATCH 101/152] Add Detach and Duplicate Prefab basic workflow auto test (#4506) - Add a new automated test PrefabBasicWorkflow_CreateReparentAndDetachPrefab for verifying prefab detachment basic workflow. - Add a new automated test PrefabBasicWorkflow_CreateAndDuplicatePrefab for verifying prefab detachment basic workflow. - Fix a bug related to sets of entity ids in Reparent helper function . --- .../editor_python_test_tools/prefab_utils.py | 206 ++++++++++++++---- .../Gem/PythonTests/Prefab/TestSuite_Main.py | 8 + ...efabBasicWorkflow_CreateAndDeletePrefab.py | 7 +- ...bBasicWorkflow_CreateAndDuplicatePrefab.py | 32 +++ ...abBasicWorkflow_CreateAndReparentPrefab.py | 11 +- .../tests/PrefabBasicWorkflow_CreatePrefab.py | 5 +- ...cWorkflow_CreateReparentAndDetachPrefab.py | 51 +++++ .../PrefabBasicWorkflow_InstantiatePrefab.py | 3 +- .../Prefab/tests/PrefabTestUtils.py | 27 --- .../Prefab/EditorPrefabComponent.cpp | 9 + .../Prefab/EditorPrefabComponent.h | 4 +- .../Prefab/PrefabPublicHandler.cpp | 9 +- .../Prefab/PrefabPublicHandler.h | 2 +- .../Prefab/PrefabPublicInterface.h | 8 +- .../Prefab/PrefabPublicRequestBus.h | 24 ++ .../Prefab/PrefabPublicRequestHandler.cpp | 17 ++ .../Prefab/PrefabPublicRequestHandler.h | 3 + .../AzToolsFramework/Prefab/PrefabUndo.cpp | 2 +- .../AzToolsFramework/Prefab/PrefabUndo.h | 2 +- 19 files changed, 328 insertions(+), 102 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDuplicatePrefab.py create mode 100644 AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateReparentAndDetachPrefab.py diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py index 2d8b124125..10a6ab1ef4 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py @@ -9,6 +9,7 @@ from __future__ import annotations from collections import Counter from collections import deque from os import path +from pathlib import Path from PySide2 import QtWidgets @@ -20,6 +21,10 @@ from editor_python_test_tools.utils import Report import azlmbr.entity as entity import azlmbr.bus as bus +import azlmbr.components as components +import azlmbr.editor as editor +import azlmbr.globals +import azlmbr.math as math import azlmbr.prefab as prefab import editor_python_test_tools.pyside_utils as pyside_utils @@ -57,26 +62,46 @@ class PrefabInstance: def __hash__(self): return hash(self.container_entity.id) - """ - See if this instance is valid to be used with other prefab operations. - :return: Whether the target instance is valid or not. - """ def is_valid(self) -> bool: + """ + See if this instance is valid to be used with other prefab operations. + :return: Whether the target instance is valid or not. + """ return self.container_entity.id.IsValid() and self.prefab_file_name in Prefab.existing_prefabs - """ - Reparent this instance to target parent entity. - The function will also check pop up dialog ui in editor to see if there's prefab cyclical dependency error while reparenting prefabs. - :param parent_entity_id: The id of the entity this instance should be a child of in the transform hierarchy next. - """ + def has_editor_prefab_component(self) -> bool: + """ + Check if the instance's container entity contains EditorPrefabComponent. + :return: Whether the container entity of target instance has EditorPrefabComponent in it or not. + """ + return editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", self.container_entity.id, azlmbr.globals.property.EditorPrefabComponentTypeId) + + def is_at_position(self, expected_position): + """ + Check if the instance's container entity is at expected position given. + :return: Whether the container entity of target instance is at expected position or not. + """ + actual_position = components.TransformBus(bus.Event, "GetWorldTranslation", self.container_entity.id) + is_at_position = actual_position.IsClose(expected_position) + + if not is_at_position: + Report.info(f"Prefab Instance Container Entity '{self.container_entity.id.ToString()}'\'s expected position: {expected_position.ToString()}, actual position: {actual_position.ToString()}") + + return is_at_position + async def ui_reparent_prefab_instance(self, parent_entity_id: EntityId): + """ + Reparent this instance to target parent entity. + The function will also check pop up dialog ui in editor to see if there's prefab cyclical dependency error while reparenting prefabs. + :param parent_entity_id: The id of the entity this instance should be a child of in the transform hierarchy next. + """ container_entity_id_before_reparent = self.container_entity.id original_parent = EditorEntity(self.container_entity.get_parent_id()) - original_parent_before_reparent_children_ids = set(original_parent.get_children_ids()) + original_parent_before_reparent_children_ids = {child_id.ToString(): child_id for child_id in original_parent.get_children_ids()} new_parent = EditorEntity(parent_entity_id) - new_parent_before_reparent_children_ids = set(new_parent.get_children_ids()) + new_parent_before_reparent_children_ids = {child_id.ToString(): child_id for child_id in new_parent.get_children_ids()} pyside_utils.run_soon(lambda: self.container_entity.set_parent_entity(parent_entity_id)) pyside_utils.run_soon(lambda: wait_for_propagation()) @@ -90,23 +115,28 @@ class PrefabInstance: except pyside_utils.EventLoopTimeoutException: pass - original_parent_after_reparent_children_ids = set(original_parent.get_children_ids()) + original_parent_after_reparent_children_ids = {child_id.ToString(): child_id for child_id in original_parent.get_children_ids()} assert len(original_parent_after_reparent_children_ids) == len(original_parent_before_reparent_children_ids) - 1, \ "The children count of the Prefab Instance's original parent should be decreased by 1." assert not container_entity_id_before_reparent in original_parent_after_reparent_children_ids, \ "This Prefab Instance is still a child entity of its original parent entity." - new_parent_after_reparent_children_ids = set(new_parent.get_children_ids()) + new_parent_after_reparent_children_ids = {child_id.ToString(): child_id for child_id in new_parent.get_children_ids()} assert len(new_parent_after_reparent_children_ids) == len(new_parent_before_reparent_children_ids) + 1, \ "The children count of the Prefab Instance's new parent should be increased by 1." - container_entity_id_after_reparent = set(new_parent_after_reparent_children_ids).difference(new_parent_before_reparent_children_ids).pop() + after_before_diff = set(new_parent_after_reparent_children_ids.keys()).difference(set(new_parent_before_reparent_children_ids.keys())) + container_entity_id_after_reparent = new_parent_after_reparent_children_ids[after_before_diff.pop()] reparented_container_entity = EditorEntity(container_entity_id_after_reparent) reparented_container_entity_parent_id = reparented_container_entity.get_parent_id() has_correct_parent = reparented_container_entity_parent_id.ToString() == parent_entity_id.ToString() assert has_correct_parent, "Prefab Instance reparented is *not* under the expected parent entity" + current_instance_prefab = Prefab.get_prefab(self.prefab_file_name) + current_instance_prefab.instances.remove(self) + self.container_entity = reparented_container_entity + current_instance_prefab.instances.add(self) # This is a helper class which contains some of the useful information about a prefab template. class Prefab: @@ -117,31 +147,32 @@ class Prefab: self.file_path: str = get_prefab_file_path(file_path) self.instances: set[PrefabInstance] = set() - """ - Check if a prefab is ready to be used to generate its instances. - :param file_path: A unique file path of the target prefab. - :return: Whether the target prefab is loaded or not. - """ @classmethod def is_prefab_loaded(cls, file_path: str) -> bool: + """ + Check if a prefab is ready to be used to generate its instances. + :param file_path: A unique file path of the target prefab. + :return: Whether the target prefab is loaded or not. + """ return file_path in Prefab.existing_prefabs - """ - Check if a prefab exists in the directory for files of prefab tests. - :param file_name: A unique file name of the target prefab. - :return: Whether the target prefab exists or not. - """ + @classmethod def prefab_exists(cls, file_path: str) -> bool: + """ + Check if a prefab exists in the directory for files of prefab tests. + :param file_name: A unique file name of the target prefab. + :return: Whether the target prefab exists or not. + """ return path.exists(get_prefab_file_path(file_path)) - """ - Return a prefab which can be used immediately. - :param file_name: A unique file name of the target prefab. - :return: The prefab with given file name. - """ @classmethod def get_prefab(cls, file_name: str) -> Prefab: + """ + Return a prefab which can be used immediately. + :param file_name: A unique file name of the target prefab. + :return: The prefab with given file name. + """ assert file_name, "Received an empty file_name" if Prefab.is_prefab_loaded(file_name): return Prefab.existing_prefabs[file_name] @@ -151,15 +182,15 @@ class Prefab: Prefab.existing_prefabs[file_name] = Prefab(file_name) return new_prefab - """ - Create a prefab in memory and return it. The very first instance of this prefab will also be created. - :param entities: The entities that should form the new prefab (along with their descendants). - :param file_name: A unique file name of new prefab. - :param prefab_instance_name: A name for the very first instance generated while prefab creation. The default instance name is the same as file_name. - :return: Created Prefab object and the very first PrefabInstance object owned by the prefab. - """ @classmethod def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> tuple(Prefab, PrefabInstance): + """ + Create a prefab in memory and return it. The very first instance of this prefab will also be created. + :param entities: The entities that should form the new prefab (along with their descendants). + :param file_name: A unique file name of new prefab. + :param prefab_instance_name: A name for the very first instance generated while prefab creation. The default instance name is the same as file_name. + :return: Created Prefab object and the very first PrefabInstance object owned by the prefab. + """ assert not Prefab.is_prefab_loaded(file_name), f"Can't create Prefab '{file_name}' since the prefab already exists" new_prefab = Prefab(file_name) @@ -169,6 +200,9 @@ class Prefab: container_entity_id = create_prefab_result.GetValue() container_entity = EditorEntity(container_entity_id) + children_entity_ids = container_entity.get_children_ids() + + assert len(children_entity_ids) == len(entities), f"Entity count of created prefab instance does *not* match the count of given entities." if prefab_instance_name: container_entity.set_name(prefab_instance_name) @@ -180,12 +214,12 @@ class Prefab: Prefab.existing_prefabs[file_name] = new_prefab return new_prefab, new_prefab_instance - """ - Remove target prefab instances. - :param prefab_instances: Instances to be removed. - """ @classmethod def remove_prefabs(cls, prefab_instances: list[PrefabInstance]): + """ + Remove target prefab instances. + :param prefab_instances: Instances to be removed. + """ entity_ids_to_remove = [] entity_id_queue = [prefab_instance.container_entity for prefab_instance in prefab_instances] while entity_id_queue: @@ -212,15 +246,89 @@ class Prefab: instance_deleted_prefab = Prefab.get_prefab(instance.prefab_file_name) instance_deleted_prefab.instances.remove(instance) instance = PrefabInstance() - - """ - Instantiate an instance of this prefab. - :param parent_entity: The entity the prefab should be a child of in the transform hierarchy. - :param name: A name for newly instantiated prefab instance. The default instance name is the same as this prefab's file name. - :param prefab_position: The position in world space the prefab should be instantiated in. - :return: Instantiated PrefabInstance object owned by this prefab. - """ + + @classmethod + def duplicate_prefabs(cls, prefab_instances: list[PrefabInstance]): + """ + Duplicate target prefab instances. + :param prefab_instances: Instances to be duplicated. + :return: PrefabInstance objects of given prefab instances' duplicates. + """ + assert prefab_instances, "Input list of prefab instances should *not* be empty." + + common_parent = EditorEntity(prefab_instances[0].container_entity.get_parent_id()) + common_parent_children_ids_before_duplicate = set([child_id.ToString() for child_id in common_parent.get_children_ids()]) + + container_entity_ids = [prefab_instance.container_entity.id for prefab_instance in prefab_instances] + + duplicate_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'DuplicateEntitiesInInstance', container_entity_ids) + assert duplicate_prefab_result.IsSuccess(), f"Prefab operation 'DuplicateEntitiesInInstance' failed. Error: {duplicate_prefab_result.GetError()}" + + wait_for_propagation() + + duplicate_container_entity_ids = duplicate_prefab_result.GetValue() + common_parent_children_ids_after_duplicate = set([child_id.ToString() for child_id in common_parent.get_children_ids()]) + + assert set([container_entity_id.ToString() for container_entity_id in container_entity_ids]).issubset(common_parent_children_ids_after_duplicate), \ + "Provided prefab instances are *not* the children of their common parent anymore after duplication." + assert common_parent_children_ids_before_duplicate.issubset(common_parent_children_ids_after_duplicate), \ + "Some children of provided entities' common parent before duplication are *not* the children of the common parent anymore after duplication." + assert len(common_parent_children_ids_after_duplicate) == len(common_parent_children_ids_before_duplicate) + len(prefab_instances), \ + "The children count of the given prefab instances' common parent entity is *not* increased to the expected number." + assert EditorEntity(duplicate_container_entity_ids[0]).get_parent_id().ToString() == common_parent.id.ToString(), \ + "Provided prefab instances' parent should be the same as duplicates' parent." + + duplicate_instances = [] + for duplicate_container_entity_id in duplicate_container_entity_ids: + prefab_file_path = prefab.PrefabPublicRequestBus(bus.Broadcast, 'GetOwningInstancePrefabPath', duplicate_container_entity_id) + assert prefab_file_path, "Returned file path should *not* be empty." + + prefab_file_name = Path(prefab_file_path).stem + duplicate_instance_prefab = Prefab.get_prefab(prefab_file_name) + duplicate_instance = PrefabInstance(prefab_file_path, EditorEntity(duplicate_container_entity_id)) + duplicate_instance_prefab.instances.add(duplicate_instance) + duplicate_instances.append(duplicate_instance) + + return duplicate_instances + + @classmethod + def detach_prefab(cls, prefab_instance: PrefabInstance): + """ + Detach target prefab instance. + :param prefab_instances: Instance to be detached. + """ + parent = EditorEntity(prefab_instance.container_entity.get_parent_id()) + parent_children_ids_before_detach = set([child_id.ToString() for child_id in parent.get_children_ids()]) + + assert prefab_instance.has_editor_prefab_component(), f"Container entity should have EditorPrefabComponent before detachment." + + detach_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'DetachPrefab', prefab_instance.container_entity.id) + assert detach_prefab_result.IsSuccess(), f"Prefab operation 'DetachPrefab' failed. Error: {detach_prefab_result.GetError()}" + + assert not prefab_instance.has_editor_prefab_component(), f"Container entity should *not* have EditorPrefabComponent after detachment." + + parent_children_ids_after_detach = set([child_id.ToString() for child_id in parent.get_children_ids()]) + + assert prefab_instance.container_entity.id.ToString() in parent_children_ids_after_detach, \ + "Target prefab instance's container entity id should still exists after the detachment and before the propagation." + + assert len(parent_children_ids_after_detach) == len(parent_children_ids_before_detach), \ + "Parent entity should still keep the same amount of children entities." + + wait_for_propagation() + + instance_owner_prefab = Prefab.get_prefab(prefab_instance.prefab_file_name) + instance_owner_prefab.instances.remove(prefab_instance) + prefab_instance = PrefabInstance() + def instantiate(self, parent_entity: EditorEntity=None, name: str=None, prefab_position: Vector3=Vector3()) -> PrefabInstance: + """ + Instantiate an instance of this prefab. + :param parent_entity: The entity the prefab should be a child of in the transform hierarchy. + :param name: A name for newly instantiated prefab instance. The default instance name is the same as this prefab's file name. + :param prefab_position: The position in world space the prefab should be instantiated in. + :return: Instantiated PrefabInstance object owned by this prefab. + """ parent_entity_id = parent_entity.id if parent_entity is not None else EntityId() instantiate_prefab_result = prefab.PrefabPublicRequestBus( @@ -240,4 +348,6 @@ class Prefab: assert not new_prefab_instance in self.instances, "This prefab instance is already existed before this instantiation." self.instances.add(new_prefab_instance) + assert new_prefab_instance.is_at_position(prefab_position), "This prefab instance is *not* at expected position." + return new_prefab_instance diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py index 30a0055d5a..5337f0669c 100644 --- a/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py @@ -47,3 +47,11 @@ class TestAutomation(TestAutomationBase): def test_PrefabBasicWorkflow_CreateAndReparentPrefab(self, request, workspace, editor, launcher_platform): from .tests import PrefabBasicWorkflow_CreateAndReparentPrefab as test_module self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False) + + def test_PrefabBasicWorkflow_CreateReparentAndDetachPrefab(self, request, workspace, editor, launcher_platform): + from .tests import PrefabBasicWorkflow_CreateReparentAndDetachPrefab as test_module + self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False) + + def test_PrefabBasicWorkflow_CreateAndDuplicatePrefab(self, request, workspace, editor, launcher_platform): + from .tests import PrefabBasicWorkflow_CreateAndDuplicatePrefab as test_module + self._run_prefab_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py index 9ae4614d80..bbebd70e04 100644 --- a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDeletePrefab.py @@ -16,16 +16,15 @@ def PrefabBasicWorkflow_CreateAndDeletePrefab(): prefab_test_utils.open_base_tests_level() - # Creates a new Entity at the root level - # Asserts if creation didn't succeed + # Creates a new entity at the root level car_entity = EditorEntity.create_editor_entity() car_prefab_entities = [car_entity] - # Asserts if prefab creation doesn't succeeds + # Creates a prefab from the new entity _, car = Prefab.create_prefab( car_prefab_entities, CAR_PREFAB_FILE_NAME) - # Asserts if prefab deletion fails + # Deletes the prefab instance Prefab.remove_prefabs([car]) if __name__ == "__main__": diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDuplicatePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDuplicatePrefab.py new file mode 100644 index 0000000000..2479ae549e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndDuplicatePrefab.py @@ -0,0 +1,32 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +def PrefabBasicWorkflow_CreateAndDuplicatePrefab(): + + CAR_PREFAB_FILE_NAME = 'car_prefab' + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.prefab_utils import Prefab + + import PrefabTestUtils as prefab_test_utils + + prefab_test_utils.open_base_tests_level() + + # Creates a new entity at the root level + car_entity = EditorEntity.create_editor_entity() + car_prefab_entities = [car_entity] + + # Creates a prefab from the new entity + _, car = Prefab.create_prefab( + car_prefab_entities, CAR_PREFAB_FILE_NAME) + + # Duplicates the prefab instance + Prefab.duplicate_prefabs([car]) + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(PrefabBasicWorkflow_CreateAndDuplicatePrefab) diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py index f78bbf483d..1cbc591c29 100644 --- a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateAndReparentPrefab.py @@ -22,24 +22,23 @@ def PrefabBasicWorkflow_CreateAndReparentPrefab(): prefab_test_utils.open_base_tests_level() - # Creates a new Entity at the root level - # Asserts if creation didn't succeed + # Creates a new car entity at the root level car_entity = EditorEntity.create_editor_entity() car_prefab_entities = [car_entity] - # Checks for prefab creation passed or not + # Creates a prefab from the car entity _, car = Prefab.create_prefab( car_prefab_entities, CAR_PREFAB_FILE_NAME) - # Creates another new Entity at the root level + # Creates another new wheel entity at the root level wheel_entity = EditorEntity.create_editor_entity() wheel_prefab_entities = [wheel_entity] - # Checks for wheel prefab creation passed or not + # Creates another prefab from the wheel entity _, wheel = Prefab.create_prefab( wheel_prefab_entities, WHEEL_PREFAB_FILE_NAME) - # Checks for prefab reparenting passed or not + # Reparents the wheel prefab instance to the container entity of the car prefab instance await wheel.ui_reparent_prefab_instance(car.container_entity.id) run_test() diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreatePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreatePrefab.py index 568a2c15b4..cae105a9a9 100644 --- a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreatePrefab.py +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreatePrefab.py @@ -17,12 +17,11 @@ def PrefabBasicWorkflow_CreatePrefab(): prefab_test_utils.open_base_tests_level() - # Creates a new Entity at the root level - # Asserts if creation didn't succeed + # Creates a new entity at the root level car_entity = EditorEntity.create_editor_entity() car_prefab_entities = [car_entity] - # Checks for prefab creation passed or not + # Creates a prefab from the new entity Prefab.create_prefab(car_prefab_entities, CAR_PREFAB_FILE_NAME) if __name__ == "__main__": diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateReparentAndDetachPrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateReparentAndDetachPrefab.py new file mode 100644 index 0000000000..bdf77c4bf3 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_CreateReparentAndDetachPrefab.py @@ -0,0 +1,51 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +def PrefabBasicWorkflow_CreateReparentAndDetachPrefab(): + + CAR_PREFAB_FILE_NAME = 'car_prefab' + WHEEL_PREFAB_FILE_NAME = 'wheel_prefab' + + import editor_python_test_tools.pyside_utils as pyside_utils + + @pyside_utils.wrap_async + async def run_test(): + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.prefab_utils import Prefab + + import PrefabTestUtils as prefab_test_utils + + prefab_test_utils.open_base_tests_level() + + # Creates a new car entity at the root level + car_entity = EditorEntity.create_editor_entity() + car_prefab_entities = [car_entity] + + # Creates a prefab from the car entity + _, car = Prefab.create_prefab( + car_prefab_entities, CAR_PREFAB_FILE_NAME) + + # Creates another new wheel entity at the root level + wheel_entity = EditorEntity.create_editor_entity() + wheel_prefab_entities = [wheel_entity] + + # Creates another prefab from the wheel entity + _, wheel = Prefab.create_prefab( + wheel_prefab_entities, WHEEL_PREFAB_FILE_NAME) + + # Reparents the wheel prefab instance to the container entity of the car prefab instance + await wheel.ui_reparent_prefab_instance(car.container_entity.id) + + # Detaches the wheel prefab instance + Prefab.detach_prefab(wheel) + + run_test() + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(PrefabBasicWorkflow_CreateReparentAndDetachPrefab) diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py index 1b962d2ca7..a701802cd4 100644 --- a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabBasicWorkflow_InstantiatePrefab.py @@ -19,9 +19,8 @@ def PrefabBasicWorkflow_InstantiatePrefab(): prefab_test_utils.open_base_tests_level() - # Checks for prefab instantiation passed or not + # Instantiates a new car prefab instance test_prefab = Prefab.get_prefab(EXISTING_TEST_PREFAB_FILE_NAME) - test_instance = test_prefab.instantiate( prefab_position=INSTANTIATED_TEST_PREFAB_POSITION) diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabTestUtils.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabTestUtils.py index f82af23023..f865daf41a 100644 --- a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabTestUtils.py +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabTestUtils.py @@ -18,20 +18,6 @@ import azlmbr.components as components import azlmbr.entity as entity import azlmbr.legacy.general as general -def check_entity_at_position(entity_id, expected_entity_position): - entity_at_expected_position_result = ( - "entity is at expected position", - "entity is *not* at expected position") - - actual_entity_position = components.TransformBus(bus.Event, "GetWorldTranslation", entity_id) - is_at_position = actual_entity_position.IsClose(expected_entity_position) - Report.result(entity_at_expected_position_result, is_at_position) - - if not is_at_position: - Report.info(f"Entity '{entity_id.ToString()}'\'s expected position: {expected_entity_position.ToString()}, actual position: {actual_entity_position.ToString()}") - - return is_at_position - def check_entity_children_count(entity_id, expected_children_count): entity_children_count_matched_result = ( "Entity with a unique name found", @@ -47,19 +33,6 @@ def check_entity_children_count(entity_id, expected_children_count): return entity_children_count_matched -def get_children_ids_by_name(entity_id, entity_name): - entity = EditorEntity(entity_id) - children_entity_ids = entity.get_children_ids() - - result = [] - for child_entity_id in children_entity_ids: - child_entity = EditorEntity(child_entity_id) - child_entity_name = child_entity.get_name() - if child_entity_name == entity_name: - result.append(child_entity_id) - - return result - def open_base_tests_level(): helper.init_idle() helper.open_level("Prefab", "Base") diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.cpp index d95a704e84..e9a5bee87a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -38,6 +40,13 @@ namespace AzToolsFramework AZ::Edit::SliceFlags::DontGatherReference); } } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty( + "EditorPrefabComponentTypeId", BehaviorConstant(AZ::Uuid(EditorPrefabComponent::EditorPrefabComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); + } } void EditorPrefabComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.h index 8873787bd3..aa15b63ac2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/EditorPrefabComponent.h @@ -16,7 +16,9 @@ namespace AzToolsFramework class EditorPrefabComponent : public AzToolsFramework::Components::EditorComponentBase { public: - AZ_COMPONENT(EditorPrefabComponent, "{756E5F9C-3E08-4F8D-855C-A5AEEFB6FCDD}", EditorComponentBase); + static constexpr const char* const EditorPrefabComponentTypeId = "{756E5F9C-3E08-4F8D-855C-A5AEEFB6FCDD}"; + + AZ_COMPONENT(EditorPrefabComponent, EditorPrefabComponentTypeId, EditorComponentBase); static void Reflect(AZ::ReflectContext* context); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index ee34b628bb..d976c91c3e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -974,7 +974,7 @@ namespace AzToolsFramework return DeleteFromInstance(entityIds, true); } - PrefabOperationResult PrefabPublicHandler::DuplicateEntitiesInInstance(const EntityIdList& entityIds) + DuplicatePrefabResult PrefabPublicHandler::DuplicateEntitiesInInstance(const EntityIdList& entityIds) { if (entityIds.empty()) { @@ -1021,6 +1021,7 @@ namespace AzToolsFramework ScopedUndoBatch undoBatch("Duplicate Entities"); + EntityIdList duplicatedEntityAndInstanceIds; { AZ_PROFILE_SCOPE(AzToolsFramework, "DuplicateEntitiesInInstance::UndoCaptureAndDuplicateEntities"); @@ -1033,7 +1034,7 @@ namespace AzToolsFramework if (!retrieveEntitiesAndInstancesOutcome.IsSuccess()) { - return AZStd::move(retrieveEntitiesAndInstancesOutcome); + return AZ::Failure(retrieveEntitiesAndInstancesOutcome.TakeError()); } // Take a snapshot of the instance DOM before we manipulate it @@ -1044,8 +1045,6 @@ namespace AzToolsFramework PrefabDom instanceDomAfter; instanceDomAfter.CopyFrom(instanceDomBefore, instanceDomAfter.GetAllocator()); - EntityIdList duplicatedEntityAndInstanceIds; - // Duplicate any nested entities and instances as requested AZStd::unordered_map newInstanceAliasToOldInstanceMap; AZStd::unordered_map duplicateEntityAliasMap; @@ -1114,7 +1113,7 @@ namespace AzToolsFramework ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds); } - return AZ::Success(); + return AZ::Success(AZStd::move(duplicatedEntityAndInstanceIds)); } PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index a9dadc3336..4961be9d77 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -63,7 +63,7 @@ namespace AzToolsFramework PrefabOperationResult DeleteEntitiesInInstance(const EntityIdList& entityIds) override; PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) override; - PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) override; + DuplicatePrefabResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) override; PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h index 528d4f6d1b..ede857dd2b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h @@ -26,6 +26,7 @@ namespace AzToolsFramework { typedef AZ::Outcome CreatePrefabResult; typedef AZ::Outcome InstantiatePrefabResult; + typedef AZ::Outcome DuplicatePrefabResult; typedef AZ::Outcome PrefabOperationResult; typedef AZ::Outcome PrefabRequestResult; typedef AZ::Outcome PrefabEntityResult; @@ -160,14 +161,15 @@ namespace AzToolsFramework /** * Duplicates all entities in the owning instance. Bails if the entities don't all belong to the same instance. * @param entities The entities to duplicate. - * @return An outcome object; on failure, it comes with an error message detailing the cause of the error. + * @return An outcome object with a list of ids of target entities' duplicates if duplication succeeded; + * on failure, it comes with an error message detailing the cause of the error. */ - virtual PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) = 0; + virtual DuplicatePrefabResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) = 0; /** * If the entity id is a container entity id, detaches the prefab instance corresponding to it. This includes converting * the container entity into a regular entity and putting it under the parent prefab, removing the link between this - * instance and the parent, removing links between this instance and it's nested instances, adding entities directly + * instance and the parent, removing links between this instance and its nested instances, and adding entities directly * owned by this instance under the parent instance. * Bails if the entity is not a container entity or belongs to the level prefab instance. * @param containerEntityId The container entity id of the instance to detach. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h index 7b23fffb7f..fd4b8a5f17 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h @@ -25,6 +25,7 @@ namespace AzToolsFramework { using CreatePrefabResult = AZ::Outcome; using InstantiatePrefabResult = AZ::Outcome; + using DuplicatePrefabResult = AZ::Outcome; using PrefabOperationResult = AZ::Outcome; /** @@ -69,6 +70,29 @@ namespace AzToolsFramework * Return an outcome object; on failure, it comes with an error message detailing the cause of the error. */ virtual PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) = 0; + + /** + * If the entity id is a container entity id, detaches the prefab instance corresponding to it. This includes converting + * the container entity into a regular entity and putting it under the parent prefab, removing the link between this + * instance and the parent, removing links between this instance and its nested instances, and adding entities directly + * owned by this instance under the parent instance. + * Bails if the entity is not a container entity or belongs to the level prefab instance. + * Return an outcome object; on failure, it comes with an error message detailing the cause of the error. + */ + virtual PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) = 0; + + /** + * Duplicates all entities in the owning instance. Bails if the entities don't all belong to the same instance. + * Return an outcome object with a list of ids of given entities' duplicates if duplication succeeded; + * on failure, it comes with an error message detailing the cause of the error. + */ + virtual DuplicatePrefabResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) = 0; + + /** + * Get the file path to the prefab file for the prefab instance owning the entity provided. + * Returns the path to the prefab, or an empty path if the entity is owned by the level. + */ + virtual AZStd::string GetOwningInstancePrefabPath(AZ::EntityId entityId) const = 0; }; using PrefabPublicRequestBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp index 0aaf81c4c9..3b69dcdfe4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp @@ -28,6 +28,9 @@ namespace AzToolsFramework ->Event("CreatePrefabInMemory", &PrefabPublicRequests::CreatePrefabInMemory) ->Event("InstantiatePrefab", &PrefabPublicRequests::InstantiatePrefab) ->Event("DeleteEntitiesAndAllDescendantsInInstance", &PrefabPublicRequests::DeleteEntitiesAndAllDescendantsInInstance) + ->Event("DetachPrefab", &PrefabPublicRequests::DetachPrefab) + ->Event("DuplicateEntitiesInInstance", &PrefabPublicRequests::DuplicateEntitiesInInstance) + ->Event("GetOwningInstancePrefabPath", &PrefabPublicRequests::GetOwningInstancePrefabPath) ; } } @@ -62,5 +65,19 @@ namespace AzToolsFramework return m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance(entityIds); } + PrefabOperationResult PrefabPublicRequestHandler::DetachPrefab(const AZ::EntityId& containerEntityId) + { + return m_prefabPublicInterface->DetachPrefab(containerEntityId); + } + + DuplicatePrefabResult PrefabPublicRequestHandler::DuplicateEntitiesInInstance(const EntityIdList& entityIds) + { + return m_prefabPublicInterface->DuplicateEntitiesInInstance(entityIds); + } + + AZStd::string PrefabPublicRequestHandler::GetOwningInstancePrefabPath(AZ::EntityId entityId) const + { + return m_prefabPublicInterface->GetOwningInstancePrefabPath(entityId).Native(); + } } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h index ae0ed2a5d1..b24ea7ec2a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h @@ -34,6 +34,9 @@ namespace AzToolsFramework CreatePrefabResult CreatePrefabInMemory(const EntityIdList& entityIds, AZStd::string_view filePath) override; InstantiatePrefabResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override; PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) override; + PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) override; + DuplicatePrefabResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) override; + AZStd::string GetOwningInstancePrefabPath(AZ::EntityId entityId) const override; private: PrefabPublicInterface* m_prefabPublicInterface = nullptr; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp index 6c96209d56..1c2230fa83 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp @@ -23,7 +23,7 @@ namespace AzToolsFramework } //PrefabInstanceUndo - PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation) + PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName, bool useImmediatePropagation) : PrefabUndoBase(undoOperationName) { m_useImmediatePropagation = useImmediatePropagation; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h index 0946a36951..bc0b86a8c6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h @@ -45,7 +45,7 @@ namespace AzToolsFramework : public PrefabUndoBase { public: - explicit PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation = true); + explicit PrefabUndoInstance(const AZStd::string& undoOperationName, bool useImmediatePropagation = true); void Capture( const PrefabDom& initialState, From 37873f81cf8a540a42236ce1f2f52b42ea67c930 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:09:46 -0400 Subject: [PATCH 102/152] Setting owning connection id based on the hierarchy root network entity. Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../NetworkHierarchyChildComponent.h | 10 +- .../NetworkHierarchyRootComponent.h | 12 +- .../NetworkHierarchyChildComponent.cpp | 60 ++++++--- .../NetworkHierarchyRootComponent.cpp | 69 ++++++---- .../Code/Tests/ClientHierarchyTests.cpp | 32 ++++- .../Code/Tests/ServerHierarchyTests.cpp | 122 +++++++++++++++++- 6 files changed, 246 insertions(+), 59 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h index a31cd90efd..af1878dd49 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -58,11 +58,10 @@ namespace Multiplayer void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} - protected: - //! Used by @NetworkHierarchyRootComponent - void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); - private: + //! Used by @NetworkHierarchyRootComponent + void SetTopLevelHierarchyRootEntity(AZ::Entity* previousHierarchyRoot, AZ::Entity* newHierarchyRoot); + AZ::ChildChangedEvent::Handler m_childChangedHandler; void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child); @@ -80,5 +79,8 @@ namespace Multiplayer bool m_isHierarchyEnabled = true; void NotifyChildrenHierarchyDisbanded(); + + AzNetworking::ConnectionId m_previousOwningConnectionId = AzNetworking::InvalidConnectionId; + void SetOwningConnectionId(AzNetworking::ConnectionId connectionId) override; }; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index cffcd3e97d..874b8cd296 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -61,10 +61,9 @@ namespace Multiplayer bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer); - protected: - void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); - private: + void SetTopLevelHierarchyRootEntity(AZ::Entity* previousHierarchyRoot, AZ::Entity* newHierarchyRoot); + AZ::ChildChangedEvent::Handler m_childChangedHandler; AZ::ParentChangedEvent::Handler m_parentChangedHandler; @@ -81,16 +80,19 @@ namespace Multiplayer //! Rebuilds hierarchy starting from this root component's entity. void RebuildHierarchy(); - + //! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy. //! Builds the hierarchy using breadth-first iterative method. void InternalBuildHierarchyList(AZ::Entity* underEntity); - void SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity); + void SetRootForEntity(AZ::Entity* previousKnownRoot, AZ::Entity* newRoot, const AZ::Entity* childEntity); //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. bool m_isHierarchyEnabled = true; + AzNetworking::ConnectionId m_previousOwningConnectionId = AzNetworking::InvalidConnectionId; + void SetOwningConnectionId(AzNetworking::ConnectionId connectionId) override; + friend class HierarchyBenchmarkBase; }; diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index 1f32387893..37125a88b9 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -129,35 +129,53 @@ namespace Multiplayer handler.Connect(m_networkHierarchyLeaveEvent); } - void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) + void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* previousHierarchyRoot, AZ::Entity* newHierarchyRoot) { - if (m_rootEntity != hierarchyRoot) + if (newHierarchyRoot) { - m_rootEntity = hierarchyRoot; - - if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + if (m_rootEntity != newHierarchyRoot) { - NetworkHierarchyChildComponentController* controller = static_cast(GetController()); - if (m_rootEntity) + m_rootEntity = newHierarchyRoot; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); controller->SetHierarchyRoot(netRootId); - - m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); } - else - { - controller->SetHierarchyRoot(InvalidNetEntityId); - m_networkHierarchyLeaveEvent.Signal(); - } + GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent()->GetOwningConnectionId()); + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); } - - if (m_rootEntity == nullptr) + } + else + { + if (m_rootEntity == previousHierarchyRoot || !previousHierarchyRoot) { + m_rootEntity = nullptr; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + controller->SetHierarchyRoot(InvalidNetEntityId); + } + + GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); + m_networkHierarchyLeaveEvent.Signal(); + NotifyChildrenHierarchyDisbanded(); } } + + } + + void NetworkHierarchyChildComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId) + { + NetworkHierarchyChildComponentBase::SetOwningConnectionId(connectionId); + if (IsHierarchicalChild() == false) + { + m_previousOwningConnectionId = connectionId; + } } void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child) @@ -180,14 +198,18 @@ namespace Multiplayer if (m_rootEntity != newRoot) { m_rootEntity = newRoot; + + m_previousOwningConnectionId = GetNetBindComponent()->GetOwningConnectionId(); + GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent()->GetOwningConnectionId()); + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); } } else { + GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); m_isHierarchyEnabled = false; m_rootEntity = nullptr; - m_networkHierarchyLeaveEvent.Signal(); } } @@ -203,11 +225,11 @@ namespace Multiplayer { if (auto* hierarchyChildComponent = childEntity->FindComponent()) { - hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr); + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr, nullptr); } else if (auto* hierarchyRootComponent = childEntity->FindComponent()) { - hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr); + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr, nullptr); } } } diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 8d485a973e..854aec521b 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -107,7 +107,7 @@ namespace Multiplayer { if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(childEntityId)) { - SetRootForEntity(nullptr, childEntity); + SetRootForEntity(GetEntity(), nullptr, childEntity); } } } @@ -209,7 +209,7 @@ namespace Multiplayer auto [rootComponent, childComponent] = GetHierarchyComponents(parentEntity); if (rootComponent == nullptr && childComponent == nullptr) { - RebuildHierarchy(); + SetRootForEntity(nullptr, nullptr, GetEntity()); } else { @@ -219,7 +219,7 @@ namespace Multiplayer else { // Detached from parent - RebuildHierarchy(); + SetRootForEntity(nullptr, nullptr, GetEntity()); } } @@ -247,14 +247,14 @@ namespace Multiplayer { // This is a newly added entity to the network hierarchy. hierarchyChanged = true; - SetRootForEntity(GetEntity(), currentEntity); + SetRootForEntity(nullptr, GetEntity(), currentEntity); } } // These entities were removed since last rebuild. for (const AZ::Entity* previousEntity : previousEntities) { - SetRootForEntity(nullptr, previousEntity); + SetRootForEntity(GetEntity(), nullptr, previousEntity); } if (!previousEntities.empty()) @@ -307,42 +307,61 @@ namespace Multiplayer } } - void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity) + void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* previousKnownRoot, AZ::Entity* newRoot, const AZ::Entity* childEntity) { auto [hierarchyRootComponent, hierarchyChildComponent] = GetHierarchyComponents(childEntity); if (hierarchyChildComponent) { - hierarchyChildComponent->SetTopLevelHierarchyRootEntity(root); + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(previousKnownRoot, newRoot); } else if (hierarchyRootComponent) { - hierarchyRootComponent->SetTopLevelHierarchyRootEntity(root); + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(previousKnownRoot, newRoot); } } - void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) + void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* previousHierarchyRoot, AZ::Entity* newHierarchyRoot) { - m_rootEntity = hierarchyRoot; - - if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + if (newHierarchyRoot) { - NetworkHierarchyChildComponentController* controller = static_cast(GetController()); - if (hierarchyRoot) + m_rootEntity = newHierarchyRoot; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) { - const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetId()); + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); controller->SetHierarchyRoot(netRootId); - } - else - { - controller->SetHierarchyRoot(InvalidNetEntityId); + GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent()->GetOwningConnectionId()); } } - - if (m_rootEntity == nullptr) + else { - // We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy. - RebuildHierarchy(); + if (previousHierarchyRoot && m_rootEntity == previousHierarchyRoot || !previousHierarchyRoot) + { + m_rootEntity = nullptr; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + + controller->SetHierarchyRoot(InvalidNetEntityId); + GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); + } + + // We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy. + RebuildHierarchy(); + } + } + } + + void NetworkHierarchyRootComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId) + { + NetworkHierarchyRootComponentBase::SetOwningConnectionId(connectionId); + if (IsHierarchicalChild() == false) + { + m_previousOwningConnectionId = connectionId; } } @@ -370,7 +389,7 @@ namespace Multiplayer void NetworkHierarchyRootComponentController::CreateInput(Multiplayer::NetworkInput& input, float deltaTime) { NetworkHierarchyRootComponent& component = GetParent(); - if(!component.IsHierarchicalRoot()) + if (!component.IsHierarchicalRoot()) { return; } @@ -386,7 +405,7 @@ namespace Multiplayer for (AZ::Entity* child : entities) { - if(child == component.GetEntity()) + if (child == component.GetEntity()) { continue; // Avoid infinite recursion } diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index badee73e04..d6ce8a3ff7 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -303,6 +303,36 @@ namespace Multiplayer SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); } + TEST_F(ClientSimpleHierarchyTests, ChildHasOwningConnectionIdOfParent) + { + // disconnect and assign new connection ids + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); + + m_root->m_entity->FindComponent()->SetOwningConnectionId(ConnectionId{ 1 }); + m_child->m_entity->FindComponent()->SetOwningConnectionId(ConnectionId{ 2 }); + + const ConnectionId previousConnectionId = m_child->m_entity->FindComponent()->GetOwningConnectionId(); + + // re-attach, child's owning connection id should then be root's connection id + SetParentIdOnNetworkTransform(m_child->m_entity, RootNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, RootNetEntityId); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetOwningConnectionId(), + m_root->m_entity->FindComponent()->GetOwningConnectionId() + ); + + // detach, the child should roll back to his previous owning connection id + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetOwningConnectionId(), + previousConnectionId + ); + } + /* * Parent -> Child -> ChildOfChild */ @@ -397,7 +427,7 @@ namespace Multiplayer using MultiplayerTest::TestMultiplayerComponentNetworkInput; auto* rootNetBind = m_root->m_entity->FindComponent(); - + NetworkInputArray inputArray(rootNetBind->GetEntityHandle()); NetworkInput& input = inputArray[0]; diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp index 1b5aa31780..2888071508 100644 --- a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -195,6 +195,49 @@ namespace Multiplayer m_child->m_entity.reset(); } + TEST_F(ServerSimpleHierarchyTests, ChildPointsToRootAfterReattachment) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + m_root->m_entity->FindComponent()->GetNetEntityId() + ); + } + + TEST_F(ServerSimpleHierarchyTests, ChildHasOwningConnectionIdOfParent) + { + // disconnect and assign new connection ids + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetOwningConnectionId(ConnectionId{ 1 }); + m_child->m_entity->FindComponent()->SetOwningConnectionId(ConnectionId{ 2 }); + + const ConnectionId previousConnectionId = m_child->m_entity->FindComponent()->GetOwningConnectionId(); + + // re-attach, child's owning connection id should then be root's connection id + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetOwningConnectionId(), + m_root->m_entity->FindComponent()->GetOwningConnectionId() + ); + + // detach, the child should roll back to his previous owning connection id + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetOwningConnectionId(), + previousConnectionId + ); + } + /* * Parent -> Child -> ChildOfChild */ @@ -410,7 +453,7 @@ namespace Multiplayer { MockNetworkHierarchyCallbackHandler mock; EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root->m_entity->GetId())).Times(2); - + m_root->m_entity->FindComponent()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler); m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); @@ -822,6 +865,22 @@ namespace Multiplayer } } + TEST_F(ServerHierarchyOfHierarchyTests, InnerChildrenPointToInnerRootAfterDetachmentFromTopRoot) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_child2->m_entity->FindComponent()->GetHierarchyRoot(), + m_root2->m_entity->FindComponent()->GetNetEntityId() + ); + EXPECT_EQ( + m_childOfChild2->m_entity->FindComponent()->GetHierarchyRoot(), + m_root2->m_entity->FindComponent()->GetNetEntityId() + ); + } + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) { m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); @@ -1005,6 +1064,59 @@ namespace Multiplayer m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); } + TEST_F(ServerHierarchyOfHierarchyTests, InnerRootAndItsChildrenHaveOwningConnectionIdOfTopRoot) + { + // Assign new connection ids. + m_root->m_entity->FindComponent()->SetOwningConnectionId(ConnectionId{ 1 }); + m_root2->m_entity->FindComponent()->SetOwningConnectionId(ConnectionId{ 2 }); + + // Attach then inner hierarchy's owning connection id should then be top root's connection id. + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetOwningConnectionId(), + m_root->m_entity->FindComponent()->GetOwningConnectionId() + ); + + EXPECT_EQ( + m_child2->m_entity->FindComponent()->GetOwningConnectionId(), + m_root->m_entity->FindComponent()->GetOwningConnectionId() + ); + + EXPECT_EQ( + m_childOfChild2->m_entity->FindComponent()->GetOwningConnectionId(), + m_root->m_entity->FindComponent()->GetOwningConnectionId() + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, InnerRootAndItsChildrenHaveTheirOriginalOwningConnectionIdAfterDetachingFromTopRoot) + { + // Assign new connection ids. + m_root->m_entity->FindComponent()->SetOwningConnectionId(ConnectionId{ 1 }); + m_root2->m_entity->FindComponent()->SetOwningConnectionId(ConnectionId{ 2 }); + + // Attach then inner hierarchy's owning connection id should then be top root's connection id. + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + // detach, inner hierarchy should roll back to his previous owning connection id + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetOwningConnectionId(), + ConnectionId{ 2 } + ); + + EXPECT_EQ( + m_child2->m_entity->FindComponent()->GetOwningConnectionId(), + m_root2->m_entity->FindComponent()->GetOwningConnectionId() + ); + + EXPECT_EQ( + m_childOfChild2->m_entity->FindComponent()->GetOwningConnectionId(), + m_root2->m_entity->FindComponent()->GetOwningConnectionId() + ); + } + /* * Parent -> Child -> ChildOfChild (not marked as in a hierarchy) */ @@ -1242,15 +1354,15 @@ namespace Multiplayer ); } - TEST_F(ServerHierarchyWithThreeRoots, ReattachMiddleChildWhileLastChildGetsLeaveEventOnce) + TEST_F(ServerHierarchyWithThreeRoots, InnerRootLeftTopRootThenLastChildGetsJoinedEventOnce) { m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); MockNetworkHierarchyCallbackHandler mock; - EXPECT_CALL(mock, OnNetworkHierarchyLeave()); - - m_childOfChild3->m_entity->FindComponent()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler); + EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root3->m_entity->GetId())); + + m_childOfChild3->m_entity->FindComponent()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler); m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); } From f4d5a75574098ed9e67f6badf29ddf9e95de9d3e Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 21 Oct 2021 12:13:45 -0700 Subject: [PATCH 103/152] Fixed an issue where the wrong version number was being reported in a warning message. It showed the latest version instead of the original version number. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Code/Source/RPI.Edit/Material/MaterialSourceData.cpp | 2 +- .../RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp | 6 ++++-- Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp | 2 ++ .../RPI/Code/Tests/Material/MaterialSourceDataTests.cpp | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index 9d44963942..5aed6b2993 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -119,7 +119,7 @@ namespace AZ if (changesWereApplied) { AZ_Warning("MaterialSourceData", false, - "This material is based on version %u of '%s', but the material type is now at version %u. " + "This material is based on version '%u' of '%s', but the material type is now at version '%u'. " "Automatic updates are available. Consider updating the .material source file.", m_materialTypeVersion, m_materialType.c_str(), materialTypeSourceData.m_version); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index 2b7c1c58c7..e9d8a42641 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -208,6 +208,8 @@ namespace AZ return; } + const uint32_t originalVersion = m_materialTypeVersion; + bool changesWereApplied = false; for (const MaterialVersionUpdate& versionUpdate : m_materialTypeAsset->GetMaterialVersionUpdateList()) @@ -225,9 +227,9 @@ namespace AZ if (changesWereApplied) { AZ_Warning("MaterialAsset", false, - "This material is based on version %u of '%s', but the material type is now at version %u. " + "This material is based on version '%u' of %s, but the material type is now at version '%u'. " "Automatic updates are available. Consider updating the .material source file.", - m_materialTypeVersion, m_materialTypeAsset.ToString().c_str(), m_materialTypeAsset->GetVersion()); + originalVersion, m_materialTypeAsset.ToString().c_str(), m_materialTypeAsset->GetVersion()); } m_materialTypeVersion = m_materialTypeAsset->GetVersion(); diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp index cf8b55d581..58a852f176 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialAssetTests.cpp @@ -264,6 +264,8 @@ namespace UnitTest // This can find errors and warnings, we are looking for a warning when the version update is applied ErrorMessageFinder warningFinder; warningFinder.AddExpectedErrorMessage("Automatic updates are available. Consider updating the .material source file"); + warningFinder.AddExpectedErrorMessage("This material is based on version '1'"); + warningFinder.AddExpectedErrorMessage("material type is now at version '2'"); // Even though this material was created using the old version of the material type, it's property values should get automatically // updated to align with the new property layout in the latest MaterialTypeAsset. diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp index 6a6947e7c7..acce52ae8e 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp @@ -728,6 +728,8 @@ namespace UnitTest // Then we force the material data to update to the latest material type version specification ErrorMessageFinder warningFinder; // Note this finds errors and warnings, and we're looking for a warning. warningFinder.AddExpectedErrorMessage("Automatic updates are available. Consider updating the .material source file"); + warningFinder.AddExpectedErrorMessage("This material is based on version '1'"); + warningFinder.AddExpectedErrorMessage("material type is now at version '10'"); material.ApplyVersionUpdates(); warningFinder.CheckExpectedErrorsFound(); From 317bb7aa6726fa01afa1c33e1e733fae500e7a7b Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:14:00 -0400 Subject: [PATCH 104/152] Owning connection id will happen on clients as well. Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Source/Components/NetworkHierarchyRootComponent.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 854aec521b..6858242cd4 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -333,8 +333,9 @@ namespace Multiplayer const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); controller->SetHierarchyRoot(netRootId); - GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent()->GetOwningConnectionId()); } + + GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent()->GetOwningConnectionId()); } else { @@ -347,9 +348,10 @@ namespace Multiplayer NetworkHierarchyChildComponentController* controller = static_cast(GetController()); controller->SetHierarchyRoot(InvalidNetEntityId); - GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); } + GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); + // We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy. RebuildHierarchy(); } From 3fc32675fd48429b85512c7f8e6655466ec8dbbd Mon Sep 17 00:00:00 2001 From: jromnoa Date: Thu, 21 Oct 2021 12:39:09 -0700 Subject: [PATCH 105/152] removes general.log() checks for pass/fail status as these do not appear in AR and only appear locally Signed-off-by: jromnoa --- AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py | 5 ++--- .../tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py | 1 - .../PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py index 381f266fab..de48ab2371 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py @@ -89,7 +89,7 @@ class TestAllComponentsIndepthTests(object): level_creation_expected_lines = [ "Viewport is set to the expected size: True", - "Basic level created" + "Exited game mode" ] unexpected_lines = [ "Trace::Assert", @@ -189,8 +189,7 @@ class TestPerformanceBenchmarkSuite(object): "Benchmark metadata captured.", "Pass timestamps captured.", "CPU frame time captured.", - "Capturing complete.", - "Captured data successfully." + "Exited game mode" ] unexpected_lines = [ diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py index fbd9f3459a..de7c9d2326 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py @@ -90,7 +90,6 @@ def run(): benchmarker.capture_cpu_frame_time(i) general.exit_game_mode() helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=2.0) - general.log("Capturing complete.") if __name__ == "__main__": diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py index 93e78a7c0a..9515712583 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py @@ -215,7 +215,6 @@ def run(): ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{'AtomBasicLevelSetup'}.ppm") general.exit_game_mode() helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=2.0) - general.log("Basic level created") if __name__ == "__main__": From 244878483a7fa04f6d7e10b06339f4e9669a6a7b Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 21 Oct 2021 12:40:10 -0700 Subject: [PATCH 106/152] Update open file limit on linux for applications (#4878) * Programmatically update the ulimit for open files if the current limit is not enough Signed-off-by: Steve Pham --- .../Application/Application_Linux.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp index 158240210e..85669e093c 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp @@ -7,17 +7,35 @@ */ #include +#include #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB #include #endif +constexpr rlim_t g_minimumOpenFileHandles = 65536L; + //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { //////////////////////////////////////////////////////////////////////////////////////////////// Application::Implementation* Application::Implementation::Create() { + // The default open file limit for processes may not be enough for O3DE applications. + // We will need to increase to the recommended value if the current open file limit + // is not sufficient. + rlimit currentLimit; + int get_limit_result = getrlimit(RLIMIT_NOFILE, ¤tLimit); + AZ_Warning("Application", get_limit_result == 0, "Unable to read current ulimit open file limits"); + if ((get_limit_result == 0) && (currentLimit.rlim_cur < g_minimumOpenFileHandles || currentLimit.rlim_max < g_minimumOpenFileHandles)) + { + rlimit newLimit; + newLimit.rlim_cur = g_minimumOpenFileHandles; // Soft Limit + newLimit.rlim_max = g_minimumOpenFileHandles; // Hard Limit + [[maybe_unused]] int set_limit_result = setrlimit(RLIMIT_NOFILE, &newLimit); + AZ_Assert(set_limit_result == 0, "Unable to update open file limits"); + } + #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB return aznew XcbApplication(); #elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND From 94f54e858637a79eca09b56abd2fa29e5d21f75d Mon Sep 17 00:00:00 2001 From: jromnoa Date: Thu, 21 Oct 2021 12:52:05 -0700 Subject: [PATCH 107/152] re-add the 'Captured data successfully.' log line to test_AtomFeatureIntegrationBenchmark() test, removed on accident Signed-off-by: jromnoa --- AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py | 1 + 1 file changed, 1 insertion(+) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py index de48ab2371..23fb249761 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py @@ -189,6 +189,7 @@ class TestPerformanceBenchmarkSuite(object): "Benchmark metadata captured.", "Pass timestamps captured.", "CPU frame time captured.", + "Captured data successfully.", "Exited game mode" ] From b32a6a1369a28d19c2af3865c8e81f52fbc88e50 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 21 Oct 2021 12:52:28 -0700 Subject: [PATCH 108/152] Reverted accidental change to StandardPBR.material type's color property name. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Common/Assets/Materials/Types/StandardPBR.materialtype | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index 84b4c29fcb..7527a7658a 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -152,7 +152,7 @@ ], "baseColor": [ { - "name": "colorX", + "name": "color", "displayName": "Color", "description": "Color is displayed as sRGB but the values are stored as linear color.", "type": "Color", From 24d7a90e5f4499250f334e2f7adf07d9091619c4 Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Thu, 21 Oct 2021 13:01:46 -0700 Subject: [PATCH 109/152] Fix a warning Signed-off-by: AMZN-Phil --- Code/Tools/ProjectManager/Source/DownloadController.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Tools/ProjectManager/Source/DownloadController.h b/Code/Tools/ProjectManager/Source/DownloadController.h index 385ceb4772..608d9b1a2b 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.h +++ b/Code/Tools/ProjectManager/Source/DownloadController.h @@ -47,7 +47,8 @@ namespace O3DE::ProjectManager } else { - return QString(); + static const QString emptyString; + return emptyString; } } public slots: From 7ffcbe080e57d236e3b765b1f8213696fdc2bf1a Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 21 Oct 2021 13:23:18 -0700 Subject: [PATCH 110/152] Updated Material Editor's MaterialDocument to apply version updates to the materials that it opens. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Code/Source/Document/MaterialDocument.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 51164a70f4..98af749261 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -685,6 +685,12 @@ namespace MaterialEditor return false; } m_materialTypeSourceData = materialTypeOutcome.GetValue(); + + if (MaterialSourceData::ApplyVersionUpdatesResult::Failed == m_materialSourceData.ApplyVersionUpdates(m_absolutePath)) + { + AZ_Error("MaterialDocument", false, "Material source data could not be auto updated to the latest version of the material type: '%s'.", m_materialSourceData.m_materialType.c_str()); + return false; + } } else if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), MaterialTypeSourceData::Extension)) { From 50f66bde69c00e507df4861e0eb8aa934b0f8923 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:38:56 -0500 Subject: [PATCH 111/152] Remove the use of a .Stub version of the Wwise Gem (#4879) * Remove the definition of a 'Stub' Wwise Gem This removes the conditional compilation of a '.Stub' version of the Wwise Gem. Now, all we do is not define any of the AudioEngineWwise targets in CMake whenever Wwise is not available (SDK not found or platform unsupported). Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Remove the remaining stub files, no longer needed This removes the files.cmake and stub module cpp files that made up the .Stub version of this Gem. These are no longer needed. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- Gems/AudioEngineWwise/Code/CMakeLists.txt | 27 +++++-------------- .../Code/Platform/Android/PAL_android.cmake | 2 +- .../Code/Platform/Linux/PAL_linux.cmake | 2 +- .../Code/Platform/Mac/PAL_mac.cmake | 2 +- .../Code/Platform/Windows/PAL_windows.cmake | 2 +- .../Code/Platform/iOS/PAL_ios.cmake | 2 +- .../Source/AudioEngineWwiseModule_Stub.cpp | 11 -------- .../Code/audioenginewwise_stub_files.cmake | 11 -------- 8 files changed, 12 insertions(+), 47 deletions(-) delete mode 100644 Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseModule_Stub.cpp delete mode 100644 Gems/AudioEngineWwise/Code/audioenginewwise_stub_files.cmake diff --git a/Gems/AudioEngineWwise/Code/CMakeLists.txt b/Gems/AudioEngineWwise/Code/CMakeLists.txt index 98a31fb91a..33c80bc31c 100644 --- a/Gems/AudioEngineWwise/Code/CMakeLists.txt +++ b/Gems/AudioEngineWwise/Code/CMakeLists.txt @@ -18,31 +18,18 @@ set(AUDIOENGINEWWISE_COMPILEDEFINITIONS find_package(Wwise MODULE) ################################################################################ -# Server / Unsupported +# Servers +# (and situations where Wwise SDK is not found or otherwise unavailable) ################################################################################ -if (PAL_TRAIT_BUILD_SERVER_SUPPORTED OR PAL_TRAIT_AUDIO_ENGINE_WWISE_USE_STUB OR NOT Wwise_FOUND) - # Stub gem for server and unsupported platforms. Audio Engine Wwise is client only - ly_add_target( - NAME AudioEngineWwise.Stub ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} - NAMESPACE Gem - FILES_CMAKE - audioenginewwise_stub_files.cmake - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore - ) -endif() - -if (PAL_TRAIT_AUDIO_ENGINE_WWISE_USE_STUB OR NOT Wwise_FOUND) - # setup aliases so stubs will be used if something references AudioEngineWwise(.Editor) - add_library(Gem::AudioEngineWwise ALIAS AudioEngineWwise.Stub) - add_library(Gem::AudioEngineWwise.Editor ALIAS AudioEngineWwise.Stub) +if(NOT PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED OR NOT Wwise_FOUND) + # Don't create any Gem targets and aliases. Nothing should depend on this + # Gem directly, because if it doesn't define targets it will cause an error. return() endif() ################################################################################ -# Runtime / Game +# Clients ################################################################################ ly_add_target( NAME AudioEngineWwise.Static STATIC @@ -181,7 +168,7 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) endif() ################################################################################ -# Tools / Editor +# Tools / Builders ################################################################################ if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( diff --git a/Gems/AudioEngineWwise/Code/Platform/Android/PAL_android.cmake b/Gems/AudioEngineWwise/Code/Platform/Android/PAL_android.cmake index e3729baea7..c706c464d3 100644 --- a/Gems/AudioEngineWwise/Code/Platform/Android/PAL_android.cmake +++ b/Gems/AudioEngineWwise/Code/Platform/Android/PAL_android.cmake @@ -6,4 +6,4 @@ # # -set(PAL_TRAIT_AUDIO_ENGINE_WWISE_USE_STUB FALSE) +set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE) diff --git a/Gems/AudioEngineWwise/Code/Platform/Linux/PAL_linux.cmake b/Gems/AudioEngineWwise/Code/Platform/Linux/PAL_linux.cmake index e3729baea7..c706c464d3 100644 --- a/Gems/AudioEngineWwise/Code/Platform/Linux/PAL_linux.cmake +++ b/Gems/AudioEngineWwise/Code/Platform/Linux/PAL_linux.cmake @@ -6,4 +6,4 @@ # # -set(PAL_TRAIT_AUDIO_ENGINE_WWISE_USE_STUB FALSE) +set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE) diff --git a/Gems/AudioEngineWwise/Code/Platform/Mac/PAL_mac.cmake b/Gems/AudioEngineWwise/Code/Platform/Mac/PAL_mac.cmake index e3729baea7..c706c464d3 100644 --- a/Gems/AudioEngineWwise/Code/Platform/Mac/PAL_mac.cmake +++ b/Gems/AudioEngineWwise/Code/Platform/Mac/PAL_mac.cmake @@ -6,4 +6,4 @@ # # -set(PAL_TRAIT_AUDIO_ENGINE_WWISE_USE_STUB FALSE) +set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE) diff --git a/Gems/AudioEngineWwise/Code/Platform/Windows/PAL_windows.cmake b/Gems/AudioEngineWwise/Code/Platform/Windows/PAL_windows.cmake index e3729baea7..c706c464d3 100644 --- a/Gems/AudioEngineWwise/Code/Platform/Windows/PAL_windows.cmake +++ b/Gems/AudioEngineWwise/Code/Platform/Windows/PAL_windows.cmake @@ -6,4 +6,4 @@ # # -set(PAL_TRAIT_AUDIO_ENGINE_WWISE_USE_STUB FALSE) +set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE) diff --git a/Gems/AudioEngineWwise/Code/Platform/iOS/PAL_ios.cmake b/Gems/AudioEngineWwise/Code/Platform/iOS/PAL_ios.cmake index e3729baea7..c706c464d3 100644 --- a/Gems/AudioEngineWwise/Code/Platform/iOS/PAL_ios.cmake +++ b/Gems/AudioEngineWwise/Code/Platform/iOS/PAL_ios.cmake @@ -6,4 +6,4 @@ # # -set(PAL_TRAIT_AUDIO_ENGINE_WWISE_USE_STUB FALSE) +set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE) diff --git a/Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseModule_Stub.cpp b/Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseModule_Stub.cpp deleted file mode 100644 index 4344eb6072..0000000000 --- a/Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseModule_Stub.cpp +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include - -AZ_DECLARE_MODULE_CLASS(Gem_AudioEngineWwise, AZ::Module) diff --git a/Gems/AudioEngineWwise/Code/audioenginewwise_stub_files.cmake b/Gems/AudioEngineWwise/Code/audioenginewwise_stub_files.cmake deleted file mode 100644 index 5597c28d04..0000000000 --- a/Gems/AudioEngineWwise/Code/audioenginewwise_stub_files.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -set(FILES - Source/AudioEngineWwiseModule_Stub.cpp -) From 52112be1acaa4c10c5dbc3e7e2a0f5d87ea34148 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Thu, 21 Oct 2021 13:43:21 -0700 Subject: [PATCH 112/152] LYN-7448 + LYN-7542 + LYN-7543 | Focus Mode - UX Improvements (#4837) * Introduce Outliner button to simplify Prefab editing Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Fix Focus Mode and disabled entities colors Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Fix - propagate the event if the OnOutlinerItemClick function returns false. This does not change current behavior but makes more sense in the context of future handlers. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Adjust disabled colors to match UX recommendations. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Display the edit button even if the prefab is disabled. Remove prefabWip check (it will be removed for focus mode by the time this goes in). Default to disabled capsule color for borders to save on checks. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Disable edit button on disabled prefabs as it caused conflicts in nested prefabs. May explore that possibility later. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Change disabled text color to be darker, as asked by UX. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Images/Entity/prefab_edit_close.svg | 4 + .../Images/Entity/prefab_edit_open.svg | 4 + .../AzQtComponents/Images/resources.qrc | 2 + .../Prefab/PrefabFocusHandler.cpp | 28 ++++ .../Prefab/PrefabFocusHandler.h | 1 + .../Prefab/PrefabFocusPublicInterface.h | 7 +- .../EditorEntityUiHandlerBase.cpp | 19 ++- .../EditorEntityUiHandlerBase.h | 13 +- .../UI/Outliner/EntityOutlinerListModel.cpp | 28 +++- .../UI/Outliner/EntityOutlinerTreeView.cpp | 7 + .../UI/Outliner/EntityOutlinerTreeView.hxx | 2 + .../UI/Outliner/EntityOutlinerWidget.cpp | 21 ++- .../UI/Prefab/PrefabUiHandler.cpp | 131 +++++++++++++++++- .../UI/Prefab/PrefabUiHandler.h | 11 +- .../ViewportUi/ViewportUiDisplay.cpp | 2 +- 15 files changed, 265 insertions(+), 15 deletions(-) create mode 100644 Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_close.svg create mode 100644 Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_open.svg diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_close.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_close.svg new file mode 100644 index 0000000000..3fccf0e716 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_open.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_open.svg new file mode 100644 index 0000000000..55704ec230 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc index 0c8fedc79d..15049aeb69 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc @@ -6,6 +6,8 @@ Entity/layer.svg Entity/prefab.svg Entity/prefab_edit.svg + Entity/prefab_edit_open.svg + Entity/prefab_edit_close.svg Level/level.svg diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 09b5745a90..8bc258fef9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -206,6 +206,34 @@ namespace AzToolsFramework::Prefab return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); } + bool PrefabFocusHandler::IsOwningPrefabInFocusHierarchy(AZ::EntityId entityId) const + { + if (!m_focusedInstance.has_value()) + { + // PrefabFocusHandler has not been initialized yet. + return false; + } + + if (!entityId.IsValid()) + { + return false; + } + + InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); + + while (instance.has_value()) + { + if (&instance->get() == &m_focusedInstance->get()) + { + return true; + } + + instance = instance->get().GetParentInstance(); + } + + return false; + } + const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_instanceFocusPath; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 6a71365d8e..cb8165e7f0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -53,6 +53,7 @@ namespace AzToolsFramework::Prefab PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override; AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const override; bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override; + bool IsOwningPrefabInFocusHierarchy(AZ::EntityId entityId) const override; const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override; const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h index 86e476b56f..5bd4c6b0f6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h @@ -37,10 +37,15 @@ namespace AzToolsFramework::Prefab //! Returns the entity id of the container entity for the instance the prefab system is focusing on. virtual AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const = 0; + //! Returns whether the entity belongs to the instance that is being focused on. + //! @param entityId The entityId of the queried entity. + //! @return true if the entity belongs to the focused instance, false otherwise. + virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0; + //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. //! @param entityId The entityId of the queried entity. //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. - virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0; + virtual bool IsOwningPrefabInFocusHierarchy(AZ::EntityId entityId) const = 0; //! Returns the path from the root instance to the currently focused instance. //! @return A path composed from the names of the container entities for the instance path. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.cpp index 3cdcade1b0..866080a83f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.cpp @@ -101,8 +101,25 @@ namespace AzToolsFramework { } - void EditorEntityUiHandlerBase::OnDoubleClick([[maybe_unused]] AZ::EntityId entityId) const + bool EditorEntityUiHandlerBase::OnOutlinerItemClick( + [[maybe_unused]] const QPoint& position, + [[maybe_unused]] const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index) const + { + return false; + } + + void EditorEntityUiHandlerBase::OnOutlinerItemExpand([[maybe_unused]] const QModelIndex& index) const + { + } + + void EditorEntityUiHandlerBase::OnOutlinerItemCollapse([[maybe_unused]] const QModelIndex& index) const { } + bool EditorEntityUiHandlerBase::OnEntityDoubleClick([[maybe_unused]] AZ::EntityId entityId) const + { + return false; + } + } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h index 9554ad01fc..96d393efa1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h @@ -61,8 +61,17 @@ namespace AzToolsFramework virtual void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, const QModelIndex& descendantIndex) const; - //! Triggered when the entity is double clicked in the Outliner. - virtual void OnDoubleClick(AZ::EntityId entityId) const; + //! Triggered when the entity is clicked in the Outliner. + //! @return True if the click has been handled and should not be propagated, false otherwise. + virtual bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const; + //! Triggered when an entity's children are expanded in the Outliner. + virtual void OnOutlinerItemExpand(const QModelIndex& index) const; + //! Triggered when an entity's children are collapsed in the Outliner. + virtual void OnOutlinerItemCollapse(const QModelIndex& index) const; + + //! Triggered when the entity is double clicked in the Outliner or in the Viewport. + //! @return True if the double click has been handled and should not be propagated, false otherwise. + virtual bool OnEntityDoubleClick(AZ::EntityId entityId) const; private: EditorEntityUiHandlerId m_handlerId = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index 3f8023c1e3..a5f1e29942 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -11,10 +11,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -2287,7 +2289,14 @@ namespace AzToolsFramework // Now we setup a Text Document so it can draw the rich text QTextDocument textDoc; textDoc.setDefaultFont(optionV4.font); - textDoc.setDefaultStyleSheet("body {color: white}"); + if (option.state & QStyle::State_Enabled) + { + textDoc.setDefaultStyleSheet("body {color: white}"); + } + else + { + textDoc.setDefaultStyleSheet("body {color: #7C7C7C}"); + } textDoc.setHtml("" + entityNameRichText + ""); painter->translate(textRect.topLeft()); textDoc.setTextWidth(textRect.width()); @@ -2326,6 +2335,23 @@ namespace AzToolsFramework return true; } + if (event->type() == QEvent::MouseButtonPress) + { + AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); + + if (auto editorEntityUiInterface = AZ::Interface::Get(); editorEntityUiInterface != nullptr) + { + auto mouseEvent = static_cast(event); + + auto entityUiHandler = editorEntityUiInterface->GetHandler(entityId); + + if (entityUiHandler && entityUiHandler->OnOutlinerItemClick(mouseEvent->pos(), option, index)) + { + return true; + } + } + } + return QStyledItemDelegate::editorEvent(event, model, option, index); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index d3138f5139..d94ced392c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -73,6 +73,8 @@ namespace AzToolsFramework void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event) { m_mousePosition = QPoint(); + m_currentHoveredIndex = QModelIndex(); + update(); } void EntityOutlinerTreeView::mousePressEvent(QMouseEvent* event) @@ -129,6 +131,11 @@ namespace AzToolsFramework } m_mousePosition = event->pos(); + if (QModelIndex hoveredIndex = indexAt(m_mousePosition); m_currentHoveredIndex != indexAt(m_mousePosition)) + { + m_currentHoveredIndex = hoveredIndex; + update(); + } //process mouse movement as normal, potentially triggering drag and drop QTreeView::mouseMoveEvent(event); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx index 5d76ec6db2..42b42a59b4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx @@ -90,6 +90,8 @@ namespace AzToolsFramework const QColor m_selectedColor = QColor(255, 255, 255, 45); const QColor m_hoverColor = QColor(255, 255, 255, 30); + QModelIndex m_currentHoveredIndex; + EditorEntityUiInterface* m_editorEntityFrameworkInterface; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index 4d53102edb..3e97f967fc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -902,6 +902,7 @@ namespace AzToolsFramework EditorPickModeRequestBus::Broadcast( &EditorPickModeRequests::StopEntityPickMode); + return; } switch (index.column()) @@ -918,18 +919,30 @@ namespace AzToolsFramework { if (AZ::EntityId entityId = GetEntityIdFromIndex(index); auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId)) { - entityUiHandler->OnDoubleClick(entityId); + entityUiHandler->OnEntityDoubleClick(entityId); } } void EntityOutlinerWidget::OnTreeItemExpanded(const QModelIndex& index) { - m_listModel->OnEntityExpanded(GetEntityIdFromIndex(index)); + AZ::EntityId entityId = GetEntityIdFromIndex(index); + if (auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId)) + { + entityUiHandler->OnOutlinerItemExpand(index); + } + + m_listModel->OnEntityExpanded(entityId); } void EntityOutlinerWidget::OnTreeItemCollapsed(const QModelIndex& index) { - m_listModel->OnEntityCollapsed(GetEntityIdFromIndex(index)); + AZ::EntityId entityId = GetEntityIdFromIndex(index); + if (auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId)) + { + entityUiHandler->OnOutlinerItemCollapse(index); + } + + m_listModel->OnEntityCollapsed(entityId); } void EntityOutlinerWidget::OnExpandEntity(const AZ::EntityId& entityId, bool expand) @@ -1163,7 +1176,7 @@ namespace AzToolsFramework { QTimer::singleShot(1, this, [this]() { m_gui->m_objectTree->setUpdatesEnabled(true); - m_gui->m_objectTree->expandToDepth(0); + m_gui->m_objectTree->expand(m_proxyModel->index(0,0)); }); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index 7c2c65d204..447f94fc15 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -21,10 +21,16 @@ namespace AzToolsFramework { + const QColor PrefabUiHandler::m_backgroundColor = QColor("#444444"); + const QColor PrefabUiHandler::m_backgroundHoverColor = QColor("#5A5A5A"); + const QColor PrefabUiHandler::m_backgroundSelectedColor = QColor("#656565"); const QColor PrefabUiHandler::m_prefabCapsuleColor = QColor("#1E252F"); + const QColor PrefabUiHandler::m_prefabCapsuleDisabledColor = QColor("#35383C"); const QColor PrefabUiHandler::m_prefabCapsuleEditColor = QColor("#4A90E2"); const QString PrefabUiHandler::m_prefabIconPath = QString(":/Entity/prefab.svg"); const QString PrefabUiHandler::m_prefabEditIconPath = QString(":/Entity/prefab_edit.svg"); + const QString PrefabUiHandler::m_prefabEditOpenIconPath = QString(":/Entity/prefab_edit_open.svg"); + const QString PrefabUiHandler::m_prefabEditCloseIconPath = QString(":/Entity/prefab_edit_close.svg"); PrefabUiHandler::PrefabUiHandler() { @@ -75,7 +81,7 @@ namespace AzToolsFramework if (!path.empty()) { - tooltip = QObject::tr("%1").arg(path.Native().data()); + tooltip = QObject::tr("Double click to edit.\n%1").arg(path.Native().data()); } return tooltip; @@ -102,13 +108,20 @@ namespace AzToolsFramework AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); const bool isFirstColumn = index.column() == EntityOutlinerListModel::ColumnName; const bool isLastColumn = index.column() == EntityOutlinerListModel::ColumnLockToggle; - const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value() && index.model()->hasChildren(index); + QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName); + const bool hasVisibleChildren = + firstColumnIndex.data(EntityOutlinerListModel::ExpandedRole).value() && + firstColumnIndex.model()->hasChildren(firstColumnIndex); QColor backgroundColor = m_prefabCapsuleColor; if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { backgroundColor = m_prefabCapsuleEditColor; } + else if (!(option.state & QStyle::State_Enabled)) + { + backgroundColor = m_prefabCapsuleDisabledColor; + } QPainterPath backgroundPath; backgroundPath.setFillRule(Qt::WindingFill); @@ -184,7 +197,8 @@ namespace AzToolsFramework const bool isFirstColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnName; const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle; - QColor borderColor = m_prefabCapsuleColor; + // There is no legal way of opening prefabs in their default state, so default to disabled. + QColor borderColor = m_prefabCapsuleDisabledColor; if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { borderColor = m_prefabCapsuleEditColor; @@ -273,6 +287,71 @@ namespace AzToolsFramework painter->restore(); } + void PrefabUiHandler::PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, [[maybe_unused]] const QModelIndex& index) const + { + AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); + const QPoint offset = QPoint(-18, 3); + QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName); + const int iconSize = 16; + const bool isHovered = (option.state & QStyle::State_MouseOver); + const bool isSelected = index.data(EntityOutlinerListModel::SelectedRole).template value(); + const bool isFirstColumn = index.column() == EntityOutlinerListModel::ColumnName; + const bool isExpanded = + firstColumnIndex.data(EntityOutlinerListModel::ExpandedRole).value() && + firstColumnIndex.model()->hasChildren(firstColumnIndex); + + if (!isFirstColumn || !(option.state & QStyle::State_Enabled)) + { + return; + } + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + + if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) + { + // Only show the close icon if the prefab is expanded. + // This allows the prefab container to be opened if it was collapsed during propagation. + if (!isExpanded) + { + return; + } + + // Use the same color as the background. + QColor backgroundColor = m_backgroundColor; + if (isSelected) + { + backgroundColor = m_backgroundSelectedColor; + } + else if (isHovered) + { + backgroundColor = m_backgroundHoverColor; + } + + // Paint a rect to cover up the expander. + QRect rect = QRect(0, 0, 16, 16); + rect.translate(option.rect.topLeft() + offset); + painter->fillRect(rect, backgroundColor); + + // Paint the icon. + QIcon closeIcon = QIcon(m_prefabEditCloseIconPath); + painter->drawPixmap(option.rect.topLeft() + offset, closeIcon.pixmap(iconSize)); + } + else + { + // Only show the edit icon on hover. + if (!isHovered) + { + return; + } + + QIcon openIcon = QIcon(m_prefabEditOpenIconPath); + painter->drawPixmap(option.rect.topLeft() + offset, openIcon.pixmap(iconSize)); + } + + painter->restore(); + } + bool PrefabUiHandler::IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child) { QModelIndex lastVisibleItemIndex = GetLastVisibleChild(parent); @@ -314,9 +393,53 @@ namespace AzToolsFramework return Internal_GetLastVisibleChild(model, lastChild); } - void PrefabUiHandler::OnDoubleClick(AZ::EntityId entityId) const + bool PrefabUiHandler::OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const + { + AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); + const QPoint offset = QPoint(-18, 3); + + if (m_prefabFocusPublicInterface->IsOwningPrefabInFocusHierarchy(entityId)) + { + QRect iconRect = QRect(0, 0, 16, 16); + iconRect.translate(option.rect.topLeft() + offset); + + if (iconRect.contains(position)) + { + if (!m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) + { + // Focus on this prefab. + m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); + } + + // Don't propagate event. + return true; + } + } + + return false; + } + + void PrefabUiHandler::OnOutlinerItemCollapse(const QModelIndex& index) const + { + AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); + + if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) + { + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + // Go one level up. + int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(editorEntityContextId); + m_prefabFocusPublicInterface->FocusOnPathIndex(editorEntityContextId, length - 2); + } + } + + bool PrefabUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const { // Focus on this prefab m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); + + // Don't propagate event. + return true; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h index 7c68d9fd95..6c78afc5b7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h @@ -36,7 +36,10 @@ namespace AzToolsFramework void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, const QModelIndex& descendantIndex) const override; - void OnDoubleClick(AZ::EntityId entityId) const override; + void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + void OnOutlinerItemCollapse(const QModelIndex& index) const override; + bool OnEntityDoubleClick(AZ::EntityId entityId) const override; private: Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; @@ -48,9 +51,15 @@ namespace AzToolsFramework static constexpr int m_prefabCapsuleRadius = 6; static constexpr int m_prefabBorderThickness = 2; + static const QColor m_backgroundColor; + static const QColor m_backgroundHoverColor; + static const QColor m_backgroundSelectedColor; static const QColor m_prefabCapsuleColor; + static const QColor m_prefabCapsuleDisabledColor; static const QColor m_prefabCapsuleEditColor; static const QString m_prefabIconPath; static const QString m_prefabEditIconPath; + static const QString m_prefabEditOpenIconPath; + static const QString m_prefabEditCloseIconPath; }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index a289d914d6..43e2a6793f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -20,7 +20,7 @@ namespace AzToolsFramework::ViewportUi::Internal { const static int HighlightBorderSize = 5; const static int TopHighlightBorderSize = 25; - const static char* HighlightBorderColor = "#44B2F8"; + const static char* HighlightBorderColor = "#4A90E2"; static void UnparentWidgets(ViewportUiElementIdInfoLookup& viewportUiElementIdInfoLookup) { From ea442b80d1e8725269dc41bb039c0ad8dbb6a676 Mon Sep 17 00:00:00 2001 From: AMZN-AlexOteiza <82234181+AMZN-AlexOteiza@users.noreply.github.com> Date: Thu, 21 Oct 2021 22:03:18 +0100 Subject: [PATCH 113/152] Renamed ctest_pytest.ini to pytest.ini so it is used by default, added TestSuite_ as collection file (#4822) * Fixed warnings of unused marks, renamed ctest_pytest.ini to pytest.ini to better consistency on runs * Fixed some test suites to run propertly * Fix missing arguments * Fixed missing cmakelists and renamed missing file * Temp disable editor_testing_tests as timeout in jenkins --- .../Gem/PythonTests/Atom/TestSuite_Sandbox.py | 4 ---- .../Gem/PythonTests/CMakeLists.txt | 3 +++ .../PythonTests/Physics/TestSuite_Utils.py | 21 +++++++--------- .../editor_test_testing/CMakeLists.txt | 24 +++++++++++++++++++ .../editor_test_testing/TestSuite_Main.py | 9 +++---- .../editor_test_testing/conftest.py | 8 ------- Tools/LyTestTools/setup.py | 3 ++- cmake/LYTestWrappers.cmake | 2 +- cmake/Platform/Common/Install_common.cmake | 2 +- ctest_pytest.ini => pytest.ini | 3 ++- 10 files changed, 47 insertions(+), 32 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt delete mode 100644 AutomatedTesting/Gem/PythonTests/editor_test_testing/conftest.py rename ctest_pytest.ini => pytest.ini (91%) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py index 9bb7f9c50e..ad45e51080 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py @@ -14,10 +14,6 @@ import editor_python_test_tools.hydra_test_utils as hydra logger = logging.getLogger(__name__) TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("level", ["auto_test"]) class TestAtomEditorComponentsSandbox(object): # It requires at least one test diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index 2ca7f7ea2b..fd3222ba83 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -58,3 +58,6 @@ add_subdirectory(smoke) ## AWS ## add_subdirectory(AWS) + +## Integration tests for editor testing framework ## +add_subdirectory(editor_test_testing) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Utils.py b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Utils.py index a3fd5c741f..61c2816f50 100755 --- a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Utils.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Utils.py @@ -9,20 +9,17 @@ import pytest import sys import ly_test_tools.environment.file_system as fs -from .FileManagement import FileManagement as fm +from .utils.FileManagement import FileManagement as fm sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared') -from .base import TestAutomationBase +from base import TestAutomationBase - -@pytest.mark.parametrize("platform", ["win_x64_vs2017"]) -@pytest.mark.parametrize("configuration", ["profile"]) -@pytest.mark.parametrize("spec", ["all"]) +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) class TestUtils(TestAutomationBase): @fm.file_revert("UtilTest_Physmaterial_Editor_TestLibrary.physmaterial", r"AutomatedTesting\Levels\Physics\Physmaterial_Editor_Test") - def test_physmaterial_editor(self, request, workspace, editor): + def test_physmaterial_editor(self, request, workspace, launcher_platform, editor): """ Tests functionality of physmaterial editing utility :param workspace: Fixture containing platform and project detail @@ -35,11 +32,11 @@ class TestUtils(TestAutomationBase): unexpected_lines = ["Assert"] self._run_test(request, workspace, editor, physmaterial_editor_test_module, expected_lines, unexpected_lines) - def test_UtilTest_Tracer_PicksErrorsAndWarnings(self, request, workspace, editor): + def test_UtilTest_Tracer_PicksErrorsAndWarnings(self, request, workspace, launcher_platform, editor): from .utils import UtilTest_Tracer_PicksErrorsAndWarnings as testcase_module self._run_test(request, workspace, editor, testcase_module, [], []) - def test_FileManagement_FindingFiles(self, workspace): + def test_FileManagement_FindingFiles(self, workspace, launcher_platform): """ Tests the functionality of "searching for files" with FileManagement._find_files() :param workspace: ly_test_tools workspace fixture @@ -110,7 +107,7 @@ class TestUtils(TestAutomationBase): find_me_too_path, found_me["FindMeToo.txt"] ) - def test_FileManagement_FileBackup(self, workspace): + def test_FileManagement_FileBackup(self, workspace, launcher_platform): """ Tests the functionality of the file back up system via the FileManagement class :param workspace: ly_test_tools workspace fixture @@ -167,7 +164,7 @@ class TestUtils(TestAutomationBase): del file_map[target_file_path] fm._save_file_map(file_map) - def test_FileManagement_FileRestoration(self, workspace): + def test_FileManagement_FileRestoration(self, workspace, launcher_platform): """ Tests the restore file system via the FileManagement class :param workspace: ly_test_tools workspace fixture @@ -261,7 +258,7 @@ class TestUtils(TestAutomationBase): ["FindMe.txt", "FindMeToo.txt"], parent_path=r"AutomatedTesting\levels\Utils\Managed_files", search_subdirs=True ) @fm.file_override("default.physxconfiguration", "UtilTest_PhysxConfig_Override.physxconfiguration") - def test_UtilTest_Managed_Files(self, request, workspace, editor): + def test_UtilTest_Managed_Files(self, request, workspace, editor, launcher_platform): from .utils import UtilTest_Managed_Files as test_module expected_lines = [] diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt new file mode 100644 index 0000000000..dbf26d66cd --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +# This timeouts on jenkins, investigation is needed. Commment for now +# +#if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) +# ly_add_pytest( +# NAME AutomatedTesting::EditorTestTesting +# TEST_SUITE main +# TEST_SERIAL +# PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py +# RUNTIME_DEPENDENCIES +# Legacy::Editor +# AZ::AssetProcessor +# AutomatedTesting.Assets +# COMPONENT +# TestTools +# ) +#endif() diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py index 7c7e063951..15ba6690a1 100644 --- a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py @@ -35,10 +35,11 @@ class TestEditorTest: @classmethod def setup_class(cls): TestEditorTest.args = sys.argv.copy() - build_dir_arg_index = TestEditorTest.args.index("--build-directory") - if build_dir_arg_index < 0: - print("Error: Must pass --build-directory argument in order to run this test") - sys.exit(-2) + build_dir_arg_index = -1 + try: + build_dir_arg_index = TestEditorTest.args.index("--build-directory") + except ValueError as ex: + raise ValueError("Must pass --build-directory argument in order to run this test") TestEditorTest.args[build_dir_arg_index+1] = os.path.abspath(TestEditorTest.args[build_dir_arg_index+1]) TestEditorTest.args.append("-s") diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/conftest.py b/AutomatedTesting/Gem/PythonTests/editor_test_testing/conftest.py deleted file mode 100644 index 1f49f7111b..0000000000 --- a/AutomatedTesting/Gem/PythonTests/editor_test_testing/conftest.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Copyright (c) Contributors to the Open 3D Engine Project. -For complete copyright and license terms please see the LICENSE at the root of this distribution. - -SPDX-License-Identifier: Apache-2.0 OR MIT -""" - -pytest_plugins = ["pytester"] diff --git a/Tools/LyTestTools/setup.py b/Tools/LyTestTools/setup.py index e23f914dab..a523cbc2dd 100755 --- a/Tools/LyTestTools/setup.py +++ b/Tools/LyTestTools/setup.py @@ -48,7 +48,8 @@ if __name__ == '__main__': 'ly_test_tools=ly_test_tools._internal.pytest_plugin.test_tools_fixtures', 'testrail_filter=ly_test_tools._internal.pytest_plugin.case_id', 'terminal_report=ly_test_tools._internal.pytest_plugin.terminal_report', - 'editor_test=ly_test_tools._internal.pytest_plugin.editor_test' + 'editor_test=ly_test_tools._internal.pytest_plugin.editor_test', + 'pytester=_pytest.pytester' ], }, ) diff --git a/cmake/LYTestWrappers.cmake b/cmake/LYTestWrappers.cmake index 87649c5be6..9b139645fb 100644 --- a/cmake/LYTestWrappers.cmake +++ b/cmake/LYTestWrappers.cmake @@ -17,7 +17,7 @@ set(LY_GOOGLETEST_EXTRA_PARAMS CACHE STRING "Allows injection of additional opti find_package(Python REQUIRED MODULE) -ly_set(LY_PYTEST_EXECUTABLE ${LY_PYTHON_CMD} -B -m pytest -v --tb=short --show-capture=log -c ${LY_ROOT_FOLDER}/ctest_pytest.ini --build-directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$") +ly_set(LY_PYTEST_EXECUTABLE ${LY_PYTHON_CMD} -B -m pytest -v --tb=short --show-capture=log -c ${LY_ROOT_FOLDER}/pytest.ini --build-directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$") ly_set(LY_TEST_GLOBAL_KNOWN_SUITE_NAMES "smoke" "main" "periodic" "benchmark" "sandbox" "awsi") ly_set(LY_TEST_GLOBAL_KNOWN_REQUIREMENTS "gpu") diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index c453fadd2e..44cdeb1994 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -364,7 +364,7 @@ function(ly_setup_o3de_install) # Misc install(FILES - ${LY_ROOT_FOLDER}/ctest_pytest.ini + ${LY_ROOT_FOLDER}/pytest.ini ${LY_ROOT_FOLDER}/LICENSE.txt ${LY_ROOT_FOLDER}/README.md DESTINATION . diff --git a/ctest_pytest.ini b/pytest.ini similarity index 91% rename from ctest_pytest.ini rename to pytest.ini index 93310a522d..65c93e0eb2 100644 --- a/ctest_pytest.ini +++ b/pytest.ini @@ -7,10 +7,11 @@ # [pytest] -python_files = 'test_*.py' , '*_test.py' , '*_tests.py' +python_files = 'test_*.py' , '*_test.py' , '*_tests.py', 'TestSuite_*.py' norecursedirs = Python/2.7.* Python/3.7.5 BinTemp Cache SDKs JenkinsScripts cmake junit_family=legacy log_format=%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s) +addopts='--tb=short' '--show-capture=log' # primary suite markers which should appear on every filterable test and be mutually exclusive: markers = SUITE_smoke: Tiny, quick tests of fundamental operation (tests with no suite marker will also execute here in CI) From 081bfc253097b54e7f55ed5a7a718dd260142c88 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Thu, 21 Oct 2021 17:06:12 -0500 Subject: [PATCH 114/152] Added helper method to az_qt_helpers for retrieving the main window instance. Signed-off-by: Chris Galvan --- Gems/QtForPython/Editor/Scripts/az_qt_helpers.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Gems/QtForPython/Editor/Scripts/az_qt_helpers.py b/Gems/QtForPython/Editor/Scripts/az_qt_helpers.py index 6b9fbc8f08..758cc539d7 100755 --- a/Gems/QtForPython/Editor/Scripts/az_qt_helpers.py +++ b/Gems/QtForPython/Editor/Scripts/az_qt_helpers.py @@ -18,6 +18,14 @@ from shiboken2 import wrapInstance, getCppPointer view_pane_handlers = {} registration_handlers = {} +# Helper method for retrieving the Editor QMainWindow instance +def get_editor_main_window(): + params = azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, "GetQtBootstrapParameters") + editor_id = QtWidgets.QWidget.find(params.mainWindowId) + editor_main_window = wrapInstance(int(getCppPointer(editor_id)[0]), QtWidgets.QMainWindow) + + return editor_main_window + # Helper method for registering a Python widget as a tool/view pane with the Editor def register_view_pane(name, widget_type, options=editor.ViewPaneOptions()): global view_pane_handlers @@ -29,9 +37,7 @@ def register_view_pane(name, widget_type, options=editor.ViewPaneOptions()): # This method will be invoked by the ViewPaneCallbackBus::CreateViewPaneWidget # when our view pane needs to be created def on_create_view_pane_widget(parameters): - params = azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, "GetQtBootstrapParameters") - editor_id = QtWidgets.QWidget.find(params.mainWindowId) - editor_main_window = wrapInstance(int(getCppPointer(editor_id)[0]), QtWidgets.QMainWindow) + editor_main_window = get_editor_main_window() dock_main_window = editor_main_window.findChild(QtWidgets.QMainWindow) # Create the view pane widget parented to the Editor QMainWindow, so it can be found From 4cd6ce1e96e6174062fb4b002119688c05b62bee Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 21 Oct 2021 15:38:43 -0700 Subject: [PATCH 115/152] Fixing broken include path Signed-off-by: kberg-amzn --- .../AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml index fbe6ff2135..b789506d0f 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -8,7 +8,7 @@ OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - + From 2df466228aa7393f58a1d1c25c17ad77bb6a2132 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 21 Oct 2021 15:58:01 -0700 Subject: [PATCH 116/152] fixing one more broken include Signed-off-by: kberg-amzn --- Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index 2e91fecaac..248c97a60f 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include namespace Multiplayer From e3cfcd4cc7da7153b5980a03069b3ec774f2740f Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Thu, 21 Oct 2021 16:07:07 -0700 Subject: [PATCH 117/152] Add the ability for Python to pass download progress to Project Manager and to cancel downloads. Signed-off-by: AMZN-Phil --- .../Source/DownloadController.cpp | 29 +++++++--- .../Source/DownloadController.h | 4 +- .../GemCatalog/GemCatalogHeaderWidget.cpp | 11 +++- .../GemCatalog/GemCatalogHeaderWidget.h | 1 + .../ProjectManager/Source/PythonBindings.cpp | 54 +++++++++++++++++++ .../ProjectManager/Source/PythonBindings.h | 1 + .../Source/PythonBindingsInterface.h | 11 ++++ scripts/o3de/o3de/utils.py | 42 ++++++++++++++- 8 files changed, 142 insertions(+), 11 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/DownloadController.cpp b/Code/Tools/ProjectManager/Source/DownloadController.cpp index fa3fdb10d1..3ee800bba8 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadController.cpp @@ -8,10 +8,15 @@ #include #include +#include + +#include #include + + namespace O3DE::ProjectManager { DownloadController::DownloadController(QWidget* parent) @@ -46,6 +51,24 @@ namespace O3DE::ProjectManager } } + void DownloadController::CancelGemDownload(const QString& gemName) + { + auto findResult = AZStd::find(m_gemNames.begin(), m_gemNames.end(), gemName); + + if (findResult != m_gemNames.end()) + { + if (findResult == m_gemNames.begin()) + { + // HandleResults will remove the gem upon cancelling + PythonBindingsInterface::Get()->CancelDownload(); + } + else + { + m_gemNames.erase(findResult); + } + } + } + void DownloadController::UpdateUIProgress(int progress) { m_lastProgress = progress; @@ -75,10 +98,4 @@ namespace O3DE::ProjectManager m_workerThread.wait(); } } - - void DownloadController::HandleCancel() - { - m_workerThread.quit(); - emit Done(false); - } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/DownloadController.h b/Code/Tools/ProjectManager/Source/DownloadController.h index 608d9b1a2b..11ceaacddb 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.h +++ b/Code/Tools/ProjectManager/Source/DownloadController.h @@ -27,7 +27,8 @@ namespace O3DE::ProjectManager explicit DownloadController(QWidget* parent = nullptr); ~DownloadController(); - void AddGemDownload(const QString& m_gemName); + void AddGemDownload(const QString& gemName); + void CancelGemDownload(const QString& gemName); bool IsDownloadQueueEmpty() { @@ -54,7 +55,6 @@ namespace O3DE::ProjectManager public slots: void UpdateUIProgress(int progress); void HandleResults(const QString& result); - void HandleCancel(); signals: void StartGemDownload(const QString& gemName); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 79ff7624b7..875d5ada8c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -154,6 +154,11 @@ namespace O3DE::ProjectManager update(); } + void CartOverlayWidget::OnCancelDownloadActivated(const QString& gemName) + { + m_downloadController->CancelGemDownload(gemName); + } + void CartOverlayWidget::CreateDownloadSection() { QWidget* widget = new QWidget(); @@ -188,6 +193,8 @@ namespace O3DE::ProjectManager downloadingItemLayout->setAlignment(Qt::AlignTop); downloadingItemWidget->setLayout(downloadingItemLayout); + m_downloadController->AddGemDownload("TestGem"); + auto update = [=](int downloadProgress) { if (m_downloadController->IsDownloadQueueEmpty()) @@ -234,7 +241,9 @@ namespace O3DE::ProjectManager nameProgressLayout->addWidget(progress); QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); nameProgressLayout->addSpacerItem(spacer); - QLabel* cancelText = new QLabel(tr("Cancel")); + QLabel* cancelText = new QLabel(QString("Cancel").arg(downloadQueue[downloadingGemNumber])); + cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse); + connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated); nameProgressLayout->addWidget(cancelText); downloadingItemLayout->addLayout(nameProgressLayout); QProgressBar* downloadProgessBar = new QProgressBar(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 8e0eaa13ba..9f1e592d6e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -40,6 +40,7 @@ namespace O3DE::ProjectManager using GetTagIndicesCallback = AZStd::function()>; void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices); void CreateDownloadSection(); + void OnCancelDownloadActivated(const QString& link); QVBoxLayout* m_layout = nullptr; GemModel* m_gemModel = nullptr; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index bc8773b0c8..8b716666de 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -223,6 +223,50 @@ namespace RedirectOutput } } // namespace RedirectOutput +namespace O3DEProjectManagerPy +{ + using DownloadProgressFunc = AZStd::function; + DownloadProgressFunc currentProgressCallback; + bool requestCancelDownload = false; + + static PyObject* CLICancelDownload(PyObject* /*self*/, PyObject* /*args*/) + { + if (requestCancelDownload) + { + return Py_True; + } + else + { + return Py_False; + } + } + + static PyObject* CLIDownloadProgress(PyObject* /*self*/, PyObject* args) + { + int progress_percentage = 0; + if (!PyArg_ParseTuple(args, "i", &progress_percentage)) + return NULL; + if (currentProgressCallback) + { + currentProgressCallback(progress_percentage); + } + + Py_RETURN_NONE; + } + + static PyMethodDef O3DEPMPyMethods[] = { { "download_progress", CLIDownloadProgress, METH_VARARGS, "Used to call back to the UI to inform of download progress." }, + { "request_cancel_download", CLICancelDownload, METH_NOARGS, "Returns that the UI is requesting that the current download be cancelled." }, + { NULL, NULL, 0, NULL } }; + + static PyModuleDef O3DEPMPyModule = { PyModuleDef_HEAD_INIT, "o3de_projectmanager", NULL, -1, O3DEPMPyMethods, NULL, NULL, NULL, NULL }; + + static PyObject* PyInit_O3DEPMPy(void) + { + return PyModule_Create(&O3DEPMPyModule); + } +} // namespace O3DEProjectManagerPy + + namespace O3DE::ProjectManager { PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath) @@ -270,6 +314,7 @@ namespace O3DE::ProjectManager AZ_TracePrintf("python", "Py_GetProgramFullPath=%ls \n", Py_GetProgramFullPath()); PyImport_AppendInittab("azlmbr_redirect", RedirectOutput::PyInit_RedirectOutput); + PyImport_AppendInittab("o3de_projectmanager", &O3DEProjectManagerPy::PyInit_O3DEPMPy); try { @@ -1080,6 +1125,8 @@ namespace O3DE::ProjectManager AZ::Outcome PythonBindings::DownloadGem(const QString& gemName, std::function gemProgressCallback) { bool downloadSucceeded = false; + O3DEProjectManagerPy::currentProgressCallback = gemProgressCallback; + O3DEProjectManagerPy::requestCancelDownload = false; auto result = ExecuteWithLockErrorHandling( [&] { @@ -1091,6 +1138,8 @@ namespace O3DE::ProjectManager downloadSucceeded = (downloadResult.cast() == 0); }); + O3DEProjectManagerPy::currentProgressCallback = nullptr; + if (!result.IsSuccess()) { return result; @@ -1102,4 +1151,9 @@ namespace O3DE::ProjectManager return AZ::Success(); } + + void PythonBindings::CancelDownload() + { + O3DEProjectManagerPy::requestCancelDownload = true; + } } diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 638ce6b1d4..386268b5b2 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -62,6 +62,7 @@ namespace O3DE::ProjectManager bool RemoveGemRepo(const QString& repoUri) override; AZ::Outcome, AZStd::string> GetAllGemRepoInfos() override; AZ::Outcome DownloadGem(const QString& gemName, std::function gemProgressCallback) override; + void CancelDownload() override; private: AZ_DISABLE_COPY_MOVE(PythonBindings); diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index 19442540a4..928e9a8d48 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -188,7 +188,18 @@ namespace O3DE::ProjectManager */ virtual AZ::Outcome, AZStd::string> GetAllGemRepoInfos() = 0; + /** + * Downloads and registers a Gem. + * @param gemName the name of the Gem to download + * @param gemProgressCallback a callback function that is called with an int percentage download value + * @return an outcome with a string error message on failure. + */ virtual AZ::Outcome DownloadGem(const QString& gemName, std::function gemProgressCallback) = 0; + + /** + * Cancels the current download. + */ + virtual void CancelDownload() = 0; }; using PythonBindingsInterface = AZ::Interface; diff --git a/scripts/o3de/o3de/utils.py b/scripts/o3de/o3de/utils.py index c5be21c60f..cce51bbd8f 100755 --- a/scripts/o3de/o3de/utils.py +++ b/scripts/o3de/o3de/utils.py @@ -10,15 +10,44 @@ This file contains utility functions """ import sys import uuid +import os import pathlib import shutil import urllib.request import logging import zipfile +try: + import o3de_projectmanager +except ImportError: + pass logger = logging.getLogger() logging.basicConfig() +COPY_BUFSIZE = 64 * 1024 + +def copyfileobj(fsrc, fdst, callback, length=0): + # This is functionally the same as the python shutil copyfileobj but + # allows for a callback to return the download progress in blocks and allows + # to early out to cancel the copy. + if not length: + length = COPY_BUFSIZE + + fsrc_read = fsrc.read + fdst_write = fdst.write + + copied = 0 + while True: + if o3de_projectmanager and o3de_projectmanager.request_cancel_download(): + return 1 + buf = fsrc_read(length) + if not buf: + break + fdst_write(buf) + copied += len(buf) + callback(copied) + return 0 + def validate_identifier(identifier: str) -> bool: """ Determine if the identifier supplied is valid. @@ -93,7 +122,6 @@ def backup_folder(folder: str or pathlib.Path) -> None: if backup_folder_name.is_dir(): renamed = True - def download_file(parsed_uri, download_path: pathlib.Path) -> int: """ :param parsed_uri: uniform resource identifier to zip file to download @@ -103,8 +131,18 @@ def download_file(parsed_uri, download_path: pathlib.Path) -> int: logger.warn(f'File already downloaded to {download_path}.') elif parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: with urllib.request.urlopen(parsed_uri.geturl()) as s: + download_file_size = 0 + try: + download_file_size = s.headers['content-length'] + except KeyError: + pass + def download_progress(blocks): + if o3de_projectmanager and download_file_size: + o3de_projectmanager.download_progress(int(blocks/int(download_file_size) * 100)) with download_path.open('wb') as f: - shutil.copyfileobj(s, f) + download_cancelled = copyfileobj(s, f, download_progress) + if download_cancelled: + return 1 else: origin_file = pathlib.Path(parsed_uri.geturl()).resolve() if not origin_file.is_file(): From 5e6ecddbf98c59c491740f0953f9f7a9e227523d Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Thu, 21 Oct 2021 16:11:28 -0700 Subject: [PATCH 118/152] Remove some test code and extra newlines Signed-off-by: AMZN-Phil --- Code/Tools/ProjectManager/Source/DownloadController.cpp | 3 --- .../Source/GemCatalog/GemCatalogHeaderWidget.cpp | 2 -- 2 files changed, 5 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/DownloadController.cpp b/Code/Tools/ProjectManager/Source/DownloadController.cpp index 3ee800bba8..224b90299c 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadController.cpp @@ -14,9 +14,6 @@ #include - - - namespace O3DE::ProjectManager { DownloadController::DownloadController(QWidget* parent) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 875d5ada8c..e62499d963 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -193,8 +193,6 @@ namespace O3DE::ProjectManager downloadingItemLayout->setAlignment(Qt::AlignTop); downloadingItemWidget->setLayout(downloadingItemLayout); - m_downloadController->AddGemDownload("TestGem"); - auto update = [=](int downloadProgress) { if (m_downloadController->IsDownloadQueueEmpty()) From 26fdd5a553d9af2856380ae0cfe8bf0fece39101 Mon Sep 17 00:00:00 2001 From: evanchia Date: Thu, 21 Oct 2021 16:13:20 -0700 Subject: [PATCH 119/152] adding the --no-tests=error ctest arg to linux and windows test jobs Signed-off-by: evanchia --- scripts/build/Platform/Linux/build_config.json | 10 +++++----- scripts/build/Platform/Windows/build_config.json | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index ee4c27b0a9..b76a950beb 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -83,7 +83,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest", + "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest --no-tests=error", "TEST_RESULTS": "True" } }, @@ -96,7 +96,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest", + "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest --no-tests=error", "TEST_RESULTS": "True" } }, @@ -145,7 +145,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", - "CTEST_OPTIONS": "-L (SUITE_periodic)", + "CTEST_OPTIONS": "-L (SUITE_periodic) --no-tests=error", "TEST_RESULTS": "True" } }, @@ -165,7 +165,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-L (SUITE_sandbox)" + "CTEST_OPTIONS": "-L (SUITE_sandbox) --no-tests=error" } }, "benchmark_test_profile": { @@ -181,7 +181,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", - "CTEST_OPTIONS": "-L (SUITE_benchmark)", + "CTEST_OPTIONS": "-L (SUITE_benchmark) --no-tests=error", "TEST_RESULTS": "True" } }, diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 7c39661273..de83294639 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -117,7 +117,7 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", - "CTEST_OPTIONS": "-L \"(SUITE_smoke|SUITE_main)\" -LE \"(REQUIRES_gpu)\" -T Test", + "CTEST_OPTIONS": "-L \"(SUITE_smoke|SUITE_main)\" -LE \"(REQUIRES_gpu)\" -T Test --no-tests=error", "TEST_METRICS": "True", "TEST_RESULTS": "True" } @@ -166,7 +166,7 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", - "CTEST_OPTIONS": "-L \"(SUITE_smoke|SUITE_main)\" -LE \"(REQUIRES_gpu)\" -T Test", + "CTEST_OPTIONS": "-L \"(SUITE_smoke|SUITE_main)\" -LE \"(REQUIRES_gpu)\" -T Test --no-tests=error", "TEST_METRICS": "True", "TEST_RESULTS": "True" } @@ -187,7 +187,7 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", - "CTEST_OPTIONS": "-L \"(SUITE_smoke_REQUIRES_gpu|SUITE_main_REQUIRES_gpu)\" -T Test", + "CTEST_OPTIONS": "-L \"(SUITE_smoke_REQUIRES_gpu|SUITE_main_REQUIRES_gpu)\" -T Test --no-tests=error", "TEST_METRICS": "True", "TEST_RESULTS": "True", "TEST_SCREENSHOTS": "True" @@ -238,7 +238,7 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_awsi", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", - "CTEST_OPTIONS": "-L \"(SUITE_awsi)\" -T Test", + "CTEST_OPTIONS": "-L \"(SUITE_awsi)\" -T Test --no-tests=error", "TEST_METRICS": "True", "TEST_RESULTS": "True" } @@ -257,7 +257,7 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", - "CTEST_OPTIONS": "-L \"(SUITE_periodic)\" -T Test", + "CTEST_OPTIONS": "-L \"(SUITE_periodic)\" -T Test --no-tests=error", "TEST_METRICS": "True", "TEST_RESULTS": "True" } @@ -279,7 +279,7 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_sandbox", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", - "CTEST_OPTIONS": "-L \"(SUITE_sandbox)\" -T Test", + "CTEST_OPTIONS": "-L \"(SUITE_sandbox)\" -T Test --no-tests=error", "TEST_METRICS": "True", "TEST_RESULTS": "True" } @@ -298,7 +298,7 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", - "CTEST_OPTIONS": "-L \"(SUITE_benchmark)\" -T Test", + "CTEST_OPTIONS": "-L \"(SUITE_benchmark)\" -T Test --no-tests=error", "TEST_METRICS": "True", "TEST_RESULTS": "True" } From 214a2899ad195a3316091c65c0125f540a4e0fa9 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 21 Oct 2021 16:41:01 -0700 Subject: [PATCH 120/152] oops =( Signed-off-by: kberg-amzn --- Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp | 3 --- Gems/Multiplayer/Code/Tests/NetworkInputTests.cpp | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index 248c97a60f..12a49a9ffc 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -276,9 +276,6 @@ namespace Multiplayer m_child->m_entity->FindComponent()->GetHierarchicalRoot(), nullptr ); - AZStd::size_t test; - AZStd::vector test2; - test2.erase } TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Updated_Event_On_Child_Detached_On_Server) diff --git a/Gems/Multiplayer/Code/Tests/NetworkInputTests.cpp b/Gems/Multiplayer/Code/Tests/NetworkInputTests.cpp index 64381bf195..0df0fce610 100644 --- a/Gems/Multiplayer/Code/Tests/NetworkInputTests.cpp +++ b/Gems/Multiplayer/Code/Tests/NetworkInputTests.cpp @@ -17,9 +17,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include namespace Multiplayer { From 7b779619621a78baeca3afe2b4c41f6915fe7932 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 21 Oct 2021 16:52:11 -0700 Subject: [PATCH 121/152] removing unused local variable causing a compiler warning Signed-off-by: kberg-amzn --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index c2f10684a3..f628119aef 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -1148,9 +1148,6 @@ namespace Multiplayer void MultiplayerSystemComponent::EnableAutonomousControl(NetworkEntityHandle entityHandle, AzNetworking::ConnectionId connectionId) { - INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); - AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); - entityHandle.GetNetBindComponent()->SetOwningConnectionId(connectionId); if (connectionId == InvalidConnectionId) { From a4321baecc8b28dcec9a73eb3eb32480249b8af3 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 21 Oct 2021 17:00:06 -0700 Subject: [PATCH 122/152] Hopefully fix a Jenkins build failure about "function cannot access 'AZ::RPI::MaterialVersionUpdate::RenamePropertyAction::m_fromPropertyId'" Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../RPI.Reflect/Material/MaterialVersionUpdate.h | 2 ++ .../Material/MaterialVersionUpdate.cpp | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h index 0acd1d4432..eb71ad9cb7 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h @@ -32,6 +32,8 @@ namespace AZ struct RenamePropertyAction { AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate::RenameAction, "{A1FBEB19-EA05-40F0-9700-57D048DF572B}"); + + static void Reflect(ReflectContext* context); AZ::Name m_fromPropertyId; AZ::Name m_toPropertyId; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp index bf503f3c67..b387e502e2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp @@ -14,16 +14,24 @@ namespace AZ { namespace RPI { - void MaterialVersionUpdate::Reflect(ReflectContext* context) + void MaterialVersionUpdate::RenamePropertyAction::Reflect(ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { - serializeContext->Class() + serializeContext->Class() ->Version(1) - ->Field("From", &MaterialVersionUpdate::RenamePropertyAction::m_fromPropertyId) - ->Field("To", &MaterialVersionUpdate::RenamePropertyAction::m_toPropertyId) + ->Field("From", &RenamePropertyAction::m_fromPropertyId) + ->Field("To", &RenamePropertyAction::m_toPropertyId) ; + } + } + void MaterialVersionUpdate::Reflect(ReflectContext* context) + { + MaterialVersionUpdate::RenamePropertyAction::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { serializeContext->RegisterGenericType(); serializeContext->Class() From b779f358d0a0b9e070eccb6d2b948bc639ec5600 Mon Sep 17 00:00:00 2001 From: Jonny Galloway Date: Thu, 21 Oct 2021 19:03:49 -0500 Subject: [PATCH 123/152] Refactor DCCsi to work better with O3DE changes (#4226) * moving files in refactor/re-org Signed-off-by: Jonny Gallowy * dev env refactor/re-org Signed-off-by: Jonny Gallowy * moving/re-org so we can deprate the SDK nomiclature Signed-off-by: Jonny Gallowy * moving/re-org so we can deprate the SDK nomiclature Signed-off-by: Jonny Gallowy * Refactoring env so I can replace SDK with Tools folder Signed-off-by: Jonny Gallowy * re-org and prune, from SKD to Tools Signed-off-by: Jonny Gallowy * Removing old .bat files from SDK Signed-off-by: Jonny Gallowy * updated how to retreive PYTHONHOME Signed-off-by: Jonny Gallowy * Can't use/set PYTHONHOME with DCC Tools Signed-off-by: Jonny Gallowy * small refactor Signed-off-by: Jonny Gallowy * repaired a missed typo (causes error) Signed-off-by: Jonny Gallowy * Added missing EntityId.h include to FocusModeInterface.h Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * refactor additional LY to O3DE tags Signed-off-by: Jonny Gallowy * Some design scaffold file stubs Signed-off-by: Jonny Gallowy * stubbing in scaffold file pattern Signed-off-by: Jonny Gallowy * Removal of corrupted prefabs Signed-off-by: Benjamin Black * refactored config_utils to maintain py2.7 support for maya 2020 Signed-off-by: Jonny Gallowy * refactored config_utils to maintain py2.7 support for maya 2020 Signed-off-by: Jonny Gallowy * pruning and cleanup Signed-off-by: Jonny Gallowy * .env is for local dev overrides Signed-off-by: Jonny Gallowy * .env is for local dev overrides Signed-off-by: Jonny Gallowy * clean up init and logging patterns Signed-off-by: Jonny Gallowy * Core refactoring, config.py functional standalone Signed-off-by: Jonny Gallowy * cleaned up some bad logging, fixed up boostrap Signed-off-by: Jonny Gallowy * fixed validation errors Signed-off-by: Jonny Gallowy * fix logging issue, tiddy up Signed-off-by: Jonny Gallowy * tiddy up maya for .bat bootstraping and launch Signed-off-by: Jonny Gallowy * name stub, fix validation Signed-off-by: Jonny Gallowy Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Co-authored-by: Benjamin Black --- .../{.env => .env.example} | 28 +- .../DccScriptingInterface/.gitignore | 4 +- .../DccScriptingInterface/.p4ignore | 1 + .../3rdParty/Python/README.txt | 36 + .../Editor/Scripts/bootstrap.py | 180 ++- .../DccScriptingInterface/README.txt | 87 ++ .../SDK/Atom/Scripts/Python/minspect.py | 132 --- .../Python/kitbash_converter/launcher.bat | 85 -- .../Scripts/Python/kitbash_converter/main.py | 4 +- .../legacy_asset_converter/Launch_Cmd.bat | 45 - .../Launch_Maya_2020.bat | 68 -- .../Launch_WingIDE-7-1.bat | 81 -- .../legacy_asset_converter/Project_Env.bat | 72 -- .../Python/legacy_asset_converter/main.py | 4 +- .../Python/maya_dcc_materials/minspect.py | 135 --- .../stingrayPBS_converter.py | 2 +- .../stingrayPBS_converter_maya.py | 2 +- .../SDK/Maya/Scripts/set_callbacks.py | 2 +- .../SDK/Maya/Scripts/set_defaults.py | 2 +- .../SDK/Maya/Scripts/set_menu.py | 2 +- .../SDK/Maya/Scripts/userSetup.py | 30 +- .../blender_materials.py | 85 -- .../DCC_Material_Converter/cli_control.py | 52 - .../dcc_material_mapping.py | 65 -- .../DCC_Material_Converter/drag_and_drop.py | 65 -- .../DCC_Material_Converter/launcher.bat | 85 -- .../DCC_Material_Converter/main.py | 1026 ----------------- .../DCC_Material_Converter/maya_materials.py | 169 --- .../DCC_Material_Converter/model.py | 154 --- .../DCC_Material_Converter/standalone.py | 37 - .../SDK/PythonTools/Launcher/main.py | 74 -- .../SDK/Substance/builder/atom_material.py | 2 +- .../SDK/Substance/builder/bootstrap.py | 28 +- .../SDK/Substance/builder/sb_gui_main.py | 46 +- .../SDK/Substance/builder/sbs_to_sbsar.py | 6 +- .../SDK/Substance/builder/sbsar_info.py | 8 +- .../SDK/Substance/builder/sbsar_render.py | 6 +- .../SDK/Substance/builder/sbsar_utils.py | 8 +- .../SDK/Substance/builder/substance_tools.py | 6 +- .../Substance/builder/watchdog/__init__.py | 8 +- .../Tools/DCC/3dsMax/stub | 9 + .../AddOns/MaterialExporter/_init__.py | 9 + .../Blender/AddOns/MaterialExporter/main.py | 21 + .../Tools/DCC/Blender/config.py | 11 + .../stub => Tools/DCC/Blender/settings.json} | 0 .../Tools/DCC/Blender/start.py | 11 + .../Tools/DCC/Houdini/stub | 9 + .../Tools/DCC/Marmoset/stub | 9 + .../2020/Prefs/icons/MayaStartupImage.png | 3 + .../Tools/DCC/Maya/2020/plugins/stub | 0 .../Tools/DCC/Maya/2020/scripts/stub | 0 .../Tools/DCC/Maya/2020/siteDir/stub | 0 .../Tools/DCC/Maya/Help/HelpStub | 0 .../DCC/Maya/Projects/default/workspace.mel | 36 + .../Maya/Resources/PBR/IBLbaker_brdf_lut.dds | 3 + .../artist_workshop_4k_lighting_diffuse.dds | 3 + .../artist_workshop_4k_lighting_specular.dds | 3 + .../ly_cubempas/artist_workshop_4k_skybox.dds | 3 + .../SourceImages/MayaStartupImage.psd | 3 + .../DCC/Maya/Resources/SourceImages/stub | 0 .../Tools/DCC/Maya/Resources/workspace.mel | 69 ++ .../Tools/DCC/Maya/Scripts/Mel/stub | 0 .../Tools/DCC/Maya/Scripts/Python/stub | 9 + .../DCC/Maya/Scripts/Python/stub_util.py} | 16 +- .../Tools/DCC/Maya/Scripts/constants.py | 32 + .../Tools/DCC/Maya/Scripts/set_callbacks.py | 201 ++++ .../Tools/DCC/Maya/Scripts/set_defaults.py | 93 ++ .../Tools/DCC/Maya/Scripts/set_menu.py | 88 ++ .../Tools/DCC/Maya/Scripts/set_shelf.py | 130 +++ .../Tools/DCC/Maya/Scripts/userSetup.py | 325 ++++++ .../Tools/DCC/Maya/Shaders/stub | 0 .../Tools/DCC/Maya/Tools/stub | 9 + .../Tools/DCC/Maya/config.py | 23 + .../Tools/DCC/Maya/constants.py | 16 + .../Tools/DCC/Maya/readme.txt | 78 ++ .../Tools/DCC/Maya/requirements.txt | 84 ++ .../Tools/DCC/Maya/settings.json | 0 .../Tools/DCC/Maya/start.py | 24 + .../Substance/resources/custom_shaders/stub | 0 .../DCC/Substance/resources/ly_presets/stub | 0 .../Tools/DCC/Substance/resources/stub | 0 .../DCC/Substance/resources/templates/stub | 0 .../Tools/DCC/Substance/scripts/stub | 9 + .../Tools/DCC/Substance/stub | 9 + .../Dev}/Windows/.gitignore | 0 .../Dev}/Windows/Env_Core.bat | 54 +- .../Dev}/Windows/Env_Maya.bat | 24 +- .../Dev}/Windows/Env_PyCharm.bat | 2 +- .../Dev}/Windows/Env_Python.bat | 27 +- .../Dev}/Windows/Env_Qt.bat | 10 +- .../Dev}/Windows/Env_Substance.bat | 4 +- .../Dev}/Windows/Env_VScode.bat | 0 .../Dev}/Windows/Env_WingIDE.bat | 13 +- .../Dev}/Windows/Launch_Env_Cmd.bat | 2 +- .../Dev}/Windows/Launch_MayaPy_PyCharmPro.bat | 10 +- .../Dev}/Windows/Launch_Maya_2020.bat | 2 +- .../Dev}/Windows/Launch_PyCharmPro.bat | 0 .../Dev}/Windows/Launch_PyMin_Cmd.bat | 2 +- .../Dev}/Windows/Launch_Qt_PyMin_Cmd.bat | 4 +- .../Dev}/Windows/Launch_VScode.bat | 8 +- .../Dev}/Windows/Launch_WingIDE-7-1.bat | 28 +- .../Dev}/Windows/Launch_mayaPy_2020.bat | 2 +- .../Windows/Launch_mayapy_WingIDE-7-1.bat | 12 +- .../Dev}/Windows/Launch_pyBASE.bat | 0 .../Dev}/Windows/Launch_pyBASE_Cmd.bat | 2 +- .../Tools/Dev/Windows/README.txt | 78 ++ .../Dev}/Windows/Setuo_copy_oiio.bat | 6 +- .../Dev/Windows}/Solutions/.dev/readme.txt | 0 .../Tools/Dev/Windows/Solutions/.dev/stub | 0 .../Dev/Windows}/Solutions/.gitignore | 0 .../Dev/Windows}/Solutions/.idea/.gitignore | 0 .../Dev/Windows}/Solutions/.idea/.p4ignore | 0 .../Solutions/.idea/DccScriptingInterface.iml | 0 .../Windows}/Solutions/.idea/encodings.xml | 0 .../Dev/Windows}/Solutions/.idea/main.py | 0 .../Dev/Windows}/Solutions/.idea/misc.xml | 0 .../Dev/Windows}/Solutions/.idea/modules.xml | 0 .../Dev/Windows}/Solutions/.idea/vcs.xml | 0 .../Windows}/Solutions/.idea/webResources.xml | 0 .../Solutions/.vscode/dccsi.code-workspace | 0 .../Windows}/Solutions/.vscode/launch.json | 0 .../Windows}/Solutions/.vscode/settings.json | 0 .../Dev/Windows}/Solutions/.wing/.gitignore | 0 .../Dev/Windows}/Solutions/.wing/DCCsi_7x.wpr | 75 +- .../Dev/Windows}/Solutions/readme.txt | 0 .../Tools/Python/MockTool/README.txt | 9 + .../Python/MockTool/Resources/resource.txt | 9 + .../Tools/Python/MockTool/config.py | 12 + .../Tools/Python/MockTool/main.py | 12 + .../Tools/Python/MockTool/settings.json | 17 + .../Tests/OpenImageIO/AA_Gun_01_ddna.tif | 3 + .../Python/Tests/OpenImageIO/test_oiio.py | 66 ++ .../DccScriptingInterface/Tools/Python/stub | 0 .../Atom/StandardPBR_AllProperties.material | 47 + .../Tools/start_service.py | 21 + .../DccScriptingInterface/azpy/__init__.py | 114 +- .../azpy/config_utils.py | 142 ++- .../DccScriptingInterface/azpy/constants.py | 158 +-- .../azpy/core/__init__.py | 42 + .../azpy/core/py2/__init__.py | 20 + .../azpy/core/py2/utils.py | 59 + .../azpy/core/py3/__init__.py | 20 + .../azpy/core/py3/utils.py | 52 + .../azpy/{ => dcc}/3dsmax/__init__.py | 59 +- .../azpy/dcc/__init__.py | 53 + .../azpy/{ => dcc}/blender/__init__.py | 57 +- .../azpy/{ => dcc}/houdini/__init__.py | 31 +- .../azpy/{ => dcc}/marmoset/__init__.py | 31 +- .../azpy/{ => dcc}/maya/__init__.py | 38 +- .../azpy/{ => dcc}/maya/callbacks/__init__.py | 19 +- .../maya/callbacks/event_callback_handler.py | 4 +- .../node_message_callback_handler.py | 4 +- .../maya/callbacks/on_shader_rename.py | 4 +- .../azpy/{ => dcc}/maya/helpers/__init__.py | 23 +- .../{ => dcc}/maya/helpers/undo_context.py | 4 +- .../azpy/{ => dcc}/maya/helpers/utils.py | 8 +- .../azpy/dcc/maya/toolbits/__init__.py | 38 + .../azpy/{ => dcc}/maya/toolbits/detach.py | 8 +- .../azpy/{ => dcc}/maya/utils/__init__.py | 0 .../{ => dcc}/maya/utils/execute_wing_code.py | 8 +- .../maya/utils/simple_command_port.py | 4 +- .../azpy/{ => dcc}/maya/utils/wing_to_maya.py | 2 +- .../azpy/{lumberyard => dcc/o3de}/__init__.py | 55 +- .../{render => dcc/o3de/atom}/__init__.py | 32 +- .../azpy/{ => dcc}/substance/__init__.py | 56 +- .../azpy/dev/ide/__init__.py | 2 +- .../azpy/dev/ide/wing/hot_keys.py | 8 +- .../azpy/dev/ide/wing/test.py | 2 +- .../azpy/dev/utils/check/maya_app.py | 46 +- .../azpy/dev/utils/check/running_state.py | 64 +- .../DccScriptingInterface/azpy/env_base.py | 22 +- .../azpy/maya/toolbits/__init__.py | 37 - .../DccScriptingInterface/azpy/return_stub.py | 4 +- .../azpy/shared/__init__.py | 18 +- .../azpy/shared/boxDumpTest.json | 17 - .../azpy/shared/common/__init__.py | 18 +- .../azpy/shared/common/core_utils.py | 34 +- .../azpy/shared/common/envar_utils.py | 47 +- .../azpy/shared/noodely/__init__.py | 24 +- .../azpy/shared/noodely/find_arg.py | 4 +- .../azpy/shared/noodely/node.py | 8 +- .../azpy/shared/noodely/pathnode.py | 6 +- .../azpy/shared/ui/__init__.py | 20 +- .../azpy/shared/ui/base_widget.py | 2 +- .../azpy/shared/ui/custom_treemodel.py | 4 +- .../azpy/shared/ui/help_menu.py | 2 +- .../shared/ui/pyside2_qtextedit_stdout.py | 8 +- .../azpy/shared/ui/pyside2_ui_utils.py | 4 +- .../azpy/shared/ui/qt_settings.py | 4 +- .../azpy/shared/ui/templates.py | 2 +- .../azpy/synthetic_env.py | 150 ++- .../azpy/test/__init__.py | 52 +- .../azpy/test/entry_test.py | 46 +- .../DccScriptingInterface/config.py | 711 +++++++++--- .../DccScriptingInterface/settings.json | 14 +- python/get_python_path.bat | 33 + python/get_python_path.py | 16 + 197 files changed, 3779 insertions(+), 3636 deletions(-) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{.env => .env.example} (63%) create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/README.txt create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/README.txt delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Atom/Scripts/Python/minspect.py delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/kitbash_converter/launcher.bat delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_Cmd.bat delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_Maya_2020.bat delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_WingIDE-7-1.bat delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Project_Env.bat delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/maya_dcc_materials/minspect.py delete mode 100755 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/blender_materials.py delete mode 100755 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/cli_control.py delete mode 100755 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/dcc_material_mapping.py delete mode 100755 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/drag_and_drop.py delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/launcher.bat delete mode 100755 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/main.py delete mode 100755 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/maya_materials.py delete mode 100755 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/model.py delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/standalone.py delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/Launcher/main.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/3dsMax/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/AddOns/MaterialExporter/_init__.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/AddOns/MaterialExporter/main.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/config.py rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Solutions/.dev/stub => Tools/DCC/Blender/settings.json} (100%) create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/start.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Houdini/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Marmoset/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/Prefs/icons/MayaStartupImage.png create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/plugins/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/scripts/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/siteDir/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Help/HelpStub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Projects/default/workspace.mel create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/IBLbaker_brdf_lut.dds create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_lighting_diffuse.dds create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_lighting_specular.dds create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_skybox.dds create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/SourceImages/MayaStartupImage.psd create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/SourceImages/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/workspace.mel create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Mel/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Python/stub rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{SDK/PythonTools/DCC_Material_Converter/max_materials.py => Tools/DCC/Maya/Scripts/Python/stub_util.py} (51%) mode change 100755 => 100644 create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/constants.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_callbacks.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_defaults.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_menu.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_shelf.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/userSetup.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Shaders/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Tools/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/config.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/constants.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/readme.txt create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/requirements.txt create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/settings.json create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/start.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/custom_shaders/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/ly_presets/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/templates/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/scripts/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/stub rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/.gitignore (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Env_Core.bat (67%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Env_Maya.bat (87%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Env_PyCharm.bat (95%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Env_Python.bat (76%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Env_Qt.bat (85%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Env_Substance.bat (92%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Env_VScode.bat (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Env_WingIDE.bat (78%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_Env_Cmd.bat (97%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_MayaPy_PyCharmPro.bat (91%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_Maya_2020.bat (98%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_PyCharmPro.bat (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_PyMin_Cmd.bat (97%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_Qt_PyMin_Cmd.bat (93%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_VScode.bat (92%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_WingIDE-7-1.bat (74%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_mayaPy_2020.bat (98%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_mayapy_WingIDE-7-1.bat (87%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_pyBASE.bat (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Launch_pyBASE_Cmd.bat (97%) create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/README.txt rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{Launchers => Tools/Dev}/Windows/Setuo_copy_oiio.bat (76%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.dev/readme.txt (100%) create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.dev/stub rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.gitignore (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/.gitignore (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/.p4ignore (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/DccScriptingInterface.iml (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/encodings.xml (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/main.py (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/misc.xml (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/modules.xml (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/vcs.xml (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.idea/webResources.xml (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.vscode/dccsi.code-workspace (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.vscode/launch.json (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.vscode/settings.json (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.wing/.gitignore (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/.wing/DCCsi_7x.wpr (57%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/{ => Tools/Dev/Windows}/Solutions/readme.txt (100%) create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/README.txt create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/Resources/resource.txt create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/config.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/main.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/settings.json create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/Tests/OpenImageIO/AA_Gun_01_ddna.tif create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/Tests/OpenImageIO/test_oiio.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/stub create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Resources/Atom/StandardPBR_AllProperties.material create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/start_service.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/__init__.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py2/__init__.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py2/utils.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py3/__init__.py create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py3/utils.py rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/3dsmax/__init__.py (71%) mode change 100755 => 100644 create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/__init__.py rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/blender/__init__.py (71%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/houdini/__init__.py (70%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/marmoset/__init__.py (70%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/__init__.py (64%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/callbacks/__init__.py (64%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/callbacks/event_callback_handler.py (98%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/callbacks/node_message_callback_handler.py (98%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/callbacks/on_shader_rename.py (98%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/helpers/__init__.py (55%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/helpers/undo_context.py (97%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/helpers/utils.py (98%) mode change 100755 => 100644 create mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/toolbits/__init__.py rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/toolbits/detach.py (94%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/utils/__init__.py (100%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/utils/execute_wing_code.py (95%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/utils/simple_command_port.py (98%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/maya/utils/wing_to_maya.py (99%) rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{lumberyard => dcc/o3de}/__init__.py (68%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{render => dcc/o3de/atom}/__init__.py (72%) mode change 100755 => 100644 rename Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/{ => dcc}/substance/__init__.py (71%) mode change 100755 => 100644 delete mode 100755 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/toolbits/__init__.py delete mode 100644 Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/boxDumpTest.json create mode 100644 python/get_python_path.bat create mode 100644 python/get_python_path.py diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.env b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.env.example similarity index 63% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.env rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.env.example index 080b4e92f7..29c1739992 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.env +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.env.example @@ -4,18 +4,18 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # -# -- This line is 75 characters ------------------------------------------- +# ------------------------------------------------------------------------- # Sets up the project environment for python scripting using the export DYNACONF_COMPANY=Amazon -# if a lumberyard project isn't set use this gem -export DYNACONF_LY_PROJECT=DccScriptingInterface -export DYNACONF_LY_PROJECT_PATH=`pwd` -export DYNACONF_LY_DEV=${LY_PROJECT_PATH}\..\..\..\.. +# if a O3DE project isn't set use this gem +export DYNACONF_O3DE_PROJECT=DccScriptingInterface +export DYNACONF_O3DE_PROJECT_PATH=`pwd` +export DYNACONF_O3DE_DEV=${O3DE_PROJECT_PATH}\..\..\..\.. # LY build folder -export DYNACONF_LY_BUILD_PATH=${LY_DEV}\build -export DYNACONF_LY_BIN_PATH=${LY_BUILD_PATH}\bin\profile +export DYNACONF_O3DE_BUILD_PATH=${O3DE_DEV}\build +export DYNACONF_O3DE_BIN_PATH=${O3DE_BUILD_PATH}\bin\profile # default IDE and debug settings #export DYNACONF_DCCSI_GDEBUG=false @@ -24,9 +24,9 @@ export DYNACONF_DCCSI_GDEBUGGER=WING export DYNACONF_DCCSI_LOGLEVEL=20 # defaults for DccScriptingInterface (DCCsi) -export DYNACONF_DCCSIG_PATH=${LY_DEV}\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface +export DYNACONF_DCCSIG_PATH=${O3DE_DEV}\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface -# set up default python interpreter (Lumberyard) +# set up default python interpreter (O3DE) # we may want to entirely remove these and rely on config.py to dynamically set up # however VScode can be configured with a .env so might be valueable to keep export DYNACONF_DCCSI_PY_VERSION_MAJOR=3 @@ -40,13 +40,13 @@ export DYNACONF_DCCSI_PYTHON_PATH=${DCCSIG_PATH}\3rdParty\Python export DYNACONF_DCCSI_PYTHON_LIB_PATH=${DCCSI_PYTHON_PATH}\Lib\${DCCSI_PY_VERSION_MAJOR}.x\${DCCSI_PY_VERSION_MAJOR}.${DCCSI_PY_VERSION_MINOR}.x\site-packages # TO DO: figure out how to best deal with OS folder (i.e. 'windows') -export DYNACONF_DCCSI_PYTHON_INSTALL=${LY_DEV}\python -export DYNACONF_DDCCSI_PY_BASE=${DCCSI_PYTHON_INSTALL}\python.cmd +export DYNACONF_O3DE_PYTHON_INSTALL=${O3DE_DEV}\python +export DYNACONF_DCCSI_PY_BASE=${O3DE_PYTHON_INSTALL}\python.cmd # set up Qt / PySide2 # TO DO: These should NOT be set in the global env as they will cause conflicts # with other Qt apps (like DCC tools), only set in local.env, or modify config.py # for utils/tools/apps that need them ( see config.init_ly_pyside() ) -#export DYNACONF_QTFORPYTHON_PATH=${LY_DEV}\Gems\QtForPython\3rdParty\pyside2\windows\release -#export DYNACONF_QT_PLUGIN_PATH=${LY_BUILD_PATH}\bin\profile\EditorPlugins -#export DYNACONF_QT_QPA_PLATFORM_PLUGIN_PATH=${LY_BUILD_PATH}\bin\profile\EditorPlugins\platforms +#export DYNACONF_QTFORPYTHON_PATH=${O3DE_DEV}\Gems\QtForPython\3rdParty\pyside2\windows\release +#export DYNACONF_QT_PLUGIN_PATH=${O3DE_BUILD_PATH}\bin\profile\EditorPlugins +#export DYNACONF_QT_QPA_PLATFORM_PLUGIN_PATH=${O3DE_BUILD_PATH}\bin\profile\EditorPlugins\platforms diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.gitignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.gitignore index 9ad4717873..086d189eba 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.gitignore +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.gitignore @@ -8,4 +8,6 @@ workspace.xml # Ignore dynaconf secret files .secrets.* settings.local.json -azpy/_sample_package_/* \ No newline at end of file +azpy/_sample_package_/* +.env +settings_export.json.tmp \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.p4ignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.p4ignore index dedde06bc4..2ff9b6b614 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.p4ignore +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.p4ignore @@ -19,3 +19,4 @@ __WIP__/* !.gitignore .secrets.* settings.local.json +.env \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/README.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/README.txt new file mode 100644 index 0000000000..7f5a72fb1c --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/README.txt @@ -0,0 +1,36 @@ +DccScriptingInterface (DCCsi) + +This location can be extended with additional 3rdParty Python Utils, Tools, Packages, etc. + +These are not installed or distributed with O3DE + +However, there is some stubbed scaffolding in place. + +This is a bootstrapped sandbox for installing python libs (version bootstrapped procedurally): +C:\Depot\o3de-engine\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\3rdParty\Python\Lib\2.x\2.7.x\site-packages +C:\Depot\o3de-engine\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\3rdParty\Python\Lib\3.x\3.7.x\site-packages + +Any libs installed to this location will be accessible (use at your own risk) + +For instance, if you want to add py2.7 compatible libs, for apps like Maya2020: +"C:\Depot\o3de-engine\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Tools\DCC\Maya\readme.txt" + +These pyside2-tools can be useful, and they are not pip installed, nor distributed. +C:\Depot\o3de-engine\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\3rdParty\Python\pyside2-tools + +pyside2-tools instructions: + +1. clone the repo in this location: C:\Depot\o3de-engine\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\3rdParty\Python + >git clone https://github.com/pyside/pyside2-tools + +2. to use as a python package ... + find this and copy: + "< local DCCsi >\3rdParty\Python\pyside2-tools\pyside2uic\__init__.py.in" + + and rename to this: + "< local DCCsi >\3rdParty\Python\pyside2-tools\pyside2uic\__init__.py" + +3. add to PYTHONPATH: < local DCCsi >\3rdParty\Python + in .py something like: site.addsitedir(DCCSI_PYSIDE2_TOOLS) + +See: "< local DCCsi >\config.py" \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py index d417c572e9..406e2b04ff 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py @@ -7,9 +7,9 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # -# -- This line is 75 characters ------------------------------------------- +# ------------------------------------------------------------------------- """This module is for use in boostrapping the DccScriptingInterface Gem -with Lumberyard. Note: this boostrap is only designed fo be py3 compatible. +with O3DE. Note: this boostrap is only designed fo be py3 compatible. If you need DCCsi access in py27 (Autodesk Maya for instance) you may need to implement your own boostrapper module. Currently this is boostrapped from add_dccsi.py, as a temporty measure related to this Jira: @@ -24,40 +24,64 @@ import logging as _logging # ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- +_O3DE_RUNNING=None +try: + import azlmbr + _O3DE_RUNNING=True +except: + _O3DE_RUNNING=False +# ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- # we don't use dynaconf setting here as we might not yet have access # to that site-dir. -_MODULE = 'DCCsi.bootstrap' +_MODULENAME = __name__ +if _MODULENAME is '__main__': + _MODULENAME = 'O3DE.DCCsi.bootstrap' + +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_MODULENAME) # we need to set up basic access to the DCCsi _MODULE_PATH = os.path.realpath(__file__) # To Do: what if frozen? -_DCCSIG_PATH = os.path.normpath(os.path.join(_MODULE_PATH, '../../..')) -_DCCSIG_PATH = os.getenv('DCCSIG_PATH', _DCCSIG_PATH) -site.addsitedir(_DCCSIG_PATH) +_DCCSI_PATH = os.path.normpath(os.path.join(_MODULE_PATH, '../../..')) +_DCCSI_PATH = os.getenv('DCCSI_PATH', _DCCSI_PATH) +site.addsitedir(_DCCSI_PATH) -# we can get basic access to the DCCsi.azpy api now -import azpy +# now we have azpy api access +from azpy.env_bool import env_bool +from azpy.constants import ENVAR_DCCSI_GDEBUG +from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import ENVAR_DCCSI_LOGLEVEL +from azpy.constants import FRMT_LOG_LONG -# early attach WingIDE debugger (can refactor to include other IDEs later) -while 0: # flag on to attemp to connect wingIDE debugger - from azpy.env_bool import env_bool - if not env_bool('DCCSI_DEBUGGER_ATTACHED', False): - # if not already attached lets do it here - from azpy.test.entry_test import connect_wing - foo = connect_wing() +# set up global space, logging etc. +# set these true if you want them set globally for debugging +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_LOGLEVEL = int(env_bool(ENVAR_DCCSI_LOGLEVEL, int(20))) +if _DCCSI_GDEBUG: + _DCCSI_LOGLEVEL = int(10) + +_logging.basicConfig(format=FRMT_LOG_LONG, level=_DCCSI_LOGLEVEL) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -# settings.setenv() # doing this will add the additional DYNACONF_ envars -def get_dccsi_config(DCCSIG_PATH=_DCCSIG_PATH): +# _settings.setenv() # doing this will add the additional DYNACONF_ envars +def get_dccsi_config(DCCSI_PATH=_DCCSI_PATH): """Convenience method to set and retreive settings directly from module.""" # we can go ahead and just make sure the the DCCsi env is set - # config is SO generic this ensures we are importing a specific one - _spec_dccsi_config = importlib.util.spec_from_file_location("dccsi.config", - Path(DCCSIG_PATH, + # _config is SO generic this ensures we are importing a specific one + _spec_dccsi_config = importlib.util.spec_from_file_location("dccsi._config", + Path(DCCSI_PATH, "config.py")) _dccsi_config = importlib.util.module_from_spec(_spec_dccsi_config) _spec_dccsi_config.loader.exec_module(_dccsi_config) @@ -65,9 +89,12 @@ def get_dccsi_config(DCCSIG_PATH=_DCCSIG_PATH): return _dccsi_config # ------------------------------------------------------------------------- -# set and retreive the base settings on import -config = get_dccsi_config() -settings = config.get_config_settings() +# set and retreive the base env context/_settings on import +_config = get_dccsi_config() +_settings = _config.get_config_settings() + +if _DCCSI_DEV_MODE: + _config.attach_debugger() # attempts to start debugger # done with basic setup # --- END ----------------------------------------------------------------- @@ -77,50 +104,99 @@ settings = config.get_config_settings() # ------------------------------------------------------------------------- if __name__ == '__main__': """Run this file as main""" + + # ------------------------------------------------------------------------- + _O3DE_RUNNING=None + try: + import azlmbr + _O3DE_RUNNING=True + except: + _O3DE_RUNNING=False + # ------------------------------------------------------------------------- - _G_DEBUG = False - _G_TEST_PYSIDE = False + _MODULENAME = __name__ + if _MODULENAME is '__main__': + _MODULENAME = 'O3DE.DCCsi.bootstrap' + + from azpy.constants import STR_CROSSBAR + + # module internal debugging flags + while 0: # temp internal debug flag + _DCCSI_GDEBUG = True + break + + # overide logger for standalone to be more verbose and log to file + import azpy + _LOGGER = azpy.initialize_logger(_MODULENAME, + log_to_file=_DCCSI_GDEBUG, + default_log_level=_DCCSI_LOGLEVEL) + # happy print + _LOGGER.info(STR_CROSSBAR) + _LOGGER.info('~ constants.py ... Running script as __main__') + _LOGGER.info(STR_CROSSBAR) + + # parse the command line args + import argparse + parser = argparse.ArgumentParser( + description='O3DE DCCsi Boostrap (Test)', + epilog="Will externally test the DCCsi boostrap") _config = get_dccsi_config() - _settings = config.get_config_settings() - - _log_level = int(_settings.DCCSI_LOGLEVEL) - if _G_DEBUG: - _log_level = int(10) # force debug level - _LOGGER = azpy.initialize_logger(_MODULE, - log_to_file=True, - default_log_level=_log_level) + _settings = _config.get_config_settings(enable_o3de_python=True, + enable_o3de_pyside2=True) + parser.add_argument('-gd', '--global-debug', + type=bool, + required=False, + help='Enables global debug flag.') + parser.add_argument('-dm', '--developer-mode', + type=bool, + required=False, + help='Enables dev mode for early auto attaching debugger.') + parser.add_argument('-tp', '--test-pyside2', + type=bool, + required=False, + help='Runs Qt/PySide2 tests and reports.') + args = parser.parse_args() - # we can now grab values from the DCCsi.config.py dynamic env settings - # the rest of this block is basic debug testing the dynamic settings at boot - _LOGGER.info(f'Running module: {_MODULE}') - _LOGGER.info(f'DCCSIG_PATH: {_settings.DCCSIG_PATH}') - _LOGGER.info(f'DCCSI_G_DEBUG: {_settings.DCCSI_GDEBUG}') - _LOGGER.info(f'DCCSI_DEV_MODE: {_settings.DCCSI_DEV_MODE}') - - _LOGGER.info(f'OS_FOLDER: {_settings.OS_FOLDER}') - _LOGGER.info(f'LY_PROJECT: {_settings.LY_PROJECT}') - _LOGGER.info(f'LY_PROJECT_PATH: {_settings.LY_PROJECT_PATH}') - _LOGGER.info(f'LY_DEV: {_settings.LY_DEV}') - _LOGGER.info(f'LY_BUILD_PATH: {_settings.LY_BUILD_PATH}') - _LOGGER.info(f'LY_BIN_PATH: {_settings.LY_BIN_PATH}') + # easy overrides + if args.global_debug: + _DCCSI_GDEBUG = True + if args.developer_mode: + _DCCSI_DEV_MODE = True + _config.attach_debugger() # attempts to start debugger - _LOGGER.info(f'DCCSIG_PATH: {_settings.DCCSIG_PATH}') - _LOGGER.info(f'DCCSI_PYTHON_LIB_PATH: {_settings.DCCSI_PYTHON_LIB_PATH}') - _LOGGER.info(f'DDCCSI_PY_BASE: {_settings.DDCCSI_PY_BASE}') + if _DCCSI_GDEBUG: + _LOGGER.info(f'DCCSI_PATH: {_settings.DCCSI_PATH}') + _LOGGER.info(f'DCCSI_G_DEBUG: {_settings.DCCSI_GDEBUG}') + _LOGGER.info(f'DCCSI_DEV_MODE: {_settings.DCCSI_DEV_MODE}') + + _LOGGER.info(f'DCCSI_OS_FOLDER: {_settings.DCCSI_OS_FOLDER}') + _LOGGER.info(f'O3DE_PROJECT: {_settings.O3DE_PROJECT}') + _LOGGER.info(f'O3DE_PROJECT_PATH: {_settings.O3DE_PROJECT_PATH}') + _LOGGER.info(f'O3DE_DEV: {_settings.O3DE_DEV}') + _LOGGER.info(f'O3DE_BUILD_PATH: {_settings.O3DE_BUILD_PATH}') + _LOGGER.info(f'O3DE_BIN_PATH: {_settings.O3DE_BIN_PATH}') + + _LOGGER.info(f'DCCSI_PATH: {_settings.DCCSI_PATH}') + _LOGGER.info(f'DCCSI_PYTHON_LIB_PATH: {_settings.DCCSI_PYTHON_LIB_PATH}') + _LOGGER.info(f'DCCSI_PY_BASE: {_settings.DCCSI_PY_BASE}') - if _G_TEST_PYSIDE: + if _DCCSI_GDEBUG or args.test_pyside2: try: import PySide2 except: # set up Qt/PySide2 access and test - _settings = _config.get_config_settings(setup_ly_pyside=True) + _settings = _config.get_config_settings(enable_o3de_pyside2=True) import PySide2 _LOGGER.info(f'PySide2: {PySide2}') - _LOGGER.info(f'LY_BIN_PATH: {_settings.LY_BIN_PATH}') + _LOGGER.info(f'O3DE_BIN_PATH: {_settings.O3DE_BIN_PATH}') _LOGGER.info(f'QT_PLUGIN_PATH: {_settings.QT_PLUGIN_PATH}') _LOGGER.info(f'QT_QPA_PLATFORM_PLUGIN_PATH: {_settings.QT_QPA_PLATFORM_PLUGIN_PATH}') _config.test_pyside2() + + if not _O3DE_RUNNING: + # return + sys.exit() # --- END ----------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/README.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/README.txt new file mode 100644 index 0000000000..9a3efe589a --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/README.txt @@ -0,0 +1,87 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- + +This folder contains the DccScriptingInterface (DCCsi) for O3DE + +Notice: The old \\SDK folder is being replaced with \\Tools +The scripts in \\SDK may be out of data (and not run) +When scripts are finished being updated and refactored into \\Tools the \\SDK will be removed + +What is the DCCsi? + +- A shared development environment for technical art oriented to working with Python across a number of DCC tools. +- Leverage the existing python ecosystem for technical art. +- Integrate a DCC app like Substance (or Substance SAT api) from the Python driven VFX and Games ecosystem. +- Extend O3DE and unlock its potential for content creators, and the Technical Artists that service them. + +Tenets: +(1) Interoperability: Design DCC-agnostic modules and DCC-bespoke modules + to work together efficiently and intuitively. + +(2) Encapsulation: Define a module in terms of its essential features and interface to other components, + to facilitate logical layered design and easy maintenance. + +(3) Extensibility: Design the tool set to be easily extensible with new functionality and new tools. + Individual pieces should have a generic communication mechanism to allow newly written tools to slot cleanly and transparently into the tool chain. + +What is provided (High Level): +- DCC-Agnostic Python Framework (as a modular Gem) related to multiple integrations for: + O3DE Editor (python scripting, utils and PySide2 tools) + DCC applications and their Python APIs/SDKs + Custom standalone tools and utils (python based) + external from cmd line + external standalone + integrated to run within O3DE Editor + +What is provided (by folder): + +\3rdParty: Allows third party libs/packages to be integrated outside of O3DE + Example: O3DE is py3, Maya 2020 (and earlier) is py27 + O3DE provides a patterns for Gems to provide a requirements.txt + See: + DccScriptingInterface\reqiurements.txt + ^ These packages will be fetched and installed into O3DE python at build time + + This means for some applications like Maya we need another way to add the same packages + See: + DccScriptingInterface\SDK\Maya\readme.txt + DccScriptingInterface\SDK\Maya\requirements.txt + DccScriptingInterface\3rdParty\Python\Lib\2.x\2.7.x\site-packages\* + + Packages that reside in 3rdParty are never commited to the repo (only fetched+installed) + +\Assets: All O3DE Gems can maintain an asset folder + If a Gem contains an \Asset folder, these assets are folded into the projects asset data + These assets are processed by the Asset Processor for use in the Editor and Runtime + In the DCCsi the \Assets folder primarily contains TestData + +\azpy Core (shared) API, A pure python Package and Modules + +\Code Contains the bare bones C++ scaffold to build and integrate the Gem with O3DE + Notes: portions of the DCCsi can be utilized outside of O3DE + thus this Gem doens't have to be enabled and built for some use cases + +\Editor This folder provides an entry point pattern for extending O3DE Editor with python + When a Gem is enabled ... + If the following if found, it will be executed when the Editor boots: + "Editor\Scripts\bootstrap.py" + + This can be used to initialize code access, extend the editor (PySide2), etc. + +\Tools This is where the following is maintained: + +\Tools\DCC Integration for DCC tools: + configuration of tool (managed env, etc.) + bootstrapping, such as providing the tool access to azpy api code + extensibility, such as adding new functionality or tool to the app + +\Tools\DCC\Maya An example of adding a integration for Autodesk Maya + +\Tools\Env\Windows This provides a .bat file managed env to configure and bootsrap windows apps +\Tools\Launchers\windows Provides .bat files based tool launchers for windows (accesses env) + diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Atom/Scripts/Python/minspect.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Atom/Scripts/Python/minspect.py deleted file mode 100644 index 760807042c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Atom/Scripts/Python/minspect.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -Copyright (c) Contributors to the Open 3D Engine Project. -For complete copyright and license terms please see the LICENSE at the root of this distribution. - -SPDX-License-Identifier: Apache-2.0 OR MIT -""" -# ------------------------------------------------------------------------- - -import pymel.core as pmc -import sys -import types - - -def syspath(): - print 'sys.path:' - for p in sys.path: - print ' ' + p - - -def info(obj): - """Prints information about the object.""" - - lines = ['Info for %s' % obj.name(), - 'Attributes:'] - # Get the name of all attributes - for a in obj.listAttr(): - lines.append(' ' + a.name()) - lines.append('MEL type: %s' % obj.type()) - lines.append('MRO:') - lines.extend([' ' + t.__name__ for t in type(obj).__mro__]) - result = '\n'.join(lines) - print result - - -def _is_pymel(obj): - try: # (1) - module = obj.__module__ # (2) - except AttributeError: # (3) - try: - module = obj.__name__ # (4) - except AttributeError: - return None # (5) - return module.startswith('pymel') # (6) - - -def _py_to_helpstr(obj): - if isinstance(obj, basestring): - return 'search.html?q=%s' % (obj.replace(' ', '+')) - if not _is_pymel(obj): - return None - if isinstance(obj, types.ModuleType): - return ('generated/%(module)s.html#module-%(module)s' % - dict(module=obj.__name__)) - if isinstance(obj, types.MethodType): - return ('generated/classes/%(module)s/' - '%(module)s.%(typename)s.html' - '#%(module)s.%(typename)s.%(methname)s' % dict( - module=obj.__module__, - typename=obj.im_class.__name__, - methname=obj.__name__)) - if isinstance(obj, types.FunctionType): - return ('generated/functions/%(module)s/' - '%(module)s.%(funcname)s.html' - '#%(module)s.%(funcname)s' % dict( - module=obj.__module__, - funcname=obj.__name__)) - if not isinstance(obj, type): - obj = type(obj) - return ('generated/classes/%(module)s/' - '%(module)s.%(typename)s.html' - '#%(module)s.%(typename)s' % dict( - module=obj.__module__, - typename=obj.__name__)) - - -def test_py_to_helpstr(): - def dotest(obj, ideal): - result = _py_to_helpstr(obj) - assert result == ideal, '%s != %s' % (result, ideal) - dotest('maya rocks', 'search.html?q=maya+rocks') - dotest(pmc.nodetypes, - 'generated/pymel.core.nodetypes.html' - '#module-pymel.core.nodetypes') - dotest(pmc.nodetypes.Joint, - 'generated/classes/pymel.core.nodetypes/' - 'pymel.core.nodetypes.Joint.html' - '#pymel.core.nodetypes.Joint') - dotest(pmc.nodetypes.Joint(), - 'generated/classes/pymel.core.nodetypes/' - 'pymel.core.nodetypes.Joint.html' - '#pymel.core.nodetypes.Joint') - dotest(pmc.nodetypes.Joint().getTranslation, - 'generated/classes/pymel.core.nodetypes/' - 'pymel.core.nodetypes.Joint.html' - '#pymel.core.nodetypes.Joint.getTranslation') - dotest(pmc.joint, - 'generated/functions/pymel.core.animation/' - 'pymel.core.animation.joint.html' - '#pymel.core.animation.joint') - dotest(object(), None) - dotest(10, None) - dotest([], None) - dotest(sys, None) - - -def test_py_to_helpstrFAIL(): - assert 1 == 2, '1 != 2' - - -import webbrowser # (1) -HELP_ROOT_URL = ('http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/PyMel/')# (2) - - -def pmhelp(obj): # (3) - """Gives help for a pymel or python object. - - If obj is not a PyMEL object, use Python's built-in - `help` function. - If obj is a string, open a web browser to a search in the - PyMEL help for the string. - Otherwise, open a web browser to the page for the object. - """ - tail = _py_to_helpstr(obj) - if tail is None: - help(obj) # (4) - else: - webbrowser.open(HELP_ROOT_URL + tail) # (5) - - -if __name__ == '__main__': - test_py_to_helpstr() - print 'Tests ran successfully.' diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/kitbash_converter/launcher.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/kitbash_converter/launcher.bat deleted file mode 100644 index fb07178344..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/kitbash_converter/launcher.bat +++ /dev/null @@ -1,85 +0,0 @@ -@echo off - -REM -REM Copyright (c) Contributors to the Open 3D Engine Project. -REM For complete copyright and license terms please see the LICENSE at the root of this distribution. -REM -REM SPDX-License-Identifier: Apache-2.0 OR MIT -REM -REM - -:: Set up and run LY Python CMD prompt -:: Sets up the DccScriptingInterface_Env, -:: Puts you in the CMD within the dev environment - -:: Set up window -TITLE Lumberyard DCC Scripting Interface Cmd -:: Use obvious color to prevent confusion (Grey with Yellow Text) -COLOR 8E - -%~d0 -cd %~dp0 - -:: Keep changes local -SETLOCAL enableDelayedExpansion - -:: This maps up to the \Dev folder -IF "%DEV_REL_PATH%"=="" (set DEV_REL_PATH=..\..\..\..\..\..\..\..\..) - -:: Change to root Lumberyard dev dir -:: Don't use the LY_DEV so we can test that ENVAR!!! -CD /d %DEV_REL_PATH% -set Rel_Dev=%CD% -echo Rel_Dev = %Rel_Dev% -:: Restore original directory -popd - -set DCCSI_PYTHON_INSTALL=%Rel_Dev%\Tools\Python\3.7.5\windows - -:: add to the PATH -SET PATH=%DCCSI_PYTHON_INSTALL%;%PATH% - -:: dcc scripting interface gem path -set DCCSIG_PATH=%Rel_Dev%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface -echo DCCSIG_PATH = %DCCSIG_PATH% - -:: add to the PATH -SET PATH=%DCCSIG_PATH%;%PATH% - -:: Constant Vars (Global) -:: global debug (propogates) -IF "%DCCSI_GDEBUG%"=="" (set DCCSI_GDEBUG=false) -echo DCCSI_GDEBUG = %DCCSI_GDEBUG% -:: initiates debugger connection -IF "%DCCSI_DEV_MODE%"=="" (set DCCSI_DEV_MODE=false) -echo DCCSI_DEV_MODE = %DCCSI_DEV_MODE% -:: sets debugger, options: WING, PYCHARM -IF "%DCCSI_GDEBUGGER%"=="" (set DCCSI_GDEBUGGER=WING) -echo DCCSI_GDEBUGGER = %DCCSI_GDEBUGGER% - -echo. -echo _____________________________________________________________________ -echo. -echo ~ LY DCCsi, DCC Material Converter -echo _____________________________________________________________________ -echo. - -:: Change to root dir -CD /D %DCCSIG_PATH% - -:: add to the PATH -SET PATH=%DCCSIG_PATH%;%PATH% - -set PYTHONPATH=%DCCSIG_PATH%;%PYTHONPATH% - -CALL %DCCSI_PYTHON_INSTALL%\python.exe "%DCCSIG_PATH%\SDK\Maya\Scripts\Python\kitbash_converter\standalone.py" - - -ENDLOCAL - -:: Return to starting directory -POPD - -:END_OF_FILE - -exit /b 0 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/kitbash_converter/main.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/kitbash_converter/main.py index e554b1ca68..d0e9759208 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/kitbash_converter/main.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/kitbash_converter/main.py @@ -51,8 +51,8 @@ module_name = 'kitbash_converter.main' log_file_path = os.path.join(settings.DCCSI_LOG_PATH, f'{module_name}.log') _log_level = int(20) -_G_DEBUG = True -if _G_DEBUG: +_DCCSI_GDEBUG = True +if _DCCSI_GDEBUG: _log_level = int(10) from azpy.constants import FRMT_LOG_LONG diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_Cmd.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_Cmd.bat deleted file mode 100644 index ce118d7710..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_Cmd.bat +++ /dev/null @@ -1,45 +0,0 @@ -:: coding:utf-8 -:: !/usr/bin/python -:: -:: Copyright (c) Contributors to the Open 3D Engine Project. -:: For complete copyright and license terms please see the LICENSE at the root of this distribution. -:: -:: SPDX-License-Identifier: Apache-2.0 OR MIT -:: -:: - -@echo off -:: Set up and run LY Python CMD prompt -:: Sets up the DccScriptingInterface_Env, -:: Puts you in the CMD within the dev environment - -:: Set up window -TITLE Lumberyard DCC Scripting Interface Cmd -:: Use obvious color to prevent confusion (Grey with Yellow Text) -COLOR 8E - -%~d0 -cd %~dp0 -PUSHD %~dp0 - -:: Keep changes local -SETLOCAL enableDelayedExpansion - -CALL %~dp0\Project_Env.bat - -echo. -echo _____________________________________________________________________ -echo. -echo ~ LY DCC Scripting Interface CMD ... -echo _____________________________________________________________________ -echo. - -:: Create command prompt with environment -CALL %windir%\system32\cmd.exe - -ENDLOCAL - -:: Return to starting directory -POPD - -:END_OF_FILE diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_Maya_2020.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_Maya_2020.bat deleted file mode 100644 index 32e910790e..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_Maya_2020.bat +++ /dev/null @@ -1,68 +0,0 @@ -:: coding:utf-8 -:: !/usr/bin/python -:: -:: Copyright (c) Contributors to the Open 3D Engine Project. -:: For complete copyright and license terms please see the LICENSE at the root of this distribution. -:: -:: SPDX-License-Identifier: Apache-2.0 OR MIT -:: -:: - -@echo off -:: Launches maya with a bunch of local hooks for Lumberyard -:: ToDo: move all of this to a .json data driven boostrapping system - -%~d0 -cd %~dp0 -PUSHD %~dp0 - -echo ________________________________ -echo ~ calling PROJ_Env.bat - -:: Keep changes local -SETLOCAL enableDelayedExpansion - -:: PY version Major -set DCCSI_PY_VERSION_MAJOR=2 -echo DCCSI_PY_VERSION_MAJOR = %DCCSI_PY_VERSION_MAJOR% - -:: PY version Major -set DCCSI_PY_VERSION_MINOR=7 -echo DCCSI_PY_VERSION_MINOR = %DCCSI_PY_VERSION_MINOR% - -:: Maya Version -set MAYA_VERSION=2020 -echo MAYA_VERSION = %MAYA_VERSION% - -:: if a local customEnv.bat exists, run it -IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat - -echo ________________________________ -echo Launching Maya %MAYA_VERSION% for Lumberyard... - -:::: Set Maya native project acess to this project -::set MAYA_PROJECT=%LY_PROJECT% -::echo MAYA_PROJECT = %MAYA_PROJECT% - -:: DX11 Viewport -Set MAYA_VP2_DEVICE_OVERRIDE = VirtualDeviceDx11 - -:: Default to the right version of Maya if we can detect it... and launch -IF EXIST "%MAYA_LOCATION%\bin\Maya.exe" ( - start "" "%MAYA_LOCATION%\bin\Maya.exe" %* -) ELSE ( - Where maya.exe 2> NUL - IF ERRORLEVEL 1 ( - echo Maya.exe could not be found - pause - ) ELSE ( - start "" Maya.exe %* - ) -) - -:: Return to starting directory -POPD - -:END_OF_FILE - -exit /b 0 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_WingIDE-7-1.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_WingIDE-7-1.bat deleted file mode 100644 index 4d82bd3e16..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Launch_WingIDE-7-1.bat +++ /dev/null @@ -1,81 +0,0 @@ -:: coding:utf-8 -:: !/usr/bin/python -:: -:: Copyright (c) Contributors to the Open 3D Engine Project. -:: For complete copyright and license terms please see the LICENSE at the root of this distribution. -:: -:: SPDX-License-Identifier: Apache-2.0 OR MIT -:: -:: - -@echo off -:: Launches Wing IDE and the DccScriptingInterface Project Files - -echo. -echo _____________________________________________________________________ -echo. -echo ~ Setting up LY DCCsi WingIDE Dev Env... -echo _____________________________________________________________________ -echo. - -:: Store current dir -%~d0 -cd %~dp0 -PUSHD %~dp0 - -:: Keep changes local -SETLOCAL enableDelayedExpansion - -SET ABS_PATH=%~dp0 -echo Current Dir, %ABS_PATH% - -:: WingIDE version Major -SET WING_VERSION_MAJOR=7 -echo WING_VERSION_MAJOR = %WING_VERSION_MAJOR% - -:: WingIDE version Major -SET WING_VERSION_MINOR=1 -echo WING_VERSION_MINOR = %WING_VERSION_MINOR% - -:: note the changed path from IDE to Pro -set WINGHOME=%PROGRAMFILES(X86)%\Wing Pro %WING_VERSION_MAJOR%.%WING_VERSION_MINOR% -echo WINGHOME = %WINGHOME% - -CALL %~dp0\Project_Env.bat - -echo. -echo _____________________________________________________________________ -echo. -echo ~ WingIDE Version %WING_VERSION_MAJOR%.%WING_VERSION_MINOR% -echo _____________________________________________________________________ -echo. - -SET WING_PROJ=%DCCSIG_PATH%\Solutions\.wing\DCCsi_%WING_VERSION_MAJOR%x.wpr -echo WING_PROJ = %WING_PROJ% - -echo. -echo _____________________________________________________________________ -echo. -echo ~ Launching %LY_PROJECT% project in WingIDE %WING_VERSION_MAJOR%.%WING_VERSION_MINOR% ... -echo _____________________________________________________________________ -echo. - - -IF EXIST "%WINGHOME%\bin\wing.exe" ( - start "" "%WINGHOME%\bin\wing.exe" "%WING_PROJ%" -) ELSE ( - Where wing.exe 2> NUL - IF ERRORLEVEL 1 ( - echo wing.exe could not be found - pause - ) ELSE ( - start "" wing.exe "%WING_PROJ%" - ) -) - -ENDLOCAL - -:: Return to starting directory -POPD - -:END_OF_FILE diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Project_Env.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Project_Env.bat deleted file mode 100644 index f958d98616..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/Project_Env.bat +++ /dev/null @@ -1,72 +0,0 @@ -:: coding:utf-8 -:: !/usr/bin/python -:: -:: Copyright (c) Contributors to the Open 3D Engine Project. -:: For complete copyright and license terms please see the LICENSE at the root of this distribution. -:: -:: SPDX-License-Identifier: Apache-2.0 OR MIT -:: -:: - -@echo off -:: Sets up environment for Lumberyard DCC tools and code access - -:: Store current dir -%~d0 -cd %~dp0 -PUSHD %~dp0 - -for %%a in (.) do set LY_PROJECT=%%~na - -echo. -echo _____________________________________________________________________ -echo. -echo ~ Setting up LY DSI PROJECT Environment ... -echo _____________________________________________________________________ -echo. - -echo LY_PROJECT = %LY_PROJECT% - -:: Put you project env vars and overrides here - -:: chanhe the relative path up to dev -set DEV_REL_PATH=../../.. -set ABS_PATH=%~dp0 - -:: Override the default maya version -set MAYA_VERSION=2020 -echo MAYA_VERSION = %MAYA_VERSION% - -set LY_PROJECT_PATH=%ABS_PATH% -echo LY_PROJECT_PATH = %LY_PROJECT_PATH% - -:: Change to root Lumberyard dev dir -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 - -rem :: Constant Vars (Global) -rem SET LYPY_GDEBUG=0 -rem echo LYPY_GDEBUG = %LYPY_GDEBUG% -rem SET LYPY_DEV_MODE=0 -rem echo LYPY_DEV_MODE = %LYPY_DEV_MODE% -rem SET LYPY_DEBUGGER=WING -rem echo LYPY_DEBUGGER = %LYPY_DEBUGGER% - -:: Restore original directory -popd - -:: Change to root dir -CD /D %ABS_PATH% - -:: if the user has set up a custom env call it -IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat - -GOTO END_OF_FILE - -:: Return to starting directory -POPD - -:END_OF_FILE diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/main.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/main.py index 3b985199bb..09caef836f 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/main.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/legacy_asset_converter/main.py @@ -80,8 +80,8 @@ module_name = 'legacy_asset_converter.main' log_file_path = os.path.join(settings.DCCSI_LOG_PATH, f'{module_name}.log') _log_level = int(20) -_G_DEBUG = True -if _G_DEBUG: +_DCCSI_GDEBUG = True +if _DCCSI_GDEBUG: _log_level = int(10) from azpy.constants import FRMT_LOG_LONG diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/maya_dcc_materials/minspect.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/maya_dcc_materials/minspect.py deleted file mode 100644 index 668382480c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/maya_dcc_materials/minspect.py +++ /dev/null @@ -1,135 +0,0 @@ -# -*- coding: utf-8 -*- -# !/usr/bin/python -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# -# ------------------------------------------------------------------------- - -import pymel.core as pmc -import sys -import types - - -def syspath(): - print 'sys.path:' - for p in sys.path: - print ' ' + p - - -def info(obj): - """Prints information about the object.""" - - lines = ['Info for %s' % obj.name(), - 'Attributes:'] - # Get the name of all attributes - for a in obj.listAttr(): - lines.append(' ' + a.name()) - lines.append('MEL type: %s' % obj.type()) - lines.append('MRO:') - lines.extend([' ' + t.__name__ for t in type(obj).__mro__]) - result = '\n'.join(lines) - print result - - -def _is_pymel(obj): - try: # (1) - module = obj.__module__ # (2) - except AttributeError: # (3) - try: - module = obj.__name__ # (4) - except AttributeError: - return None # (5) - return module.startswith('pymel') # (6) - - -def _py_to_helpstr(obj): - if isinstance(obj, basestring): - return 'search.html?q=%s' % (obj.replace(' ', '+')) - if not _is_pymel(obj): - return None - if isinstance(obj, types.ModuleType): - return ('generated/%(module)s.html#module-%(module)s' % - dict(module=obj.__name__)) - if isinstance(obj, types.MethodType): - return ('generated/classes/%(module)s/' - '%(module)s.%(typename)s.html' - '#%(module)s.%(typename)s.%(methname)s' % dict( - module=obj.__module__, - typename=obj.im_class.__name__, - methname=obj.__name__)) - if isinstance(obj, types.FunctionType): - return ('generated/functions/%(module)s/' - '%(module)s.%(funcname)s.html' - '#%(module)s.%(funcname)s' % dict( - module=obj.__module__, - funcname=obj.__name__)) - if not isinstance(obj, type): - obj = type(obj) - return ('generated/classes/%(module)s/' - '%(module)s.%(typename)s.html' - '#%(module)s.%(typename)s' % dict( - module=obj.__module__, - typename=obj.__name__)) - - -def test_py_to_helpstr(): - def dotest(obj, ideal): - result = _py_to_helpstr(obj) - assert result == ideal, '%s != %s' % (result, ideal) - dotest('maya rocks', 'search.html?q=maya+rocks') - dotest(pmc.nodetypes, - 'generated/pymel.core.nodetypes.html' - '#module-pymel.core.nodetypes') - dotest(pmc.nodetypes.Joint, - 'generated/classes/pymel.core.nodetypes/' - 'pymel.core.nodetypes.Joint.html' - '#pymel.core.nodetypes.Joint') - dotest(pmc.nodetypes.Joint(), - 'generated/classes/pymel.core.nodetypes/' - 'pymel.core.nodetypes.Joint.html' - '#pymel.core.nodetypes.Joint') - dotest(pmc.nodetypes.Joint().getTranslation, - 'generated/classes/pymel.core.nodetypes/' - 'pymel.core.nodetypes.Joint.html' - '#pymel.core.nodetypes.Joint.getTranslation') - dotest(pmc.joint, - 'generated/functions/pymel.core.animation/' - 'pymel.core.animation.joint.html' - '#pymel.core.animation.joint') - dotest(object(), None) - dotest(10, None) - dotest([], None) - dotest(sys, None) - - -def test_py_to_helpstrFAIL(): - assert 1 == 2, '1 != 2' - - -import webbrowser # (1) -HELP_ROOT_URL = ('http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/PyMel/')# (2) - - -def pmhelp(obj): # (3) - """Gives help for a pymel or python object. - - If obj is not a PyMEL object, use Python's built-in - `help` function. - If obj is a string, open a web browser to a search in the - PyMEL help for the string. - Otherwise, open a web browser to the page for the object. - """ - tail = _py_to_helpstr(obj) - if tail is None: - help(obj) # (4) - else: - webbrowser.open(HELP_ROOT_URL + tail) # (5) - - -if __name__ == '__main__': - test_py_to_helpstr() - print 'Tests ran successfully.' diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/stingraypbs_converter/stingrayPBS_converter.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/stingraypbs_converter/stingrayPBS_converter.py index 10cdac813a..4c24a61b59 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/stingraypbs_converter/stingrayPBS_converter.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/stingraypbs_converter/stingrayPBS_converter.py @@ -23,7 +23,7 @@ def returnStubDir(stub): break if (len(tail) == 0): path = "" - if _G_DEBUG: + if _DCCSI_GDEBUG: print('~ Debug Message: I was not able to find the ' 'path to that file (stub) in a walk-up from currnet path') break diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/stingraypbs_converter/stingrayPBS_converter_maya.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/stingraypbs_converter/stingrayPBS_converter_maya.py index 3ea7b1e5e7..3a7ec49e74 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/stingraypbs_converter/stingrayPBS_converter_maya.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/Python/stingraypbs_converter/stingrayPBS_converter_maya.py @@ -24,7 +24,7 @@ def returnStubDir(stub, start_path): break if (len(tail) == 0): path = "" - if _G_DEBUG: + if _DCCSI_GDEBUG: print('~ Debug Message: I was not able to find the ' 'path to that file (stub) in a walk-up from currnet path') break diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_callbacks.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_callbacks.py index 4dda18c403..8c3b0f6a64 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_callbacks.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_callbacks.py @@ -42,7 +42,7 @@ from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, True) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, True) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, True) _MODULENAME = r'DCCsi.SDK.Maya.Scripts.set_callbacks' diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_defaults.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_defaults.py index d6db5e42f4..639b03c297 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_defaults.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_defaults.py @@ -37,7 +37,7 @@ from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _MODULENAME = r'DCCsi.SDK.Maya.Scripts.set_defaults' diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_menu.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_menu.py index 4e85a448c1..e417324d0b 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_menu.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/set_menu.py @@ -37,7 +37,7 @@ from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _MODULENAME = r'DCCsi.SDK.Maya.Scripts.set_menu' diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/userSetup.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/userSetup.py index aee5563c3d..04eb027142 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/userSetup.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/Scripts/userSetup.py @@ -56,7 +56,7 @@ import maya.mel as mel # ------------------------------------------------------------------------- # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) #_DCCSI_DEV_MODE = True # force true for debugger testing @@ -69,7 +69,7 @@ _MODULENAME = str('{0}.{1}'.format(_APP_TAG, _TOOL_TAG)) _LOGGER = azpy.initialize_logger(_MODULENAME, default_log_level=int(20)) _LOGGER.info('Initializing: {0}.'.format({_MODULENAME})) -_LOGGER.info('DCCSI_GDEBUG: {0}.'.format({_G_DEBUG})) +_LOGGER.info('DCCSI_GDEBUG: {0}.'.format({_DCCSI_GDEBUG})) _LOGGER.info('DCCSI_DEV_MODE: {0}.'.format({_DCCSI_DEV_MODE})) # flag to turn off setting up callbacks, until they are fully implemented @@ -175,17 +175,17 @@ try: except Exception as e: _LOGGER.critical(_STR_ERROR_ENVAR.format(_BASE_ENVVAR_DICT[ENVAR_DCCSI_SDK_PATH])) -_LY_PROJECT_PATH = None +_O3DE_PROJECT_PATH = None try: - _LY_PROJECT_PATH = _BASE_ENVVAR_DICT[ENVAR_LY_PROJECT_PATH] + _O3DE_PROJECT_PATH = _BASE_ENVVAR_DICT[ENVAR_O3DE_PROJECT_PATH] except Exception as e: - _LOGGER.critical(_STR_ERROR_ENVAR.format(_BASE_ENVVAR_DICT[ENVAR_LY_PROJECT_PATH])) + _LOGGER.critical(_STR_ERROR_ENVAR.format(_BASE_ENVVAR_DICT[ENVAR_O3DE_PROJECT_PATH])) # check some env var tags (fail if no, likely means no proper code access) -_LY_DEV = _BASE_ENVVAR_DICT[ENVAR_LY_DEV] -_LY_DCCSIG_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSIG_PATH] -_LY_DCCSI_LOG_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSI_LOG_PATH] -_LY_AZPY_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSI_AZPY_PATH] +_O3DE_DEV = _BASE_ENVVAR_DICT[ENVAR_O3DE_DEV] +_O3DE_DCCSIG_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSIG_PATH] +_O3DE_DCCSI_LOG_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSI_LOG_PATH] +_O3DE_AZPY_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSI_AZPY_PATH] # ------------------------------------------------------------------------- @@ -270,18 +270,18 @@ def post_startup(): install_fix_paths() # set the project workspace - #_LY_PROJECT_PATH = _BASE_ENVVAR_DICT[ENVAR_LY_PROJECT_PATH] - _project_workspace = os.path.join(_LY_PROJECT_PATH, TAG_MAYA_WORKSPACE) + #_O3DE_PROJECT_PATH = _BASE_ENVVAR_DICT[ENVAR_O3DE_PROJECT_PATH] + _project_workspace = os.path.join(_O3DE_PROJECT_PATH, TAG_MAYA_WORKSPACE) if os.path.isfile(_project_workspace): try: # load workspace - maya.cmds.workspace(_LY_PROJECT_PATH, openWorkspace=True) + maya.cmds.workspace(_O3DE_PROJECT_PATH, openWorkspace=True) _LOGGER.info('Loaded workspace file: {0}'.format(_project_workspace)) - maya.cmds.workspace(_LY_PROJECT_PATH, update=True) + maya.cmds.workspace(_O3DE_PROJECT_PATH, update=True) except Exception as e: _LOGGER.error(e) else: - _LOGGER.warning('Workspace file not found: {1}'.format(_LY_PROJECT_PATH)) + _LOGGER.warning('Workspace file not found: {1}'.format(_O3DE_PROJECT_PATH)) # Set up Lumberyard, maya default setting from set_defaults import set_defaults @@ -292,7 +292,7 @@ def post_startup(): _LOGGER.info('Add UI dependent tools') # wrap in a try, because we haven't implmented it yet try: - mel.eval(str(r'source "{}"'.format(TAG_LY_DCC_MAYA_MEL))) + mel.eval(str(r'source "{}"'.format(TAG_O3DE_DCC_MAYA_MEL))) except Exception as e: _LOGGER.error(e) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/blender_materials.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/blender_materials.py deleted file mode 100755 index 6d7bd10f89..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/blender_materials.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -import bpy -import collections -import json - - -def get_shader_information(): - """ - Queries all materials and corresponding material attributes and file textures in the Blender scene. - - :return: - """ - # TODO - link file texture location to PBR material plugs- finding it difficult to track down how this is achieved - # in the Blender Python API documentation and/or in forums - - materials_count = 1 - shader_types = get_blender_shader_types() - materials_dictionary = {} - for target_mesh in [o for o in bpy.data.objects if type(o.data) is bpy.types.Mesh]: - material_information = collections.OrderedDict(DccApplication='Blender', AppliedMesh=target_mesh, - SceneName=bpy.data.filepath, MaterialAttributes={}, - FileConnections={}) - for target_material in target_mesh.data.materials: - material_information['MaterialName'] = target_material.name - shader_attributes = {} - shader_file_connections = {} - - for node in target_material.node_tree.nodes: - socket = node.inputs[0] - print('NODE: {}'.format(node)) - print('Socket: {}'.format(socket)) - - for material_input in node.inputs: - attribute_name = material_input.name - try: - attribute_value = material_input.default_value - print('Name: [{}] [{}] ValueType ::::::> {}'.format(attribute_name, attribute_value, - type(attribute_value))) - material_information['MaterialAttributes'].update({attribute_name: str(attribute_value)}) - except Exception as e: - pass - print('\n') - if node.type == 'TEX_IMAGE': - material_information['FileConnections'].update({str(node): str(node.image.filepath)}) - if node.name in shader_types.keys(): - material_information['MaterialType'] = shader_types[node.name] - - -# material_information['MaterialAttributes'] = shader_attributes - materials_dictionary['Material_{}'.format(materials_count)] = material_information - materials_count += 1 - print('_________________________________________________________________\n') - - return materials_dictionary - - -def get_blender_shader_types(): - """ - This returns all the material types present in the Blender scene - :return: - """ - shader_types = {} - ddir = lambda data, filter_str: [i for i in dir(data) if i.startswith(filter_str)] - get_nodes = lambda cat: [i for i in getattr(bpy.types, cat).category.items(None)] - cycles_categories = ddir(bpy.types, "NODE_MT_category_SH_NEW") - for cat in cycles_categories: - if cat == 'NODE_MT_category_SH_NEW_SHADER': - for node in get_nodes(cat): - shader_types[node.label] = node.nodetype - return shader_types - - -materials_dictionary = get_shader_information() -#print('Materials Dictionary:') -#print(materials_dictionary) -#parsed = json.loads(str(materials_dictionary)) -#print(json.dumps(parsed, indent=4, sort_keys=True)) - - diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/cli_control.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/cli_control.py deleted file mode 100755 index 1d91275545..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/cli_control.py +++ /dev/null @@ -1,52 +0,0 @@ -# coding:utf-8 -#!/usr/bin/python -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# -# -- This line is 75 characters ------------------------------------------- - -import click -import os -import main as app_main - - -@click.version_option('1.0.0') -@click.option('--output', default='PBR', help='Lumberyard material type. Current options: [pbr_basic]') -@click.argument('operands', type=click.STRING, nargs=-1) -@click.command(context_settings=dict(ignore_unknown_options=True)) -def main(output, operands): - target_files = [] - for index, operand in enumerate(operands): - entry_path = os.path.abspath(str(operand)) - if os.path.isdir(entry_path): - for directory_path, directory_names, file_names in os.walk(entry_path): - for file_name in file_names: - if is_valid_file(file_name): - target_files.append(os.path.join(entry_path, file_name)) - else: - if is_valid_file(operand): - target_files.append(operand) - - if len(target_files): - app_main.launch_material_converter('standalone', output, target_files) - - -def is_valid_file(file_name): - """ - Allows only supported DCC application files by extensions - :param file_name: The name of the file. - :return: - """ - target_extensions = 'ma mb fbx blend max'.split(' ') - if file_name.split('.')[-1] in target_extensions: - return True - return False - - -if __name__ == '__main__': - main() - diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/dcc_material_mapping.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/dcc_material_mapping.py deleted file mode 100755 index 6369cb5658..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/dcc_material_mapping.py +++ /dev/null @@ -1,65 +0,0 @@ -# coding:utf-8 -#!/usr/bin/python -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# -# -- This line is 75 characters ------------------------------------------- - -import logging - -logging.basicConfig(level=logging.DEBUG) - -def get_maya_material_mapping(name, material_type, file_connections): - """ - Helps map found material DCC attribute values/file connections with Lumberyard materials. - - :param name: Material name from within Maya - :param material_type: Maya Material type to match values to (i.e. Stingray PBS, aiStandardSurface(Arnold) - :param file_connections: List of all connected texture files from Maya - :return: Key value pairs for attributes/file textures assigned as Lumberyard material values - """ - material_properties = {} - if material_type == 'StingrayPBS': - logging.debug('Mapping StingrayPBS') - maps = 'color, metallic, roughness, normal, emissive, ao, opacity'.split(', ') - naming_exceptions = {'color': 'baseColor', 'ao': 'ambientOcclusion'} - for m in maps: - texture_attribute = 'TEX_{}_map'.format(m) - for tex in file_connections.keys(): - if tex.find(texture_attribute) != -1: - key = m if m not in naming_exceptions else naming_exceptions.get(m) - logging.debug('Key, Value: {} {}.{}'.format(key, name, texture_attribute)) - material_properties[key] = {'useTexture': 'true', - 'textureMap': file_connections.get( - '{}.{}'.format(name, texture_attribute))} - elif material_type == 'aiStandardSurface': - logging.debug('Mapping AiStandardSurface') - # TODO- Occlusion is based on a more difficult setup- there is no standard channel. Set this up as time permits - maps = 'baseColor, metalness, specularRoughness, normal, emissionColor, opacity'.split(', ') - naming_exceptions = {'metalness': 'metallic', 'specularRoughness': 'roughness', 'emissionColor': 'emissive'} - for m in maps: - key = m if m not in naming_exceptions.keys() else naming_exceptions.get(m) - texture_attribute = m - for tex in file_connections.keys(): - if tex.find(texture_attribute) != -1: - logging.debug('Key, Value: {} {}.{}'.format(key, name, texture_attribute)) - material_properties[key] = {'useTexture': 'true', - 'textureMap': file_connections.get( - '{}.{}'.format(name, texture_attribute))} - else: - pass - - return material_properties - - -def get_blender_material_mapping(name, material_type, file_connections): - pass - - -def get_max_material_mapping(name, material_type, file_connections): - pass - diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/drag_and_drop.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/drag_and_drop.py deleted file mode 100755 index d02104b12c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/drag_and_drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# coding:utf-8 -#!/usr/bin/python -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# -# -- This line is 75 characters ------------------------------------------- - -from PySide2 import QtWidgets, QtCore -from PySide2.QtCore import Signal - -class DragAndDrop(QtWidgets.QWidget): - drop_update = QtCore.Signal(list) - drop_over = QtCore.Signal(bool) - - def __init__(self, frame_color=None, highlight=None, parent=None): - super(DragAndDrop, self).__init__(parent) - - self.urls = [] - self.frame_color = frame_color - self.frame_highlight = highlight - self.setContentsMargins(0, 0, 0, 0) - self.setAcceptDrops(True) - - self.drag_and_drop_frame = QtWidgets.QFrame(self) - self.drag_and_drop_frame.setGeometry(0, 0, 5000, 5000) - self.drag_and_drop_frame.setStyleSheet('background-color:rgb({});'.format(self.frame_color)) - - def dragEnterEvent(self, e): - if e.mimeData().hasUrls: - e.accept() - self.drop_over.emit(True) - if self.frame_highlight: - self.drag_and_drop_frame.setStyleSheet('background-color:rgb({});'.format(self.frame_highlight)) - else: - e.ignore() - - def dragLeaveEvent(self, e): - self.drop_over.emit(False) - - if self.frame_highlight: - self.drag_and_drop_frame.setStyleSheet('background-color:rgb({});'.format(self.frame_color)) - - def dragMoveEvent(self, e): - if e.mimeData().hasUrls: - e.accept() - else: - e.ignore() - - def dropEvent(self, e): - if e.mimeData().hasUrls: - e.setDropAction(QtCore.Qt.CopyAction) - e.accept() - - for url in e.mimeData().urls(): - file_name = str(url.toLocalFile()) - self.urls.append(file_name) - self.drop_update.emit(self.urls) - if self.frame_highlight: - self.drag_and_drop_frame.setStyleSheet('background-color:rgb({});'.format(self.frame_color)) - else: - e.ignore() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/launcher.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/launcher.bat deleted file mode 100644 index d609c26897..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/launcher.bat +++ /dev/null @@ -1,85 +0,0 @@ -@echo off - -REM -REM Copyright (c) Contributors to the Open 3D Engine Project. -REM For complete copyright and license terms please see the LICENSE at the root of this distribution. -REM -REM SPDX-License-Identifier: Apache-2.0 OR MIT -REM -REM - -:: Set up and run LY Python CMD prompt -:: Sets up the DccScriptingInterface_Env, -:: Puts you in the CMD within the dev environment - -:: Set up window -TITLE Lumberyard DCC Scripting Interface Cmd -:: Use obvious color to prevent confusion (Grey with Yellow Text) -COLOR 8E - -%~d0 -cd %~dp0 - -:: Keep changes local -SETLOCAL enableDelayedExpansion - -:: This maps up to the \Dev folder -IF "%DEV_REL_PATH%"=="" (set DEV_REL_PATH=..\..\..\..\..\..\..) - -:: Change to root Lumberyard dev dir -:: Don't use the LY_DEV so we can test that ENVAR!!! -CD /d %DEV_REL_PATH% -set Rel_Dev=%CD% -echo Rel_Dev = %Rel_Dev% -:: Restore original directory -popd - -set DCCSI_PYTHON_INSTALL=%Rel_Dev%\Tools\Python\3.7.5\windows - -:: add to the PATH -SET PATH=%DCCSI_PYTHON_INSTALL%;%PATH% - -:: dcc scripting interface gem path -set DCCSIG_PATH=%Rel_Dev%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface -echo DCCSIG_PATH = %DCCSIG_PATH% - -:: add to the PATH -SET PATH=%DCCSIG_PATH%;%PATH% - -:: Constant Vars (Global) -:: global debug (propogates) -IF "%DCCSI_GDEBUG%"=="" (set DCCSI_GDEBUG=false) -echo DCCSI_GDEBUG = %DCCSI_GDEBUG% -:: initiates debugger connection -IF "%DCCSI_DEV_MODE%"=="" (set DCCSI_DEV_MODE=false) -echo DCCSI_DEV_MODE = %DCCSI_DEV_MODE% -:: sets debugger, options: WING, PYCHARM -IF "%DCCSI_GDEBUGGER%"=="" (set DCCSI_GDEBUGGER=WING) -echo DCCSI_GDEBUGGER = %DCCSI_GDEBUGGER% - -echo. -echo _____________________________________________________________________ -echo. -echo ~ LY DCCsi, DCC Material Converter -echo _____________________________________________________________________ -echo. - -:: Change to root dir -CD /D %DCCSIG_PATH% - -:: add to the PATH -SET PATH=%DCCSIG_PATH%;%PATH% - -set PYTHONPATH=%DCCSIG_PATH%;%PYTHONPATH% - -CALL %DCCSI_PYTHON_INSTALL%\python.exe "%DCCSIG_PATH%\SDK\PythonTools\DCC_Material_Converter\standalone.py" - - -ENDLOCAL - -:: Return to starting directory -POPD - -:END_OF_FILE - -exit /b 0 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/main.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/main.py deleted file mode 100755 index f9359d7d82..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/main.py +++ /dev/null @@ -1,1026 +0,0 @@ -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -""" -Usage -===== -Put usage instructions here. - -Output -====== -Put output information here. - -Notes: -In order to run this, you'll need to verify that the "mayapy_path" class attribute corresponds to the location on -your machine. Currently I've just included mapping instructions for Maya StingrayPBS materials, although most of -the needed elements are in place to carry out additional materials inside of Maya pretty quickly moving forward. -I've marked areas that still need refinement (or to be added altogether) with TODO comments - -TODO- Docstrings need work... wanted to get descriptions in but they need to be set for Sphinx -TODO- Add 3ds Max interoperability -Links: -https://blender.stackexchange.com/questions/100497/use-blenders-bpy-in-projects-outside-blender -https://knowledge.autodesk.com/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2019/ENU/3DSMax-Batch/files/ -GUID-0968FF0A-5ADD-454D-B8F6-1983E76A4AF9-htm.html - -TODO- Look at dynaconf and wire in a solid means for configuration settings -TODO- This hasn't been "designed"- might be worth it to consider the visual design to ensure the most effective and - attractive UI -TODO- Allow revisions to Model - -Reading FBX file information (might come in handy later) --- Materials information can be extracted from ASCII fbx pretty easily, binary is possible but more difficult --- FBX files could be exported as ASCII files and I could use regex there to extract material information --- I couldn't get pyfbx_i42 to work, but purportedly it can extract information from binary files. You may just have -to use the specified python versions -""" -# built-ins -import collections -import logging -import subprocess -import json -import sys -import os -import re - -# should give access to Lumberyard Qt dlls and PySide2 -from PySide2 import QtWidgets, QtCore, QtGui -from PySide2.QtCore import Slot -from PySide2.QtWidgets import QApplication -import shiboken2 -from shiboken2 import wrapInstance - -# local imports -from model import MaterialsModel -from drag_and_drop import DragAndDrop -import dcc_material_mapping as dcc_map - -# global space -main_window_pointer = None -main_app_window = None - - -class MaterialsToLumberyard(QtWidgets.QWidget): - def __init__(self, output_material_type='PBR', cli_values=None, parent=None): - super(MaterialsToLumberyard, self).__init__(parent) - - self.app = QtWidgets.QApplication.instance() - self.setWindowFlags(QtCore.Qt.Window) - self.setGeometry(50, 50, 800, 520) - self.setObjectName('MaterialsToLumberyard') - self.setWindowTitle(' ') - self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowMinMaxButtonsHint) - self.isTopLevel() - - self.cli_enabled = cli_values - self.output_material_type = output_material_type - self.desktop_location = os.path.join(os.path.expanduser('~'), 'Desktop') - self.directory_path = os.path.dirname(os.path.abspath(__file__)) - self.mayapy_path = os.path.abspath("C:/Program Files/Autodesk/Maya2020/bin/mayapy.exe") - self.blender_path = self.get_blender_path() - self.bold_font_large = QtGui.QFont('Helvetica', 7, QtGui.QFont.Bold) - self.medium_font = QtGui.QFont('Helvetica', 7, QtGui.QFont.Normal) - self.blessed_file_extensions = 'ma mb fbx max blend'.split(' ') - - self.dcc_materials_dictionary = {} - self.lumberyard_materials_dictionary = {} - self.lumberyard_material_nodes = [] - self.target_file_list = [] - self.current_scene = None - self.model = None - self.total_materials = 0 - - self.main_container = QtWidgets.QVBoxLayout(self) - self.main_container.setContentsMargins(0, 0, 0, 0) - self.main_container.setAlignment(QtCore.Qt.AlignTop) - self.setLayout(self.main_container) - self.content_layout = QtWidgets.QVBoxLayout() - self.content_layout.setAlignment(QtCore.Qt.AlignTop) - self.content_layout.setContentsMargins(10, 3, 10, 5) - self.main_container.addLayout(self.content_layout) - - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - # ---->> Header Bar - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - self.header_bar_layout = QtWidgets.QHBoxLayout() - self.lumberyard_logo_layout = QtWidgets.QHBoxLayout() - self.lumberyard_logo_layout.setAlignment(QtCore.Qt.AlignLeft) - logo_path = os.path.join(self.directory_path, 'resources', 'lumberyard_logo.png') - logo_pixmap = QtGui.QPixmap(logo_path) - self.lumberyard_logo = QtWidgets.QLabel() - self.lumberyard_logo.setPixmap(logo_pixmap) - self.lumberyard_logo_layout.addWidget(self.lumberyard_logo) - self.header_bar_layout.addLayout(self.lumberyard_logo_layout) - - self.switch_combobox_layout = QtWidgets.QHBoxLayout() - self.switch_combobox_layout.setAlignment(QtCore.Qt.AlignRight) - self.switch_layout_combobox = QtWidgets.QComboBox() - self.set_combobox_items_accessibility() - self.switch_layout_combobox.setFixedSize(250, 30) - self.combobox_items = ['Add Source Files', 'Source File List', 'DCC Material Values', 'Export Materials'] - self.switch_layout_combobox.setStyleSheet('QComboBox {padding-left:6px;}') - self.switch_layout_combobox.addItems(self.combobox_items) - self.switch_combobox_layout.addWidget(self.switch_layout_combobox) - self.header_bar_layout.addLayout(self.switch_combobox_layout) - - self.content_layout.addSpacing(5) - self.content_layout.addLayout(self.header_bar_layout) - - # ++++++++++++++++++++++++++++++++++++++++++++++++# - # File Source Table / Attributes (Stacked Layout) # - # ++++++++++++++++++++++++++++++++++++++++++++++++# - - self.content_stacked_layout = QtWidgets.QStackedLayout() - self.content_layout.addLayout(self.content_stacked_layout) - self.switch_layout_combobox.currentIndexChanged.connect(self.layout_combobox_changed) - - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - # ---->> Add Source Files - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - frame_color_value = '75,75,75' - highlight_color_value = '20,106,30' - self.drag_and_drop_widget = DragAndDrop(frame_color_value, highlight_color_value) - self.drag_and_drop_widget.drop_update.connect(self.drag_and_drop_file_update) - self.drag_and_drop_widget.drop_over.connect(self.drag_and_drop_over) - self.drag_and_drop_layout = QtWidgets.QVBoxLayout() - self.drag_and_drop_layout.setContentsMargins(0, 0, 0, 0) - self.drag_and_drop_layout.setAlignment(QtCore.Qt.AlignCenter) - self.drag_and_drop_widget.setLayout(self.drag_and_drop_layout) - - start_message = 'Drag source files here, or use file browser button below to get started.' - self.drag_and_drop_label = QtWidgets.QLabel(start_message) - self.drag_and_drop_label.setStyleSheet('color: white;') - self.drag_and_drop_layout.addWidget(self.drag_and_drop_label) - self.drag_and_drop_layout.addSpacing(10) - - self.select_files_button_layout = QtWidgets.QHBoxLayout() - self.select_files_button_layout.setAlignment(QtCore.Qt.AlignCenter) - self.select_files_button = QtWidgets.QPushButton('Select Files') - self.select_files_button_layout.addWidget(self.select_files_button) - self.select_files_button.clicked.connect(self.select_files_button_clicked) - self.select_files_button.setFixedSize(80, 35) - self.drag_and_drop_layout.addLayout(self.select_files_button_layout) - self.content_stacked_layout.addWidget(self.drag_and_drop_widget) - - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - # ---->> Files Table - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - self.target_files_table = QtWidgets.QTableWidget() - self.target_files_table.setFocusPolicy(QtCore.Qt.NoFocus) - self.target_files_table.setColumnCount(2) - self.target_files_table.setAlternatingRowColors(True) - self.target_files_table.setHorizontalHeaderLabels(['File List', '']) - self.target_files_table.horizontalHeader().setStyleSheet('QHeaderView::section ' - '{background-color: rgb(220, 220, 220); ' - 'padding-top:7px; padding-left:5px;}') - self.target_files_table.verticalHeader().hide() - files_header = self.target_files_table.horizontalHeader() - files_header.setFixedHeight(30) - files_header.setDefaultAlignment(QtCore.Qt.AlignLeft) - files_header.setContentsMargins(10, 10, 0, 0) - files_header.setDefaultSectionSize(60) - files_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) - files_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed) - self.target_files_table.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) - self.content_stacked_layout.addWidget(self.target_files_table) - - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - # ---->> Scene Information Table - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - self.material_tree_view = QtWidgets.QTreeView() - self.headers = ['Key', 'Value'] - self.material_tree_view.setStyleSheet('QTreeView::item {height:25px;} QHeaderView::section ' - '{background-color: rgb(220, 220, 220); height:30px; padding-left:10px}') - self.material_tree_view.setFocusPolicy(QtCore.Qt.NoFocus) - self.material_tree_view.setAlternatingRowColors(True) - self.material_tree_view.setUniformRowHeights(True) - self.content_stacked_layout.addWidget(self.material_tree_view) - - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - # ---->> LY Material Definitions - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - self.lumberyard_material_definitions_widget = QtWidgets.QWidget() - self.lumberyard_material_definitions_layout = QtWidgets.QHBoxLayout(self.lumberyard_material_definitions_widget) - self.lumberyard_material_definitions_layout.setSpacing(0) - self.lumberyard_material_definitions_layout.setContentsMargins(0, 0, 0, 0) - self.lumberyard_material_definitions_frame = QtWidgets.QFrame(self.lumberyard_material_definitions_widget) - self.lumberyard_material_definitions_frame.setGeometry(0, 0, 5000, 5000) - self.lumberyard_material_definitions_frame.setStyleSheet('background-color:rgb(75,75,75);') - self.lumberyard_material_definitions_scroller = QtWidgets.QScrollArea() - self.scroller_widget = QtWidgets.QWidget() - self.scroller_layout = QtWidgets.QVBoxLayout() - self.scroller_widget.setLayout(self.scroller_layout) - self.lumberyard_material_definitions_scroller.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) - self.lumberyard_material_definitions_scroller.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.lumberyard_material_definitions_scroller.setWidgetResizable(True) - self.lumberyard_material_definitions_scroller.setWidget(self.scroller_widget) - self.lumberyard_material_definitions_layout.addWidget(self.lumberyard_material_definitions_scroller) - self.content_stacked_layout.addWidget(self.lumberyard_material_definitions_widget) - - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - # ---->> File processing buttons - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - self.process_files_layout = QtWidgets.QHBoxLayout() - self.content_layout.addLayout(self.process_files_layout) - self.process_files_button = QtWidgets.QPushButton('Process Added Files') - self.process_files_button.setFixedHeight(50) - self.process_files_button.clicked.connect(self.process_listed_files_clicked) - self.process_files_layout.addWidget(self.process_files_button) - - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - # ---->> Status bar / Loader - # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - # TODO- Move all processing of files to another thread and display progress with loader - - self.status_bar = QtWidgets.QStatusBar() - self.status_bar.setStyleSheet('background-color: rgb(220, 220, 220);') - self.status_bar.setContentsMargins(0, 0, 0, 0) - self.status_bar.setSizeGripEnabled(False) - self.message_readout_label = QtWidgets.QLabel('Ready.') - self.message_readout_label.setStyleSheet('padding-left: 10px') - self.status_bar.addWidget(self.message_readout_label) - - self.progress_bar = QtWidgets.QProgressBar() - self.progress_bar_widget = QtWidgets.QWidget() - self.progress_bar_widget_layout = QtWidgets.QHBoxLayout() - self.progress_bar_widget_layout.setContentsMargins(0, 0, 0, 0) - self.progress_bar_widget_layout.setAlignment(QtCore.Qt.AlignRight) - self.progress_bar_widget.setLayout(self.progress_bar_widget_layout) - self.status_bar.addPermanentWidget(self.progress_bar_widget) - self.progress_bar_widget_layout.addWidget(self.progress_bar) - self.progress_bar.setFixedSize(180, 20) - self.main_container.addWidget(self.status_bar) - self.initialize() - - ############################ - # UI Display Layers ######## - ############################ - - def initialize(self): - if self.cli_enabled: - print('CLI ACCESS:::::::::::\nValues passed: {}'.format(self.cli_enabled)) - self.target_file_list = self.cli_enabled - self.process_file_list() - self.export_selected_materials() - - def populate_source_files_table(self): - """ - Adds selected files from the 'Source Files' section of the UI. This creates each item listing in the table - as well as adds a 'Remove' button that will clear corresponding item from the table. Processed files will - get color coded, based on whether or not the materials in the file could be successfully processed. Subsequent - searches will not clear items from the table currently, as each item acts as a register of materials that have - and have not yet been processed. - :return: - """ - self.target_files_table.setRowCount(0) - for index, entry in enumerate(self.target_file_list): - entry = entry[1] if type(entry) == list else entry - self.target_files_table.insertRow(index) - item = QtWidgets.QTableWidgetItem(' {}'.format(entry)) - self.target_files_table.setRowHeight(index, 45) - remove_button = QtWidgets.QPushButton('Remove') - remove_button.setFixedWidth(60) - remove_button.clicked.connect(self.remove_source_file_clicked) - self.target_files_table.setItem(index, 0, item) - self.target_files_table.setCellWidget(index, 1, remove_button) - - def populate_dcc_material_values_tree(self): - """ - Sets the materials model class to the file attribute tree. - :return: - """ - # TODO- Create mechanism for collapsing previously gathered materials, and or pushing them further down the list - self.material_tree_view.setModel(self.model) - self.material_tree_view.expandAll() - self.material_tree_view.resizeColumnToContents(0) - - def populate_export_materials_list(self): - """ - Once all materials have been analyzed inside of DCC applications, the 'Export Materials' view lists all - materials presented as their Lumberyard counterparts. Each listing displays a representation of the material - file based on its corresponding DCC material values and file connections. - :return: - """ - self.reset_export_materials_description() - for count, value in enumerate(self.lumberyard_materials_dictionary): - material_definition_node = MaterialNode([value, self.lumberyard_materials_dictionary[value]], count) - self.lumberyard_material_nodes.append(material_definition_node) - self.scroller_layout.addWidget(material_definition_node) - self.scroller_layout.addLayout(self.create_separator_line()) - - ############################ - # TBD ######## - ############################ - - def process_file_list(self): - """ - The entry point for reading DCC files and extracting values. Files are filtered and separated - by DCC app (based on file extensions) before processing is done. - - Supported DCC applications: - Maya (.ma, .mb, .fbx), 3dsMax(.max), Blender(.blend) - :return: - """ - files_dict = {'maya': [], 'max': [], 'blender': [], 'na': []} - for file_location in self.target_file_list: - file_name = os.path.basename(str(file_location)) - file_extension = os.path.splitext(file_name)[1] - target_application = self.get_target_application(file_extension) - if target_application in files_dict.keys(): - files_dict[target_application].append(file_location) - - for key, values in files_dict.items(): - try: - if key == 'maya' and len(values): - self.get_maya_material_values(values) - elif key == 'max' and len(values): - self.get_max_material_values(values) - elif key == 'blender' and len(values): - self.get_blender_material_values(values) - else: - pass - except Exception as e: - # TODO- Allow corrective actions or some display of errors if this fails? - logging.warning('Could not process files. Error: {}'.format(e)) - - if self.dcc_materials_dictionary: - self.set_transfer_status(self.dcc_materials_dictionary) - # Create Model with extracted values from file list - self.set_material_model() - # Setup Lumberyard Material File Values - self.set_export_materials_description() - # Update UI Layout - self.populate_export_materials_list() - self.switch_layout_combobox.setCurrentIndex(3) - self.set_ui_buttons() - self.message_readout_label.setText('Ready.') - - def reset_export_materials_description(self): - pass - - def reset_all_values(self): - pass - - def create_separator_line(self): - """ Convenience function for adding separation line to the UI. """ - layout = QtWidgets.QHBoxLayout() - line = QtWidgets.QLabel() - line.setFrameStyle(QtWidgets.QFrame.HLine | QtWidgets.QFrame.Sunken) - line.setLineWidth(1) - line.setFixedHeight(10) - layout.addWidget(line) - layout.setContentsMargins(8, 0, 8, 0) - return layout - - def export_selected_materials(self): - """ - This will eventually be revised to save material definitions in the proper place in the user's project folder, - but for now material definitions will be saved to the desktop. - :return: - """ - for node in self.lumberyard_material_nodes: - if node.material_name_checkbox.isChecked(): - output_path = os.path.dirname(node.material_info['sourceFile']) - node.material_info.pop('sourceFile') - output = os.path.join(output_path, '{}.material'.format(node.material_name)) - with open(output, 'w', encoding='utf-8') as material_file: - json.dump(node.material_info, material_file, ensure_ascii=False, indent=4) - - ############################ - # Getters/Setters ########## - ############################ - - @staticmethod - def get_target_application(file_extension): - """ - Searches compatible file extensions and returns one of three Application names- Maya, 3dsMax, or Blender. - :param file_extension: Passed file extension used to determine DCC Application it originated from. - :return: Returns the application corresponding to the extension if found- otherwise returns a Boolean None - """ - app_extensions = {'maya': ['.ma', '.mb', '.fbx'], 'max': ['.max'], 'blender': ['.blend']} - target_dcc_application = [key for key, values in app_extensions.items() if file_extension in values] - if target_dcc_application: - return target_dcc_application[0] - return None - - @staticmethod - def get_lumberyard_material_template(shader_type): - """ - Loads material descriptions from the Lumberyard installation, providing a template to compare and convert DCC - shaders to Lumberyard material definitions. This is the first step in the comparison. The second step is to - compare these values with specific mapping instructions for DCC Application and DCC material type to arrive at - a converted material. - :param shader_type: The type of Lumberyard shader to pair material attributes to (i.e. PBR Shader) - :return: File dictionary of the available boilerplate Lumberyard shader settings. - """ - definitions = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', - '{}.template.material'.format(shader_type)) - if os.path.exists(definitions): - with open(definitions) as f: - return json.load(f) - - @staticmethod - def get_lumberyard_material_properties(name, dcc_app, material_type, file_connections): - """ - This system will probably need rethinking if DCCs and compatible materials grow. I've tried to keep this - flexible so that it can be expanded with more apps and materials. - - :param name: Material name from within the DCC application - :param dcc_app: The application that the material was sourced from - :param material_type: DCC material type - :param file_connections: Texture files found attached to the materials - """ - - material_properties = {} - if dcc_app == 'Maya': - material_properties = dcc_map.get_maya_material_mapping(name, material_type, file_connections) - elif dcc_app == 'Blender': - material_properties = dcc_map.get_blender_material_mapping(name, material_type, file_connections) - elif dcc_app == '3dsMax': - material_properties = dcc_map.get_max_material_mapping(name, material_type, file_connections) - else: - pass - return material_properties - - @staticmethod - def get_filename_increment(name): - """ - Convenience function that assists in ensuring that if any materials are encountered with the same name, an - underscore and number is appended to it to prevent overwrites. - :param name: The name of the material. The function searches the string for increment numbers, and either adds - one to any encountered, or adds an "_1" if passed name is the first duplicate encountered. - :return: The adjusted name with a unique incremental value. - """ - last_number = re.compile(r'(?:[^\d]*(\d+)[^\d]*)+') - number_found = last_number.search(name) - if number_found: - next_number = str(int(number_found.group(1)) + 1) - start, end = number_found.span(1) - name = name[:max(end - len(next_number), start)] + next_number + name[end:] - return name - - def get_maya_material_values(self, target_files): - """ - Launches Maya Standalone and processes list of materials for each scene passed to the 'target_files' argument. - Also sets the environment paths needed for an instance of Maya's Python distribution. After files are processed - a single dictionary of scene materials is returned, and added to the "materials_dictionary" scene attribute. - :param target_files: List of files filtered from total list of files requested for processing that have a - Maya file extension - :return: - """ - - # TODO- Set load process to a separate thread and wire load progress bar up - - try: - script_path = str(os.path.join(self.directory_path, 'maya_materials.py')) - target_files.append(self.total_materials) - runtime_env = os.environ.copy() - runtime_env['MAYA_LOCATION'] = os.path.dirname(self.mayapy_path) - runtime_env['PYTHONPATH'] = os.path.dirname(self.mayapy_path) - command = f'{self.mayapy_path} "{script_path}"' - for file in target_files: - command += f' "{file}"' - p = subprocess.Popen(command, shell=False, env=runtime_env, stdout=subprocess.PIPE) - output = p.communicate()[0] - self.set_material_dictionary(json.loads(output)) - except Exception as e: - logging.warning('maya error: {}'.format(e)) - - def get_max_material_values(self, target_files): - """ - This has not been implemented yet. - - :param target_files: List of files filtered from total list of files requested for processing that have a - .max file extension - :return: - """ - logging.debug('Max Target file: {}'.format(target_files)) - - def get_blender_material_values(self, target_files): - """ - This has not been implemented yet. - - :param target_files: List of files filtered from total list of files requested for processing that have a - .blend file extension - :return: - """ - logging.debug('Blender Target file: {}'.format(target_files)) - script_path = str(os.path.join(self.directory_path, 'blender_materials.py')) - target_files.append(self.total_materials) - p = subprocess.Popen([self.blender_path, '--background', '--python', script_path, '--', target_files]) - output = p.communicate()[0] - self.set_material_dictionary(json.loads(output)) - - def get_blender_path(self): - """ - Finds latest Blender version installed on the user machine for command line file processing. - - :return: Most current version available (or none) - """ - blender_base_directory = os.path.join(os.path.join('C:\\', 'Program Files', 'Blender Foundation')) - blender_versions_found = [] - for (directory_path, directory_name, filenames) in os.walk(blender_base_directory): - for filename in filenames: - if filename == 'blender.exe': - blender_versions_found.append(os.path.join(directory_path, filename)) - - if blender_versions_found: - return max(blender_versions_found, key=os.path.getctime) - else: - return None - - def set_combobox_items_accessibility(self): - """ - Locks items from within the combobox until the sections they connect to have content - :return: - """ - # TODO- Add this functionality - pass - - def set_transfer_status(self, transfer_info): - """ - Colorizes listings in the 'Source Files' view of the UI after processing to green or red, indicating whether or - not scene analysis successfully returned compatible materials and their values. - :param transfer_info: Each file the scripts attempt to process return a receipt of the success or failure of - the analysis. - :return: - """ - - # TODO- Include some way to get error information if analysis fails, and potentially offer the means to - # repair values as they map to intended Lumberyard shader type - - for row in range(self.target_files_table.rowCount()): - for key, values in transfer_info.items(): - row_path = self.target_files_table.item(row, 0).text().strip() - scene_processed = {x for x in transfer_info if values['SceneName'].replace('\\', '/') == row_path} - if scene_processed: - self.target_files_table.item(row, 0).setBackground(QtGui.QColor(192, 255, 171)) - break - else: - self.target_files_table.item(row, 0).setBackground(QtGui.QColor(255, 177, 171)) - - def set_export_materials_description(self): - root = self.model.rootItem - for row in range(self.model.rowCount()): - source_file = self.model.get_attribute_value('SceneName', root.child(row)) - name = self.model.get_attribute_value('MaterialName', root.child(row)) - material_type = self.model.get_attribute_value('MaterialType', root.child(row)) - dcc_app = self.model.get_attribute_value('DccApplication', root.child(row)) - file_connections = {} - shader_attributes = {} - - for childIndex in range(root.child(row).childCount()): - child_item = root.child(row).child(childIndex) - child_value = child_item.itemData - if child_item.childCount(): - target_dict = file_connections if child_value[0] == 'FileConnections' else shader_attributes - for subChildIndex in range(child_item.childCount()): - sub_child_data = child_item.child(subChildIndex).itemData - target_dict[sub_child_data[0]] = sub_child_data[1] - self.set_material_description(source_file, name, dcc_app, material_type, file_connections) - - def set_material_dictionary(self, dcc_dictionary): - """ - Adds all material descriptions pulled from each DCC file analyzed to the "materials_dictionary" class attribute. - This function runs each time a subprocess is launched to gather DCC application material values. - :param dcc_dictionary: The dictionary of values for each material analyzed by each specific DCC file list - return analyzed values - :return: - """ - logging.debug('DCC Dictionary: {}'.format(json.dumps(dcc_dictionary, indent=4))) - self.total_materials += len(dcc_dictionary) - self.dcc_materials_dictionary.update(dcc_dictionary) - - def set_material_model(self, initialize=True): - """ - Once all materials have been gathered across a selected file set query, this organizes the values into a - QT Model Class - :param initialize: Default is set to boolean True. If a model has already been established in the current - session, the initialize parameter would be set to false, and the values added to the Model. All changes to - the model would then be redistributed to other informational views in the UI. - :return: - """ - if initialize: - self.model = MaterialsModel(self.headers, self.dcc_materials_dictionary) - else: - self.model.update() - self.dcc_materials_dictionary.clear() - self.populate_dcc_material_values_tree() - - def set_ui_buttons(self): - """ - Handles UI buttons for each of the three stacked layout views (Source Files, DCC Material Values, - Export Materials) - :return: - """ - display_index = self.content_stacked_layout.currentIndex() - self.switch_layout_combobox.setEnabled(True) - self.process_files_button.setText('Process Listed Files') - # Add Source Files Layout ------------------------------->> - if display_index == 0: - self.process_files_button.setEnabled(True) - - # Source File List -------------------------------------->> - elif display_index == 1: - self.process_files_button.setEnabled(True) - - # DCC Material Values Layout ---------------------------->> - elif display_index == 2: - self.process_files_button.setEnabled(False) - - # Export Materials Layout ------------------------------->> - else: - self.process_files_button.setText('Export Selected Materials') - if self.lumberyard_materials_dictionary: - self.process_files_button.setEnabled(True) - - def set_material_description(self, source_file, name, dcc_app, material_type, file_connections): - """ - Build dictionary for material description based on extracted values - - :param source_file: The file that the material was extracted from - :param name: Name of material - :param dcc_app: Source file type of material (Maya, Blender or 3ds Max) - :param material_type: Material type within app (i.e. Stingray PBS) - :param file_connections: Texture files found connected to the shader - :return: - """ - - default_settings = self.get_lumberyard_material_template('standardPBR') - material = collections.OrderedDict(sourceFile=source_file, description=name, - materialType=default_settings.get('materialType'), - parentMaterial=default_settings.get('parentMaterial'), - propertyLayoutVersion=default_settings.get('propertyLayoutVersion'), - properties=self.get_lumberyard_material_properties(name, dcc_app, - material_type, - file_connections)) - name += self.output_material_type - self.lumberyard_materials_dictionary[name if name not in self.lumberyard_materials_dictionary.keys() else - self.get_filename_increment(name)] = material - - ############################ - # Button Actions ########### - ############################ - - def remove_source_file_clicked(self): - """ - In the Source File view of the UI layout, this will remove the listed file in its respective row. If files - have not been processed yet, it prevents that file from being analyzed. If the files have already been - analyzed, this will remove the materials from stored values. - :return: - """ - file_index = self.target_files_table.indexAt(self.sender().pos()) - del self.target_file_list[file_index.row()] - self.populate_files_table() - - def process_listed_files_clicked(self): - """ - The button serves a dual purpose, depending on the current layout of the window. 'Process listed files' - initiates the DCC file analysis that extracts material information. In the "Export Materials" layout, this - button (for now) will export material files corresponding to each analyzed material. Exported material files - are routed to the directories of the respective files processed. - :return: - """ - - if self.sender().text() == 'Process Added Files': - self.message_readout_label.setText('Gathering Material Information...') - self.app.processEvents() - self.process_file_list() - else: - self.export_selected_materials() - - def select_files_button_clicked(self): - """ - This dialog allows user to select DCC files to be processed for the materials present for conversion. - :return: - """ - - # TODO- Eventually it might be worth it to allow files from multiple locations to be selected. Currently - # this only allows single/multiple files from a single directory to be selected, although drag and drop - # allows multiple locations - - dialog = QtWidgets.QFileDialog(self, 'Shift-Select Target Files', self.desktop_location) - dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) - dialog.setNameFilter('Compatible Files (*.ma *.mb *.fbx *.max *.blend)') - dialog.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, True) - file_view = dialog.findChild(QtWidgets.QListView, 'listView') - - # Workaround for selecting multiple files with File Dialog - if file_view: - file_view.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) - f_tree_view = dialog.findChild(QtWidgets.QTreeView) - if f_tree_view: - f_tree_view.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) - - if dialog.exec_() == QtWidgets.QDialog.Accepted: - self.target_file_list += dialog.selectedFiles() - if self.target_file_list: - self.populate_source_files_table() - self.message_readout_label.setText('Source files added: {}'.format(len(self.target_file_list))) - self.process_files_button.setEnabled(True) - - def layout_combobox_changed(self): - """ - Handles main window layout combobox index change. - :return: - """ - self.content_stacked_layout.setCurrentIndex(self.switch_layout_combobox.currentIndex()) - self.set_ui_buttons() - - def reset_clicked(self): - """ - Brings the application and all variables back to their initial state. - :return: - """ - self.reset_all_values() - - ############################ - # Slots #################### - ############################ - - @Slot(list) - def drag_and_drop_file_update(self, file_list): - for file in file_list: - if os.path.basename(file).split('.')[-1] in self.blessed_file_extensions: - self.target_file_list.append(file) - self.drag_and_drop_widget.urls.clear() - self.populate_source_files_table() - self.message_readout_label.setText('Source files added: {}'.format(len(self.target_file_list))) - self.drag_and_drop_label.setStyleSheet('color: white;') - - @Slot(bool) - def drag_and_drop_over(self, is_over): - if is_over: - self.drag_and_drop_label.setStyleSheet('color: rgb(0, 255, 0);') - else: - self.drag_and_drop_label.setStyleSheet('color: white;') - - -class MaterialNode(QtWidgets.QWidget): - def __init__(self, material_info, current_position, parent=None): - super(MaterialNode, self).__init__(parent) - - self.material_name = material_info[0] - self.material_info = material_info[1] - self.current_position = current_position - self.property_settings = {} - - self.small_font = QtGui.QFont("Helvetica", 7, QtGui.QFont.Bold) - self.bold_font = QtGui.QFont("Helvetica", 8, QtGui.QFont.Bold) - self.main_layout = QtWidgets.QVBoxLayout() - self.main_layout.setContentsMargins(0, 0, 0, 0) - self.setLayout(self.main_layout) - - self.background_frame = QtWidgets.QFrame(self) - self.background_frame.setGeometry(0, 0, 5000, 5000) - self.background_frame.setStyleSheet('background-color:rgb(220, 220, 220);') - - # ######################## - # Title Bar - # ######################## - - self.title_bar_widget = QtWidgets.QWidget() - self.title_bar_layout = QtWidgets.QHBoxLayout(self.title_bar_widget) - self.title_bar_layout.setContentsMargins(10, 0, 10, 0) - self.title_bar_layout.setAlignment(QtCore.Qt.AlignTop) - self.title_bar_frame = QtWidgets.QFrame(self.title_bar_widget) - self.title_bar_frame.setGeometry(0, 0, 5000, 40) - self.title_bar_frame.setStyleSheet('background-color:rgb(193,154,255);') - self.main_layout.addWidget(self.title_bar_widget) - self.material_name_checkbox = QtWidgets.QCheckBox(self.material_name) - self.material_name_checkbox.setFixedHeight(35) - self.material_name_checkbox.setStyleSheet('spacing:10px; color:white') - self.material_name_checkbox.setFont(self.bold_font) - self.material_name_checkbox.setChecked(True) - self.title_bar_layout.addWidget(self.material_name_checkbox) - - self.material_file_layout = QtWidgets.QHBoxLayout() - self.material_file_layout.setAlignment(QtCore.Qt.AlignRight) - self.source_file = QtWidgets.QLabel(os.path.basename(self.material_info['sourceFile'])) - self.source_file.setStyleSheet('color:white;') - self.source_file.setFont(self.small_font) - self.material_file_layout.addWidget(self.source_file) - self.material_file_layout.addSpacing(10) - - self.edit_button = QtWidgets.QPushButton('Edit') - self.edit_button.clicked.connect(self.edit_button_clicked) - self.edit_button.setFixedWidth(55) - self.material_file_layout.addWidget(self.edit_button) - self.title_bar_layout.addLayout(self.material_file_layout) - - self.information_layout = QtWidgets.QHBoxLayout() - self.information_layout.setContentsMargins(10, 0, 10, 10) - self.main_layout.addLayout(self.information_layout) - - # ######################## - # Details layout - # ######################## - - self.details_layout = QtWidgets.QVBoxLayout() - self.details_layout.setAlignment(QtCore.Qt.AlignTop) - self.details_groupbox = QtWidgets.QGroupBox("Details") - self.details_groupbox.setFixedWidth(200) - self.details_groupbox.setStyleSheet("QGroupBox {font:bold; border: 1px solid silver; " - "margin-top: 6px;} QGroupBox::title { color: rgb(150, 150, 150); " - "subcontrol-position: top left;}") - self.details_layout.addSpacing(15) - self.material_type_label = QtWidgets.QLabel('Material Type') - self.material_type_label.setStyleSheet('padding-left: 6px; color: white; background-color:rgb(175, 175, 175);') - self.material_type_label.setFixedHeight(25) - self.material_type_label.setFont(self.bold_font) - self.details_layout.addWidget(self.material_type_label) - - self.material_type_combobox = QtWidgets.QComboBox() - self.material_type_combobox.setFixedHeight(30) - self.material_type_combobox.setStyleSheet('QCombobox QAbstractItemView { padding-left: 15px; }') - material_type_items = [' Standard PBR'] - self.material_type_combobox.addItems(material_type_items) - self.details_layout.addWidget(self.material_type_combobox) - self.details_layout.addSpacing(10) - - self.description_label = QtWidgets.QLabel('Description') - self.description_label.setStyleSheet('padding-left: 6px; color: white; background-color:rgb(175, 175, 175);') - self.description_label.setFixedHeight(25) - self.description_label.setFont(self.bold_font) - self.details_layout.addWidget(self.description_label) - - self.description_box = QtWidgets.QTextEdit('This space is reserved for additional information.') - self.details_layout.addWidget(self.description_box) - self.information_layout.addWidget(self.details_groupbox) - self.details_groupbox.setLayout(self.details_layout) - - # ######################## - # Properties layout - # ######################## - - self.properties_layout = QtWidgets.QVBoxLayout() - self.properties_layout.setAlignment(QtCore.Qt.AlignTop) - self.properties_groupbox = QtWidgets.QGroupBox("Properties") - self.properties_groupbox.setFixedWidth(150) - self.properties_groupbox.setStyleSheet("QGroupBox {font:bold; border: 1px solid silver; " - "margin-top: 6px;} QGroupBox::title { color: rgb(150, 150, 150); " - "subcontrol-position: top left;}") - self.properties_list_widget = QtWidgets.QListWidget() - self.material_properties = ['ambientOcclusion', 'baseColor', 'emissive', 'metallic', 'roughness', 'specularF0', - 'normal', 'opacity'] - self.properties_list_widget.addItems(self.material_properties) - self.properties_list_widget.itemSelectionChanged.connect(self.property_selection_changed) - self.properties_layout.addSpacing(15) - self.properties_layout.addWidget(self.properties_list_widget) - self.information_layout.addWidget(self.properties_groupbox) - self.properties_groupbox.setLayout(self.properties_layout) - - # ######################## - # Attributes layout - # ######################## - - self.attributes_layout = QtWidgets.QVBoxLayout() - self.attributes_layout.setAlignment(QtCore.Qt.AlignTop) - self.attributes_groupbox = QtWidgets.QGroupBox("Attributes") - self.attributes_groupbox.setStyleSheet("QGroupBox {font:bold; border: 1px solid silver; " - "margin-top: 6px;} QGroupBox::title { color: rgb(150, 150, 150); " - "subcontrol-position: top left;}") - self.information_layout.addWidget(self.attributes_groupbox) - self.attributes_layout.addSpacing(15) - self.attributes_table = QtWidgets.QTableWidget() - self.attributes_table.setFocusPolicy(QtCore.Qt.NoFocus) - self.attributes_table.setColumnCount(2) - self.attributes_table.setAlternatingRowColors(True) - self.attributes_table.setHorizontalHeaderLabels(['Attribute', 'Value']) - self.attributes_table.verticalHeader().hide() - attributes_table_header = self.attributes_table.horizontalHeader() - attributes_table_header.setStyleSheet('QHeaderView::section {background-color: rgb(220, 220, 220);}') - attributes_table_header.setDefaultAlignment(QtCore.Qt.AlignLeft) - attributes_table_header.setContentsMargins(10, 10, 0, 0) - attributes_table_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) - attributes_table_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) - attributes_table_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Interactive) - self.attributes_layout.addWidget(self.attributes_table) - self.attributes_groupbox.setLayout(self.attributes_layout) - self.initialize_display_values() - - def initialize_display_values(self): - """ - Initializes all of the widget item information for material based on the DCC application info the class has - been passed. - :return: - """ - for material_property in self.material_properties: - if material_property in self.material_info.get('properties'): - self.property_settings[material_property] = self.material_info['properties'].get(material_property) - current_row = self.material_properties.index(material_property) - current_item = self.properties_list_widget.takeItem(current_row) - self.properties_list_widget.insertItem(0, current_item) - else: - self.property_settings[material_property] = 'inactive' - current_row = self.material_properties.index(material_property) - item = self.properties_list_widget.item(current_row) - item.setFlags(item.flags() & ~QtCore.Qt.ItemIsEnabled) - item.setFlags(item.flags() & ~QtCore.Qt.ItemIsSelectable) - - self.properties_list_widget.setCurrentRow(0) - self.set_attributes_table(self.get_selected_property()) - - def set_attributes_table(self, selected_property): - """ - Displays the key, value pairs for the item selected in the Properties list widget - :param selected_property: The item in the Properties list widget that is currently selected. Only active - values are displayed. - :return: - """ - self.attributes_table.setRowCount(0) - row_count = 0 - for key, value in self.property_settings[selected_property].items(): - self.attributes_table.insertRow(row_count) - key_item = QtWidgets.QTableWidgetItem(key) - self.attributes_table.setItem(row_count, 0, key_item) - value_item = QtWidgets.QTableWidgetItem(value) - self.attributes_table.setItem(row_count, 1, value_item) - row_count += 1 - - def get_selected_property(self): - """ - Convenience function to get current value selected in the Properties list widget. - :return: - """ - return self.properties_list_widget.currentItem().text() - - def update_model(self): - """ - Not sure if this will go away, but if desired, I could make attribute values able to be revised after - materials have been scraped from the DCC materials - :return: - """ - pass - - def edit_button_clicked(self): - """ - This is in place in the event that we want to allow material revisions for properties to be made after - DCC processing step has already been executed. The idea would basically be to surface an editable - table where values can be added, removed or changed within the final material definition. - :return: - """ - logging.debug('Edit button clicked') - - def property_selection_changed(self): - """ - Fired when index of list view selected property selection has changed. - :return: - """ - self.set_attributes_table(self.get_selected_property()) - - -def is_valid_file(file_name): - """ - The acts as a clearinghouse for DCC file types supported by the script - :param file_name: Reads the extension of the filename for filtering - :return: - """ - target_extensions = 'ma mb fbx blend max'.split(' ') - if file_name.split('.')[-1] in target_extensions: - return True - return False - - -def launch_material_converter(window_type='standalone', material_type='PBR', target_files=None): - """ - The setup for this will be revised once this is fully integrated into the DCCsi system. Currently only the - standalone (default) and command line entry points work as intended. - :param window_type: The method of access for material conversion (standalone, command_line, maya_native, max_native) - :param material_type: Type of output material desired for import into Lumberyard. Currently only PBR is supported - :param target_files: DCC app files to process for converted Lumberyard materials - :return: - """ - if window_type == 'command_line': - MaterialsToLumberyard(material_type, target_files) - elif window_type == 'maya_native': - from maya import OpenMayaUI as omui - main_window_pointer = omui.MQtUtil.mainWindow() - main_app_window = wrapInstance(long(main_window_pointer), QtWidgets.QWidget) - MaterialsToLumberyard(material_type, None, main_app_window) - elif window_type == 'max_native': - from pymxs import runtime as rt - main_window_pointer = QtWidgets.QWidget.find(rt.windows.getMAXHWND()) - main_app_window = shiboken2.wrapInstance(shiboken2.getCppPointer(main_window_pointer)[0], QtWidgets.QMainWindow) - MaterialsToLumberyard(material_type, None, main_app_window) - else: - app = QApplication(sys.argv) - app_ui = MaterialsToLumberyard() - app_ui.show() - sys.exit(app.exec_()) - - -if __name__ == '__main__': - launch_material_converter() - diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/maya_materials.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/maya_materials.py deleted file mode 100755 index 4e66f83661..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/maya_materials.py +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# - -from PySide2 import QtCore -import maya.standalone -maya.standalone.initialize(name='python') -import maya.cmds as mc -import collections -import logging -import json -import sys -import os - - -for handler in logging.root.handlers[:]: - logging.root.removeHandler(handler) - -logging.basicConfig(level=logging.INFO, - format='%(name)s - %(levelname)s - %(message)s', - datefmt='%m-%d %H:%M', - filename='output.log', - filemode='w') - - -class MayaMaterials(QtCore.QObject): - def __init__(self, files_list, materials_count, parent=None): - super(MayaMaterials, self).__init__(parent) - - self.files_list = files_list - self.current_scene = None - self.materials_dictionary = {} - self.materials_count = int(materials_count) - self.get_material_information() - - def get_material_information(self): - """ - Main entry point for the material information extraction. Because this class is run - in Standalone mode as a subprocess, the list is passed as a string- some parsing/measures - need to be taken in order to separate values that originated as a list before passed. - - :return: A dictionary of all of the materials gathered. Sent back to main UI through stdout - """ - for target_file in file_list: - self.current_scene = os.path.abspath(target_file.replace('\'', '')) - mc.file(self.current_scene, open=True, force=True) - self.set_material_descriptions() - json.dump(self.materials_dictionary, sys.stdout) - - @staticmethod - def get_materials(target_mesh): - """ - Gathers a list of all materials attached to each mesh's shader - - :param target_mesh: The target mesh to pull attached material information from. - :return: List of unique material values attached to the mesh passed as an argument. - """ - shading_group = mc.listConnections(target_mesh, type='shadingEngine') - materials = mc.ls(mc.listConnections(shading_group), materials=1) - return list(set(materials)) - - @staticmethod - def get_shader(material_name): - """ - Convenience function for obtaining the shader that the specified material (as an argument) - is attached to. - - :param material_name: Takes the material name as an argument to get associated shader object - :return: - """ - connections = mc.listConnections(material_name, type='shadingEngine')[0] - shader_name = '{}.surfaceShader'.format(connections) - shader = mc.listConnections(shader_name)[0] - return shader - - def get_shader_information(self, shader, material_mesh): - """ - Helper function for extracting shader/material attributes used to form the DCC specific dictionary - of found material values for conversion. - - :param shader: The target shader object to analyze - :param material_mesh: The material mesh needs to be passed to search for textures attached to it. - :return: Complete set (in the form of two dictionaries) of file connections and material attribute values - """ - shader_file_connections = {} - materials = self.get_materials(material_mesh) - for material in materials: - material_files = [x for x in mc.listConnections(material, plugs=1, source=1) if x.startswith('file')] - for file_name in material_files: - file_texture = mc.getAttr('{}.fileTextureName'.format(file_name.split('.')[0])) - if os.path.basename(file_texture).split('.')[-1] != 'dds': - key_name = mc.listConnections(file_name, plugs=1, source=1)[0] - shader_file_connections[key_name] = file_texture - - shader_attributes = {} - for shader_attribute in mc.listAttr(shader, s=True, iu=True): - try: - shader_attributes[str(shader_attribute)] = str(mc.getAttr('{}.{}'.format(shader, shader_attribute))) - except Exception as e: - logging.error('MayaAttributeError: {}'.format(e)) - - return shader_file_connections, shader_attributes - - def set_material_dictionary(self, material_name, material_type, material_mesh): - """ - When a unique material has been found, this creates a dictionary entry with all relevant material values. This - includes material attributes as well as attached file textures. Later in the process this information is - leveraged when creating the Lumberyard material definition. - - :param material_name: The name attached to the material - :param material_type: Specific type of material (Arnold, Stingray, etc.) - :param material_mesh: Mesh that the material is applied to - :return: - """ - self.materials_count += 1 - shader = self.get_shader(material_name) - shader_file_connections, shader_attributes = self.get_shader_information(shader, material_mesh) - material_dictionary = collections.OrderedDict(MaterialName=material_name, MaterialType=material_type, - DccApplication='Maya', AppliedMesh=material_mesh, - FileConnections=shader_file_connections, - SceneName=str(self.current_scene), - MaterialAttributes=shader_attributes) - material_name = 'Material_{}'.format(self.materials_count) - self.materials_dictionary[material_name] = material_dictionary - logging.info('\n\n:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n' - 'MATERIAL DEFINITION: {} \n' - ':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n{}'.format( - self.materials_dictionary[material_name]['MaterialType'], - json.dumps(self.materials_dictionary[material_name], indent=4))) - - def set_material_descriptions(self): - """ - This function serves as the clearinghouse for all analyzed materials passing through the system. - It will determine whether or not the found material has already been processed, or if it needs to - be added to the final material dictionary. In the event that an encountered material has already - been processed, this function creates a register of all meshes it is applied to in the 'AppliedMesh' - attribute. - :return: - """ - scene_geo = mc.ls(v=True, geometry=True) - for target_mesh in scene_geo: - material_list = self.get_materials(target_mesh) - for material_name in material_list: - material_type = mc.nodeType(material_name) - - if material_type != 'lambert': - material_listed = [x for x in self.materials_dictionary - if self.materials_dictionary[x]['MaterialName'] == material_name] - - if not material_listed: - self.set_material_dictionary(str(material_name), str(material_type), str(target_mesh)) - else: - mesh_list = self.materials_dictionary[material_name].get('AppliedMesh') - if not isinstance(mesh_list, list): - self.materials_dictionary[str(material_name)]['AppliedMesh'] = [mesh_list, target_mesh] - else: - mesh_list.append(target_mesh) - - -# ++++++++++++++++++++++++++++++++++++++++++++++++# -# Maya Specific Shader Mapping # -# ++++++++++++++++++++++++++++++++++++++++++++++++# - -file_list = sys.argv[1:-1] -count = sys.argv[-1] -instance = MayaMaterials(file_list, count) - diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/model.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/model.py deleted file mode 100755 index 3c23635438..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/model.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -from PySide2.QtCore import QAbstractItemModel, QModelIndex, Qt - - -class MaterialsModel(QAbstractItemModel): - def __init__(self, headers, data, parent=None): - super(MaterialsModel, self).__init__(parent) - - self.rootItem = TreeNode(headers) - self.parents = [self.rootItem] - self.indentations = [0] - self.create_data(data) - - def create_data(self, data, indent=-1): - """ - Recursive loop that structures Model data into tree form. - :param data: Row information. - :param indent: Column information. This helps to facilitate the creation of nested rows. - :return: - """ - if type(data) == dict: - indent += 1 - position = 4 * indent - for key, value in data.items(): - if position > self.indentations[-1]: - if self.parents[-1].childCount() > 0: - self.parents.append(self.parents[-1].child(self.parents[-1].childCount() - 1)) - self.indentations.append(position) - else: - while position < self.indentations[-1] and len(self.parents) > 0: - self.parents.pop() - self.indentations.pop() - parent = self.parents[-1] - parent.insertChildren(parent.childCount(), 1, parent.columnCount()) - parent.child(parent.childCount() - 1).setData(0, key) - value_string = str(value) if type(value) != dict else str('') - parent.child(parent.childCount() - 1).setData(1, value_string) - try: - self.create_data(value, indent) - except RuntimeError: - pass - - @staticmethod - def get_attribute_value(search_string, search_column): - """ Convenience function for quickly accessing row information based on attribute keys. """ - for childIndex in range(search_column.childCount()): - child_item = search_column.child(childIndex) - child_value = child_item.itemData - if child_value[0] == search_string: - return child_value[1] - return None - - def index(self, row, column, index=QModelIndex()): - """ Returns the index of the item in the model specified by the given row, column and parent index """ - if not self.hasIndex(row, column, index): - return QModelIndex() - if not index.isValid(): - item = self.rootItem - else: - item = index.internalPointer() - - child = item.child(row) - if child: - return self.createIndex(row, column, child) - return QModelIndex() - - def parent(self, index): - """ - Returns the parent of the model item with the given index If the item has no parent, - an invalid QModelIndex is returned - """ - if not index.isValid(): - return QModelIndex() - item = index.internalPointer() - if not item: - return QModelIndex() - - parent = item.parentItem - if parent == self.rootItem: - return QModelIndex() - else: - return self.createIndex(parent.childNumber(), 0, parent) - - def rowCount(self, index=QModelIndex()): - """ - Returns the number of rows under the given parent. When the parent is valid it means that - rowCount is returning the number of children of parent - """ - if index.isValid(): - parent = index.internalPointer() - else: - parent = self.rootItem - return parent.childCount() - - def columnCount(self, index=QModelIndex()): - """ Returns the number of columns for the children of the given parent """ - return self.rootItem.columnCount() - - def data(self, index, role=Qt.DisplayRole): - """ Returns the data stored under the given role for the item referred to by the index """ - if index.isValid() and role == Qt.DisplayRole: - return index.internalPointer().data(index.column()) - elif not index.isValid(): - return self.rootItem.data(index.column()) - - def headerData(self, section, orientation, role=Qt.DisplayRole): - """ Returns the data for the given role and section in the header with the specified orientation """ - if orientation == Qt.Horizontal and role == Qt.DisplayRole: - return self.rootItem.data(section) - - -class TreeNode(object): - def __init__(self, data, parent=None): - self.parentItem = parent - self.itemData = data - self.children = [] - - def child(self, row): - return self.children[row] - - def childCount(self): - return len(self.children) - - def childNumber(self): - if self.parentItem is not None: - return self.parentItem.children.index(self) - - def columnCount(self): - return len(self.itemData) - - def data(self, column): - return self.itemData[column] - - def insertChildren(self, position, count, columns): - if position < 0 or position > len(self.children): - return False - for row in range(count): - data = [v for v in range(columns)] - item = TreeNode(data, self) - self.children.insert(position, item) - - def parent(self): - return self.parentItem - - def setData(self, column, value): - if column < 0 or column >= len(self.itemData): - return False - self.itemData[column] = value diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/standalone.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/standalone.py deleted file mode 100644 index e7a8445953..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/standalone.py +++ /dev/null @@ -1,37 +0,0 @@ -# coding:utf-8 -#!/usr/bin/python -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# - -"""Boostraps and Starts Standalone DCC Material Converter utility""" - -# built in's -import os -import site - -# ------------------------------------------------------------------------- -# \dev\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\SDK\PythonTools\DCC_Material_Converter\standalone.py -_MODULE_PATH = os.path.abspath(__file__) - -_DCCSIG_REL_PATH = "../../../.." -_DCCSIG_PATH = os.path.join(_MODULE_PATH, _DCCSIG_REL_PATH) -_DCCSIG_PATH = os.path.normpath(_DCCSIG_PATH) - -_DCCSIG_PATH = os.getenv('DCCSIG_PATH', - os.path.abspath(_DCCSIG_PATH)) - -# we don't have access yet to the DCCsi Lib\site-packages -site.addsitedir(_DCCSIG_PATH) # PYTHONPATH - -# azpy bootstrapping and extensions -import azpy.config_utils -_config = azpy.config_utils.get_dccsi_config() -settings = _config.get_config_settings(setup_ly_pyside=True) - -from main import launch_material_converter - -launch_material_converter() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/Launcher/main.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/Launcher/main.py deleted file mode 100644 index 5812755035..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/Launcher/main.py +++ /dev/null @@ -1,74 +0,0 @@ -# coding:utf-8 -#!/usr/bin/python -# coding:utf-8 -#!/usr/bin/python -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# -# -- This line is 75 characters ------------------------------------------- -# built-ins -import os -import sys -import logging as _logging - -# azpy extensions -import azpy.config_utils -_config = azpy.config_utils.get_dccsi_config() -settings = _config.get_config_settings(setup_ly_pyside=True) - -# 3rd Party (we may or do provide) -from pathlib import Path -from pathlib import PurePath - -# Lumberyard extensions -from azpy.env_bool import env_bool -from azpy.constants import ENVAR_DCCSI_GDEBUG -from azpy.constants import ENVAR_DCCSI_DEV_MODE - -# ------------------------------------------------------------------------- -# set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, settings.DCCSI_GDEBUG) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, settings.DCCSI_GDEBUG) - -for handler in _logging.root.handlers[:]: - _logging.root.removeHandler(handler) - -_MODULENAME = 'DCCsi.SDK.pythontools.launcher.main' - -_log_level = _logging.INFO -if _G_DEBUG: - _log_level = _logging.DEBUG - -_LOGGER = azpy.initialize_logger(name=_MODULENAME, - log_to_file=True, - default_log_level=_log_level) - -_LOGGER.debug('Starting up: {0}.'.format({_MODULENAME})) -# ------------------------------------------------------------------------- - - -# ------------------------------------------------------------------------- -def main(): - from PySide2.QtWidgets import QApplication, QPushButton - - app = QApplication(sys.argv) -# ------------------------------------------------------------------------- - - -# -------------------------------------------------------------------------- -if __name__ == '__main__': - """Run this file as main""" - -app = QApplication([]) # Start an application. -window = QWidget() # Create a window. -layout = QVBoxLayout() # Create a layout. -button = QPushButton("I'm just a Button man") # Define a button -layout.addWidget(QLabel('Hello World!')) # Add a label -layout.addWidget(button) # Add the button man -window.setLayout(layout) # Pass the layout to the window -window.show() # Show window -app.exec_() # Execute the App diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/atom_material.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/atom_material.py index 4c35861b9c..b7a6bfa3a1 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/atom_material.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/atom_material.py @@ -29,7 +29,7 @@ from pathlib import Path # ------------------------------------------------------------------------- # set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/bootstrap.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/bootstrap.py index 50a4d3fdda..f3e91b59ce 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/bootstrap.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/bootstrap.py @@ -34,20 +34,20 @@ from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE # these are for module debugging, set to false on submit -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = 'DCCsi.SDK.substance.builder.bootstrap' _log_level = int(20) -if _G_DEBUG: +if _DCCSI_GDEBUG: _log_level = int(10) _LOGGER = azpy.initialize_logger(_PACKAGENAME, log_to_file=True, default_log_level=_log_level) _LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME})) _LOGGER.debug('_DCCSIG_PATH: {}'.format(_DCCSIG_PATH)) -_LOGGER.debug('_G_DEBUG: {}'.format(_G_DEBUG)) +_LOGGER.debug('_G_DEBUG: {}'.format(_DCCSI_GDEBUG)) _LOGGER.debug('_DCCSI_DEV_MODE: {}'.format(_DCCSI_DEV_MODE)) if _DCCSI_DEV_MODE: @@ -69,7 +69,7 @@ from dynaconf import settings try: from PySide2.QtWidgets import QApplication except: - _dccsi_config.init_ly_pyside(settings.LY_DEV) # init for standalone + _dccsi_config.init_o3de_pyside(settings.O3DE_DEV) # init for standalone # running in the editor if the QtForPython Gem is enabled # you should already have access and shouldn't need to set up @@ -92,20 +92,20 @@ os.environ["PYSBS_DIR_PATH"] = str(_PYSBS_DIR_PATH) # standard paths we may use downstream # To Do: move these into a dynaconf config extension specific to this tool? -from azpy.constants import ENVAR_LY_DEV -_LY_DEV = Path(os.getenv(ENVAR_LY_DEV, - settings.LY_DEV)).resolve() +from azpy.constants import ENVAR_O3DE_DEV +_O3DE_DEV = Path(os.getenv(ENVAR_O3DE_DEV, + settings.O3DE_DEV)).resolve() -from azpy.constants import ENVAR_LY_PROJECT_PATH -_LY_PROJECT_PATH = Path(os.getenv(ENVAR_LY_PROJECT_PATH, - settings.LY_PROJECT_PATH)).resolve() +from azpy.constants import ENVAR_O3DE_PROJECT_PATH +_O3DE_PROJECT_PATH = Path(os.getenv(ENVAR_O3DE_PROJECT_PATH, + settings.O3DE_PROJECT_PATH)).resolve() from azpy.constants import ENVAR_DCCSI_SDK_PATH _DCCSI_SDK_PATH = Path(os.getenv(ENVAR_DCCSI_SDK_PATH, settings.DCCSIG_SDK_PATH)).resolve() # build some reuseable path parts for the substance builder -_PROJECT_ASSETS_PATH = Path(_LY_PROJECT_PATH, 'Assets').resolve() +_PROJECT_ASSETS_PATH = Path(_O3DE_PROJECT_PATH, 'Assets').resolve() _PROJECT_MATERIALS_PATH = Path(_PROJECT_ASSETS_PATH, 'Materials').resolve() # ------------------------------------------------------------------------- @@ -116,15 +116,15 @@ _PROJECT_MATERIALS_PATH = Path(_PROJECT_ASSETS_PATH, 'Materials').resolve() if __name__ == "__main__": """Run this file as main""" - _LOGGER.info('_LY_DEV: {}'.format(_LY_DEV)) - _LOGGER.info('_LY_PROJECT_PATH: {}'.format(_LY_PROJECT_PATH)) + _LOGGER.info('_O3DE_DEV: {}'.format(_O3DE_DEV)) + _LOGGER.info('_O3DE_PROJECT_PATH: {}'.format(_O3DE_PROJECT_PATH)) _LOGGER.info('_DCCSI_SDK_PATH: {}'.format(_DCCSI_SDK_PATH)) _LOGGER.info('_PYSBS_DIR_PATH: {}'.format(_PYSBS_DIR_PATH)) _LOGGER.info('_PROJECT_ASSETS_PATH: {}'.format(_PROJECT_ASSETS_PATH)) _LOGGER.info('_PROJECT_MATERIALS_PATH: {}'.format(_PROJECT_MATERIALS_PATH)) - if _G_DEBUG: + if _DCCSI_GDEBUG: _dccsi_config.test_pyside2() # runs a small PySdie2 test # remove the logger diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sb_gui_main.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sb_gui_main.py index cc501fe872..9408b94919 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sb_gui_main.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sb_gui_main.py @@ -35,7 +35,7 @@ from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE # set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, settings.DCCSI_GDEBUG) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, settings.DCCSI_GDEBUG) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, settings.DCCSI_GDEBUG) for handler in _logging.root.handlers[:]: @@ -44,7 +44,7 @@ for handler in _logging.root.handlers[:]: _MODULENAME = 'DCCsi.SDK.substance.builder.sb_gui_main' _log_level = _logging.INFO -if _G_DEBUG: +if _DCCSI_GDEBUG: _log_level = _logging.DEBUG _LOGGER = azpy.initialize_logger(name=_MODULENAME, @@ -71,12 +71,12 @@ import config _LOGGER.debug('config.py is: {}'.format(config)) # initialize the Lumberyard Qt / PySide2 -config.init_ly_pyside(settings.LY_DEV) # for standalone +config.init_o3de_pyside(settings.O3DE_DEV) # for standalone settings.setenv() # for standalone # log debug info about Qt/PySide2 _LOGGER.debug('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) -_LOGGER.debug('LY_BIN_PATH: {}'.format(settings.LY_BIN_PATH)) +_LOGGER.debug('O3DE_BIN_PATH: {}'.format(settings.O3DE_BIN_PATH)) _LOGGER.debug('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH)) _LOGGER.debug('QT_QPA_PLATFORM_PLUGIN_PATH: {}'.format(settings.QT_QPA_PLATFORM_PLUGIN_PATH)) # ------------------------------------------------------------------------- @@ -123,26 +123,26 @@ from atom_material import AtomMaterial # ------------------------------------------------------------------------- # To Do: still should manage via dynaconf (dynamic config and settings) -from azpy.constants import ENVAR_LY_DEV -_LY_DEV = Path(os.getenv(ENVAR_LY_DEV, None)).resolve() +from azpy.constants import ENVAR_O3DE_DEV +_O3DE_DEV = Path(os.getenv(ENVAR_O3DE_DEV, None)).resolve() -from azpy.constants import ENVAR_LY_PROJECT -_LY_PROJECT = os.getenv(ENVAR_LY_PROJECT, None) +from azpy.constants import ENVAR_O3DE_PROJECT +_O3DE_PROJECT = os.getenv(ENVAR_O3DE_PROJECT, None) -from azpy.constants import ENVAR_LY_PROJECT_PATH -_LY_PROJECT_PATH = Path(os.getenv(ENVAR_LY_PROJECT_PATH, None)).resolve() +from azpy.constants import ENVAR_O3DE_PROJECT_PATH +_O3DE_PROJECT_PATH = Path(os.getenv(ENVAR_O3DE_PROJECT_PATH, None)).resolve() from azpy.constants import ENVAR_DCCSI_SDK_PATH _DCCSI_SDK_PATH = Path(os.getenv(ENVAR_DCCSI_SDK_PATH, None)).resolve() # build some reuseable path parts -_PROJECT_ASSET_PATH = Path(_LY_PROJECT_PATH).resolve() -_PROJECT_ASSETS_PATH = Path(_LY_PROJECT_PATH, 'Materials').resolve() +_PROJECT_ASSET_PATH = Path(_O3DE_PROJECT_PATH).resolve() +_PROJECT_ASSETS_PATH = Path(_O3DE_PROJECT_PATH, 'Materials').resolve() # To Do: figure out a proper way to deal with Lumberyard game projects -_GEM_MATPLAY_PATH = Path(_LY_DEV, 'Gems', 'AtomContent', 'AtomMaterialPlayground').resolve() -_GEM_ROYALTYFREE = Path(_LY_DEV, 'Gems', 'AtomContent', 'RoyaltyFreeAssets').resolve() -_GEM_SUBSOURCELIBRARY = Path(_LY_DEV, 'Gems', 'AtomContent', 'SubstanceSourceLibrary').resolve() +_GEM_MATPLAY_PATH = Path(_O3DE_DEV, 'Gems', 'AtomContent', 'AtomMaterialPlayground').resolve() +_GEM_ROYALTYFREE = Path(_O3DE_DEV, 'Gems', 'AtomContent', 'RoyaltyFreeAssets').resolve() +_GEM_SUBSOURCELIBRARY = Path(_O3DE_DEV, 'Gems', 'AtomContent', 'SubstanceSourceLibrary').resolve() _SUB_LIBRARY_PATH = Path(_GEM_SUBSOURCELIBRARY, 'Assets', 'SubstanceSource', 'Library').resolve() # ^ This hard codes a bunch of known asset gems, again bad # To Do: figure out a proper way to scrap the gem registry from project @@ -150,9 +150,9 @@ _SUB_LIBRARY_PATH = Path(_GEM_SUBSOURCELIBRARY, 'Assets', 'SubstanceSource', 'Li # path to watcher script _WATCHER_SCRIPT_PATH = Path(_DCCSI_SDK_PATH, 'substance', 'builder', 'watchdog', '__init__.py').resolve() -_TEX_RNDR_PATH = Path(_LY_PROJECT_PATH, 'Materials', 'Substance').resolve() -_MAT_OUTPUT_PATH = Path(_LY_PROJECT_PATH, 'Materials', 'Substance').resolve() -_SBSAR_COOK_PATH = Path(_LY_PROJECT_PATH, 'Materials', 'Substance').resolve() +_TEX_RNDR_PATH = Path(_O3DE_PROJECT_PATH, 'Materials', 'Substance').resolve() +_MAT_OUTPUT_PATH = Path(_O3DE_PROJECT_PATH, 'Materials', 'Substance').resolve() +_SBSAR_COOK_PATH = Path(_O3DE_PROJECT_PATH, 'Materials', 'Substance').resolve() # ------------------------------------------------------------------------- @@ -171,7 +171,7 @@ class Window(QtWidgets.QDialog): # we should really init non-Qt stuff and set things up as properties if project_path is None: - self.project_path = str(_LY_PROJECT_PATH) + self.project_path = str(_O3DE_PROJECT_PATH) else: self.project_path = Path(project_path) @@ -213,7 +213,7 @@ class Window(QtWidgets.QDialog): self.matOutputPathComboBox = self.createComboBox(str(_MAT_OUTPUT_PATH)) # self.directoryComboBox = self.createComboBox(QtCore.QDir.currentPath()) - # I changed this to scan the _LY_PROJECT + # I changed this to scan the _O3DE_PROJECT # self.sbsarDirectory = self.return_1st_sbsar(Path(self.project_path, 'Assets')).resolve().parent self.sbsarDirectory = QtCore.QDir() self.sbsarDirectory.setCurrent(str(_PROJECT_ASSET_PATH)) @@ -672,13 +672,13 @@ class Window(QtWidgets.QDialog): # if you want relative paths here is a better way # first of all, assume we know the project we are in - #_LY_PROJECT_PATH + #_O3DE_PROJECT_PATH texture_output_path = Path(self.texRenderPathComboBox.currentText()).resolve() rel_tex_path = None for p in texture_output_path.parts: - if _LY_PROJECT == p: - index = texture_output_path.parts.index(_LY_PROJECT) + if _O3DE_PROJECT == p: + index = texture_output_path.parts.index(_O3DE_PROJECT) rel_tuple = texture_output_path.parts[index + 1:] rel_tex_path = Path(*list(rel_tuple)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbs_to_sbsar.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbs_to_sbsar.py index 4763bf430c..441110b8d4 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbs_to_sbsar.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbs_to_sbsar.py @@ -47,7 +47,7 @@ import pysbs.context as pysbs_context # ------------------------------------------------------------------------- # set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ @@ -68,10 +68,10 @@ _SYNTH_ENV_DICT = OrderedDict() _SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT) # grab a specific path from the base_env _PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH] -_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH] +_O3DE_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_O3DE_PROJECT_PATH] # build some reuseable path parts -_PATH_MOCK_ASSETS = Path(_LY_PROJECT_PATH, 'Assets').norm() +_PATH_MOCK_ASSETS = Path(_O3DE_PROJECT_PATH, 'Assets').norm() _PATH_MOCK_SUBLIB = Path(_PATH_MOCK_ASSETS, 'SubstanceSource').norm() _PATH_MOCK_SBS = Path(_PATH_MOCK_SUBLIB, 'sbs').norm() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_info.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_info.py index 2a26f13be2..9652b8990a 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_info.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_info.py @@ -47,7 +47,7 @@ import pysbs.context as pysbs_context # ------------------------------------------------------------------------- # set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ @@ -62,7 +62,7 @@ _LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- # global space debug flag -_G_DEBUG = os.getenv(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = os.getenv(ENVAR_DCCSI_GDEBUG, False) # global space debug flag _DCCSI_DEV_MODE = os.getenv(ENVAR_DCCSI_DEV_MODE, False) @@ -88,10 +88,10 @@ _SYNTH_ENV_DICT = OrderedDict() _SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT) # grab a specific path from the base_env _PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH] -_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH] +_O3DE_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_O3DE_PROJECT_PATH] # build some reuseable path parts -_PATH_MOCK_ASSETS = Path(_LY_PROJECT_PATH, 'Assets').norm() +_PATH_MOCK_ASSETS = Path(_O3DE_PROJECT_PATH, 'Assets').norm() _PATH_MOCK_SUBLIB = Path(_PATH_MOCK_ASSETS, 'SubstanceSource').norm() _PATH_MOCK_SBS = Path(_PATH_MOCK_SUBLIB, 'sbs').norm() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_render.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_render.py index 31e2c81a57..6285ea0335 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_render.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_render.py @@ -45,7 +45,7 @@ import pysbs.context as pysbs_context # ------------------------------------------------------------------------- # set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ @@ -66,10 +66,10 @@ _SYNTH_ENV_DICT = OrderedDict() _SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT) # grab a specific path from the base_env _PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH] -_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH] +_O3DE_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_O3DE_PROJECT_PATH] # build some reuseable path parts -_PATH_MOCK_ASSETS = Path(_LY_PROJECT_PATH, 'Assets').norm() +_PATH_MOCK_ASSETS = Path(_O3DE_PROJECT_PATH, 'Assets').norm() _PATH_MOCK_SUBLIB = Path(_PATH_MOCK_ASSETS, 'SubstanceSource').norm() _PATH_MOCK_SBS = Path(_PATH_MOCK_SUBLIB, 'sbs').norm() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_utils.py index 74293a8d77..8ffdf30e35 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/sbsar_utils.py @@ -28,7 +28,7 @@ from azpy.constants import ENVAR_DCCSI_DEV_MODE from dynaconf import settings from pathlib import Path -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, settings.DCCSI_GDEBUG) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, settings.DCCSI_GDEBUG) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, settings.DCCSI_DEV_MODE) _MODULENAME = 'DCCsi.SDK.substance.builder.sbsar_utils' @@ -190,15 +190,15 @@ if __name__ == "__main__": _SYNTH_ENV_DICT = synthetic_env.stash_env() from azpy.constants import ENVAR_DCCSIG_PATH - from azpy.constants import ENVAR_LY_PROJECT_PATH + from azpy.constants import ENVAR_O3DE_PROJECT_PATH # grab a specific path from the base_env _PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH] # use DCCsi as the project path for this test - _LY_PROJECT_PATH = _PATH_DCCSI + _O3DE_PROJECT_PATH = _PATH_DCCSI - _PROJECT_ASSETS_PATH = Path(_LY_PROJECT_PATH, 'Assets').resolve() + _PROJECT_ASSETS_PATH = Path(_O3DE_PROJECT_PATH, 'Assets').resolve() _PROJECT_MATERIALS_PATH = Path(_PROJECT_ASSETS_PATH, 'Materials').resolve() # this will combine two parts into a single path (object) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/substance_tools.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/substance_tools.py index af001902ed..0407a1db08 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/substance_tools.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/substance_tools.py @@ -46,7 +46,7 @@ import pysbs.context as pysbs_context # ------------------------------------------------------------------------- # set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ @@ -67,10 +67,10 @@ _SYNTH_ENV_DICT = OrderedDict() _SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT) # grab a specific path from the base_env _PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH] -_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH] +_O3DE_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_O3DE_PROJECT_PATH] # build some reuseable path parts -_PATH_MOCK_ASSETS = Path(_LY_PROJECT_PATH, 'Assets').norm() +_PATH_MOCK_ASSETS = Path(_O3DE_PROJECT_PATH, 'Assets').norm() _PATH_MOCK_SUBLIB = Path(_PATH_MOCK_ASSETS, 'SubstanceSource').norm() _PATH_MOCK_SBS = Path(_PATH_MOCK_SUBLIB, 'sbs').norm() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/watchdog/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/watchdog/__init__.py index d37a9cb5ec..35c970e711 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/watchdog/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Substance/builder/watchdog/__init__.py @@ -53,7 +53,7 @@ import pysbs.context as pysbs_context # ------------------------------------------------------------------------- # set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ @@ -71,8 +71,8 @@ _LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME})) from collections import OrderedDict _SYNTH_ENV_DICT = OrderedDict() _SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT) -_LY_DEV = _SYNTH_ENV_DICT[ENVAR_LY_DEV] -_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH] +_O3DE_DEV = _SYNTH_ENV_DICT[ENVAR_O3DE_DEV] +_O3DE_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_O3DE_PROJECT_PATH] # ------------------------------------------------------------------------- @@ -90,7 +90,7 @@ class MyHandler(PatternMatchingEventHandler): """ self.outputName = event.src_path.split(".sbsar")[0].split("/")[-1] self.outputCookPath = event.src_path.split(self.outputName) - self.outputRenderPath = Path(_LY_PROJECT_PATH, 'Assets', 'Textures', 'Substance').norm() + self.outputRenderPath = Path(_O3DE_PROJECT_PATH, 'Assets', 'Textures', 'Substance').norm() _LOGGER.debug(self.outputCookPath, self.outputName, self.outputRenderPath) pysbs_batch.sbsrender_info(input=event.src_path) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/3dsMax/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/3dsMax/stub new file mode 100644 index 0000000000..d365a5f2c9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/3dsMax/stub @@ -0,0 +1,9 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/AddOns/MaterialExporter/_init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/AddOns/MaterialExporter/_init__.py new file mode 100644 index 0000000000..d365a5f2c9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/AddOns/MaterialExporter/_init__.py @@ -0,0 +1,9 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/AddOns/MaterialExporter/main.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/AddOns/MaterialExporter/main.py new file mode 100644 index 0000000000..b08991f63e --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/AddOns/MaterialExporter/main.py @@ -0,0 +1,21 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- +# DCCsi\\Tools\\Blender\\AddOns\\MaterialExporter\\main.py + +""" A in Blender tool for exporting BRDF materials as O3DE Atom StandardPBR +""" + +########################################################################### +# Main Code Block, runs this script as main (testing) +# ------------------------------------------------------------------------- +if __name__ == '__main__': + """Run this file as main""" + + print('MaterialExporter.main() not implemented') \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/config.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/config.py new file mode 100644 index 0000000000..b0f67b1034 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/config.py @@ -0,0 +1,11 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- + +print('Not Implemented') \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.dev/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/settings.json similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.dev/stub rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/settings.json diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/start.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/start.py new file mode 100644 index 0000000000..b0f67b1034 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Blender/start.py @@ -0,0 +1,11 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- + +print('Not Implemented') \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Houdini/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Houdini/stub new file mode 100644 index 0000000000..d365a5f2c9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Houdini/stub @@ -0,0 +1,9 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Marmoset/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Marmoset/stub new file mode 100644 index 0000000000..d365a5f2c9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Marmoset/stub @@ -0,0 +1,9 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/Prefs/icons/MayaStartupImage.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/Prefs/icons/MayaStartupImage.png new file mode 100644 index 0000000000..8f5d8290a8 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/Prefs/icons/MayaStartupImage.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:083ab199e273431963fc5f80bb80b7d1b1d428f7b12a5180d7199a7431291982 +size 311865 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/plugins/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/plugins/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/scripts/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/scripts/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/siteDir/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/2020/siteDir/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Help/HelpStub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Help/HelpStub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Projects/default/workspace.mel b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Projects/default/workspace.mel new file mode 100644 index 0000000000..c6474c4414 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Projects/default/workspace.mel @@ -0,0 +1,36 @@ +//Maya 2016 Project Definition + +workspace -fr "fluidCache" "cache/nCache/fluid"; +workspace -fr "images" "images"; +workspace -fr "offlineEdit" "scenes/edits"; +workspace -fr "furShadowMap" "renderData/fur/furShadowMap"; +workspace -fr "iprImages" "renderData/iprImages"; +workspace -fr "renderData" "renderData"; +workspace -fr "scripts" "scripts"; +workspace -fr "fileCache" "cache/nCache"; +workspace -fr "eps" "data"; +workspace -fr "shaders" "renderData/shaders"; +workspace -fr "3dPaintTextures" "sourceimages/3dPaintTextures"; +workspace -fr "translatorData" "data"; +workspace -fr "mel" "scripts"; +workspace -fr "furFiles" "renderData/fur/furFiles"; +workspace -fr "OBJ" "data"; +workspace -fr "particles" "cache/particles"; +workspace -fr "scene" "scenes"; +workspace -fr "furEqualMap" "renderData/fur/furEqualMap"; +workspace -fr "sourceImages" "sourceimages"; +workspace -fr "furImages" "renderData/fur/furImages"; +workspace -fr "clips" "clips"; +workspace -fr "depth" "renderData/depth"; +workspace -fr "movie" "movies"; +workspace -fr "audio" "sound"; +workspace -fr "bifrostCache" "cache/bifrost"; +workspace -fr "autoSave" "autosave"; +workspace -fr "mayaAscii" "scenes"; +workspace -fr "move" "data"; +workspace -fr "sound" "sound"; +workspace -fr "diskCache" "data"; +workspace -fr "illustrator" "data"; +workspace -fr "mayaBinary" "scenes"; +workspace -fr "templates" "assets"; +workspace -fr "furAttrMap" "renderData/fur/furAttrMap"; diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/IBLbaker_brdf_lut.dds b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/IBLbaker_brdf_lut.dds new file mode 100644 index 0000000000..81e62781b4 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/IBLbaker_brdf_lut.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:646b6d93b2c672bbbbcb46af1bfcaf26ca37c8a0c2b218989b145417bb6b7c93 +size 262272 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_lighting_diffuse.dds b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_lighting_diffuse.dds new file mode 100644 index 0000000000..980477af4b --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_lighting_diffuse.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86209db0389b152c709d529e9d5705219b44bfbf712a26788a5c2ab0adc0c373 +size 98452 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_lighting_specular.dds b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_lighting_specular.dds new file mode 100644 index 0000000000..fd6e1f99fc --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_lighting_specular.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd95898a04b81b80b095dbef34523f3b70a8c14bc9f82116f732ab648f25658b +size 2096788 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_skybox.dds b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_skybox.dds new file mode 100644 index 0000000000..304aaa0a1f --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/PBR/ly_cubempas/artist_workshop_4k_skybox.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a85a15d5c60102f414b4ad03604dbd1c78e3d1c1fe445f60db158b2c069f792d +size 25165972 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/SourceImages/MayaStartupImage.psd b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/SourceImages/MayaStartupImage.psd new file mode 100644 index 0000000000..8d630620ca --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/SourceImages/MayaStartupImage.psd @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1318f73ca32ec56dfeb0233679504f6fc723081f0cafa8e7e2d0517b878defd1 +size 2000893 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/SourceImages/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/SourceImages/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/workspace.mel b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/workspace.mel new file mode 100644 index 0000000000..2cbf02f1aa --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Resources/workspace.mel @@ -0,0 +1,69 @@ +//Maya 2016 Project Definition + +workspace -fr "fluidCache" "mayaData/cache/nCache/fluid"; +workspace -fr "JT_DC" "mayaData/Trans/JT"; +workspace -fr "CATIAV4_DC" "mayaData/Trans/CATIAV4"; +workspace -fr "images" "mayaData/Images"; +workspace -fr "offlineEdit" "ArtSource/Maya"; +workspace -fr "STEP_DC" "mayaData/Trans/STEP"; +workspace -fr "furShadowMap" "mayaData/renderData/fur/furShadowMap"; +workspace -fr "SPF_DCE" "mayaData/Trans/SPF"; +workspace -fr "scripts" "mayaData/Scripts"; +workspace -fr "CATIAV5_DC" "mayaData/Trans/CATIAV5"; +workspace -fr "DAE_FBX" "mayaData/Trans/DAE_FBX"; +workspace -fr "shaders" "mayaData/renderData/shaders"; +workspace -fr "furFiles" "mayaData/renderData/fur/furFiles"; +workspace -fr "OBJ" "mayaData/OBJ"; +workspace -fr "FBX export" "mayaData/Trans/FBX_export"; +workspace -fr "furEqualMap" "mayaData/renderData/fur/furEqualMap"; +workspace -fr "Autodesk Packet File" "mayaData/Trans"; +workspace -fr "DAE_FBX export" "mayaData/Trans/DAE_FBX_export"; +workspace -fr "SPF_DC" "mayaData/Trans/SPF"; +workspace -fr "movie" "mayaData/movies"; +workspace -fr "DXF_DCE" "mayaData/Trans/DXF"; +workspace -fr "move" "mayaData/move"; +workspace -fr "mayaAscii" "ArtSource"; +workspace -fr "autoSave" "mayaData"; +workspace -fr "sound" "mayaData/Sounds"; +workspace -fr "mayaBinary" "ArtSource"; +workspace -fr "ZPR_DCE" "mayaData/Trans/ZPR"; +workspace -fr "STL_DCE" "mayaData/Trans/STL"; +workspace -fr "iprImages" "mayaData/renderData/iprImages"; +workspace -fr "PhysX" "mayaData/Trans/Physx"; +workspace -fr "DXF_DC" "mayaData/Trans/DXF"; +workspace -fr "FBX" "mayaData/Trans/FBX"; +workspace -fr "studioImport" "mayaData/Trans"; +workspace -fr "UG_DCE" "mayaData/Trans/UG"; +workspace -fr "renderData" "mayaData/renderData"; +workspace -fr "fileCache" "mayaData/cache/nCache"; +workspace -fr "eps" "mayaData/EPS"; +workspace -fr "Fbx" "Objects"; +workspace -fr "3dPaintTextures" "mayaData/images/3dPaintTextures"; +workspace -fr "translatorData" "mayaData"; +workspace -fr "mel" "mayaData/Scripts/Mel"; +workspace -fr "particles" "mayaData/cache/particles"; +workspace -fr "IV_DC" "mayaData/Trans/IV"; +workspace -fr "scene" "ArtSource"; +workspace -fr "DWG_DCE" "mayaData/Trans/DWG"; +workspace -fr "MayaCryExport" "Objects"; +workspace -fr "sourceImages" "ArtSource/Textures"; +workspace -fr "furImages" "mayaData/renderData/fur/furImages"; +workspace -fr "clips" "mayaData/clips"; +workspace -fr "PTC_DC" "mayaData/Trans/PTC"; +workspace -fr "STL_DC" "mayaData/Trans/STL"; +workspace -fr "IPT_DC" "mayaData/Trans/IPT"; +workspace -fr "CSB_DC" "mayaData/Trans/CSB"; +workspace -fr "SW_DC" "mayaData/Trans/SW"; +workspace -fr "depth" "mayaData/renderData/depth"; +workspace -fr "audio" "mayaData/Sounds"; +workspace -fr "DWG_DC" "mayaData/Trans/DWG"; +workspace -fr "bifrostCache" "mayaData/cache/bifrost"; +workspace -fr "IGES_DCE" "mayaData/Trans/IGES"; +workspace -fr "Alembic" "mayaData/Trans/Alembic"; +workspace -fr "illustrator" "mayaData/AI"; +workspace -fr "diskCache" "mayaData"; +workspace -fr "UG_DC" "mayaData/Trans/UG"; +workspace -fr "templates" "mayaData/assets"; +workspace -fr "OBJexport" "mayaData/Trans/Obj"; +workspace -fr "furAttrMap" "mayaData/renderData/fur/furAttrMap"; +workspace -fr "IGES_DC" "mayaData/Trans/IGES"; diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Mel/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Mel/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Python/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Python/stub new file mode 100644 index 0000000000..d365a5f2c9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Python/stub @@ -0,0 +1,9 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/max_materials.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Python/stub_util.py old mode 100755 new mode 100644 similarity index 51% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/max_materials.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Python/stub_util.py index a67c39910e..1dc43d8485 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/PythonTools/DCC_Material_Converter/max_materials.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/Python/stub_util.py @@ -1,18 +1,12 @@ +# coding:utf-8 +#!/usr/bin/python +# # Copyright (c) Contributors to the Open 3D Engine Project. # For complete copyright and license terms please see the LICENSE at the root of this distribution. # # SPDX-License-Identifier: Apache-2.0 OR MIT # # +# ------------------------------------------------------------------------- - -import MaxPlus -import sys - - -def get_material_information(): - for mesh_object in MaxPlus.Core.GetRootNode().Children: - print('Object---> {}'.format(mesh_object)) - - -get_material_information() +print('Not Implemented') \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/constants.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/constants.py new file mode 100644 index 0000000000..6ed8297ab9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/constants.py @@ -0,0 +1,32 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- + +""" +Module Documentation: + DccScriptingInterface:: Tools//maya//scripts//constants.py + +This module is mainly a bunch of commony used constants, and default strings +So we can make an update here once that is used elsewhere +""" +# ------------------------------------------------------------------------- +# built-ins +# none + +# -- External Python modules + +# -- DCCsi Extension Modules +#import azpy + +# -- maya imports +# none +# ------------------------------------------------------------------------- +OBJ_DCCSI_MAINMENU = 'O3deDCCsiMainMenu' +TAG_DCCSI_MAINMENU = 'DCCsi (O3DE:Atom)' diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_callbacks.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_callbacks.py new file mode 100644 index 0000000000..4a2b756a5e --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_callbacks.py @@ -0,0 +1,201 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- +""" +Module Documentation: + DccScriptingInterface:: SDK//maya//scripts//set_callbacks.py + +This module manages a set of predefined callbacks for maya +""" +# ------------------------------------------------------------------------- +# -- Standard Python modules +import os +import sys +import logging as _logging +# -- External Python modules +from box import Box +# maya imports +import maya.cmds as mc +import maya.api.OpenMaya as om +# -- DCCsi Extension Modules +from azpy.constants import * +import azpy.dcc.maya +azpy.dcc.maya.init() # <-- should have already run? +import azpy.dcc.maya.callbacks.event_callback_handler as azEvCbH +import azpy.dcc.maya.callbacks.node_message_callback_handler as azNdMsH +# Node Message Callback Setup +import azpy.dcc.maya.callbacks.on_shader_rename as oSR +from set_defaults import set_defaults +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +from azpy.env_bool import env_bool +from azpy.constants import ENVAR_DCCSI_GDEBUG +from azpy.constants import ENVAR_DCCSI_DEV_MODE + +# global space +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, True) +_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, True) + +_MODULENAME = r'DCCsi.SDK.Maya.Scripts.set_callbacks' + +_LOGGER = azpy.initialize_logger(_MODULENAME, default_log_level=int(20)) +_LOGGER.debug('Invoking:: {0}.'.format({_MODULENAME})) +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# global scope callbacks, set up set and initialize all to None +# To Do: should callback initialization use data-driven settings? +# To Do: should we move callback initialization to a sub-module? +# To Do: move the callback key like 'NewSceneOpened' here (instead of None) +# ^ this would provide ability to loop through and replace key with CB object + +_G_CALLBACKS = Box(box_dots=True) # global scope container +_G_PRIMEKEY = 'DCCsi_callbacks' +_G_CALLBACKS[_G_PRIMEKEY] = True # required prime key + + +# ------------------------------------------------------------------------- +def init_callbacks(_callbacks=_G_CALLBACKS): + # store as a dict (Box is a fancy dict) + _callbacks[_G_PRIMEKEY] = True # required prime key + + # signature dict['callback key'] = ('CallBack'(type), func, callbackObj) + _callbacks['on_new_file'] = ['NewSceneOpened', set_defaults, None] + _callbacks['new_scene_fix_paths'] = ['NewSceneOpened', install_fix_paths, None] + _callbacks['post_scene_fix_paths'] = ['PostSceneRead', install_fix_paths, None] + _callbacks['workspace_changed'] = ['workspaceChanged', update_workspace, None] + _callbacks['quit_app'] = ['quitApplication', uninstall_callbacks, None] + + # nodeMessage style callbacks + # fire a function + _func_00 = oSR.on_shader_rename_rename_shading_group + # using a nodeMessage callback trigger + _cb_00 = om.MNodeMessage.addNameChangedCallback + # all nodeMessage type callbacks can use 'nodeMessageType' key + _callbacks['shader_rename'] = ['nodeMessageType', (_func_00, _cb_00), None] + + return _callbacks +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +def uninstall_callbacks(): + """Bulk uninstalls hte globally defined set of callbacks: + _G_callbacks""" + + global _G_CALLBACKS + + _LOGGER.debug('uninstall_callbacks() fired') + + for key, value in _G_CALLBACKS: + if value[2] is not None: # have a cb + value[2].uninstall() # so uninstall it + else: + _LOGGER.warning('No callback in: key {0}, value:{1}' + ''.format(key, value)) + _G_CALLBACKS = None + _LOGGER.info('DCCSI CALLBACKS UNINSTALLED ... EXITING') + return _G_CALLBACKS +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +def install_callbacks(_callbacks=_G_CALLBACKS): + """Bulk installs the globally defined set of callbacks: + _G_callbacks""" + + _LOGGER.debug('install_callback_set() fired') + + _callbacks = init_callbacks(_callbacks) + + # we initialized the box with this so pop it + if 'box_dots' in _callbacks: + _callbacks.pop('box_dots') + + # don't pass anything but carefully considered dict + if _G_PRIMEKEY in _callbacks: + _primekey = _callbacks.pop(_G_PRIMEKEY) + else: + _LOGGER.error('No prime key, use a correct dictionary') + #To Do: implement error handling and return codes + return _callbacks[None] + + for key, value in _G_CALLBACKS.items(): + # we popped the prime key should the rest should be safe + if value[0] != 'nodeMessageType': + # set callback up + _cb = azEvCbH.EventCallbackHandler(value[0], + value[1]) + # ^ installs by default + # stash it back into managed dict + value[2] = _cb + # value[2].install() + else: + # set up callback, value[1] should be tupple(func, trigger) + _cb = azNdMsH.NodeMessageCallbackHandler(value[1][0], + value[1][1]) + # ^ installs by default + # stash it back into managed dict + value[2] = _cb + # value[2].install() + + return _callbacks +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +def install_fix_paths(foo=None): + """Installs and triggers a fix paths module. + This can repair broken reference paths in shaders""" + global _fix_paths + _fix_paths = None + + _LOGGER.debug('install_fix_paths() fired') + + # if we don't have it already, this function is potentially triggered + # by a callback, so we don't need to keep importing it. + try: + _fix_paths + reload(_fix_paths) + except Exception as e: + try: + import fixPaths as _fix_paths + except Exception as e: + # To Do: not implemented yet + _LOGGER.warning('NOT IMPLEMENTED: {0}'.format(e)) + + # if we have it, use it + if _fix_paths: + return _fix_paths.main() + else: + # To Do: implement error handling and return codes + return 1 +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +def update_workspace(foo=None): + """Forces and update of the workspace (workspace.mel)""" + _LOGGER.debug('update_workspace() fired') + result = mc.workspace(update=True) + return result +# ------------------------------------------------------------------------- + +# install and init callbacks on an import obj +_G_CALLBACKS = install_callbacks(_G_CALLBACKS) + +# ========================================================================== +# Module Tests +#========================================================================== +if __name__ == '__main__': + _G_CALLBACKS = install_callbacks(_G_CALLBACKS) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_defaults.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_defaults.py new file mode 100644 index 0000000000..639b03c297 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_defaults.py @@ -0,0 +1,93 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- +""" +Module Documentation: + DccScriptingInterface:: SDK//maya//scripts//set_pref_defaults.py + +This module manages a predefined set of prefs for maya +""" +# ------------------------------------------------------------------------- +# -- Standard Python modules +import os +import sys +# -- External Python modules + +# -- DCCsi Extension Modules +import azpy +from azpy.constants import * + +# -- maya imports +import maya.cmds as mc +import maya.mel as mm + +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +from azpy.env_bool import env_bool +from azpy.constants import ENVAR_DCCSI_GDEBUG +from azpy.constants import ENVAR_DCCSI_DEV_MODE + +# global space +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) + +_MODULENAME = r'DCCsi.SDK.Maya.Scripts.set_defaults' + +_LOGGER = azpy.initialize_logger(_MODULENAME, default_log_level=int(20)) +_LOGGER.debug('Invoking:: {0}.'.format({_MODULENAME})) +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +def set_defaults(units='meter'): + """This method will make defined settings changes to Maya prefs, + to better configure maya to work with Lumberyard""" + # To Do: make this data-driven env/settings, game teams should be able + # to opt out and/or set their prefered configuration. + + _LOGGER.debug('set_defaults_lumberyard() fired') + + # set up default units ... this should be moved to bootstrap config + _LOGGER.info('Default, 1 Linear Game Unit in Lumberyard == 1 Meter' + ' in Maya content. Setting default linear units to Meters' + ' (user can change to other units in the preferences)') + + result = mc.currentUnit(linear=units) + + # set up grid defaults + _LOGGER.info('Setting Grid defaults, to match default unit scale.' + '(user can change grid config manually') + try: + mc.grid(size=32, spacing=1, divisions=10) + except Exception as e: + _LOGGER.warning('{0}'.format(e)) + + # viewFit + _LOGGER.info('Changing default mc.viewFit') + try: + mc.viewFit() + except Exception as e: + _LOGGER.warning('{0}'.format(e)) + + # some mel commands + _LOGGER.info('Changing sersp camera clipping planes') + try: + mm.eval(str(r'setAttr "perspShape.nearClipPlane" 0.01;')) + mm.eval(str(r'setAttr "perspShape.farClipPlane" 1000;')) + except Exception as e: + _LOGGER.warning('{0}'.format(e)) + + # set up fixPaths + _LOGGER.info('~ Setting up fixPaths in default scene') + + return 0 +# ------------------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_menu.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_menu.py new file mode 100644 index 0000000000..e417324d0b --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_menu.py @@ -0,0 +1,88 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- +""" +Module Documentation: + DccScriptingInterface:: SDK//maya//scripts//set_menu.py + +This module creates and manages a DCCsi mainmenu +""" +# ------------------------------------------------------------------------- +# -- Standard Python modules +# none + +# -- External Python modules +# none + +# -- DCCsi Extension Modules +import azpy +from constants import OBJ_DCCSI_MAINMENU +from constants import TAG_DCCSI_MAINMENU + +# -- maya imports +import pymel.core as pm +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +from azpy.env_bool import env_bool +from azpy.constants import ENVAR_DCCSI_GDEBUG +from azpy.constants import ENVAR_DCCSI_DEV_MODE + +# global space +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) + +_MODULENAME = r'DCCsi.SDK.Maya.Scripts.set_menu' + +_LOGGER = azpy.initialize_logger(_MODULENAME, default_log_level=int(20)) +_LOGGER.debug('Invoking:: {0}.'.format({_MODULENAME})) +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +def menu_cmd_test(): + _LOGGER.info('test_func(), is TESTING main menu') + return +# ------------------------------------------------------------------------- + +# ------------------------------------------------------------------------- +def set_main_menu(obj_name=OBJ_DCCSI_MAINMENU, label=TAG_DCCSI_MAINMENU): + _main_window = pm.language.melGlobals['gMainWindow'] + + _menu_obj = obj_name + _menu_label = label + + # check if it already exists and remove (so we don't duplicate) + if pm.menu(_menu_obj, label=_menu_label, exists=True, parent=_main_window): + pm.deleteUI(pm.menu(_menu_obj, e=True, deleteAllItems=True)) + + # create the main menu object + _custom_tools_menu = pm.menu(_menu_obj, + label=_menu_label, + parent=_main_window, + tearOff=True) + + # make a dummpy sub-menu + pm.menuItem(label='Menu Item Stub', + subMenu=True, + parent=_custom_tools_menu, + tearOff=True) + + # make a dummy menu item to test + pm.menuItem(label='Test', command=pm.Callback(menu_cmd_test)) + return _custom_tools_menu + +# ========================================================================== +# Run as LICENSE +#========================================================================== +if __name__ == '__main__': + + _custom_menu = set_main_menu() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_shelf.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_shelf.py new file mode 100644 index 0000000000..1bee05afe8 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/set_shelf.py @@ -0,0 +1,130 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- +""" +Module Documentation: + DccScriptingInterface:: SDK//maya//scripts//set_shelf.py + +This module manages a custom shelf in maya for the DCCsi +Reference: https://gist.github.com/vshotarov/1c3176fe9e38dcaadd1e56c2f15c95d9 +""" +# ------------------------------------------------------------------------- +# -- Standard Python modules +# none + +# -- External Python modules +# none + +# -- DCCsi Extension Modules +# none + +# -- Maya Extension Modules +import maya.cmds as mc +# ------------------------------------------------------------------------- + +def _null(*args): + pass + +# ------------------------------------------------------------------------- +class customShelf(_Custom_Shelf): + '''This is an example shelf.''' + + def build(self): + self.add_button(label="button1") + self.add_button("button2") + self.add_button("popup") + p = mc.popupMenu(b=1) + self.add_menu_item(p, "popupMenuItem1") + self.add_menu_item(p, "popupMenuItem2") + sub = self.add_submenu(p, "subMenuLevel1") + self.add_menu_item(sub, "subMenuLevel1Item1") + sub2 = self.add_submenu(sub, "subMenuLevel2") + self.add_menu_item(sub2, "subMenuLevel2Item1") + self.add_menu_item(sub2, "subMenuLevel2Item2") + self.add_menu_item(sub, "subMenuLevel1Item2") + self.add_menu_item(p, "popupMenuItem3") + self.add_button("button3") +# ------------------------------------------------------------------------- + + + +class _Custom_Shelf(): + '''A simple class to build custom shelves in maya. + The build method is empty and an inheriting class should override''' + + def __init__(self, name="DCCsi", icon_path=""): + self._name = name + + self._icon_path = icon_path + + self._label_background_color = (0, 0, 0, 0) + self._label_colour = (.9, .9, .9) + + self._clean_old_shlef() + + mc.setParent(self._name) + + self.build() + + def build(self): + '''Override this method in custom class. + Otherwise, nothing is added to the shelf.''' + pass + + def add_button(self, + label='', + icon="commandButton.png", + command=_null, + doubleCommand=_null): + '''Adds a shelf button with the specified label, + command, double click command and image.''' + mc.setParent(self._name) + if icon: + icon = self._icon_path + icon + mc.shelfButton(width=37, height=37, + image=icon, + label=label, + command=command, + doubleClickCommand=doubleCommand, + imageOverlayLabel=label, + overlayLabelBackColor=self._label_background_color, + overlayLabelColor=self._label_colour) + + def add_menu_item(self, parent, label, command=_null, icon=""): + '''Adds a shelf button with the specified label, + command, double click command and image.''' + if icon: + icon = self._icon_path + icon + return mc.menuItem(p=parent, l=label, c=command, i="") + + def add_submenu(self, parent, label, icon=None): + '''Adds a sub menu item with the specified label and icon + to the specified parent popup menu.''' + if icon: + icon = self._icon_path + icon + return mc.menuItem(p=parent, l=label, i=icon, subMenu=1) + + def _clean_old_shlef(self): + '''Checks if the shelf exists and empties it if it does or + creates it if it does not.''' + if mc.shelfLayout(self._name, ex=1): + if mc.shelfLayout(self._name, q=1, ca=1): + for each in mc.shelfLayout(self._name, q=1, ca=1): + mc.deleteUI(each) + else: + mc.shelfLayout(self._name, p="ShelfLayout") + + +# ========================================================================== +# Module Tests +# ========================================================================== +if __name__ == '__main__': + customShelf() + pass diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/userSetup.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/userSetup.py new file mode 100644 index 0000000000..c68eb8d256 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Scripts/userSetup.py @@ -0,0 +1,325 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- +from __future__ import unicode_literals + +""" +This module fullfils the maya bootstrap pattern as described in their docs +https://tinyurl.com/y2aoz8es + +Pattern is similar to Lumberyard Editor\\Scripts\\bootstrap.py + +For now the proper way to initiate Maya boostrapping the DCCsi, is to use +the provided env and launcher bat files. + +If you are developing for the DCCsi you can use this launcher to start Maya: +DccScriptingInterface\\Launchers\\Windows\\Launch_Maya_2020.bat" + +To Do: ATOM-5861 +""" +__project__ = 'DccScriptingInterface' + +# it is really hard to debug userSetup bootstrapping +# this enables some rudimentary logging for debugging +_BOOT_INFO = True + +# ------------------------------------------------------------------------- +# built in's +import os +import sys +import site +import inspect +import traceback +import logging as _logging + +# -- DCCsi Extension Modules +import azpy +from azpy.constants import * +from azpy.env_bool import env_bool +from azpy.constants import ENVAR_DCCSI_GDEBUG +from azpy.constants import ENVAR_DCCSI_DEV_MODE + +# To Do: needs to be updated to use dynaconf and config.py +from azpy.env_base import _BASE_ENVVAR_DICT + +# -- maya imports +import maya.cmds as cmds +import maya.mel as mel +#from pymel.all import * +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# global space +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_DEV_MODE = True # force true for debugger testing + +_ORG_TAG = r'Amazon::Lumberyard' +_APP_TAG = r'DCCsi' +_TOOL_TAG = r'SDK.Maya.Scripts.userSetup' +_TYPE_TAG = r'entrypoint' # bootstrap + +_MODULENAME = str('{0}.{1}'.format(_APP_TAG, _TOOL_TAG)) + +_LOGGER = azpy.initialize_logger(_MODULENAME, default_log_level=int(20)) +_LOGGER.info('Initializing: {0}.'.format({_MODULENAME})) +_LOGGER.info('DCCSI_GDEBUG: {0}.'.format({_DCCSI_GDEBUG})) +_LOGGER.info('DCCSI_DEV_MODE: {0}.'.format({_DCCSI_DEV_MODE})) + +# flag to turn off setting up callbacks, until they are fully implemented +# To Do: consider making it a settings option to define and enable/disable +_G_LOAD_CALLBACKS = True # couple bugs, couple NOT IMPLEMENTED +_LOGGER.info('DCCSI_MAYA_SET_CALLBACKS: {0}.'.format({_G_LOAD_CALLBACKS})) + +# early attach WingIDE debugger (can refactor to include other IDEs later) +if _DCCSI_DEV_MODE: + from azpy.test.entry_test import connect_wing + foo = connect_wing() +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# To Do REMOVE this block and replace with dev module +# debug prints, To Do: this should be moved to bootstrap config +#_G_DEBUGGER = os.getenv(ENVAR_DCCSI_GDEBUGGER, "WING") + +#if _DCCSI_DEV_MODE: + #if _G_DEBUGGER == "WING": + #_LOGGER.info('{0}'.format('-' * 74)) + #_LOGGER.info('Developer Debug Mode: {0}, Basic debugger: {1}'.format(_G_DEBUG, _G_DEBUGGER)) + #try: + #_LOGGER.info('Attempting to start basic WING debugger') + #import azpy.lmbr.test + + #_LOGGER.info('Package Imported: azpy.test') + #ouput = azpy.entry_test.main(verbose=False, + #connectDebugger=True, + #returnOuput=_G_DEBUG) + #_LOGGER.info(ouput) + #pass + #except Exception as e: + #_LOGGER.info("Error: azpy.test, entry_test (didn't perform)") + #_LOGGER.info("Exception: {0}".format(e)) + #pass + #elif _G_DEBUGGER == "PYCHARM": + ## https://github.com/juggernate/PyCharm-Maya-Debugging + #_LOGGER.info('{0}'.format('-' * 74)) + #_LOGGER.info('Developer Debug Mode: {0}, Basic debugger: {1}'.format(_G_DEBUG, _G_DEBUGGER)) + #sys.path.append('C:\Program Files\JetBrains\PyCharm 2019.1.3\debug-eggs\pydevd-pycharm.egg') + #try: + #_LOGGER.info('Attempting to start basic PYCHARM debugger') + ## Inside Maya Python Console (Tip: add to a shelf button for quick access) + #import pydevd + + #_LOGGER.info('Package Imported: pydevd') + #pydevd.settrace('localhost', port=7720, suspend=False) + #_LOGGER.info('PYCHARM Debugger Attach Success!!!') + ## To disconnect run: + ## pydevd.stoptrace() + #pass + #except Exception as e: + #_LOGGER.info("Error: pydevd.settrace (didn't perform)") + #_LOGGER.info("Exception: {0}".format(e)) + #pass + #else: + #pass +## ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# validate access to the DCCsi and it's Lib site-packages +# bootstrap site-packages by version +from azpy.constants import PATH_DCCSI_PYTHON_LIB_PATH + +try: + os.path.exists(PATH_DCCSI_PYTHON_LIB_PATH) + site.addsitedir(PATH_DCCSI_PYTHON_LIB_PATH) + _LOGGER.info('azpy 3rdPary site-packages: is: {0}'.format(PATH_DCCSI_PYTHON_LIB_PATH)) +except Exception as e: + _LOGGER.error('ERROR: {0}, {1}'.format(e, PATH_DCCSI_PYTHON_LIB_PATH)) + raise e + +# 3rdparty +from unipath import Path +from box import Box +# ------------------------------------------------------------------------- + +# ------------------------------------------------------------------------- +# Maya is frozen +#_MODULE_PATH = Path(__file__) +# https://tinyurl.com/y49t3zzn +# module path when frozen +_MODULE_FILEPATH = os.path.abspath(inspect.getfile(inspect.currentframe())) +_MODULE_PATH = os.path.dirname(_MODULE_FILEPATH) +if _BOOT_INFO: + _LOGGER.debug('Boot: CWD: {}'.format(os.getcwd())) + _LOGGER.debug('Frozen: _MODULE_FILEPATH: {}'.format(_MODULE_FILEPATH)) + _LOGGER.debug('Frozen: _MODULE_PATH: {}'.format(_MODULE_PATH)) + _LOGGER.debug('Module __name__: {}'.format(__name__)) +# root: INFO: Module __name__: __main__ + +_LOGGER.info('_MODULENAME: {}'.format(_MODULENAME)) + +# ------------------------------------------------------------------------- +# check some env var tags (fail if no, likely means no proper code access) +_STR_ERROR_ENVAR = "Envar 'key' does not exist in base_env: {0}" +_DCCSI_TOOLS_PATH = None +# To Do: needs to be updated to use dynaconf and config.py +try: + _DCCSI_TOOLS_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSI_TOOLS_PATH] +except Exception as e: + _LOGGER.critical(_STR_ERROR_ENVAR.format(_BASE_ENVVAR_DICT[ENVAR_DCCSI_TOOLS_PATH])) + +_O3DE_PROJECT_PATH = None +try: + _O3DE_PROJECT_PATH = _BASE_ENVVAR_DICT[ENVAR_O3DE_PROJECT_PATH] +except Exception as e: + _LOGGER.critical(_STR_ERROR_ENVAR.format(_BASE_ENVVAR_DICT[ENVAR_O3DE_PROJECT_PATH])) + +# check some env var tags (fail if no, likely means no proper code access) +_O3DE_DEV = _BASE_ENVVAR_DICT[ENVAR_O3DE_DEV] +_O3DE_DCCSIG_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSIG_PATH] +_O3DE_DCCSI_LOG_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSI_LOG_PATH] +_O3DE_AZPY_PATH = _BASE_ENVVAR_DICT[ENVAR_DCCSI_AZPY_PATH] +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# To Do: implement data driven config +# Currently not used, but will be where we store the ordered dict +# which is parsed from the project bootstrapping config files. +_G_app_config = {} + +# global scope maya callbacks container +_G_callbacks = Box(box_dots=True) # global scope container + +# used to store fixPaths in the global scope +_fix_paths = None +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# add appropriate common tools paths to the maya environment variables +def startup(): + """Early starup execution before mayautils.executeDeferred(). + Some things like UI and plugins should be defered to avoid failure""" + _LOGGER.info('startup() fired') + + # get known paths + _KNOWN_PATHS = site._init_pathinfo() + + if os.path.isdir(_DCCSI_TOOLS_PATH): + site.addsitedir(_DCCSI_TOOLS_PATH, _KNOWN_PATHS) + try: + import azpy.test + _LOGGER.info('SUCCESS, import azpy.test') + except Exception as e: + _LOGGER.warning('startup(), could not import azpy.test') + + _LOGGER.info('startup(), COMPLETE') + return 0 +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# verify Shared\Python exists and add it as a site dir. Begin imports and config. +def post_startup(): + """Allows for a defered execution startup sequence""" + + _LOGGER.info('post_startup() fired') + + # plugins, To Do: these should be moved to bootstrapping config + try: + maya.cmds.loadPlugin("dx11Shader") + except Exception as e: + _LOGGER.error(e) # not a hard failure + + # Lumberyard DCCsi environment ready or error out. + try: + import azpy.dcc.maya + _LOGGER.info('Python module imported: azpy.dcc.maya') + except Exception as e: + _LOGGER.error(e) + _LOGGER.error(traceback.print_exc()) + return 1 + + # Dccsi azpy maya ready or error out. + try: + azpy.dcc.maya.init() + _LOGGER.info('SUCCESS, azpy.dcc.maya.init(), code accessible.') + except Exception as e: + _LOGGER.error(e) + _LOGGER.error(traceback.print_exc()) + return 1 + + # callbacks, To Do: these should also be moved to the bootstrapping config + # Defered startup after the Ui is running. + _G_CALLBACKS = Box(box_dots=True) # this just ensures a global scope container + if _G_LOAD_CALLBACKS: + from set_callbacks import _G_CALLBACKS + # ^ need to hold on to this as the install repopulate set + + # this ensures the fixPaths callback is loaded + # even when the other global callbacks are disabled + from set_callbacks import install_fix_paths + install_fix_paths() + + # set the project workspace + #_O3DE_PROJECT_PATH = _BASE_ENVVAR_DICT[ENVAR_O3DE_PROJECT_PATH] + _project_workspace = os.path.join(_O3DE_PROJECT_PATH, TAG_MAYA_WORKSPACE) + if os.path.isfile(_project_workspace): + try: + # load workspace + maya.cmds.workspace(_O3DE_PROJECT_PATH, openWorkspace=True) + _LOGGER.info('Loaded workspace file: {0}'.format(_project_workspace)) + maya.cmds.workspace(_O3DE_PROJECT_PATH, update=True) + except Exception as e: + _LOGGER.error(e) + else: + _LOGGER.warning('Workspace file not found: {1}'.format(_O3DE_PROJECT_PATH)) + + # Set up Lumberyard, maya default setting + from set_defaults import set_defaults + set_defaults() + + # Setup UI tools + if not maya.cmds.about(batch=True): + _LOGGER.info('Add UI dependent tools') + # wrap in a try, because we haven't implmented it yet + try: + mel.eval(str(r'source "{}"'.format(TAG_O3DE_DCC_MAYA_MEL))) + except Exception as e: + _LOGGER.error(e) + + # manage custom menu in a sub-module + from set_menu import set_main_menu + set_main_menu() + + # To Do: manage custom shelf in a sub-module + + _LOGGER.info('post_startup(), COMPLETE') + _LOGGER.info('DCCsi Bootstrap, COMPLETE') + return 0 +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +if __name__ == '__main__': + try: + # Early startup config. + startup() + + # This allows defered action post boot (atfer UI is active) + from maya.utils import executeDeferred + post = executeDeferred(post_startup) + + except Exception as e: + traceback.print_exc() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Shaders/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Shaders/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Tools/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Tools/stub new file mode 100644 index 0000000000..d365a5f2c9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/Tools/stub @@ -0,0 +1,9 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/config.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/config.py new file mode 100644 index 0000000000..50d6de8de4 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/config.py @@ -0,0 +1,23 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- +# DCCsi\\Tools\\DCC\\Maya\\config.py + +"""DccScriptingInterface (DCCsi) +This is the dynamic config (dynaconf) for O3DE Maya DCCsi interface +""" + +########################################################################### +# Main Code Block, runs this script as main (testing) +# ------------------------------------------------------------------------- +if __name__ == '__main__': + """Run this file as main""" + + print('Maya.config() not implemented') \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/constants.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/constants.py new file mode 100644 index 0000000000..6695b6185c --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/constants.py @@ -0,0 +1,16 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- +# DCCsi\\Tools\\DCC\\Maya\\constsants.py + +"""DccScriptingInterface (DCCsi) +This module contains constants for the O3DE Maya DCCsi interface +""" + diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/readme.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/readme.txt new file mode 100644 index 0000000000..57c8317737 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/readme.txt @@ -0,0 +1,78 @@ +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +------------------------------------------------------------------------------- + +"DccScriptingInterface" aka DCCsi is a Gem for O3DE to extend and interface with dcc tools +in the python ecosystem. Each dcc tool may have it's own specific version of python. +Most are some version of py3+. O3DE provides an install of py3+ and manages package +dependancies with requirements.txt files and the cmake build system. + +However Autodesk Maya still uses a version of py2.7 and so we need an alternate way +to deal with package management for this DCC tool. + +Maya ships with it's own python interpreter called mayapy.exe + +Generally it is located here: +C:\Program Files\Autodesk\Maya2020\bin\mayapy.exe + +The python install and site-packages are here: +C:\Program Files\Autodesk\Maya2020\Python\Lib\site-packages + +A general goal of the DCCsi is be self-maintained, and to not taint the users installed applications of environment. + +So we boostrap additional access to site-packages in our userSetup.py: +"C:\Depot\Lumberyard\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\SDK\Maya\Scripts\userSetup.py" + +We don't want users to have to install or use Python2.7 although with maya and possibly other dcc tools we don't have that control. Maya 2020 and earlier versions are still on Python2.7, so instead of forcing another install of python we can just use mayapy to manage extensions. + +Pip may already be installed, you can check like so (your maya install path may be different): + +C:\Program Files\Autodesk\Maya2020\bin>mayapy -m pip --version + +If pip is not available yet for your mayapy. + +First find out where th site-packages is located + +C:\Program Files\Autodesk\Maya2020\bin>mayapy -m site +sys.path = [ + 'C:\\Program Files\\Autodesk\\Maya2020\\bin', + 'C:\\Program Files\\Autodesk\\Maya2020\\bin\\python27.zip', + 'C:\\Program Files\\Autodesk\\Maya2020\\Python\\DLLs', + 'C:\\Program Files\\Autodesk\\Maya2020\\Python\\lib', + 'C:\\Program Files\\Autodesk\\Maya2020\\Python\\lib\\plat-win', + 'C:\\Program Files\\Autodesk\\Maya2020\\Python\\lib\\lib-tk', + 'C:\\Program Files\\Autodesk\\Maya2020\\Python', + 'C:\\Program Files\\Autodesk\\Maya2020\\Python\\lib\\site-packages', +] +USER_BASE: 'C:\\Users\\gallowj\\AppData\\Roaming\\Python' (exists) +USER_SITE: 'C:\\Users\\gallowj\\AppData\\Roaming\\Python\\Python27\\site-packages' (doesn't exist) +ENABLE_USER_SITE: True + +This is the location we are looking for: +C:\\Program Files\\Autodesk\\Maya2020\\Python\\lib\\site-packages + +download get-pip.py and put into the above ^ directory: +https://bootstrap.pypa.io/pip/2.7/get-pip.py + +Put that in the root of site-packages: +C:\\Program Files\\Autodesk\\Maya2020\\Python\\lib\\site-packages\\get-pip.py + +With get-pip module ready, we run it to install pip: +C:\Program Files\Autodesk\Maya2020\bin>mayapy -m get-pip + +Now you should be able to run the following command and verify pip: +C:\Program Files\Autodesk\Maya2020\bin>mayapy -m pip --version +pip 20.3.4 from C:\Users\< you >\AppData\Roaming\Python\Python27\site-packages\pip (python 2.7) + +Now your local maya install is all set up with pip so you can install additional python packages to use in maya. (note: not all packages are compatible with maya) + +Now you will want to run the following file to finish setup... +We have a requirements.txt file with the extension packages we use in the DCCsi. +You'll need the repo/branch path of your O3DE (aka Lumberyard) install. +And you'll need to know where the DCCsi is located, we will install package dependancies there. + +Note: you may need to update the paths below to match your local o3de engine install! + +C:\Program Files\Autodesk\Maya2020\bin>mayapy -m pip install -r C:\Depot\o3de\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\SDK\Maya\requirements.txt -t C:\Depot\o3de\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\3rdParty\Python\Lib\2.x\2.7.x\site-packages diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/requirements.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/requirements.txt new file mode 100644 index 0000000000..ceb5be4dea --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/requirements.txt @@ -0,0 +1,84 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile --generate-hashes requirements.txt +# +certifi==2020.6.20 \ + --hash=sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3 \ + --hash=sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41 + # via -r requirements.txt +cachetools==3.1.1 \ + --hash=sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae \ + --hash=sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a + # via -r requirements.txt +click==7.1.2 \ + --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \ + --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc + # via + # -r requirements.txt + # pip-tools +dynaconf==3.1.4 \ + --hash=sha256:b2f472d83052f809c5925565b8a2ba76a103d5dc1dbb9748b693ed67212781b9 \ + --hash=sha256:e6f383b84150b70fc439c8b2757581a38a58d07962aa14517292dcce1a77e160 + # via -r requirements.txt +hashids==1.3.1 \ + --hash=sha256:6c3dc775e65efc2ce2c157a65acb776d634cb814598f406469abef00ae3f635c \ + --hash=sha256:8bddd1acba501bfc9306e7e5a99a1667f4f2cacdc20cbd70bcc5ddfa5147c94c + # via -r requirements.txt +pathlib2==2.3.5 \ + --hash=sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db \ + --hash=sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868 + # via -r requirements.txt +pathlib==1.0.1 \ + --hash=sha256:6940718dfc3eff4258203ad5021090933e5c04707d5ca8cc9e73c94a7894ea9f + # via -r requirements.txt +python-box==3.4.6 \ + --hash=sha256:694a7555e3ff9fbbce734bbaef3aad92b8e4ed0659d3ed04d56b6a0a0eff26a9 \ + --hash=sha256:a71d3dc9dbaa34c8597d3517c89a8041bd62fa875f23c0f3dad55e1958e3ce10 + # via -r requirements.txt +scandir==1.10.0 \ + --hash=sha256:2586c94e907d99617887daed6c1d102b5ca28f1085f90446554abf1faf73123e \ + --hash=sha256:2ae41f43797ca0c11591c0c35f2f5875fa99f8797cb1a1fd440497ec0ae4b022 \ + --hash=sha256:2b8e3888b11abb2217a32af0766bc06b65cc4a928d8727828ee68af5a967fa6f \ + --hash=sha256:2c712840c2e2ee8dfaf36034080108d30060d759c7b73a01a52251cc8989f11f \ + --hash=sha256:4d4631f6062e658e9007ab3149a9b914f3548cb38bfb021c64f39a025ce578ae \ + --hash=sha256:67f15b6f83e6507fdc6fca22fedf6ef8b334b399ca27c6b568cbfaa82a364173 \ + --hash=sha256:7d2d7a06a252764061a020407b997dd036f7bd6a175a5ba2b345f0a357f0b3f4 \ + --hash=sha256:8c5922863e44ffc00c5c693190648daa6d15e7c1207ed02d6f46a8dcc2869d32 \ + --hash=sha256:92c85ac42f41ffdc35b6da57ed991575bdbe69db895507af88b9f499b701c188 \ + --hash=sha256:b24086f2375c4a094a6b51e78b4cf7ca16c721dcee2eddd7aa6494b42d6d519d \ + --hash=sha256:cb925555f43060a1745d0a321cca94bcea927c50114b623d73179189a4e100ac + # via + # -r requirements.txt + # pathlib2 +six==1.15.0 \ + --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \ + --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced + # via + # -r requirements.txt + # pathlib2 +typing==3.7.4.3 \ + --hash=sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9 \ + --hash=sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5 + # via + # -r requirements.txt + # dynaconf +unipath==1.1 \ + --hash=sha256:09839adcc72e8a24d4f76d63656f30b5a1f721fc40c9bcd79d8c67bdd8b47dae \ + --hash=sha256:e6257e508d8abbfb6ddd8ec357e33589f1f48b1599127f23b017124d90b0fff7 + # via -r requirements.txt +qdarkstyle==3.0.2 \ + --hash=sha256:55d149cf5f40ee297397f1818e091118cefb855a4a9c5c38566c47acd2d8c7ae \ + --hash=sha256:7c791535cc20b3cc1e8e1bf6b88dabe53cb0615983df702be83597e73ada2558 + # via -r c:\temp\requirements.txt +qtpy==1.9.0 \ + --hash=sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d \ + --hash=sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea + # via + # -r c:\temp\requirements.txt + # qdarkstyle +wincertstore==0.2 \ + --hash=sha256:22d5eebb52df88a8d4014d5cf6d1b6c3a5d469e6c3b2e2854f3a003e48872356 \ + --hash=sha256:780bd1557c9185c15d9f4221ea7f905cb20b93f7151ca8ccaed9714dce4b327a + # via -r requirements.txt diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/settings.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/settings.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/start.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/start.py new file mode 100644 index 0000000000..4d965aec28 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Maya/start.py @@ -0,0 +1,24 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- +# DCCsi\\Tools\\DCC\\Maya\\start.py + +"""DccScriptingInterface (DCCsi) +The DCCsi bootstraps tools like Maya with additional code access and extensions. +This module starts up maya in the DCCsi managed synthetic environment context. +""" + +########################################################################### +# Main Code Block, runs this script as main (testing) +# ------------------------------------------------------------------------- +if __name__ == '__main__': + """Run this file as main""" + + print('Maya.Start() not implemented') \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/custom_shaders/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/custom_shaders/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/ly_presets/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/ly_presets/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/templates/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/resources/templates/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/scripts/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/scripts/stub new file mode 100644 index 0000000000..d365a5f2c9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/scripts/stub @@ -0,0 +1,9 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/stub new file mode 100644 index 0000000000..d365a5f2c9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/DCC/Substance/stub @@ -0,0 +1,9 @@ +# coding:utf-8 +#!/usr/bin/python +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/.gitignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/.gitignore similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/.gitignore rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/.gitignore diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Core.bat similarity index 67% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Core.bat index 5146d5f3e8..b37170d605 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Core.bat @@ -31,11 +31,11 @@ echo ~ O3DE DCC Scripting Interface Environment ... echo _____________________________________________________________________ echo. -IF "%DCCSI_LAUNCHERS_PATH%"=="" (set DCCSI_LAUNCHERS_PATH=%~dp0) -echo DCCSI_LAUNCHERS_PATH = %DCCSI_LAUNCHERS_PATH% +IF "%DCCSI_DEV_ENV%"=="" (set DCCSI_DEV_ENV=%~dp0) +echo DCCSI_DEV_ENV = %DCCSI_DEV_ENV% :: add to the PATH -SET PATH=%DCCSI_LAUNCHERS_PATH%;%PATH% +SET PATH=%DCCSI_DEV_ENV%;%PATH% :: Constant Vars (Global) :: global debug flag (propogates) @@ -61,24 +61,21 @@ IF "%DCCSI_LOGLEVEL%"=="" (set DCCSI_LOGLEVEL=20) echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL% :: This maps up to the \Dev folder -IF "%DEV_REL_PATH%"=="" (set DEV_REL_PATH=..\..\..\..) -echo DEV_REL_PATH = %DEV_REL_PATH% +IF "%O3DE_REL_PATH%"=="" (set O3DE_REL_PATH=..\..\..\..) +echo O3DE_REL_PATH = %O3DE_REL_PATH% :: You can define the project name -IF "%LY_PROJECT_NAME%"=="" ( - for %%a in (%CD%..\..\..) do set LY_PROJECT_NAME=%%~na +IF "%O3DE_PROJECT%"=="" ( + for %%a in (%CD%..\..\..\..) do set O3DE_PROJECT=%%~na ) -echo LY_PROJECT_NAME = %LY_PROJECT_NAME% - -:: if not defined we just use the DCCsi path as stand-in -IF "%LY_PROJECT%"=="" (set LY_PROJECT=%CD%) -echo LY_PROJECT = %LY_PROJECT% +echo O3DE_PROJECT = %O3DE_PROJECT% :: set up the default project path (dccsi) :: if not set we also use the DCCsi path as stand-in -CD /D ..\..\ -IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%CD%) -echo LY_PROJECT_PATH = %LY_PROJECT_PATH% +CD /D ..\..\..\ +:: To Do: remove one of these +IF "%O3DE_PROJECT_PATH%"=="" (set O3DE_PROJECT_PATH=%CD%) +echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH% IF "%ABS_PATH%"=="" (set ABS_PATH=%CD%) echo ABS_PATH = %ABS_PATH% @@ -87,37 +84,40 @@ echo ABS_PATH = %ABS_PATH% pushd %ABS_PATH% :: Change to root Lumberyard dev dir -CD /d %LY_PROJECT_PATH%\%DEV_REL_PATH% -IF "%LY_DEV%"=="" (set LY_DEV=%CD%) -echo LY_DEV = %LY_DEV% +CD /d %O3DE_PROJECT_PATH%\%O3DE_REL_PATH% +IF "%O3DE_DEV%"=="" (set O3DE_DEV=%CD%) +echo O3DE_DEV = %O3DE_DEV% :: Restore original directory popd :: dcc scripting interface gem path :: currently know relative path to this gem -set DCCSIG_PATH=%LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface +set DCCSIG_PATH=%O3DE_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface echo DCCSIG_PATH = %DCCSIG_PATH% :: Change to DCCsi root dir CD /D %DCCSIG_PATH% :: per-dcc sdk path -set DCCSI_SDK_PATH=%DCCSIG_PATH%\SDK -echo DCCSI_SDK_PATH = %DCCSI_SDK_PATH% +set DCCSI_TOOLS_PATH=%DCCSIG_PATH%\Tools +echo DCCSI_TOOLS_PATH = %DCCSI_TOOLS_PATH% :: temp log location specific to this gem -set DCCSI_LOG_PATH=%DCCSIG_PATH%\.temp\logs +set DCCSI_LOG_PATH=%O3DE_PROJECT_PATH%\.temp\logs echo DCCSI_LOG_PATH = %DCCSI_LOG_PATH% :: O3DE build path -IF "%TAG_LY_BUILD_PATH%"=="" (set TAG_LY_BUILD_PATH=build) -echo TAG_LY_BUILD_PATH = %TAG_LY_BUILD_PATH% +IF "%O3DE_BUILD_FOLDER%"=="" (set O3DE_BUILD_FOLDER=build) +echo O3DE_BUILD_FOLDER = %O3DE_BUILD_FOLDER% -IF "%LY_BUILD_PATH%"=="" (set LY_BUILD_PATH=%LY_DEV%\%TAG_LY_BUILD_PATH%\bin\profile) -echo LY_BUILD_PATH = %LY_BUILD_PATH% +IF "%O3DE_BUILD_PATH%"=="" (set O3DE_BUILD_PATH=%O3DE_DEV%\%O3DE_BUILD_FOLDER%) +echo O3DE_BUILD_PATH = %O3DE_BUILD_PATH% + +IF "%O3DE_BIN_PATH%"=="" (set O3DE_BIN_PATH=%O3DE_BUILD_PATH%\bin\profile) +echo O3DE_BIN_PATH = %O3DE_BIN_PATH% :: add to the PATH -SET PATH=%LY_BUILD_PATH%;%DCCSIG_PATH%;%DCCSI_AZPY_PATH%;%PATH% +SET PATH=%O3DE_BIN_PATH%;%DCCSIG_PATH%;%PATH% ::ENDLOCAL diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Maya.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat similarity index 87% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Maya.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat index cceaa80b75..5e3c600124 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Maya.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat @@ -27,14 +27,12 @@ IF "%DCCSI_PY_VERSION_MINOR%"=="" (set DCCSI_PY_VERSION_MINOR=7) IF "%DCCSI_PY_VERSION_RELEASE%"=="" (set DCCSI_PY_VERSION_RELEASE=11) :: Default Maya Version -IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=%MAYA_VERSION%) +IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) :: Initialize env CALL %~dp0\Env_Core.bat CALL %~dp0\Env_Python.bat -::SETLOCAL ENABLEDELAYEDEXPANSION - echo. echo _____________________________________________________________________ echo. @@ -48,14 +46,14 @@ echo DCCSI_PY_VERSION_RELEASE = %DCCSI_PY_VERSION_RELEASE% echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% :::: Set Maya native project acess to this project -IF "%MAYA_PROJECT%"=="" (set MAYA_PROJECT=%LY_PROJECT%) +IF "%MAYA_PROJECT%"=="" (set MAYA_PROJECT=%O3DE_PROJECT%) echo MAYA_PROJECT = %MAYA_PROJECT% :: maya sdk path -set DCCSI_SDK_MAYA_PATH=%DCCSI_SDK_PATH%\Maya -echo DCCSI_SDK_MAYA_PATH = %DCCSI_SDK_MAYA_PATH% +set DCCSI_TOOLS_MAYA_PATH=%DCCSI_TOOLS_PATH%\DCC\Maya +echo DCCSI_TOOLS_MAYA_PATH = %DCCSI_TOOLS_MAYA_PATH% -set MAYA_MODULE_PATH=%DCCSI_SDK_MAYA_PATH%;%MAYA_MODULE_PATH% +set MAYA_MODULE_PATH=%DCCSI_TOOLS_MAYA_PATH%;%MAYA_MODULE_PATH% echo MAYA_MODULE_PATH = %MAYA_MODULE_PATH% :: Maya File Paths, etc @@ -93,36 +91,36 @@ echo DCCSI_PY_MAYA = %DCCSI_PY_MAYA% SET PATH=%MAYA_BIN_PATH%;%PATH% :: Local DCCsi Maya plugins access (ours) -set DCCSI_MAYA_PLUG_IN_PATH=%DCCSI_SDK_MAYA_PATH%\plugins +set DCCSI_MAYA_PLUG_IN_PATH=%DCCSI_TOOLS_MAYA_PATH%\plugins :: also attached to maya's built-it env var set MAYA_PLUG_IN_PATH=%DCCSI_MAYA_PLUG_IN_PATH%;MAYA_PLUG_IN_PATH echo DCCSI_MAYA_PLUG_IN_PATH = %DCCSI_MAYA_PLUG_IN_PATH% :: Local DCCsi Maya shelves (ours) -set DCCSI_MAYA_SHELF_PATH=%DCCSI_SDK_MAYA_PATH%\Prefs\Shelves +set DCCSI_MAYA_SHELF_PATH=%DCCSI_TOOLS_MAYA_PATH%\Prefs\Shelves set MAYA_SHELF_PATH=%DCCSI_MAYA_SHELF_PATH% echo DCCSI_MAYA_SHELF_PATH = %DCCSI_MAYA_SHELF_PATH% :: Local DCCsi Maya icons path (ours) -set DCCSI_MAYA_XBMLANGPATH=%DCCSI_SDK_MAYA_PATH%\Prefs\icons +set DCCSI_MAYA_XBMLANGPATH=%DCCSI_TOOLS_MAYA_PATH%\Prefs\icons :: also attached to maya's built-it env var set XBMLANGPATH=%DCCSI_MAYA_XBMLANGPATH%;%XBMLANGPATH% echo DCCSI_MAYA_XBMLANGPATH = %DCCSI_MAYA_XBMLANGPATH% :: Local DCCsi Maya Mel scripts (ours) -set DCCSI_MAYA_SCRIPT_MEL_PATH=%DCCSI_SDK_MAYA_PATH%\Scripts\Mel +set DCCSI_MAYA_SCRIPT_MEL_PATH=%DCCSI_TOOLS_MAYA_PATH%\Scripts\Mel :: also attached to maya's built-it env var set MAYA_SCRIPT_PATH=%DCCSI_MAYA_SCRIPT_MEL_PATH%;%MAYA_SCRIPT_PATH% echo DCCSI_MAYA_SCRIPT_MEL_PATH = %DCCSI_MAYA_SCRIPT_MEL_PATH% :: Local DCCsi Maya Python scripts (ours) -set DCCSI_MAYA_SCRIPT_PY_PATH=%DCCSI_SDK_MAYA_PATH%\Scripts\Python +set DCCSI_MAYA_SCRIPT_PY_PATH=%DCCSI_TOOLS_MAYA_PATH%\Scripts\Python :: also attached to maya's built-it env var set MAYA_SCRIPT_PATH=%DCCSI_MAYA_SCRIPT_PY_PATH%;%MAYA_SCRIPT_PATH% echo DCCSI_MAYA_SCRIPT_PY_PATH = %DCCSI_MAYA_SCRIPT_PY_PATH% :: DCCsi Maya boostrap, userSetup.py access (ours) -set DCCSI_MAYA_SCRIPT_PATH=%DCCSI_SDK_MAYA_PATH%\Scripts +set DCCSI_MAYA_SCRIPT_PATH=%DCCSI_TOOLS_MAYA_PATH%\Scripts :: also attached to maya's built-it env var set MAYA_SCRIPT_PATH=%DCCSI_MAYA_SCRIPT_PATH%;%MAYA_SCRIPT_PATH% echo DCCSI_MAYA_SCRIPT_PATH = %DCCSI_MAYA_SCRIPT_PATH% diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_PyCharm.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_PyCharm.bat similarity index 95% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_PyCharm.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_PyCharm.bat index 67f2c3d81e..7be9154e6d 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_PyCharm.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_PyCharm.bat @@ -34,7 +34,7 @@ CALL %~dp0\Env_Python.bat CALL %~dp0\Env_Qt.bat :: Wing and other IDEs probably prefer access directly to the python.exe -set DCCSI_PY_IDE = %DCCSI_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python +set DCCSI_PY_IDE = %O3DE_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python echo DCCSI_PY_IDE = %DCCSI_PY_IDE% :: ide and debugger plug diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Python.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Python.bat similarity index 76% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Python.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Python.bat index 196d7b09bb..388ab531df 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Python.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Python.bat @@ -19,8 +19,6 @@ PUSHD %~dp0 CALL %~dp0\Env_Core.bat -::SETLOCAL ENABLEDELAYEDEXPANSION - echo. echo _____________________________________________________________________ echo. @@ -56,32 +54,29 @@ echo DCCSI_PYTHON_LIB_PATH = %DCCSI_PYTHON_LIB_PATH% SET PATH=%DCCSI_PYTHON_LIB_PATH%;%PATH% :: shared location for default O3DE python location -set DCCSI_PYTHON_INSTALL=%LY_DEV%\Python -echo DCCSI_PYTHON_INSTALL = %DCCSI_PYTHON_INSTALL% +set O3DE_PYTHON_INSTALL=%O3DE_DEV%\python +echo O3DE_PYTHON_INSTALL = %O3DE_PYTHON_INSTALL% :: location for O3DE python 3.7 location -set DCCSI_PY_BASE=%DCCSI_PYTHON_INSTALL%\python.cmd +:: Note, many DCC tools (like Maya) include thier own python interpretter +:: Some DCC apps may not operate correctly if PYTHONHOME is set (this is definitely the case with Maya) +:: Be aware the python.cmd below does set PYTHONHOME +set DCCSI_PY_BASE=%O3DE_PYTHON_INSTALL%\python.cmd echo DCCSI_PY_BASE = %DCCSI_PY_BASE% -:: ide and debugger plug -set DCCSI_PY_DEFAULT=%DCCSI_PY_BASE% +CALL %O3DE_PYTHON_INSTALL%\get_python_path.bat -:: Wing and other IDEs probably prefer access directly to the python.exe -set DCCSI_PY_IDE=%DCCSI_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python +:: Some IDEs like Wing, may in some cases need acess directly to the exe to operate correctly +IF "%DCCSI_PY_IDE%"=="" (set DCCSI_PY_IDE=%O3DE_PYTHONHOME%\python.exe) echo DCCSI_PY_IDE = %DCCSI_PY_IDE% -set DCCSI_PY_IDE_PACKAGES=%DCCSI_PY_IDE%\Lib\site-packages -echo DCCSI_PY_IDE_PACKAGES = %DCCSI_PY_IDE_PACKAGES% - :: add to the PATH -SET PATH=%DCCSI_PYTHON_INSTALL%;%DCCSI_PY_IDE%;%DCCSI_PY_IDE_PACKAGES%;%PATH% +SET PATH=%O3DE_PYTHON_INSTALL%;%O3DE_PYTHONHOME%;%DCCSI_PY_IDE%;%PATH% :: add all python related paths to PYTHONPATH for package imports -set PYTHONPATH=%DCCSIG_PATH%;%DCCSI_PYTHON_LIB_PATH%;%LY_BUILD_PATH%;%PYTHONPATH% +set PYTHONPATH=%DCCSIG_PATH%;%DCCSI_PYTHON_LIB_PATH%;%O3DE_BUILD_PATH%;%PYTHONPATH% echo PYTHONPATH = %PYTHONPATH% -::ENDLOCAL - :: Set flag so we don't initialize dccsi environment twice SET DCCSI_ENV_PY_INIT=1 GOTO END_OF_FILE diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Qt.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Qt.bat similarity index 85% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Qt.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Qt.bat index 2ad49b4416..6202ead2f2 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Qt.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Qt.bat @@ -34,23 +34,23 @@ echo. :: set up Qt/Pyside paths :: set up PySide2/Shiboken -set QTFORPYTHON_PATH=%LY_DEV%\Gems\QtForPython\3rdParty\pyside2\windows\release +set QTFORPYTHON_PATH=%O3DE_DEV%\Gems\QtForPython\3rdParty\pyside2\windows\release echo QTFORPYTHON_PATH = %QTFORPYTHON_PATH% :: add to the PATH SET PATH=%QTFORPYTHON_PATH%;%PATH% SET PYTHONPATH=%QTFORPYTHON_PATH%;%PYTHONPATH% -set QT_PLUGIN_PATH=%LY_BUILD_PATH%\bin\profile\EditorPlugins +set QT_PLUGIN_PATH=%O3DE_BUILD_PATH%\bin\profile\EditorPlugins echo QT_PLUGIN_PATH = %QT_PLUGIN_PATH% :: add to the PATH SET PATH=%QT_PLUGIN_PATH%;%PATH% SET PYTHONPATH=%QT_PLUGIN_PATH%;%PYTHONPATH% -set LY_BIN_PATH=%LY_BUILD_PATH%\bin\profile -echo LY_BIN_PATH = %LY_BIN_PATH% -SET PATH=%LY_BIN_PATH%;%PATH% +set O3DE_BIN_PATH=%O3DE_BUILD_PATH%\bin\profile +echo O3DE_BIN_PATH = %O3DE_BIN_PATH% +SET PATH=%O3DE_BIN_PATH%;%PATH% ::ENDLOCAL diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Substance.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Substance.bat similarity index 92% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Substance.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Substance.bat index 3c92b306ca..d44ed985da 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Substance.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Substance.bat @@ -31,7 +31,7 @@ echo. : Substance Designer :: maya sdk path -set DCCSI_SUBSTANCE_PATH=%DCCSI_SDK_PATH%\Substance +set DCCSI_SUBSTANCE_PATH=%DCCSI_TOOLS_PATH%\Substance echo DCCSI_SUBSTANCE_PATH = %DCCSI_SUBSTANCE_PATH% :: https://docs.substance3d.com/sddoc/project-preferences-107118596.html#ProjectPreferences-ConfigurationFile :: Path to .exe, "C:\Program Files\Allegorithmic\Substance Designer\Substance Designer.exe" @@ -39,7 +39,7 @@ set SUBSTANCE_PATH="%ProgramFiles%\Allegorithmic\Substance Designer" echo SUBSTANCE_PATH = %SUBSTANCE_PATH% :: default config -set SUBSTANCE_CFG_PATH=%LY_PROJECT_PATH%\DCCsi_default.sbscfg +set SUBSTANCE_CFG_PATH=%O3DE_PROJECT_PATH%\DCCsi_default.sbscfg echo SUBSTANCE_CFG_PATH = %SUBSTANCE_CFG_PATH% ::ENDLOCAL diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_VScode.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_VScode.bat similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_VScode.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_VScode.bat diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_WingIDE.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_WingIDE.bat similarity index 78% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_WingIDE.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_WingIDE.bat index 56209ca5aa..a8c3b3d073 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_WingIDE.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_WingIDE.bat @@ -27,18 +27,9 @@ CALL %~dp0\Env_Core.bat CALL %~dp0\Env_Python.bat CALL %~dp0\Env_Qt.bat -:: Wing and other IDEs probably prefer access directly to the python.exe -set DCCSI_PY_IDE = %DCCSI_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python -echo DCCSI_PY_IDE = %DCCSI_PY_IDE% - -:: ide and debugger plug -set DCCSI_PY_DEFAULT=%DCCSI_PY_IDE%\python.exe - :: put project env variables/paths here set WINGHOME=%PROGRAMFILES(X86)%\Wing Pro %DCCSI_WING_VERSION_MAJOR%.%DCCSI_WING_VERSION_MINOR% -SET WING_PROJ=%DCCSIG_PATH%\Solutions\.wing\DCCsi_%DCCSI_WING_VERSION_MAJOR%x.wpr - -::SETLOCAL ENABLEDELAYEDEXPANSION +SET WING_PROJ=%DCCSIG_PATH%\Tools\Dev\Windows\Solutions\.wing\DCCsi_%DCCSI_WING_VERSION_MAJOR%x.wpr echo. echo _____________________________________________________________________ @@ -55,8 +46,6 @@ echo WING_PROJ = %WING_PROJ% :: add to the PATH SET PATH=%WINGHOME%;%PATH% -::ENDLOCAL - :: Set flag so we don't initialize dccsi environment twice SET DCCSI_ENV_WINGIDE_INIT=1 GOTO END_OF_FILE diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Env_Cmd.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Env_Cmd.bat similarity index 97% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Env_Cmd.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Env_Cmd.bat index a8476ccc22..5b76de1401 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Env_Cmd.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Env_Cmd.bat @@ -40,7 +40,7 @@ CALL %~dp0\Env_WingIDE.bat IF EXIST "%~dp0Env_Dev.bat" CALL %~dp0Env_Dev.bat :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% :: Create command prompt with environment CALL %windir%\system32\cmd.exe diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_MayaPy_PyCharmPro.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_MayaPy_PyCharmPro.bat similarity index 91% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_MayaPy_PyCharmPro.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_MayaPy_PyCharmPro.bat index d98b8d81e7..e451050ab8 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_MayaPy_PyCharmPro.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_MayaPy_PyCharmPro.bat @@ -56,14 +56,14 @@ echo ~ MayaPy.exe (default python interpreter) echo _____________________________________________________________________ echo. -echo LY_DEV = %LY_DEV% +echo O3DE_DEV = %O3DE_DEV% :: shared location for default O3DE python location -set DCCSI_PYTHON_INSTALL=%LY_DEV%\Python -echo DCCSI_PYTHON_INSTALL = %DCCSI_PYTHON_INSTALL% +set O3DE_PYTHON_INSTALL=%O3DE_DEV%\Python +echo O3DE_PYTHON_INSTALL = %O3DE_PYTHON_INSTALL% :: Wing and other IDEs probably prefer access directly to the python.exe -set DCCSI_PY_IDE = %DCCSI_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python +set DCCSI_PY_IDE = %O3DE_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python echo DCCSI_PY_IDE = %DCCSI_PY_IDE% :: ide and debugger plug @@ -80,7 +80,7 @@ IF EXIST "%~dp0Env_Dev.bat" CALL %~dp0Env_Dev.bat echo. :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% IF EXIST "%PYCHARM_HOME%\bin\pycharm64.exe" ( start "" "%PYCHARM_HOME%\bin\pycharm64.exe" "%PYCHARM_PROJ%" diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Maya_2020.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Maya_2020.bat similarity index 98% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Maya_2020.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Maya_2020.bat index 2c4e1cc941..ee2ea5bf1f 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Maya_2020.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Maya_2020.bat @@ -50,7 +50,7 @@ echo MAYA_LOCATION = %MAYA_LOCATION% echo MAYA_BIN_PATH = %MAYA_BIN_PATH% :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% :: Default to the right version of Maya if we can detect it... and launch IF EXIST "%MAYA_BIN_PATH%\maya.exe" ( diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_PyCharmPro.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_PyCharmPro.bat similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_PyCharmPro.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_PyCharmPro.bat diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_PyMin_Cmd.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_PyMin_Cmd.bat similarity index 97% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_PyMin_Cmd.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_PyMin_Cmd.bat index d001d8a75e..193cb40de8 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_PyMin_Cmd.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_PyMin_Cmd.bat @@ -38,7 +38,7 @@ echo _____________________________________________________________________ echo. :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% :: Create command prompt with environment CALL %windir%\system32\cmd.exe diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Qt_PyMin_Cmd.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Qt_PyMin_Cmd.bat similarity index 93% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Qt_PyMin_Cmd.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Qt_PyMin_Cmd.bat index d50989004b..75d1ff8cc4 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_Qt_PyMin_Cmd.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_Qt_PyMin_Cmd.bat @@ -10,7 +10,7 @@ REM :: Set up and run LY Python CMD prompt :: Sets up the DccScriptingInterface_Env, -:: Puts you in the CMD within the LY_DEV environment +:: Puts you in the CMD within the O3DE_DEV environment :: Set up window TITLE O3DE DCC Scripting Interface Py Qt Cmd @@ -39,7 +39,7 @@ echo _____________________________________________________________________ echo. :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% :: Create command prompt with environment CALL %windir%\system32\cmd.exe diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_VScode.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_VScode.bat similarity index 92% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_VScode.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_VScode.bat index 120584ed6a..2dc5b06032 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_VScode.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_VScode.bat @@ -61,14 +61,14 @@ echo ~ Launching DCCsi Project in VScode echo _____________________________________________________________________ echo. -echo LY_DEV = %LY_DEV% +echo O3DE_DEV = %O3DE_DEV% :: shared location for default O3DE python location -set DCCSI_PYTHON_INSTALL=%LY_DEV%\Python -echo DCCSI_PYTHON_INSTALL = %DCCSI_PYTHON_INSTALL% +set O3DE_PYTHON_INSTALL=%O3DE_DEV%\Python +echo O3DE_PYTHON_INSTALL = %O3DE_PYTHON_INSTALL% :: Wing and other IDEs probably prefer access directly to the python.exe -set DCCSI_PY_IDE = %DCCSI_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python +set DCCSI_PY_IDE = %O3DE_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python echo DCCSI_PY_IDE = %DCCSI_PY_IDE% :: ide and debugger plug diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_WingIDE-7-1.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_WingIDE-7-1.bat similarity index 74% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_WingIDE-7-1.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_WingIDE-7-1.bat index c933670bab..56e278da08 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_WingIDE-7-1.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_WingIDE-7-1.bat @@ -20,6 +20,9 @@ COLOR 8E cd %~dp0 PUSHD %~dp0 +:: if the user has set up a custom env call it +IF EXIST "%~dp0Env_Dev.bat" CALL %~dp0Env_Dev.bat + :: Constant Vars (Global) :: global debug (propogates) IF "%DCCSI_GDEBUG%"=="" (set DCCSI_GDEBUG=True) @@ -51,35 +54,20 @@ echo. echo _____________________________________________________________________ echo. echo ~ WingIDE Version %DCCSI_WING_VERSION_MAJOR%.%DCCSI_WING_VERSION_MINOR% -echo ~ Launching O3DE %LY_PROJECT% project in WingIDE %DCCSI_WING_VERSION_MAJOR%.%DCCSI_WING_VERSION_MINOR% ... +echo ~ Launching O3DE %O3DE_PROJECT% project in WingIDE %DCCSI_WING_VERSION_MAJOR%.%DCCSI_WING_VERSION_MINOR% ... echo _____________________________________________________________________ echo. -echo LY_DEV = %LY_DEV% +echo O3DE_DEV = %O3DE_DEV% :: shared location for default O3DE python location -set DCCSI_PYTHON_INSTALL=%LY_DEV%\Python -echo DCCSI_PYTHON_INSTALL = %DCCSI_PYTHON_INSTALL% - -:: Wing and other IDEs probably prefer access directly to the python.exe -set DCCSI_PY_IDE = %DCCSI_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python -echo DCCSI_PY_IDE = %DCCSI_PY_IDE% - -:: ide and debugger plug -set DCCSI_PY_BASE=%DCCSI_PY_IDE%\python.exe -echo DCCSI_PY_BASE = %DCCSI_PY_BASE% - -:: ide and debugger plug -set DCCSI_PY_DEFAULT=%DCCSI_PY_BASE% -echo DCCSI_PY_DEFAULT = %DCCSI_PY_DEFAULT% - -:: if the user has set up a custom env call it -IF EXIST "%~dp0Env_Dev.bat" CALL %~dp0Env_Dev.bat +set O3DE_PYTHON_INSTALL=%O3DE_DEV%\Python +echo O3DE_PYTHON_INSTALL = %O3DE_PYTHON_INSTALL% echo. :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% IF EXIST "%WINGHOME%\bin\wing.exe" ( start "" "%WINGHOME%\bin\wing.exe" "%WING_PROJ%" diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_mayaPy_2020.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_mayaPy_2020.bat similarity index 98% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_mayaPy_2020.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_mayaPy_2020.bat index f07ec093cb..33b8bc6392 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_mayaPy_2020.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_mayaPy_2020.bat @@ -48,7 +48,7 @@ echo MAYA_LOCATION = %MAYA_LOCATION% echo MAYA_BIN_PATH = %MAYA_BIN_PATH% :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% SETLOCAL ENABLEDELAYEDEXPANSION diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_mayapy_WingIDE-7-1.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_mayapy_WingIDE-7-1.bat similarity index 87% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_mayapy_WingIDE-7-1.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_mayapy_WingIDE-7-1.bat index 631250d06b..ca5b175c39 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_mayapy_WingIDE-7-1.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_mayapy_WingIDE-7-1.bat @@ -50,19 +50,19 @@ echo. echo _____________________________________________________________________ echo. echo ~ WingIDE Version %DCCSI_WING_VERSION_MAJOR%.%DCCSI_WING_VERSION_MINOR% -echo ~ Launching O3DE %LY_PROJECT% project in WingIDE %DCCSI_WING_VERSION_MAJOR%.%DCCSI_WING_VERSION_MINOR% ... +echo ~ Launching O3DE %O3DE_PROJECT% project in WingIDE %DCCSI_WING_VERSION_MAJOR%.%DCCSI_WING_VERSION_MINOR% ... echo ~ MayaPy.exe (default python interpreter) echo _____________________________________________________________________ echo. -echo LY_DEV = %LY_DEV% +echo O3DE_DEV = %O3DE_DEV% :: shared location for default O3DE python location -set DCCSI_PYTHON_INSTALL=%LY_DEV%\Python -echo DCCSI_PYTHON_INSTALL = %DCCSI_PYTHON_INSTALL% +set O3DE_PYTHON_INSTALL=%O3DE_DEV%\Python +echo O3DE_PYTHON_INSTALL = %O3DE_PYTHON_INSTALL% :: Wing and other IDEs probably prefer access directly to the python.exe -set DCCSI_PY_IDE = %DCCSI_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python +set DCCSI_PY_IDE = %O3DE_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python echo DCCSI_PY_IDE = %DCCSI_PY_IDE% :: ide and debugger plug @@ -79,7 +79,7 @@ IF EXIST "%~dp0Env_Dev.bat" CALL %~dp0Env_Dev.bat echo. :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% IF EXIST "%WINGHOME%\bin\wing.exe" ( start "" "%WINGHOME%\bin\wing.exe" "%WING_PROJ%" diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_pyBASE.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_pyBASE.bat similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_pyBASE.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_pyBASE.bat diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_pyBASE_Cmd.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_pyBASE_Cmd.bat similarity index 97% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_pyBASE_Cmd.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_pyBASE_Cmd.bat index 9f8950f59f..e1b075e065 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Launch_pyBASE_Cmd.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Launch_pyBASE_Cmd.bat @@ -33,7 +33,7 @@ CALL %~dp0\Env_Maya.bat IF EXIST "%~dp0Env_Dev.bat" CALL %~dp0Env_Dev.bat :: Change to root dir -CD /D %LY_PROJECT_PATH% +CD /D %O3DE_PROJECT_PATH% :: Create command prompt with environment CALL %windir%\system32\cmd.exe diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/README.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/README.txt new file mode 100644 index 0000000000..e5b7946276 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/README.txt @@ -0,0 +1,78 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- + +DccScriptingInterface (DCCsi) is a framework for O3DE extensions, for example: +- Lightweight python integrations with DCC tools like Maya +- O3DE configuration, customization and extensions of tools +- Standalone PySide2/Qt tools +- ^ These might utilize a mix of O3DE and DDC python APIs + +The DccScriptingInterface\config.py, procedurally provides a synthetic env context. +This env is a data-driven approach to configuring layered and managed env settings. + +This env provides the hooks for DDC apps and/or standalone tools, +to configure acess to O3DE code (for boostrapping), safely retreive known paths, set/get developer flags, etc. + +DccScriptingInterface\Tools\Dev\Windows\ + +This is a .bat file based version of the default env context for development on windows. +This is what we use to boot the default env context such that it is available, when launching a development tool such as a IDE. + +This allows a developer to troubleshoot/debug code, like config.py + +Other tools, can use config.py to stand up the env context. + +What is in this folder ... + +Core env modules +--------------------- +Env_Core.bat : core access to O3DE and DCCsi +Env_Python.bat : access to O3DE python and general py configuration +Env_Qt.bat : access to O3DE Qt .dll files and PySide2 + +DCC add on envars +--------------------- +Env_Maya.bat : configures Maya with code O3DE/DCCsi access +Env_Substance.bat : Configures Substance Designer + +IDE env +--------------------- +Env_WingIDE.bat : configures WingIDE for DCCsi development +Env_VScode.bat : configures VScode for DCCsi development +Env_PyCharm.bat : configures PyCharm for DCCsi development + +Launchers +--------------------- +Launch_env_Cmd.bat : Starts a cmd with entire managed env context + : ^ allows use to validate env + : ^ display all default ENVAR plugs + : ^ allows user to test O3DE python + scripts from cmd +Launch_PyMin_Cmd.bat : Starts minimal cmd with O3DE python access only + : ^ for instance, test Dccsi\config.py like this: + : {DCCsi prommpt}>python config.py +Launch_Maya_2020.bat : Starts Maya2020 within managed env context +Launch_WindIDE-7-1.bat : Starts WingIDE within managed env context + +--------------------- +Instructions: How to test the synthetic environment and settings externally + +1. Run the cmd: Launch_PyMin_Cmd.bat + + C:\Depot\o3de-engine\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface> + +2. Run command:>python config.py -dm=True -py=True -qt=True + +What this does? +- runs the O3DE python exe +- starts the config.py which begins to procedurally create synthetic/dynamic environment (hooks) +- ^ this starts with DCCsi hooks +- enters 'dev mode'(-dm) and attempts to attach debugger (Wing IDE only for now, others planned) +- enables additional O3DE python hooks and code access +- ^ great for standalone tools, but you don't want that functionality to interfer with other DCC tools python environments (like Maya!) +- enables access to O3DE Qt .dlls and PySide2 python package support(-qt) +- ^ great for standalone PySide2 which can operate outside of the O3DE editor \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Setuo_copy_oiio.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Setuo_copy_oiio.bat similarity index 76% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Setuo_copy_oiio.bat rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Setuo_copy_oiio.bat index 590e634d54..18a58e1371 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Setuo_copy_oiio.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Setuo_copy_oiio.bat @@ -13,12 +13,12 @@ cd %~dp0 PUSHD %~dp0 :: This maps up to the \Dev folder -set LY_DEV=..\..\..\..\..\.. +set O3DE_DEV=..\..\..\..\..\.. :: shared location for default O3DE python location -set DCCSI_PYTHON_INSTALL=%LY_DEV%\Python +set O3DE_PYTHON_INSTALL=%O3DE_DEV%\Python -set PY_SITE=%DCCSI_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python\Lib\site-packages +set PY_SITE=%O3DE_PYTHON_INSTALL%\runtime\python-3.7.10-rev2-windows\python\Lib\site-packages set PACKAGE_LOC=C:\Depot\3rdParty\packages\openimageio-2.1.16.0-rev1-windows\OpenImageIO\2.1.16.0\win_x64\bin diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.dev/readme.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.dev/readme.txt similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.dev/readme.txt rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.dev/readme.txt diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.dev/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.dev/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.gitignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.gitignore similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.gitignore rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.gitignore diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/.gitignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/.gitignore similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/.gitignore rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/.gitignore diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/.p4ignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/.p4ignore similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/.p4ignore rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/.p4ignore diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/DccScriptingInterface.iml b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/DccScriptingInterface.iml similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/DccScriptingInterface.iml rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/DccScriptingInterface.iml diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/encodings.xml b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/encodings.xml similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/encodings.xml rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/encodings.xml diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/main.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/main.py similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/main.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/main.py diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/misc.xml b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/misc.xml similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/misc.xml rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/misc.xml diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/modules.xml b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/modules.xml similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/modules.xml rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/modules.xml diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/vcs.xml b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/vcs.xml similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/vcs.xml rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/vcs.xml diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/webResources.xml b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/webResources.xml similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.idea/webResources.xml rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.idea/webResources.xml diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.vscode/dccsi.code-workspace b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.vscode/dccsi.code-workspace similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.vscode/dccsi.code-workspace rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.vscode/dccsi.code-workspace diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.vscode/launch.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.vscode/launch.json similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.vscode/launch.json rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.vscode/launch.json diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.vscode/settings.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.vscode/settings.json similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.vscode/settings.json rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.vscode/settings.json diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/.gitignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.wing/.gitignore similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/.gitignore rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.wing/.gitignore diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.wing/DCCsi_7x.wpr similarity index 57% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.wing/DCCsi_7x.wpr index aa7a8c4bd9..18af1298db 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/.wing/DCCsi_7x.wpr @@ -5,7 +5,22 @@ ################################################################## [project attributes] debug.launch-configs = (2, - {'launch-GeaM41WYMGA1sEfm': ({'shared': True}, + {'launch-0L6se5pxC9AWvpY0': ({'shared': True}, + {'buildcmd': ('project', + None), + 'env': ('project', + [u'']), + 'name': 'DCCSI_PY_IDE', + 'pyexec': ('custom', + u'${DCCSI_PY_IDE}'), + 'pypath': ('project', + []), + 'pyrunargs': ('project', + '-u'), + 'runargs': u'', + 'rundir': ('project', + u'')}), + 'launch-GeaM41WYMGA1sEfm': ({'shared': True}, {'buildcmd': ('project', None), 'env': ('custom', @@ -33,52 +48,30 @@ debug.launch-configs = (2, 'pyrunargs': ('project', '-u'), 'runargs': u'', - 'rundir': ('project', - u'')}), - 'launch-oobMrvXFf1SwtYBg': ({'shared': True}, - {'buildcmd': ('project', - None), - 'env': ('custom', - [u'']), - 'name': u'DCCSI_PY_DEFAULT', - 'pyexec': ('custom', - u'${DCCSI_PY_DEFAULT}'), - 'pypath': ('project', - []), - 'pyrunargs': ('project', - '-u'), - 'runargs': u'', 'rundir': ('project', u'')})}) -proj.directory-list = [{'dirloc': loc('../..'), +proj.directory-list = [{'dirloc': loc('../../../../..'), 'excludes': (), 'filter': u'*', - 'include_hidden': True, + 'include_hidden': False, + 'recursive': True, + 'watch_for_changes': True}, + {'dirloc': loc('../../../../../../../../Atom/Feature/Common/Editor/Scripts/ColorGrading'), + 'excludes': (), + 'filter': u'*', + 'include_hidden': False, + 'recursive': True, + 'watch_for_changes': True}, + {'dirloc': loc('../../../../../../../../../python'), + 'excludes': (), + 'filter': u'*', + 'include_hidden': False, 'recursive': True, 'watch_for_changes': True}] proj.file-type = 'shared' -proj.home-dir = loc('../..') -proj.launch-config = {loc('../../SDK/Atom/Scripts/Python/DCC_Materials/maya_materials_export.py'): ('c'\ - 'ustom', +proj.launch-config = {loc('../../../../../azpy/core/py2/utils.py'): ('custom', (u'', - 'launch-GeaM41WYMGA1sEfm')), - loc('../../SDK/Maya/Scripts/Python/legacy_asset_converter/main.py'): ('c'\ - 'ustom', + 'launch-0L6se5pxC9AWvpY0')), + loc('../../../../../azpy/core/py3/utils.py'): ('custom', (u'', - 'launch-WUN9lgYK6qYU7qE9')), - loc('../../azpy/__init__.py'): ('custom', - (u'', - 'launch-oobMrvXFf1SwtYBg')), - loc('../../azpy/constants.py'): ('custom', - (u'', - 'launch-GeaM41WYMGA1sEfm')), - loc('../../azpy/env_base.py'): ('project', - (u'', - 'launch-GeaM41WYMGA1sEfm')), - loc('../../azpy/maya/callbacks/node_message_callback_handler.py'): ('c'\ - 'ustom', - (u'', - 'launch-GeaM41WYMGA1sEfm')), - loc('../../config.py'): ('custom', - (u'', - 'launch-GeaM41WYMGA1sEfm'))} + 'launch-0L6se5pxC9AWvpY0'))} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/readme.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/readme.txt similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/readme.txt rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Solutions/readme.txt diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/README.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/README.txt new file mode 100644 index 0000000000..05aa948943 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/README.txt @@ -0,0 +1,9 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- + +This folder resprents a Mock Tool - currently just a scaffold (not implemented) \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/Resources/resource.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/Resources/resource.txt new file mode 100644 index 0000000000..30df2e0eb9 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/Resources/resource.txt @@ -0,0 +1,9 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- + +This folder is for tool resources - this file is a resource stub. \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/config.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/config.py new file mode 100644 index 0000000000..1dc43d8485 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/config.py @@ -0,0 +1,12 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- + +print('Not Implemented') \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/main.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/main.py new file mode 100644 index 0000000000..1dc43d8485 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/main.py @@ -0,0 +1,12 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- + +print('Not Implemented') \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/settings.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/settings.json new file mode 100644 index 0000000000..d58fc7e10c --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/MockTool/settings.json @@ -0,0 +1,17 @@ +{ + "default": { + "DCCSI_GDEBUG": true, + "SPDX-LICENSE-IDENTIFIER": "Apache-2.0 OR MIT", + "COPYWRITE":"Copyright (c) Contributors to the Open 3D Engine Project.", + "COPYWRITE_MSG":"For complete copyright and license terms please see the LICENSE at the root of this distribution." + }, + "development": { + "DCCSI_GDEBUG": true, + "TEST_RULE": "/dccsi_with_json", + "MESSAGE": "The O3DE DCCsi manages dynamic configuration and settings with dynaconf", + "DYNACONF_HELP": "https://dynaconf.readthedocs.io/" + }, + "production": { + "DCCSI_GDEBUG": false + } +} \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/Tests/OpenImageIO/AA_Gun_01_ddna.tif b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/Tests/OpenImageIO/AA_Gun_01_ddna.tif new file mode 100644 index 0000000000..0cefbe9c25 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/Tests/OpenImageIO/AA_Gun_01_ddna.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c34bbeafae5c5b62d3571ac9eee295fff12ac0744f81375f53a3faf2c5095e4 +size 286400 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/Tests/OpenImageIO/test_oiio.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/Tests/OpenImageIO/test_oiio.py new file mode 100644 index 0000000000..b33fa8ef56 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/Tests/OpenImageIO/test_oiio.py @@ -0,0 +1,66 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- + +import OpenImageIO as oiio + +def convert(src): + try: + rgba = oiio.ImageBuf(src) + spec = get_image_spec(rgba) + + # Normal output ------> + rgb = oiio.ImageBufAlgo.channels(rgba, (0, 1, 2)) + normal_output = src.replace('ddna', 'Normal') + + # Roughness output ------> + alpha = oiio.ImageBufAlgo.channels(rgba, (3,)) + roughness = oiio.ImageBufAlgo.invert(alpha) + roughness_output = normal_output.replace('Normal', 'Roughness') + + write_image(rgba, normal_output, spec['format']) + write_image(roughness, roughness_output, spec['format']) + # logging.info('Output Normal Map: {}'.format(normal_output)) + # logging.info('Output Roughness Map: {}'.format(roughness_output)) + + except Exception as e: + logging.info('OIIO error in image conversion: {}'.format(e)) + return None + +def get_image_spec(target_image): + spec = target_image.spec() + info = {'resolution': (spec.width, spec.height, spec.x, spec.y), 'channels': spec.channelnames, + 'format': str(spec.format)} + if spec.channelformats : + info['channelformats'] = str(spec.channelformats) + info['alpha channel'] = str(spec.alpha_channel) + info['z channel'] = str(spec.z_channel) + info['deep'] = str(spec.deep) + for i in range(len(spec.extra_attribs)): + if type(spec.extra_attribs[i].value) == str: + info[spec.extra_attribs[i].name] = spec.extra_attribs[i].value + else: + info[spec.extra_attribs[i].name] = spec.extra_attribs[i].value + return info + +def write_image(image, filename, image_format): + if not image.has_error: + image.set_write_format(image_format) + image.write(filename) + if image.has_error: + print("Error writing", filename, ":", image.geterror()) + +########################################################################### +# Main Code Block, runs this script as main (testing) +# ------------------------------------------------------------------------- +if __name__ == '__main__': + # run a test + convert('AA_Gun_01_ddna.tif') + # validate() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/stub b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Python/stub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Resources/Atom/StandardPBR_AllProperties.material b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Resources/Atom/StandardPBR_AllProperties.material new file mode 100644 index 0000000000..7d38d1a1a3 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Resources/Atom/StandardPBR_AllProperties.material @@ -0,0 +1,47 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "general": { + "texcoord": 0 + }, + "baseColor": { + "colorLinear": [ 1.0, 1.0, 1.0 ], + "factor": 1.0, + "useTexture": true, + "textureMap": "EngineAssets/TextureMsg/DefaultNoUVs.tif" + }, + "metallic": { + "factor": 0.0, + "useTexture": false, + "textureMap": "" + }, + "roughness": { + "factor": 1.0, + "useTexture": true, + "textureMap": "EngineAssets/TextureMsg/DefaultNoUVs_spec.tif" + }, + "specularF0": { + "factor": 0.5, + "useTexture": false, + "textureMap": "" + }, + "normal": { + "factor": 1.0, + "useTexture": true, + "textureMap": "EngineAssets/TextureMsg/DefaultNoUVs_ddn.tif" + }, + "opacity": { + "doubleSided": false, + "factor": 1.0, + "cutoutAlpha": false, + "cutoutThreshold": 0.5, + "useBaseColorTextureAlpha": false, + "useTexture": false, + "textureMap": "" + } + } +} + diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/start_service.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/start_service.py new file mode 100644 index 0000000000..ac9d02cf5d --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/start_service.py @@ -0,0 +1,21 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- +"""DCCsi Tool and Application service launcher""" + +def start_service(): + """Not Implemented""" + print('DCCsi.Tools.DCC.start_service() not implemented') + return None + +########################################################################### +# Main Code Block, runs this script as main (testing) +# ------------------------------------------------------------------------- +if __name__ == '__main__': + """Run this file as main""" + + start_service() \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py index 0577c50474..958508c227 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py @@ -26,26 +26,18 @@ import logging as _logging # ------------------------------------------------------------------------- -_ORG_TAG = 'Amazon_Lumberyard' -_APP_TAG = 'DCCsi' -_TOOL_TAG = 'azpy' -_TYPE_TAG = 'module' +# global scope +_PACKAGENAME = 'azpy' -_PACKAGENAME = _TOOL_TAG +__all__ = ['config_utils', + 'constants', + 'env_bool', + 'return_stub', + 'core', + 'dcc', + 'dev', + 'test'] -__all__ = ['config_utils', 'render', - 'constants', 'return_stub', 'synthetic_env', - 'env_base', 'env_bool', 'test', 'dev', - 'lumberyard', 'marmoset'] # 'blender', 'maya', 'substance', 'houdini'] -# ------------------------------------------------------------------------- - - -# ------------------------------------------------------------------------- -# _ROOT_LOGGER = _logging.getLogger() # only use this if debugging -# https://stackoverflow.com/questions/56733085/how-to-know-the-current-file-path-after-being-frozen-into-an-executable-using-cx/56748839 -#os.chdir(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) -# ------------------------------------------------------------------------- -# global space # we need to set up basic access to the DCCsi _MODULE_PATH = os.path.realpath(__file__) # To Do: what if frozen? _DCCSIG_PATH = os.path.normpath(os.path.join(_MODULE_PATH, '../..')) @@ -58,8 +50,11 @@ import azpy.env_bool as env_bool import azpy.constants as constants import azpy.config_utils as config_utils -_G_DEBUG = env_bool.env_bool(constants.ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool.env_bool(constants.ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool.env_bool(constants.ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_LOGLEVEL = int(env_bool.env_bool(constants.ENVAR_DCCSI_LOGLEVEL, int(20))) +if _DCCSI_GDEBUG: + _DCCSI_LOGLEVEL = int(10) # for py2.7 (Maya) we provide this, so we must assume some bootstrapping # has occured, see DccScriptingInterface\\config.py (_DCCSI_PYTHON_LIB_PATH) @@ -69,38 +64,44 @@ try: except: import pathlib2 as pathlib from pathlib import Path -if _G_DEBUG: - print('DCCsi debug breadcrumb, pathlib is: {}'.format(pathlib)) +if _DCCSI_GDEBUG: + print('[DCCsi][AZPY] DEBUG BREADCRUMB, pathlib is: {}'.format(pathlib)) +# ------------------------------------------------------------------------- -# to be continued... +# ------------------------------------------------------------------------- +# set up module logging +#for handler in _logging.root.handlers[:]: + #_logging.root.removeHandler(handler) +_logging.basicConfig(format=constants.FRMT_LOG_LONG, level=_DCCSI_LOGLEVEL) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- # get/set the project name -_LY_DEV = os.getenv(constants.ENVAR_LY_DEV, +_O3DE_DEV = Path(os.getenv(constants.ENVAR_O3DE_DEV, config_utils.get_stub_check_path(in_path=os.getcwd(), - check_stub='engine.json')) + check_stub='engine.json'))) +_LOGGER.debug('_O3DE_DEV" {}'.format(_O3DE_DEV.resolve())) + +_O3DE_PROJECT_PATH = Path(os.getenv(constants.ENVAR_O3DE_PROJECT_PATH, + config_utils.get_o3de_project_path())) +_LOGGER.debug('_O3DE_PROJECT_PATH" {}'.format(_O3DE_PROJECT_PATH.resolve())) # get/set the project name -_LY_PROJECT_NAME = os.getenv(constants.ENVAR_LY_PROJECT, - config_utils.get_current_project().name) +if _O3DE_PROJECT_PATH: + _O3DE_PROJECT = str(os.getenv(constants.ENVAR_O3DE_PROJECT, + _O3DE_PROJECT_PATH.name)) +else: + _O3DE_PROJECT='o3de' # project cache log dir path -_DCCSI_LOG_PATH = Path(os.getenv(constants.ENVAR_DCCSI_LOG_PATH, - Path(_LY_DEV, - _LY_PROJECT_NAME, - 'Cache', - 'pc', 'user', 'log', 'logs'))) - - -for handler in _logging.root.handlers[:]: - _logging.root.removeHandler(handler) - -# very basic root logger for early debugging, flip to while 1: -while 0: - _logging.basicConfig(level=_logging.DEBUG, - format=constants.FRMT_LOG_LONG, - datefmt='%m-%d %H:%M') - - _logging.debug('azpy.rootlogger> root logger set up for debugging') # root logger +from azpy.constants import TAG_DCCSI_NICKNAME +from azpy.constants import PATH_DCCSI_LOG_PATH +_DCCSI_LOG_PATH = Path(PATH_DCCSI_LOG_PATH.format(O3DE_PROJECT_PATH=_O3DE_PROJECT_PATH.resolve(), + TAG_DCCSI_NICKNAME=TAG_DCCSI_NICKNAME)) # ------------------------------------------------------------------------- @@ -145,14 +146,15 @@ if sys.version_info.major < 3: # ------------------------------------------------------------------------- def initialize_logger(name, log_to_file=False, - default_log_level=_logging.NOTSET): + default_log_level=_logging.NOTSET, + propogate=False): """Start a azpy logger""" _logger = _logging.getLogger(name) - _logger.propagate = False + _logger.propagate = propogate if not _logger.handlers: _log_level = int(os.getenv('DCCSI_LOGLEVEL', default_log_level)) - if _G_DEBUG: + if _DCCSI_GDEBUG: _log_level = int(10) # force when debugging print('_log_level: {}'.format(_log_level)) @@ -201,27 +203,21 @@ def initialize_logger(name, return _logger # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- -# set up logger with both console and file _logging -if _G_DEBUG: - _LOGGER = initialize_logger(_PACKAGENAME, log_to_file=True) -else: - _LOGGER = initialize_logger(_PACKAGENAME, log_to_file=False) - -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) - # some simple logger tests # evoke the filehandlers and test writting to the log file -if _G_DEBUG: +if _DCCSI_GDEBUG: _LOGGER.info('Forced Info! for {0}.'.format({_PACKAGENAME})) _LOGGER.error('Forced ERROR! for {0}.'.format({_PACKAGENAME})) # debug breadcrumbs to check this module and used paths _LOGGER.debug('MODULE_PATH: {}'.format(_MODULE_PATH)) -_LOGGER.debug('LY_DEV_PATH: {}'.format(_LY_DEV)) +_LOGGER.debug('O3DE_DEV_PATH: {}'.format(_O3DE_DEV)) _LOGGER.debug('DCCSI_PATH: {}'.format(_DCCSIG_PATH)) -_LOGGER.debug('LY_PROJECT_TAG: {}'.format(_LY_PROJECT_NAME)) +_LOGGER.debug('O3DE_PROJECT_TAG: {}'.format(_O3DE_PROJECT)) _LOGGER.debug('DCCSI_LOG_PATH: {}'.format(_DCCSI_LOG_PATH)) +# ------------------------------------------------------------------------- # ------------------------------------------------------------------------- @@ -258,9 +254,9 @@ del _LOGGER # Main Code Block, runs this script as main (testing) # ------------------------------------------------------------------------- if __name__ == '__main__': - _G_DEBUG = True + _DCCSI_GDEBUG = True _DCCSI_DEV_MODE = True - if _G_DEBUG: + if _DCCSI_GDEBUG: print(_DCCSIG_PATH) test_imports() diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py index 2df85cbfaa..f6b71a7d97 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py @@ -18,14 +18,31 @@ import logging as _logging # -------------------------------------------------------------------------- -_PACKAGENAME = 'azpy.config_utils' +# note: this module is called in other root modules +# must avoid cyclical imports +# global scope +# normally would pull the constant envar string +# but avoiding cyclical imports here FRMT_LOG_LONG = "[%(name)s][%(levelname)s] >> %(message)s (%(asctime)s; %(filename)s:%(lineno)d)" -_logging.basicConfig(level=_logging.INFO, - format=FRMT_LOG_LONG, - datefmt='%m-%d %H:%M') -_LOGGER = _logging.getLogger(_PACKAGENAME) -_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) +from azpy.env_bool import env_bool +_DCCSI_GDEBUG = env_bool('DCCSI_GDEBUG', False) +_DCCSI_LOGLEVEL = env_bool('DCCSI_LOGLEVEL', False) +_DCCSI_LOGLEVEL = int(env_bool('DCCSI_LOGLEVEL', int(20))) +if _DCCSI_GDEBUG: + _DCCSI_LOGLEVEL = int(10) + +_MODULENAME = __name__ +if _MODULENAME is '__main__': + _MODULENAME = 'azpy.config_utils' + +# set up module logging +#for handler in _logging.root.handlers[:]: + #_logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_MODULENAME) +#_logging.basicConfig(format=FRMT_LOG_LONG, level=_DCCSI_LOGLEVEL) +_LOGGER.propagate = False +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) __all__ = ['get_os', 'return_stub', 'get_stub_check_path', 'get_dccsi_config', 'get_current_project'] @@ -34,13 +51,15 @@ __all__ = ['get_os', 'return_stub', 'get_stub_check_path', # ------------------------------------------------------------------------- # just a quick check to ensure what paths have code access -_G_DEBUG = False # enable for debug prints -if _G_DEBUG: +if _DCCSI_GDEBUG: known_paths = list() for p in sys.path: known_paths.append(p) _LOGGER.debug(known_paths) +# ------------------------------------------------------------------------- + +# ------------------------------------------------------------------------- # this import can fail in Maya 2020 (and earlier) stuck on py2.7 # wrapped in a try, to trap and providing messaging to help user correct try: @@ -76,6 +95,15 @@ def get_os(): # ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- +from azpy.core import get_datadir +# there was a method here refactored out to add py2.7 support for Maya 2020 +#"DccScriptingInterface\azpy\core\py2\utils.py get_datadir()" +#"DccScriptingInterface\azpy\core\py3\utils.py get_datadir()" +# Warning: planning to deprecate py2 support +# ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- def return_stub_dir(stub_file='dccsi_stub'): _dir_to_last_file = None @@ -126,6 +154,30 @@ def get_stub_check_path(in_path=os.getcwd(), check_stub='engine.json'): # ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- +def get_o3de_engine_root(check_stub='engine.json'): + # get the O3DE engine root folder + # if we are running within O3DE we can ensure which engine is running + _O3DE_DEV = None + try: + import azlmbr # this file will fail outside of O3DE + except ImportError as e: + # if that fails, we can search up + # search up to get \dev + _O3DE_DEV = get_stub_check_path(check_stub='engine.json') + # To Do: What if engine.json doesn't exist? + else: + # execute if no exception + # allow for external ENVAR override + from azpy.constants import ENVAR_O3DE_DEV + _O3DE_DEV = Path(os.getenv(ENVAR_O3DE_DEV, azlmbr.paths.engroot)) + finally: + # note: can't use fstrings as this module gets called with py2.7 in maya + _LOGGER.info('O3DE engine root: {}'.format(_O3DE_DEV.resolve())) + return _O3DE_DEV +# ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- # settings.setenv() # doing this will add the additional DYNACONF_ envars def get_dccsi_config(dccsi_dirpath=return_stub_dir()): @@ -176,46 +228,75 @@ def get_current_project_cfg(dev_folder=get_stub_check_path()): # ------------------------------------------------------------------------- -def get_current_project(): +def get_check_global_project(): """Gets o3de project via .o3de data in user directory""" from azpy.constants import PATH_USER_O3DE_BOOTSTRAP from collections import OrderedDict from box import Box + from azpy.core import get_datadir bootstrap_box = None - - try: - bootstrap_box = Box.from_json(filename=str(Path(PATH_USER_O3DE_BOOTSTRAP).resolve()), - encoding="utf-8", - errors="strict", - object_pairs_hook=OrderedDict) - except Exception as e: - # this file runs in py2.7 for Maya 2020, FileExistsError is not defined - _LOGGER.error('FileExistsError: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) - _LOGGER.error('exception is: {}'.format(e)) - + json_file_path = Path(PATH_USER_O3DE_BOOTSTRAP) + if json_file_path.exists(): + try: + bootstrap_box = Box.from_json(filename=str(json_file_path.resolve()), + encoding="utf-8", + errors="strict", + object_pairs_hook=OrderedDict) + except IOError as e: + # this file runs in py2.7 for Maya 2020, FileExistsError is not defined + _LOGGER.error('Bad file interaction: {}'.format(json_file_path.resolve())) + _LOGGER.error('Exception is: {}'.format(e)) + pass if bootstrap_box: # this seems fairly hard coded - what if the data changes? project_path=Path(bootstrap_box.Amazon.AzCore.Bootstrap.project_path) - return project_path.resolve() + return project_path else: return None # ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- +def get_o3de_project_path(): + """figures out the o3de project path + if not found defaults to the engine folder""" + _O3DE_PROJECT_PATH = None + try: + import azlmbr # this file will fail outside of O3DE + except ImportError as e: + # (fallback 1) this checks if a global project is set + # This check user home for .o3de data + _O3DE_PROJECT_PATH = get_check_global_project() + else: + # execute if no exception, this would indicate we are in O3DE land + # allow for external ENVAR override + from azpy.constants import ENVAR_O3DE_PROJECT_PATH + _O3DE_PROJECT_PATH = Path(os.getenv(ENVAR_O3DE_PROJECT_PATH, azlmbr.paths.projectroot)) + finally: + # (fallback 2) if None, fallback to engine folder + if not _O3DE_PROJECT_PATH: + _O3DE_PROJECT_PATH = get_o3de_engine_root() + # note: can't use fstrings as this module gets called with py2.7 in maya + _LOGGER.info('O3DE project root: {}'.format(_O3DE_PROJECT_PATH.resolve())) + return _O3DE_PROJECT_PATH +# ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- 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 = STR_DCCSI_PYTHON_LIB_PATH.format(dccsi_dirpath, - sys.version_info[0], - sys.version_info[1]) + _DCCSI_PYTHON_LIB_PATH = 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)) - site.addsitedir(_DCCSI_PYTHON_LIB_PATH) # PYTHONPATH + if _DCCSI_PYTHON_LIB_PATH.exists(): + site.addsitedir(_DCCSI_PYTHON_LIB_PATH.resolve()) # PYTHONPATH + _LOGGER.debug('Performed site.addsitedir({})' + ''.format(_DCCSI_PYTHON_LIB_PATH.resolve())) return _DCCSI_PYTHON_LIB_PATH else: message = "Doesn't exist: {}".format(_DCCSI_PYTHON_LIB_PATH) @@ -243,13 +324,10 @@ if __name__ == '__main__': _config = get_dccsi_config() _LOGGER.info('DCCSI_CONFIG_PATH: {}'.format(_config)) - _LOGGER.info('LY_DEV: {}'.format(get_stub_check_path('engine.json'))) - - # this will be deprecated and shouldn't work soon (returns None) - _LOGGER.info('LY_PROJECT: {}'.format(get_current_project_cfg(get_stub_check_path('bootstrap.cfg')))) + _LOGGER.info('O3DE_DEV: {}'.format(get_o3de_engine_root(check_stub='engine.json'))) # new o3de version - _LOGGER.info('LY_PROJECT: {}'.format(get_current_project())) + _LOGGER.info('O3DE_PROJECT: {}'.format(get_check_global_project())) _LOGGER.info('DCCSI_PYTHON_LIB_PATH: {}'.format(bootstrap_dccsi_py_libs(return_stub_dir('dccsi_stub')))) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py index b9619fa584..d23601a33d 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py @@ -25,7 +25,16 @@ import sys import site from os.path import expanduser import logging as _logging +# ------------------------------------------------------------------------- + +# ------------------------------------------------------------------------- +# global scope +_MODULENAME = __name__ +if _MODULENAME is '__main__': + _MODULENAME = 'azpy.constants' + +os.environ['PYTHONINSPECT'] = 'True' # for this module to perform standalone # we need to set up basic access to the DCCsi _MODULE_PATH = os.path.realpath(__file__) # To Do: what if frozen? @@ -33,10 +42,10 @@ _DCCSIG_PATH = os.path.normpath(os.path.join(_MODULE_PATH, '../..')) _DCCSIG_PATH = os.getenv('DCCSIG_PATH', _DCCSIG_PATH) site.addsitedir(_DCCSIG_PATH) -# azpy module -#import azpy.constants as cnst +# now we have azpy api access +import azpy +from azpy.env_bool import env_bool from azpy.config_utils import return_stub_dir -import azpy.env_bool as env_bool # ------------------------------------------------------------------------- @@ -49,24 +58,26 @@ ENVAR_DCCSI_LOGLEVEL = str('DCCSI_LOGLEVEL') # Log formating FRMT_LOG_LONG = "[%(name)s][%(levelname)s] >> %(message)s (%(asctime)s; %(filename)s:%(lineno)d)" FRMT_LOG_SHRT = "[%(asctime)s][%(name)s][%(levelname)s] >> %(message)s" +# ------------------------------------------------------------------------- -# global space -_G_DEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) +# ------------------------------------------------------------------------- +# global debug stuff +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_LOGLEVEL = int(env_bool(ENVAR_DCCSI_LOGLEVEL, int(20))) +if _DCCSI_GDEBUG: + _DCCSI_LOGLEVEL = int(10) +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# set up module logging for handler in _logging.root.handlers[:]: _logging.root.removeHandler(handler) - -_PACKAGENAME = 'azpy.constants' - -_LOG_LEVEL = int(20) -if _G_DEBUG: - _LOG_LEVEL = int(10) -_logging.basicConfig(level=_LOG_LEVEL, - format=FRMT_LOG_LONG, - datefmt='%m-%d %H:%M') -_LOGGER = _logging.getLogger(_PACKAGENAME) -_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) +_LOGGER = _logging.getLogger(_MODULENAME) +_logging.basicConfig(format=FRMT_LOG_LONG, level=_DCCSI_LOGLEVEL) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) # ------------------------------------------------------------------------- @@ -80,24 +91,25 @@ STR_CROSSBAR_RL = str('{0}\r'.format(STR_CROSSBAR)) STR_CROSSBAR_NL = str('{0}\n'.format(STR_CROSSBAR)) # some common str tags -TAG_DEFAULT_COMPANY = str('Amazon.Lumberyard') +TAG_DEFAULT_COMPANY = str('Amazon.O3DE') TAG_DEFAULT_PROJECT = str('DccScriptingInterface') +TAG_DCCSI_NICKNAME = str('DCCsi') TAG_MOCK_PROJECT = str('MockProject') -TAG_DIR_LY_DEV = str('dev') +TAG_DIR_O3DE_DEV = str('dev') TAG_DIR_DCCSI_AZPY = str('azpy') -TAG_DIR_DCCSI_SDK = str('SDK') -TAG_DIR_LY_BUILD = str('build') +TAG_DIR_DCCSI_TOOLS = str('Tools') +TAG_DIR_O3DE_BUILD_FOLDER = str('build') TAG_QT_PLUGIN_PATH = str('QT_PLUGIN_PATH') TAG_O3DE_FOLDER = str('.o3de') TAG_O3DE_BOOTSTRAP = str('bootstrap.setreg') +TAG_DCCSI_CONFIG = str('dccsiconfiguration.setreg') # filesystem markers, stub file names. -STUB_LY_DEV = str('engine.json') -STUB_LY_ROOT_PROJECT = str('ly_project_stub') -STUB_LY_ROOT_DCCSI = str('dccsi_stub') -STUB_LY_DCCSI_AZPY = str('dccsi_azpy_stub') -STUB_LY_DCCSI_SDK = str('dccsi_sdk_stub') +STUB_O3DE_DEV = str('engine.json') +STUB_O3DE_ROOT_DCCSI = str('dccsi_stub') +STUB_O3DE_DCCSI_AZPY = str('dccsi_azpy_stub') +STUB_O3DE_DCCSI_TOOLS = str('dccsi_tools_stub') # config string consts, Meta Qualifiers QUALIFIER_COMMENT = str('_meta_COMMENT') @@ -135,18 +147,18 @@ PATH_PROGRAMFILES_X64 = str(os.environ['PROGRAMFILES']) # base env var key as str ENVAR_COMPANY = str('COMPANY') -ENVAR_LY_PROJECT = str('LY_PROJECT') -ENVAR_LY_PROJECT_PATH = str('LY_PROJECT_PATH') -ENVAR_LY_DEV = str('LY_DEV') +ENVAR_O3DE_PROJECT = str('O3DE_PROJECT') +ENVAR_O3DE_PROJECT_PATH = str('O3DE_PROJECT_PATH') +ENVAR_O3DE_DEV = str('O3DE_DEV') ENVAR_DCCSIG_PATH = str('DCCSIG_PATH') ENVAR_DCCSI_AZPY_PATH = str('DCCSI_AZPY_PATH') -ENVAR_DCCSI_SDK_PATH = str('DCCSI_SDK_PATH') -ENVAR_LY_BUILD_DIR_NAME = str('LY_BUILD_DIR_NAME') +ENVAR_DCCSI_TOOLS_PATH = str('DCCSI_TOOLS_PATH') +ENVAR_O3DE_BUILD_DIR_NAME = str('O3DE_BUILD_DIR_NAME') -ENVAR_LY_BUILD_PATH = str('LY_BUILD_PATH') +ENVAR_O3DE_BUILD_PATH = str('O3DE_BUILD_PATH') ENVAR_QT_PLUGIN_PATH = TAG_QT_PLUGIN_PATH ENVAR_QTFORPYTHON_PATH = str('QTFORPYTHON_PATH') -ENVAR_LY_BIN_PATH = str('LY_BIN_PATH') +ENVAR_O3DE_BIN_PATH = str('O3DE_BIN_PATH') ENVAR_DCCSI_LOG_PATH = str('DCCSI_LOG_PATH') ENVAR_DCCSI_LAUNCHERS_PATH = str('DCCSI_LAUNCHERS_PATH') @@ -155,7 +167,7 @@ ENVAR_DCCSI_PY_VERSION_MAJOR = str('DCCSI_PY_VERSION_MAJOR') ENVAR_DCCSI_PY_VERSION_MINOR = str('DCCSI_PY_VERSION_MINOR') ENVAR_DCCSI_PYTHON_PATH = str('DCCSI_PYTHON_PATH') ENVAR_DCCSI_PYTHON_LIB_PATH = str('DCCSI_PYTHON_LIB_PATH') -ENVAR_DCCSI_PYTHON_INSTALL = str('DCCSI_PYTHON_INSTALL') +ENVAR_O3DE_PYTHON_INSTALL = str('O3DE_PYTHON_INSTALL') ENVAR_WINGHOME = str('WINGHOME') ENVAR_DCCSI_WING_VERSION_MAJOR = str('DCCSI_WING_VERSION_MAJOR') @@ -169,7 +181,7 @@ ENVAR_DCCSI_PY_DEFAULT = str('DCCSI_PY_DEFAULT') ENVAR_DCCSI_MAYA_VERSION = str('DCCSI_MAYA_VERSION') ENVAR_MAYA_LOCATION = str('MAYA_LOCATION') -ENVAR_DCCSI_SDK_MAYA_PATH = str('DCCSI_SDK_MAYA_PATH') +ENVAR_DCCSI_TOOLS_MAYA_PATH = str('DCCSI_TOOLS_MAYA_PATH') ENVAR_MAYA_MODULE_PATH = str('MAYA_MODULE_PATH') ENVAR_MAYA_BIN_PATH = str('MAYA_BIN_PATH') @@ -188,33 +200,33 @@ ENVAR_MAYA_SCRIPT_PATH = str('MAYA_SCRIPT_PATH') ENVAR_DCCSI_MAYA_SET_CALLBACKS = str('DCCSI_MAYA_SET_CALLBACKS') -TAG_LY_DCC_MAYA_MEL = 'dccsi_setup.mel' +TAG_O3DE_DCC_MAYA_MEL = 'dccsi_setup.mel' TAG_MAYA_WORKSPACE = 'workspace.mel' # dcc scripting interface common and default paths -PATH_LY_DEV = str(return_stub_dir(STUB_LY_DEV)) -PATH_DCCSIG_PATH = str(return_stub_dir(STUB_LY_ROOT_DCCSI)) -PATH_DCCSI_AZPY_PATH = str(return_stub_dir(STUB_LY_DCCSI_AZPY)) -PATH_DCCSI_SDK_PATH = str('{0}\\{1}'.format(PATH_DCCSIG_PATH, TAG_DIR_DCCSI_SDK)) +PATH_O3DE_DEV = str(return_stub_dir(STUB_O3DE_DEV)) +PATH_DCCSIG_PATH = str(return_stub_dir(STUB_O3DE_ROOT_DCCSI)) +PATH_DCCSI_AZPY_PATH = str(return_stub_dir(STUB_O3DE_DCCSI_AZPY)) +PATH_DCCSI_TOOLS_PATH = str('{0}\\{1}'.format(PATH_DCCSIG_PATH, TAG_DIR_DCCSI_TOOLS)) # logging into the cache -PATH_DCCSI_LOG_PATH = str('{LY_DEV}\\Cache\\{LY_PROJECT}\\pc\\user\\log\\logs') +PATH_DCCSI_LOG_PATH = str('{O3DE_PROJECT_PATH}\\user\\log\{TAG_DCCSI_NICKNAME}') # dev \ \ -STR_CONSTRUCT_LY_BUILD_PATH = str('{0}\\{1}') -PATH_LY_BUILD_PATH = str(STR_CONSTRUCT_LY_BUILD_PATH.format(PATH_LY_DEV, - TAG_DIR_LY_BUILD)) +STR_CONSTRUCT_O3DE_BUILD_PATH = str('{0}\\{1}') +PATH_O3DE_BUILD_PATH = str(STR_CONSTRUCT_O3DE_BUILD_PATH.format(PATH_O3DE_DEV, + TAG_DIR_O3DE_BUILD_FOLDER)) # ENVAR_QT_PLUGIN_PATH = TAG_QT_PLUGIN_PATH STR_QTPLUGIN_DIR = str('{0}\\bin\\profile\\EditorPlugins') STR_QTFORPYTHON_PATH = str('{0}\\Gems\\QtForPython\\3rdParty\\pyside2\\windows\\release') -STR_LY_BIN_PATH = str('{0}\\bin\\profile') +STR_O3DE_BIN_PATH = str('{0}\\bin\\profile') -PATH_LY_BUILD_PATH = str('{0}\\{1}'.format(PATH_LY_DEV, TAG_DIR_LY_BUILD)) -PATH_QTFORPYTHON_PATH = str(STR_QTFORPYTHON_PATH.format(PATH_LY_DEV)) -PATH_QT_PLUGIN_PATH = str(STR_QTPLUGIN_DIR).format(PATH_LY_BUILD_PATH) -PATH_LY_BIN_PATH = str(STR_LY_BIN_PATH).format(PATH_LY_BUILD_PATH) +PATH_O3DE_BUILD_PATH = str('{0}\\{1}'.format(PATH_O3DE_DEV, TAG_DIR_O3DE_BUILD_FOLDER)) +PATH_QTFORPYTHON_PATH = str(STR_QTFORPYTHON_PATH.format(PATH_O3DE_DEV)) +PATH_QT_PLUGIN_PATH = str(STR_QTPLUGIN_DIR).format(PATH_O3DE_BUILD_PATH) +PATH_O3DE_BIN_PATH = str(STR_O3DE_BIN_PATH).format(PATH_O3DE_BUILD_PATH) # py path string, parts, etc. TAG_DEFAULT_PY = str('Launch_pyBASE.bat') @@ -233,12 +245,19 @@ parts = os.path.split(PATH_USER_HOME) if str(parts[1].lower()) == 'documents': PATH_USER_HOME = parts[0] _LOGGER.debug('user home CORRECTED: {}'.format(PATH_USER_HOME)) + +STR_USER_O3DE_PATH = str('{home}\\{o3de}') -PATH_USER_O3DE = str('{home}\\{o3de}').format(home=PATH_USER_HOME, +PATH_USER_O3DE = str(STR_USER_O3DE_PATH).format(home=PATH_USER_HOME, o3de=TAG_O3DE_FOLDER) -PATH_USER_O3DE_REGISTRY = str('{0}\\Registry').format(PATH_USER_O3DE) -PATH_USER_O3DE_BOOTSTRAP = str('{reg}\\{file}').format(reg=PATH_USER_O3DE_REGISTRY, - file=TAG_O3DE_BOOTSTRAP) + +TAG_DIR_REGISTRY = str('Registry') +STR_USER_O3DE_REGISTRY_PATH = str('{0}\\{1}') +PATH_USER_O3DE_REGISTRY = str(STR_USER_O3DE_REGISTRY_PATH).format(PATH_USER_O3DE, TAG_DIR_REGISTRY) + +STR_USER_O3DE_BOOTSTRAP_PATH = str('{reg}\\{file}') +PATH_USER_O3DE_BOOTSTRAP = str(STR_USER_O3DE_BOOTSTRAP_PATH).format(reg=PATH_USER_O3DE_REGISTRY, + file=TAG_O3DE_BOOTSTRAP) #python and site-dir TAG_DCCSI_PY_VERSION_MAJOR = str(3) @@ -247,8 +266,8 @@ TAG_DCCSI_PY_VERSION_RELEASE = str(10) TAG_PYTHON_EXE = str('python.exe') TAG_TOOLS_DIR = str('Tools\\Python') TAG_PLATFORM = str('windows') -STR_CONSTRUCT_DCCSI_PYTHON_INSTALL = str('{0}\\{1}\\{2}.{3}.{4}\\{5}') -PATH_DCCSI_PYTHON_PATH = str(STR_CONSTRUCT_DCCSI_PYTHON_INSTALL.format(PATH_LY_DEV, +STR_CONSTRUCT_O3DE_PYTHON_INSTALL = str('{0}\\{1}\\{2}.{3}.{4}\\{5}') +PATH_DCCSI_PYTHON_PATH = str(STR_CONSTRUCT_O3DE_PYTHON_INSTALL.format(PATH_O3DE_DEV, TAG_TOOLS_DIR, TAG_DCCSI_PY_VERSION_MAJOR, TAG_DCCSI_PY_VERSION_MINOR, @@ -290,19 +309,12 @@ PATH_SAT_INSTALL_PATH = str('{0}\\{1}\\{2}\\{3}\\{4}' # Main Code Block, runs this script as main (testing) # ------------------------------------------------------------------------- if __name__ == '__main__': - # there are not really tests to run here due to this being a list of - # constants for shared use. - _G_DEBUG = True - _DCCSI_DEV_MODE = True - _LOGGER.setLevel(_logging.DEBUG) # force debugging - - # this is a top level module and to reduce cyclical azpy imports - # it only has a basic logger configured, add log to console - _handler = _logging.StreamHandler(sys.stdout) - _handler.setLevel(_logging.DEBUG) - _formatter = _logging.Formatter(FRMT_LOG_LONG) - _handler.setFormatter(_formatter) - _LOGGER.addHandler(_handler) + """Run this file as a standalone script""" + + # overide logger for standalone to be more verbose and lof to file + _LOGGER = azpy.initialize_logger(_MODULENAME, + log_to_file=_DCCSI_GDEBUG, + default_log_level=_DCCSI_LOGLEVEL) # happy print _LOGGER.info(STR_CROSSBAR) @@ -321,15 +333,15 @@ if __name__ == '__main__': from pathlib import Path _stash_dict = {} - _stash_dict['LY_DEV'] = Path(PATH_LY_DEV) + _stash_dict['O3DE_DEV'] = Path(PATH_O3DE_DEV) _stash_dict['DCCSIG_PATH'] = Path(PATH_DCCSIG_PATH) _stash_dict['DCCSI_AZPY_PATH'] = Path(PATH_DCCSI_AZPY_PATH) - _stash_dict['DCCSI_SDK_PATH'] = Path(PATH_DCCSI_SDK_PATH) + _stash_dict['DCCSI_TOOLS_PATH'] = Path(PATH_DCCSI_TOOLS_PATH) _stash_dict['DCCSI_PYTHON_PATH'] = Path(PATH_DCCSI_PYTHON_PATH) _stash_dict['DCCSI_PY_BASE'] = Path(PATH_DCCSI_PY_BASE) _stash_dict['DCCSI_PYTHON_LIB_PATH'] = Path(PATH_DCCSI_PYTHON_LIB_PATH) - _stash_dict['LY_BUILD_PATH'] = Path(PATH_LY_BUILD_PATH) - _stash_dict['LY_BIN_PATH'] = Path(PATH_LY_BIN_PATH) + _stash_dict['O3DE_BUILD_PATH'] = Path(PATH_O3DE_BUILD_PATH) + _stash_dict['O3DE_BIN_PATH'] = Path(PATH_O3DE_BIN_PATH) _stash_dict['QTFORPYTHON_PATH'] = Path(PATH_QTFORPYTHON_PATH) _stash_dict['QT_PLUGIN_PATH'] = Path(PATH_QT_PLUGIN_PATH) _stash_dict['SAT_INSTALL_PATH'] = Path(PATH_SAT_INSTALL_PATH) @@ -339,7 +351,7 @@ if __name__ == '__main__': # py 2 and 3 compatible iter def get_items(dict_object): for key in dict_object: - yield key, dict_object[key] + yield key, dict_object[key] for key, value in get_items(_stash_dict): # check if path exists diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/__init__.py new file mode 100644 index 0000000000..9269739f96 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/__init__.py @@ -0,0 +1,42 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- +__copyright__ = "Copyright 2021, Amazon" +# ------------------------------------------------------------------------- +import sys +import logging as _logging +# ------------------------------------------------------------------------- + +#pulling from azpy.constants causes cyclical imports :( +FRMT_LOG_LONG = "[%(name)s][%(levelname)s] >> %(message)s (%(asctime)s; %(filename)s:%(lineno)d)" +_DCCSI_GDEBUG = False +_DCCSI_LOGLEVEL = int(20) +if _DCCSI_GDEBUG: + _DCCSI_LOGLEVEL = int(10) + +_PACKAGENAME = 'azpy.core' +_logging.basicConfig(format=FRMT_LOG_LONG, level=_DCCSI_LOGLEVEL) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) + +__all__ = [] + +if sys.version_info >= (3, 6): + from azpy.core.py3.utils import get_datadir +elif sys.version_info >= (2, 6) and sys.version_info < (3, 6): + _LOGGER.warning('Python vesion < 3 will be deprecated in the future') + from azpy.core.py2.utils import get_datadir +else: + _LOGGER.warning('Python vesion < 2.6 not recommended') + from azpy.core.py2.utils import get_datadir + +__all__ = ['get_datadir'] # you should hope it works + +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py2/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py2/__init__.py new file mode 100644 index 0000000000..35727ddda6 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py2/__init__.py @@ -0,0 +1,20 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- +__copyright__ = "Copyright 2021, Amazon" +# ------------------------------------------------------------------------- +import logging as _logging +# ------------------------------------------------------------------------- +_PACKAGENAME = 'azpy.core.py2' +_LOGGER = _logging.getLogger(_PACKAGENAME) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) + +__all__ = [] +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py2/utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py2/utils.py new file mode 100644 index 0000000000..3a79b1bccf --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py2/utils.py @@ -0,0 +1,59 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- +"""DCCsi.azpy.core.py2.utils +This module contain versions of utils speciufic to py3 snyntax""" +import sys +import logging as _logging + +try: + import pathlib +except: + import pathlib2 as pathlib + +__all__ = ['get_datadir'] + +_MODULENAME = 'azpy.core.py2.utils' +_LOGGER = _logging.getLogger(_MODULENAME) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) + +# ------------------------------------------------------------------------- +def get_datadir(): + """ + persistent application data. + # linux: ~/.local/share + # macOS: ~/Library/Application Support + # windows: C:/Users//AppData/Roaming + """ + + home = pathlib.Path.home() + + if sys.platform.startswith('win'): + datadir = pathlib.Path(home, "AppData/Roaming") + return datadir + elif sys.platform == "linux": + datadir = pathlib.Path(home, ".local/share") + return datadir + elif sys.platform == "darwin": + datadir = pathlib.Path(home, "Library/Application Support") + return datadir + else: # unknown + return None +# ------------------------------------------------------------------------- + + +########################################################################### +# Main Code Block, runs this script as main (testing) +# ------------------------------------------------------------------------- +if __name__ == '__main__': + """module testing""" + + user_data_dir = get_datadir() + _LOGGER.info(user_data_dir.resolve()) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py3/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py3/__init__.py new file mode 100644 index 0000000000..f5d26c9ebe --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py3/__init__.py @@ -0,0 +1,20 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- +__copyright__ = "Copyright 2021, Amazon" +# ------------------------------------------------------------------------- +import logging as _logging +# ------------------------------------------------------------------------- +_PACKAGENAME = 'azpy.core.py3' +_LOGGER = _logging.getLogger(_PACKAGENAME) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) + +__all__ = [] +# ------------------------------------------------------------------------- \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py3/utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py3/utils.py new file mode 100644 index 0000000000..cca07eb7dc --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/core/py3/utils.py @@ -0,0 +1,52 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- +"""DCCsi.azpy.core.py3.utils +This module contain versions of utils speciufic to py3 snyntax""" +import sys +import pathlib +import logging as _logging + +__all__ = ['get_datadir'] + +_MODULENAME = 'azpy.core.py3.utils' +_LOGGER = _logging.getLogger(_MODULENAME) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) + +# ------------------------------------------------------------------------- +def get_datadir() -> pathlib.Path: + """ + persistent application data. + # linux: ~/.local/share + # macOS: ~/Library/Application Support + # windows: C:/Users//AppData/Roaming + """ + + home = pathlib.Path.home() + + if sys.platform.startswith('win'): + return home / "AppData/Roaming" + elif sys.platform == "linux": + return home / ".local/share" + elif sys.platform == "darwin": + return home / "Library/Application Support" + else: # unknown + return None +# ------------------------------------------------------------------------- + + +########################################################################### +# Main Code Block, runs this script as main (testing) +# ------------------------------------------------------------------------- +if __name__ == '__main__': + """module testing""" + + user_data_dir = get_datadir() + _LOGGER.info(user_data_dir.resolve()) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/3dsmax/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/3dsmax/__init__.py old mode 100755 new mode 100644 similarity index 71% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/3dsmax/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/3dsmax/__init__.py index 017730d63c..a98de89ad7 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/3dsmax/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/3dsmax/__init__.py @@ -12,29 +12,53 @@ # importing all of the modules """azpy.3dsmax.__init__""" -import os -from azpy.env_bool import env_bool +import logging as _logging + +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.3dsmax' + _PACKAGENAME = 'azpy.dcc.3dsmax' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- - +# These are explicit imports for now __all__ = [] +# To Do: procedurally discover dcc access and extend __all__ +# ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- +def init(): + """If the 3dsmax api is required for a package/module to import, + then it should be initialized and added here so general imports + don't fail""" + + # Make sure we can import the native apis + import pymxs + import MaxPlus + + # extend all with submodules + #__all__.append('foo', 'bar') + + # Importing local packages/modules + pass +# ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- if _DCCSI_DEV_MODE: @@ -46,21 +70,4 @@ if _DCCSI_DEV_MODE: _logger=_LOGGER) # ------------------------------------------------------------------------- - -# ------------------------------------------------------------------------- -def init(): - """If the 3dsmax api is required for a package/module to import, - then it should be initialized and added here so general imports - don't fail""" - - # __all__.append() - - # Make sure we can import the native apis - import pymxs - import MaxPlus - - # Importing local packages/modules - pass -# ------------------------------------------------------------------------- - del _LOGGER diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/__init__.py new file mode 100644 index 0000000000..7f2b402c95 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/__init__.py @@ -0,0 +1,53 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -- This line is 75 characters ------------------------------------------- + +"""azpy.shared.__init__""" + +import logging as _logging + +import azpy.env_bool as env_bool +from azpy.constants import ENVAR_DCCSI_GDEBUG +from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG + +# global space +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) + +_PACKAGENAME = __name__ +if _PACKAGENAME is '__main__': + _PACKAGENAME = 'azpy.dcc' + +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) + +# ------------------------------------------------------------------------- +# These are explicit imports for now +__all__ = [] +# To Do: procedurally discover dcc access and extend __all__ +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +if _DCCSI_DEV_MODE: + # If in dev mode this will test imports of __all__ + from azpy import test_imports + _LOGGER.debug('Testing Imports from {0}'.format(_PACKAGENAME)) + test_imports(__all__, + _pkg=_PACKAGENAME, + _logger=_LOGGER) +# ------------------------------------------------------------------------- + +del _LOGGER diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/blender/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/blender/__init__.py old mode 100755 new mode 100644 similarity index 71% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/blender/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/blender/__init__.py index 903b76e3c4..b46c7146c2 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/blender/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/blender/__init__.py @@ -12,28 +12,49 @@ # importing all of the modules """azpy.blender.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.blender' - -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -#_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) + _PACKAGENAME = 'azpy.dcc.blender' + +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- - +# These are explicit imports for now __all__ = [] +# To Do: procedurally discover dcc access and extend __all__ +# ------------------------------------------------------------------------- + +# ------------------------------------------------------------------------- +def init(): + """If the blender bpy api is required for a package/module to import, + then it should be initialized and added here so general imports + don't fail""" + + # Make sure we can import the native apis + import bpy + + # extend all with submodules + #__all__.append('foo', 'bar') + + # Importing local packages/modules + pass # ------------------------------------------------------------------------- @@ -47,20 +68,4 @@ if _DCCSI_DEV_MODE: _logger=_LOGGER) # ------------------------------------------------------------------------- - -# ------------------------------------------------------------------------- -def init(): - """If the blender bpy api is required for a package/module to import, - then it should be initialized and added here so general imports - don't fail""" - - # __all__.append() - - # Make sure we can import the native apis - import bpy - - # Importing local packages/modules - pass -# ------------------------------------------------------------------------- - del _LOGGER diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/houdini/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/houdini/__init__.py old mode 100755 new mode 100644 similarity index 70% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/houdini/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/houdini/__init__.py index c0b1ced437..0c16872073 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/houdini/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/houdini/__init__.py @@ -12,28 +12,32 @@ # importing all of the modules """azpy.houdini.__init__""" -import os +import logging as _logging -from azpy import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.houdini' + _PACKAGENAME = 'azpy.dcc.houdini' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -#_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- - +# These are explicit imports for now __all__ = [] - +# To Do: procedurally discover dcc access and extend __all__ # ------------------------------------------------------------------------- @@ -53,12 +57,13 @@ def init(): """If the houdini api is required for a package/module to import, then it should be initialized and added here so general imports don't fail""" - - # __all__.append() # Make sure we can import the native apis import hou - + + # extend all with submodules + #__all__.append('foo', 'bar') + # Importing local packages/modules pass # ------------------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/marmoset/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/marmoset/__init__.py old mode 100755 new mode 100644 similarity index 70% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/marmoset/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/marmoset/__init__.py index 35a081dcbf..7e95b5f19e --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/marmoset/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/marmoset/__init__.py @@ -12,28 +12,32 @@ # importing all of the modules """azpy.houdini.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.marmoset' + _PACKAGENAME = 'azpy.dcc.marmoset' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- - +# These are explicit imports for now __all__ = [] - +# To Do: procedurally discover dcc access and extend __all__ # ------------------------------------------------------------------------- @@ -53,12 +57,13 @@ def init(): """If the marmoset api is required for a package/module to import, then it should be initialized and added here so general imports don't fail""" - - # __all__.append() # Make sure we can import the native apis import mset - + + # extend all with submodules + #__all__.append('foo', 'bar') + # Importing local packages/modules pass # ------------------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/__init__.py old mode 100755 new mode 100644 similarity index 64% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/__init__.py index e66ee63739..870ace6166 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/__init__.py @@ -10,40 +10,32 @@ # -- This line is 75 characters ------------------------------------------- # The __init__.py files help guide import statements without automatically # importing all of the modules -"""azpy.maya.__init__""" +"""azpy.dcc.maya.__init__""" -from azpy.env_bool import env_bool +import logging as _logging + +import azpy.env_bool as env_bool +from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya' + _PACKAGENAME = 'azpy.dcc.maya' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) - -# ------------------------------------------------------------------------- +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) __all__ = [] -# ------------------------------------------------------------------------- - - -# ------------------------------------------------------------------------- -if _DCCSI_DEV_MODE: - # If in dev mode this will test imports of __all__ - from azpy import test_imports - _LOGGER.debug('Testing Imports from {0}'.format(_PACKAGENAME)) - test_imports(__all__, - _pkg=_PACKAGENAME, - _logger=_LOGGER) -# ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- def init(): """If the maya api is required for a package/module to import, diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/__init__.py old mode 100755 new mode 100644 similarity index 64% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/__init__.py index 3f36bb0fba..7de93fe2bb --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/__init__.py @@ -12,23 +12,26 @@ # importing all of the modules """azpy.maya.callbacks.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -# global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': _PACKAGENAME = 'azpy.maya.callbacks' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) __all__ = ['event_callback_handler', 'node_message_callback_handler', diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/event_callback_handler.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/event_callback_handler.py old mode 100755 new mode 100644 similarity index 98% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/event_callback_handler.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/event_callback_handler.py index 7603e969e3..d2ca596d3c --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/event_callback_handler.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/event_callback_handler.py @@ -73,12 +73,12 @@ import maya.api.OpenMaya as openmaya #-------------------------------------------------------------------------- # -- Misc Global Space Definitions -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya.callbacks.event_callback_handler' + _PACKAGENAME = 'azpy.dcc.maya.callbacks.event_callback_handler' _LOGGER = azpy.initialize_logger(_PACKAGENAME, default_log_level=int(20)) _LOGGER.debug('Invoking:: {0}.'.format({_PACKAGENAME})) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/node_message_callback_handler.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/node_message_callback_handler.py old mode 100755 new mode 100644 similarity index 98% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/node_message_callback_handler.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/node_message_callback_handler.py index cfb883be18..e41c9dbbc7 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/node_message_callback_handler.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/node_message_callback_handler.py @@ -80,12 +80,12 @@ import maya.cmds as mc # ------------------------------------------------------------------------- # -- Misc Global Space Definitions -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya.callbacks.event_callback_handler' + _PACKAGENAME = 'azpy.dcc.maya.callbacks.event_callback_handler' _LOGGER = azpy.initialize_logger(_PACKAGENAME, default_log_level=int(20)) _LOGGER.debug('Invoking:: {0}.'.format({_PACKAGENAME})) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/on_shader_rename.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/on_shader_rename.py old mode 100755 new mode 100644 similarity index 98% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/on_shader_rename.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/on_shader_rename.py index 84e2426c2b..834505f076 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/callbacks/on_shader_rename.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/callbacks/on_shader_rename.py @@ -85,12 +85,12 @@ import maya.cmds as mc # -------------------------------------------------------------------------- # -- Misc Global Space Definitions -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya.callbacks.on_shader_rename' + _PACKAGENAME = 'azpy.dcc.maya.callbacks.on_shader_rename' _LOGGER = azpy.initialize_logger(_PACKAGENAME, default_log_level=int(20)) _LOGGER.debug('Invoking:: {0}.'.format({_PACKAGENAME})) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/__init__.py old mode 100755 new mode 100644 similarity index 55% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/__init__.py index 7dab357d22..ecb164c1f7 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/__init__.py @@ -10,25 +10,28 @@ # -- This line is 75 characters ------------------------------------------- # The __init__.py files help guide import statements without automatically # importing all of the modules -"""azpy.maya.helpers.__init__""" +"""azpy.dcc.maya.helpers.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -# global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya.callbacks' + _PACKAGENAME = 'azpy.dcc.maya.callbacks' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) __all__ = ['undo_context', 'utils'] diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/undo_context.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/undo_context.py old mode 100755 new mode 100644 similarity index 97% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/undo_context.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/undo_context.py index 90c12af2c9..7a6de2224b --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/undo_context.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/undo_context.py @@ -42,12 +42,12 @@ import maya.cmds as mc # ------------------------------------------------------------------------- # -- Misc Global Space Definitions -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya.helpers.undo_context' + _PACKAGENAME = 'azpy.dcc.maya.helpers.undo_context' _LOGGER = initialize_logger(_PACKAGENAME, default_log_level=int(20)) _LOGGER.debug('Invoking:: {0}.'.format({_PACKAGENAME})) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/utils.py old mode 100755 new mode 100644 similarity index 98% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/utils.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/utils.py index 5fe6cd5876..5f1522e6e1 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/helpers/utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/helpers/utils.py @@ -10,7 +10,7 @@ # -- This line is 75 characters ------------------------------------------- """ -azpy.maya utility module +azpy.dcc.maya utility module """ # ------------------------------------------------------------------------- # built in's @@ -32,12 +32,12 @@ import maya.cmds as cmds # ------------------------------------------------------------------------- # -- Misc Global Space Definitions -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya.helpers.undo_context' + _PACKAGENAME = 'azpy.dcc.maya.helpers.undo_context' _LOGGER = initialize_logger(_PACKAGENAME, default_log_level=int(20)) _LOGGER.debug('Invoking:: {0}.'.format({_PACKAGENAME})) @@ -46,7 +46,7 @@ _LOGGER.debug('Invoking:: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- # Initiate the Wing IDE debug connection. -if _G_DEBUG: +if _DCCSI_GDEBUG: #import azpy.dev.connectDebugger as lyDevConnnect # lyDevConnnect() pass diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/toolbits/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/toolbits/__init__.py new file mode 100644 index 0000000000..41f177bcf0 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/toolbits/__init__.py @@ -0,0 +1,38 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# -------------------------------------------------------------------------- +"""azpy.dcc.maya.toolbits.__init__""" + +import logging as _logging + +import azpy.env_bool as env_bool +from azpy.constants import ENVAR_DCCSI_GDEBUG +from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG + +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) + +_PACKAGENAME = __name__ +if _PACKAGENAME is '__main__': + _PACKAGENAME = 'azpy.dcc.maya.toolbits' + +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) + +__all__ = ['detach'] + +del _LOGGER +#-------------------------------------------------------------------------- + diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/toolbits/detach.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/toolbits/detach.py old mode 100755 new mode 100644 similarity index 94% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/toolbits/detach.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/toolbits/detach.py index 1afcbfa9f6..51d3bb9b66 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/toolbits/detach.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/toolbits/detach.py @@ -44,12 +44,12 @@ from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya.toolbits.detatch' + _PACKAGENAME = 'azpy.dcc.maya.toolbits.detatch' import azpy _LOGGER = azpy.initialize_logger(_PACKAGENAME) @@ -65,13 +65,13 @@ def clean_detach(detachType=0, args=None, name=None, or duplicating those faces without harming the orignal ''' - sel = azpy.maya.helpers.utils.Selection() + sel = azpy.dcc.maya.helpers.utils.Selection() for obj in sel.selection.keys(): print("~ cleanDetach:: Working on: {0}".format(obj)) # set up / open the maya undo context - with azpy.maya.helpers.UndoContext(): + with azpy.dcc.maya.helpers.UndoContext(): if deletHistoyIn: mc.delete( obj, constructionHistory = True) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/__init__.py similarity index 100% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/__init__.py diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/execute_wing_code.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/execute_wing_code.py similarity index 95% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/execute_wing_code.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/execute_wing_code.py index 34aaa2cbb7..3c8e7a75a3 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/execute_wing_code.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/execute_wing_code.py @@ -43,13 +43,13 @@ def get_stub_check_path(in_path=__file__, check_stub='engineroot.txt'): # ------------------------------------------------------------------------- # -- Global Definitions -- -_MODULENAME = 'azpy.maya.utils.execute_wing_code' +_MODULENAME = 'azpy.dcc.maya.utils.execute_wing_code' _LOGGER = _logging.getLogger(_MODULENAME) -_LY_DEV = get_stub_check_path() -_LOGGER.info('_LY_DEV: {}'.format(_LY_DEV)) +_O3DE_DEV = get_stub_check_path() +_LOGGER.info('_O3DE_DEV: {}'.format(_O3DE_DEV)) -_PROJ_CACHE = os.path.join(_LY_DEV, 'cache', 'DCCsi', 'wing') +_PROJ_CACHE = os.path.join(_O3DE_DEV, 'cache', 'DCCsi', 'wing') _LOGGER.info('_PROJ_CACHE: {}'.format(_PROJ_CACHE)) _LOCAL_HOST = socket.gethostbyname(socket.gethostname()) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/simple_command_port.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/simple_command_port.py similarity index 98% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/simple_command_port.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/simple_command_port.py index 659cae985d..3a130cd85f 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/simple_command_port.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/simple_command_port.py @@ -24,7 +24,7 @@ import logging as _logging # -------------------------------------------------------------------------- # -- Global Definitions -- -_MODULENAME = 'azpy.maya.utils.simple_command_port' +_MODULENAME = 'azpy.dcc.maya.utils.simple_command_port' _LOGGER = _logging.getLogger(_MODULENAME) _LOCAL_HOST = socket.gethostbyname(socket.gethostname()) @@ -205,7 +205,7 @@ class SimpleCommandPort: # Main Code Block, runs this script as main (testing) # ------------------------------------------------------------------------- if __name__ == '__main__': - _G_DEBUG = True + _DCCSI_GDEBUG = True _DCCSI_DEV_MODE = True _LOGGER.setLevel(_logging.DEBUG) # force debugging diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/wing_to_maya.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/wing_to_maya.py similarity index 99% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/wing_to_maya.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/wing_to_maya.py index f170090823..0c06b912d9 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/utils/wing_to_maya.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/maya/utils/wing_to_maya.py @@ -24,7 +24,7 @@ from simple_command_port import SimpleCommandPort # -------------------------------------------------------------------------- # -- Global Definitions -- -_MODULENAME = 'azpy.maya.utils.wing_to_maya' +_MODULENAME = 'azpy.dcc.maya.utils.wing_to_maya' _LOGGER = _logging.getLogger(_MODULENAME) _LOCAL_HOST = socket.gethostbyname(socket.gethostname()) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/lumberyard/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/o3de/__init__.py old mode 100755 new mode 100644 similarity index 68% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/lumberyard/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/o3de/__init__.py index 9da4e8975a..95aa781557 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/lumberyard/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/o3de/__init__.py @@ -11,30 +11,50 @@ # The __init__.py files help guide import statements without automatically # importing all of the modules """azpy.lumberyard.__init__ -All Lumberyard render related packages/modules should live here.""" +All O3DE related extension packages/modules should live here.""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.lumberyard' + _PACKAGENAME = 'azpy.dcc.o3de' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- - +# These are explicit imports for now __all__ = [] +# To Do: procedurally discover dcc access and extend __all__ +# ------------------------------------------------------------------------- + +# ------------------------------------------------------------------------- +def init(): + """If the lumberyard azlmbr api is required for a package/module to + import, then it should be initialized and added here so general imports + don't fail""" + + import azlmbr + + # extend all with submodules + __all__.append('atom') + + # Importing local packages/modules + pass # ------------------------------------------------------------------------- @@ -49,19 +69,4 @@ if _DCCSI_DEV_MODE: # ------------------------------------------------------------------------- -# ------------------------------------------------------------------------- -def init(): - """If the lumberyard azlmbr api is required for a package/module to - import, then it should be initialized and added here so general imports - don't fail""" - - # __all__.append() - - # Make sure we can import the native apis - #import - - # Importing local packages/modules - pass -# ------------------------------------------------------------------------- - del _LOGGER diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/render/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/o3de/atom/__init__.py old mode 100755 new mode 100644 similarity index 72% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/render/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/o3de/atom/__init__.py index a6eb9ea373..ecd1db3ef0 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/render/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/o3de/atom/__init__.py @@ -14,28 +14,33 @@ This package generically uses 'render' to refer to Atom (which is a code name.) All Atom render related packages/modules should live here.""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) + _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.render' + _PACKAGENAME = 'azpy.dcc.o3de.atom' - -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- - +# These are explicit imports for now __all__ = [] - +# To Do: procedurally discover dcc access and extend __all__ # ------------------------------------------------------------------------- @@ -55,12 +60,13 @@ def init(): """If the atom render api is required for a package/module to import, then it should be initialized and added here so general imports don't fail""" - - # __all__.append() # Make sure we can import the native apis # import - + + # extend all with submodules + #__all__.append('foo', 'bar') + # Importing local packages/modules pass # ------------------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/substance/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/substance/__init__.py old mode 100755 new mode 100644 similarity index 71% rename from Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/substance/__init__.py rename to Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/substance/__init__.py index 8f9d542906..1e5ee058cc --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/substance/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dcc/substance/__init__.py @@ -12,28 +12,49 @@ # importing all of the modules """azpy.substance.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.substance' + _PACKAGENAME = 'azpy.dcc.substance' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- - +# These are explicit imports for now __all__ = [] +# To Do: procedurally discover dcc access and extend __all__ +# ------------------------------------------------------------------------- + +# ------------------------------------------------------------------------- +def init(): + """If the substance api is required for a package/module to import, + then it should be initialized and added here so general imports + don't fail""" + + # Make sure we can import the native apis + # import + + # extend all with submodules + #__all__.append('foo', 'bar') + + # Importing local packages/modules + pass # ------------------------------------------------------------------------- @@ -47,21 +68,4 @@ if _DCCSI_DEV_MODE: _logger=_LOGGER) # ------------------------------------------------------------------------- - -# ------------------------------------------------------------------------- -def init(): - """If the substance api is required for a package/module to import, - then it should be initialized and added here so general imports - don't fail""" - - # __all__.append() - - # Make sure we can import the native apis - # import - - # Importing local packages/modules - pass - -# ------------------------------------------------------------------------- - del _LOGGER diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/__init__.py index cbd58ba5e8..0db99ff838 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/__init__.py @@ -15,7 +15,7 @@ from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = 'azpy.dev.ide' diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/wing/hot_keys.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/wing/hot_keys.py index 2c91398b70..1e179003fe 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/wing/hot_keys.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/wing/hot_keys.py @@ -119,10 +119,10 @@ def get_stub_check_path(in_path=__file__, check_stub='engineroot.txt'): # ------------------------------------------------------------------------- # globals -_LY_DEV = get_stub_check_path() -_LOGGER.info('_LY_DEV: {}'.format(_LY_DEV)) +_O3DE_DEV = get_stub_check_path() +_LOGGER.info('_O3DE_DEV: {}'.format(_O3DE_DEV)) -_PROJ_CACHE = os.path.join(_LY_DEV, 'cache', 'DCCsi', 'wing') +_PROJ_CACHE = os.path.join(_O3DE_DEV, 'cache', 'DCCsi', 'wing') _LOGGER.info('_PROJ_CACHE: {}'.format(_PROJ_CACHE)) if not os.path.exists(_PROJ_CACHE): @@ -339,7 +339,7 @@ mel_selection_to_maya.contexts = [ if __name__ == '__main__': # there are not really tests to run here due to this being a list of # constants for shared use. - _G_DEBUG = True + _DCCSI_GDEBUG = True _DCCSI_DEV_MODE = True _LOGGER.setLevel(_logging.DEBUG) # force debugging diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/wing/test.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/wing/test.py index dbf5025f88..838e890184 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/wing/test.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/ide/wing/test.py @@ -73,7 +73,7 @@ dccsi_test_script.contexts = [ if __name__ == '__main__': # there are not really tests to run here due to this being a list of # constants for shared use. - _G_DEBUG = True + _DCCSI_GDEBUG = True _DCCSI_DEV_MODE = True _LOGGER.setLevel(_logging.DEBUG) # force debugging diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/utils/check/maya_app.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/utils/check/maya_app.py index 76dc3d9b2a..3e4344aecc 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/utils/check/maya_app.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/utils/check/maya_app.py @@ -25,10 +25,10 @@ from azpy.constants import ENVAR_DCCSI_DEV_MODE # -------------------------------------------------------------------------- # -- Global Definitions -- -_DCCSI_DCC_APP = None +_DCCSI_G_DCC_APP = None # set up global space, logging etc. -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _MODULENAME = 'azpy.dev.utils.check.maya_app' @@ -43,14 +43,14 @@ _LOGGER = _logging.getLogger(_MODULENAME) def set_dcc_app(dcc_app='maya'): """ azpy.dev.utils.check.maya.set_dcc_app() - this will set global _DCCSI_DCC_APP = 'maya' - and os.environ["DCCSI_DCC_APP"] = 'maya' + this will set global _DCCSI_G_DCC_APP = 'maya' + and os.environ["DCCSI_G_DCC_APP"] = 'maya' """ - _DCCSI_DCC_APP = dcc_app + _DCCSI_G_DCC_APP = dcc_app - _LOGGER.info('Setting DCCSI_DCC_APP to: {0}'.format(dcc_app)) + _LOGGER.info('Setting DCCSI_G_DCC_APP to: {0}'.format(dcc_app)) - return _DCCSI_DCC_APP + return _DCCSI_G_DCC_APP # ------------------------------------------------------------------------- @@ -58,44 +58,44 @@ def set_dcc_app(dcc_app='maya'): def clear_dcc_app(dcc_app=False): """ azpy.dev.utils.check.maya.set_dcc_app() - this will set global _DCCSI_DCC_APP = False - and os.environ["DCCSI_DCC_APP"] = False + this will set global _DCCSI_G_DCC_APP = False + and os.environ["DCCSI_G_DCC_APP"] = False """ - _DCCSI_DCC_APP = dcc_app + _DCCSI_G_DCC_APP = dcc_app - _LOGGER.info('Setting DCCSI_DCC_APP to: {0}'.format(dcc_app)) + _LOGGER.info('Setting DCCSI_G_DCC_APP to: {0}'.format(dcc_app)) - return _DCCSI_DCC_APP + return _DCCSI_G_DCC_APP # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -def validate_state(DCCSI_DCC_APP=_DCCSI_DCC_APP): +def validate_state(DCCSI_G_DCC_APP=_DCCSI_G_DCC_APP): ''' This will detect if we are running in Maya or not, then will call either, set_dcc_app('maya') or clear_dcc_app(dcc_app=False) ''' - if _G_DEBUG: + if _DCCSI_GDEBUG: _LOGGER.debug(autolog()) try: import maya.cmds as cmds - DCCSI_DCC_APP = set_dcc_app('maya') + DCCSI_G_DCC_APP = set_dcc_app('maya') except ImportError as e: _LOGGER.warning('Can not perform: import maya.cmds as cmds') - DCCSI_DCC_APP = clear_dcc_app() + DCCSI_G_DCC_APP = clear_dcc_app() else: try: if cmds.about(batch=True): - DCCSI_DCC_APP = set_dcc_app('maya') + DCCSI_G_DCC_APP = set_dcc_app('maya') except AttributeError as e: _LOGGER.warning("maya.cmds module isn't fully loaded/populated, " "(cmds populates only in batch, maya.standalone, or maya GUI)") # NO Maya - DCCSI_DCC_APP=clear_dcc_app() + DCCSI_G_DCC_APP=clear_dcc_app() - return DCCSI_DCC_APP + return DCCSI_G_DCC_APP # ------------------------------------------------------------------------- @@ -120,7 +120,7 @@ def autolog(): # ------------------------------------------------------------------------- # run the check on import -_DCCSI_DCC_APP = validate_state() +_DCCSI_G_DCC_APP = validate_state() # ------------------------------------------------------------------------- @@ -130,7 +130,7 @@ _DCCSI_DCC_APP = validate_state() if __name__ == '__main__': # there are not really tests to run here due to this being a list of # constants for shared use. - _G_DEBUG = True + _DCCSI_GDEBUG = True _DCCSI_DEV_MODE = True _LOGGER.setLevel(_logging.DEBUG) # force debugging @@ -150,5 +150,5 @@ if __name__ == '__main__': _LOGGER.info('{} ... Running script as __main__'.format(_MODULENAME)) _LOGGER.info(STR_CROSSBAR) - _DCCSI_DCC_APP = validate_state() - _LOGGER.info('Is Maya Running? _DCCSI_DCC_APP = {}'.format(_DCCSI_DCC_APP)) + _DCCSI_G_DCC_APP = validate_state() + _LOGGER.info('Is Maya Running? _DCCSI_G_DCC_APP = {}'.format(_DCCSI_G_DCC_APP)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/utils/check/running_state.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/utils/check/running_state.py index 301d49a0ea..e5304cf34c 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/utils/check/running_state.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/dev/utils/check/running_state.py @@ -21,10 +21,11 @@ import logging as _logging # -------------------------------------------------------------------------- # -- Global Definitions -- -_DCCSI_DCC_APP = None +_DCCSI_G_DCC_APP = None _MODULENAME = 'azpy.dev.utils.check.running_state' _LOGGER = _logging.getLogger(_MODULENAME) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) # ------------------------------------------------------------------------- @@ -36,7 +37,7 @@ class CheckRunningState(object): """ # Class Variables - DCCSI_DCC_APP = None + DCCSI_G_DCC_APP = None def __init__(self, *args, **kwargs): ''' @@ -82,32 +83,35 @@ class CheckRunningState(object): def check_known(self): # -- init -- # first let's check if any of these DCC apps are running + + # To Do?: Add O3DE checks, treat as a DCC app? + # 0 - maya first - CheckRunningState.DCCSI_DCC_APP = self.maya_running() + CheckRunningState.DCCSI_G_DCC_APP = self.maya_running() # 1 - then max - if not CheckRunningState.DCCSI_DCC_APP: - CheckRunningState.DCCSI_DCC_APP = self.max_running() + if not CheckRunningState.DCCSI_G_DCC_APP: + CheckRunningState.DCCSI_G_DCC_APP = self.max_running() else: - _LOGGER.warning('DCCSI_DCC_APP is already set: {}'.format(CheckRunningState.DCCSI_DCC_APP)) + _LOGGER.warning('DCCSI_G_DCC_APP is already set: {}'.format(CheckRunningState.DCCSI_G_DCC_APP)) # 2 - then blender - if not CheckRunningState.DCCSI_DCC_APP: - CheckRunningState.DCCSI_DCC_APP = self.blender_running() + if not CheckRunningState.DCCSI_G_DCC_APP: + CheckRunningState.DCCSI_G_DCC_APP = self.blender_running() else: - _LOGGER.warning('DCCSI_DCC_APP is already set: {}'.format(CheckRunningState.DCCSI_DCC_APP)) + _LOGGER.warning('DCCSI_G_DCC_APP is already set: {}'.format(CheckRunningState.DCCSI_G_DCC_APP)) # store checks for DCC info - if CheckRunningState.DCCSI_DCC_APP: + if CheckRunningState.DCCSI_G_DCC_APP: self.dcc_py = True # store check for is maya running headless - if CheckRunningState.DCCSI_DCC_APP == 'maya': + if CheckRunningState.DCCSI_G_DCC_APP == 'maya': self.maya_headless = self.is_maya_headless() # set a envar other modules can easily check - if CheckRunningState.DCCSI_DCC_APP: - os.environ['DCCSI_DCC_APP'] = CheckRunningState.DCCSI_DCC_APP + if CheckRunningState.DCCSI_G_DCC_APP: + os.environ['DCCSI_G_DCC_APP'] = CheckRunningState.DCCSI_G_DCC_APP # --------------------------------------------------------------------- @@ -164,13 +168,13 @@ class CheckRunningState(object): """< To Do: Need to document >""" try: import azpy.dev.utils.check.maya_app as check_dcc - DCCSI_DCC_APP = check_dcc.validate_state() + DCCSI_G_DCC_APP = check_dcc.validate_state() except ImportError as e: _LOGGER.info('Not Implemented: azpy.dev.utils.check.maya_app') - if DCCSI_DCC_APP: - CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state() - os.environ["DCCSI_DCC_APP"] = str(DCCSI_DCC_APP) - return CheckRunningState.DCCSI_DCC_APP + if DCCSI_G_DCC_APP: + CheckRunningState.DCCSI_G_DCC_APP = check_dcc.validate_state() + os.environ["DCCSI_G_DCC_APP"] = str(DCCSI_G_DCC_APP) + return CheckRunningState.DCCSI_G_DCC_APP #---------------------------------------------------------------------- # --method------------------------------------------------------------- @@ -200,13 +204,13 @@ class CheckRunningState(object): """ try: import azpy.dev.utils.check.max_app as check_dcc - CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state() + CheckRunningState.DCCSI_G_DCC_APP = check_dcc.validate_state() except ImportError as e: _LOGGER.info('Not Implemented: azpy.dev.utils.check.max') - if CheckRunningState.DCCSI_DCC_APP: - CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state() - os.environ["DCCSI_DCC_APP"] = str(CheckRunningState.DCCSI_DCC_APP) - return CheckRunningState.DCCSI_DCC_APP + if CheckRunningState.DCCSI_G_DCC_APP: + CheckRunningState.DCCSI_G_DCC_APP = check_dcc.validate_state() + os.environ["DCCSI_G_DCC_APP"] = str(CheckRunningState.DCCSI_G_DCC_APP) + return CheckRunningState.DCCSI_G_DCC_APP #---------------------------------------------------------------------- @@ -217,13 +221,13 @@ class CheckRunningState(object): """ try: import azpy.dev.utils.check.blender_app as check_dcc - CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state() + CheckRunningState.DCCSI_G_DCC_APP = check_dcc.validate_state() except ImportError as e: _LOGGER.info('Not Implemented: azpy.dev.utils.check.blender') - if CheckRunningState.DCCSI_DCC_APP: - CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state() - os.environ["DCCSI_DCC_APP"] = str(CheckRunningState.DCCSI_DCC_APP) - return CheckRunningState.DCCSI_DCC_APP + if CheckRunningState.DCCSI_G_DCC_APP: + CheckRunningState.DCCSI_G_DCC_APP = check_dcc.validate_state() + os.environ["DCCSI_G_DCC_APP"] = str(CheckRunningState.DCCSI_G_DCC_APP) + return CheckRunningState.DCCSI_G_DCC_APP #---------------------------------------------------------------------- @@ -231,7 +235,7 @@ class CheckRunningState(object): # Class Test #========================================================================== if __name__ == '__main__': - _G_DEBUG = True + _DCCSI_GDEBUG = True _DCCSI_DEV_MODE = True _LOGGER.setLevel(_logging.DEBUG) # force debugging @@ -251,4 +255,4 @@ if __name__ == '__main__': _LOGGER.info(STR_CROSSBAR) foo = CheckRunningState() - _LOGGER.info('DCCSI_DCC_APP: {}'.format(foo.DCCSI_DCC_APP)) + _LOGGER.info('DCCSI_G_DCC_APP: {}'.format(foo.DCCSI_G_DCC_APP)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/env_base.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/env_base.py index dd28c98927..cdcf99d762 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/env_base.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/env_base.py @@ -10,7 +10,7 @@ # -- This line is 75 characters ------------------------------------------- from __future__ import unicode_literals ''' -Module: \azpy\shared\common\base_env.py +Module: \\azpy\\shared\\common\\base_env.py This module packs the most basic set of environment variables. @@ -57,7 +57,7 @@ _LOGGER = _logging.getLogger(_PACKAGENAME) _LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) # set up base totally non-functional defauls (denoted with $) @@ -65,15 +65,15 @@ _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _BASE_ENVVAR_DICT = OrderedDict() # project tag -_BASE_ENVVAR_DICT[ENVAR_LY_PROJECT] = '${0}'.format(ENVAR_LY_PROJECT) +_BASE_ENVVAR_DICT[ENVAR_O3DE_PROJECT] = '${0}'.format(ENVAR_O3DE_PROJECT) # paths -_BASE_ENVVAR_DICT[ENVAR_LY_DEV] = Path('${0}'.format(ENVAR_LY_DEV)) -_BASE_ENVVAR_DICT[ENVAR_LY_PROJECT_PATH] = Path('${0}'.format(ENVAR_LY_PROJECT_PATH)) +_BASE_ENVVAR_DICT[ENVAR_O3DE_DEV] = Path('${0}'.format(ENVAR_O3DE_DEV)) +_BASE_ENVVAR_DICT[ENVAR_O3DE_PROJECT_PATH] = Path('${0}'.format(ENVAR_O3DE_PROJECT_PATH)) _BASE_ENVVAR_DICT[ENVAR_DCCSIG_PATH] = Path('${0}'.format(ENVAR_DCCSIG_PATH)) _BASE_ENVVAR_DICT[ENVAR_DCCSI_LOG_PATH] = Path('${0}'.format(ENVAR_DCCSI_LOG_PATH)) _BASE_ENVVAR_DICT[ENVAR_DCCSI_AZPY_PATH] = Path('${0}'.format(ENVAR_DCCSI_AZPY_PATH)) -_BASE_ENVVAR_DICT[ENVAR_DCCSI_SDK_PATH] = Path('${0}'.format(ENVAR_DCCSI_SDK_PATH)) +_BASE_ENVVAR_DICT[ENVAR_DCCSI_TOOLS_PATH] = Path('${0}'.format(ENVAR_DCCSI_TOOLS_PATH)) # dev env flags _BASE_ENVVAR_DICT[ENVAR_DCCSI_GDEBUG] = '${0}'.format(ENVAR_DCCSI_GDEBUG) @@ -110,16 +110,16 @@ if __name__ == '__main__': # print(setEnvarDefaults(), '\r') #<-- not necissary, already called # print(BASE_ENVVAR_VALUES, '\r') _LOGGER.info('Pretty print: _BASE_ENVVAR_DICT') - print(json.dumps(_BASE_ENVVAR_DICT, - indent=4, sort_keys=False, - ensure_ascii=False), '\r') + _LOGGER.debug(json.dumps(_BASE_ENVVAR_DICT, + indent=4, sort_keys=False, + ensure_ascii=False), '\r') # retreive a Path type key from the Box - foo = _BASE_ENVVAR_DICT[ENVAR_LY_DEV] + foo = _BASE_ENVVAR_DICT[ENVAR_O3DE_DEV] _LOGGER.info('~ foo is: {0}'.format(type(foo), foo)) # simple tests - _ENV_TAG = 'LY_DEV' + _ENV_TAG = 'O3DE_DEV' foo = get_envar_default(_ENV_TAG) _LOGGER.info("~ Results of getVar on tag, '{0}':'{1}'\r".format(_ENV_TAG, foo)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/toolbits/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/toolbits/__init__.py deleted file mode 100755 index be62dedcbe..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/maya/toolbits/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# coding:utf-8 -#!/usr/bin/python -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# -# -- This line is 75 characters ------------------------------------------- -# The __init__.py files help guide import statements without automatically -# importing all of the modules -"""azpy.maya.toolbits.__init__""" - -mport os - -from azpy.env_bool import env_bool -from azpy.constants import ENVAR_DCCSI_GDEBUG -from azpy.constants import ENVAR_DCCSI_DEV_MODE - -# global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) - -_PACKAGENAME = __name__ -if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.maya.toolbits' - -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) - -__all__ = ['detach'] - -del _LOGGER -#-------------------------------------------------------------------------- - diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/return_stub.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/return_stub.py index a5a4d40aad..478d3c355e 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/return_stub.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/return_stub.py @@ -19,7 +19,7 @@ import logging as _logging # ------------------------------------------------------------------------- # global space debug flag, no fancy stuff here we use in bootstrap -_G_DEBUG = False # manually enable to debug this file +_DCCSI_GDEBUG = False # manually enable to debug this file _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': @@ -44,7 +44,7 @@ def return_stub(stub): break if (len(tail) == 0): path = "" - if _G_DEBUG: + if _DCCSI_GDEBUG: _LOGGER.debug('~ Debug Message: I was not able to find the ' 'path to that file (stub) in a walk-up ' 'from currnet path') diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/__init__.py index 7c109f42cb..d6b6bc0d1d 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/__init__.py @@ -11,23 +11,27 @@ """azpy.shared.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': _PACKAGENAME = 'azpy.shared' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/boxDumpTest.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/boxDumpTest.json deleted file mode 100644 index 52c556733c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/boxDumpTest.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "ordered_box": true, - "COMPANY": "Amazon.Lumberyard", - "LY_PROJECT": "DccScriptingInterface", - "LY_DEV": "G:\\depot\\JG_PC1_spectrAtom\\dev", - "LY_BUILD_DIR_NAME": "windows_vs2019", - "LY_BUILD_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\windows_vs2019", - "QT_PLUGIN_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\bin\\profile\\EditorPlugins", - "LY_PROJECT_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface", - "DCCSIG_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface", - "DCCSI_AZPY_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface\\azpy", - "DCCSI_SDK_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface\\SDK", - "DCCSI_WING_VERSION_MAJOR": "7", - "DCCSI_WING_VERSION_MINOR": "1", - "WINGHOME": "C:\\Program Files (x86)\\Wing Pro 7.1", - "DCCSI_PY_DEFAULT": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Tools\\Python\\3.7.5\\windows\\python.exe" -} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/__init__.py index ab425ef427..1d552e7ea7 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/__init__.py @@ -12,23 +12,27 @@ # importing all of the modules """azpy.shared.common.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG # global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': _PACKAGENAME = 'azpy.shared.common' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- # diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/core_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/core_utils.py index 4040620002..629f52bbd7 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/core_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/core_utils.py @@ -48,34 +48,34 @@ import os import sys import site import fnmatch +import logging as _logging # 3rd Party from pathlib import Path # from progress.spinner import Spinner # deprecate use (or refactor) - -# Lumberyard extensions -from azpy.constants import * -from azpy import initialize_logger # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -# global space debug flag -from azpy.env_bool import env_bool +# global space +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -# global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) -_PACKAGENAME = __name__ -if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.shared.common.core_utils' +_MODULENAME = __name__ +if _MODULENAME is '__main__': + _MODULENAME = 'azpy.shared.common.core_utils' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_MODULENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) # ------------------------------------------------------------------------- @@ -421,7 +421,7 @@ def return_stub(stub): if __name__ == "__main__": '''To Do: Document''' # constants for shared use. - _G_DEBUG = True + _DCCSI_GDEBUG = True # happy _LOGGER.info _LOGGER.info("# {0} #".format('-' * 72)) @@ -435,7 +435,7 @@ if __name__ == "__main__": _KNOWN_SITEDIR_PATHS = site._init_pathinfo() # this is just a debug developer convenience _LOGGER.info (for testing acess) - if _G_DEBUG: + if _DCCSI_GDEBUG: import pkgutil _LOGGER.info('Current working dir: {0}'.format(cwd)) search_path = ['.'] # set to None to see all modules importable from sys.path diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/envar_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/envar_utils.py index fc99df0f30..fc2db6be0c 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/envar_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/common/envar_utils.py @@ -12,7 +12,7 @@ from __future__ import unicode_literals # ------------------------------------------------------------------------- ''' -Module: \azpy\shared\common\config_utils.py +Module: \\azpy\\shared\\common\\config_utils.py A set of utility functions @@ -33,28 +33,31 @@ import logging as _logging # 3rd Party from box import Box from unipath import Path - -# Lumberyard extensions -from azpy.constants import * # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -from azpy.env_bool import env_bool +# global space +import azpy.env_bool as env_bool +from azpy.constants import ENVAR_O3DE_DEV +from azpy.constants import ENVAR_O3DE_PROJECT from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -# global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) -_PACKAGENAME = __name__ -if _PACKAGENAME is '__main__': - _PACKAGENAME = 'azpy.shared.common.envar_utils' +_MODULENAME = __name__ +if _MODULENAME is '__main__': + _MODULENAME = 'azpy.shared.common.envar_utils' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_MODULENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) # ------------------------------------------------------------------------- @@ -65,7 +68,7 @@ def get_envar_default(envar, envar_default=None, envar_set=Box(ordered_box=True) Get from the system environment, or the module dictionary (a Box): like the test one in __main__ below, TEST_ENV_VALUES = Box(ordered_box=True) - TEST_ENV_VALUES[ENVAR_LY_PROJECT] = '${0}'.format(ENVAR_LY_PROJECT) + TEST_ENV_VALUES[ENVAR_O3DE_PROJECT] = '${0}'.format(ENVAR_O3DE_PROJECT) This dictionary provides a simple way to pack a default set into a structure and decouple the getter implementation. @@ -88,7 +91,7 @@ def get_envar_default(envar, envar_default=None, envar_set=Box(ordered_box=True) # -- envar util ---------------------------------------------------------- -def set_envar_defaults(envar_set, env_root=get_envar_default(ENVAR_LY_DEV)): +def set_envar_defaults(envar_set, env_root=get_envar_default(ENVAR_O3DE_DEV)): """ Set each environment variable if not alreay set with value. Must be safe, will not over-write existing. @@ -98,8 +101,8 @@ def set_envar_defaults(envar_set, env_root=get_envar_default(ENVAR_LY_DEV)): env_root = Path(env_root) if env_root.exists(): - os.environ[ENVAR_LY_DEV] = env_root - envar_set[ENVAR_LY_DEV] = env_root + os.environ[ENVAR_O3DE_DEV] = env_root + envar_set[ENVAR_O3DE_DEV] = env_root else: raise ValueError("EnvVar Root is not valid: {0}".format(env_root)) @@ -107,7 +110,7 @@ def set_envar_defaults(envar_set, env_root=get_envar_default(ENVAR_LY_DEV)): envar = str(envar) value = os.getenv(envar) - if _G_DEBUG: + if _DCCSI_GDEBUG: if not value: _LOGGER.debug('~ EnVar value NOT found: {0}\r'.format(envar)) @@ -191,8 +194,8 @@ if __name__ == '__main__': # it should be benign but leaving this comment here in case of funk # tes envars - TEST_ENV_VALUES[ENVAR_LY_PROJECT] = '${0}'.format(ENVAR_LY_PROJECT) - TEST_ENV_VALUES[ENVAR_LY_DEV] = Path('${0}'.format(ENVAR_LY_DEV)) + TEST_ENV_VALUES[ENVAR_O3DE_PROJECT] = '${0}'.format(ENVAR_O3DE_PROJECT) + TEST_ENV_VALUES[ENVAR_O3DE_DEV] = Path('${0}'.format(ENVAR_O3DE_DEV)) # try to fetch and set the base values from the environment # this makes sure all envars set, are resolved on import @@ -204,7 +207,7 @@ if __name__ == '__main__': ensure_ascii=False), '\r') # simple tests - _ENV_TAG = 'LY_DEV' + _ENV_TAG = 'O3DE_DEV' foo = get_envar_default(_ENV_TAG) _LOGGER.info("~ Results of getVar on tag, '{0}':'{1}'\r".format(_ENV_TAG, foo)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/__init__.py index c18cf9d81a..36a354a965 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/__init__.py @@ -13,31 +13,35 @@ SPDX-License-Identifier: Apache-2.0 OR MIT import os from pathlib import Path import logging as _logging +# ------------------------------------------------------------------------- -from azpy.env_bool import env_bool +# ------------------------------------------------------------------------- +# global space +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -# global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': _PACKAGENAME = 'azpy.shared.noodely' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) -# ------------------------------------------------------------------------- -# +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) + __all__ = ['find_arg', 'helpers', 'node', 'synth', 'synth_arg_kwarg', 'test_foo'] -# # ------------------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/find_arg.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/find_arg.py index 681c88b33c..dd939f3973 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/find_arg.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/find_arg.py @@ -74,7 +74,7 @@ if __name__ == "__main__": print ('~ find_arg.py ... Running script as __main__') print ("# ----------------------------------------------------------------------- #\r") - _G_DEBUG = True + _DCCSI_GDEBUG = True from test_foo import Foo @@ -102,7 +102,7 @@ if __name__ == "__main__": self._kwargsDict[key] = value # synthesize(self, '{0}'.format(key), value) <-- I have a method, # which synthesizes properties... with gettr, settr, etc. - if _G_DEBUG: + if _DCCSI_GDEBUG: print("{0}:{1}".format(key, value)) # representation diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/node.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/node.py index ca44b9a6c8..59e25d45e1 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/node.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/node.py @@ -47,13 +47,13 @@ from azpy.constants import ENVAR_DCCSI_DEV_MODE # global space # To Do: update to dynaconf dynamic env and settings? -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _MODULENAME = 'azpy.shared.noodely.node' _log_level = int(20) -if _G_DEBUG: +if _DCCSI_GDEBUG: _log_level = int(10) _LOGGER = azpy.initialize_logger(_MODULENAME, log_to_file=False, @@ -66,7 +66,7 @@ _LOGGER.debug('Starting:: {}.'.format({_MODULENAME})) # quick test code (remove later) from hashids import Hashids hashids = Hashids(min_length=16, salt='DCCsi') -if _G_DEBUG: +if _DCCSI_GDEBUG: print (hashids.encrypt(193487)) # test hash # ------------------------------------------------------------------------- @@ -107,7 +107,7 @@ class Node(object): """Class constructor: makes a node.""" # share the debug state - _DEBUG = _G_DEBUG + _DEBUG = _DCCSI_GDEBUG # logger _LOGGER = _G_LOGGER diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/pathnode.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/pathnode.py index 5c5402cb7d..401e524060 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/pathnode.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/pathnode.py @@ -47,13 +47,13 @@ from azpy.constants import ENVAR_DCCSI_DEV_MODE # ------------------------------------------------------------------------- # global space # To Do: update to dynaconf dynamic env and settings? -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _MODULENAME = 'azpy.shared.noodely.pathnode' _log_level = int(20) -if _G_DEBUG: +if _DCCSI_GDEBUG: _log_level = int(10) _LOGGER = azpy.initialize_logger(_MODULENAME, log_to_file=False, @@ -78,7 +78,7 @@ class PathNode(Node): """doc string""" # share the debug state - _DEBUG = _G_DEBUG + _DEBUG = _DCCSI_GDEBUG # class header _message_header = 'noodly, PathNode(): Message' diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/__init__.py index a73ee00d7c..745ec9a7be 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/__init__.py @@ -11,24 +11,26 @@ """azpy.shared.ui.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -# global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': _PACKAGENAME = 'azpy.shared.ui' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) - +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) # ------------------------------------------------------------------------- # __all__ = [] diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/base_widget.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/base_widget.py index 311ed484ed..493130eb64 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/base_widget.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/base_widget.py @@ -34,7 +34,7 @@ from shiboken2 import wrapInstance # ------------------------------------------------------------------------- # global space debug flag -_G_DEBUG = settings.DCCSI_GDEBUG +_DCCSI_GDEBUG = settings.DCCSI_GDEBUG # global space debug flag _DCCSI_DEV_MODE = settings.DCCSI_DEV_MODE diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/custom_treemodel.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/custom_treemodel.py index d7b096468d..871b050c7a 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/custom_treemodel.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/custom_treemodel.py @@ -30,7 +30,7 @@ import PySide2.QtGui as QtGui # ------------------------------------------------------------------------- # global space # To Do: update to dynaconf dynamic env and settings? -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) _MODULE_PATH = Path(__file__) @@ -38,7 +38,7 @@ _MODULE_PATH = Path(__file__) _MODULENAME = 'azpy.shared.ui.custom_treemodel' _log_level = int(20) -if _G_DEBUG: +if _DCCSI_GDEBUG: _log_level = int(10) _LOGGER = azpy.initialize_logger(_MODULENAME, log_to_file=False, diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/help_menu.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/help_menu.py index f4b53f6b79..7fdeadc4c2 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/help_menu.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/help_menu.py @@ -27,7 +27,7 @@ import PySide2.QtWidgets as QtWidgets # ------------------------------------------------------------------------- # global space debug flag -_G_DEBUG = os.getenv('DCCSI_GDEBUG', False) +_DCCSI_GDEBUG = os.getenv('DCCSI_GDEBUG', False) # global space developer mode flag _DCCSI_DEV_MODE = os.getenv('DCCSI_DEV_MODE', False) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/pyside2_qtextedit_stdout.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/pyside2_qtextedit_stdout.py index 6fd4f359bc..d14d5bcbd6 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/pyside2_qtextedit_stdout.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/pyside2_qtextedit_stdout.py @@ -32,7 +32,7 @@ from PySide2.QtCore import QTimer # ------------------------------------------------------------------------- # global space debug flag -_G_DEBUG = os.getenv('DCCSI_GDEBUG', False) +_DCCSI_GDEBUG = os.getenv('DCCSI_GDEBUG', False) # global space debug flag _DCCSI_DEV_MODE = os.getenv('DCCSI_DEV_MODE', False) @@ -48,7 +48,7 @@ _MODULENAME = __name__ if _MODULENAME is '__main__': _MODULENAME = _TOOL_TAG -if _G_DEBUG: +if _DCCSI_GDEBUG: _LOGGER = initialize_logger(_MODULENAME, log_to_file=True) _LOGGER.debug('Something invoked :: {0}.'.format({_MODULENAME})) else: @@ -130,7 +130,7 @@ if __name__ == '__main__': _TOOL_TAG = 'azpy.shared.ui.pyside2_qtextedit_stdout' _TYPE_TAG = 'test' - if _G_DEBUG: + if _DCCSI_GDEBUG: _LOGGER = initialize_logger('{0}-TEST'.format(_TOOL_TAG), log_to_file=True) _LOGGER.debug('Something invoked :: {0}.'.format({_MODULENAME})) @@ -155,7 +155,7 @@ if __name__ == '__main__': _READER.start('python', ['-u', _TEST_PY_FILE]) # start the process # after that starts, this will show the console - # LY_QSS = Path(_MODULE_PATH.parent, 'resources', 'stylesheets', 'LYstyle.qss') + # O3DE_QSS = Path(_MODULE_PATH.parent, 'resources', 'stylesheets', 'LYstyle.qss') _DARK_STYLE = Path(_MODULE_PATH.parent, 'resources', 'qdarkstyle', 'style.qss') _CONSOLE.qapp.setStyleSheet(_DARK_STYLE.read_file()) _CONSOLE.show() # make the console visible diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/pyside2_ui_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/pyside2_ui_utils.py index b996d9baf6..0f229e45b6 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/pyside2_ui_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/pyside2_ui_utils.py @@ -51,7 +51,7 @@ import azpy.shared.ui.help_menu as help_menu # ------------------------------------------------------------------------- # global space debug flag -_G_DEBUG = settings.DCCSI_GDEBUG +_DCCSI_GDEBUG = settings.DCCSI_GDEBUG # global space debug flag _DCCSI_DEV_MODE = settings.DCCSI_DEV_MODE @@ -112,7 +112,7 @@ def from_ui_generate_form_and_base_class(filename, return_output=False): ui_file.exists() except FileNotFoundError as error: output += 'File does not exist: {0}/r'.format(error) - if _G_DEBUG: + if _DCCSI_GDEBUG: print(error) if return_output: return False, output diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/qt_settings.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/qt_settings.py index de2bc1aaf4..28f6e23ac2 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/qt_settings.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/qt_settings.py @@ -24,7 +24,7 @@ import azpy.config_utils _config = azpy.config_utils.get_dccsi_config() # ^ this is effectively an import and retreive of \config.py # init lumberyard Qy/PySide2 access -_config.init_ly_pyside(settings.LY_DEV) +_config.init_o3de_pyside(settings.O3DE_DEV) # now we can import lumberyards PySide2 import PySide2.QtCore as QtCore @@ -32,7 +32,7 @@ import PySide2.QtWidgets as QtWidgets # ------------------------------------------------------------------------- # global space debug flag -_G_DEBUG = settings.DCCSI_GDEBUG +_DCCSI_GDEBUG = settings.DCCSI_GDEBUG # global space debug flag _DCCSI_DEV_MODE = settings.DCCSI_DEV_MODE diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/templates.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/templates.py index a3700e8cf4..ec1d00b6d0 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/templates.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/templates.py @@ -47,7 +47,7 @@ import PySide2.QtUiTools as QtUiTools # ------------------------------------------------------------------------- # global space debug flag -_G_DEBUG = settings.DCCSI_GDEBUG +_DCCSI_GDEBUG = settings.DCCSI_GDEBUG # global space debug flag _DCCSI_DEV_MODE = settings.DCCSI_DEV_MODE diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py index 71f2682242..33ac6c6814 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py @@ -40,12 +40,11 @@ Configures several useful environment config settings and paths, [key] : [value] # this is the required base environment - LY_PROJECT : name of project (project directory) - LY_DEV : path to Lumberyard \dev root - LY_PROJECT_PATH : path to project dir + O3DE_PROJECT : name of project (project directory) + O3DE_DEV : path to Lumberyard \dev root + O3DE_PROJECT_PATH : path to project dir DCCSIG_PATH : path to the DCCsi Gem root - DCCSI_AZPY_PATH * : path to azpy Python API (code) - DCCSI_SDK_PATH : path to associated (non-api code) DCC SDK + DCCSI_TOOLS_PATH : path to associated (non-api code) DCC SDK # nice to haves in base env to define core support DCCSI_GDEBUG : sets global debug prints @@ -59,7 +58,7 @@ Configures several useful environment config settings and paths, :: Default version py37 has a launcher (activates the env, starts py interpreter) - set DCCSI_PY_BASE=%DCCSI_PYTHON_INSTALL%\python.exe + set DCCSI_PY_BASE=%O3DE_PYTHON_INSTALL%\python.exe :: shared location for 64bit python 3.7 BASE location set DCCSI_PY_DCCSI=%DCCSI_LAUNCHERS_PATH%\Launch_pyBASE.bat @@ -74,7 +73,7 @@ Configures several useful environment config settings and paths, :: shared location for 64bit DCCSI_PY_MAYA python 2.7 DEV location set DCCSI_PY_MAYA=%MAYA_LOCATION%\bin\mayapy.exe :: wingIDE can use more then one defined/managed interpreters - :: allowing you to _G_DEBUG code in multiple runtimes in one session + :: allowing you to _DCCSI_GDEBUG code in multiple runtimes in one session ${DCCSI_PY_MAYA} # related to the WING as the default DCCSI_GDEBUGGER @@ -92,8 +91,9 @@ import os import sys import site import re -#import inspect +import inspect import json +import importlib.util import logging as _logging from collections import OrderedDict @@ -110,35 +110,33 @@ _MODULE_PATH = os.path.realpath(__file__) # To Do: what if frozen? _DCCSIG_PATH = os.path.normpath(os.path.join(_MODULE_PATH, '../..')) _DCCSIG_PATH = os.getenv('DCCSIG_PATH', _DCCSIG_PATH) site.addsitedir(_DCCSIG_PATH) -print(_DCCSIG_PATH) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -# Lumberyard extensions +# O3DE extensions from pathlib import Path # set up global space, logging etc. -import azpy -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) -_PACKAGENAME = 'DCCsi.azpy.sunthetic_env' +_PACKAGENAME = 'DCCsi.azpy.synthetic_env' -_log_level = int(20) -if _G_DEBUG: - _log_level = int(10) -_LOGGER = azpy.initialize_logger(_PACKAGENAME, - log_to_file=True, - default_log_level=_log_level) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) -_LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME})) _LOGGER.debug('_DCCSIG_PATH: {}'.format(_DCCSIG_PATH)) -_LOGGER.debug('_G_DEBUG: {}'.format(_G_DEBUG)) +_LOGGER.debug('_DCCSI_GDEBUG: {}'.format(_DCCSI_GDEBUG)) _LOGGER.debug('_DCCSI_DEV_MODE: {}'.format(_DCCSI_DEV_MODE)) if _DCCSI_DEV_MODE: @@ -170,7 +168,7 @@ if os.path.exists(_DCCSI_PYTHON_LIB_PATH): # ------------------------------------------------------------------------- # post-bootstrap global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) # ------------------------------------------------------------------------- @@ -225,7 +223,7 @@ def return_stub(stub='dccsi_stub'): break if (len(tail) == 0): path = "" - if _G_DEBUG: + if _DCCSI_GDEBUG: _LOGGER.debug('~Not able to find the path to that file ' '(stub) in a walk-up from currnet path.') break @@ -262,7 +260,7 @@ def get_stub_check_path(in_path, check_stub='engineroot.txt'): # ------------------------------------------------------------------------- # TO DO: Move to a util package or module -def resolve_envar_path(envar='LY_DEV', +def resolve_envar_path(envar='O3DE_DEV', start_path=__file__, check_stub='engineroot.txt', dir_name='dev', @@ -276,7 +274,7 @@ def resolve_envar_path(envar='LY_DEV', That is a pretty safe indicator that we found the right '\dev' - Second it checks if the env var 'LY_DEV' is set, use that instead! + Second it checks if the env var 'O3DE_DEV' is set, use that instead! """ @@ -360,14 +358,14 @@ def stash_env(_SYNTH_ENV_DICT = OrderedDict()): # \dev Lumberyard ROOT PATH # someone decided to use this as a root stub (for similar reasons in C++?) - # STUB_LY_DEV = str('engineroot.txt') + # STUB_O3DE_DEV = str('engineroot.txt') # I don't own \dev so I didn't want to check in anything new there - _LY_DEV = resolve_envar_path(ENVAR_LY_DEV, # envar + _O3DE_DEV = resolve_envar_path(ENVAR_O3DE_DEV, # envar _THIS_MODULE_PATH, # search path - STUB_LY_DEV, # stub - TAG_DIR_LY_DEV) # dir + STUB_O3DE_DEV, # stub + TAG_DIR_O3DE_DEV) # dir - _SYNTH_ENV_DICT[ENVAR_LY_DEV] = _LY_DEV.as_posix() + _SYNTH_ENV_DICT[ENVAR_O3DE_DEV] = _O3DE_DEV.as_posix() # project name is a string, it should be project dir name # for siloed testing and a purely synthetc env (nothing previously set) @@ -376,17 +374,17 @@ def stash_env(_SYNTH_ENV_DICT = OrderedDict()): # for testing overrides of the default synthetic env # we can do two things here, - # first we can try to fetch from the env os.getenv('LY_PROJECT') + # first we can try to fetch from the env os.getenv('O3DE_PROJECT') # If comes back None, allows you to specify a default fallback # changed to just make the fallback what is set in boostrap # so now it's less of a fallnack and more correct if not # explicitly set - _LY_PROJECT = os.getenv(ENVAR_LY_PROJECT) - _SYNTH_ENV_DICT[ENVAR_LY_PROJECT] = _LY_PROJECT + _O3DE_PROJECT = os.getenv(ENVAR_O3DE_PROJECT) + _SYNTH_ENV_DICT[ENVAR_O3DE_PROJECT] = _O3DE_PROJECT - _LY_BUILD_DIR_NAME = os.getenv(ENVAR_LY_BUILD_DIR_NAME, - TAG_DIR_LY_BUILD) - _SYNTH_ENV_DICT[ENVAR_LY_BUILD_DIR_NAME] = _LY_BUILD_DIR_NAME + _O3DE_BUILD_DIR_NAME = os.getenv(ENVAR_O3DE_BUILD_DIR_NAME, + TAG_DIR_O3DE_BUILD_FOLDER) + _SYNTH_ENV_DICT[ENVAR_O3DE_BUILD_DIR_NAME] = _O3DE_BUILD_DIR_NAME # pattern for the above is (and will be repeated) # _SOME_ENVAR = resolve_envar_path('ENVAR', @@ -406,31 +404,31 @@ def stash_env(_SYNTH_ENV_DICT = OrderedDict()): # so we guess based on how I set up the original dev environment # -- envar -- - _LY_BUILD_PATH = Path(os.getenv(ENVAR_LY_BUILD_PATH, - PATH_LY_BUILD_PATH)) - _SYNTH_ENV_DICT[ENVAR_LY_BUILD_PATH] = _LY_BUILD_PATH.as_posix() + _O3DE_BUILD_PATH = Path(os.getenv(ENVAR_O3DE_BUILD_PATH, + PATH_O3DE_BUILD_PATH)) + _SYNTH_ENV_DICT[ENVAR_O3DE_BUILD_PATH] = _O3DE_BUILD_PATH.as_posix() # -- envar -- - _LY_BIN_PATH = Path(os.getenv(ENVAR_LY_BIN_PATH, - PATH_LY_BIN_PATH)) + _O3DE_BIN_PATH = Path(os.getenv(ENVAR_O3DE_BIN_PATH, + PATH_O3DE_BIN_PATH)) # some of these need hard checks - if not _LY_BIN_PATH.exists(): - raise Exception('LY_BIN_PATH does NOT exist: {0}'.format(_LY_BIN_PATH)) + if not _O3DE_BIN_PATH.exists(): + raise Exception('O3DE_BIN_PATH does NOT exist: {0}'.format(_O3DE_BIN_PATH)) else: - _SYNTH_ENV_DICT[ENVAR_LY_BIN_PATH] = _LY_BIN_PATH.as_posix() + _SYNTH_ENV_DICT[ENVAR_O3DE_BIN_PATH] = _O3DE_BIN_PATH.as_posix() # adding to sys.path apparently doesn't work for .dll locations like Qt - os.environ['PATH'] = _LY_BIN_PATH.as_posix() + os.pathsep + os.environ['PATH'] + os.environ['PATH'] = _O3DE_BIN_PATH.as_posix() + os.pathsep + os.environ['PATH'] # -- envar -- # if that stub marker doesn't exist assume DCCsi path (fallback 1) - _LY_PROJECT_PATH = Path(os.getenv(ENVAR_LY_PROJECT_PATH, - Path(_LY_DEV, _LY_PROJECT))) - _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH] = _LY_PROJECT_PATH.as_posix() + _O3DE_PROJECT_PATH = Path(os.getenv(ENVAR_O3DE_PROJECT_PATH, + Path(_O3DE_DEV, _O3DE_PROJECT))) + _SYNTH_ENV_DICT[ENVAR_O3DE_PROJECT_PATH] = _O3DE_PROJECT_PATH.as_posix() # -- envar -- _DCCSIG_PATH = resolve_envar_path(ENVAR_DCCSIG_PATH, # envar _THIS_MODULE_PATH, # search path - STUB_LY_ROOT_DCCSI, # stub name + STUB_O3DE_ROOT_DCCSI, # stub name TAG_DEFAULT_PROJECT) # dir _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH] = _DCCSIG_PATH.as_posix() @@ -440,9 +438,9 @@ def stash_env(_SYNTH_ENV_DICT = OrderedDict()): _SYNTH_ENV_DICT[ENVAR_DCCSI_AZPY_PATH] = _AZPY_PATH.as_posix() # -- envar -- - _DCCSI_SDK_PATH = Path(os.getenv(ENVAR_DCCSI_SDK_PATH, - Path(_DCCSIG_PATH, TAG_DIR_DCCSI_SDK))) - _SYNTH_ENV_DICT[ENVAR_DCCSI_SDK_PATH] = _DCCSI_SDK_PATH.as_posix() + _DCCSI_TOOLS_PATH = Path(os.getenv(ENVAR_DCCSI_TOOLS_PATH, + Path(_DCCSIG_PATH, TAG_DIR_DCCSI_TOOLS))) + _SYNTH_ENV_DICT[ENVAR_DCCSI_TOOLS_PATH] = _DCCSI_TOOLS_PATH.as_posix() # -- envar -- # external dccsi site-packages @@ -497,7 +495,7 @@ def init_ly_pyside(env_dict=_SYNTH_ENV_DICT): - QTFORPYTHON_PATH = Path.joinpath(LY_DEV, + QTFORPYTHON_PATH = Path.joinpath(O3DE_DEV, 'Gems', 'QtForPython', '3rdParty', @@ -509,16 +507,16 @@ def init_ly_pyside(env_dict=_SYNTH_ENV_DICT): sys.path.insert(1, str(QTFORPYTHON_PATH)) site.addsitedir(str(QTFORPYTHON_PATH)) - LY_BIN_PATH = Path.joinpath(LY_DEV, + O3DE_BIN_PATH = Path.joinpath(O3DE_DEV, 'windows_vs2019', 'bin', 'profile').resolve() - os.environ["DYNACONF_LY_BIN_PATH"] = str(LY_BIN_PATH) - os.environ["LY_BIN_PATH"] = str(LY_BIN_PATH) - site.addsitedir(str(LY_BIN_PATH)) - sys.path.insert(1, str(LY_BIN_PATH)) + os.environ["DYNACONF_O3DE_BIN_PATH"] = str(O3DE_BIN_PATH) + os.environ["O3DE_BIN_PATH"] = str(O3DE_BIN_PATH) + site.addsitedir(str(O3DE_BIN_PATH)) + sys.path.insert(1, str(O3DE_BIN_PATH)) - QT_PLUGIN_PATH = Path.joinpath(LY_BIN_PATH, + QT_PLUGIN_PATH = Path.joinpath(O3DE_BIN_PATH, 'EditorPlugins').resolve() os.environ["DYNACONF_QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH) os.environ["QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH) @@ -536,7 +534,7 @@ def init_ly_pyside(env_dict=_SYNTH_ENV_DICT): if sys.platform.startswith('win'): path = os.environ['PATH'] newPath = '' - newPath += str(LY_BIN_PATH) + os.pathsep + newPath += str(O3DE_BIN_PATH) + os.pathsep newPath += str(Path.joinpath(QTFORPYTHON_PATH, 'shiboken2').resolve()) + os.pathsep newPath += str(Path.joinpath(QTFORPYTHON_PATH, @@ -591,12 +589,12 @@ def set_env(dict_object): def test_Qt(): try: import PySide2 - print('PySide2: {0}'.format(Path(PySide2.__file__).as_posix())) + _LOGGER.info('PySide2: {0}'.format(Path(PySide2.__file__).as_posix())) # builtins.ImportError: DLL load failed: The specified procedure could not be found. from PySide2 import QtCore from PySide2 import QtWidgets except IOError as e: - print('ERROR: {0}'.format(e)) + _LOGGER.error('ERROR: {0}'.format(e)) raise e try: @@ -610,7 +608,7 @@ def test_Qt(): qapp.instance().quit qapp.exit() except Exception as e: - print('ERROR: {0}'.format(e)) + _LOGGER.error('ERROR: {0}'.format(e)) raise e # ------------------------------------------------------------------------- @@ -620,14 +618,14 @@ def main(argv, env_dict_object, debug=False, devmode=False): import getopt try: opts, args = getopt.getopt(argv, "hvt:", ["verbose=", "test="]) - except getopt.GetoptError: + except getopt.GetoptError as e: # not logging, print to cmd line console - print('synthetic_env.py -v -t ') + _LOGGER.error('synthetic_env.py -v -t ') sys.exit(2) for opt, arg in opts: if opt == '-h': - print('synthetic_env.py -v -t ') + _LOGGER.info('synthetic_env.py -v -t ') sys.exit() elif opt in ("-t", "--test"): @@ -639,14 +637,14 @@ def main(argv, env_dict_object, debug=False, devmode=False): try: from box import Box except ImportError as e: - print('ERROR: {0}'.format(e)) + _LOGGER.error('ERROR: {0}'.format(e)) raise e try: env_dict_object = Box(env_dict_object) - print(str(env_dict_object.to_json(sort_keys=False, + _LOGGER.info(str(env_dict_object.to_json(sort_keys=False, indent=4))) except Exception as e: - print('ERROR: {0}'.format(e)) + _LOGGER.error('ERROR: {0}'.format(e)) raise e # ------------------------------------------------------------------------- @@ -656,16 +654,16 @@ def main(argv, env_dict_object, debug=False, devmode=False): # ------------------------------------------------------------------------- if __name__ == '__main__': # run simple tests? - _G_DEBUG = True + _DCCSI_GDEBUG = True _DCCSI_DEV_MODE = True if _DCCSI_DEV_MODE: try: import azpy.test.entry_test - print('SUCCESS: import azpy.test.entry_test') + _LOGGER.info('SUCCESS: import azpy.test.entry_test') azpy.test.entry_test.main(verbose=True, connect_debugger=True) except ImportError as e: - print('ERROR: {0}'.format(e)) + _LOGGER.error('ERROR: {0}'.format(e)) raise e # init, stash and then activate @@ -673,9 +671,9 @@ if __name__ == '__main__': _SYNTH_ENV_DICT = stash_env(_SYNTH_ENV_DICT) _SYNTH_ENV_DICT = set_env(_SYNTH_ENV_DICT) - main(sys.argv[1:], _SYNTH_ENV_DICT, _G_DEBUG, _DCCSI_DEV_MODE) + main(sys.argv[1:], _SYNTH_ENV_DICT, _DCCSI_GDEBUG, _DCCSI_DEV_MODE) - if _G_DEBUG: + if _DCCSI_GDEBUG: tempBoxJsonFilePath = Path(_SYNTH_ENV_DICT['DCCSIG_PATH'], '.temp') tempBoxJsonFilePath = Path(tempBoxJsonFilePath, 'boxDumpTest.json') diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/test/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/test/__init__.py index 20df0e5813..554d320654 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/test/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/test/__init__.py @@ -12,28 +12,44 @@ # importing all of the modules """azpy.test.__init__""" -import os +import logging as _logging -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -# global space -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) _PACKAGENAME = __name__ if _PACKAGENAME is '__main__': _PACKAGENAME = 'azpy.test' -import azpy -_LOGGER = azpy.initialize_logger(_PACKAGENAME) -_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME})) - -# ------------------------------------------------------------------------- +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_PACKAGENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) __all__ = ['entry_test'] +# ------------------------------------------------------------------------- + +# ------------------------------------------------------------------------- +def init(): + """If the substance api is required for a package/module to import, + then it should be initialized and added here so general imports + don't fail""" + + # Make sure we can import the native apis + # import + + # __all__.append() + + # Importing local packages/modules + pass # ------------------------------------------------------------------------- @@ -48,20 +64,4 @@ if _DCCSI_DEV_MODE: # ------------------------------------------------------------------------- -# ------------------------------------------------------------------------- -def init(): - """If the substance api is required for a package/module to import, - then it should be initialized and added here so general imports - don't fail""" - - # __all__.append() - - # Make sure we can import the native apis - # import - - # Importing local packages/modules - pass - -# ------------------------------------------------------------------------- - del _LOGGER diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/test/entry_test.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/test/entry_test.py index d9d136f96e..aad440d11f 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/test/entry_test.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/test/entry_test.py @@ -11,9 +11,9 @@ from __future__ import unicode_literals # ------------------------------------------------------------------------- -import sys import os import site +import logging as _logging # note: some modules not available in py2.7 unless we boostrap with config.py # See example: @@ -23,34 +23,34 @@ from pathlib import Path # ------------------------------------------------------------------------- _BOOT_CHECK = False # set true to test breakpoint in this module directly -import azpy -from azpy.env_bool import env_bool +import azpy.env_bool as env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import FRMT_LOG_LONG -# global space -# To Do: update to dynaconf dynamic env and settings? -_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) -_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) +_DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False) +_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False) -_MODULENAME = 'azpy.test.entry_test' +_MODULENAME = __name__ +if _MODULENAME is '__main__': + _MODULENAME = 'azpy.test.entry_test' -_log_level = int(20) -if _G_DEBUG: - _log_level = int(10) -_LOGGER = azpy.initialize_logger(_MODULENAME, - log_to_file=False, - default_log_level=_log_level) -_LOGGER.debug('Starting:: {}.'.format({_MODULENAME})) +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) +_LOGGER = _logging.getLogger(_MODULENAME) +_logging.basicConfig(format=FRMT_LOG_LONG) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -def main(verbose=_G_DEBUG, connect_debugger=True): - _LOGGER.info('{}'.format('-' * 74)) - _LOGGER.info('entry_test.main()') - _LOGGER.info('Root test import successful:') - _LOGGER.info('~ {}'.format(__file__)) +def main(verbose=_DCCSI_GDEBUG, connect_debugger=True): + if verbose: + _LOGGER.info('{}'.format('-' * 74)) + _LOGGER.info('entry_test.main()') + _LOGGER.info('Root test import successful:') + _LOGGER.info('~ {}'.format(__file__)) if connect_debugger: status = connect_wing() @@ -67,7 +67,7 @@ def connect_wing(): _WINGHOME = os.environ['WINGHOME'] # test _LOGGER.info('~ WINGHOME: {0}'.format(_WINGHOME)) except Exception as e: - _LOGGER.warning(e) + _LOGGER.info(e) from azpy.constants import PATH_DEFAULT_WINGHOME _WINGHOME = PATH_DEFAULT_WINGHOME os.environ['WINGHOME'] = PATH_DEFAULT_WINGHOME @@ -134,5 +134,5 @@ def connect_wing(): # Main Code Block, runs this script as main (testing) # ------------------------------------------------------------------------- if __name__ == '__main__': - _G_DEBUG = True - main(verbose=_G_DEBUG, connect_debugger=_G_DEBUG) + _DCCSI_GDEBUG = True + main(verbose=_DCCSI_GDEBUG, connect_debugger=_DCCSI_GDEBUG) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py index 0c25d16133..5578bcd577 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py @@ -6,8 +6,11 @@ SPDX-License-Identifier: Apache-2.0 OR MIT """ # ------------------------------------------------------------------------- -"""Extend the .env using dynaconf (dynamic configuration and settings) -This config.py module assumes a minimal enviornment is defined in the .env +""".config +Generate dynamic and synethetic environment contest and settings +using dynaconf (dynamic configuration and settings) +This config.py synthetic env can be overriden or extended with a local .env +See: example.env.tmp (copy and rename to .env) To do: ensure that we can stack/layer the dynamic env to work with O3DE projects """ @@ -16,60 +19,97 @@ import os import sys import site import re +import logging as _logging # 3rdParty (possibly) py3 ships with pathlib, 2.7 does not # import pathlib # our framework for dcc tools need to run in apps like Maya that may still be # on py27 so we need to import and use after some boostrapping +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +_O3DE_RUNNING=None +try: + import azlmbr + _O3DE_RUNNING=True +except: + _O3DE_RUNNING=False +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +def attach_debugger(): + _DCCSI_GDEBUG = True + os.environ["DYNACONF_DCCSI_GDEBUG"] = str(_DCCSI_GDEBUG) + + _DCCSI_DEV_MODE = True + os.environ["DYNACONF_DCCSI_DEV_MODE"] = str(_DCCSI_DEV_MODE) + + from azpy.test.entry_test import connect_wing + _debugger = connect_wing() + + return _debugger +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +# global scope +_MODULENAME = __name__ +if _MODULENAME is '__main__': + _MODULENAME = 'DCCsi.config' -# -------------------------------------------------------------------+------ #os.environ['PYTHONINSPECT'] = 'True' _MODULE_PATH = os.path.abspath(__file__) # we don't have access yet to the DCCsi Lib\site-packages # (1) this will give us import access to azpy (always?) -_DCCSIG_PATH = os.getenv('DCCSIG_PATH', +_DCCSI_PATH = os.getenv('DCCSI_PATH', os.path.abspath(os.path.dirname(_MODULE_PATH))) # ^ we assume this config is in the root of the DCCsi -# if it's not, be sure to set envar 'DCCSIG_PATH' to ensure it -site.addsitedir(_DCCSIG_PATH) # PYTHONPATH +# if it's not, be sure to set envar 'DCCSI_PATH' to ensure it +site.addsitedir(_DCCSI_PATH) # must be done for azpy # now we have azpy api access import azpy from azpy.env_bool import env_bool from azpy.constants import ENVAR_DCCSI_GDEBUG from azpy.constants import ENVAR_DCCSI_DEV_MODE +from azpy.constants import ENVAR_DCCSI_LOGLEVEL # set up global space, logging etc. # set these true if you want them set globally for debugging _DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False) _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False) - -_PACKAGENAME = 'DCCsi.config' - -_LOG_LEVEL = int(20) +_DCCSI_LOGLEVEL = int(env_bool(ENVAR_DCCSI_LOGLEVEL, int(20))) if _DCCSI_GDEBUG: - _LOG_LEVEL = int(10) -_LOGGER = azpy.initialize_logger(_PACKAGENAME, - log_to_file=False, - default_log_level=_LOG_LEVEL) -_LOGGER.info('Starting up: {}.'.format({_PACKAGENAME})) -_LOGGER.info('site.addsitedir({})'.format(_DCCSIG_PATH)) -_LOGGER.debug('_DCCSI_GDEBUG: {}'.format(_DCCSI_GDEBUG)) -_LOGGER.debug('_DCCSI_DEV_MODE: {}'.format(_DCCSI_DEV_MODE)) + _DCCSI_LOGLEVEL = int(10) # early attach WingIDE debugger (can refactor to include other IDEs later) +# requires externally enabling via ENVAR if _DCCSI_DEV_MODE: - from azpy.test.entry_test import connect_wing - foo = connect_wing() + _debugger = attach_debugger() # to do: ^ this should be replaced with full featured azpy.dev.util # that supports additional debuggers (pycharm, vscode, etc.) + +# set up module logging +for handler in _logging.root.handlers[:]: + _logging.root.removeHandler(handler) + +_LOGGER = azpy.initialize_logger(_MODULENAME, + log_to_file=_DCCSI_GDEBUG, + default_log_level=_DCCSI_LOGLEVEL) +_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME})) +_LOGGER.info('site.addsitedir({})'.format(_DCCSI_PATH)) +_LOGGER.debug('_DCCSI_GDEBUG: {}'.format(_DCCSI_GDEBUG)) +_LOGGER.debug('_DCCSI_DEV_MODE: {}'.format(_DCCSI_DEV_MODE)) +_LOGGER.debug('_DCCSI_LOGLEVEL: {}'.format(_DCCSI_LOGLEVEL)) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -# (2) this will give us import access to modules we provide -_DCCSI_PYTHON_LIB_PATH = azpy.config_utils.bootstrap_dccsi_py_libs(_DCCSIG_PATH) +# this will give us import access to additional modules we provide with DCCsi +_DCCSI_PYTHON_LIB_PATH = azpy.config_utils.bootstrap_dccsi_py_libs(_DCCSI_PATH) # Now we should be able to just carry on with pth lib and dynaconf from dynaconf import Dynaconf @@ -79,82 +119,103 @@ except: import pathlib2 as pathlib from pathlib import Path -_DCCSIG_PATH = Path(_DCCSIG_PATH).resolve() -_DCCSI_PYTHON_LIB_PATH = Path(_DCCSI_PYTHON_LIB_PATH).resolve() +_DCCSI_PATH = Path(_DCCSI_PATH) # pathify +_DCCSI_PYTHON_PATH = Path(_DCCSI_PATH,'3rdParty','Python') +_DCCSI_PYTHON_LIB_PATH = Path(_DCCSI_PYTHON_LIB_PATH) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -def init_ly_pyside(LY_DEV=None): - """sets access to lumberyards Qt dlls and PySide""" +# start locally prepping known default values for dyanmic environment settings +_O3DE_DCCSI_PATH = os.environ['PATH'] +os.environ["DYNACONF_PATH"] = _O3DE_DCCSI_PATH - LY_DEV = Path(LY_DEV).resolve() - if not LY_DEV.exists(): - raise Exception('LY_DEV does NOT exist: {0}'.format(LY_DEV)) +# this will retreive the O3DE engine root +_O3DE_DEV = azpy.config_utils.get_o3de_engine_root() +# set up dynamic config envars +os.environ["DYNACONF_O3DE_DEV"] = str(_O3DE_DEV.resolve()) + +from azpy.constants import TAG_DIR_O3DE_BUILD_FOLDER +_O3DE_BUILD_FOLDER = TAG_DIR_O3DE_BUILD_FOLDER +os.environ["DYNACONF_O3DE_BUILD_FOLDER"] = str(_O3DE_BUILD_FOLDER) +_O3DE_BUILD_PATH = Path(_O3DE_DEV, TAG_DIR_O3DE_BUILD_FOLDER) +os.environ["DYNACONF_O3DE_BUILD_PATH"] = str(_O3DE_BUILD_PATH.resolve()) + +from azpy.constants import STR_O3DE_BIN_PATH +_O3DE_BIN_PATH = Path(STR_O3DE_BIN_PATH.format(_O3DE_BUILD_PATH)) +os.environ["DYNACONF_O3DE_BIN_PATH"] = str(_O3DE_BIN_PATH.resolve()) + +# this in most cases will return the project folder +# if it returns a matching engine folder then we don't know the project folder +_O3DE_PROJECT_PATH = azpy.config_utils.get_o3de_project_path() +os.environ["DYNACONF_O3DE_PROJECT_PATH"] = str(_O3DE_PROJECT_PATH.resolve()) + +# special, a home for stashing PYTHONPATHs into managed settings +_O3DE_PYTHONPATH = list() +_O3DE_PYTHONPATH.append(_DCCSI_PATH) +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +def init_o3de_pyside2(dccsi_path=_DCCSI_PATH, + engine_bin=_O3DE_BIN_PATH): + """Initialize the DCCsi Qt/PySide dynamic env and settings + sets access to lumberyards Qt dlls and PySide""" + + _DCCSI_PATH = Path(dccsi_path) + _O3DE_BIN_PATH = Path(engine_bin) + + if not _O3DE_BIN_PATH.exists(): + raise Exception('_O3DE_BIN_PATH does NOT exist: {0}'.format(_O3DE_BIN_PATH)) else: - # to do: 'windows_vs2019' might change or be different locally - # 'windows_vs2019' is defined as a str tag in constants - # we may not yet have access to azpy.constants :( - from azpy.constants import TAG_DIR_LY_BUILD - from azpy.constants import PATH_LY_BUILD_PATH - from azpy.constants import PATH_LY_BIN_PATH - # to do: pull some of these str and tags from constants - LY_BUILD_PATH = Path.joinpath(LY_DEV, - TAG_DIR_LY_BUILD).resolve() - LY_BIN_PATH = Path.joinpath(LY_BUILD_PATH, - 'bin', - 'profile').resolve() + pass + + # python config + _DCCSI_PYTHON_PATH = Path(_DCCSI_PATH,'3rdParty','Python') + os.environ["DYNACONF_DCCSI_PYTHON_PATH"] = str(_DCCSI_PYTHON_PATH.resolve()) # # allows to retreive from settings.QTFORPYTHON_PATH # from azpy.constants import STR_QTFORPYTHON_PATH # a path string constructor - # QTFORPYTHON_PATH = Path(STR_QTFORPYTHON_PATH.format(LY_DEV)).resolve() + # QTFORPYTHON_PATH = Path(STR_QTFORPYTHON_PATH.format(O3DE_DEV)).resolve() # os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH) # site.addsitedir(str(QTFORPYTHON_PATH)) # PYTHONPATH - QT_PLUGIN_PATH = Path.joinpath(LY_BIN_PATH, - 'EditorPlugins').resolve() - os.environ["DYNACONF_QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH) + QT_PLUGIN_PATH = Path.joinpath(_O3DE_BIN_PATH,'EditorPlugins') + os.environ["DYNACONF_QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH.resolve()) os.environ['PATH'] = QT_PLUGIN_PATH.as_posix() + os.pathsep + os.environ['PATH'] - QT_QPA_PLATFORM_PLUGIN_PATH = Path.joinpath(QT_PLUGIN_PATH, - 'platforms').resolve() - os.environ["DYNACONF_QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH) + QT_QPA_PLATFORM_PLUGIN_PATH = Path.joinpath(QT_PLUGIN_PATH, 'platforms') + os.environ["DYNACONF_QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH.resolve()) # if the line below is removed external standalone apps can't load PySide2 - os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH) + os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH.resolve()) # ^^ bypass trying to set only with DYNACONF environment os.environ['PATH'] = QT_QPA_PLATFORM_PLUGIN_PATH.as_posix() + os.pathsep + os.environ['PATH'] # ^^ this particular env only works correctly if put on the PATH in this manner # add Qt binaries to the Windows path to handle findings DLL file dependencies if sys.platform.startswith('win'): - # path = os.environ['PATH'] - # newPath = '' - # newPath += str(LY_BIN_PATH) + os.pathsep - # newPath += str(Path.joinpath(QTFORPYTHON_PATH, - # 'shiboken2').resolve()) + os.pathsep - # newPath += str(Path.joinpath(QTFORPYTHON_PATH, - # 'PySide2').resolve()) + os.pathsep - # newPath += path - # os.environ['PATH']=newPath - _LOGGER.debug('PySide2 bootstrapped PATH for Windows.') + _LOGGER.info('~ Qt/PySide2 bootstrapped PATH for Windows.') + else: + _LOGGER.warning('~ Not tested on Non-Windows platforms.') + # To Do: figure out how to test and/or modify to work try: import PySide2 - _LOGGER.debug('DCCsi, config.py: SUCCESS: import PySide2') + _LOGGER.info('~ SUCCESS: import PySide2') _LOGGER.debug(PySide2) status = True except ImportError as e: - _LOGGER.debug('DCCsi, config.py: FAILURE: import PySide2') + _LOGGER.error('~ FAILURE: import PySide2') status = False raise(e) try: import shiboken2 - _LOGGER.debug('DCCsi, config.py: SUCCESS: import shiboken2') + _LOGGER.info('~ SUCCESS: import shiboken2') _LOGGER.debug(shiboken2) status = True except ImportError as e: - _LOGGER.debug('DCCsi, config.py: FAILURE: import shiboken2') + _LOGGER.error('~ FAILURE: import shiboken2') status = False raise(e) @@ -162,19 +223,44 @@ def init_ly_pyside(LY_DEV=None): # to do: move path construction string to constants and build off of SDK # have not done that yet as I really want to get legal approval and # add this to the QtForPython Gem - # please pass this on the current code review - DCCSI_PYSIDE2_TOOLS = Path.joinpath(LY_DEV, - 'Gems', - 'AtomLyIntegration', - 'TechnicalArt', - 'DccScriptingInterface', - '.dev', - 'QtForPython', - 'pyside2-tools-dev') - os.environ["DYNACONF_DCCSI_PYSIDE2_TOOLS"] = str(DCCSI_PYSIDE2_TOOLS.resolve()) - os.environ['PATH'] = DCCSI_PYSIDE2_TOOLS.as_posix() + os.pathsep + os.environ['PATH'] + # please pass this in current code reviews + _DCCSI_PYSIDE2_TOOLS = Path(_DCCSI_PYTHON_PATH,'pyside2-tools') + if _DCCSI_PYSIDE2_TOOLS.exists(): + os.environ["DYNACONF_DCCSI_PYSIDE2_TOOLS"] = str(_DCCSI_PYSIDE2_TOOLS.resolve()) + os.environ['PATH'] = _DCCSI_PYSIDE2_TOOLS.as_posix() + os.pathsep + os.environ['PATH'] + + site.addsitedir(_DCCSI_PYSIDE2_TOOLS) + _O3DE_PYTHONPATH.append(_DCCSI_PYSIDE2_TOOLS.resolve()) + _LOGGER.info('~ PySide2-Tools bootstrapped PATH for Windows.') + try: + import pyside2uic + _LOGGER.info('~ SUCCESS: import pyside2uic') + _LOGGER.debug(shiboken2) + status = True + except ImportError as e: + _LOGGER.error('~ FAILURE: import pyside2uic') + status = False + raise(e) + else: + _LOGGER.warning('~ No PySide2 Tools: {}'.format(_DCCSI_PYSIDE2_TOOLS.resolve)) + + _O3DE_DCCSI_PATH = os.environ['PATH'] + os.environ["DYNACONF_PATH"] = _O3DE_DCCSI_PATH + + try: + _DCCSI_PYTHONPATH = os.environ['PYTHONPATH'] + os.environ["DYNACONF_PYTHONPATH"] = _DCCSI_PYTHONPATH + except: + pass - return status + from dynaconf import settings + + _LOGGER.info('~ config.init_o3de_pyside() ... DONE') + + if status: + return settings + else: + return None # ------------------------------------------------------------------------- @@ -182,90 +268,219 @@ def init_ly_pyside(LY_DEV=None): def test_pyside2(): """Convenience method to test Qt / PySide2 access""" # now test - _LOGGER.info('Testing Qt / PySide2') + _LOGGER.info('~ Testing Qt / PySide2') try: from PySide2.QtWidgets import QApplication, QPushButton app = QApplication(sys.argv) - hello = QPushButton("Hello world!") + hello = QPushButton("~ O3DE DCCsi PySide2 Test!") hello.resize(200, 60) hello.show() except Exception as e: - _LOGGER.error('FAILURE: Qt / PySide2') + _LOGGER.error('~ FAILURE: Qt / PySide2') status = False raise(e) - _LOGGER.info('SUCCESS: .test_pyside2()') + _LOGGER.info('~ SUCCESS: .test_pyside2()') sys.exit(app.exec_()) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- -# `envvar_prefix` = export envvars with `export DYNACONF_FOO=bar`. -# `settings_files` = Load this files in the order. -# here we are modifying or adding to the dynamic config settings on import -settings = Dynaconf( - envvar_prefix="DYNACONF", - settings_files=['settings.json', '.secrets.json'], -) +def init_o3de_core(engine_path=_O3DE_DEV, + build_folder=_O3DE_BUILD_FOLDER, + project_name=None, + project_path=_O3DE_PROJECT_PATH): + """Initialize the DCCsi Core dynamic env and settings""" + # `envvar_prefix` = export envvars with `export DYNACONF_FOO=bar`. + # `settings_files` = Load this files in the order. + # here we are modifying or adding to the dynamic config settings on import + settings = Dynaconf(envvar_prefix='DYNACONF', + settings_files=['settings.json', + 'dev.settings.json', + 'user.settings.json', + '.secrets.json']) + + # global settings + os.environ["DYNACONF_DCCSI_OS_FOLDER"] = azpy.config_utils.get_os() + os.environ["DYNACONF_DCCSI_GDEBUG"] = str(_DCCSI_GDEBUG) + os.environ["DYNACONF_DCCSI_DEV_MODE"] = str(_DCCSI_DEV_MODE) + os.environ['DYNACONF_DCCSI_LOGLEVEL'] = str(_DCCSI_LOGLEVEL) -from azpy.constants import PATH_LY_BUILD_PATH -from azpy.constants import PATH_LY_BIN_PATH + os.environ["DYNACONF_DCCSI_PATH"] = str(_DCCSI_PATH.resolve()) + os.environ['PATH'] = _DCCSI_PATH.as_posix() + os.pathsep + os.environ['PATH'] -# global settings -os.environ["DYNACONF_DCCSI_GDEBUG"] = str(_DCCSI_GDEBUG) -os.environ["DYNACONF_DCCSI_DEV_MODE"] = str(_DCCSI_DEV_MODE) + # we already defaulted to discovering these two early because of importance + #os.environ["DYNACONF_O3DE_DEV"] = str(_O3DE_DEV.resolve()) + #os.environ["DYNACONF_O3DE_PROJECT_PATH"] = str(_O3DE_PROJECT_PATH) + # we also already added them to DYNACONF_ + # this in an explicit pass in + if project_path: + _project_path = Path(project_path) + try: + _project_path.exists() + _O3DE_PROJECT_PATH = _project_path + os.environ["DYNACONF_O3DE_PROJECT_PATH"] = str(_O3DE_PROJECT_PATH.resolve()) + except FileExistsError as e: + _LOGGER.error('~ The project path specified does not appear to exist!') + _LOGGER.warning('~ project_path: {}'.format(project_path)) + _LOGGER.warning('~ fallback to engine root: {}'.format()) + project_path = _O3DE_DEV + os.environ["DYNACONF_O3DE_PROJECT_PATH"] = str(_O3DE_DEV.resolve()) -# search up to get \dev -_LY_DEV = azpy.config_utils.get_stub_check_path(in_path=_DCCSIG_PATH, - check_stub='engine.json') -os.environ["DYNACONF_LY_DEV"] = str(_LY_DEV.resolve()) -_LY_PROJECT = azpy.config_utils.get_current_project() -os.environ["DYNACONF_LY_PROJECT"] = str(_LY_PROJECT.resolve()) -_LY_PROJECT_PATH = Path(_LY_DEV, _LY_PROJECT) -os.environ["DYNACONF_LY_PROJECT_PATH"] = str(_LY_PROJECT_PATH) -os.environ["DYNACONF_DCCSIG_PATH"] = str(_DCCSIG_PATH) -_DCCSI_CONFIG_PATH = Path(_MODULE_PATH).resolve() -os.environ["DYNACONF_DCCSI_CONFIG_PATH"] = str(_DCCSI_CONFIG_PATH) -_DCCSIG_SDK_PATH = Path.joinpath(_DCCSIG_PATH, 'SDK').resolve() -os.environ["DYNACONF_DCCSIG_SDK_PATH"] = str(_DCCSIG_SDK_PATH) -os.environ["DYNACONF_DCCSI_PYTHON_LIB_PATH"] = str(_DCCSI_PYTHON_LIB_PATH) -os.environ["DYNACONF_OS_FOLDER"] = azpy.config_utils.get_os() + # we can pull the O3DE_PROJECT (name) from the project path + if not project_name: + project_name = Path(_O3DE_PROJECT_PATH).name + os.environ["DYNACONF_O3DE_PROJECT"] = str(project_name) + # To Do: there might be a project namespace in the project.json? -# we need to set up the Ly dev build \bin\path (for Qt dll access) -_LY_BUILD_PATH = Path(PATH_LY_BUILD_PATH).resolve() -os.environ["DYNACONF_LY_BUILD_PATH"] = str(_LY_BUILD_PATH) -_LY_BIN_PATH = Path(PATH_LY_BIN_PATH).resolve() -os.environ["DYNACONF_LY_BIN_PATH"] = str(_LY_BIN_PATH) + # -- O3DE build -- set up \bin\path (for Qt dll access) + os.environ["DYNACONF_O3DE_BUILD_FOLDER"] = str(build_folder) + _O3DE_BUILD_PATH = Path(_O3DE_DEV, build_folder) + + os.environ["DYNACONF_O3DE_BUILD_PATH"] = str(_O3DE_BUILD_PATH.resolve()) + + _O3DE_BIN_PATH = Path(STR_O3DE_BIN_PATH.format(_O3DE_BUILD_PATH)) + os.environ["DYNACONF_O3DE_BIN_PATH"] = str(_O3DE_BIN_PATH.resolve()) + + # hard check + if not _O3DE_BIN_PATH.exists(): + raise Exception('O3DE_BIN_PATH does NOT exist: {0}'.format(_O3DE_BIN_PATH)) + else: + # adding to sys.path apparently doesn't work for .dll locations like Qt + os.environ['PATH'] = _O3DE_BIN_PATH.as_posix() + os.pathsep + os.environ['PATH'] + # -- + + from azpy.constants import TAG_DIR_DCCSI_TOOLS + _DCCSI_TOOLS_PATH = Path(_DCCSI_PATH, TAG_DIR_DCCSI_TOOLS) + os.environ["DYNACONF_DCCSI_TOOLS_PATH"] = str(_DCCSI_TOOLS_PATH.resolve()) + + from azpy.constants import TAG_DCCSI_NICKNAME + from azpy.constants import PATH_DCCSI_LOG_PATH + _DCCSI_LOG_PATH = Path(PATH_DCCSI_LOG_PATH.format(O3DE_PROJECT_PATH=project_path, + TAG_DCCSI_NICKNAME=TAG_DCCSI_NICKNAME)) + os.environ["DYNACONF_DCCSI_LOG_PATH"] = str(_DCCSI_LOG_PATH) + + from azpy.constants import TAG_DIR_REGISTRY, TAG_DCCSI_CONFIG + _DCCSI_CONFIG_PATH = Path(project_path, TAG_DIR_REGISTRY, TAG_DCCSI_CONFIG) + os.environ["DYNACONF_DCCSI_CONFIG_PATH"] = str(_DCCSI_CONFIG_PATH.resolve()) + + from azpy.constants import TAG_DIR_DCCSI_TOOLS + _DCCSIG_TOOLS_PATH = Path.joinpath(_DCCSI_PATH, TAG_DIR_DCCSI_TOOLS) + os.environ["DYNACONF_DCCSIG_TOOLS_PATH"] = str(_DCCSIG_TOOLS_PATH.resolve()) + + _O3DE_DCCSI_PATH = os.environ['PATH'] + os.environ["DYNACONF_PATH"] = _O3DE_DCCSI_PATH + + from dynaconf import settings + + _LOGGER.info('~ config.init_o3de_core() ... DONE') + + return settings +# ------------------------------------------------------------------------- -# project cache log dir path -from azpy.constants import ENVAR_DCCSI_LOG_PATH -from azpy.constants import PATH_DCCSI_LOG_PATH -_DCCSI_LOG_PATH = Path(os.getenv(ENVAR_DCCSI_LOG_PATH, - Path(PATH_DCCSI_LOG_PATH.format(LY_DEV=_LY_DEV, - LY_PROJECT=_LY_PROJECT)))) -os.environ["DYNACONF_DCCSI_LOG_PATH"] = str(_DCCSI_LOG_PATH) -# hard checks -if not _LY_BIN_PATH.exists(): - raise Exception('LY_BIN_PATH does NOT exist: {0}'.format(_LY_BIN_PATH)) -else: - # adding to sys.path apparently doesn't work for .dll locations like Qt - os.environ['PATH'] = _LY_BIN_PATH.as_posix() + os.pathsep + os.environ['PATH'] +# ------------------------------------------------------------------------- +def init_o3de_python(engine_path=_O3DE_DEV, + engine_bin=_O3DE_BIN_PATH, + dccsi_path=_DCCSI_PATH): + + # pathify + _O3DE_DEV = Path(engine_path) + _O3DE_BIN_PATH = Path(engine_bin) + _DCCSI_PATH = Path(dccsi_path) + + # python config + _DCCSI_PYTHON_PATH = Path(_DCCSI_PATH,'3rdParty','Python') + os.environ["DYNACONF_DCCSI_PYTHON_PATH"] = str(_DCCSI_PYTHON_PATH.resolve()) + + _DCCSI_PYTHON_LIB_PATH = azpy.config_utils.bootstrap_dccsi_py_libs(_DCCSI_PATH) + os.environ["DYNACONF_DCCSI_PYTHON_LIB_PATH"] = str(_DCCSI_PYTHON_LIB_PATH.resolve()) + os.environ['PATH'] = _DCCSI_PYTHON_LIB_PATH.as_posix() + os.pathsep + os.environ['PATH'] + site.addsitedir(_DCCSI_PYTHON_LIB_PATH) + _O3DE_PYTHONPATH.append(_DCCSI_PYTHON_LIB_PATH.resolve()) + + site.addsitedir(_O3DE_BIN_PATH) + _O3DE_PYTHONPATH.append(_O3DE_BIN_PATH.resolve()) + + _O3DE_PY_EXE = Path(sys.executable) + _DCCSI_PY_IDE = Path(_O3DE_PY_EXE) + os.environ["DYNACONF_DCCSI_PY_IDE"] = str(_DCCSI_PY_IDE.resolve()) + + _O3DE_PYTHONHOME = Path(_O3DE_PY_EXE.parents[0]) + os.environ["DYNACONF_O3DE_PYTHONHOME"] = str(_O3DE_PYTHONHOME.resolve()) + os.environ['PATH'] = _O3DE_PYTHONHOME.as_posix() + os.pathsep + os.environ['PATH'] + _LOGGER.info('~ O3DE_PYTHONHOME - is now the folder containing O3DE python executable') + + _O3DE_PYTHON_INSTALL = Path(_O3DE_DEV, 'python') + os.environ["DYNACONF_O3DE_PYTHON_INSTALL"] = str(_O3DE_PYTHON_INSTALL.resolve()) + os.environ['PATH'] = _O3DE_PYTHON_INSTALL.as_posix() + os.pathsep + os.environ['PATH'] -_LOGGER.info('Dynaconf config.py ... DONE') + if sys.platform.startswith('win'): + _DCCSI_PY_BASE = Path(_O3DE_PYTHON_INSTALL, 'python.cmd') + elif sys.platform == "linux": + _DCCSI_PY_BASE = Path(_O3DE_PYTHON_INSTALL, 'python.sh') + elif sys.platform == "darwin": + _DCCSI_PY_BASE = Path(_O3DE_PYTHON_INSTALL, 'python.sh') + else: + _DCCSI_PY_BASE = None + + if _DCCSI_PY_BASE: + os.environ["DYNACONF_DCCSI_PY_BASE"] = str(_DCCSI_PY_BASE.resolve()) + + _O3DE_DCCSI_PATH = os.environ['PATH'] + os.environ["DYNACONF_PATH"] = _O3DE_DCCSI_PATH + + try: + _DCCSI_PYTHONPATH = os.environ['PYTHONPATH'] + os.environ["DYNACONF_PYTHONPATH"] = _DCCSI_PYTHONPATH + except: + pass + + from dynaconf import settings + + _LOGGER.info('~ config.init_o3de_python() ... DONE') + + return settings # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # settings.setenv() # doing this will add the additional DYNACONF_ envars -def get_config_settings(setup_ly_pyside=False): - """Convenience method to set and retreive settings directly from module.""" +def get_config_settings(engine_path=_O3DE_DEV, + build_folder=_O3DE_BUILD_FOLDER, + project_name=None, + project_path=_O3DE_PROJECT_PATH, + enable_o3de_python=None, + enable_o3de_pyside2=None, + set_env=True): + """Convenience method to initialize and retreive settings directly from module.""" + + settings = init_o3de_core(engine_path, + build_folder, + project_name, + project_path) + + if enable_o3de_python: + settings = init_o3de_python(settings.O3DE_DEV, + settings.O3DE_BIN_PATH, + settings.DCCSI_PATH) + + # These should ONLY be set for O3DE and non-DCC environments + # They will most likely cause other Qt/PySide DCC apps to fail + # or hopefully they can be overridden for DCC envionments + # that provide their own Qt dlls and Pyside2 + # _LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) + # _LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH)) + # assume our standalone python tools wants this access? + # it's safe to do this for dev and from ide + if enable_o3de_pyside2: + settings = init_o3de_pyside2(settings.DCCSI_PATH, + settings.O3DE_BIN_PATH) + + # now standalone we can validate the config. env, settings. from dynaconf import settings - - if setup_ly_pyside: - init_ly_pyside(settings.LY_DEV) - - settings.setenv() + if set_env: + settings.setenv() return settings # --- END ----------------------------------------------------------------- @@ -274,54 +489,216 @@ def get_config_settings(setup_ly_pyside=False): # Main Code Block, runs this script as main (testing) # ------------------------------------------------------------------------- if __name__ == '__main__': - """Run this file as main""" + """Run this file as a standalone cli script""" + + _MODULENAME = __name__ + if _MODULENAME is '__main__': + _MODULENAME = 'DCCsi.config' + + from azpy.constants import STR_CROSSBAR + + while 0: # temp internal debug flag + _DCCSI_GDEBUG = True + break + + # overide logger for standalone to be more verbose and log to file + _LOGGER = azpy.initialize_logger(_MODULENAME, + log_to_file=_DCCSI_GDEBUG, + default_log_level=_DCCSI_LOGLEVEL) - _LOG_LEVEL = int(10) # same as _logging.DEBUG + # happy print + _LOGGER.info(STR_CROSSBAR) + _LOGGER.info('~ constants.py ... Running script as __main__') + _LOGGER.info(STR_CROSSBAR) - _LOGGER = azpy.initialize_logger(_PACKAGENAME, - log_to_file=True, - default_log_level=_LOG_LEVEL) + # go ahead and run the rest of the configuration + # parse the command line args + import argparse + parser = argparse.ArgumentParser( + description='O3DE DCCsi Dynamic Config (dynaconf)', + epilog="Attempts to determine O3DE project if -pp not set") + parser.add_argument('-gd', '--global-debug', + type=bool, + required=False, + help='Enables global debug flag.') + parser.add_argument('-dm', '--developer-mode', + type=bool, + required=False, + help='Enables dev mode for early auto attaching debugger.') + parser.add_argument('-ep', '--engine-path', + type=pathlib.Path, + required=False, + help='The path to the o3de engine.') + parser.add_argument('-bf', '--build-folder', + type=str, + required=False, + help='The name (tag) of the o3de build folder, example build or windows_vs2019.') + parser.add_argument('-pp', '--project-path', + type=pathlib.Path, + required=False, + help='The path to the project.') + parser.add_argument('-pn', '--project-name', + type=str, + required=False, + help='The name of the project.') + parser.add_argument('-py', '--enable-python', + type=bool, + required=False, + help='Enables O3DE python access.') + parser.add_argument('-qt', '--enable-qt', + type=bool, + required=False, + help='Enables O3DE Qt\PySide2 access.') + parser.add_argument('-sd', '--set-debugger', + type=str, + required=False, + help='Default debugger: WING, others: PYCHARM, VSCODE (not yet implemented).') + parser.add_argument('-pc', '--project-config', + type=bool, + required=False, + help='Enables reading the projects registry\dccsiconfiguration.setreg.') + parser.add_argument('-es', '--export-settings', + type=pathlib.Path, + required=False, + help='Writes managed settings to specified path.') + parser.add_argument('-ec', '--export-configuration', + type=bool, + required=False, + help='writes settings as a O3DE registry\dccsiconfiguration.setreg.') + parser.add_argument('-tp', '--test-pyside2', + type=bool, + required=False, + help='Runs Qt/PySide2 tests and reports.') + args = parser.parse_args() - from dynaconf import settings + # easy overrides + if args.global_debug: + _DCCSI_GDEBUG = True + os.environ["DYNACONF_DCCSI_GDEBUG"] = str(_DCCSI_GDEBUG) + if args.developer_mode: + attach_debugger() # attempts to start debugger + if args.set_debugger: + _LOGGER.info('Setting and switching debugger type from WingIDE not implemented.') + # To Do: implement debugger plugin pattern + + # need to do a little plumbing + if not args.engine_path: + args.engine_path=_O3DE_DEV + if not args.build_folder: + from azpy.constants import TAG_DIR_O3DE_BUILD_FOLDER + args.build_folder = TAG_DIR_O3DE_BUILD_FOLDER + if not args.project_path: + args.project_path=_O3DE_PROJECT_PATH + + if _DCCSI_GDEBUG: + args.enable_python = True + args.enable_qt = True + + # now standalone we can validate the config. env, settings. + settings = get_config_settings(engine_path=args.engine_path, + build_folder=args.build_folder, + project_name=args.project_name, + project_path=args.project_path, + enable_o3de_python=args.enable_python, + enable_o3de_pyside2=args.enable_qt) + + ## CORE + _LOGGER.info(STR_CROSSBAR) # not using fstrings in this module because it might run in py2.7 (maya) _LOGGER.info('DCCSI_GDEBUG: {}'.format(settings.DCCSI_GDEBUG)) _LOGGER.info('DCCSI_DEV_MODE: {}'.format(settings.DCCSI_DEV_MODE)) _LOGGER.info('DCCSI_LOGLEVEL: {}'.format(settings.DCCSI_LOGLEVEL)) - - _LOGGER.info('OS_FOLDER: {}'.format(settings.OS_FOLDER)) - _LOGGER.info('LY_PROJECT: {}'.format(settings.LY_PROJECT)) - _LOGGER.info('LY_PROJECT_PATH: {}'.format(settings.LY_PROJECT_PATH)) - _LOGGER.info('LY_DEV: {}'.format(settings.LY_DEV)) - _LOGGER.info('LY_BUILD_PATH: {}'.format(settings.LY_BUILD_PATH)) - _LOGGER.info('LY_BIN_PATH: {}'.format(settings.LY_BIN_PATH)) - + _LOGGER.info('DCCSI_OS_FOLDER: {}'.format(settings.DCCSI_OS_FOLDER)) + + _LOGGER.info('O3DE_DEV: {}'.format(settings.O3DE_DEV)) + _LOGGER.info('O3DE_O3DE_BUILD_FOLDER: {}'.format(settings.O3DE_BUILD_PATH)) + _LOGGER.info('O3DE_BUILD_PATH: {}'.format(settings.O3DE_BUILD_PATH)) + _LOGGER.info('O3DE_BIN_PATH: {}'.format(settings.O3DE_BIN_PATH)) + + _LOGGER.info('O3DE_PROJECT: {}'.format(settings.O3DE_PROJECT)) + _LOGGER.info('O3DE_PROJECT_PATH: {}'.format(settings.O3DE_PROJECT_PATH)) + + _LOGGER.info('DCCSI_PATH: {}'.format(settings.DCCSI_PATH)) _LOGGER.info('DCCSI_LOG_PATH: {}'.format(settings.DCCSI_LOG_PATH)) - _LOGGER.info('DCCSI_CONFIG_PATH: {}'.format(settings.DCCSI_CONFIG_PATH)) - _LOGGER.info('DCCSIG_PATH: {}'.format(settings.DCCSIG_PATH)) - _LOGGER.info('DCCSI_PYTHON_LIB_PATH: {}'.format(settings.DCCSI_PYTHON_LIB_PATH)) - _LOGGER.info('DDCCSI_PY_BASE: {}'.format(settings.DDCCSI_PY_BASE)) - - # To Do: These should ONLY be set for Lumberyard and non-DCC environments - # They will most likely cause Qt/PySide DCC apps to fail - # or hopefully they can be overridden for DCC envionments - # that provide their own Qt dlls and Pyside2 - #_LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) - #_LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH)) - - init_ly_pyside(settings.LY_DEV) # init lumberyard Qt/PySide2 - # from dynaconf import settings # <-- no need to reimport + + if settings.O3DE_DCCSI_ENV_TEST: + _LOGGER.info('O3DE_DCCSI_ENV_TEST: {}'.format(settings.O3DE_DCCSI_ENV_TEST)) + + _LOGGER.info(STR_CROSSBAR) + _LOGGER.info('') + + if args.enable_python: + _LOGGER.info(STR_CROSSBAR) + _LOGGER.info('DCCSI_PYTHON_PATH'.format(settings.DCCSI_PYTHON_PATH)) + _LOGGER.info('DCCSI_PYTHON_LIB_PATH: {}'.format(settings.DCCSI_PYTHON_LIB_PATH)) + _LOGGER.info('DCCSI_PY_IDE'.format(settings.DCCSI_PY_IDE)) + _LOGGER.info('O3DE_PYTHONHOME'.format(settings.O3DE_PYTHONHOME)) + _LOGGER.info('O3DE_PYTHON_INSTALL'.format(settings.O3DE_PYTHON_INSTALL)) + _LOGGER.info('DCCSI_PY_BASE: {}'.format(settings.DCCSI_PY_BASE)) + _LOGGER.info(STR_CROSSBAR) + _LOGGER.info('') + else: + _LOGGER.info('Tip: add arg --enable-python to extend the environment with O3DE python access') + + if args.enable_qt: + _LOGGER.info(STR_CROSSBAR) + # _LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) + _LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH)) + _LOGGER.info('QT_QPA_PLATFORM_PLUGIN_PATH: {}'.format(settings.QT_QPA_PLATFORM_PLUGIN_PATH)) + _LOGGER.info('DCCSI_PYSIDE2_TOOLS: {}'.format(settings.DCCSI_PYSIDE2_TOOLS)) + _LOGGER.info(STR_CROSSBAR) + _LOGGER.info('') + else: + _LOGGER.info('Tip: add arg --enable-qt to extend the environment with O3DE Qt/PySide2 support') + settings.setenv() # doing this will add/set the additional DYNACONF_ envars + + if _DCCSI_GDEBUG or args.export_settings: + # to do: need to add malformed json validation to \settings.json + # this can cause a bad error that is deep and hard to debug + + # writting settings + from dynaconf import loaders + from dynaconf.utils.boxing import DynaBox + + _settings_dict = settings.as_dict() + + # default temp filename + _settings_file = Path('settings_export.json.tmp') + + # writes to a file, the format is inferred by extension + # can be .yaml, .toml, .ini, .json, .py + #loaders.write(_settings_file, DynaBox(data).to_dict(), merge=False, env='development') + #loaders.write(_settings_file, DynaBox(data).to_dict()) + + # we want to possibly modify or stash our settings into a o3de .setreg + from box import Box + _settings_box = Box(_settings_dict) + + _LOGGER.info('Pretty print, _settings_box: {}'.format(_settings_file)) + _LOGGER.info(str(_settings_box.to_json(sort_keys=True, + indent=4))) + + # writes settings box + _settings_box.to_json(filename=_settings_file.as_posix(), + sort_keys=True, + indent=4) + + if _DCCSI_GDEBUG or args.test_pyside2: + test_pyside2() # test PySide2 access with a pop-up button + try: + import pyside2uic + except ImportError as e: + _LOGGER.warning("Could not import 'pyside2uic'") + _LOGGER.warning("Refer to: '< local DCCsi >\3rdParty\Python\README.txt'") + _LOGGER.error(e) - #_LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) - _LOGGER.info('LY_BIN_PATH: {}'.format(settings.LY_BIN_PATH)) - _LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH)) - _LOGGER.info('QT_QPA_PLATFORM_PLUGIN_PATH: {}'.format(settings.QT_QPA_PLATFORM_PLUGIN_PATH)) - _LOGGER.info('DCCSI_PYSIDE2_TOOLS: {}'.format(settings.DCCSI_PYSIDE2_TOOLS)) - - test_pyside2() # test PySide2 access with a pop-up button + # return + sys.exit() +# --- END ----------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/settings.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/settings.json index 0967ef424b..718c6110c3 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/settings.json +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/settings.json @@ -1 +1,13 @@ -{} +{ + "LOAD_DOTENV": true, + "COMPANY": "Amazon", + "DCCSI_GDEBUG": "False", + "DCCSI_DEV_MODE": "False", + "DCCSI_GDEBUGGER": "WING", + "DCCSI_LOGLEVEL": 20, + "DEFAULT_SETTINGS_PATHS": [ + "settings.py", + "settings.json", + ".secrets.json" + ] +} \ No newline at end of file diff --git a/python/get_python_path.bat b/python/get_python_path.bat new file mode 100644 index 0000000000..66942efd70 --- /dev/null +++ b/python/get_python_path.bat @@ -0,0 +1,33 @@ +@echo off +REM +REM Copyright (c) Contributors to the Open 3D Engine Project. +REM For complete copyright and license terms please see the LICENSE at the root of this distribution. +REM +REM SPDX-License-Identifier: Apache-2.0 OR MIT +REM +REM + +:: Retreives the path to the O3DE python executable(s) + +:: Skip initialization if already completed +IF "%O3DE_PYTHONHOME_INIT%"=="1" GOTO :END_OF_FILE + +SET CMD_DIR=%~dp0 + +:: Note, many DCC tools (like Maya) include thier own versioned python interpretter. +:: Some apps may not operate correctly if PYTHONHOME is set/propogated. +:: This is definitely the case with Maya, doing so causes Maya to not boot. +FOR /F "tokens=* USEBACKQ" %%F IN (`%CMD_DIR%\python.cmd %CMD_DIR%\get_python_path.py`) DO (SET O3DE_PYTHONHOME=%%F) +echo O3DE_PYTHONHOME - is now the folder containing O3DE python executable +echo O3DE_PYTHONHOME = %O3DE_PYTHONHOME% + +SET PYTHON=%O3DE_PYTHONHOME%\python.exe + +:: Set flag so we don't initialize dccsi environment twice +SET O3DE_PYTHONHOME_INIT=1 +GOTO END_OF_FILE + +:: Return to starting directory +POPD + +:END_OF_FILE diff --git a/python/get_python_path.py b/python/get_python_path.py new file mode 100644 index 0000000000..fc503ae4ab --- /dev/null +++ b/python/get_python_path.py @@ -0,0 +1,16 @@ +# coding:utf-8 +#!/usr/bin/python +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# +# ------------------------------------------------------------------------- +"""retreive the O3DE python path""" +import sys +from pathlib import Path +py_exe = Path(sys.executable) +py_dir = py_exe.parents[0] +print(py_dir.resolve()) \ No newline at end of file From 15fbe97504ccbd5d61bc3937e92c69eff348fcd1 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 21 Oct 2021 17:12:08 -0700 Subject: [PATCH 124/152] Adding a guard for this, since tests attempt to apply autonomy to an invalid entity Signed-off-by: kberg-amzn --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index f628119aef..fdd7602f71 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -1148,6 +1148,12 @@ namespace Multiplayer void MultiplayerSystemComponent::EnableAutonomousControl(NetworkEntityHandle entityHandle, AzNetworking::ConnectionId connectionId) { + if (!entityHandle.Exists()) + { + AZLOG_WARN("Attempting to enable autonomous control for an invalid entity"); + return; + } + entityHandle.GetNetBindComponent()->SetOwningConnectionId(connectionId); if (connectionId == InvalidConnectionId) { From e5c7703aa72339e2a36d5a4fbf977cc6f7040536 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Thu, 21 Oct 2021 20:15:39 -0700 Subject: [PATCH 125/152] Addressing review feedback Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- .../Assets/Passes/PostProcessParent.pass | 1 + .../NewDepthOfFieldComposite.azsl | 7 ++-- .../NewDepthOfFieldDownsample.azsl | 3 +- .../NewDepthOfFieldFilterLarge.azsl | 7 ++-- .../NewDepthOfFieldFilterSmall.azsl | 5 +-- .../NewDepthOfFieldTile3x3.azsl | 33 ++++++++++--------- .../NewDepthOfFieldTileReduce.azsl | 3 +- .../PostProcessing/NewDepthOfFieldPasses.h | 23 +++++++++++++ 8 files changed, 56 insertions(+), 26 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass index dc1c0f1664..67c5072513 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass @@ -112,6 +112,7 @@ } ] }, + // Todo: remove the old depth of field implementation and rename NewDepthOfField -> DepthOfField //{ // "Name": "DepthOfFieldPass", // "TemplateName": "DepthOfFieldTemplate", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl index b15095535d..468f93db78 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.azsl @@ -22,6 +22,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_halfResColorAndCoc; // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + // Auto-filled by the pass system when "ShaderImageDimensionsConstant" is specified in the .pass file float4 m_fullResDimensions; float4 m_halfResDimensions; @@ -69,15 +70,15 @@ PSOutput MainPS(VSOutput IN) // --- Weights based on pixel proximity --- - // Based on which pixel we're shading, we'll be closer/further to half res pixels - // Here are the pre-caculated weights, arranged to match the Gather pattern + // Based on which pixel we're shading, we'll be closer/farther to half res pixels + // Here are the pre-calculated weights, arranged to match the Gather pattern // // W Z // X Y // // Note: These weights come down to the same contributions as if we did a linear sample int2 pixel = int2(fullResPixelPos); - float4 weights = (pixel.x & 1) + float4 weights = (pixel.x & 1) ? ( (pixel.y & 1) ? float4(0.1875f, 0.0625f, 0.1875f, 0.5625f) : float4(0.5625f, 0.1875f, 0.0625f, 0.1875f) ) : ( (pixel.y & 1) ? float4(0.0625f, 0.1875f, 0.5625f, 0.1875f) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl index d9972b8400..01ff4b9493 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.azsl @@ -22,6 +22,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_depth; // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + // Auto-filled by the pass system when "ShaderImageDimensionsConstant" is specified in the .pass file float4 m_inputDimensions; float4 m_outputDimensions; @@ -61,7 +62,7 @@ PSOutput MainPS(VSOutput IN) // Clamp CoC cocGather = clamp(cocGather, -1.0f, 1.0f); - // Weigh samles by CoC to avoid in foces pixels bleeding into bokeh effect + // Weight samples by CoC to avoid in focus pixels bleeding into bokeh effect float4 weights = abs(cocGather) + COC_EPSILON; weights = weights / (weights.x + weights.y + weights.z + weights.w); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl index ff955493d6..3b979b9c39 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.azsl @@ -19,6 +19,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_minMaxCocTile; // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + // Auto-filled by the pass system when "ShaderImageDimensionsConstant" is specified in the .pass file float4 m_textureDimensions; NewDepthOfFieldConstants m_dofConstants; @@ -51,7 +52,7 @@ float3 GetOffset(uint index, float2 offsetUVMultiplier) return offset; } -float CaclulateWeight(float offsetRadius, float samplingRadius, float sampleCoc, float centerCoc) +float CalculateWeight(float offsetRadius, float samplingRadius, float sampleCoc, float centerCoc) { // The maximum distance for which samples are valid is the min of the sample CoC and the center CoC float maxRadius = abs(min(sampleCoc, centerCoc)); @@ -109,7 +110,7 @@ PSOutput MainPS(VSOutput IN) float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::LinearSampler, pixelUV + offset.xy).rgba; // Calculate weight for sample - float weight = CaclulateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); + float weight = CalculateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); // Accumulate color += weight * sampleColorCoc; @@ -135,7 +136,7 @@ PSOutput MainPS(VSOutput IN) sampleColorCoc.rgb /= abs(sampleColorCoc.a); // Calculate weight for sample - float weight = CaclulateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); + float weight = CalculateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); sampleColorCoc.rgb *= weight; bool isBackground = (sampleColorCoc.a < backgroundMin); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl index 04bc095a93..38762254a8 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.azsl @@ -19,6 +19,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_minMaxCocTile; // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + // Auto-filled by the pass system when "ShaderImageDimensionsConstant" is specified in the .pass file float4 m_textureDimensions; NewDepthOfFieldConstants m_dofConstants; @@ -51,7 +52,7 @@ float3 GetOffset(uint index, float2 offsetUVMultiplier) return offset; } -float CaclulateWeight(float offsetRadius, float samplingRadius, float sampleCoc, float centerCoc) +float CalculateWeight(float offsetRadius, float samplingRadius, float sampleCoc, float centerCoc) { // The maximum distance for which samples are valid is the min of the sample CoC and the center CoC float maxRadius = abs(min(sampleCoc, centerCoc)); @@ -102,7 +103,7 @@ PSOutput MainPS(VSOutput IN) float4 sampleColorCoc = PassSrg::m_colorAndCoc.Sample(PassSrg::PointSampler, pixelUV + offset.xy).rgba; // Calculate weight - float weight = CaclulateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); + float weight = CalculateWeight(offset.z, cocRadius, sampleColorCoc.a, centerCoc); // Accumulate sample and weight color.rgb += sampleColorCoc.rgb * weight; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl index e7ce99aae9..6c56b0b79c 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.azsl @@ -15,6 +15,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_minMaxSource; // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + // Auto-filled by the pass system when "ShaderImageDimensionsConstant" is specified in the .pass file float4 m_textureDimensions; Sampler PointSampler @@ -38,28 +39,28 @@ PSOutput MainPS(VSOutput IN) { // We want the min/max in a 3x3 region. Start sampling up left. float2 startPixelPos = IN.m_position.xy - float2(1, 1); - + float2 pixelSizeInUV = PassSrg::m_textureDimensions.zw; float2 startUV = startPixelPos * pixelSizeInUV; - + float cocMin = 1.0f; - float cocMax = -1.0f; - + float cocMax = -1.0f; + // Gather min/max in 3x3 region - [unroll] - for(float Y = 0.0f; Y < 3.0f; Y += 1.0f) + [unroll] + for(float Y = 0.0f; Y < 3.0f; Y += 1.0f) { - [unroll] - for(float X = 0.0f; X < 3.0f; X += 1.0f) - { - float2 sampleUV = mad(float2(X, Y), pixelSizeInUV, startUV); - float2 minMax = PassSrg::m_minMaxSource.SampleLevel(PassSrg::PointSampler, sampleUV, 0).xy; - - cocMin = min(cocMin, minMax.x); - cocMax = max(cocMax, minMax.y); - } + [unroll] + for(float X = 0.0f; X < 3.0f; X += 1.0f) + { + float2 sampleUV = mad(float2(X, Y), pixelSizeInUV, startUV); + float2 minMax = PassSrg::m_minMaxSource.SampleLevel(PassSrg::PointSampler, sampleUV, 0).xy; + + cocMin = min(cocMin, minMax.x); + cocMax = max(cocMax, minMax.y); + } } - + PSOutput output; output.m_color.x = cocMin; output.m_color.y = cocMax; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl index 740ebabaff..6d55648f22 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.azsl @@ -19,6 +19,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass RWTexture2D m_minMaxCoC; // Texture dimensions. XY channels are width and height and ZW channels are 1 / width and 1 / height + // Auto-filled by the pass system when "ShaderImageDimensionsConstant" is specified in the .pass file float4 m_inputDimensions; float4 m_outputDimensions; @@ -59,7 +60,7 @@ void MainCS(uint3 group_thread_id : SV_GroupThreadID, uint3 group_id : SV_GroupI // For atomic min/max to work with uints, floating point values should be positive // Map from [-1, 1] range to [0, 2] and cast as uint InterlockedMin( LDS_MIN_COC[group_thread_id.x], asuint(cocMin + 1) ); - InterlockedMax( LDS_MAX_COC[group_thread_id.x], asuint(cocMax + 1) ); + InterlockedMax( LDS_MAX_COC[group_thread_id.x], asuint(cocMax + 1) ); // Sync LDS GroupMemoryBarrierWithGroupSync(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h index d529623c0a..159f91e744 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/NewDepthOfFieldPasses.h @@ -17,6 +17,29 @@ namespace AZ { namespace Render { + // Technique + // + // 1. This Depth of Field technique starts by downsampling the lighting buffer and calculating + // the circle of confusion (CoC) for each downsampled pixel. + // + // 2. It then computes the min and max CoC for tiles of 16x16 pixels + // + // 3. It expands the min and max in a 3x3 region (twice, so 5x5 at the end) so that each tile + // tile pixel has the min and max CoCs of the 5x5 tile region around it + // + // 4. We perform a 48 tap scatter-as-gather blur around each pixel + // + // 5. We perform a follow up 8 tap scatter-as-gather blur to fill the holes from the first blur + // + // 6. We composite the blurred half resolution image onto the full resolution lighting buffer + // + // See http://advances.realtimerendering.com/s2013/Sousa_Graphics_Gems_CryENGINE3.pptx + // for a more detailed explanation. + // + // Notes: The name NewDepthOfField is in contrast to the previously implemented depth of field method + // That method will be removed in a follow up change and at that point NewDepthOfField will be renamed + // to simple DepthOfField. + //! Parent pass for the new depth of field technique //! Main updates the view srg via the depth of field settings //! And enables/disables all depth of field passes based on component activation From d394f6da3ca723cc83bb98bb0ac11a5accecebb7 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Fri, 22 Oct 2021 09:22:19 +0200 Subject: [PATCH 126/152] EMotion FX: AnimGraphNode::CalcSyncFactors crashes because of improper MCORE_INVALIDINDEX32 initialization (#4869) Signed-off-by: Benjamin Jillich --- .../EMotionFX/Source/AnimGraphMotionNode.cpp | 2 +- .../Code/EMotionFX/Source/AnimGraphNode.h | 6 +- .../Code/EMotionFX/Source/AnimGraphNodeData.h | 66 +++++++++---------- .../Source/AnimGraphStateTransition.cpp | 2 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp index c5e50ed9dd..a0a811eff0 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp @@ -597,7 +597,7 @@ namespace EMotionFX // reset several settings to rewind the motion instance motionInstance->ResetTimes(); motionInstance->SetIsFrozen(false); - SetSyncIndex(animGraphInstance, MCORE_INVALIDINDEX32); + SetSyncIndex(animGraphInstance, InvalidIndex); uniqueData->SetCurrentPlayTime(motionInstance->GetCurrentTime()); uniqueData->SetDuration(motionInstance->GetDuration()); uniqueData->SetPreSyncTime(uniqueData->GetCurrentPlayTime()); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphNode.h b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphNode.h index b515621c5b..95d9c0d77d 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphNode.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphNode.h @@ -213,12 +213,12 @@ namespace EMotionFX */ virtual void SkipOutput([[maybe_unused]] AnimGraphInstance* animGraphInstance) {} - MCORE_INLINE float GetDuration(AnimGraphInstance* animGraphInstance) const { return FindOrCreateUniqueNodeData(animGraphInstance)->GetDuration(); } + float GetDuration(AnimGraphInstance* animGraphInstance) const { return FindOrCreateUniqueNodeData(animGraphInstance)->GetDuration(); } virtual void SetCurrentPlayTime(AnimGraphInstance* animGraphInstance, float timeInSeconds) { FindOrCreateUniqueNodeData(animGraphInstance)->SetCurrentPlayTime(timeInSeconds); } virtual float GetCurrentPlayTime(AnimGraphInstance* animGraphInstance) const { return FindOrCreateUniqueNodeData(animGraphInstance)->GetCurrentPlayTime(); } - MCORE_INLINE size_t GetSyncIndex(AnimGraphInstance* animGraphInstance) const { return FindOrCreateUniqueNodeData(animGraphInstance)->GetSyncIndex(); } - MCORE_INLINE void SetSyncIndex(AnimGraphInstance* animGraphInstance, size_t syncIndex) { FindOrCreateUniqueNodeData(animGraphInstance)->SetSyncIndex(syncIndex); } + size_t GetSyncIndex(AnimGraphInstance* animGraphInstance) const { return FindOrCreateUniqueNodeData(animGraphInstance)->GetSyncIndex(); } + void SetSyncIndex(AnimGraphInstance* animGraphInstance, size_t syncIndex) { FindOrCreateUniqueNodeData(animGraphInstance)->SetSyncIndex(syncIndex); } virtual void SetPlaySpeed(AnimGraphInstance* animGraphInstance, float speedFactor) { FindOrCreateUniqueNodeData(animGraphInstance)->SetPlaySpeed(speedFactor); } virtual float GetPlaySpeed(AnimGraphInstance* animGraphInstance) const { return FindOrCreateUniqueNodeData(animGraphInstance)->GetPlaySpeed(); } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphNodeData.h b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphNodeData.h index 1ad82b1d55..5c2f0b281b 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphNodeData.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphNodeData.h @@ -51,52 +51,52 @@ namespace EMotionFX void Init(AnimGraphInstance* animGraphInstance, AnimGraphNode* node); void Init(AnimGraphNodeData* nodeData); - MCORE_INLINE AnimGraphNode* GetNode() const { return reinterpret_cast(m_object); } - MCORE_INLINE void SetNode(AnimGraphNode* node) { m_object = reinterpret_cast(node); } + AnimGraphNode* GetNode() const { return reinterpret_cast(m_object); } + void SetNode(AnimGraphNode* node) { m_object = reinterpret_cast(node); } - MCORE_INLINE void SetSyncIndex(size_t syncIndex) { m_syncIndex = syncIndex; } - MCORE_INLINE size_t GetSyncIndex() const { return m_syncIndex; } + void SetSyncIndex(size_t syncIndex) { m_syncIndex = syncIndex; } + size_t GetSyncIndex() const { return m_syncIndex; } - MCORE_INLINE void SetCurrentPlayTime(float absoluteTime) { m_currentTime = absoluteTime; } - MCORE_INLINE float GetCurrentPlayTime() const { return m_currentTime; } + void SetCurrentPlayTime(float absoluteTime) { m_currentTime = absoluteTime; } + float GetCurrentPlayTime() const { return m_currentTime; } - MCORE_INLINE void SetPlaySpeed(float speed) { m_playSpeed = speed; } - MCORE_INLINE float GetPlaySpeed() const { return m_playSpeed; } + void SetPlaySpeed(float speed) { m_playSpeed = speed; } + float GetPlaySpeed() const { return m_playSpeed; } - MCORE_INLINE void SetDuration(float durationInSeconds) { m_duration = durationInSeconds; } - MCORE_INLINE float GetDuration() const { return m_duration; } + void SetDuration(float durationInSeconds) { m_duration = durationInSeconds; } + float GetDuration() const { return m_duration; } - MCORE_INLINE void SetPreSyncTime(float timeInSeconds) { m_preSyncTime = timeInSeconds; } - MCORE_INLINE float GetPreSyncTime() const { return m_preSyncTime; } + void SetPreSyncTime(float timeInSeconds) { m_preSyncTime = timeInSeconds; } + float GetPreSyncTime() const { return m_preSyncTime; } - MCORE_INLINE void SetGlobalWeight(float weight) { m_globalWeight = weight; } - MCORE_INLINE float GetGlobalWeight() const { return m_globalWeight; } + void SetGlobalWeight(float weight) { m_globalWeight = weight; } + float GetGlobalWeight() const { return m_globalWeight; } - MCORE_INLINE void SetLocalWeight(float weight) { m_localWeight = weight; } - MCORE_INLINE float GetLocalWeight() const { return m_localWeight; } + void SetLocalWeight(float weight) { m_localWeight = weight; } + float GetLocalWeight() const { return m_localWeight; } - MCORE_INLINE uint8 GetInheritFlags() const { return m_inheritFlags; } + uint8 GetInheritFlags() const { return m_inheritFlags; } - MCORE_INLINE bool GetIsBackwardPlaying() const { return (m_inheritFlags & INHERITFLAGS_BACKWARD) != 0; } - MCORE_INLINE void SetBackwardFlag() { m_inheritFlags |= INHERITFLAGS_BACKWARD; } - MCORE_INLINE void ClearInheritFlags() { m_inheritFlags = 0; } + bool GetIsBackwardPlaying() const { return (m_inheritFlags & INHERITFLAGS_BACKWARD) != 0; } + void SetBackwardFlag() { m_inheritFlags |= INHERITFLAGS_BACKWARD; } + void ClearInheritFlags() { m_inheritFlags = 0; } - MCORE_INLINE uint8 GetPoseRefCount() const { return m_poseRefCount; } - MCORE_INLINE void IncreasePoseRefCount() { m_poseRefCount++; } - MCORE_INLINE void DecreasePoseRefCount() { m_poseRefCount--; } - MCORE_INLINE void SetPoseRefCount(uint8 refCount) { m_poseRefCount = refCount; } + uint8 GetPoseRefCount() const { return m_poseRefCount; } + void IncreasePoseRefCount() { m_poseRefCount++; } + void DecreasePoseRefCount() { m_poseRefCount--; } + void SetPoseRefCount(uint8 refCount) { m_poseRefCount = refCount; } - MCORE_INLINE uint8 GetRefDataRefCount() const { return m_refDataRefCount; } - MCORE_INLINE void IncreaseRefDataRefCount() { m_refDataRefCount++; } - MCORE_INLINE void DecreaseRefDataRefCount() { m_refDataRefCount--; } - MCORE_INLINE void SetRefDataRefCount(uint8 refCount) { m_refDataRefCount = refCount; } + uint8 GetRefDataRefCount() const { return m_refDataRefCount; } + void IncreaseRefDataRefCount() { m_refDataRefCount++; } + void DecreaseRefDataRefCount() { m_refDataRefCount--; } + void SetRefDataRefCount(uint8 refCount) { m_refDataRefCount = refCount; } - MCORE_INLINE void SetRefCountedData(AnimGraphRefCountedData* data) { m_refCountedData = data; } - MCORE_INLINE AnimGraphRefCountedData* GetRefCountedData() const { return m_refCountedData; } + void SetRefCountedData(AnimGraphRefCountedData* data) { m_refCountedData = data; } + AnimGraphRefCountedData* GetRefCountedData() const { return m_refCountedData; } - MCORE_INLINE const AnimGraphSyncTrack* GetSyncTrack() const { return m_syncTrack; } - MCORE_INLINE AnimGraphSyncTrack* GetSyncTrack() { return m_syncTrack; } - MCORE_INLINE void SetSyncTrack(AnimGraphSyncTrack* syncTrack) { m_syncTrack = syncTrack; } + const AnimGraphSyncTrack* GetSyncTrack() const { return m_syncTrack; } + AnimGraphSyncTrack* GetSyncTrack() { return m_syncTrack; } + void SetSyncTrack(AnimGraphSyncTrack* syncTrack) { m_syncTrack = syncTrack; } bool GetIsMirrorMotion() const { return m_isMirrorMotion; } void SetIsMirrorMotion(bool newValue) { m_isMirrorMotion = newValue; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateTransition.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateTransition.cpp index e90491b696..c6d9b4eeec 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateTransition.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateTransition.cpp @@ -326,7 +326,7 @@ namespace EMotionFX uniqueData->m_totalSeconds = 0.0f; uniqueData->m_blendProgress = 0.0f; - m_targetNode->SetSyncIndex(animGraphInstance, MCORE_INVALIDINDEX32); + m_targetNode->SetSyncIndex(animGraphInstance, InvalidIndex); // Trigger action for (AnimGraphTriggerAction* action : m_actionSetup.GetActions()) From 618da447a6118e5101daaeba64fee7c95abe1beb Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Fri, 22 Oct 2021 09:22:28 +0200 Subject: [PATCH 127/152] Added option to change the line color for plots and bar color for histograms in the histogram container (#4859) Signed-off-by: Benjamin Jillich --- Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h | 5 +++++ Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h b/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h index 6129c3ec08..e31d5743c3 100644 --- a/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h +++ b/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h @@ -85,6 +85,10 @@ namespace ImGui //! Calculate the min and maximum values for the present samples. void CalcMinMaxValues(float& outMin, float& outMax); + //! Set/get color used by either the lines in case ViewType is Lines or bars in case or Histogram. + void SetBarLineColor(const ImColor& color) { m_barLineColor = color; } + ImColor GetBarLineColor() const { return m_barLineColor; } + private: // Set the Max Size and clear the container void SetMaxSize(int size); @@ -99,6 +103,7 @@ namespace ImGui bool m_dispalyOverlays; ScaleMode m_scaleMode; //! Determines if the vertical range of the histogram will be manually specified, auto-expanded or automatically scaled based on the samples. float m_autoScaleSpeed = 0.05f; //! Indicates how fast the min max values and the visible vertical range are adapting to new samples. + ImColor m_barLineColor = ImColor(66, 166, 178); //! Color used by either the lines in case ViewType is Lines or bars in case or Histogram. bool m_collapsed; bool m_drawMostRecentValueText; }; diff --git a/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp b/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp index 1915cc3721..51bcc29b99 100644 --- a/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp +++ b/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp @@ -147,6 +147,8 @@ namespace ImGui float imGuiHistoWidgetHeight = m_collapsed ? histogramHeight : (histogramHeight - 15); if (GetSize() > 0) { + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, m_barLineColor.Value); + switch (m_viewType) { default: @@ -160,6 +162,8 @@ namespace ImGui ImGui::PlotLines(AZStd::string::format("##%s_lines", m_histogramName.c_str()).c_str(), ImGui::LYImGuiUtils::s_histogramContainerGetter, this, GetSize(), 0, m_histogramName.c_str(), m_minScale, m_maxScale, ImVec2(histogramWidth - 10, imGuiHistoWidgetHeight)); break; } + + ImGui::PopStyleColor(); } From 63140dc2478de4fe01cffb77f49002d19397d071 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 22 Oct 2021 05:52:50 -0500 Subject: [PATCH 128/152] Atom/galibzon/atom 4608/inline to root constant (#4897) * Changed references to "InlineConstant" to "RootConstant". Updated AZSLC to version 1.7.34 for mac, linux & windows Signed-off-by: garrieta --- .../Shader/Code/Source/Editor/AzslCompiler.cpp | 16 ++++++++-------- .../Editor/AzslShaderBuilderSystemComponent.cpp | 4 ++-- .../Code/Source/Editor/ShaderBuilderUtility.cpp | 2 +- .../Source/Editor/ShaderVariantAssetBuilder.cpp | 2 +- .../StandardMultilayerPBR_ForwardPass.shader | 8 ++++++++ .../StandardMultilayerPBR_ForwardPass_EDS.shader | 8 ++++++++ .../Features/RayTracing/RayTracingSrgs.shader | 2 +- .../Platform/Linux/BuiltInPackages_linux.cmake | 2 +- .../Platform/Mac/BuiltInPackages_mac.cmake | 3 +-- .../Windows/BuiltInPackages_windows.cmake | 2 +- 10 files changed, 32 insertions(+), 17 deletions(-) diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp index 79a39ff793..3edc7bbe67 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp @@ -404,18 +404,18 @@ namespace AZ bool AzslCompiler::ParseSrgPopulateRootConstantData(const rapidjson::Document& input, RootConstantData& rootConstantData) const { - if (input.HasMember("InlineConstantBuffer")) + if (input.HasMember("RootConstantBuffer")) { - const rapidjson::Value& rootConstantBufferValue = input["InlineConstantBuffer"]; - AZ_Assert(rootConstantBufferValue.IsObject(), "InlineConstantBuffer is not an object"); + const rapidjson::Value& rootConstantBufferValue = input["RootConstantBuffer"]; + AZ_Assert(rootConstantBufferValue.IsObject(), "RootConstantBuffer is not an object"); for (rapidjson::Value::ConstMemberIterator itr = rootConstantBufferValue.MemberBegin(); itr != rootConstantBufferValue.MemberEnd(); ++itr) { AZStd::string_view rootConstantBufferMemberName = itr->name.GetString(); const rapidjson::Value& rootConstantBufferMemberValue = itr->value; - if (rootConstantBufferMemberName == "bufferForInlineConstants") + if (rootConstantBufferMemberName == "bufferForRootConstants") { - AZ_Assert(rootConstantBufferMemberValue.IsObject(), "bufferForInlineConstants is not an object"); + AZ_Assert(rootConstantBufferMemberValue.IsObject(), "bufferForRootConstants is not an object"); for (rapidjson::Value::ConstMemberIterator itr2 = rootConstantBufferMemberValue.MemberBegin(); itr2 != rootConstantBufferMemberValue.MemberEnd(); ++itr2) { @@ -442,14 +442,14 @@ namespace AZ } } } - else if (rootConstantBufferMemberName == "inputsForInlineConstants") + else if (rootConstantBufferMemberName == "inputsForRootConstants") { - AZ_Assert(rootConstantBufferMemberValue.IsArray(), "inputsForInlineConstants is not an array"); + AZ_Assert(rootConstantBufferMemberValue.IsArray(), "inputsForRootConstants is not an array"); for (rapidjson::Value::ConstValueIterator itr2 = rootConstantBufferMemberValue.Begin(); itr2 != rootConstantBufferMemberValue.End(); ++itr2) { const rapidjson::Value& rootConstantBufferValue2 = *itr2; - AZ_Assert(rootConstantBufferValue2.IsObject(), "Entry in inputsForInlineConstants is not an object"); + AZ_Assert(rootConstantBufferValue2.IsObject(), "Entry in inputsForRootConstants is not an object"); SrgConstantData rootConstantInputs; diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp index cacb310918..ffc5f40ef4 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp @@ -81,7 +81,7 @@ namespace AZ // Register Shader Asset Builder AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilderDescriptor; shaderAssetBuilderDescriptor.m_name = "Shader Asset Builder"; - shaderAssetBuilderDescriptor.m_version = 104; // ATOM-15871 + shaderAssetBuilderDescriptor.m_version = 105; // [AZSL] Changing inlineConstant to rootConstant keyword work. // .shader file changes trigger rebuilds shaderAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern( AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); shaderAssetBuilderDescriptor.m_busId = azrtti_typeid(); @@ -96,7 +96,7 @@ namespace AZ shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder"; // Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update // ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder". - shaderVariantAssetBuilderDescriptor.m_version = 25; // ATOM-15871 + shaderVariantAssetBuilderDescriptor.m_version = 26; // [AZSL] Changing inlineConstant to rootConstant keyword work. shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid(); shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp index e9ac18a432..2c87a79068 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp @@ -183,7 +183,7 @@ namespace AZ // access the root constants reflection if (!azslc.ParseSrgPopulateRootConstantData( outcomes[AzslSubProducts::srg].GetValue(), - rootConstantData)) // consuming data from --srg ("InlineConstantBuffer" subjson section) + rootConstantData)) // consuming data from --srg ("RootConstantBuffer" subjson section) { AZ_Error(builderName, false, "Failed to obtain root constant data reflection"); return AssetBuilderSDK::ProcessJobResult_Failed; diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp index 27ae90dcdc..bb40baca7d 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp @@ -561,7 +561,7 @@ namespace AZ // Access the root constants reflection if (!azslCompiler.ParseSrgPopulateRootConstantData( jsonOutcome.GetValue(), - rootConstantData)) // consuming data from --srg ("InlineConstantBuffer" subjson section) + rootConstantData)) // consuming data from --srg ("RootConstantBuffer" subjson section) { AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to obtain root constant data reflection"); return false; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.shader b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.shader index 28322d68ed..00efca6056 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.shader +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.shader @@ -48,6 +48,14 @@ } ] }, + + "Supervariants": + [ + { + "Name": "", + "PlusArguments": "--no-alignment-validation" + } + ], "DrawList" : "forward" } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass_EDS.shader b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass_EDS.shader index 42366d6067..983245ffb0 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass_EDS.shader +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass_EDS.shader @@ -49,5 +49,13 @@ ] }, + "Supervariants": + [ + { + "Name": "", + "PlusArguments": "--no-alignment-validation" + } + ], + "DrawList" : "forward" } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSrgs.shader b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSrgs.shader index 5512b1ad4d..475a1b1b54 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSrgs.shader +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSrgs.shader @@ -37,7 +37,7 @@ [ { "Name": "", - "PlusArguments": "", + "PlusArguments": "--no-alignment-validation", "MinusArguments": "--strip-unused-srgs" } ] diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index d6fdc5cd9b..6210b9cc18 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -39,7 +39,7 @@ ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-linux TARGETS OpenSSL PACKAGE_HASH b779426d1e9c5ddf71160d5ae2e639c3b956e0fb5e9fcaf9ce97c4526024e3bc) ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev3-linux TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 88c4a359325d749bc34090b9ac466424847f3b71ba0de15045cf355c17c07099) ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-linux TARGETS SPIRVCross PACKAGE_HASH 7889ee5460a688e9b910c0168b31445c0079d363affa07b25d4c8aeb608a0b80) -ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev2-linux TARGETS azslc PACKAGE_HASH 1ba84d8321a566d35a1e9aa7400211ba8e6d1c11c08e4be3c93e6e74b8f7aef1) +ly_associate_package(PACKAGE_NAME azslc-1.7.34-rev1-linux TARGETS azslc PACKAGE_HASH 6d7dc671936c34ff70d2632196107ca1b8b2b41acdd021bfbc69a9fd56215c22) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-linux TARGETS ZLIB PACKAGE_HASH 9be5ea85722fc27a8645a9c8a812669d107c68e6baa2ca0740872eaeb6a8b0fc) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-linux TARGETS squish-ccr PACKAGE_HASH 85fecafbddc6a41a27c5f59ed4a5dfb123a94cb4666782cf26e63c0a4724c530) ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-linux TARGETS astc-encoder PACKAGE_HASH 2ba97a06474d609945f0ab4419af1f6bbffdd294ca6b869f5fcebec75c573c0f) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 41df718b71..d054ba22e1 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -17,7 +17,6 @@ ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev1-multiplatform TARGETS azslc PACKAGE_HASH 664439954bad54cc43731c684adbc1249d971ad7379fcd83ca8bba5e1cc4a2d0) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) @@ -43,4 +42,4 @@ ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-mac ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-mac TARGETS astc-encoder PACKAGE_HASH 96f6ea8c3e45ec7fe525230c7c53ca665c8300d8e28456cc19bb3159ce6f8dcc) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-mac TARGETS ISPCTexComp PACKAGE_HASH 8a4e93277b8face6ea2fd57c6d017bdb55643ed3d6387110bc5f6b3b884dd169) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-mac TARGETS lz4 PACKAGE_HASH 891ff630bf34f7ab1d8eaee2ea0a8f1fca89dbdc63fca41ee592703dd488a73b) - +ly_associate_package(PACKAGE_NAME azslc-1.7.34-rev1-mac TARGETS azslc PACKAGE_HASH a9d81946b42ffa55c0d14d6a9249b3340e59a8fb8835e7a96c31df80f14723bc) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index cccd2591e8..fce2229771 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -17,7 +17,6 @@ ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev1-multiplatform TARGETS azslc PACKAGE_HASH 664439954bad54cc43731c684adbc1249d971ad7379fcd83ca8bba5e1cc4a2d0) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) @@ -50,3 +49,4 @@ ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-windows ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-windows TARGETS astc-encoder PACKAGE_HASH 3addc6fc1a7eb0d6b7f3d530e962af967e6d92b3825ef485da243346357cf78e) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-windows TARGETS ISPCTexComp PACKAGE_HASH b6fa6ea28a2808a9a5524c72c37789c525925e435770f2d94eb2d387360fa2d0) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-windows TARGETS lz4 PACKAGE_HASH 4ea457b833cd8cfaf8e8e06ed6df601d3e6783b606bdbc44a677f77e19e0db16) +ly_associate_package(PACKAGE_NAME azslc-1.7.34-rev1-windows TARGETS azslc PACKAGE_HASH 44eb2e0fc4b0f1c75d0fb6f24c93a5753655b84dbc3e6ad45389ed3b9cf7a4b0) From 54b9ed27375f9efc2b349e4b73a5a7c12f034747 Mon Sep 17 00:00:00 2001 From: AMZN-nggieber <52797929+AMZN-nggieber@users.noreply.github.com> Date: Fri, 22 Oct 2021 04:11:32 -0700 Subject: [PATCH 129/152] Added Menu to Gem Catalog with Action to Navigate to Gem Repo Screen (#4829) * Added menu to Gem Catalog that with option to navigate to Gem Repo screen Signed-off-by: nggieber * Changed Goto to GoTo and added a tr Signed-off-by: nggieber * Gem repo button works from new project creation workflow as well and users are warned if they have pending changes in the gem catalog before changing screens. Signed-off-by: nggieber --- .../Resources/ProjectManager.qss | 12 ++++++ .../Source/CreateProjectCtrl.cpp | 4 +- .../Source/EngineScreenCtrl.cpp | 38 +++++++++++++++---- .../ProjectManager/Source/EngineScreenCtrl.h | 5 +++ .../GemCatalog/GemCatalogHeaderWidget.cpp | 26 ++++++++++++- .../GemCatalog/GemCatalogHeaderWidget.h | 9 ++++- .../Source/GemCatalog/GemCatalogScreen.cpp | 23 +++++++++++ .../Source/GemCatalog/GemCatalogScreen.h | 4 ++ .../ProjectManager/Source/ScreenWidget.h | 10 ++++- .../ProjectManager/Source/ScreensCtrl.cpp | 28 ++++++++++++-- .../Tools/ProjectManager/Source/ScreensCtrl.h | 2 +- .../Source/UpdateProjectCtrl.cpp | 6 ++- 12 files changed, 151 insertions(+), 16 deletions(-) diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 507cf38726..298d2a749a 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -218,6 +218,10 @@ QTabBar::tab:focus { color: #666666; } +#verticalSeparatingLine { + color: #888888; +} + /************** Project Settings **************/ #projectSettings { margin-top:42px; @@ -481,6 +485,14 @@ QProgressBar::chunk { font-weight: 600; } +#gemCatalogMenuButton { + qproperty-flat: true; + max-width:36px; + min-width:36px; + max-height:24px; + min-height:24px; +} + #GemCatalogCartOverlayGemDownloadHeader { margin:0; padding: 0px; diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 84b931cb30..65e01803aa 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -49,6 +49,8 @@ namespace O3DE::ProjectManager m_stack->addWidget(m_gemCatalogScreen); vLayout->addWidget(m_stack); + connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); + // When there are multiple project templates present, we re-gather the gems when changing the selected the project template. connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex) { @@ -133,7 +135,7 @@ namespace O3DE::ProjectManager } else { - emit GotoPreviousScreenRequest(); + emit GoToPreviousScreenRequest(); } } diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp index c78a9426db..f30a8e0daa 100644 --- a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp @@ -29,17 +29,17 @@ namespace O3DE::ProjectManager topBarFrameWidget->setLayout(topBarHLayout); - QTabWidget* tabWidget = new QTabWidget(); - tabWidget->setObjectName("engineTab"); - tabWidget->tabBar()->setObjectName("engineTabBar"); - tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus); + m_tabWidget = new QTabWidget(); + m_tabWidget->setObjectName("engineTab"); + m_tabWidget->tabBar()->setObjectName("engineTabBar"); + m_tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus); m_engineSettingsScreen = new EngineSettingsScreen(); m_gemRepoScreen = new GemRepoScreen(); - tabWidget->addTab(m_engineSettingsScreen, tr("General")); - tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories")); - topBarHLayout->addWidget(tabWidget); + m_tabWidget->addTab(m_engineSettingsScreen, tr("General")); + m_tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories")); + topBarHLayout->addWidget(m_tabWidget); vLayout->addWidget(topBarFrameWidget); @@ -61,4 +61,28 @@ namespace O3DE::ProjectManager return true; } + bool EngineScreenCtrl::ContainsScreen(ProjectManagerScreen screen) + { + if (screen == m_engineSettingsScreen->GetScreenEnum() || screen == m_gemRepoScreen->GetScreenEnum()) + { + return true; + } + + return false; + } + + void EngineScreenCtrl::GoToScreen(ProjectManagerScreen screen) + { + if (screen == m_engineSettingsScreen->GetScreenEnum()) + { + m_tabWidget->setCurrentWidget(m_engineSettingsScreen); + m_engineSettingsScreen->NotifyCurrentScreen(); + } + else if (screen == m_gemRepoScreen->GetScreenEnum()) + { + m_tabWidget->setCurrentWidget(m_gemRepoScreen); + m_gemRepoScreen->NotifyCurrentScreen(); + } + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h index 9e799f13e7..b7142ba226 100644 --- a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h @@ -11,6 +11,8 @@ #include #endif +QT_FORWARD_DECLARE_CLASS(QTabWidget) + namespace O3DE::ProjectManager { QT_FORWARD_DECLARE_CLASS(EngineSettingsScreen) @@ -26,7 +28,10 @@ namespace O3DE::ProjectManager QString GetTabText() override; bool IsTab() override; + bool ContainsScreen(ProjectManagerScreen screen) override; + void GoToScreen(ProjectManagerScreen screen) override; + QTabWidget* m_tabWidget = nullptr; EngineSettingsScreen* m_engineSettingsScreen = nullptr; GemRepoScreen* m_gemRepoScreen = nullptr; }; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 79ff7624b7..0ecea215bf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -8,12 +8,14 @@ #include #include +#include + #include #include #include #include +#include #include -#include namespace O3DE::ProjectManager { @@ -404,6 +406,28 @@ namespace O3DE::ProjectManager CartButton* cartButton = new CartButton(gemModel, downloadController); hLayout->addWidget(cartButton); + + hLayout->addSpacing(16); + + // Separating line + QFrame* vLine = new QFrame(); + vLine->setFrameShape(QFrame::VLine); + vLine->setObjectName("verticalSeparatingLine"); + hLayout->addWidget(vLine); + + hLayout->addSpacing(16); + + QMenu* gemMenu = new QMenu(this); + m_openGemReposAction = gemMenu->addAction(tr("Show Gem Repos")); + + connect(m_openGemReposAction, &QAction::triggered, this,[this](){ emit OpenGemsRepo(); }); + + QPushButton* gemMenuButton = new QPushButton(this); + gemMenuButton->setObjectName("gemCatalogMenuButton"); + gemMenuButton->setMenu(gemMenu); + gemMenuButton->setIcon(QIcon(":/menu.svg")); + gemMenuButton->setIconSize(QSize(36, 24)); + hLayout->addWidget(gemMenuButton); } void GemCatalogHeaderWidget::ReinitForProject() diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 8e0eaa13ba..fa381e54ae 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -15,13 +15,15 @@ #include #include #include +#include + #include #include #include #include #include #include -#include +#include #endif namespace O3DE::ProjectManager @@ -84,8 +86,13 @@ namespace O3DE::ProjectManager void ReinitForProject(); + signals: + void OpenGemsRepo(); + private: AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr; inline constexpr static int s_height = 60; + + QAction* m_openGemReposAction = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 58f3b48c81..cbe36400cf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -38,6 +38,8 @@ namespace O3DE::ProjectManager m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController); vLayout->addWidget(m_headerWidget); + connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo); + QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); vLayout->addLayout(hLayout); @@ -194,6 +196,27 @@ namespace O3DE::ProjectManager return EnableDisableGemsResult::Success; } + void GemCatalogScreen::HandleOpenGemRepo() + { + QVector gemsToBeAdded = m_gemModel->GatherGemsToBeAdded(true); + QVector gemsToBeRemoved = m_gemModel->GatherGemsToBeRemoved(true); + + if (!gemsToBeAdded.empty() || !gemsToBeRemoved.empty()) + { + QMessageBox::StandardButton warningResult = QMessageBox::warning( + nullptr, "Pending Changes", + "There are some unsaved changes to the gem selection,
they will be lost if you change screens.
Are you sure?", + QMessageBox::No | QMessageBox::Yes); + + if (warningResult != QMessageBox::Yes) + { + return; + } + } + + emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); + } + ProjectManagerScreen GemCatalogScreen::GetScreenEnum() { return ProjectManagerScreen::GemCatalog; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 361e34b214..456a5fe91c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -41,7 +41,11 @@ namespace O3DE::ProjectManager GemModel* GetGemModel() const { return m_gemModel; } DownloadController* GetDownloadController() const { return m_downloadController; } + private slots: + void HandleOpenGemRepo(); + private: + void FillModel(const QString& projectPath); GemListView* m_gemListView = nullptr; diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h index 9563fc2f6a..148dcdb8c8 100644 --- a/Code/Tools/ProjectManager/Source/ScreenWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h @@ -47,6 +47,14 @@ namespace O3DE::ProjectManager return tr("Missing"); } + virtual bool ContainsScreen([[maybe_unused]] ProjectManagerScreen screen) + { + return false; + } + virtual void GoToScreen([[maybe_unused]] ProjectManagerScreen screen) + { + } + //! Notify this screen it is the current screen virtual void NotifyCurrentScreen() { @@ -55,7 +63,7 @@ namespace O3DE::ProjectManager signals: void ChangeScreenRequest(ProjectManagerScreen screen); - void GotoPreviousScreenRequest(); + void GoToPreviousScreenRequest(); void ResetScreenRequest(ProjectManagerScreen screen); void NotifyCurrentProject(const QString& projectPath); void NotifyBuildProject(const ProjectInfo& projectInfo); diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index df0bdb29f4..314765def0 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -83,11 +83,28 @@ namespace O3DE::ProjectManager bool ScreensCtrl::ForceChangeToScreen(ProjectManagerScreen screen, bool addVisit) { + ScreenWidget* newScreen = nullptr; + const auto iterator = m_screenMap.find(screen); if (iterator != m_screenMap.end()) + { + newScreen = iterator.value(); + } + else + { + // Check if screen is contained by another screen + for (ScreenWidget* checkingScreen : m_screenMap) + { + if (checkingScreen->ContainsScreen(screen)) + { + newScreen = checkingScreen; + break; + } + } + } + if (newScreen) { ScreenWidget* currentScreen = GetCurrentScreen(); - ScreenWidget* newScreen = iterator.value(); if (currentScreen != newScreen) { @@ -109,6 +126,11 @@ namespace O3DE::ProjectManager newScreen->NotifyCurrentScreen(); + if (iterator == m_screenMap.end()) + { + newScreen->GoToScreen(screen); + } + return true; } } @@ -116,7 +138,7 @@ namespace O3DE::ProjectManager return false; } - bool ScreensCtrl::GotoPreviousScreen() + bool ScreensCtrl::GoToPreviousScreen() { if (!m_screenVisitOrder.isEmpty()) { @@ -171,7 +193,7 @@ namespace O3DE::ProjectManager m_screenMap.insert(screen, newScreen); connect(newScreen, &ScreenWidget::ChangeScreenRequest, this, &ScreensCtrl::ChangeToScreen); - connect(newScreen, &ScreenWidget::GotoPreviousScreenRequest, this, &ScreensCtrl::GotoPreviousScreen); + connect(newScreen, &ScreenWidget::GoToPreviousScreenRequest, this, &ScreensCtrl::GoToPreviousScreen); connect(newScreen, &ScreenWidget::ResetScreenRequest, this, &ScreensCtrl::ResetScreen); connect(newScreen, &ScreenWidget::NotifyCurrentProject, this, &ScreensCtrl::NotifyCurrentProject); connect(newScreen, &ScreenWidget::NotifyBuildProject, this, &ScreensCtrl::NotifyBuildProject); diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.h b/Code/Tools/ProjectManager/Source/ScreensCtrl.h index 7132d64dd0..ab69a09ea3 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.h +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.h @@ -41,7 +41,7 @@ namespace O3DE::ProjectManager public slots: bool ChangeToScreen(ProjectManagerScreen screen); bool ForceChangeToScreen(ProjectManagerScreen screen, bool addVisit = true); - bool GotoPreviousScreen(); + bool GoToPreviousScreen(); void ResetScreen(ProjectManagerScreen screen); void ResetAllScreens(); void DeleteScreen(ProjectManagerScreen screen); diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index fd2ebf340f..1c8f9a6931 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -40,6 +40,10 @@ namespace O3DE::ProjectManager m_updateSettingsScreen = new UpdateProjectSettingsScreen(); m_gemCatalogScreen = new GemCatalogScreen(); + connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, [this](ProjectManagerScreen screen){ + emit ChangeScreenRequest(screen); + }); + m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); @@ -118,7 +122,7 @@ namespace O3DE::ProjectManager { if (UpdateProjectSettings(true)) { - emit GotoPreviousScreenRequest(); + emit GoToPreviousScreenRequest(); } } } From d5431653aaf8f2c81f609f38638a8367f15a3aa8 Mon Sep 17 00:00:00 2001 From: Adi Bar-Lev Date: Fri, 22 Oct 2021 09:21:06 -0400 Subject: [PATCH 130/152] Hair and Tools Pipeline bug fixes (#4902) - Fixed fallback connections for hair pipeline to allow disabling the parent pass hierarchy when not required - Renamed the Thumbnail pipeline to be used as generic minimal tools pipeline - Reused the Tools pipeline for the preview renderer - minimal FPs and passes Remark: - The tools pipeline should have folloup submits for reducing passes to minimal required render passes Signed-off-by: Adi-Amazon Co-authored-by: Adi-Amazon --- .../Assets/Passes/PassTemplates.azasset | 8 +++---- ...mbnailPipeline.pass => ToolsPipeline.pass} | 2 +- ...pass => ToolsPipelineRenderToTexture.pass} | 4 ++-- .../atom_feature_common_asset_files.cmake | 4 ++-- .../PreviewRenderer/PreviewRenderer.cpp | 2 +- .../Assets/Passes/HairParentPass.pass | 6 +++++ .../Assets/Passes/HairParentShortCutPass.pass | 17 +++++++++---- .../Code/Rendering/HairFeatureProcessor.cpp | 24 ++++--------------- 8 files changed, 33 insertions(+), 34 deletions(-) rename Gems/Atom/Feature/Common/Assets/Passes/{ThumbnailPipeline.pass => ToolsPipeline.pass} (99%) rename Gems/Atom/Feature/Common/Assets/Passes/{ThumbnailPipelineRenderToTexture.pass => ToolsPipelineRenderToTexture.pass} (88%) diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset index 7770b326a6..81b7d36484 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset +++ b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset @@ -469,12 +469,12 @@ "Path": "Passes/OpaqueParent.pass" }, { - "Name": "ThumbnailPipeline", - "Path": "Passes/ThumbnailPipeline.pass" + "Name": "ToolsPipeline", + "Path": "Passes/ToolsPipeline.pass" }, { - "Name": "ThumbnailPipelineRenderToTexture", - "Path": "Passes/ThumbnailPipelineRenderToTexture.pass" + "Name": "ToolsPipelineRenderToTexture", + "Path": "Passes/ToolsPipelineRenderToTexture.pass" }, { "Name": "TransparentParentTemplate", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ThumbnailPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/ToolsPipeline.pass similarity index 99% rename from Gems/Atom/Feature/Common/Assets/Passes/ThumbnailPipeline.pass rename to Gems/Atom/Feature/Common/Assets/Passes/ToolsPipeline.pass index 932b6ac435..51c169348b 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/ThumbnailPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/ToolsPipeline.pass @@ -4,7 +4,7 @@ "ClassName": "PassAsset", "ClassData": { "PassTemplate": { - "Name": "ThumbnailPipeline", + "Name": "ToolsPipeline", "PassClass": "ParentPass", "Slots": [ { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ThumbnailPipelineRenderToTexture.pass b/Gems/Atom/Feature/Common/Assets/Passes/ToolsPipelineRenderToTexture.pass similarity index 88% rename from Gems/Atom/Feature/Common/Assets/Passes/ThumbnailPipelineRenderToTexture.pass rename to Gems/Atom/Feature/Common/Assets/Passes/ToolsPipelineRenderToTexture.pass index 11e2cb717a..b98ec46d0e 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/ThumbnailPipelineRenderToTexture.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/ToolsPipelineRenderToTexture.pass @@ -4,7 +4,7 @@ "ClassName": "PassAsset", "ClassData": { "PassTemplate": { - "Name": "ThumbnailPipelineRenderToTexture", + "Name": "ToolsPipelineRenderToTexture", "PassClass": "RenderToTexturePass", "PassData": { "$type": "RenderToTexturePassData", @@ -15,7 +15,7 @@ "PassRequests": [ { "Name": "Pipeline", - "TemplateName": "ThumbnailPipeline", + "TemplateName": "ToolsPipeline", "Connections": [ { "LocalSlot": "SwapChainOutput", diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 123e5da7ba..e13465307a 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -161,8 +161,8 @@ set(FILES Passes/LutGeneration.pass Passes/MainPipeline.pass Passes/MainPipelineRenderToTexture.pass - Passes/ThumbnailPipeline.pass - Passes/ThumbnailPipelineRenderToTexture.pass + Passes/ToolsPipeline.pass + Passes/ToolsPipelineRenderToTexture.pass Passes/MeshMotionVector.pass Passes/ModulateTexture.pass Passes/MorphTarget.pass diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp index a0d2034082..2b39a87623 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp @@ -61,7 +61,7 @@ namespace AtomToolsFramework AZ::RPI::RenderPipelineDescriptor pipelineDesc; pipelineDesc.m_mainViewTagName = "MainCamera"; pipelineDesc.m_name = pipelineName; - pipelineDesc.m_rootPassTemplate = "MainPipelineRenderToTexture"; + pipelineDesc.m_rootPassTemplate = "ToolsPipelineRenderToTexture"; // We have to set the samples to 4 to match the pipeline passes' setting, otherwise it may lead to device lost issue // [GFX TODO] [ATOM-13551] Default value sand validation required to prevent pipeline crash and device lost diff --git a/Gems/AtomTressFX/Assets/Passes/HairParentPass.pass b/Gems/AtomTressFX/Assets/Passes/HairParentPass.pass index 6ae8b9526e..c8c90e24f7 100644 --- a/Gems/AtomTressFX/Assets/Passes/HairParentPass.pass +++ b/Gems/AtomTressFX/Assets/Passes/HairParentPass.pass @@ -71,6 +71,12 @@ } } ], + "FallbackConnections": [ + { + "Input": "DepthLinearInput", + "Output": "DepthLinear" + } + ], "PassRequests": [ { "Name": "HairGlobalShapeConstraintsComputePass", diff --git a/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass b/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass index 7322611e2a..83f9f0d432 100644 --- a/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass +++ b/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass @@ -12,7 +12,8 @@ "SlotType": "InputOutput", "ScopeAttachmentUsage": "RenderTarget" }, - { // used for copy from MSAA to regular RT + { + // used for copy from MSAA to regular RT "Name": "RenderTargetInputOnly", "SlotType": "Input", "ScopeAttachmentUsage": "Shader" @@ -29,7 +30,7 @@ // If DepthLinear is not availbale - connect to another viewport (non MSAA) image. { "Name": "DepthLinearInput", - "SlotType": "InputOutput" + "SlotType": "Input" }, { "Name": "DepthLinear", @@ -71,6 +72,12 @@ } } ], + "FallbackConnections": [ + { + "Input": "DepthLinearInput", + "Output": "DepthLinear" + } + ], "PassRequests": [ { "Name": "HairGlobalShapeConstraintsComputePass", @@ -257,7 +264,8 @@ "Attachment": "HairColorRenderTarget" } }, - { // The final render target - this is MSAA mode RT - would it be cheaper to + { + // The final render target - this is MSAA mode RT - would it be cheaper to // use non-MSAA and then copy? "LocalSlot": "RenderTargetInputOutput", "AttachmentRef": { @@ -340,7 +348,8 @@ "TemplateName": "HairShortCutResolveColorPassTemplate", "Enabled": true, "Connections": [ - { // The final render target - this is MSAA mode RT - would it be cheaper to + { + // The final render target - this is MSAA mode RT - would it be cheaper to // use non-MSAA and then copy? "LocalSlot": "RenderTargetInputOutput", "AttachmentRef": { diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index b7c6bbce13..a0be18f0e2 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -144,25 +144,11 @@ namespace AZ void HairFeatureProcessor::EnablePasses([[maybe_unused]] bool enable) { - return; - - // [To Do] - This part should be enabled (remove the return) to reduce overhead - // when Hair is disabled / doesn't exist in the scene. - // Currently it might break features such as fog that depend on the output and for some - // reason doesn't quite work for ShortCut. - // The current overhead is minimal (< 0.1 msec) and this Gem is disabled by default. -/* - if (!m_initialized) - { - return; - } - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName); if (desiredPass) { desiredPass->SetEnabled(enable); } -*/ } bool HairFeatureProcessor::RemoveHairRenderObject(Data::Instance renderObject) @@ -184,15 +170,13 @@ namespace AZ void HairFeatureProcessor::UpdateHairSkinning() { - // Copying CPU side m_SimCB content to the GPU buffer (matrices, wind parameters..) - - for (auto objIter = m_hairRenderObjects.begin(); objIter != m_hairRenderObjects.end(); ++objIter) + // Copying CPU side m_SimCB content to the GPU buffer (matrices, wind parameters..) + for (auto& hairRenderObject : m_hairRenderObjects) { - if (!objIter->get()->IsEnabled()) + if (hairRenderObject->IsEnabled()) { - return; + hairRenderObject->Update(); } - objIter->get()->Update(); } } From a2592e9ff8ecab11c0b11048d74ca68f018f7dbf Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Fri, 22 Oct 2021 09:43:52 -0400 Subject: [PATCH 131/152] Correction in set hierarchy entity logic. Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../NetworkHierarchyChildComponent.cpp | 26 +++++------ .../NetworkHierarchyRootComponent.cpp | 46 +++++++++---------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index 37125a88b9..59782b1f4d 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -148,25 +148,21 @@ namespace Multiplayer m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); } } - else + else if ((previousHierarchyRoot && m_rootEntity == previousHierarchyRoot) || !previousHierarchyRoot) { - if (m_rootEntity == previousHierarchyRoot || !previousHierarchyRoot) + m_rootEntity = nullptr; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) { - m_rootEntity = nullptr; - - if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) - { - NetworkHierarchyChildComponentController* controller = static_cast(GetController()); - controller->SetHierarchyRoot(InvalidNetEntityId); - } - - GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); - m_networkHierarchyLeaveEvent.Signal(); - - NotifyChildrenHierarchyDisbanded(); + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + controller->SetHierarchyRoot(InvalidNetEntityId); } - } + GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); + m_networkHierarchyLeaveEvent.Signal(); + + NotifyChildrenHierarchyDisbanded(); + } } void NetworkHierarchyChildComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 6858242cd4..76f4bddb1a 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -325,37 +325,37 @@ namespace Multiplayer { if (newHierarchyRoot) { - m_rootEntity = newHierarchyRoot; - - if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + if (m_rootEntity != newHierarchyRoot) { - NetworkHierarchyChildComponentController* controller = static_cast(GetController()); - - const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); - controller->SetHierarchyRoot(netRootId); - } - - GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent()->GetOwningConnectionId()); - } - else - { - if (previousHierarchyRoot && m_rootEntity == previousHierarchyRoot || !previousHierarchyRoot) - { - m_rootEntity = nullptr; + m_rootEntity = newHierarchyRoot; if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) { - NetworkHierarchyChildComponentController* controller = static_cast(GetController()); - - controller->SetHierarchyRoot(InvalidNetEntityId); + NetworkHierarchyRootComponentController* controller = static_cast(GetController()); + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); + controller->SetHierarchyRoot(netRootId); } - GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); - - // We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy. - RebuildHierarchy(); + GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent()->GetOwningConnectionId()); + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); } } + else if ((previousHierarchyRoot && m_rootEntity == previousHierarchyRoot) || !previousHierarchyRoot) + { + m_rootEntity = nullptr; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyRootComponentController* controller = static_cast(GetController()); + controller->SetHierarchyRoot(InvalidNetEntityId); + } + + GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId); + m_networkHierarchyLeaveEvent.Signal(); + + // We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy. + RebuildHierarchy(); + } } void NetworkHierarchyRootComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId) From 3d67be162ce584b7fe4a7d8a8a37032117b25831 Mon Sep 17 00:00:00 2001 From: John Jones-Steele <82226755+jjjoness@users.noreply.github.com> Date: Fri, 22 Oct 2021 16:19:36 +0100 Subject: [PATCH 132/152] Terrain Physics Heightfield support * New Heightfield Components Signed-off-by: John Jones-Steele * Misc PR fixes * Fixed linux build failure from bad #include * Renamed "Terrain Physics Collider" to "Terrain Physics Heightfield Collider" per physics team feedback * Fixed 1/5 -> 1/4 typo in a comment * Added missing member copies in HeightfieldShapeConfiguration Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Addressed PR feedback Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Changes from review Signed-off-by: John Jones-Steele * Remove tabs accidently added Signed-off-by: John Jones-Steele * Fixed overly complicated scaling math. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added comments to make it more obvious what's happening on CreateEnd / DestroyBegin. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Moved Heightfield CreatePxGeometryFromConfig into its own function Signed-off-by: John Jones-Steele Co-authored-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Physics/HeightfieldProviderBus.h | 97 ++ .../Mocks/MockHeightfieldProviderBus.h | 33 + .../Physics/ShapeConfiguration.cpp | 122 +++ .../AzFramework/Physics/ShapeConfiguration.h | 52 + .../AzFramework/Physics/SystemBus.h | 4 + .../AzFramework/AzFramework/Physics/Utils.cpp | 1 + .../Physics/physics_mock_files.cmake | 11 + .../AzFramework/azframework_files.cmake | 2 + Code/Framework/AzFramework/CMakeLists.txt | 3 +- Gems/Blast/Code/Tests/Mocks/BlastMocks.h | 1 + .../Code/Tests/Mocks/PhysicsSystem.h | 1 + Gems/GradientSignal/Code/CMakeLists.txt | 12 +- .../Ebuses/MockGradientRequestBus.h | 34 + .../Code/gradientsignal_mocks_files.cmake | 11 + .../Shape/AxisAlignedBoxShapeComponent.h | 6 - .../LmbrCentral/Shape/BoxShapeComponentBus.h | 6 + Gems/PhysX/Code/Editor/DebugDraw.cpp | 12 +- Gems/PhysX/Code/Editor/DebugDraw.h | 10 +- .../Code/Include/PhysX/SystemComponentBus.h | 13 +- .../Code/Source/ComponentDescriptors.cpp | 2 + .../Source/EditorComponentDescriptors.cpp | 2 + .../EditorHeightfieldColliderComponent.cpp | 341 +++++++ .../EditorHeightfieldColliderComponent.h | 104 ++ .../Source/HeightfieldColliderComponent.cpp | 365 +++++++ .../Source/HeightfieldColliderComponent.h | 104 ++ Gems/PhysX/Code/Source/SystemComponent.cpp | 24 + Gems/PhysX/Code/Source/SystemComponent.h | 2 + Gems/PhysX/Code/Source/Utils.cpp | 193 ++++ Gems/PhysX/Code/Source/Utils.h | 2 + Gems/PhysX/Code/physx_editor_files.cmake | 2 + Gems/PhysX/Code/physx_files.cmake | 2 + Gems/Terrain/Code/CMakeLists.txt | 1 + Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h | 42 +- .../MockTerrainAreaSurfaceRequestBus.h | 34 + .../Mocks/Terrain/MockTerrainLayerSpawner.h | 39 + .../TerrainHeightGradientListComponent.cpp | 2 +- .../TerrainLayerSpawnerComponent.cpp | 12 +- .../Components/TerrainLayerSpawnerComponent.h | 6 - .../TerrainPhysicsColliderComponent.cpp | 321 +++++++ .../TerrainPhysicsColliderComponent.h | 91 ++ .../EditorTerrainPhysicsColliderComponent.cpp | 24 + .../EditorTerrainPhysicsColliderComponent.h | 32 + .../Code/Source/EditorTerrainModule.cpp | 2 + Gems/Terrain/Code/Source/TerrainModule.cpp | 2 + Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp | 7 +- .../Tests/TerrainHeightGradientListTests.cpp | 146 +++ .../Tests/TerrainPhysicsColliderTests.cpp | 292 ++++++ .../Tests/TerrainSurfaceGradientListTests.cpp | 129 +++ Gems/Terrain/Code/Tests/TerrainSystemTest.cpp | 889 ++++++++++-------- .../Code/terrain_editor_shared_files.cmake | 2 + Gems/Terrain/Code/terrain_files.cmake | 2 + Gems/Terrain/Code/terrain_mocks_files.cmake | 2 + Gems/Terrain/Code/terrain_tests_files.cmake | 3 + 53 files changed, 3218 insertions(+), 436 deletions(-) create mode 100644 Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h create mode 100644 Code/Framework/AzFramework/AzFramework/Physics/Mocks/MockHeightfieldProviderBus.h create mode 100644 Code/Framework/AzFramework/AzFramework/Physics/physics_mock_files.cmake create mode 100644 Gems/GradientSignal/Code/Mocks/GradientSignal/Ebuses/MockGradientRequestBus.h create mode 100644 Gems/GradientSignal/Code/gradientsignal_mocks_files.cmake create mode 100644 Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp create mode 100644 Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.h create mode 100644 Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp create mode 100644 Gems/PhysX/Code/Source/HeightfieldColliderComponent.h create mode 100644 Gems/Terrain/Code/Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h create mode 100644 Gems/Terrain/Code/Mocks/Terrain/MockTerrainLayerSpawner.h create mode 100644 Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp create mode 100644 Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h create mode 100644 Gems/Terrain/Code/Source/EditorComponents/EditorTerrainPhysicsColliderComponent.cpp create mode 100644 Gems/Terrain/Code/Source/EditorComponents/EditorTerrainPhysicsColliderComponent.h create mode 100644 Gems/Terrain/Code/Tests/TerrainHeightGradientListTests.cpp create mode 100644 Gems/Terrain/Code/Tests/TerrainPhysicsColliderTests.cpp create mode 100644 Gems/Terrain/Code/Tests/TerrainSurfaceGradientListTests.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h new file mode 100644 index 0000000000..73523ee1ba --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Physics +{ + //! The QuadMeshType specifies the property of the heightfield quad. + enum class QuadMeshType : uint8_t + { + SubdivideUpperLeftToBottomRight, //!< Subdivide the quad, from upper left to bottom right |\|, into two triangles. + SubdivideBottomLeftToUpperRight, //!< Subdivide the quad, from bottom left to upper right |/|, into two triangles. + Hole //!< The quad should be treated as a hole in the heightfield. + }; + + struct HeightMaterialPoint + { + float m_height{ 0.0f }; //!< Holds the height of this point in the heightfield relative to the heightfield entity location. + QuadMeshType m_quadMeshType{ QuadMeshType::SubdivideUpperLeftToBottomRight }; //!< By default, create two triangles like this |\|, where this point is in the upper left corner. + uint8_t m_materialIndex{ 0 }; //!< The surface material index for the upper left corner of this quad. + uint16_t m_padding{ 0 }; //!< available for future use. + }; + + //! An interface to provide heightfield values. + class HeightfieldProviderRequests + : public AZ::ComponentBus + { + public: + //! Returns the distance between each height in the map. + //! @return Vector containing Column Spacing, Rows Spacing. + virtual AZ::Vector2 GetHeightfieldGridSpacing() const = 0; + + //! Returns the height field gridsize. + //! @param numColumns contains the size of the grid in the x direction. + //! @param numRows contains the size of the grid in the y direction. + virtual void GetHeightfieldGridSize(int32_t& numColumns, int32_t& numRows) const = 0; + + //! Returns the height field min and max height bounds. + //! @param minHeightBounds contains the minimum height that the heightfield can contain. + //! @param maxHeightBounds contains the maximum height that the heightfield can contain. + virtual void GetHeightfieldHeightBounds(float& minHeightBounds, float& maxHeightBounds) const = 0; + + //! Returns the AABB of the heightfield. + //! This is provided separately from the shape AABB because the heightfield might choose to modify the AABB bounds. + //! @return AABB of the heightfield. + virtual AZ::Aabb GetHeightfieldAabb() const = 0; + + //! Returns the world transform for the heightfield. + //! This is provided separately from the entity transform because the heightfield might want to clear out the rotation or scale. + //! @return world transform that should be used with the heightfield data. + virtual AZ::Transform GetHeightfieldTransform() const = 0; + + //! Returns the list of materials used by the height field. + //! @return returns a vector of all materials. + virtual AZStd::vector GetMaterialList() const = 0; + + //! Returns the list of heights used by the height field. + //! @return the rows*columns vector of the heights. + virtual AZStd::vector GetHeights() const = 0; + + //! Returns the list of heights and materials used by the height field. + //! @return the rows*columns vector of the heights and materials. + virtual AZStd::vector GetHeightsAndMaterials() const = 0; + }; + + using HeightfieldProviderRequestsBus = AZ::EBus; + + //! Broadcasts notifications when heightfield data changes - heightfield providers implement HeightfieldRequests bus. + class HeightfieldProviderNotifications + : public AZ::ComponentBus + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + + //! Called whenever the heightfield data changes. + //! @param the AABB of the area of data that changed. + virtual void OnHeightfieldDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion) + { + } + + protected: + ~HeightfieldProviderNotifications() = default; + }; + + using HeightfieldProviderNotificationBus = AZ::EBus; +} // namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Mocks/MockHeightfieldProviderBus.h b/Code/Framework/AzFramework/AzFramework/Physics/Mocks/MockHeightfieldProviderBus.h new file mode 100644 index 0000000000..221c52258d --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Physics/Mocks/MockHeightfieldProviderBus.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +namespace UnitTest +{ + class MockHeightfieldProviderNotificationBusListener + : private Physics::HeightfieldProviderNotificationBus::Handler + { + public: + MockHeightfieldProviderNotificationBusListener(AZ::EntityId entityid) + { + Physics::HeightfieldProviderNotificationBus::Handler::BusConnect(entityid); + } + + ~MockHeightfieldProviderNotificationBusListener() + { + Physics::HeightfieldProviderNotificationBus::Handler::BusDisconnect(); + } + + MOCK_METHOD1(OnHeightfieldDataChanged, void(const AZ::Aabb&)); + }; +} // namespace UnitTest diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp index e90c9d4eed..9bf00d9707 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp @@ -37,6 +37,7 @@ namespace Physics REFLECT_SHAPETYPE_ENUM_VALUE(Sphere); REFLECT_SHAPETYPE_ENUM_VALUE(Cylinder); REFLECT_SHAPETYPE_ENUM_VALUE(PhysicsAsset); + REFLECT_SHAPETYPE_ENUM_VALUE(Heightfield); #undef REFLECT_SHAPETYPE_ENUM_VALUE } @@ -305,4 +306,125 @@ namespace Physics m_cachedNativeMesh = nullptr; } } + + void HeightfieldShapeConfiguration::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext + ->RegisterGenericType>(); + + serializeContext->Class() + ->Version(1); + } + } + + HeightfieldShapeConfiguration::~HeightfieldShapeConfiguration() + { + SetCachedNativeHeightfield(nullptr); + } + + HeightfieldShapeConfiguration::HeightfieldShapeConfiguration(const HeightfieldShapeConfiguration& other) + : ShapeConfiguration(other) + , m_gridResolution(other.m_gridResolution) + , m_numColumns(other.m_numColumns) + , m_numRows(other.m_numRows) + , m_samples(other.m_samples) + , m_minHeightBounds(other.m_minHeightBounds) + , m_maxHeightBounds(other.m_maxHeightBounds) + , m_cachedNativeHeightfield(nullptr) + { + } + + HeightfieldShapeConfiguration& HeightfieldShapeConfiguration::operator=(const HeightfieldShapeConfiguration& other) + { + ShapeConfiguration::operator=(other); + + m_gridResolution = other.m_gridResolution; + m_numColumns = other.m_numColumns; + m_numRows = other.m_numRows; + m_samples = other.m_samples; + m_minHeightBounds = other.m_minHeightBounds; + m_maxHeightBounds = other.m_maxHeightBounds; + + // Prevent raw pointer from being copied + m_cachedNativeHeightfield = nullptr; + + return *this; + } + + void* HeightfieldShapeConfiguration::GetCachedNativeHeightfield() const + { + return m_cachedNativeHeightfield; + } + + void HeightfieldShapeConfiguration::SetCachedNativeHeightfield(void* cachedNativeHeightfield) const + { + if (m_cachedNativeHeightfield) + { + Physics::SystemRequestBus::Broadcast(&Physics::SystemRequests::ReleaseNativeHeightfieldObject, m_cachedNativeHeightfield); + } + + m_cachedNativeHeightfield = cachedNativeHeightfield; + } + + AZ::Vector2 HeightfieldShapeConfiguration::GetGridResolution() const + { + return m_gridResolution; + } + + void HeightfieldShapeConfiguration::SetGridResolution(const AZ::Vector2& gridResolution) + { + m_gridResolution = gridResolution; + } + + int32_t HeightfieldShapeConfiguration::GetNumColumns() const + { + return m_numColumns; + } + + void HeightfieldShapeConfiguration::SetNumColumns(int32_t numColumns) + { + m_numColumns = numColumns; + } + + int32_t HeightfieldShapeConfiguration::GetNumRows() const + { + return m_numRows; + } + + void HeightfieldShapeConfiguration::SetNumRows(int32_t numRows) + { + m_numRows = numRows; + } + + const AZStd::vector& HeightfieldShapeConfiguration::GetSamples() const + { + return m_samples; + } + + void HeightfieldShapeConfiguration::SetSamples(const AZStd::vector& samples) + { + m_samples = samples; + } + + float HeightfieldShapeConfiguration::GetMinHeightBounds() const + { + return m_minHeightBounds; + } + + void HeightfieldShapeConfiguration::SetMinHeightBounds(float minBounds) + { + m_minHeightBounds = minBounds; + } + + float HeightfieldShapeConfiguration::GetMaxHeightBounds() const + { + return m_maxHeightBounds; + } + + void HeightfieldShapeConfiguration::SetMaxHeightBounds(float maxBounds) + { + m_maxHeightBounds = maxBounds; + } } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h index b40a8b1edd..3bcf336af6 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h @@ -9,10 +9,13 @@ #pragma once #include +#include #include #include #include +#include + namespace Physics { /// Used to identify shape configuration type from base class. @@ -27,6 +30,7 @@ namespace Physics Native, ///< Native shape configuration if user wishes to bypass generic shape configurations. PhysicsAsset, ///< Shapes configured in the asset. CookedMesh, ///< Stores a blob of mesh data cooked for the specific engine. + Heightfield ///< Interacts with the physics system heightfield }; class ShapeConfiguration @@ -196,4 +200,52 @@ namespace Physics mutable void* m_cachedNativeMesh = nullptr; }; + class HeightfieldShapeConfiguration + : public ShapeConfiguration + { + public: + AZ_CLASS_ALLOCATOR(HeightfieldShapeConfiguration, AZ::SystemAllocator, 0); + AZ_RTTI(HeightfieldShapeConfiguration, "{8DF47C83-D2A9-4E7C-8620-5E173E43C0B3}", ShapeConfiguration); + static void Reflect(AZ::ReflectContext* context); + HeightfieldShapeConfiguration() = default; + HeightfieldShapeConfiguration(const HeightfieldShapeConfiguration&); + HeightfieldShapeConfiguration& operator=(const HeightfieldShapeConfiguration&); + ~HeightfieldShapeConfiguration(); + + ShapeType GetShapeType() const override + { + return ShapeType::Heightfield; + } + + void* GetCachedNativeHeightfield() const; + void SetCachedNativeHeightfield(void* cachedNativeHeightfield) const; + AZ::Vector2 GetGridResolution() const; + void SetGridResolution(const AZ::Vector2& gridSpacing); + int32_t GetNumColumns() const; + void SetNumColumns(int32_t numColumns); + int32_t GetNumRows() const; + void SetNumRows(int32_t numRows); + const AZStd::vector& GetSamples() const; + void SetSamples(const AZStd::vector& samples); + float GetMinHeightBounds() const; + void SetMinHeightBounds(float minBounds); + float GetMaxHeightBounds() const; + void SetMaxHeightBounds(float maxBounds); + + private: + //! The number of meters between each heightfield sample. + AZ::Vector2 m_gridResolution{ 1.0f }; + //! The number of columns in the heightfield sample grid. + int32_t m_numColumns{ 0 }; + //! The number of rows in the heightfield sample grid. + int32_t m_numRows{ 0 }; + //! The minimum and maximum heights that can be used by this heightfield. + //! This can be used by the physics system to choose a more optimal heightfield data type internally (ex: int16, uint8) + float m_minHeightBounds{AZStd::numeric_limits::lowest()}; + float m_maxHeightBounds{AZStd::numeric_limits::max()}; + //! The grid of sample points for the heightfield. + AZStd::vector m_samples; + //! An optional storage pointer for the physics system to cache its native heightfield representation. + mutable void* m_cachedNativeHeightfield{ nullptr }; + }; } // namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h b/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h index 868a27ed36..33313d612b 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h @@ -132,6 +132,10 @@ namespace Physics virtual AZStd::shared_ptr CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) = 0; + /// Releases the height field object created by the physics backend. + /// @param nativeHeightfieldObject Pointer to the height field object. + virtual void ReleaseNativeHeightfieldObject(void* nativeHeightfieldObject) = 0; + /// Releases the mesh object created by the physics backend. /// @param nativeMeshObject Pointer to the mesh object. virtual void ReleaseNativeMeshObject(void* nativeMeshObject) = 0; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp index 3dcb37c541..d989847ae8 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp @@ -107,6 +107,7 @@ namespace Physics PhysicsAssetShapeConfiguration::Reflect(context); NativeShapeConfiguration::Reflect(context); CookedMeshShapeConfiguration::Reflect(context); + HeightfieldShapeConfiguration::Reflect(context); AzPhysics::SystemInterface::Reflect(context); AzPhysics::Scene::Reflect(context); AzPhysics::CollisionLayer::Reflect(context); diff --git a/Code/Framework/AzFramework/AzFramework/Physics/physics_mock_files.cmake b/Code/Framework/AzFramework/AzFramework/Physics/physics_mock_files.cmake new file mode 100644 index 0000000000..162cc1ea86 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Physics/physics_mock_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + Mocks/MockHeightfieldProviderBus.h +) \ No newline at end of file diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index c5616d84c0..b338dbb0a8 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -228,6 +228,7 @@ set(FILES Physics/Configuration/SimulatedBodyConfiguration.cpp Physics/Configuration/SystemConfiguration.h Physics/Configuration/SystemConfiguration.cpp + Physics/HeightfieldProviderBus.h Physics/SimulatedBodies/RigidBody.h Physics/SimulatedBodies/RigidBody.cpp Physics/SimulatedBodies/StaticRigidBody.h @@ -251,6 +252,7 @@ set(FILES Physics/Shape.h Physics/ShapeConfiguration.h Physics/ShapeConfiguration.cpp + Physics/HeightfieldProviderBus.h Physics/SystemBus.h Physics/ColliderComponentBus.h Physics/RagdollPhysicsBus.h diff --git a/Code/Framework/AzFramework/CMakeLists.txt b/Code/Framework/AzFramework/CMakeLists.txt index b22586162b..c8eeac5c2d 100644 --- a/Code/Framework/AzFramework/CMakeLists.txt +++ b/Code/Framework/AzFramework/CMakeLists.txt @@ -42,6 +42,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAMESPACE AZ FILES_CMAKE Tests/framework_shared_tests_files.cmake + AzFramework/Physics/physics_mock_files.cmake INCLUDE_DIRECTORIES PUBLIC Tests @@ -53,7 +54,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest AZ::AzTestShared ) - + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( diff --git a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h index 1fe6e35d75..949d3add0d 100644 --- a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h +++ b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h @@ -209,6 +209,7 @@ namespace Blast AZStd::shared_ptr( const Physics::ColliderConfiguration&, const Physics::ShapeConfiguration&)); MOCK_METHOD1(ReleaseNativeMeshObject, void(void*)); + MOCK_METHOD1(ReleaseNativeHeightfieldObject, void(void*)); MOCK_METHOD1(CreateMaterial, AZStd::shared_ptr(const Physics::MaterialConfiguration&)); MOCK_METHOD0(GetDefaultMaterial, AZStd::shared_ptr()); MOCK_METHOD1( diff --git a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h index 882d198092..6649cea55e 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h @@ -46,6 +46,7 @@ namespace Physics } MOCK_METHOD2(CreateShape, AZStd::shared_ptr(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& configuration)); MOCK_METHOD1(ReleaseNativeMeshObject, void(void* nativeMeshObject)); + MOCK_METHOD1(ReleaseNativeHeightfieldObject, void(void* nativeMeshObject)); MOCK_METHOD1(CreateMaterial, AZStd::shared_ptr(const Physics::MaterialConfiguration& materialConfiguration)); MOCK_METHOD3(CookConvexMeshToFile, bool(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount)); MOCK_METHOD3(CookConvexMeshToMemory, bool(const AZ::Vector3* vertices, AZ::u32 vertexCount, AZStd::vector& result)); diff --git a/Gems/GradientSignal/Code/CMakeLists.txt b/Gems/GradientSignal/Code/CMakeLists.txt index 64f565cf99..5b88044116 100644 --- a/Gems/GradientSignal/Code/CMakeLists.txt +++ b/Gems/GradientSignal/Code/CMakeLists.txt @@ -107,7 +107,16 @@ endif() # Tests ################################################################################ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) - + ly_add_target( + NAME GradientSignal.Mocks HEADERONLY + NAMESPACE Gem + FILES_CMAKE + gradientsignal_mocks_files.cmake + INCLUDE_DIRECTORIES + INTERFACE + Mocks + ) + ly_add_target( NAME GradientSignal.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE Gem @@ -122,6 +131,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest Gem::GradientSignal.Static Gem::LmbrCentral + Gem::GradientSignal.Mocks ) ly_add_googletest( NAME Gem::GradientSignal.Tests diff --git a/Gems/GradientSignal/Code/Mocks/GradientSignal/Ebuses/MockGradientRequestBus.h b/Gems/GradientSignal/Code/Mocks/GradientSignal/Ebuses/MockGradientRequestBus.h new file mode 100644 index 0000000000..69e96268f6 --- /dev/null +++ b/Gems/GradientSignal/Code/Mocks/GradientSignal/Ebuses/MockGradientRequestBus.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include + +namespace UnitTest +{ + class MockGradientRequests + : private GradientSignal::GradientRequestBus::Handler + { + public: + MockGradientRequests(AZ::EntityId entityId) + { + GradientSignal::GradientRequestBus::Handler::BusConnect(entityId); + } + + ~MockGradientRequests() + { + GradientSignal::GradientRequestBus::Handler::BusDisconnect(); + } + + MOCK_CONST_METHOD1(GetValue, float(const GradientSignal::GradientSampleParams&)); + MOCK_CONST_METHOD1(IsEntityInHierarchy, bool(const AZ::EntityId&)); + }; +} // namespace UnitTest + diff --git a/Gems/GradientSignal/Code/gradientsignal_mocks_files.cmake b/Gems/GradientSignal/Code/gradientsignal_mocks_files.cmake new file mode 100644 index 0000000000..82c8c3793f --- /dev/null +++ b/Gems/GradientSignal/Code/gradientsignal_mocks_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + Mocks/GradientSignal/Ebuses/MockGradientRequestBus.h +) diff --git a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h index 094f9591d1..e180e1d536 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h +++ b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h @@ -15,12 +15,6 @@ namespace LmbrCentral { - /// Type ID for the AxisAlignedBoxShapeComponent - static const AZ::Uuid AxisAlignedBoxShapeComponentTypeId = "{641D817E-1BC6-406A-BBB2-218541808E45}"; - - /// Type ID for the EditorAxisAlignedBoxShapeComponent - static const AZ::Uuid EditorAxisAlignedBoxShapeComponentTypeId = "{8C027DF6-E157-4159-9BF8-F1B925466F1F}"; - /// Provide a Component interface for AxisAlignedBoxShape functionality. class AxisAlignedBoxShapeComponent : public AZ::Component diff --git a/Gems/LmbrCentral/Code/include/LmbrCentral/Shape/BoxShapeComponentBus.h b/Gems/LmbrCentral/Code/include/LmbrCentral/Shape/BoxShapeComponentBus.h index 488e8b5617..2e13334a76 100644 --- a/Gems/LmbrCentral/Code/include/LmbrCentral/Shape/BoxShapeComponentBus.h +++ b/Gems/LmbrCentral/Code/include/LmbrCentral/Shape/BoxShapeComponentBus.h @@ -24,6 +24,12 @@ namespace LmbrCentral /// Type ID for the BoxShapeConfig static const AZ::Uuid BoxShapeConfigTypeId = "{F034FBA2-AC2F-4E66-8152-14DFB90D6283}"; + /// Type ID for the AxisAlignedBoxShapeComponent + static const AZ::Uuid AxisAlignedBoxShapeComponentTypeId = "{641D817E-1BC6-406A-BBB2-218541808E45}"; + + /// Type ID for the EditorAxisAlignedBoxShapeComponent + static const AZ::Uuid EditorAxisAlignedBoxShapeComponentTypeId = "{8C027DF6-E157-4159-9BF8-F1B925466F1F}"; + /// Configuration data for BoxShapeComponent class BoxShapeConfig : public ShapeComponentConfig diff --git a/Gems/PhysX/Code/Editor/DebugDraw.cpp b/Gems/PhysX/Code/Editor/DebugDraw.cpp index 5936312ecf..734557205a 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.cpp +++ b/Gems/PhysX/Code/Editor/DebugDraw.cpp @@ -676,7 +676,17 @@ namespace PhysX } } - AZ::Transform Collider::GetColliderLocalTransform(const Physics::ColliderConfiguration& colliderConfig, + void Collider::DrawHeightfield( + [[maybe_unused]] AzFramework::DebugDisplayRequests& debugDisplay, + [[maybe_unused]] const Physics::ColliderConfiguration& colliderConfig, + [[maybe_unused]] const Physics::HeightfieldShapeConfiguration& heightfieldShapeConfig, + [[maybe_unused]] const AZ::Vector3& colliderScale, + [[maybe_unused]] const bool forceUniformScaling) const + { + } + + AZ::Transform Collider::GetColliderLocalTransform( + const Physics::ColliderConfiguration& colliderConfig, const AZ::Vector3& colliderScale) const { // Apply entity world transform scale to collider offset diff --git a/Gems/PhysX/Code/Editor/DebugDraw.h b/Gems/PhysX/Code/Editor/DebugDraw.h index 774def6900..74f08aae99 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.h +++ b/Gems/PhysX/Code/Editor/DebugDraw.h @@ -104,7 +104,15 @@ namespace PhysX const AZ::Vector3& meshScale, AZ::u32 geomIndex) const; - void DrawPolygonPrism(AzFramework::DebugDisplayRequests& debugDisplay, + void DrawHeightfield( + AzFramework::DebugDisplayRequests& debugDisplay, + const Physics::ColliderConfiguration& colliderConfig, + const Physics::HeightfieldShapeConfiguration& heightfieldShapeConfig, + const AZ::Vector3& colliderScale = AZ::Vector3::CreateOne(), + const bool forceUniformScaling = false) const; + + void DrawPolygonPrism( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, const AZStd::vector& points) const; AZ::Transform GetColliderLocalTransform(const Physics::ColliderConfiguration& colliderConfig, diff --git a/Gems/PhysX/Code/Include/PhysX/SystemComponentBus.h b/Gems/PhysX/Code/Include/PhysX/SystemComponentBus.h index f9bd2dbad5..a31a4e65b8 100644 --- a/Gems/PhysX/Code/Include/PhysX/SystemComponentBus.h +++ b/Gems/PhysX/Code/Include/PhysX/SystemComponentBus.h @@ -16,19 +16,21 @@ namespace AzPhysics { class CollisionGroup; class CollisionLayer; -} +} // namespace AzPhysics namespace physx { class PxScene; class PxSceneDesc; class PxConvexMesh; + class PxHeightField; class PxTriangleMesh; class PxShape; class PxCooking; class PxControllerManager; struct PxFilterData; -} + struct PxHeightFieldSample; +} // namespace physx namespace PhysX { @@ -63,6 +65,13 @@ namespace PhysX /// @return Pointer to the created mesh. virtual physx::PxTriangleMesh* CreateTriangleMeshFromCooked(const void* cookedMeshData, AZ::u32 bufferSize) = 0; + /// Creates a new heightfield. + /// @param samples Pointer to beginning of heightfield sample data. + /// @param numRows Number of rows in the heightfield. + /// @param numColumns Number of columns in the heightfield. + /// @return Pointer to the created heightfield. + virtual physx::PxHeightField* CreateHeightField(const physx::PxHeightFieldSample* samples, AZ::u32 numRows, AZ::u32 numColumns) = 0; + /// Creates PhysX collision filter data from generic collision filtering settings. /// @param layer The collision layer the object belongs to. /// @param group The set of collision layers the object will interact with. diff --git a/Gems/PhysX/Code/Source/ComponentDescriptors.cpp b/Gems/PhysX/Code/Source/ComponentDescriptors.cpp index ebe6efd310..83232edc40 100644 --- a/Gems/PhysX/Code/Source/ComponentDescriptors.cpp +++ b/Gems/PhysX/Code/Source/ComponentDescriptors.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ namespace PhysX BaseColliderComponent::CreateDescriptor(), MeshColliderComponent::CreateDescriptor(), BoxColliderComponent::CreateDescriptor(), + HeightfieldColliderComponent::CreateDescriptor(), SphereColliderComponent::CreateDescriptor(), CapsuleColliderComponent::CreateDescriptor(), ShapeColliderComponent::CreateDescriptor(), diff --git a/Gems/PhysX/Code/Source/EditorComponentDescriptors.cpp b/Gems/PhysX/Code/Source/EditorComponentDescriptors.cpp index d43d0edc23..362903f638 100644 --- a/Gems/PhysX/Code/Source/EditorComponentDescriptors.cpp +++ b/Gems/PhysX/Code/Source/EditorComponentDescriptors.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ namespace PhysX EditorColliderComponent::CreateDescriptor(), EditorFixedJointComponent::CreateDescriptor(), EditorForceRegionComponent::CreateDescriptor(), + EditorHeightfieldColliderComponent::CreateDescriptor(), EditorHingeJointComponent::CreateDescriptor(), EditorJointComponent::CreateDescriptor(), EditorRigidBodyComponent::CreateDescriptor(), diff --git a/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp new file mode 100644 index 0000000000..96b1bd51f5 --- /dev/null +++ b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace PhysX +{ + void EditorHeightfieldColliderComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("ColliderConfiguration", &EditorHeightfieldColliderComponent::m_colliderConfig) + ->Field("DebugDrawSettings", &EditorHeightfieldColliderComponent::m_colliderDebugDraw) + ->Field("ShapeConfig", &EditorHeightfieldColliderComponent::m_shapeConfig) + ; + + if (auto editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "PhysX Heightfield Collider", "Creates geometry in the PhysX simulation based on an attached heightfield component") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "PhysX") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXCollider.svg") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ->Attribute( + AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/heightfield-collider/") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement( + AZ::Edit::UIHandlers::Default, &EditorHeightfieldColliderComponent::m_colliderConfig, "Collider configuration", + "Configuration of the collider") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorHeightfieldColliderComponent::OnConfigurationChanged) + ->DataElement( + AZ::Edit::UIHandlers::Default, &EditorHeightfieldColliderComponent::m_colliderDebugDraw, "Debug draw settings", + "Debug draw settings") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + } + } + } + + void EditorHeightfieldColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("PhysicsWorldBodyService")); + provided.push_back(AZ_CRC_CE("PhysXColliderService")); + provided.push_back(AZ_CRC_CE("PhysXHeightfieldColliderService")); + } + + void EditorHeightfieldColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("PhysicsHeightfieldProviderService")); + } + + void EditorHeightfieldColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("PhysXColliderService")); + incompatible.push_back(AZ_CRC_CE("PhysXStaticRigidBodyService")); + incompatible.push_back(AZ_CRC_CE("PhysXRigidBodyService")); + } + + EditorHeightfieldColliderComponent::EditorHeightfieldColliderComponent() + : m_physXConfigChangedHandler( + []([[maybe_unused]] const AzPhysics::SystemConfiguration* config) + { + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast( + &AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, + AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); + }) + , m_onMaterialLibraryChangedEventHandler( + [this](const AZ::Data::AssetId& defaultMaterialLibrary) + { + m_colliderConfig.m_materialSelection.OnMaterialLibraryChanged(defaultMaterialLibrary); + Physics::ColliderComponentEventBus::Event(GetEntityId(), &Physics::ColliderComponentEvents::OnColliderChanged); + + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast( + &AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, + AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); + }) + { + } + + EditorHeightfieldColliderComponent ::~EditorHeightfieldColliderComponent() + { + ClearHeightfield(); + } + + // AZ::Component + void EditorHeightfieldColliderComponent::Activate() + { + AzToolsFramework::Components::EditorComponentBase::Activate(); + + // Heightfields don't support the following: + // - Offset: There shouldn't be a need to offset the data, since the heightfield provider is giving a physics representation + // - IsTrigger: PhysX heightfields don't support acting as triggers + // - MaterialSelection: The heightfield provider provides per-vertex material selection + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::Offset, false); + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::IsTrigger, false); + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::MaterialSelection, false); + + m_sceneInterface = AZ::Interface::Get(); + if (m_sceneInterface) + { + m_attachedSceneHandle = m_sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName); + } + + const AZ::EntityId entityId = GetEntityId(); + + AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusConnect(entityId); + + // Debug drawing + m_colliderDebugDraw.Connect(entityId); + m_colliderDebugDraw.SetDisplayCallback(this); + + Physics::HeightfieldProviderNotificationBus::Handler::BusConnect(entityId); + PhysX::ColliderShapeRequestBus::Handler::BusConnect(entityId); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(entityId); + + RefreshHeightfield(); + } + + void EditorHeightfieldColliderComponent::Deactivate() + { + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); + PhysX::ColliderShapeRequestBus::Handler::BusDisconnect(); + Physics::HeightfieldProviderNotificationBus::Handler::BusDisconnect(); + + m_colliderDebugDraw.Disconnect(); + AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusDisconnect(); + AzToolsFramework::Components::EditorComponentBase::Deactivate(); + + ClearHeightfield(); + } + + void EditorHeightfieldColliderComponent::BuildGameEntity(AZ::Entity* gameEntity) + { + auto* heightfieldColliderComponent = gameEntity->CreateComponent(); + heightfieldColliderComponent->SetShapeConfiguration( + { AZStd::make_shared(m_colliderConfig), m_shapeConfig }); + } + + void EditorHeightfieldColliderComponent::OnHeightfieldDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion) + { + RefreshHeightfield(); + } + + void EditorHeightfieldColliderComponent::ClearHeightfield() + { + // There are two references to the heightfield data, we need to clear both to make the heightfield clear out and deallocate: + // - The simulated body has a pointer to the shape, which has a GeometryHolder, which has the Heightfield inside it + // - The shape config is also holding onto a pointer to the Heightfield + + // We remove the simulated body first, since we don't want the heightfield to exist any more. + if (m_sceneInterface && m_staticRigidBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) + { + m_sceneInterface->RemoveSimulatedBody(m_attachedSceneHandle, m_staticRigidBodyHandle); + } + + // Now we can safely clear out the cached heightfield pointer. + m_shapeConfig->SetCachedNativeHeightfield(nullptr); + } + + void EditorHeightfieldColliderComponent::InitStaticRigidBody() + { + // Get the transform from the HeightfieldProvider. Because rotation and scale can indirectly affect how the heightfield itself + // is computed and the size of the heightfield, it's possible that the HeightfieldProvider will provide a different transform + // back to us than the one that's directly on that entity. + AZ::Transform transform = AZ::Transform::CreateIdentity(); + Physics::HeightfieldProviderRequestsBus::EventResult( + transform, GetEntityId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldTransform); + + AzPhysics::StaticRigidBodyConfiguration configuration; + configuration.m_orientation = transform.GetRotation(); + configuration.m_position = transform.GetTranslation(); + configuration.m_entityId = GetEntityId(); + configuration.m_debugName = GetEntity()->GetName(); + + AzPhysics::ShapeColliderPairList colliderShapePairs; + colliderShapePairs.emplace_back(AZStd::make_shared(m_colliderConfig), m_shapeConfig); + configuration.m_colliderAndShapeData = colliderShapePairs; + + if (m_sceneInterface) + { + m_staticRigidBodyHandle = m_sceneInterface->AddSimulatedBody(m_attachedSceneHandle, &configuration); + } + } + + void EditorHeightfieldColliderComponent::InitHeightfieldShapeConfiguration() + { + Physics::HeightfieldShapeConfiguration& configuration = static_cast(*m_shapeConfig); + + Utils::InitHeightfieldShapeConfiguration(GetEntityId(), configuration); + } + + void EditorHeightfieldColliderComponent::RefreshHeightfield() + { + ClearHeightfield(); + InitHeightfieldShapeConfiguration(); + InitStaticRigidBody(); + Physics::ColliderComponentEventBus::Event(GetEntityId(), &Physics::ColliderComponentEvents::OnColliderChanged); + } + + AZ::u32 EditorHeightfieldColliderComponent::OnConfigurationChanged() + { + RefreshHeightfield(); + return AZ::Edit::PropertyRefreshLevels::None; + } + + // AzToolsFramework::EntitySelectionEvents + void EditorHeightfieldColliderComponent::OnSelected() + { + if (auto* physXSystem = GetPhysXSystem()) + { + if (!m_physXConfigChangedHandler.IsConnected()) + { + physXSystem->RegisterSystemConfigurationChangedEvent(m_physXConfigChangedHandler); + } + if (!m_onMaterialLibraryChangedEventHandler.IsConnected()) + { + physXSystem->RegisterOnMaterialLibraryChangedEventHandler(m_onMaterialLibraryChangedEventHandler); + } + } + } + + // AzToolsFramework::EntitySelectionEvents + void EditorHeightfieldColliderComponent::OnDeselected() + { + m_onMaterialLibraryChangedEventHandler.Disconnect(); + m_physXConfigChangedHandler.Disconnect(); + } + + // DisplayCallback + void EditorHeightfieldColliderComponent::Display(AzFramework::DebugDisplayRequests& debugDisplay) const + { + const auto& heightfieldConfig = static_cast(*m_shapeConfig); + m_colliderDebugDraw.DrawHeightfield(debugDisplay, m_colliderConfig, heightfieldConfig); + } + + // SimulatedBodyComponentRequestsBus + void EditorHeightfieldColliderComponent::EnablePhysics() + { + if (!IsPhysicsEnabled() && m_sceneInterface) + { + m_sceneInterface->EnableSimulationOfBody(m_attachedSceneHandle, m_staticRigidBodyHandle); + } + } + + // SimulatedBodyComponentRequestsBus + void EditorHeightfieldColliderComponent::DisablePhysics() + { + if (m_sceneInterface) + { + m_sceneInterface->DisableSimulationOfBody(m_attachedSceneHandle, m_staticRigidBodyHandle); + } + } + + // SimulatedBodyComponentRequestsBus + bool EditorHeightfieldColliderComponent::IsPhysicsEnabled() const + { + if (m_sceneInterface && m_staticRigidBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) + { + if (auto* body = m_sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_staticRigidBodyHandle)) + { + return body->m_simulating; + } + } + return false; + } + + // SimulatedBodyComponentRequestsBus + AzPhysics::SimulatedBodyHandle EditorHeightfieldColliderComponent::GetSimulatedBodyHandle() const + { + return m_staticRigidBodyHandle; + } + + // SimulatedBodyComponentRequestsBus + AzPhysics::SimulatedBody* EditorHeightfieldColliderComponent::GetSimulatedBody() + { + if (m_sceneInterface && m_staticRigidBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) + { + if (auto* body = m_sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_staticRigidBodyHandle)) + { + return body; + } + } + return nullptr; + } + + // SimulatedBodyComponentRequestsBus + AzPhysics::SceneQueryHit EditorHeightfieldColliderComponent::RayCast(const AzPhysics::RayCastRequest& request) + { + if (m_sceneInterface && m_staticRigidBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) + { + if (auto* body = m_sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_staticRigidBodyHandle)) + { + return body->RayCast(request); + } + } + return AzPhysics::SceneQueryHit(); + } + + // ColliderShapeRequestBus + AZ::Aabb EditorHeightfieldColliderComponent::GetColliderShapeAabb() + { + // Get the Collider AABB directly from the heightfield provider. + AZ::Aabb colliderAabb = AZ::Aabb::CreateNull(); + Physics::HeightfieldProviderRequestsBus::EventResult( + colliderAabb, GetEntityId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldAabb); + + return colliderAabb; + } + + // SimulatedBodyComponentRequestsBus + AZ::Aabb EditorHeightfieldColliderComponent::GetAabb() const + { + // On the SimulatedBodyComponentRequestsBus, get the AABB from the simulated body instead of the collider. + if (m_sceneInterface && m_staticRigidBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) + { + if (auto* body = m_sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_staticRigidBodyHandle)) + { + return body->GetAabb(); + } + } + return AZ::Aabb::CreateNull(); + } + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.h b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.h new file mode 100644 index 0000000000..08b3ec5801 --- /dev/null +++ b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace PhysX +{ + //! Editor PhysX Heightfield Collider Component. + class EditorHeightfieldColliderComponent + : public AzToolsFramework::Components::EditorComponentBase + , protected AzToolsFramework::EntitySelectionEvents::Bus::Handler + , protected DebugDraw::DisplayCallback + , protected AzPhysics::SimulatedBodyComponentRequestsBus::Handler + , protected PhysX::ColliderShapeRequestBus::Handler + , protected Physics::HeightfieldProviderNotificationBus::Handler + { + public: + AZ_EDITOR_COMPONENT( + EditorHeightfieldColliderComponent, + "{C388C3DB-8D2E-4D26-96D3-198EDC799B77}", + AzToolsFramework::Components::EditorComponentBase); + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + EditorHeightfieldColliderComponent(); + ~EditorHeightfieldColliderComponent(); + + // AZ::Component + void Activate() override; + void Deactivate() override; + + // EditorComponentBase + void BuildGameEntity(AZ::Entity* gameEntity) override; + + protected: + + // AzToolsFramework::EntitySelectionEvents + void OnSelected() override; + void OnDeselected() override; + + // DisplayCallback + void Display(AzFramework::DebugDisplayRequests& debugDisplay) const; + + // ColliderShapeRequestBus + AZ::Aabb GetColliderShapeAabb() override; + bool IsTrigger() override + { + // PhysX Heightfields don't support triggers. + return false; + } + + // AzPhysics::SimulatedBodyComponentRequestsBus::Handler overrides ... + void EnablePhysics() override; + void DisablePhysics() override; + bool IsPhysicsEnabled() const override; + AZ::Aabb GetAabb() const override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; + AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override; + + // Physics::HeightfieldProviderNotificationBus + void OnHeightfieldDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion) override; + + private: + AZ::u32 OnConfigurationChanged(); + + void ClearHeightfield(); + void InitHeightfieldShapeConfiguration(); + void InitStaticRigidBody(); + void RefreshHeightfield(); + + DebugDraw::Collider m_colliderDebugDraw; //!< Handles drawing the collider + AzPhysics::SceneInterface* m_sceneInterface{ nullptr }; + + AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler m_physXConfigChangedHandler; + AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler m_onMaterialLibraryChangedEventHandler; + + Physics::ColliderConfiguration m_colliderConfig; //!< Stores collision layers, whether the collider is a trigger, etc. + AZStd::shared_ptr m_shapeConfig{ new Physics::HeightfieldShapeConfiguration() }; + + AzPhysics::SimulatedBodyHandle m_staticRigidBodyHandle = + AzPhysics::InvalidSimulatedBodyHandle; //!< Handle to the body in the editor physics scene if there is no rigid body component. + AzPhysics::SceneHandle m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; + }; + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp new file mode 100644 index 0000000000..9bc209982d --- /dev/null +++ b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp @@ -0,0 +1,365 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace PhysX +{ + void HeightfieldColliderComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("ShapeConfig", &HeightfieldColliderComponent::m_shapeConfig) + ; + } + } + + void HeightfieldColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("PhysicsWorldBodyService")); + provided.push_back(AZ_CRC_CE("PhysXColliderService")); + provided.push_back(AZ_CRC_CE("PhysXHeightfieldColliderService")); + provided.push_back(AZ_CRC_CE("PhysXStaticRigidBodyService")); + } + + void HeightfieldColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("PhysicsHeightfieldProviderService")); + } + + void HeightfieldColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("PhysXColliderService")); + incompatible.push_back(AZ_CRC_CE("PhysXStaticRigidBodyService")); + incompatible.push_back(AZ_CRC_CE("PhysXRigidBodyService")); + } + + HeightfieldColliderComponent::~HeightfieldColliderComponent() + { + ClearHeightfield(); + } + + void HeightfieldColliderComponent::Activate() + { + const AZ::EntityId entityId = GetEntityId(); + + Physics::HeightfieldProviderNotificationBus::Handler::BusConnect(entityId); + ColliderComponentRequestBus::Handler::BusConnect(entityId); + ColliderShapeRequestBus::Handler::BusConnect(entityId); + Physics::CollisionFilteringRequestBus::Handler::BusConnect(entityId); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(entityId); + + RefreshHeightfield(); + } + + void HeightfieldColliderComponent::Deactivate() + { + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); + Physics::CollisionFilteringRequestBus::Handler::BusDisconnect(); + ColliderShapeRequestBus::Handler::BusDisconnect(); + ColliderComponentRequestBus::Handler::BusDisconnect(); + Physics::HeightfieldProviderNotificationBus::Handler::BusDisconnect(); + + ClearHeightfield(); + } + + void HeightfieldColliderComponent::OnHeightfieldDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion) + { + RefreshHeightfield(); + } + + void HeightfieldColliderComponent::ClearHeightfield() + { + // There are two references to the heightfield data, we need to clear both to make the heightfield clear out and deallocate: + // - The simulated body has a pointer to the shape, which has a GeometryHolder, which has the Heightfield inside it + // - The shape config is also holding onto a pointer to the Heightfield + + // We remove the simulated body first, since we don't want the heightfield to exist any more. + if (auto* sceneInterface = AZ::Interface::Get(); + sceneInterface && m_staticRigidBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) + { + sceneInterface->RemoveSimulatedBody(m_attachedSceneHandle, m_staticRigidBodyHandle); + } + + // Now we can safely clear out the cached heightfield pointer. + Physics::HeightfieldShapeConfiguration& configuration = static_cast(*m_shapeConfig.second); + configuration.SetCachedNativeHeightfield(nullptr); + } + + void HeightfieldColliderComponent::InitStaticRigidBody() + { + // Get the transform from the HeightfieldProvider. Because rotation and scale can indirectly affect how the heightfield itself + // is computed and the size of the heightfield, it's possible that the HeightfieldProvider will provide a different transform + // back to us than the one that's directly on that entity. + AZ::Transform transform = AZ::Transform::CreateIdentity(); + Physics::HeightfieldProviderRequestsBus::EventResult( + transform, GetEntityId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldTransform); + + AzPhysics::StaticRigidBodyConfiguration configuration; + configuration.m_orientation = transform.GetRotation(); + configuration.m_position = transform.GetTranslation(); + configuration.m_entityId = GetEntityId(); + configuration.m_debugName = GetEntity()->GetName(); + configuration.m_colliderAndShapeData = GetShapeConfigurations(); + + if (m_attachedSceneHandle == AzPhysics::InvalidSceneHandle) + { + Physics::DefaultWorldBus::BroadcastResult(m_attachedSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); + } + if (auto* sceneInterface = AZ::Interface::Get()) + { + m_staticRigidBodyHandle = sceneInterface->AddSimulatedBody(m_attachedSceneHandle, &configuration); + } + } + + void HeightfieldColliderComponent::InitHeightfieldShapeConfiguration() + { + Physics::HeightfieldShapeConfiguration& configuration = static_cast(*m_shapeConfig.second); + + Utils::InitHeightfieldShapeConfiguration(GetEntityId(), configuration); + } + + void HeightfieldColliderComponent::RefreshHeightfield() + { + ClearHeightfield(); + InitHeightfieldShapeConfiguration(); + InitStaticRigidBody(); + Physics::ColliderComponentEventBus::Event(GetEntityId(), &Physics::ColliderComponentEvents::OnColliderChanged); + } + + void HeightfieldColliderComponent::SetShapeConfiguration(const AzPhysics::ShapeColliderPair& shapeConfig) + { + if (GetEntity()->GetState() == AZ::Entity::State::Active) + { + AZ_Warning( + "PhysX", false, "Trying to call SetShapeConfiguration for entity \"%s\" while entity is active.", + GetEntity()->GetName().c_str()); + return; + } + m_shapeConfig = shapeConfig; + } + + // SimulatedBodyComponentRequestsBus + void HeightfieldColliderComponent::EnablePhysics() + { + if (IsPhysicsEnabled()) + { + return; + } + if (auto* sceneInterface = AZ::Interface::Get()) + { + sceneInterface->EnableSimulationOfBody(m_attachedSceneHandle, m_staticRigidBodyHandle); + } + } + + // SimulatedBodyComponentRequestsBus + void HeightfieldColliderComponent::DisablePhysics() + { + if (auto* sceneInterface = AZ::Interface::Get()) + { + sceneInterface->DisableSimulationOfBody(m_attachedSceneHandle, m_staticRigidBodyHandle); + } + } + + // SimulatedBodyComponentRequestsBus + bool HeightfieldColliderComponent::IsPhysicsEnabled() const + { + if (m_staticRigidBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) + { + if (auto* sceneInterface = AZ::Interface::Get(); + sceneInterface != nullptr && sceneInterface->IsEnabled(m_attachedSceneHandle)) // check if the scene is enabled + { + if (AzPhysics::SimulatedBody* body = + sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_staticRigidBodyHandle)) + { + return body->m_simulating; + } + } + } + return false; + } + + // SimulatedBodyComponentRequestsBus + AzPhysics::SimulatedBodyHandle HeightfieldColliderComponent::GetSimulatedBodyHandle() const + { + return m_staticRigidBodyHandle; + } + + // SimulatedBodyComponentRequestsBus + AzPhysics::SimulatedBody* HeightfieldColliderComponent::GetSimulatedBody() + { + if (auto* sceneInterface = AZ::Interface::Get()) + { + return sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_staticRigidBodyHandle); + } + return nullptr; + } + + // SimulatedBodyComponentRequestsBus + AzPhysics::SceneQueryHit HeightfieldColliderComponent::RayCast(const AzPhysics::RayCastRequest& request) + { + if (auto* body = azdynamic_cast(GetSimulatedBody())) + { + return body->RayCast(request); + } + return AzPhysics::SceneQueryHit(); + } + + // ColliderComponentRequestBus + AzPhysics::ShapeColliderPairList HeightfieldColliderComponent::GetShapeConfigurations() + { + AzPhysics::ShapeColliderPairList shapeConfigurationList({ m_shapeConfig }); + return shapeConfigurationList; + } + + AZStd::shared_ptr HeightfieldColliderComponent::GetHeightfieldShape() + { + if (auto* body = azdynamic_cast(GetSimulatedBody())) + { + // Heightfields should only have one shape + AZ_Assert(body->GetShapeCount() == 1, "Heightfield rigid body has the wrong number of shapes: %zu", body->GetShapeCount()); + return body->GetShape(0); + } + + return {}; + } + + // ColliderComponentRequestBus + AZStd::vector> HeightfieldColliderComponent::GetShapes() + { + return { GetHeightfieldShape() }; + } + + // PhysX::ColliderShapeBus + AZ::Aabb HeightfieldColliderComponent::GetColliderShapeAabb() + { + // Get the Collider AABB directly from the heightfield provider. + AZ::Aabb colliderAabb = AZ::Aabb::CreateNull(); + Physics::HeightfieldProviderRequestsBus::EventResult( + colliderAabb, GetEntityId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldAabb); + + return colliderAabb; + } + + // SimulatedBodyComponentRequestsBus + AZ::Aabb HeightfieldColliderComponent::GetAabb() const + { + // On the SimulatedBodyComponentRequestsBus, get the AABB from the simulated body instead of the collider. + if (auto* sceneInterface = AZ::Interface::Get()) + { + if (AzPhysics::SimulatedBody* body = sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_staticRigidBodyHandle)) + { + return body->GetAabb(); + } + } + return AZ::Aabb::CreateNull(); + } + + // CollisionFilteringRequestBus + void HeightfieldColliderComponent::SetCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag) + { + if (auto heightfield = GetHeightfieldShape()) + { + if (Physics::Utils::FilterTag(heightfield->GetTag(), colliderTag)) + { + bool success = false; + AzPhysics::CollisionLayer layer; + Physics::CollisionRequestBus::BroadcastResult( + success, &Physics::CollisionRequests::TryGetCollisionLayerByName, layerName, layer); + if (success) + { + heightfield->SetCollisionLayer(layer); + } + } + } + } + + // CollisionFilteringRequestBus + AZStd::string HeightfieldColliderComponent::GetCollisionLayerName() + { + AZStd::string layerName; + if (auto heightfield = GetHeightfieldShape()) + { + Physics::CollisionRequestBus::BroadcastResult( + layerName, &Physics::CollisionRequests::GetCollisionLayerName, heightfield->GetCollisionLayer()); + } + return layerName; + } + + // CollisionFilteringRequestBus + void HeightfieldColliderComponent::SetCollisionGroup(const AZStd::string& groupName, AZ::Crc32 colliderTag) + { + if (auto heightfield = GetHeightfieldShape()) + { + if (Physics::Utils::FilterTag(heightfield->GetTag(), colliderTag)) + { + bool success = false; + AzPhysics::CollisionGroup group; + Physics::CollisionRequestBus::BroadcastResult( + success, &Physics::CollisionRequests::TryGetCollisionGroupByName, groupName, group); + if (success) + { + heightfield->SetCollisionGroup(group); + } + } + } + } + + // CollisionFilteringRequestBus + AZStd::string HeightfieldColliderComponent::GetCollisionGroupName() + { + AZStd::string groupName; + if (auto heightfield = GetHeightfieldShape()) + { + Physics::CollisionRequestBus::BroadcastResult( + groupName, &Physics::CollisionRequests::GetCollisionGroupName, heightfield->GetCollisionGroup()); + } + + return groupName; + } + + // CollisionFilteringRequestBus + void HeightfieldColliderComponent::ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag, bool enabled) + { + if (auto heightfield = GetHeightfieldShape()) + { + if (Physics::Utils::FilterTag(heightfield->GetTag(), colliderTag)) + { + bool success = false; + AzPhysics::CollisionLayer layer; + Physics::CollisionRequestBus::BroadcastResult( + success, &Physics::CollisionRequests::TryGetCollisionLayerByName, layerName, layer); + if (success) + { + auto group = heightfield->GetCollisionGroup(); + group.SetLayer(layer, enabled); + heightfield->SetCollisionGroup(group); + } + } + } + } + +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/HeightfieldColliderComponent.h b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.h new file mode 100644 index 0000000000..3213f7a774 --- /dev/null +++ b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include +#include + +#include +#include + +namespace AzPhysics +{ + struct SimulatedBody; +} + +namespace PhysX +{ + class StaticRigidBody; + + //! Component that provides a Heightfield Collider and associated Static Rigid Body. + //! The heightfield collider is a bit different from the other shape colliders in that it gets the heightfield data from a + //! HeightfieldProvider, which can control position, rotation, size, and even change its data at runtime. + //! + //! Due to these differences, this component directly implements both the collider and static rigid body services instead of + //! using BaseColliderComponent and StaticRigidBodyComponent. + class HeightfieldColliderComponent + : public AZ::Component + , public ColliderComponentRequestBus::Handler + , public AzPhysics::SimulatedBodyComponentRequestsBus::Handler + , protected PhysX::ColliderShapeRequestBus::Handler + , protected Physics::CollisionFilteringRequestBus::Handler + , protected Physics::HeightfieldProviderNotificationBus::Handler + { + public: + using Configuration = Physics::HeightfieldShapeConfiguration; + AZ_COMPONENT(HeightfieldColliderComponent, "{9A42672C-281A-4CE8-BFDD-EAA1E0FCED76}"); + static void Reflect(AZ::ReflectContext* context); + + HeightfieldColliderComponent() = default; + ~HeightfieldColliderComponent() override; + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + void Activate() override; + void Deactivate() override; + + void SetShapeConfiguration(const AzPhysics::ShapeColliderPair& shapeConfig); + + protected: + // ColliderComponentRequestBus + AzPhysics::ShapeColliderPairList GetShapeConfigurations() override; + AZStd::vector> GetShapes() override; + + // ColliderShapeRequestBus + AZ::Aabb GetColliderShapeAabb() override; + bool IsTrigger() override + { + // PhysX Heightfields don't support triggers. + return false; + } + + // CollisionFilteringRequestBus + void SetCollisionLayer(const AZStd::string& layerName, AZ::Crc32 filterTag) override; + AZStd::string GetCollisionLayerName() override; + void SetCollisionGroup(const AZStd::string& groupName, AZ::Crc32 filterTag) override; + AZStd::string GetCollisionGroupName() override; + void ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 filterTag, bool enabled) override; + + // SimulatedBodyComponentRequestsBus + void EnablePhysics() override; + void DisablePhysics() override; + bool IsPhysicsEnabled() const override; + AZ::Aabb GetAabb() const override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override; + + // HeightfieldProviderNotificationBus + void OnHeightfieldDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion) override; + + private: + AZStd::shared_ptr GetHeightfieldShape(); + + void ClearHeightfield(); + void InitHeightfieldShapeConfiguration(); + void InitStaticRigidBody(); + void RefreshHeightfield(); + + AzPhysics::ShapeColliderPair m_shapeConfig; + AzPhysics::SimulatedBodyHandle m_staticRigidBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; + AzPhysics::SceneHandle m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; + }; +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/SystemComponent.cpp b/Gems/PhysX/Code/Source/SystemComponent.cpp index 2f4d082ccb..000d69d982 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.cpp +++ b/Gems/PhysX/Code/Source/SystemComponent.cpp @@ -252,6 +252,22 @@ namespace PhysX return convex; } + physx::PxHeightField* SystemComponent::CreateHeightField(const physx::PxHeightFieldSample* samples, AZ::u32 numRows, AZ::u32 numColumns) + { + physx::PxHeightFieldDesc desc; + desc.format = physx::PxHeightFieldFormat::eS16_TM; + desc.nbColumns = numColumns; + desc.nbRows = numRows; + desc.samples.data = samples; + desc.samples.stride = sizeof(physx::PxHeightFieldSample); + + physx::PxHeightField* heightfield = + m_physXSystem->GetPxCooking()->createHeightField(desc, m_physXSystem->GetPxPhysics()->getPhysicsInsertionCallback()); + AZ_Error("PhysX", heightfield, "Error. Unable to create heightfield"); + + return heightfield; + } + bool SystemComponent::CookConvexMeshToFile(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount) { AZStd::vector physxData; @@ -342,6 +358,14 @@ namespace PhysX return AZStd::make_shared(materialConfiguration); } + void SystemComponent::ReleaseNativeHeightfieldObject(void* nativeHeightfieldObject) + { + if (nativeHeightfieldObject) + { + static_cast(nativeHeightfieldObject)->release(); + } + } + void SystemComponent::ReleaseNativeMeshObject(void* nativeMeshObject) { if (nativeMeshObject) diff --git a/Gems/PhysX/Code/Source/SystemComponent.h b/Gems/PhysX/Code/Source/SystemComponent.h index 3661655bb3..d581af7e67 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.h +++ b/Gems/PhysX/Code/Source/SystemComponent.h @@ -76,6 +76,7 @@ namespace PhysX physx::PxConvexMesh* CreateConvexMesh(const void* vertices, AZ::u32 vertexNum, AZ::u32 vertexStride) override; // should we use AZ::Vector3* or physx::PxVec3 here? physx::PxConvexMesh* CreateConvexMeshFromCooked(const void* cookedMeshData, AZ::u32 bufferSize) override; physx::PxTriangleMesh* CreateTriangleMeshFromCooked(const void* cookedMeshData, AZ::u32 bufferSize) override; + physx::PxHeightField* CreateHeightField(const physx::PxHeightFieldSample* samples, AZ::u32 numRows, AZ::u32 numColumns) override; bool CookConvexMeshToFile(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount) override; @@ -112,6 +113,7 @@ namespace PhysX AZStd::shared_ptr CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) override; void ReleaseNativeMeshObject(void* nativeMeshObject) override; + void ReleaseNativeHeightfieldObject(void* nativeHeightfieldObject) override; // Assets related data AZStd::vector> m_assetHandlers; diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index 8a76d6d986..72e17aff70 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -64,6 +66,136 @@ namespace PhysX } } + void CreatePxGeometryFromHeightfield( + const Physics::HeightfieldShapeConfiguration& heightfieldConfig, physx::PxGeometryHolder& pxGeometry) + { + physx::PxHeightField* heightfield = nullptr; + + const AZ::Vector2 gridSpacing = heightfieldConfig.GetGridResolution(); + + const int32_t numCols = heightfieldConfig.GetNumColumns(); + const int32_t numRows = heightfieldConfig.GetNumRows(); + + const float rowScale = gridSpacing.GetX(); + const float colScale = gridSpacing.GetY(); + + const float minHeightBounds = heightfieldConfig.GetMinHeightBounds(); + const float maxHeightBounds = heightfieldConfig.GetMaxHeightBounds(); + const float halfBounds{ (maxHeightBounds - minHeightBounds) / 2.0f }; + + // We're making the assumption right now that the min/max bounds are centered around 0. + // If we ever want to allow off-center bounds, we'll need to fix up the float-to-int16 height math below + // to account for it. + AZ_Assert( + AZ::IsClose(-halfBounds, minHeightBounds) && AZ::IsClose(halfBounds, maxHeightBounds), + "Min/Max height bounds aren't centered around 0, the height conversions below will be incorrect."); + + AZ_Assert( + maxHeightBounds >= minHeightBounds, + "Max height bounds is less than min height bounds, the height conversions below will be incorrect."); + + // To convert our floating-point heights to fixed-point representation inside of an int16, we need a scale factor + // for the conversion. The scale factor is used to map the most important bits of our floating-point height to the + // full 16-bit range. + // Note that the scaleFactor choice here affects overall precision. For each bit that the integer part of our max + // height uses, that's one less bit for the fractional part. + const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits::max() / halfBounds; + const float heightScale{ 1.0f / scaleFactor }; + + const uint8_t physxMaximumMaterialIndex = 0x7f; + + // Delete the cached heightfield object if it is there, and create a new one and save in the shape configuration + heightfieldConfig.SetCachedNativeHeightfield(nullptr); + + const AZStd::vector& samples = heightfieldConfig.GetSamples(); + AZ_Assert(samples.size() == numRows * numCols, "GetHeightsAndMaterials returned wrong sized heightfield"); + + if (!samples.empty()) + { + AZStd::vector physxSamples(samples.size()); + + for (int32_t row = 0; row < numRows; row++) + { + const bool lastRowIndex = (row == (numRows - 1)); + + for (int32_t col = 0; col < numCols; col++) + { + const bool lastColumnIndex = (col == (numCols - 1)); + + auto GetIndex = [numCols](int32_t row, int32_t col) + { + return (row * numCols) + col; + }; + + const int32_t sampleIndex = GetIndex(row, col); + + const Physics::HeightMaterialPoint& currentSample = samples[sampleIndex]; + physx::PxHeightFieldSample& currentPhysxSample = physxSamples[sampleIndex]; + AZ_Assert(currentSample.m_materialIndex < physxMaximumMaterialIndex, "MaterialIndex must be less than 128"); + currentPhysxSample.height = azlossy_cast( + AZ::GetClamp(currentSample.m_height, minHeightBounds, maxHeightBounds) * scaleFactor); + if (lastRowIndex || lastColumnIndex) + { + // In PhysX, the material indices refer to the quad down and to the right of the sample. + // If we're in the last row or last column, there aren't any quads down or to the right, + // so just clear these out. + currentPhysxSample.materialIndex0 = 0; + currentPhysxSample.materialIndex1 = 0; + } + else + { + // Our source data is providing one material index per vertex, but PhysX wants one material index + // per triangle. The heuristic that we'll go with for selecting the material index is to choose + // the material for the vertex that's not on the diagonal of each triangle. + // Ex: A *---* B + // | / | For this, we'll use A for index0 and D for index1. + // C *---* D + // + // Ex: A *---* B + // | \ | For this, we'll use C for index0 and B for index1. + // C *---* D + // + // This is a pretty arbitrary choice, so the heuristic might need to be revisited over time if this + // causes incorrect or unpredictable physics material mappings. + + switch (currentSample.m_quadMeshType) + { + case Physics::QuadMeshType::SubdivideUpperLeftToBottomRight: + currentPhysxSample.materialIndex0 = samples[GetIndex(row + 1, col)].m_materialIndex; + currentPhysxSample.materialIndex1 = samples[GetIndex(row, col + 1)].m_materialIndex; + // Set the tesselation flag to say that we need to go from UL to BR + currentPhysxSample.materialIndex0.setBit(); + break; + case Physics::QuadMeshType::SubdivideBottomLeftToUpperRight: + currentPhysxSample.materialIndex0 = currentSample.m_materialIndex; + currentPhysxSample.materialIndex1 = samples[GetIndex(row + 1, col + 1)].m_materialIndex; + break; + case Physics::QuadMeshType::Hole: + currentPhysxSample.materialIndex0 = physx::PxHeightFieldMaterial::eHOLE; + currentPhysxSample.materialIndex1 = physx::PxHeightFieldMaterial::eHOLE; + break; + default: + AZ_Warning("PhysX Heightfield", false, "Unhandled case in CreatePxGeometryFromConfig"); + currentPhysxSample.materialIndex0 = 0; + currentPhysxSample.materialIndex1 = 0; + break; + } + } + } + } + + SystemRequestsBus::BroadcastResult(heightfield, &SystemRequests::CreateHeightField, physxSamples.data(), numRows, numCols); + } + if (heightfield) + { + heightfieldConfig.SetCachedNativeHeightfield(heightfield); + + physx::PxHeightFieldGeometry hfGeom(heightfield, physx::PxMeshGeometryFlags(), heightScale, rowScale, colScale); + + pxGeometry.storeAny(hfGeom); + } + } + bool CreatePxGeometryFromConfig(const Physics::ShapeConfiguration& shapeConfiguration, physx::PxGeometryHolder& pxGeometry) { if (!shapeConfiguration.m_scale.IsGreaterThan(AZ::Vector3::CreateZero())) @@ -170,6 +302,14 @@ namespace PhysX "Please iterate over m_colliderShapes in the asset and call this function for each of them."); return false; } + case Physics::ShapeType::Heightfield: + { + const Physics::HeightfieldShapeConfiguration& heightfieldConfig = + static_cast(shapeConfiguration); + + CreatePxGeometryFromHeightfield(heightfieldConfig, pxGeometry); + break; + } default: AZ_Warning("PhysX Rigid Body", false, "Shape not supported in PhysX. Shape Type: %d", shapeType); return false; @@ -219,6 +359,26 @@ namespace PhysX physx::PxQuat pxQuat(AZ::Constants::HalfPi, physx::PxVec3(0.0f, 1.0f, 0.0f)); shape->setLocalPose(physx::PxTransform(pxQuat)); } + else if (pxGeomHolder.getType() == physx::PxGeometryType::eHEIGHTFIELD) + { + const Physics::HeightfieldShapeConfiguration& heightfieldConfig = + static_cast(shapeConfiguration); + + // PhysX heightfields have the origin at the corner, not the center, so add an offset to the passed-in transform + // to account for this difference. + const AZ::Vector2 gridSpacing = heightfieldConfig.GetGridResolution(); + AZ::Vector3 offset( + -(gridSpacing.GetX() * heightfieldConfig.GetNumColumns() / 2.0f), + -(gridSpacing.GetY() * heightfieldConfig.GetNumRows() / 2.0f), + 0.0f); + + // PhysX heightfields are always defined to have the height in the Y direction, not the Z direction, so we need + // to provide additional rotations to make it Z-up. + physx::PxQuat pxQuat = PxMathConvert( + AZ::Quaternion::CreateFromEulerAnglesRadians(AZ::Vector3(AZ::Constants::HalfPi, AZ::Constants::HalfPi, 0.0f))); + physx::PxTransform pxHeightfieldTransform = physx::PxTransform(PxMathConvert(offset), pxQuat); + shape->setLocalPose(pxHeightfieldTransform); + } // Handle a possible misconfiguration when a shape is set to be both simulated & trigger. This is illegal in PhysX. shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, colliderConfiguration.m_isSimulated && !colliderConfiguration.m_isTrigger); @@ -1357,6 +1517,39 @@ namespace PhysX return entityWorldTransformWithoutScale * jointLocalTransformWithoutScale; } + + void InitHeightfieldShapeConfiguration(AZ::EntityId entityId, Physics::HeightfieldShapeConfiguration& configuration) + { + configuration = Physics::HeightfieldShapeConfiguration(); + + AZ::Vector2 gridSpacing(1.0f); + Physics::HeightfieldProviderRequestsBus::EventResult( + gridSpacing, entityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSpacing); + + configuration.SetGridResolution(gridSpacing); + + int32_t numRows = 0; + int32_t numColumns = 0; + Physics::HeightfieldProviderRequestsBus::Event( + entityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows); + + configuration.SetNumRows(numRows); + configuration.SetNumColumns(numColumns); + + float minHeightBounds = 0.0f; + float maxHeightBounds = 0.0f; + Physics::HeightfieldProviderRequestsBus::Event( + entityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldHeightBounds, minHeightBounds, maxHeightBounds); + + configuration.SetMinHeightBounds(minHeightBounds); + configuration.SetMaxHeightBounds(maxHeightBounds); + + AZStd::vector samples; + Physics::HeightfieldProviderRequestsBus::EventResult( + samples, entityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); + + configuration.SetSamples(samples); + } } // namespace Utils namespace ReflectionUtils diff --git a/Gems/PhysX/Code/Source/Utils.h b/Gems/PhysX/Code/Source/Utils.h index ed63605bc8..54a81bb28d 100644 --- a/Gems/PhysX/Code/Source/Utils.h +++ b/Gems/PhysX/Code/Source/Utils.h @@ -188,6 +188,8 @@ namespace PhysX //! Returns defaultValue if the input is infinite or NaN, otherwise returns the input unchanged. const AZ::Vector3& Sanitize(const AZ::Vector3& input, const AZ::Vector3& defaultValue = AZ::Vector3::CreateZero()); + void InitHeightfieldShapeConfiguration(AZ::EntityId entityId, Physics::HeightfieldShapeConfiguration& configuration); + namespace Geometry { using PointList = AZStd::vector; diff --git a/Gems/PhysX/Code/physx_editor_files.cmake b/Gems/PhysX/Code/physx_editor_files.cmake index 5c0c038976..1b22c0ce99 100644 --- a/Gems/PhysX/Code/physx_editor_files.cmake +++ b/Gems/PhysX/Code/physx_editor_files.cmake @@ -27,6 +27,8 @@ set(FILES Source/EditorFixedJointComponent.h Source/EditorHingeJointComponent.cpp Source/EditorHingeJointComponent.h + Source/EditorHeightfieldColliderComponent.cpp + Source/EditorHeightfieldColliderComponent.h Source/EditorJointComponent.cpp Source/EditorJointComponent.h Source/Pipeline/MeshExporter.cpp diff --git a/Gems/PhysX/Code/physx_files.cmake b/Gems/PhysX/Code/physx_files.cmake index 654f1d9767..efefaf3105 100644 --- a/Gems/PhysX/Code/physx_files.cmake +++ b/Gems/PhysX/Code/physx_files.cmake @@ -35,6 +35,8 @@ set(FILES Source/MeshColliderComponent.h Source/BoxColliderComponent.h Source/BoxColliderComponent.cpp + Source/HeightfieldColliderComponent.h + Source/HeightfieldColliderComponent.cpp Source/SphereColliderComponent.h Source/SphereColliderComponent.cpp Source/CapsuleColliderComponent.h diff --git a/Gems/Terrain/Code/CMakeLists.txt b/Gems/Terrain/Code/CMakeLists.txt index 442093a885..4b2f32e172 100644 --- a/Gems/Terrain/Code/CMakeLists.txt +++ b/Gems/Terrain/Code/CMakeLists.txt @@ -111,6 +111,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest AZ::AzFramework Gem::LmbrCentral.Mocks + Gem::GradientSignal.Mocks Gem::Terrain.Mocks Gem::Terrain.Static ) diff --git a/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h b/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h index ea0413be9a..bb1fa5683a 100644 --- a/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h +++ b/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h @@ -10,11 +10,12 @@ #include #include +#include #include +#include namespace UnitTest { - class MockTerrainSystemService : private Terrain::TerrainSystemServiceRequestBus::Handler { public: @@ -69,11 +70,7 @@ namespace UnitTest Terrain::TerrainAreaHeightRequestBus::Handler::BusDisconnect(); } - MOCK_METHOD3(GetHeight, void( - const AZ::Vector3& inPosition, - AZ::Vector3& outPosition, - bool& terrainExists)); - + MOCK_METHOD3(GetHeight, void(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists)); }; class MockTerrainSpawnerRequests : public Terrain::TerrainSpawnerRequestBus::Handler @@ -92,4 +89,35 @@ namespace UnitTest MOCK_METHOD2(GetPriority, void(AZ::u32& outLayer, AZ::u32& outPriority)); MOCK_METHOD0(GetUseGroundPlane, bool()); }; -} + + class MockTerrainDataRequests : public AzFramework::Terrain::TerrainDataRequestBus::Handler + { + public: + MockTerrainDataRequests() + { + AzFramework::Terrain::TerrainDataRequestBus::Handler::BusConnect(); + } + + ~MockTerrainDataRequests() + { + AzFramework::Terrain::TerrainDataRequestBus::Handler::BusDisconnect(); + } + + MOCK_CONST_METHOD0(GetTerrainHeightQueryResolution, AZ::Vector2()); + MOCK_METHOD1(SetTerrainHeightQueryResolution, void(AZ::Vector2)); + MOCK_CONST_METHOD0(GetTerrainAabb, AZ::Aabb()); + MOCK_METHOD1(SetTerrainAabb, void(const AZ::Aabb&)); + MOCK_CONST_METHOD3(GetHeight, float(AZ::Vector3, Sampler, bool*)); + MOCK_CONST_METHOD4(GetHeightFromFloats, float(float, float, Sampler, bool*)); + MOCK_CONST_METHOD3(GetMaxSurfaceWeight, AzFramework::SurfaceData::SurfaceTagWeight(AZ::Vector3, Sampler, bool*)); + MOCK_CONST_METHOD3(GetMaxSurfaceWeightFromVector2, AzFramework::SurfaceData::SurfaceTagWeight(const AZ::Vector2&, Sampler, bool*)); + MOCK_CONST_METHOD4(GetMaxSurfaceWeightFromFloats, AzFramework::SurfaceData::SurfaceTagWeight(float, float, Sampler, bool*)); + MOCK_CONST_METHOD4(GetSurfaceWeights, void(const AZ::Vector3&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*)); + MOCK_CONST_METHOD4(GetSurfaceWeightsFromVector2, void(const AZ::Vector2&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*)); + MOCK_CONST_METHOD5(GetSurfaceWeightsFromFloats, void(float, float, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*)); + MOCK_CONST_METHOD3(GetMaxSurfaceName, const char*(AZ::Vector3, Sampler, bool*)); + MOCK_CONST_METHOD3(GetIsHoleFromFloats, bool(float, float, Sampler)); + MOCK_CONST_METHOD3(GetNormal, AZ::Vector3(AZ::Vector3, Sampler, bool*)); + MOCK_CONST_METHOD4(GetNormalFromFloats, AZ::Vector3(float, float, Sampler, bool*)); + }; +} // namespace UnitTest diff --git a/Gems/Terrain/Code/Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h b/Gems/Terrain/Code/Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h new file mode 100644 index 0000000000..c5a04b4265 --- /dev/null +++ b/Gems/Terrain/Code/Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include + +namespace UnitTest +{ + class MockTerrainAreaSurfaceRequestBus : public Terrain::TerrainAreaSurfaceRequestBus::Handler + { + public: + MockTerrainAreaSurfaceRequestBus(AZ::EntityId entityId) + { + Terrain::TerrainAreaSurfaceRequestBus::Handler::BusConnect(entityId); + } + + ~MockTerrainAreaSurfaceRequestBus() + { + Terrain::TerrainAreaSurfaceRequestBus::Handler::BusDisconnect(); + } + + MOCK_METHOD0(Activate, void()); + MOCK_METHOD0(Deactivate, void()); + MOCK_CONST_METHOD2(GetSurfaceWeights, void(const AZ::Vector3&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&)); + }; + +} // namespace UnitTest diff --git a/Gems/Terrain/Code/Mocks/Terrain/MockTerrainLayerSpawner.h b/Gems/Terrain/Code/Mocks/Terrain/MockTerrainLayerSpawner.h new file mode 100644 index 0000000000..d0551d0881 --- /dev/null +++ b/Gems/Terrain/Code/Mocks/Terrain/MockTerrainLayerSpawner.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include + +namespace UnitTest +{ + class MockTerrainLayerSpawnerComponent + : public AZ::Component + { + public: + AZ_COMPONENT(MockTerrainLayerSpawnerComponent, "{9F27C980-9826-4063-86D8-E981C1E842A3}"); + + static void Reflect([[maybe_unused]] AZ::ReflectContext* context) + { + } + + void Activate() override + { + } + + void Deactivate() override + { + } + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainAreaService")); + } + }; + +} //namespace UnitTest diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 4ea16018d1..01ad0bb77f 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -65,7 +65,7 @@ namespace Terrain void TerrainHeightGradientListComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) { services.push_back(AZ_CRC_CE("TerrainAreaService")); - services.push_back(AZ_CRC_CE("BoxShapeService")); + services.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); } void TerrainHeightGradientListComponent::Reflect(AZ::ReflectContext* context) diff --git a/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp index 245c98ccac..00ed9c004f 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp @@ -102,7 +102,6 @@ namespace Terrain void TerrainLayerSpawnerComponent::Activate() { - AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId()); LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); TerrainSpawnerRequestBus::Handler::BusConnect(GetEntityId()); @@ -114,8 +113,6 @@ namespace Terrain TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::UnregisterArea, GetEntityId()); TerrainSpawnerRequestBus::Handler::BusDisconnect(); LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); - AZ::TransformNotificationBus::Handler::BusDisconnect(); - } bool TerrainLayerSpawnerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) @@ -138,13 +135,12 @@ namespace Terrain return false; } - void TerrainLayerSpawnerComponent::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, [[maybe_unused]] const AZ::Transform& world) - { - RefreshArea(); - } - void TerrainLayerSpawnerComponent::OnShapeChanged([[maybe_unused]] ShapeChangeReasons changeReason) { + // This will notify us of both shape changes and transform changes. + // It's important to use this event for transform changes instead of listening to OnTransformChanged, because we need to guarantee + // the shape has received the transform change message and updated its internal state before passing it along to us. + RefreshArea(); } diff --git a/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.h b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.h index c7398bf93e..683f9e9f06 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.h @@ -18,7 +18,6 @@ #include #include -#include #include #include @@ -56,7 +55,6 @@ namespace Terrain class TerrainLayerSpawnerComponent : public AZ::Component - , private AZ::TransformNotificationBus::Handler , private LmbrCentral::ShapeComponentNotificationsBus::Handler , private Terrain::TerrainSpawnerRequestBus::Handler { @@ -81,10 +79,6 @@ namespace Terrain bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; protected: - ////////////////////////////////////////////////////////////////////////// - // AZ::TransformNotificationBus::Handler - void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; - // ShapeComponentNotificationsBus void OnShapeChanged(ShapeChangeReasons changeReason) override; diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp new file mode 100644 index 0000000000..e9c235c1bd --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Terrain +{ + void TerrainPhysicsColliderConfig::Reflect(AZ::ReflectContext* context) + { + if (auto serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(1) + ; + + if (auto edit = serialize->GetEditContext()) + { + edit->Class( + "Terrain Physics Collider Component", + "Provides terrain data to a physics collider with configurable surface mappings.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true); + } + } + } + + void TerrainPhysicsColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("PhysicsHeightfieldProviderService")); + } + + void TerrainPhysicsColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("PhysicsHeightfieldProviderService")); + } + + void TerrainPhysicsColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); + } + + void TerrainPhysicsColliderComponent::Reflect(AZ::ReflectContext* context) + { + TerrainPhysicsColliderConfig::Reflect(context); + + if (auto serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0) + ->Field("Configuration", &TerrainPhysicsColliderComponent::m_configuration) + ; + } + } + + TerrainPhysicsColliderComponent::TerrainPhysicsColliderComponent(const TerrainPhysicsColliderConfig& configuration) + : m_configuration(configuration) + { + } + + TerrainPhysicsColliderComponent::TerrainPhysicsColliderComponent() + { + + } + + void TerrainPhysicsColliderComponent::Activate() + { + const auto entityId = GetEntityId(); + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(entityId); + Physics::HeightfieldProviderRequestsBus::Handler::BusConnect(entityId); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + NotifyListenersOfHeightfieldDataChange(); + } + + void TerrainPhysicsColliderComponent::Deactivate() + { + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + Physics::HeightfieldProviderRequestsBus::Handler ::BusDisconnect(); + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); + } + + bool TerrainPhysicsColliderComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainPhysicsColliderComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + void TerrainPhysicsColliderComponent::NotifyListenersOfHeightfieldDataChange() + { + AZ::Aabb worldSize = AZ::Aabb::CreateNull(); + + LmbrCentral::ShapeComponentRequestsBus::EventResult( + worldSize, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + + Physics::HeightfieldProviderNotificationBus::Broadcast( + &Physics::HeightfieldProviderNotificationBus::Events::OnHeightfieldDataChanged, worldSize); + } + + void TerrainPhysicsColliderComponent::OnShapeChanged([[maybe_unused]] ShapeChangeReasons changeReason) + { + // This will notify us of both shape changes and transform changes. + // It's important to use this event for transform changes instead of listening to OnTransformChanged, because we need to guarantee + // the shape has received the transform change message and updated its internal state before passing it along to us. + + NotifyListenersOfHeightfieldDataChange(); + } + + void TerrainPhysicsColliderComponent::OnTerrainDataCreateEnd() + { + // The terrain system has finished creating itself, so we should now have data for creating a heightfield. + NotifyListenersOfHeightfieldDataChange(); + } + + void TerrainPhysicsColliderComponent::OnTerrainDataDestroyBegin() + { + // The terrain system is starting to destroy itself, so notify listeners of a change since the heightfield + // will no longer have any valid data. + NotifyListenersOfHeightfieldDataChange(); + } + + void TerrainPhysicsColliderComponent::OnTerrainDataChanged( + [[maybe_unused]] const AZ::Aabb& dirtyRegion, [[maybe_unused]] TerrainDataChangedMask dataChangedMask) + { + NotifyListenersOfHeightfieldDataChange(); + } + + AZ::Aabb TerrainPhysicsColliderComponent::GetHeightfieldAabb() const + { + AZ::Aabb worldSize = AZ::Aabb::CreateNull(); + + LmbrCentral::ShapeComponentRequestsBus::EventResult( + worldSize, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + + auto vector2Floor = [](const AZ::Vector2& in) + { + return AZ::Vector2(floor(in.GetX()), floor(in.GetY())); + }; + auto vector2Ceil = [](const AZ::Vector2& in) + { + return AZ::Vector2(ceil(in.GetX()), ceil(in.GetY())); + }; + + const AZ::Vector2 gridResolution = GetHeightfieldGridSpacing(); + const AZ::Vector3 boundsMin = worldSize.GetMin(); + const AZ::Vector3 boundsMax = worldSize.GetMax(); + + const AZ::Vector2 gridMinBoundLower = vector2Floor(AZ::Vector2(boundsMin) / gridResolution) * gridResolution; + const AZ::Vector2 gridMaxBoundUpper = vector2Ceil(AZ::Vector2(boundsMax) / gridResolution) * gridResolution; + + return AZ::Aabb::CreateFromMinMaxValues( + gridMinBoundLower.GetX(), gridMinBoundLower.GetY(), boundsMin.GetZ(), + gridMaxBoundUpper.GetX(), gridMaxBoundUpper.GetY(), boundsMax.GetZ() + ); + } + + void TerrainPhysicsColliderComponent::GetHeightfieldHeightBounds(float& minHeightBounds, float& maxHeightBounds) const + { + AZ::Aabb heightfieldAabb = GetHeightfieldAabb(); + + // Because our terrain heights are relative to the center of the bounding box, the min and max allowable heights are also + // relative to the center. They are also clamped to the size of the bounding box. + minHeightBounds = -(heightfieldAabb.GetZExtent() / 2.0f); + maxHeightBounds = heightfieldAabb.GetZExtent() / 2.0f; + } + + AZ::Transform TerrainPhysicsColliderComponent::GetHeightfieldTransform() const + { + // We currently don't support rotation of terrain heightfields. + AZ::Vector3 translate; + AZ::TransformBus::EventResult(translate, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation); + + AZ::Transform transform = AZ::Transform::CreateTranslation(translate); + + return transform; + } + + void TerrainPhysicsColliderComponent::GenerateHeightsInBounds(AZStd::vector& heights) const + { + const AZ::Vector2 gridResolution = GetHeightfieldGridSpacing(); + + AZ::Aabb worldSize = GetHeightfieldAabb(); + + const float worldCenterZ = worldSize.GetCenter().GetZ(); + + int32_t gridWidth, gridHeight; + GetHeightfieldGridSize(gridWidth, gridHeight); + + heights.clear(); + heights.reserve(gridWidth * gridHeight); + + for (int32_t row = 0; row < gridHeight; row++) + { + const float y = row * gridResolution.GetY() + worldSize.GetMin().GetY(); + for (int32_t col = 0; col < gridWidth; col++) + { + const float x = col * gridResolution.GetX() + worldSize.GetMin().GetX(); + float height = 0.0f; + + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + height, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y, + AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, nullptr); + + heights.emplace_back(height - worldCenterZ); + } + } + } + + void TerrainPhysicsColliderComponent::GenerateHeightsAndMaterialsInBounds( + AZStd::vector& heightMaterials) const + { + const AZ::Vector2 gridResolution = GetHeightfieldGridSpacing(); + + AZ::Aabb worldSize = GetHeightfieldAabb(); + + const float worldCenterZ = worldSize.GetCenter().GetZ(); + const float worldHeightBoundsMin = worldSize.GetMin().GetZ(); + const float worldHeightBoundsMax = worldSize.GetMax().GetZ(); + + int32_t gridWidth, gridHeight; + GetHeightfieldGridSize(gridWidth, gridHeight); + + heightMaterials.clear(); + heightMaterials.reserve(gridWidth * gridHeight); + + for (int32_t row = 0; row < gridHeight; row++) + { + const float y = row * gridResolution.GetY() + worldSize.GetMin().GetY(); + for (int32_t col = 0; col < gridWidth; col++) + { + const float x = col * gridResolution.GetX() + worldSize.GetMin().GetX(); + float height = 0.0f; + + bool terrainExists = true; + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + height, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y, + AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists); + + // Any heights that fall outside the range of our bounding box will get turned into holes. + if ((height < worldHeightBoundsMin) || (height > worldHeightBoundsMax)) + { + height = worldHeightBoundsMin; + terrainExists = false; + } + + Physics::HeightMaterialPoint point; + point.m_height = height - worldCenterZ; + point.m_quadMeshType = terrainExists ? Physics::QuadMeshType::SubdivideUpperLeftToBottomRight : Physics::QuadMeshType::Hole; + heightMaterials.emplace_back(point); + } + } + } + + AZ::Vector2 TerrainPhysicsColliderComponent::GetHeightfieldGridSpacing() const + { + AZ::Vector2 gridResolution = AZ::Vector2(1.0f); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + gridResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + + return gridResolution; + } + + void TerrainPhysicsColliderComponent::GetHeightfieldGridSize(int32_t& numColumns, int32_t& numRows) const + { + const AZ::Vector2 gridResolution = GetHeightfieldGridSpacing(); + const AZ::Aabb bounds = GetHeightfieldAabb(); + + numColumns = aznumeric_cast((bounds.GetMax().GetX() - bounds.GetMin().GetX()) / gridResolution.GetX()); + numRows = aznumeric_cast((bounds.GetMax().GetY() - bounds.GetMin().GetY()) / gridResolution.GetY()); + } + + AZStd::vector TerrainPhysicsColliderComponent::GetMaterialList() const + { + return AZStd::vector(); + } + + AZStd::vector TerrainPhysicsColliderComponent::GetHeights() const + { + AZStd::vector heights; + GenerateHeightsInBounds(heights); + + return heights; + } + + AZStd::vector TerrainPhysicsColliderComponent::GetHeightsAndMaterials() const + { + AZStd::vector heightMaterials; + GenerateHeightsAndMaterialsInBounds(heightMaterials); + + return heightMaterials; + } +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h new file mode 100644 index 0000000000..e268223689 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace Terrain +{ + class TerrainPhysicsColliderConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainPhysicsColliderConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainPhysicsColliderConfig, "{E9EADB8F-C3A5-4B9C-A62D-2DBC86B4CE59}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + + }; + + + class TerrainPhysicsColliderComponent + : public AZ::Component + , public Physics::HeightfieldProviderRequestsBus::Handler + , protected LmbrCentral::ShapeComponentNotificationsBus::Handler + , protected AzFramework::Terrain::TerrainDataNotificationBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainPhysicsColliderComponent, "{33C20287-1D37-44D0-96A0-2C3766E23624}"); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainPhysicsColliderComponent(const TerrainPhysicsColliderConfig& configuration); + TerrainPhysicsColliderComponent(); + ~TerrainPhysicsColliderComponent() = default; + + // HeightfieldProviderRequestsBus + AZ::Vector2 GetHeightfieldGridSpacing() const override; + void GetHeightfieldGridSize(int32_t& numColumns, int32_t& numRows) const override; + void GetHeightfieldHeightBounds(float& minHeightBounds, float& maxHeightBounds) const override; + AZ::Aabb GetHeightfieldAabb() const override; + AZ::Transform GetHeightfieldTransform() const override; + AZStd::vector GetMaterialList() const override; + AZStd::vector GetHeights() const override; + AZStd::vector GetHeightsAndMaterials() const override; + + protected: + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + void GenerateHeightsInBounds(AZStd::vector& heights) const; + void GenerateHeightsAndMaterialsInBounds(AZStd::vector& heightMaterials) const; + + void NotifyListenersOfHeightfieldDataChange(); + + // ShapeComponentNotificationsBus + void OnShapeChanged(ShapeChangeReasons changeReason) override; + + void OnTerrainDataCreateEnd() override; + void OnTerrainDataDestroyBegin() override; + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + + private: + TerrainPhysicsColliderConfig m_configuration; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainPhysicsColliderComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainPhysicsColliderComponent.cpp new file mode 100644 index 0000000000..6df6c3b440 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainPhysicsColliderComponent.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace Terrain +{ + void EditorTerrainPhysicsColliderComponent::Reflect(AZ::ReflectContext* context) + { + // Call ReflectSubClass in EditorWrappedComponentBase to handle all the boilerplate reflection. + BaseClassType::ReflectSubClass( + context, 1, + &LmbrCentral::EditorWrappedComponentBaseVersionConverter + ); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainPhysicsColliderComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainPhysicsColliderComponent.h new file mode 100644 index 0000000000..d2254161a0 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainPhysicsColliderComponent.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace Terrain +{ + class EditorTerrainPhysicsColliderComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainPhysicsColliderComponent, "{C43FAB8F-3968-46A6-920E-E84AEDED3DF5}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + static constexpr auto s_categoryName = "Terrain"; + static constexpr auto s_componentName = "Terrain Physics Heightfield Collider"; + static constexpr auto s_componentDescription = "Provides terrain data to a physics collider in the form of a heightfield and surface->material mapping."; + static constexpr auto s_icon = "Editor/Icons/Components/TerrainLayerSpawner.svg"; + static constexpr auto s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainLayerSpawner.svg"; + static constexpr auto s_helpUrl = ""; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp index 6fd8979e4a..0ea822e8b5 100644 --- a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp +++ b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ namespace Terrain Terrain::EditorTerrainSurfaceMaterialsListComponent::CreateDescriptor(), Terrain::EditorTerrainWorldComponent::CreateDescriptor(), Terrain::EditorTerrainWorldDebuggerComponent::CreateDescriptor(), + Terrain::EditorTerrainPhysicsColliderComponent::CreateDescriptor(), Terrain::EditorTerrainWorldRendererComponent::CreateDescriptor(), }); diff --git a/Gems/Terrain/Code/Source/TerrainModule.cpp b/Gems/Terrain/Code/Source/TerrainModule.cpp index c3a7890565..2524f3684c 100644 --- a/Gems/Terrain/Code/Source/TerrainModule.cpp +++ b/Gems/Terrain/Code/Source/TerrainModule.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,7 @@ namespace Terrain TerrainMacroMaterialComponent::CreateDescriptor(), TerrainSurfaceGradientListComponent::CreateDescriptor(), TerrainSurfaceDataSystemComponent::CreateDescriptor(), + TerrainPhysicsColliderComponent::CreateDescriptor() }); } diff --git a/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp b/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp index ee4b18e2fb..0ca5e7d99a 100644 --- a/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp +++ b/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -195,8 +196,10 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerTransformChangedUpdatesTerrainSyst m_entity->Activate(); - AZ::TransformNotificationBus::Event( - m_entity->GetId(), &AZ::TransformNotificationBus::Events::OnTransformChanged, AZ::Transform(), AZ::Transform()); + // The component gets transform change notifications via the shape bus. + LmbrCentral::ShapeComponentNotificationsBus::Event( + m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged, + LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::TransformChanged); m_entity->Deactivate(); } diff --git a/Gems/Terrain/Code/Tests/TerrainHeightGradientListTests.cpp b/Gems/Terrain/Code/Tests/TerrainHeightGradientListTests.cpp new file mode 100644 index 0000000000..c314e4e968 --- /dev/null +++ b/Gems/Terrain/Code/Tests/TerrainHeightGradientListTests.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::NiceMock; +using ::testing::Return; + +class TerrainHeightGradientListComponentTest : public ::testing::Test +{ +protected: + AZ::ComponentApplication m_app; + + AZStd::unique_ptr m_entity; + + void SetUp() override + { + AZ::ComponentApplication::Descriptor appDesc; + appDesc.m_memoryBlocksByteSize = 20 * 1024 * 1024; + appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS; + appDesc.m_stackRecordLevels = 20; + + m_app.Create(appDesc); + } + + void TearDown() override + { + m_app.Destroy(); + } + + void CreateEntity() + { + m_entity = AZStd::make_unique(); + ASSERT_TRUE(m_entity); + + // Create the required box component. + UnitTest::MockAxisAlignedBoxShapeComponent* boxComponent = m_entity->CreateComponent(); + m_app.RegisterComponentDescriptor(boxComponent->CreateDescriptor()); + + // Create the TerrainHeightGradientListComponent with an entity in its configuration. + Terrain::TerrainHeightGradientListConfig config; + config.m_gradientEntities.push_back(m_entity->GetId()); + + Terrain::TerrainHeightGradientListComponent* heightGradientListComponent = m_entity->CreateComponent(config); + m_app.RegisterComponentDescriptor(heightGradientListComponent->CreateDescriptor()); + + // Create a MockTerrainLayerSpawnerComponent to provide the required TerrainAreaService. + UnitTest::MockTerrainLayerSpawnerComponent* layerSpawner = m_entity->CreateComponent(); + m_app.RegisterComponentDescriptor(layerSpawner->CreateDescriptor()); + + m_entity->Init(); + } +}; + +TEST_F(TerrainHeightGradientListComponentTest, ActivateEntityActivateSuccess) +{ + // Check that the entity activates. + CreateEntity(); + + m_entity->Activate(); + EXPECT_EQ(m_entity->GetState(), AZ::Entity::State::Active); + + m_entity.reset(); +} + +TEST_F(TerrainHeightGradientListComponentTest, TerrainHeightGradientRefreshesTerrainSystem) +{ + // Check that the HeightGradientListComponent informs the TerrainSystem when the composition changes. + CreateEntity(); + + m_entity->Activate(); + + NiceMock terrainSystem; + + // As the TerrainHeightGradientListComponent subscribes to the dependency monitor, RefreshArea will be called twice: + // once due to OnCompositionChanged being picked up by the the dependency monitor and resending the notification, + // and once when the HeightGradientListComponent gets the OnCompositionChanged directly through the DependencyNotificationBus. + EXPECT_CALL(terrainSystem, RefreshArea(_)).Times(2); + + LmbrCentral::DependencyNotificationBus::Event(m_entity->GetId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged); + + // Stop the EXPECT_CALL check now, as OnCompositionChanged will get called twice again during the reset. + Mock::VerifyAndClearExpectations(&terrainSystem); + + m_entity.reset(); +} + +TEST_F(TerrainHeightGradientListComponentTest, TerrainHeightGradientListReturnsHeights) +{ + // Check that the HeightGradientListComponent returns expected height values. + CreateEntity(); + + NiceMock heightfieldRequestBus(m_entity->GetId()); + + m_entity->Activate(); + + const float mockGradientValue = 0.25f; + NiceMock gradientRequests(m_entity->GetId()); + ON_CALL(gradientRequests, GetValue).WillByDefault(Return(mockGradientValue)); + + // Setup a mock to provide the encompassing Aabb to the HeightGradientListComponent. + const float min = 0.0f; + const float max = 1000.0f; + const AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(min), AZ::Vector3(max)); + NiceMock mockShapeRequests(m_entity->GetId()); + ON_CALL(mockShapeRequests, GetEncompassingAabb).WillByDefault(Return(aabb)); + + const float worldMax = 10000.0f; + const AZ::Aabb worldAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(min), AZ::Vector3(worldMax)); + NiceMock mockterrainDataRequests; + ON_CALL(mockterrainDataRequests, GetTerrainHeightQueryResolution).WillByDefault(Return(AZ::Vector2(1.0f))); + ON_CALL(mockterrainDataRequests, GetTerrainAabb).WillByDefault(Return(worldAabb)); + + // Ensure the cached values in the HeightGradientListComponent are up to date. + LmbrCentral::DependencyNotificationBus::Event(m_entity->GetId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged); + + const AZ::Vector3 inPosition = AZ::Vector3::CreateZero(); + AZ::Vector3 outPosition = AZ::Vector3::CreateZero(); + bool terrainExists = false; + Terrain::TerrainAreaHeightRequestBus::Event(m_entity->GetId(), &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists); + + const float height = outPosition.GetZ(); + + EXPECT_NEAR(height, mockGradientValue * max, 0.01f); + + m_entity.reset(); +} + diff --git a/Gems/Terrain/Code/Tests/TerrainPhysicsColliderTests.cpp b/Gems/Terrain/Code/Tests/TerrainPhysicsColliderTests.cpp new file mode 100644 index 0000000000..d1deca897c --- /dev/null +++ b/Gems/Terrain/Code/Tests/TerrainPhysicsColliderTests.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +using ::testing::NiceMock; +using ::testing::AtLeast; +using ::testing::_; +using ::testing::Return; + +class TerrainPhysicsColliderComponentTest + : public ::testing::Test +{ +protected: + AZ::ComponentApplication m_app; + + AZStd::unique_ptr m_entity; + Terrain::TerrainPhysicsColliderComponent* m_colliderComponent; + UnitTest::MockAxisAlignedBoxShapeComponent* m_boxComponent; + + void SetUp() override + { + AZ::ComponentApplication::Descriptor appDesc; + appDesc.m_memoryBlocksByteSize = 20 * 1024 * 1024; + appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS; + appDesc.m_stackRecordLevels = 20; + + m_app.Create(appDesc); + } + + void TearDown() override + { + m_app.Destroy(); + } + + void CreateEntity() + { + m_entity = AZStd::make_unique(); + ASSERT_TRUE(m_entity); + + m_entity->Init(); + } + + void AddTerrainPhysicsColliderAndShapeComponentToEntity() + { + m_boxComponent = m_entity->CreateComponent(); + m_app.RegisterComponentDescriptor(m_boxComponent->CreateDescriptor()); + + m_colliderComponent = m_entity->CreateComponent(Terrain::TerrainPhysicsColliderConfig()); + m_app.RegisterComponentDescriptor(m_colliderComponent->CreateDescriptor()); + } +}; + +TEST_F(TerrainPhysicsColliderComponentTest, ActivateEntityActivateSuccess) +{ + // Check that the entity activates with a collider and the required shape attached. + CreateEntity(); + AddTerrainPhysicsColliderAndShapeComponentToEntity(); + + m_entity->Activate(); + EXPECT_EQ(m_entity->GetState(), AZ::Entity::State::Active); + + m_entity.reset(); +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderTransformChangedNotifiesHeightfieldBus) +{ + // Check that the HeightfieldBus is notified when the transform of the entity changes. + CreateEntity(); + + AddTerrainPhysicsColliderAndShapeComponentToEntity(); + + m_entity->Activate(); + + NiceMock heightfieldListener(m_entity->GetId()); + EXPECT_CALL(heightfieldListener, OnHeightfieldDataChanged(_)).Times(1); + + // The component gets transform change notifications via the shape bus. + LmbrCentral::ShapeComponentNotificationsBus::Event( + m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged, + LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::TransformChanged); + + m_entity.reset(); +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderShapeChangedNotifiesHeightfieldBus) +{ + // Check that the Heightfield bus is notified when the shape component changes. + CreateEntity(); + + AddTerrainPhysicsColliderAndShapeComponentToEntity(); + + m_entity->Activate(); + + NiceMock heightfieldListener(m_entity->GetId()); + EXPECT_CALL(heightfieldListener, OnHeightfieldDataChanged(_)).Times(1); + + LmbrCentral::ShapeComponentNotificationsBus::Event( + m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged, + LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged); + + m_entity.reset(); +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsAlignedRowBoundsCorrectly) +{ + // Check that the heightfield grid size is correct when the shape bounds match the grid resolution. + CreateEntity(); + + AddTerrainPhysicsColliderAndShapeComponentToEntity(); + + m_entity->Activate(); + + const float boundsMin = 0.0f; + const float boundsMax = 1024.0f; + + NiceMock boxShape(m_entity->GetId()); + const AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(boundsMin), AZ::Vector3(boundsMax)); + ON_CALL(boxShape, GetEncompassingAabb).WillByDefault(Return(bounds)); + + const AZ::Vector2 mockHeightResolution = AZ::Vector2(1.0f); + NiceMock terrainListener; + ON_CALL(terrainListener, GetTerrainHeightQueryResolution).WillByDefault(Return(mockHeightResolution)); + + int32_t cols, rows; + Physics::HeightfieldProviderRequestsBus::Event( + m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, cols, rows); + + // With the bounds set at 0-1024 and a resolution of 1.0, the heightfield grid should be 1024x1024. + EXPECT_EQ(cols, 1024); + EXPECT_EQ(rows, 1024); + + m_entity.reset(); +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderExpandsMinBoundsCorrectly) +{ + // Check that the heightfield grid is correctly expanded if the minimum value of the bounds needs expanding + // to correctly encompass it. + CreateEntity(); + + AddTerrainPhysicsColliderAndShapeComponentToEntity(); + + m_entity->Activate(); + + const float boundsMin = 0.1f; + const float boundsMax = 1024.0f; + + NiceMock boxShape(m_entity->GetId()); + const AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(boundsMin), AZ::Vector3(boundsMax)); + ON_CALL(boxShape, GetEncompassingAabb).WillByDefault(Return(bounds)); + + AZ::Vector2 mockHeightResolution = AZ::Vector2(1.0f); + NiceMock terrainListener; + ON_CALL(terrainListener, GetTerrainHeightQueryResolution).WillByDefault(Return(mockHeightResolution)); + + int32_t cols, rows; + Physics::HeightfieldProviderRequestsBus::Event( + m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, cols, rows); + + // If the heightfield is not expanded to ensure it encompasses the shape bounds, + // the values returned would be 1023. + EXPECT_EQ(cols, 1024); + EXPECT_EQ(rows, 1024); + + m_entity.reset(); +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderExpandsMaxBoundsCorrectly) +{ + // Check that the heightfield grid is correctly expanded if the maximum value of the bounds needs expanding + // to correctly encompass it. + CreateEntity(); + + AddTerrainPhysicsColliderAndShapeComponentToEntity(); + + m_entity->Activate(); + + const float boundsMin = 0.0f; + const float boundsMax = 1023.5f; + + NiceMock boxShape(m_entity->GetId()); + const AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(boundsMin), AZ::Vector3(boundsMax)); + ON_CALL(boxShape, GetEncompassingAabb).WillByDefault(Return(bounds)); + + AZ::Vector2 mockHeightResolution = AZ::Vector2(1.0f); + NiceMock terrainListener; + ON_CALL(terrainListener, GetTerrainHeightQueryResolution).WillByDefault(Return(mockHeightResolution)); + + int32_t cols, rows; + Physics::HeightfieldProviderRequestsBus::Event( + m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, cols, rows); + + // If the heightfield is not expanded to ensure it encompasses the shape bounds, + // the values returned would be 1023. + EXPECT_EQ(cols, 1024); + EXPECT_EQ(rows, 1024); + + m_entity.reset(); +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderGetHeightsReturnsHeights) +{ + // Check that the TerrainPhysicsCollider returns a heightfield of the expected size. + CreateEntity(); + + AddTerrainPhysicsColliderAndShapeComponentToEntity(); + + m_entity->Activate(); + + const float boundsMin = 0.0f; + const float boundsMax = 1024.0f; + + NiceMock boxShape(m_entity->GetId()); + const AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(boundsMin), AZ::Vector3(boundsMax)); + ON_CALL(boxShape, GetEncompassingAabb).WillByDefault(Return(bounds)); + + AZ::Vector2 mockHeightResolution = AZ::Vector2(1.0f); + NiceMock terrainListener; + ON_CALL(terrainListener, GetTerrainHeightQueryResolution).WillByDefault(Return(mockHeightResolution)); + + int32_t cols, rows; + Physics::HeightfieldProviderRequestsBus::Event( + m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, cols, rows); + + AZStd::vector heights; + + Physics::HeightfieldProviderRequestsBus::EventResult( + heights, m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeights); + + EXPECT_EQ(cols, 1024); + EXPECT_EQ(rows, 1024); + EXPECT_EQ(heights.size(), cols * rows); + + m_entity.reset(); +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsRelativeHeightsCorrectly) +{ + // Check that the values stored in the heightfield returned by the TerrainPhysicsCollider are correct. + CreateEntity(); + + AddTerrainPhysicsColliderAndShapeComponentToEntity(); + + m_entity->Activate(); + + const AZ::Vector3 boundsMin = AZ::Vector3(0.0f); + const AZ::Vector3 boundsMax = AZ::Vector3(256.0f, 256.0f, 32768.0f); + + const float mockHeight = 32768.0f; + AZ::Vector2 mockHeightResolution = AZ::Vector2(1.0f); + + NiceMock terrainListener; + ON_CALL(terrainListener, GetHeightFromFloats).WillByDefault(Return(mockHeight)); + ON_CALL(terrainListener, GetTerrainHeightQueryResolution).WillByDefault(Return(mockHeightResolution)); + + // Just return the bounds as setup. This is equivalent to the box being at the origin. + NiceMock boxShape(m_entity->GetId()); + const AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(boundsMin), AZ::Vector3(boundsMax)); + ON_CALL(boxShape, GetEncompassingAabb).WillByDefault(Return(bounds)); + + AZStd::vector heights; + + Physics::HeightfieldProviderRequestsBus::EventResult(heights, m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeights); + + ASSERT_FALSE(heights.empty()); + + const float expectedHeightValue = 16384.0f; + EXPECT_NEAR(heights[0], expectedHeightValue, 0.01f); + + m_entity->Reset(); +} diff --git a/Gems/Terrain/Code/Tests/TerrainSurfaceGradientListTests.cpp b/Gems/Terrain/Code/Tests/TerrainSurfaceGradientListTests.cpp new file mode 100644 index 0000000000..2a645b3f94 --- /dev/null +++ b/Gems/Terrain/Code/Tests/TerrainSurfaceGradientListTests.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +using ::testing::NiceMock; +using ::testing::AtLeast; +using ::testing::_; +using ::testing::Return; + +namespace UnitTest +{ + class TerrainSurfaceGradientListTest : public ::testing::Test + { + protected: + AZ::ComponentApplication m_app; + + AZStd::unique_ptr m_entity; + UnitTest::MockTerrainLayerSpawnerComponent* m_layerSpawnerComponent = nullptr; + AZStd::unique_ptr m_gradientEntity1, m_gradientEntity2; + + const AZStd::string surfaceTag1 = "testtag1"; + const AZStd::string surfaceTag2 = "testtag2"; + + void SetUp() override + { + AZ::ComponentApplication::Descriptor appDesc; + appDesc.m_memoryBlocksByteSize = 20 * 1024 * 1024; + appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS; + appDesc.m_stackRecordLevels = 20; + + m_app.Create(appDesc); + + CreateEntities(); + } + + void TearDown() override + { + m_gradientEntity2.reset(); + m_gradientEntity1.reset(); + m_entity.reset(); + + m_app.Destroy(); + } + + void CreateEntities() + { + m_entity = AZStd::make_unique(); + ASSERT_TRUE(m_entity); + + m_entity->Init(); + + m_gradientEntity1 = AZStd::make_unique(); + ASSERT_TRUE(m_gradientEntity1); + + m_gradientEntity1->Init(); + + m_gradientEntity2 = AZStd::make_unique(); + ASSERT_TRUE(m_gradientEntity2); + + m_gradientEntity2->Init(); + } + + void AddSurfaceGradientListToEntities() + { + m_layerSpawnerComponent = m_entity->CreateComponent(); + m_app.RegisterComponentDescriptor(m_layerSpawnerComponent->CreateDescriptor()); + + Terrain::TerrainSurfaceGradientListConfig config; + + Terrain::TerrainSurfaceGradientMapping mapping1; + mapping1.m_gradientEntityId = m_gradientEntity1->GetId(); + mapping1.m_surfaceTag = SurfaceData::SurfaceTag(surfaceTag1); + config.m_gradientSurfaceMappings.emplace_back(mapping1); + + Terrain::TerrainSurfaceGradientMapping mapping2; + mapping2.m_gradientEntityId = m_gradientEntity2->GetId(); + mapping2.m_surfaceTag = SurfaceData::SurfaceTag(surfaceTag2); + config.m_gradientSurfaceMappings.emplace_back(mapping2); + + Terrain::TerrainSurfaceGradientListComponent* terrainSurfaceGradientListComponent = + m_entity->CreateComponent(config); + m_app.RegisterComponentDescriptor(terrainSurfaceGradientListComponent->CreateDescriptor()); + } + }; + + TEST_F(TerrainSurfaceGradientListTest, SurfaceGradientReturnsSurfaceWeightsInOrder) + { + // When there is more that one surface/weight defined and added to the component, they should all + // be returned in descending weight order. + AddSurfaceGradientListToEntities(); + + m_entity->Activate(); + m_gradientEntity1->Activate(); + m_gradientEntity2->Activate(); + + const float gradient1Value = 0.3f; + NiceMock mockGradientRequests1(m_gradientEntity1->GetId()); + ON_CALL(mockGradientRequests1, GetValue).WillByDefault(Return(gradient1Value)); + + const float gradient2Value = 1.0f; + NiceMock mockGradientRequests2(m_gradientEntity2->GetId()); + ON_CALL(mockGradientRequests2, GetValue).WillByDefault(Return(gradient2Value)); + + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet weightSet; + Terrain::TerrainAreaSurfaceRequestBus::Event( + m_entity->GetId(), &Terrain::TerrainAreaSurfaceRequestBus::Events::GetSurfaceWeights, AZ::Vector3::CreateZero(), weightSet); + + AZ::Crc32 expectedCrcList[] = { AZ::Crc32(surfaceTag2), AZ::Crc32(surfaceTag1) }; + const float expectedWeightList[] = { gradient2Value, gradient1Value }; + + int index = 0; + for (const auto& surfaceWeight : weightSet) + { + EXPECT_EQ(surfaceWeight.m_surfaceType, expectedCrcList[index]); + EXPECT_NEAR(surfaceWeight.m_weight, expectedWeightList[index], 0.01f); + index++; + } + } +} // namespace UnitTest + + diff --git a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp index e4fb0f348e..f273a85e26 100644 --- a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp +++ b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp @@ -13,9 +13,10 @@ #include #include -#include +#include #include +#include #include using ::testing::AtLeast; @@ -25,284 +26,284 @@ using ::testing::IsFalse; using ::testing::Ne; using ::testing::NiceMock; using ::testing::Return; +using ::testing::SetArgReferee; -class TerrainSystemTest : public ::testing::Test +namespace UnitTest { -protected: - // Defines a structure for defining both an XY position and the expected height for that position. - struct HeightTestPoint + class TerrainSystemTest : public ::testing::Test { - AZ::Vector2 m_testLocation; - float m_expectedHeight; + protected: + // Defines a structure for defining both an XY position and the expected height for that position. + struct HeightTestPoint + { + AZ::Vector2 m_testLocation = AZ::Vector2::CreateZero(); + float m_expectedHeight = 0.0f; + }; + + AZ::ComponentApplication m_app; + AZStd::unique_ptr m_terrainSystem; + + AZStd::unique_ptr> m_boxShapeRequests; + AZStd::unique_ptr> m_shapeRequests; + AZStd::unique_ptr> m_terrainAreaHeightRequests; + + void SetUp() override + { + AZ::ComponentApplication::Descriptor appDesc; + appDesc.m_memoryBlocksByteSize = 20 * 1024 * 1024; + appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS; + appDesc.m_stackRecordLevels = 20; + + m_app.Create(appDesc); + } + + void TearDown() override + { + m_terrainSystem.reset(); + m_boxShapeRequests.reset(); + m_shapeRequests.reset(); + m_terrainAreaHeightRequests.reset(); + m_app.Destroy(); + } + + AZStd::unique_ptr CreateEntity() + { + return AZStd::make_unique(); + } + + void ActivateEntity(AZ::Entity* entity) + { + entity->Init(); + EXPECT_EQ(AZ::Entity::State::Init, entity->GetState()); + + entity->Activate(); + EXPECT_EQ(AZ::Entity::State::Active, entity->GetState()); + } + + template + AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config) + { + m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); + return entity->CreateComponent(config); + } + + template + AZ::Component* CreateComponent(AZ::Entity* entity) + { + m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); + return entity->CreateComponent(); + } + + // Create a terrain system with reasonable defaults for testing, but with the ability to override the defaults + // on a test-by-test basis. + void CreateAndActivateTerrainSystem( + AZ::Vector2 queryResolution = AZ::Vector2(1.0f), + AZ::Aabb worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-128.0f), AZ::Vector3(128.0f))) + { + // Create the terrain system and give it one tick to fully initialize itself. + m_terrainSystem = AZStd::make_unique(); + m_terrainSystem->SetTerrainAabb(worldBounds); + m_terrainSystem->SetTerrainHeightQueryResolution(queryResolution); + m_terrainSystem->Activate(); + AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + } + + AZStd::unique_ptr CreateAndActivateMockTerrainLayerSpawner( + const AZ::Aabb& spawnerBox, const AZStd::function& mockHeights) + { + // Create the base entity with a mock box shape, Terrain Layer Spawner, and height provider. + auto entity = CreateEntity(); + CreateComponent(entity.get()); + CreateComponent(entity.get()); + + m_boxShapeRequests = AZStd::make_unique>(entity->GetId()); + m_shapeRequests = AZStd::make_unique>(entity->GetId()); + + // Set up the box shape to return whatever spawnerBox was passed in. + ON_CALL(*m_shapeRequests, GetEncompassingAabb).WillByDefault(Return(spawnerBox)); + + // Set up a mock height provider to use the passed-in mock height function to generate a height. + m_terrainAreaHeightRequests = AZStd::make_unique>(entity->GetId()); + ON_CALL(*m_terrainAreaHeightRequests, GetHeight) + .WillByDefault( + [mockHeights](const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) + { + // By default, set the outPosition to the input position and terrain to always exist. + outPosition = inPosition; + terrainExists = true; + // Let the test function modify these values based on the needs of the specific test. + mockHeights(outPosition, terrainExists); + }); + + ActivateEntity(entity.get()); + return entity; + } }; - AZ::ComponentApplication m_app; - AZStd::unique_ptr m_terrainSystem; - - AZStd::unique_ptr> m_boxShapeRequests; - AZStd::unique_ptr> m_shapeRequests; - AZStd::unique_ptr> m_terrainAreaHeightRequests; - - - void SetUp() override + TEST_F(TerrainSystemTest, TrivialCreateDestroy) { - AZ::ComponentApplication::Descriptor appDesc; - appDesc.m_memoryBlocksByteSize = 20 * 1024 * 1024; - appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS; - appDesc.m_stackRecordLevels = 20; + // Trivially verify that the terrain system can successfully be constructed and destructed without errors. + + m_terrainSystem = AZStd::make_unique(); + } + + TEST_F(TerrainSystemTest, TrivialActivateDeactivate) + { + // Verify that the terrain system can be activated and deactivated without errors. - m_app.Create(appDesc); - } - - void TearDown() override - { - m_terrainSystem.reset(); - m_boxShapeRequests.reset(); - m_shapeRequests.reset(); - m_terrainAreaHeightRequests.reset(); - m_app.Destroy(); - } - - AZStd::unique_ptr CreateEntity() - { - return AZStd::make_unique(); - } - - void ActivateEntity(AZ::Entity* entity) - { - entity->Init(); - EXPECT_EQ(AZ::Entity::State::Init, entity->GetState()); - - entity->Activate(); - EXPECT_EQ(AZ::Entity::State::Active, entity->GetState()); - } - - template - AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config) - { - m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); - return entity->CreateComponent(config); - } - - template - AZ::Component* CreateComponent(AZ::Entity* entity) - { - m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); - return entity->CreateComponent(); - } - - // Create a terrain system with reasonable defaults for testing, but with the ability to override the defaults - // on a test-by-test basis. - void CreateAndActivateTerrainSystem( - AZ::Vector2 queryResolution = AZ::Vector2(1.0f), - AZ::Aabb worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-128.0f), AZ::Vector3(128.0f))) - { - // Create the terrain system and give it one tick to fully initialize itself. m_terrainSystem = AZStd::make_unique(); - m_terrainSystem->SetTerrainAabb(worldBounds); - m_terrainSystem->SetTerrainHeightQueryResolution(queryResolution); m_terrainSystem->Activate(); - AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + m_terrainSystem->Deactivate(); } - - AZStd::unique_ptr CreateAndActivateMockTerrainLayerSpawner( - const AZ::Aabb& spawnerBox, - const AZStd::function& mockHeights) + TEST_F(TerrainSystemTest, CreateEventsCalledOnActivation) { - // Create the base entity with a mock box shape, Terrain Layer Spawner, and height provider. - auto entity = CreateEntity(); - CreateComponent(entity.get()); - CreateComponent(entity.get()); + // Verify that when the terrain system is activated, the OnTerrainDataCreate* ebus notifications are generated. - m_boxShapeRequests = AZStd::make_unique>(entity->GetId()); - m_shapeRequests = AZStd::make_unique>(entity->GetId()); + NiceMock mockTerrainListener; + EXPECT_CALL(mockTerrainListener, OnTerrainDataCreateBegin()).Times(AtLeast(1)); + EXPECT_CALL(mockTerrainListener, OnTerrainDataCreateEnd()).Times(AtLeast(1)); - // Set up the box shape to return whatever spawnerBox was passed in. - ON_CALL(*m_shapeRequests, GetEncompassingAabb).WillByDefault(Return(spawnerBox)); - - // Set up a mock height provider to use the passed-in mock height function to generate a height. - m_terrainAreaHeightRequests = AZStd::make_unique>(entity->GetId()); - ON_CALL(*m_terrainAreaHeightRequests, GetHeight) - .WillByDefault( - [mockHeights](const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) - { - // By default, set the outPosition to the input position and terrain to always exist. - outPosition = inPosition; - terrainExists = true; - // Let the test function modify these values based on the needs of the specific test. - mockHeights(outPosition, terrainExists); - }); - - ActivateEntity(entity.get()); - return entity; + m_terrainSystem = AZStd::make_unique(); + m_terrainSystem->Activate(); } -}; -TEST_F(TerrainSystemTest, TrivialCreateDestroy) -{ - // Trivially verify that the terrain system can successfully be constructed and destructed without errors. - - m_terrainSystem = AZStd::make_unique(); -} - -TEST_F(TerrainSystemTest, TrivialActivateDeactivate) -{ - // Verify that the terrain system can be activated and deactivated without errors. - - m_terrainSystem = AZStd::make_unique(); - m_terrainSystem->Activate(); - m_terrainSystem->Deactivate(); -} - -TEST_F(TerrainSystemTest, CreateEventsCalledOnActivation) -{ - // Verify that when the terrain system is activated, the OnTerrainDataCreate* ebus notifications are generated. - - NiceMock mockTerrainListener; - EXPECT_CALL(mockTerrainListener, OnTerrainDataCreateBegin()).Times(AtLeast(1)); - EXPECT_CALL(mockTerrainListener, OnTerrainDataCreateEnd()).Times(AtLeast(1)); - - m_terrainSystem = AZStd::make_unique(); - m_terrainSystem->Activate(); -} - -TEST_F(TerrainSystemTest, DestroyEventsCalledOnDeactivation) -{ - // Verify that when the terrain system is deactivated, the OnTerrainDataDestroy* ebus notifications are generated. - - NiceMock mockTerrainListener; - EXPECT_CALL(mockTerrainListener, OnTerrainDataDestroyBegin()).Times(AtLeast(1)); - EXPECT_CALL(mockTerrainListener, OnTerrainDataDestroyEnd()).Times(AtLeast(1)); - - m_terrainSystem = AZStd::make_unique(); - m_terrainSystem->Activate(); - m_terrainSystem->Deactivate(); -} - -TEST_F(TerrainSystemTest, TerrainDoesNotExistWhenNoTerrainLayerSpawnersAreRegistered) -{ - // For the terrain system, terrain should only exist where terrain layer spawners are present. - - // Verify that in the active terrain system, if there are no terrain layer spawners, any arbitrary point - // will return false for terrainExists, returns a height equal to the min world bounds of the terrain system, and returns - // a normal facing up the Z axis. - - // Create and activate the terrain system with our testing defaults for world bounds and query resolution. - CreateAndActivateTerrainSystem(); - - AZ::Aabb worldBounds = m_terrainSystem->GetTerrainAabb(); - - // Loop through several points within the world bounds, including on the edges, and verify that they all return false for - // terrainExists with default heights and normals. - for (float y = worldBounds.GetMin().GetY(); y <= worldBounds.GetMax().GetY(); y += (worldBounds.GetExtents().GetY() / 4.0f)) + TEST_F(TerrainSystemTest, DestroyEventsCalledOnDeactivation) { - for (float x = worldBounds.GetMin().GetX(); x <= worldBounds.GetMax().GetX(); x += (worldBounds.GetExtents().GetX() / 4.0f)) - { - AZ::Vector3 position(x, y, 0.0f); - bool terrainExists = true; - float height = m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); - EXPECT_FALSE(terrainExists); - EXPECT_FLOAT_EQ(height, worldBounds.GetMin().GetZ()); + // Verify that when the terrain system is deactivated, the OnTerrainDataDestroy* ebus notifications are generated. - terrainExists = true; - AZ::Vector3 normal = m_terrainSystem->GetNormal( - position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); - EXPECT_FALSE(terrainExists); - EXPECT_EQ(normal, AZ::Vector3::CreateAxisZ()); + NiceMock mockTerrainListener; + EXPECT_CALL(mockTerrainListener, OnTerrainDataDestroyBegin()).Times(AtLeast(1)); + EXPECT_CALL(mockTerrainListener, OnTerrainDataDestroyEnd()).Times(AtLeast(1)); - bool isHole = m_terrainSystem->GetIsHoleFromFloats( - position.GetX(), position.GetY(), AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT); - EXPECT_TRUE(isHole); - } + m_terrainSystem = AZStd::make_unique(); + m_terrainSystem->Activate(); + m_terrainSystem->Deactivate(); } -} -TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) -{ - // Verify that the presence of a TerrainLayerSpawner causes terrain to exist in (and *only* in) the box where the TerrainLayerSpawner - // is defined. - - // The terrain system should only query Heights from the TerrainAreaHeightRequest bus within the - // TerrainLayerSpawner region, and so those values should only get returned from GetHeight for queries inside that region. - - // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and always returns a height of 5. - constexpr float spawnerHeight = 5.0f; - const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); - auto entity = CreateAndActivateMockTerrainLayerSpawner( - spawnerBox, - [](AZ::Vector3& position, bool& terrainExists) - { - position.SetZ(spawnerHeight); - terrainExists = true; - }); - - // Verify that terrain exists within the layer spawner bounds, and doesn't exist outside of it. - - // Create and activate the terrain system with our testing defaults for world bounds and query resolution. - CreateAndActivateTerrainSystem(); - - // Create a box that's twice as big as the layer spawner box. Loop through it and verify that points within the layer box contain - // terrain and the expected height & normal values, and points outside the layer box don't contain terrain. - const AZ::Aabb encompassingBox = - AZ::Aabb::CreateFromMinMax(spawnerBox.GetMin() - (spawnerBox.GetExtents() / 2.0f), - spawnerBox.GetMax() + (spawnerBox.GetExtents() / 2.0f)); - - for (float y = encompassingBox.GetMin().GetY(); y < encompassingBox.GetMax().GetY(); y += 1.0f) + TEST_F(TerrainSystemTest, TerrainDoesNotExistWhenNoTerrainLayerSpawnersAreRegistered) { - for (float x = encompassingBox.GetMin().GetX(); x < encompassingBox.GetMax().GetX(); x += 1.0f) - { - AZ::Vector3 position(x, y, 0.0f); - bool heightQueryTerrainExists = false; - float height = - m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); - bool isHole = m_terrainSystem->GetIsHoleFromFloats( - position.GetX(), position.GetY(), AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT); + // For the terrain system, terrain should only exist where terrain layer spawners are present. - if (spawnerBox.Contains(AZ::Vector3(position.GetX(), position.GetY(), spawnerBox.GetMin().GetZ()))) + // Verify that in the active terrain system, if there are no terrain layer spawners, any arbitrary point + // will return false for terrainExists, returns a height equal to the min world bounds of the terrain system, and returns + // a normal facing up the Z axis. + + // Create and activate the terrain system with our testing defaults for world bounds and query resolution. + CreateAndActivateTerrainSystem(); + + AZ::Aabb worldBounds = m_terrainSystem->GetTerrainAabb(); + + // Loop through several points within the world bounds, including on the edges, and verify that they all return false for + // terrainExists with default heights and normals. + for (float y = worldBounds.GetMin().GetY(); y <= worldBounds.GetMax().GetY(); y += (worldBounds.GetExtents().GetY() / 4.0f)) + { + for (float x = worldBounds.GetMin().GetX(); x <= worldBounds.GetMax().GetX(); x += (worldBounds.GetExtents().GetX() / 4.0f)) { - EXPECT_TRUE(heightQueryTerrainExists); - EXPECT_FALSE(isHole); - EXPECT_FLOAT_EQ(height, spawnerHeight); - } - else - { - EXPECT_FALSE(heightQueryTerrainExists); + AZ::Vector3 position(x, y, 0.0f); + bool terrainExists = true; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); + EXPECT_FALSE(terrainExists); + EXPECT_FLOAT_EQ(height, worldBounds.GetMin().GetZ()); + + terrainExists = true; + AZ::Vector3 normal = + m_terrainSystem->GetNormal(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); + EXPECT_FALSE(terrainExists); + EXPECT_EQ(normal, AZ::Vector3::CreateAxisZ()); + + bool isHole = m_terrainSystem->GetIsHoleFromFloats( + position.GetX(), position.GetY(), AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT); EXPECT_TRUE(isHole); } } } -} -TEST_F(TerrainSystemTest, TerrainHeightQueriesWithExactSamplersIgnoreQueryGrid) -{ - // Verify that when using the "EXACT" height sampler, the returned heights come directly from the height provider at the exact - // requested location, instead of the position being quantized to the height query grid. - - // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and generates a height based on a sine wave - // using a frequency of 1m and an amplitude of 10m. i.e. Heights will range between -10 to 10 meters, but will have a value of 0 - // every 0.5 meters. The sine wave value is based on the absolute X position only, for simplicity. - constexpr float amplitudeMeters = 10.0f; - constexpr float frequencyMeters = 1.0f; - const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); - auto entity = CreateAndActivateMockTerrainLayerSpawner( - spawnerBox, - [](AZ::Vector3& position, bool& terrainExists) - { - position.SetZ(amplitudeMeters * sin(AZ::Constants::TwoPi * (position.GetX() / frequencyMeters))); - terrainExists = true; - }); - - // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution that exactly matches - // the frequency of our sine wave. If our height queries rely on the query resolution, we should always get a value of 0. - const AZ::Vector2 queryResolution(frequencyMeters); - CreateAndActivateTerrainSystem(queryResolution); - - // Test an arbitrary set of points that should all produce non-zero heights with the EXACT sampler. They're not aligned with the - // query resolution, or with the 0 points on the sine wave. - const AZ::Vector2 nonZeroPoints[] = { AZ::Vector2(0.3f), AZ::Vector2(2.8f), AZ::Vector2(5.9f), AZ::Vector2(7.7f) }; - for (auto& nonZeroPoint : nonZeroPoints) + TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) { + // Verify that the presence of a TerrainLayerSpawner causes terrain to exist in (and *only* in) the box where the + // TerrainLayerSpawner is defined. + + // The terrain system should only query Heights from the TerrainAreaHeightRequest bus within the + // TerrainLayerSpawner region, and so those values should only get returned from GetHeight for queries inside that region. + + // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and always returns a height of 5. + constexpr float spawnerHeight = 5.0f; + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(spawnerHeight); + terrainExists = true; + }); + + // Verify that terrain exists within the layer spawner bounds, and doesn't exist outside of it. + + // Create and activate the terrain system with our testing defaults for world bounds and query resolution. + CreateAndActivateTerrainSystem(); + + // Create a box that's twice as big as the layer spawner box. Loop through it and verify that points within the layer box contain + // terrain and the expected height & normal values, and points outside the layer box don't contain terrain. + const AZ::Aabb encompassingBox = AZ::Aabb::CreateFromMinMax( + spawnerBox.GetMin() - (spawnerBox.GetExtents() / 2.0f), spawnerBox.GetMax() + (spawnerBox.GetExtents() / 2.0f)); + + for (float y = encompassingBox.GetMin().GetY(); y < encompassingBox.GetMax().GetY(); y += 1.0f) + { + for (float x = encompassingBox.GetMin().GetX(); x < encompassingBox.GetMax().GetX(); x += 1.0f) + { + AZ::Vector3 position(x, y, 0.0f); + bool heightQueryTerrainExists = false; + float height = m_terrainSystem->GetHeight( + position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); + bool isHole = m_terrainSystem->GetIsHoleFromFloats( + position.GetX(), position.GetY(), AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT); + + if (spawnerBox.Contains(AZ::Vector3(position.GetX(), position.GetY(), spawnerBox.GetMin().GetZ()))) + { + EXPECT_TRUE(heightQueryTerrainExists); + EXPECT_FALSE(isHole); + EXPECT_FLOAT_EQ(height, spawnerHeight); + } + else + { + EXPECT_FALSE(heightQueryTerrainExists); + EXPECT_TRUE(isHole); + } + } + } + } + + TEST_F(TerrainSystemTest, TerrainHeightQueriesWithExactSamplersIgnoreQueryGrid) + { + // Verify that when using the "EXACT" height sampler, the returned heights come directly from the height provider at the exact + // requested location, instead of the position being quantized to the height query grid. + + // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and generates a height based on a sine wave + // using a frequency of 1m and an amplitude of 10m. i.e. Heights will range between -10 to 10 meters, but will have a value of 0 + // every 0.5 meters. The sine wave value is based on the absolute X position only, for simplicity. + constexpr float amplitudeMeters = 10.0f; + constexpr float frequencyMeters = 1.0f; + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(amplitudeMeters * sin(AZ::Constants::TwoPi * (position.GetX() / frequencyMeters))); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution that exactly matches + // the frequency of our sine wave. If our height queries rely on the query resolution, we should always get a value of 0. + const AZ::Vector2 queryResolution(frequencyMeters); + CreateAndActivateTerrainSystem(queryResolution); + + // Test an arbitrary set of points that should all produce non-zero heights with the EXACT sampler. They're not aligned with the + // query resolution, or with the 0 points on the sine wave. + const AZ::Vector2 nonZeroPoints[] = { AZ::Vector2(0.3f), AZ::Vector2(2.8f), AZ::Vector2(5.9f), AZ::Vector2(7.7f) }; + for (auto& nonZeroPoint : nonZeroPoints) + { AZ::Vector3 position(nonZeroPoint.GetX(), nonZeroPoint.GetY(), 0.0f); bool heightQueryTerrainExists = false; float height = @@ -311,165 +312,253 @@ TEST_F(TerrainSystemTest, TerrainHeightQueriesWithExactSamplersIgnoreQueryGrid) // We've chosen a bunch of places on the sine wave that should return a non-zero positive or negative value. constexpr float epsilon = 0.0001f; EXPECT_GT(fabsf(height), epsilon); - } + } - // Test an arbitrary set of points that should all produce zero heights with the EXACT sampler, since they align with 0 points on the - // sine wave, regardless of whether or not they align to the query resolution. - const AZ::Vector2 zeroPoints[] = { AZ::Vector2(0.5f), AZ::Vector2(1.0f), AZ::Vector2(5.0f), AZ::Vector2(7.5f) }; - for (auto& zeroPoint : zeroPoints) - { - AZ::Vector3 position(zeroPoint.GetX(), zeroPoint.GetY(), 0.0f); - bool heightQueryTerrainExists = false; - float height = - m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); - - constexpr float epsilon = 0.0001f; - EXPECT_NEAR(height, 0.0f, epsilon); - } -} - -TEST_F(TerrainSystemTest, TerrainHeightQueriesWithClampSamplersUseQueryGrid) -{ - // Verify that when using the "CLAMP" height sampler, the requested location is quantized to the height query grid before fetching - // the height. - - // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal - // to the X + Y position, so if either one doesn't get clamped we'll get an unexpected result. - const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); - auto entity = CreateAndActivateMockTerrainLayerSpawner( - spawnerBox, - [](AZ::Vector3& position, bool& terrainExists) + // Test an arbitrary set of points that should all produce zero heights with the EXACT sampler, since they align with 0 points on + // the sine wave, regardless of whether or not they align to the query resolution. + const AZ::Vector2 zeroPoints[] = { AZ::Vector2(0.5f), AZ::Vector2(1.0f), AZ::Vector2(5.0f), AZ::Vector2(7.5f) }; + for (auto& zeroPoint : zeroPoints) { - position.SetZ(position.GetX() + position.GetY()); - terrainExists = true; - }); + AZ::Vector3 position(zeroPoint.GetX(), zeroPoint.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); - // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 0.25 meter intervals. - const AZ::Vector2 queryResolution(0.25f); - CreateAndActivateTerrainSystem(queryResolution); - - // Test some points and verify that the results always go "downward", whether they're in positive or negative space. - // (Z contains the the expected result for convenience). - const HeightTestPoint testPoints[] = - { - { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0.00 + 0.00 - { AZ::Vector2(0.3f, 0.3f), 0.5f }, // Should return a height of 0.25 + 0.25 - { AZ::Vector2(2.8f, 2.8f), 5.5f }, // Should return a height of 2.75 + 2.75 - { AZ::Vector2(5.5f, 5.5f), 11.0f }, // Should return a height of 5.50 + 5.50 - { AZ::Vector2(7.7f, 7.7f), 15.0f }, // Should return a height of 7.50 + 7.50 - - { AZ::Vector2(-0.3f, -0.3f), -1.0f }, // Should return a height of -0.50 + -0.50 - { AZ::Vector2(-2.8f, -2.8f), -6.0f }, // Should return a height of -3.00 + -3.00 - { AZ::Vector2(-5.5f, -5.5f), -11.0f }, // Should return a height of -5.50 + -5.50 - { AZ::Vector2(-7.7f, -7.7f), -15.5f } // Should return a height of -7.75 + -7.75 - }; - for (auto& testPoint : testPoints) - { - const float expectedHeight = testPoint.m_expectedHeight; - - AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); - bool heightQueryTerrainExists = false; - float height = - m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP, &heightQueryTerrainExists); - - constexpr float epsilon = 0.0001f; - EXPECT_NEAR(height, expectedHeight, epsilon); + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, 0.0f, epsilon); + } } -} -TEST_F(TerrainSystemTest, TerrainHeightQueriesWithBilinearSamplersUseQueryGridToInterpolate) -{ - // Verify that when using the "BILINEAR" height sampler, the heights are interpolated from points sampled from the query grid. + TEST_F(TerrainSystemTest, TerrainHeightQueriesWithClampSamplersUseQueryGrid) + { + // Verify that when using the "CLAMP" height sampler, the requested location is quantized to the height query grid before fetching + // the height. - // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal - // to the X + Y position, so we'll have heights that look like this on our grid: - // 0 *---* 1 - // | | - // 1 *---* 2 - // However, everywhere inside the grid box, we'll generate heights much larger than X + Y. It will have no effect on exact grid - // points, but it will noticeably affect the expected height values if any points get sampled in-between grid points. + // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal + // to the X + Y position, so if either one doesn't get clamped we'll get an unexpected result. + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(position.GetX() + position.GetY()); + terrainExists = true; + }); - const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); - const float amplitudeMeters = 10.0f; - const float frequencyMeters = 1.0f; - auto entity = CreateAndActivateMockTerrainLayerSpawner( - spawnerBox, - [amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists) + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 0.25 meter + // intervals. + const AZ::Vector2 queryResolution(0.25f); + CreateAndActivateTerrainSystem(queryResolution); + + // Test some points and verify that the results always go "downward", whether they're in positive or negative space. + // (Z contains the the expected result for convenience). + const HeightTestPoint testPoints[] = { + { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0.00 + 0.00 + { AZ::Vector2(0.3f, 0.3f), 0.5f }, // Should return a height of 0.25 + 0.25 + { AZ::Vector2(2.8f, 2.8f), 5.5f }, // Should return a height of 2.75 + 2.75 + { AZ::Vector2(5.5f, 5.5f), 11.0f }, // Should return a height of 5.50 + 5.50 + { AZ::Vector2(7.7f, 7.7f), 15.0f }, // Should return a height of 7.50 + 7.50 + + { AZ::Vector2(-0.3f, -0.3f), -1.0f }, // Should return a height of -0.50 + -0.50 + { AZ::Vector2(-2.8f, -2.8f), -6.0f }, // Should return a height of -3.00 + -3.00 + { AZ::Vector2(-5.5f, -5.5f), -11.0f }, // Should return a height of -5.50 + -5.50 + { AZ::Vector2(-7.7f, -7.7f), -15.5f } // Should return a height of -7.75 + -7.75 + }; + for (auto& testPoint : testPoints) { - // Our generated height will be X + Y. - float expectedHeight = position.GetX() + position.GetY(); + const float expectedHeight = testPoint.m_expectedHeight; - // If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height. - // This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries. - float unexpectedVariance = amplitudeMeters * - (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters)); - position.SetZ(expectedHeight + unexpectedVariance); - terrainExists = true; - }); + AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP, &heightQueryTerrainExists); - // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals. - const AZ::Vector2 queryResolution(frequencyMeters); - CreateAndActivateTerrainSystem(queryResolution); - - // Test some points and verify that the results are the expected bilinear filtered result, - // whether they're in positive or negative space. - // (Z contains the the expected result for convenience). - const HeightTestPoint testPoints[] = { - - // Queries directly on grid points. These should return values of X + Y. - { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0 + 0 - { AZ::Vector2(1.0f, 0.0f), 1.0f }, // Should return a height of 1 + 0 - { AZ::Vector2(0.0f, 1.0f), 1.0f }, // Should return a height of 0 + 1 - { AZ::Vector2(1.0f, 1.0f), 2.0f }, // Should return a height of 1 + 1 - { AZ::Vector2(3.0f, 5.0f), 8.0f }, // Should return a height of 3 + 5 - - { AZ::Vector2(-1.0f, 0.0f), -1.0f }, // Should return a height of -1 + 0 - { AZ::Vector2(0.0f, -1.0f), -1.0f }, // Should return a height of 0 + -1 - { AZ::Vector2(-1.0f, -1.0f), -2.0f }, // Should return a height of -1 + -1 - { AZ::Vector2(-3.0f, -5.0f), -8.0f }, // Should return a height of -3 + -5 - - // Queries that are on a grid edge (one axis on the grid, the other somewhere in-between). - // These should just be a linear interpolation of the points, so it should still be X + Y. - - { AZ::Vector2(0.25f, 0.0f), 0.25f }, // Should return a height of -0.25 + 0 - { AZ::Vector2(3.75f, 0.0f), 3.75f }, // Should return a height of -3.75 + 0 - { AZ::Vector2(0.0f, 0.25f), 0.25f }, // Should return a height of 0 + -0.25 - { AZ::Vector2(0.0f, 3.75f), 3.75f }, // Should return a height of 0 + -3.75 - - { AZ::Vector2(2.0f, 3.75f), 5.75f }, // Should return a height of -2 + -3.75 - { AZ::Vector2(2.25f, 4.0f), 6.25f }, // Should return a height of -2.25 + -4 - - { AZ::Vector2(-0.25f, 0.0f), -0.25f }, // Should return a height of -0.25 + 0 - { AZ::Vector2(-3.75f, 0.0f), -3.75f }, // Should return a height of -3.75 + 0 - { AZ::Vector2(0.0f, -0.25f), -0.25f }, // Should return a height of 0 + -0.25 - { AZ::Vector2(0.0f, -3.75f), -3.75f }, // Should return a height of 0 + -3.75 - - { AZ::Vector2(-2.0f, -3.75f), -5.75f }, // Should return a height of -2 + -3.75 - { AZ::Vector2(-2.25f, -4.0f), -6.25f }, // Should return a height of -2.25 + -4 - - // Queries inside a grid square (both axes are in-between grid points) - // This is a full bilinear interpolation, but because we're using X + Y for our heights, the interpolated values - // should *still* be X + Y assuming the points were sampled correctly from the grid points. - - { AZ::Vector2(3.25f, 5.25f), 8.5f }, // Should return a height of 3.25 + 5.25 - { AZ::Vector2(7.71f, 9.74f), 17.45f }, // Should return a height of 7.71 + 9.74 - - { AZ::Vector2(-3.25f, -5.25f), -8.5f }, // Should return a height of -3.25 + -5.25 - { AZ::Vector2(-7.71f, -9.74f), -17.45f }, // Should return a height of -7.71 + -9.74 - }; - - // Loop through every test point and validate it. - for (auto& testPoint : testPoints) - { - const float expectedHeight = testPoint.m_expectedHeight; - - AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); - bool heightQueryTerrainExists = false; - float height = - m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, &heightQueryTerrainExists); - - // Verify that our height query returned the bilinear filtered result we expect. - constexpr float epsilon = 0.0001f; - EXPECT_NEAR(height, expectedHeight, epsilon); + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, expectedHeight, epsilon); + } } -} + + TEST_F(TerrainSystemTest, TerrainHeightQueriesWithBilinearSamplersUseQueryGridToInterpolate) + { + // Verify that when using the "BILINEAR" height sampler, the heights are interpolated from points sampled from the query grid. + + // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal + // to the X + Y position, so we'll have heights that look like this on our grid: + // 0 *---* 1 + // | | + // 1 *---* 2 + // However, everywhere inside the grid box, we'll generate heights much larger than X + Y. It will have no effect on exact grid + // points, but it will noticeably affect the expected height values if any points get sampled in-between grid points. + + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); + const float amplitudeMeters = 10.0f; + const float frequencyMeters = 1.0f; + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists) + { + // Our generated height will be X + Y. + float expectedHeight = position.GetX() + position.GetY(); + + // If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height. + // This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries. + float unexpectedVariance = + amplitudeMeters * (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters)); + position.SetZ(expectedHeight + unexpectedVariance); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals. + const AZ::Vector2 queryResolution(frequencyMeters); + CreateAndActivateTerrainSystem(queryResolution); + + // Test some points and verify that the results are the expected bilinear filtered result, + // whether they're in positive or negative space. + // (Z contains the the expected result for convenience). + const HeightTestPoint testPoints[] = { + + // Queries directly on grid points. These should return values of X + Y. + { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0 + 0 + { AZ::Vector2(1.0f, 0.0f), 1.0f }, // Should return a height of 1 + 0 + { AZ::Vector2(0.0f, 1.0f), 1.0f }, // Should return a height of 0 + 1 + { AZ::Vector2(1.0f, 1.0f), 2.0f }, // Should return a height of 1 + 1 + { AZ::Vector2(3.0f, 5.0f), 8.0f }, // Should return a height of 3 + 5 + + { AZ::Vector2(-1.0f, 0.0f), -1.0f }, // Should return a height of -1 + 0 + { AZ::Vector2(0.0f, -1.0f), -1.0f }, // Should return a height of 0 + -1 + { AZ::Vector2(-1.0f, -1.0f), -2.0f }, // Should return a height of -1 + -1 + { AZ::Vector2(-3.0f, -5.0f), -8.0f }, // Should return a height of -3 + -5 + + // Queries that are on a grid edge (one axis on the grid, the other somewhere in-between). + // These should just be a linear interpolation of the points, so it should still be X + Y. + + { AZ::Vector2(0.25f, 0.0f), 0.25f }, // Should return a height of -0.25 + 0 + { AZ::Vector2(3.75f, 0.0f), 3.75f }, // Should return a height of -3.75 + 0 + { AZ::Vector2(0.0f, 0.25f), 0.25f }, // Should return a height of 0 + -0.25 + { AZ::Vector2(0.0f, 3.75f), 3.75f }, // Should return a height of 0 + -3.75 + + { AZ::Vector2(2.0f, 3.75f), 5.75f }, // Should return a height of -2 + -3.75 + { AZ::Vector2(2.25f, 4.0f), 6.25f }, // Should return a height of -2.25 + -4 + + { AZ::Vector2(-0.25f, 0.0f), -0.25f }, // Should return a height of -0.25 + 0 + { AZ::Vector2(-3.75f, 0.0f), -3.75f }, // Should return a height of -3.75 + 0 + { AZ::Vector2(0.0f, -0.25f), -0.25f }, // Should return a height of 0 + -0.25 + { AZ::Vector2(0.0f, -3.75f), -3.75f }, // Should return a height of 0 + -3.75 + + { AZ::Vector2(-2.0f, -3.75f), -5.75f }, // Should return a height of -2 + -3.75 + { AZ::Vector2(-2.25f, -4.0f), -6.25f }, // Should return a height of -2.25 + -4 + + // Queries inside a grid square (both axes are in-between grid points) + // This is a full bilinear interpolation, but because we're using X + Y for our heights, the interpolated values + // should *still* be X + Y assuming the points were sampled correctly from the grid points. + + { AZ::Vector2(3.25f, 5.25f), 8.5f }, // Should return a height of 3.25 + 5.25 + { AZ::Vector2(7.71f, 9.74f), 17.45f }, // Should return a height of 7.71 + 9.74 + + { AZ::Vector2(-3.25f, -5.25f), -8.5f }, // Should return a height of -3.25 + -5.25 + { AZ::Vector2(-7.71f, -9.74f), -17.45f }, // Should return a height of -7.71 + -9.74 + }; + + // Loop through every test point and validate it. + for (auto& testPoint : testPoints) + { + const float expectedHeight = testPoint.m_expectedHeight; + + AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = m_terrainSystem->GetHeight( + position, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, &heightQueryTerrainExists); + + // Verify that our height query returned the bilinear filtered result we expect. + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, expectedHeight, epsilon); + } + } + + TEST_F(TerrainSystemTest, GetSurfaceWeightsReturnsAllValidSurfaceWeights) + { + CreateAndActivateTerrainSystem(); + + const AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne()); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + aabb, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(1.0f); + terrainExists = true; + }); + + const AZ::Crc32 tag1("tag1"); + const AZ::Crc32 tag2("tag2"); + + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet orderedSurfaceWeights; + + AzFramework::SurfaceData::SurfaceTagWeight tagWeight1; + tagWeight1.m_surfaceType = tag1; + tagWeight1.m_weight = 1.0f; + orderedSurfaceWeights.emplace(tagWeight1); + + AzFramework::SurfaceData::SurfaceTagWeight tagWeight2; + tagWeight2.m_surfaceType = tag2; + tagWeight2.m_weight = 0.8f; + orderedSurfaceWeights.emplace(tagWeight2); + + NiceMock mockSurfaceRequests(entity->GetId()); + ON_CALL(mockSurfaceRequests, GetSurfaceWeights).WillByDefault(SetArgReferee<1>(orderedSurfaceWeights)); + + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet outSurfaceWeights; + + // Asking for values outside the layer spawner bounds, should result in no results. + m_terrainSystem->GetSurfaceWeights(aabb.GetMax() + AZ::Vector3::CreateOne(), outSurfaceWeights); + EXPECT_TRUE(outSurfaceWeights.empty()); + + // Inside the layer spawner box should give us both the added surface weights. + m_terrainSystem->GetSurfaceWeights(aabb.GetCenter(), outSurfaceWeights); + + EXPECT_EQ(outSurfaceWeights.size(), 2); + } + + TEST_F(TerrainSystemTest, GetMaxSurfaceWeightsReturnsBiggestValidSurfaceWeight) + { + CreateAndActivateTerrainSystem(); + + const AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne()); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + aabb, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(1.0f); + terrainExists = true; + }); + + const AZ::Crc32 tag1("tag1"); + const AZ::Crc32 tag2("tag2"); + + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet orderedSurfaceWeights; + + AzFramework::SurfaceData::SurfaceTagWeight tagWeight1; + tagWeight1.m_surfaceType = tag1; + tagWeight1.m_weight = 1.0f; + orderedSurfaceWeights.emplace(tagWeight1); + + AzFramework::SurfaceData::SurfaceTagWeight tagWeight2; + tagWeight2.m_surfaceType = tag2; + tagWeight2.m_weight = 0.8f; + orderedSurfaceWeights.emplace(tagWeight2); + + NiceMock mockSurfaceRequests(entity->GetId()); + ON_CALL(mockSurfaceRequests, GetSurfaceWeights).WillByDefault(SetArgReferee<1>(orderedSurfaceWeights)); + + // Asking for values outside the layer spawner bounds, should result in an invalid result. + AzFramework::SurfaceData::SurfaceTagWeight tagWeight = + m_terrainSystem->GetMaxSurfaceWeight(aabb.GetMax() + AZ::Vector3::CreateOne()); + + EXPECT_EQ(tagWeight.m_surfaceType, AZ::Crc32(AzFramework::SurfaceData::Constants::s_unassignedTagName)); + + // Inside the layer spawner box should give us the highest weighted tag (tag1). + tagWeight = m_terrainSystem->GetMaxSurfaceWeight(aabb.GetCenter()); + + EXPECT_EQ(tagWeight.m_surfaceType, tagWeight1.m_surfaceType); + EXPECT_NEAR(tagWeight.m_weight, tagWeight1.m_weight, 0.01f); + } +} // namespace UnitTest diff --git a/Gems/Terrain/Code/terrain_editor_shared_files.cmake b/Gems/Terrain/Code/terrain_editor_shared_files.cmake index 334c89ec73..09724751a9 100644 --- a/Gems/Terrain/Code/terrain_editor_shared_files.cmake +++ b/Gems/Terrain/Code/terrain_editor_shared_files.cmake @@ -11,6 +11,8 @@ set(FILES Source/EditorComponents/EditorTerrainHeightGradientListComponent.h Source/EditorComponents/EditorTerrainLayerSpawnerComponent.cpp Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h + Source/EditorComponents/EditorTerrainPhysicsColliderComponent.cpp + Source/EditorComponents/EditorTerrainPhysicsColliderComponent.h Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h Source/EditorComponents/EditorTerrainWorldComponent.cpp diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index 780dd1bdb0..893477e10d 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -12,6 +12,8 @@ set(FILES Source/Components/TerrainHeightGradientListComponent.h Source/Components/TerrainLayerSpawnerComponent.cpp Source/Components/TerrainLayerSpawnerComponent.h + Source/Components/TerrainPhysicsColliderComponent.cpp + Source/Components/TerrainPhysicsColliderComponent.h Source/Components/TerrainSurfaceDataSystemComponent.cpp Source/Components/TerrainSurfaceDataSystemComponent.h Source/Components/TerrainSurfaceGradientListComponent.cpp diff --git a/Gems/Terrain/Code/terrain_mocks_files.cmake b/Gems/Terrain/Code/terrain_mocks_files.cmake index 2aedd1c5d8..874e17f028 100644 --- a/Gems/Terrain/Code/terrain_mocks_files.cmake +++ b/Gems/Terrain/Code/terrain_mocks_files.cmake @@ -8,4 +8,6 @@ set(FILES Mocks/Terrain/MockTerrain.h + Mocks/Terrain/MockTerrainLayerSpawner.h + Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h ) diff --git a/Gems/Terrain/Code/terrain_tests_files.cmake b/Gems/Terrain/Code/terrain_tests_files.cmake index 793bc01ac5..3e37e509a7 100644 --- a/Gems/Terrain/Code/terrain_tests_files.cmake +++ b/Gems/Terrain/Code/terrain_tests_files.cmake @@ -10,6 +10,9 @@ set(FILES Tests/TerrainTest.cpp Tests/TerrainSystemTest.cpp Tests/LayerSpawnerTests.cpp + Tests/TerrainPhysicsColliderTests.cpp Tests/SurfaceMaterialsListTest.cpp Tests/MockAxisAlignedBoxShapeComponent.h + Tests/TerrainHeightGradientListTests.cpp + Tests/TerrainSurfaceGradientListTests.cpp ) From 95c2ee0e9d4da1ced0e796fc0da03c9bd1ef03d7 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 22 Oct 2021 10:30:48 -0500 Subject: [PATCH 133/152] Added API for finding components based on required/incompatible services. Signed-off-by: Chris Galvan --- .../Component/EditorComponentAPIBus.h | 4 ++ .../Component/EditorComponentAPIComponent.cpp | 30 +++++++++ .../Component/EditorComponentAPIComponent.h | 1 + .../Entity/EditorEntityHelpers.cpp | 62 +++++++++++++++++++ .../Entity/EditorEntityHelpers.h | 10 +++ .../ComponentPalette/ComponentPaletteUtil.cpp | 62 +------------------ .../ComponentPalette/ComponentPaletteUtil.hxx | 12 ---- .../Tests/EntityInspectorTests.cpp | 7 ++- 8 files changed, 112 insertions(+), 76 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIBus.h index f7119f3e14..b639686269 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIBus.h @@ -36,6 +36,10 @@ namespace AzToolsFramework //! the entity type. virtual AZStd::vector FindComponentTypeIdsByEntityType(const AZStd::vector& componentTypeNames, EntityType entityType) = 0; + //! Return a list of type ids for components that match the required services filter, + //! and don't conflict with any of the incompatible services filter + virtual AZStd::vector FindComponentTypeIdsByService(const AZStd::vector& serviceFilter, const AZStd::vector& incompatibleServiceFilter) = 0; + //! Finds the component names from their type ids virtual AZStd::vector FindComponentTypeNames(const AZ::ComponentTypeList& componentTypeIds) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.cpp index 30a7c603bd..2cca3804ea 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,7 @@ namespace AzToolsFramework serializeContext->Class(); serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); } if (auto behaviorContext = azrtti_cast(context)) @@ -99,6 +101,7 @@ namespace AzToolsFramework ->Attribute(AZ::Script::Attributes::Module, "editor") ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) ->Event("FindComponentTypeIdsByEntityType", &EditorComponentAPIRequests::FindComponentTypeIdsByEntityType) + ->Event("FindComponentTypeIdsByService", &EditorComponentAPIRequests::FindComponentTypeIdsByService) ->Event("FindComponentTypeNames", &EditorComponentAPIRequests::FindComponentTypeNames) ->Event("BuildComponentTypeNameListByEntityType", &EditorComponentAPIRequests::BuildComponentTypeNameListByEntityType) ->Event("AddComponentsOfType", &EditorComponentAPIRequests::AddComponentsOfType) @@ -216,6 +219,33 @@ namespace AzToolsFramework return foundTypeIds; } + AZStd::vector EditorComponentAPIComponent::FindComponentTypeIdsByService(const AZStd::vector& serviceFilter, const AZStd::vector& incompatibleServiceFilter) + { + AZStd::vector foundTypeIds; + + m_serializeContext->EnumerateDerived( + [&foundTypeIds, serviceFilter, incompatibleServiceFilter](const AZ::SerializeContext::ClassData* componentClass, const AZ::Uuid& knownType) -> bool + { + AZ_UNUSED(knownType); + + if (componentClass->m_editData) + { + // If none of the required services are offered by this component, or the component + // can not be added by the user, skip to the next component + if (!OffersRequiredServices(componentClass, serviceFilter, incompatibleServiceFilter)) + { + return true; + } + + foundTypeIds.push_back(componentClass->m_typeId); + } + + return true; + }); + + return foundTypeIds; + } + AZStd::vector EditorComponentAPIComponent::FindComponentTypeNames(const AZ::ComponentTypeList& componentTypeIds) { AZStd::vector foundTypeNames; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.h index 2b74ea98c5..3e7eede77f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.h @@ -35,6 +35,7 @@ namespace AzToolsFramework // EditorComponentAPIBus ... AZStd::vector FindComponentTypeIdsByEntityType(const AZStd::vector& componentTypeNames, EditorComponentAPIRequests::EntityType entityType) override; + AZStd::vector FindComponentTypeIdsByService(const AZStd::vector& serviceFilter, const AZStd::vector& incompatibleServiceFilter) override; AZStd::vector FindComponentTypeNames(const AZ::ComponentTypeList& componentTypeIds) override; AZStd::vector BuildComponentTypeNameListByEntityType(EditorComponentAPIRequests::EntityType entityType) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp index 8d40162f52..28d6e2bc08 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp @@ -271,6 +271,68 @@ namespace AzToolsFramework return editorComponentBaseComponent; } + bool OffersRequiredServices( + const AZ::SerializeContext::ClassData* componentClass, + const AZStd::vector& serviceFilter, + const AZStd::vector& incompatibleServiceFilter + ) + { + AZ_Assert(componentClass, "Component class must not be null"); + + if (!componentClass) + { + return false; + } + + AZ::ComponentDescriptor* componentDescriptor = nullptr; + AZ::ComponentDescriptorBus::EventResult( + componentDescriptor, componentClass->m_typeId, &AZ::ComponentDescriptor::GetDescriptor); + if (!componentDescriptor) + { + return false; + } + + // If no services are provided, this function returns true + if (serviceFilter.empty()) + { + return true; + } + + AZ::ComponentDescriptor::DependencyArrayType providedServices; + componentDescriptor->GetProvidedServices(providedServices, nullptr); + + //reject this component if it does not offer any of the required services + if (AZStd::find_first_of( + providedServices.begin(), + providedServices.end(), + serviceFilter.begin(), + serviceFilter.end()) == providedServices.end()) + { + return false; + } + + //reject this component if it does offer any of the incompatible services + if (AZStd::find_first_of( + providedServices.begin(), + providedServices.end(), + incompatibleServiceFilter.begin(), + incompatibleServiceFilter.end()) != providedServices.end()) + { + return false; + } + + return true; + } + + bool OffersRequiredServices( + const AZ::SerializeContext::ClassData* componentClass, + const AZStd::vector& serviceFilter + ) + { + const AZStd::vector incompatibleServices; + return OffersRequiredServices(componentClass, serviceFilter, incompatibleServices); + } + bool ShouldInspectorShowComponent(const AZ::Component* component) { if (!component) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h index 73acb2deb0..c2d693c553 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h @@ -105,6 +105,16 @@ namespace AzToolsFramework AZ::ComponentDescriptor* GetComponentDescriptor(const AZ::Component* component); Components::EditorComponentDescriptor* GetEditorComponentDescriptor(const AZ::Component* component); Components::EditorComponentBase* GetEditorComponent(AZ::Component* component); + // Returns true if the given component provides at least one of the services specified or no services are provided + bool OffersRequiredServices( + const AZ::SerializeContext::ClassData* componentClass, + const AZStd::vector& serviceFilter, + const AZStd::vector& incompatibleServiceFilter + ); + bool OffersRequiredServices( + const AZ::SerializeContext::ClassData* componentClass, + const AZStd::vector& serviceFilter + ); /// Return true if the editor should show this component to users, /// false if the component should be hidden from users. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp index 6f3727bdb4..b6f5b41b65 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp @@ -12,6 +12,7 @@ #include #include #include +#include AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags' needs to have dll-interface to be used by clients of class 'QLayoutItem' #include AZ_POP_DISABLE_WARNING @@ -20,67 +21,6 @@ namespace AzToolsFramework { namespace ComponentPaletteUtil { - bool OffersRequiredServices( - const AZ::SerializeContext::ClassData* componentClass, - const AZStd::vector& serviceFilter, - const AZStd::vector& incompatibleServiceFilter - ) - { - AZ_Assert(componentClass, "Component class must not be null"); - - if (!componentClass) - { - return false; - } - - AZ::ComponentDescriptor* componentDescriptor = nullptr; - EBUS_EVENT_ID_RESULT(componentDescriptor, componentClass->m_typeId, AZ::ComponentDescriptorBus, GetDescriptor); - if (!componentDescriptor) - { - return false; - } - - // If no services are provided, this function returns true - if (serviceFilter.empty()) - { - return true; - } - - AZ::ComponentDescriptor::DependencyArrayType providedServices; - componentDescriptor->GetProvidedServices(providedServices, nullptr); - - //reject this component if it does not offer any of the required services - if (AZStd::find_first_of( - providedServices.begin(), - providedServices.end(), - serviceFilter.begin(), - serviceFilter.end()) == providedServices.end()) - { - return false; - } - - //reject this component if it does offer any of the incompatible services - if (AZStd::find_first_of( - providedServices.begin(), - providedServices.end(), - incompatibleServiceFilter.begin(), - incompatibleServiceFilter.end()) != providedServices.end()) - { - return false; - } - - return true; - } - - bool OffersRequiredServices( - const AZ::SerializeContext::ClassData* componentClass, - const AZStd::vector& serviceFilter - ) - { - const AZStd::vector incompatibleServices; - return OffersRequiredServices(componentClass, serviceFilter, incompatibleServices); - } - bool IsAddableByUser(const AZ::SerializeContext::ClassData* componentClass) { AZ_Assert(componentClass, "component class must not be null"); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx index 2000d72c5c..dffb893c59 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx @@ -26,18 +26,6 @@ namespace AzToolsFramework using ComponentIconTable = AZStd::map; - // Returns true if the given component provides at least one of the services specified or no services are provided - bool OffersRequiredServices( - const AZ::SerializeContext::ClassData* componentClass, - const AZStd::vector& serviceFilter, - const AZStd::vector& incompatibleServiceFilter - ); - - bool OffersRequiredServices( - const AZ::SerializeContext::ClassData* componentClass, - const AZStd::vector& serviceFilter - ); - // Returns true if the given component is addable by the user bool IsAddableByUser(const AZ::SerializeContext::ClassData* componentClass); diff --git a/Code/Framework/AzToolsFramework/Tests/EntityInspectorTests.cpp b/Code/Framework/AzToolsFramework/Tests/EntityInspectorTests.cpp index 0fbc7eba98..d640a91eab 100644 --- a/Code/Framework/AzToolsFramework/Tests/EntityInspectorTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/EntityInspectorTests.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include // Inspector Test Includes @@ -313,17 +314,17 @@ namespace UnitTest AZ_TEST_ASSERT(testComponent1_ProvidedServices.size() == 1); const AZ::SerializeContext::ClassData* testComponent1_ClassData = context->FindClassData(testComponent1_typeId); - EXPECT_TRUE(AzToolsFramework::ComponentPaletteUtil::OffersRequiredServices(testComponent1_ClassData, testComponent1_ProvidedServices)); + EXPECT_TRUE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, testComponent1_ProvidedServices)); // Verify that OffersRequiredServices returns when given services provided by a different component AZ::ComponentDescriptor::DependencyArrayType testComponent2_ProvidedServices; Inspector_TestComponent2::GetProvidedServices(testComponent2_ProvidedServices); AZ_TEST_ASSERT(testComponent2_ProvidedServices.size() == 1); AZ_TEST_ASSERT(testComponent1_ProvidedServices != testComponent2_ProvidedServices); - EXPECT_FALSE(AzToolsFramework::ComponentPaletteUtil::OffersRequiredServices(testComponent1_ClassData, testComponent2_ProvidedServices)); + EXPECT_FALSE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, testComponent2_ProvidedServices)); // verify that OffersRequiredServices returns true when provided with an empty list of services - EXPECT_TRUE(AzToolsFramework::ComponentPaletteUtil::OffersRequiredServices(testComponent1_ClassData, AZ::ComponentDescriptor::DependencyArrayType())); + EXPECT_TRUE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, AZ::ComponentDescriptor::DependencyArrayType())); ////////////////////////////////////////////////////////////////////////// // TEST IsAddableByUser() From 136788bccfed237f51a9c41074d72a95a827db70 Mon Sep 17 00:00:00 2001 From: allisaurus <34254888+allisaurus@users.noreply.github.com> Date: Fri, 22 Oct 2021 08:53:40 -0700 Subject: [PATCH 134/152] Update env var setting instructions in AWS Gem test README (#4791) Signed-off-by: Stanko --- AutomatedTesting/Gem/PythonTests/AWS/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/PythonTests/AWS/README.md b/AutomatedTesting/Gem/PythonTests/AWS/README.md index 1bb36f178d..0d046cbe4c 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/README.md +++ b/AutomatedTesting/Gem/PythonTests/AWS/README.md @@ -8,11 +8,14 @@ ## Deploy CDK Applications 1. Go to the AWS IAM console and create an IAM role called o3de-automation-tests which adds your own account as as a trusted entity and uses the "AdministratorAccess" permissions policy. 2. Copy {engine_root}\scripts\build\Platform\Windows\deploy_cdk_applications.cmd to your engine root folder. -3. Open a new Command Prompt window at the engine root and set the following environment variables: +3. Open a new Command Prompt window at the engine root and set the following environment variables: +``` Set O3DE_AWS_PROJECT_NAME=AWSAUTO Set O3DE_AWS_DEPLOY_REGION=us-east-1 + Set O3DE_AWS_DEPLOY_ACCOUNT={your_aws_account_id} Set ASSUME_ROLE_ARN=arn:aws:iam::{your_aws_account_id}:role/o3de-automation-tests Set COMMIT_ID=HEAD +``` 4. In the same Command Prompt window, Deploy the CDK applications for AWS gems by running deploy_cdk_applications.cmd. ## Run Automation Tests From 1989316cac4d723048d85a31327ccfab2e04dd21 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 22 Oct 2021 09:11:28 -0700 Subject: [PATCH 135/152] Added toast notifications to the gem catalog --- .../Components/ToastNotification.cpp | 33 +++++++++ .../Components/ToastNotification.h | 3 + .../Components/ToastNotification.ui | 2 +- .../ToastNotificationConfiguration.h | 1 + .../Notifications/ToastNotificationsView.cpp | 10 +++ .../UI/Notifications/ToastNotificationsView.h | 3 + Code/Tools/ProjectManager/CMakeLists.txt | 2 +- .../Resources/ProjectManager.qrc | 1 + .../Resources/ProjectManager.qss | 18 +++++ Code/Tools/ProjectManager/Resources/gem.svg | 3 + .../Source/GemCatalog/GemCatalogScreen.cpp | 73 +++++++++++++++++++ .../Source/GemCatalog/GemCatalogScreen.h | 16 +++- .../Source/GemCatalog/GemModel.cpp | 35 ++++++++- .../Source/GemCatalog/GemModel.h | 3 + 14 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/gem.svg diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp index 670607900f..f79f355ccb 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace AzQtComponents { @@ -27,6 +28,13 @@ namespace AzQtComponents setAttribute(Qt::WA_ShowWithoutActivating); setAttribute(Qt::WA_DeleteOnClose); + m_borderRadius = toastConfiguration.m_borderRadius; + if (m_borderRadius > 0) + { + setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); + setAttribute(Qt::WA_TranslucentBackground); + } + m_ui->setupUi(this); QIcon toastIcon; @@ -53,6 +61,13 @@ namespace AzQtComponents m_ui->titleLabel->setText(toastConfiguration.m_title); m_ui->mainLabel->setText(toastConfiguration.m_description); + // hide the optional description if none is provided so the title is centered vertically + if (toastConfiguration.m_description.isEmpty()) + { + m_ui->mainLabel->setVisible(false); + m_ui->verticalLayout->removeWidget(m_ui->mainLabel); + } + m_lifeSpan.setInterval(aznumeric_cast(toastConfiguration.m_duration.count())); m_closeOnClick = toastConfiguration.m_closeOnClick; @@ -68,6 +83,24 @@ namespace AzQtComponents { } + void ToastNotification::paintEvent(QPaintEvent* event) + { + if (m_borderRadius > 0) + { + QPainter p(this); + p.setPen(Qt::transparent); + QColor painterColor; + painterColor.setRgbF(0, 0, 0, 255); + p.setBrush(painterColor); + p.setRenderHint(QPainter::Antialiasing); + p.drawRoundedRect(rect(), m_borderRadius, m_borderRadius); + } + else + { + QDialog::paintEvent(event); + } + } + void ToastNotification::ShowToastAtCursor() { QPoint globalCursorPos = QCursor::pos(); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h index eaf8a0751d..7f2701a803 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h @@ -52,6 +52,8 @@ namespace AzQtComponents void mousePressEvent(QMouseEvent* mouseEvent) override; bool eventFilter(QObject* object, QEvent* event) override; + void paintEvent(QPaintEvent* event) override; + public slots: void StartTimer(); void FadeOut(); @@ -65,6 +67,7 @@ namespace AzQtComponents bool m_closeOnClick; QTimer m_lifeSpan; + uint32_t m_borderRadius = 0; AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZStd::chrono::milliseconds m_fadeDuration; diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.ui b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.ui index 107281bdc2..7aefddbd98 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.ui +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.ui @@ -191,7 +191,7 @@ - 40 + 20 20 diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h index 05999017e4..5ace9d1be2 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h @@ -37,6 +37,7 @@ namespace AzQtComponents QString m_title; QString m_description; QString m_customIconImage; + uint32_t m_borderRadius = 0; AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds(5000); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp index 0ef0bf9a7b..e039230783 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp @@ -177,4 +177,14 @@ namespace AzToolsFramework DisplayQueuedNotification(); } } + + void ToastNotificationsView::SetOffset(const QPoint& offset) + { + m_offset = offset; + } + + void ToastNotificationsView::SetAnchorPoint(const QPointF& anchorPoint) + { + m_anchorPoint = anchorPoint; + } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h index c4220331d8..e13f129467 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h @@ -50,6 +50,9 @@ namespace AzToolsFramework void OnShow(); void UpdateToastPosition(); + void SetOffset(const QPoint& offset); + void SetAnchorPoint(const QPointF& anchorPoint); + private: ToastId CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration); void DisplayQueuedNotification(); diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index 28c1871616..d34abcbc6c 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -43,7 +43,7 @@ ly_add_target( 3rdParty::pybind11 AZ::AzCore AZ::AzFramework - AZ::AzQtComponents + AZ::AzToolsFramework ) ly_add_target( diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index aeaf9a9248..8dd7e4c9b5 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -40,5 +40,6 @@ Delete.svg Download.svg in_progress.gif + gem.svg
diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 298d2a749a..712c01aa02 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -61,6 +61,24 @@ QTabBar::tab:focus { color: #4082eb; } +#ToastNotification { + background-color: black; + border-radius: 20px; + border:1px solid #dddddd; + qproperty-minimumSize: 100px 50px; +} + +#ToastNotification #icon_frame { + border-radius: 4px; + qproperty-minimumSize: 44px 20px; +} + +#ToastNotification #iconLabel { + qproperty-minimumSize: 30px 20px; + qproperty-maximumSize: 30px 20px; + margin-left: 6px; +} + /************** General (Forms) **************/ #formLineEditWidget, diff --git a/Code/Tools/ProjectManager/Resources/gem.svg b/Code/Tools/ProjectManager/Resources/gem.svg new file mode 100644 index 0000000000..2b688d5db5 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/gem.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index cbe36400cf..69c5844e84 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -66,6 +66,9 @@ namespace O3DE::ProjectManager hLayout->addWidget(filterWidget); hLayout->addLayout(middleVLayout); hLayout->addWidget(m_gemInspector); + + m_notificationsView = AZStd::make_unique(this, AZ_CRC("GemCatalogNotificationsView")); + m_notificationsView->SetOffset(QPoint(10, 70)); } void GemCatalogScreen::ReinitForProject(const QString& projectPath) @@ -86,6 +89,7 @@ namespace O3DE::ProjectManager m_headerWidget->ReinitForProject(); connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter); + connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ @@ -94,6 +98,72 @@ namespace O3DE::ProjectManager }); } + void GemCatalogScreen::OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies) + { + if (m_notificationsEnabled) + { + bool added = GemModel::IsAdded(modelIndex); + bool dependency = GemModel::IsAddedDependency(modelIndex); + + bool gemStateChanged = (added && !dependency) || (!added && !dependency); + if (!gemStateChanged && !numChangedDependencies) + { + // no actual changes made + return; + } + + QString notification; + if (gemStateChanged) + { + notification = GemModel::GetDisplayName(modelIndex); + if (numChangedDependencies > 0) + { + notification += " " + tr("and") + " "; + } + } + + if (numChangedDependencies == 1 ) + { + notification += "1 Gem " + tr("dependency"); + } + else if (numChangedDependencies > 1) + { + notification += QString("%d Gem ").arg(numChangedDependencies) + tr("dependencies"); + } + notification += " " + (added ? tr("activated") : tr("deactivated")); + + AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Custom, notification, ""); + toastConfiguration.m_customIconImage = ":/gem.svg"; + toastConfiguration.m_borderRadius = 4; + toastConfiguration.m_duration = AZStd::chrono::milliseconds(3000); + m_notificationsView->ShowToastNotification(toastConfiguration); + } + } + + void GemCatalogScreen::hideEvent(QHideEvent* event) + { + ScreenWidget::hideEvent(event); + m_notificationsView->OnHide(); + } + + void GemCatalogScreen::showEvent(QShowEvent* event) + { + ScreenWidget::showEvent(event); + m_notificationsView->OnShow(); + } + + void GemCatalogScreen::resizeEvent(QResizeEvent* event) + { + ScreenWidget::resizeEvent(event); + m_notificationsView->UpdateToastPosition(); + } + + void GemCatalogScreen::moveEvent(QMoveEvent* event) + { + ScreenWidget::moveEvent(event); + m_notificationsView->UpdateToastPosition(); + } + void GemCatalogScreen::FillModel(const QString& projectPath) { AZ::Outcome, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); @@ -107,6 +177,7 @@ namespace O3DE::ProjectManager } m_gemModel->UpdateGemDependencies(); + m_notificationsEnabled = false; // Gather enabled gems for the given project. auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath); @@ -133,6 +204,8 @@ namespace O3DE::ProjectManager { QMessageBox::critical(nullptr, tr("Operation failed"), QString("Cannot retrieve enabled gems for project %1.\n\nError:\n%2").arg(projectPath, enabledGemNamesResult.GetError().c_str())); } + + m_notificationsEnabled = true; } else { diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 456a5fe91c..62528ba942 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -10,6 +10,8 @@ #if !defined(Q_MOC_RUN) #include +#include +#include #include #include #include @@ -41,13 +43,24 @@ namespace O3DE::ProjectManager GemModel* GetGemModel() const { return m_gemModel; } DownloadController* GetDownloadController() const { return m_downloadController; } + public slots: + void OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies); + + protected: + void hideEvent(QHideEvent* event) override; + void showEvent(QShowEvent* event) override; + void resizeEvent(QResizeEvent* event) override; + void moveEvent(QMoveEvent* event) override; + private slots: void HandleOpenGemRepo(); - private: + private: void FillModel(const QString& projectPath); + AZStd::unique_ptr m_notificationsView; + GemListView* m_gemListView = nullptr; GemInspector* m_gemInspector = nullptr; GemModel* m_gemModel = nullptr; @@ -56,5 +69,6 @@ namespace O3DE::ProjectManager QVBoxLayout* m_filterWidgetLayout = nullptr; GemFilterWidget* m_filterWidget = nullptr; DownloadController* m_downloadController = nullptr; + bool m_notificationsEnabled = true; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 35491f4ddd..acdef483ae 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -299,23 +300,50 @@ namespace O3DE::ProjectManager AZ_Assert(gemModel, "Failed to obtain GemModel"); QVector dependencies = gemModel->GatherGemDependencies(modelIndex); + uint32_t numChangedDependencies = 0; + if (IsAdded(modelIndex)) { for (const QModelIndex& dependency : dependencies) { - SetIsAddedDependency(*gemModel, dependency, true); + if (!IsAddedDependency(dependency)) + { + SetIsAddedDependency(*gemModel, dependency, true); + + // if the gem was already added then the state didn't really change + if (!IsAdded(dependency)) + { + numChangedDependencies++; + } + } } } else { // still a dependency if some added gem depends on this one - SetIsAddedDependency(model, modelIndex, gemModel->HasDependentGems(modelIndex)); + bool hasDependentGems = gemModel->HasDependentGems(modelIndex); + if (IsAddedDependency(modelIndex) != hasDependentGems) + { + SetIsAddedDependency(model, modelIndex, hasDependentGems); + } for (const QModelIndex& dependency : dependencies) { - SetIsAddedDependency(*gemModel, dependency, gemModel->HasDependentGems(dependency)); + hasDependentGems = gemModel->HasDependentGems(dependency); + if (IsAddedDependency(dependency) != hasDependentGems) + { + SetIsAddedDependency(*gemModel, dependency, hasDependentGems); + + // if the gem was already added then the state didn't really change + if (!IsAdded(dependency)) + { + numChangedDependencies++; + } + } } } + + gemModel->emit gemStatusChanged(modelIndex, numChangedDependencies); } void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded) @@ -488,5 +516,4 @@ namespace O3DE::ProjectManager } return result; } - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 0d1c225f74..938543eb39 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -77,6 +77,9 @@ namespace O3DE::ProjectManager int TotalAddedGems(bool includeDependencies = false) const; + signals: + void gemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies); + private: void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames); void GetAllDependingGems(const QModelIndex& modelIndex, QSet& inOutGems); From 993baeac7bf667a1964f343732db8e8e75afd645 Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Fri, 22 Oct 2021 09:37:04 -0700 Subject: [PATCH 136/152] Add a comment to inform that the process is limited to a single download Signed-off-by: AMZN-Phil --- Code/Tools/ProjectManager/Source/PythonBindings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 8b716666de..0e24d46815 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -1124,6 +1124,7 @@ namespace O3DE::ProjectManager AZ::Outcome PythonBindings::DownloadGem(const QString& gemName, std::function gemProgressCallback) { + // This process is currently limited to download a single gem at a time. bool downloadSucceeded = false; O3DEProjectManagerPy::currentProgressCallback = gemProgressCallback; O3DEProjectManagerPy::requestCancelDownload = false; From 476ff637c2c3dcf77b9f68a11cbe08338156f1ab Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Fri, 22 Oct 2021 09:47:39 -0700 Subject: [PATCH 137/152] Undo P4 check before spawning thread and fix usage of new AzToolsApplication trait (#4894) Signed-off-by: Steve Pham --- .../SourceControl/PerforceComponent.cpp | 20 +++++-------------- .../SourceControl/PerforceComponent.h | 2 -- .../Application/AtomToolsApplication.cpp | 4 +++- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.cpp index acb2cd4b1c..6f6f8dccff 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.cpp @@ -80,14 +80,7 @@ namespace AzToolsFramework // set up signals before we start thread. m_shutdownThreadSignal = false; - - // Check to see if the 'p4' command is available at the command line - int p4VersionExitCode = QProcess::execute("p4", QStringList{ "-V" }); - m_p4ApplicationDetected = (p4VersionExitCode == 0); - if (m_p4ApplicationDetected) - { - m_WorkerThread = AZStd::thread(AZStd::bind(&PerforceComponent::ThreadWorker, this)); - } + m_WorkerThread = AZStd::thread(AZStd::bind(&PerforceComponent::ThreadWorker, this)); SourceControlConnectionRequestBus::Handler::BusConnect(); SourceControlCommandBus::Handler::BusConnect(); @@ -98,13 +91,10 @@ namespace AzToolsFramework SourceControlCommandBus::Handler::BusDisconnect(); SourceControlConnectionRequestBus::Handler::BusDisconnect(); - if (m_p4ApplicationDetected) - { - m_shutdownThreadSignal = true; // tell the thread to die. - m_WorkerSemaphore.release(1); // wake up the thread so that it sees the signal - m_WorkerThread.join(); // wait for the thread to finish. - m_WorkerThread = AZStd::thread(); - } + m_shutdownThreadSignal = true; // tell the thread to die. + m_WorkerSemaphore.release(1); // wake up the thread so that it sees the signal + m_WorkerThread.join(); // wait for the thread to finish. + m_WorkerThread = AZStd::thread(); SetConnection(nullptr); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.h index e5ccf7ad29..858d9c0103 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/SourceControl/PerforceComponent.h @@ -260,7 +260,5 @@ namespace AzToolsFramework AZStd::atomic_bool m_validConnection; SourceControlState m_connectionState; - - bool m_p4ApplicationDetected { false }; }; } // namespace AzToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 58b07d8351..e8ec77e7bd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -36,6 +36,8 @@ #include #include +#include "AtomToolsFramework_Traits_Platform.h" + AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include #include @@ -217,7 +219,7 @@ namespace AtomToolsFramework AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor); #if AZ_TRAIT_ATOMTOOLSFRAMEWORK_SKIP_APP_DESTROY - _exit(0); + ::_exit(0); #else Base::Destroy(); #endif From 8c6900eb0658382d99211d586d3b4e32c24ee354 Mon Sep 17 00:00:00 2001 From: mrieggeramzn <61609885+mrieggeramzn@users.noreply.github.com> Date: Fri, 22 Oct 2021 09:48:04 -0700 Subject: [PATCH 138/152] Atom/mriegger/normaloffsetbias (#4841) * Adding directional light shadow bias Signed-off-by: mrieggeramzn * Adding normal offset bias to the directional shadow maps Signed-off-by: mrieggeramzn * not time yet for this Signed-off-by: mrieggeramzn * small comment fix Signed-off-by: mrieggeramzn * fixing tabs Signed-off-by: mrieggeramzn --- .../Materials/Special/ShadowCatcher.azsl | 1 + .../Types/EnhancedPBR_ForwardPass.azsl | 10 +++++---- .../StandardMultilayerPBR_ForwardPass.azsl | 7 +++--- .../Types/StandardPBR_ForwardPass.azsl | 6 +++-- .../Shadow/DirectionalLightShadow.azsli | 18 ++++++++++----- .../Features/Shadow/NormalOffsetShadows.azsli | 22 +++++++++++++++++++ .../Atom/Features/Vertex/VertexHelper.azsli | 1 + .../CoreLights/ViewSrg.azsli | 4 ++-- ...irectionalLightFeatureProcessorInterface.h | 3 +++ ...ProjectedShadowFeatureProcessorInterface.h | 8 ++++--- .../DirectionalLightFeatureProcessor.cpp | 9 ++++++++ .../DirectionalLightFeatureProcessor.h | 8 +++++-- .../ProjectedShadowFeatureProcessor.cpp | 8 +++++++ .../Shadows/ProjectedShadowFeatureProcessor.h | 4 +++- .../CoreLights/DirectionalLightBus.h | 8 +++++++ .../DirectionalLightComponentConfig.h | 3 +++ .../DirectionalLightComponentConfig.cpp | 3 ++- .../DirectionalLightComponentController.cpp | 20 ++++++++++++++++- .../DirectionalLightComponentController.h | 4 +++- .../CoreLights/EditorAreaLightComponent.cpp | 2 +- .../EditorDirectionalLightComponent.cpp | 9 +++++++- .../Assets/Shaders/HairLightTypes.azsli | 2 +- .../Terrain/TerrainPBR_ForwardPass.azsl | 1 + 23 files changed, 132 insertions(+), 29 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/NormalOffsetShadows.azsli diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl index 69f86cbe9f..1fc8a410a1 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl @@ -54,6 +54,7 @@ VSOutput ShadowCatcherVS(VSInput IN) DirectionalLightShadow::GetShadowCoords( ViewSrg::m_shadowIndexDirectionalLight, worldPosition, + OUT.m_worldNormal, OUT.m_shadowCoords); return OUT; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl index 11859de7d6..0f9771e480 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl @@ -112,13 +112,15 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN) PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depth) { + const float3 vertexNormal = normalize(IN.m_normal); + // ------- Tangents & Bitangets ------- float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) || o_normal_useTexture || (o_clearCoat_enabled && o_clearCoat_normal_useTexture) || o_detail_normal_useTexture) { - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); + PrepareGeneratedTangent(vertexNormal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Depth & Parallax ------- @@ -137,7 +139,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_heightmapScale, MaterialSrg::m_heightmapOffset, + GetParallaxInput(vertexNormal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_heightmapScale, MaterialSrg::m_heightmapOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, IN.m_position.w, displacementIsClipped); @@ -150,7 +152,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight; if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount) { - DirectionalLightShadow::GetShadowCoords(shadowIndex, IN.m_worldPosition, IN.m_shadowCoords); + DirectionalLightShadow::GetShadowCoords(shadowIndex, IN.m_worldPosition, vertexNormal, IN.m_shadowCoords); } } } @@ -185,7 +187,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float2 normalUv = IN.m_uv[MaterialSrg::m_normalMapUvIndex]; float3x3 uvMatrix = MaterialSrg::m_normalMapUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); // By design, only UV0 is allowed to apply transforms. float detailLayerNormalFactor = MaterialSrg::m_detail_normal_factor * detailLayerBlendFactor; - surface.vertexNormal = normalize(IN.m_normal); + surface.vertexNormal = vertexNormal; surface.normal = GetDetailedNormalInputWS( isFrontFace, IN.m_normal, tangents[MaterialSrg::m_normalMapUvIndex], bitangents[MaterialSrg::m_normalMapUvIndex], MaterialSrg::m_normalMap, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_normalFactor, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, uvMatrix, o_normal_useTexture, diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index 6a97c0e785..a53dab7a01 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -302,6 +302,7 @@ ProcessedMaterialInputs ProcessStandardMaterialInputs(StandardMaterialInputs inp PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depthNDC) { + const float3 vertexNormal = normalize(IN.m_normal); depthNDC = IN.m_position.z; s_blendMaskFromVertexStream = IN.m_blendMask; @@ -321,7 +322,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float || o_layer3_o_clearCoat_normal_useTexture ) { - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); + PrepareGeneratedTangent(vertexNormal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Debug Modes ------- @@ -368,7 +369,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight; if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount) { - DirectionalLightShadow::GetShadowCoords(shadowIndex, IN.m_worldPosition, IN.m_shadowCoords); + DirectionalLightShadow::GetShadowCoords(shadowIndex, IN.m_worldPosition, vertexNormal, IN.m_shadowCoords); } } } @@ -445,7 +446,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer3.m_normalTS); } // [GFX TODO][ATOM-14591]: This will only work if the normal maps all use the same UV stream. We would need to add support for having them in different UV streams. - surface.vertexNormal = normalize(IN.m_normal); + surface.vertexNormal = vertexNormal; surface.normal = normalize(TangentSpaceToWorld(normalTS, IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex])); // ------- Combine Albedo, roughness, specular, roughness --------- diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index 8df5e1ba56..1d5e4ad9e3 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -98,6 +98,8 @@ VSOutput StandardPbr_ForwardPassVS(VSInput IN) PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depthNDC) { + const float3 vertexNormal = normalize(IN.m_normal); + // ------- Tangents & Bitangets ------- float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; @@ -128,7 +130,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight; if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount) { - DirectionalLightShadow::GetShadowCoords(shadowIndex, IN.m_worldPosition, IN.m_shadowCoords); + DirectionalLightShadow::GetShadowCoords(shadowIndex, IN.m_worldPosition, vertexNormal, IN.m_shadowCoords); } } } @@ -146,7 +148,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float2 normalUv = IN.m_uv[MaterialSrg::m_normalMapUvIndex]; float3x3 uvMatrix = MaterialSrg::m_normalMapUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); // By design, only UV0 is allowed to apply transforms. - surface.vertexNormal = normalize(IN.m_normal); + surface.vertexNormal = vertexNormal; surface.normal = GetNormalInputWS(MaterialSrg::m_normalMap, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, isFrontFace, IN.m_normal, tangents[MaterialSrg::m_normalMapUvIndex], bitangents[MaterialSrg::m_normalMapUvIndex], uvMatrix, o_normal_useTexture, MaterialSrg::m_normalFactor); diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli index b53dda13aa..a7122aaf3a 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli @@ -14,6 +14,7 @@ #include "ShadowmapAtlasLib.azsli" #include "BicubicPcfFilters.azsli" #include "ReceiverPlaneDepthBias.azsli" +#include "NormalOffsetShadows.azsli" // Before including this azsli file, a PassSrg must be defined with the following members: // Texture2DArray m_directionalLightShadowmap; @@ -45,6 +46,7 @@ class DirectionalLightShadow static void GetShadowCoords( uint lightIndex, float3 worldPosition, + float3 worldNormal, out float3 shadowCoords[ViewSrg::MaxCascadeCount]); //! This calculates visibility ratio of the surface from the light origin. @@ -109,18 +111,22 @@ class DirectionalLightShadow void DirectionalLightShadow::GetShadowCoords( uint lightIndex, float3 worldPosition, + float3 worldNormal, out float3 shadowCoords[ViewSrg::MaxCascadeCount]) { - const uint cascadeCount = ViewSrg::m_directionalLightShadows[lightIndex].m_cascadeCount; const float shadowBias = ViewSrg::m_directionalLightShadows[lightIndex].m_shadowBias; + const float4x4 lightViewToShadowmapMatrices[ViewSrg::MaxCascadeCount] = ViewSrg::m_directionalLightShadows[lightIndex].m_lightViewToShadowmapMatrices; const float4x4 worldToLightViewMatrices[ViewSrg::MaxCascadeCount] = ViewSrg::m_directionalLightShadows[lightIndex].m_worldToLightViewMatrices; - - for (uint index = 0; index < cascadeCount; ++index) - { - float4 lightSpacePos = mul(worldToLightViewMatrices[index], float4(worldPosition, 1.)); - lightSpacePos.z += shadowBias; + const uint cascadeCount = ViewSrg::m_directionalLightShadows[lightIndex].m_cascadeCount; + const float3 shadowOffset = ComputeNormalShadowOffset(ViewSrg::m_directionalLightShadows[lightIndex].m_normalShadowBias, worldNormal, ViewSrg::m_directionalLightShadows[lightIndex].m_shadowmapSize); + + for (uint index = 0; index < cascadeCount; ++index) + { + float4 lightSpacePos = mul(worldToLightViewMatrices[index], float4(worldPosition + shadowOffset, 1.)); + lightSpacePos.z += shadowBias; + const float4 clipSpacePos = mul(lightViewToShadowmapMatrices[index], lightSpacePos); shadowCoords[index] = clipSpacePos.xyz / clipSpacePos.w; } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/NormalOffsetShadows.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/NormalOffsetShadows.azsli new file mode 100644 index 0000000000..1e78cec89e --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/NormalOffsetShadows.azsli @@ -0,0 +1,22 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +// Helper functions for normal offset shadow mapping. +// Normal Offset is an alternative to slope-scale depth bias. +// We bias the shadow map lookup by transforming the world-position along the geometric normal before hand. +// http://web.archive.org/web/20140810230446/https://www.dissidentlogic.com/old/#Normal%20Offset%20Shadows +// + +// Apply the following to the world position. Then use this modified world position to look up in the shadow map +float3 ComputeNormalShadowOffset(const float normalOffsetBias, const float3 worldNormal, const float shadowMapDimension) +{ + const float shadowmapSize = 2.0f / shadowMapDimension; + return float3(worldNormal * normalOffsetBias * shadowmapSize); +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Vertex/VertexHelper.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Vertex/VertexHelper.azsli index 4b5047d4c3..c93986cea1 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Vertex/VertexHelper.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Vertex/VertexHelper.azsli @@ -47,6 +47,7 @@ void VertexHelper(in VSInput IN, inout VSOutput OUT, float3 worldPosition, bool DirectionalLightShadow::GetShadowCoords( shadowIndex, worldPosition, + OUT.m_normal, OUT.m_shadowCoords); } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli index 27fa36182c..b3bcc186b6 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli @@ -81,10 +81,10 @@ partial ShaderResourceGroup ViewSrg uint m_shadowmapArraySlice; // array slice who has shadowmap in the atlas. uint m_shadowFilterMethod; float m_boundaryScale; - uint m_predictionSampleCount; uint m_filteringSampleCount; float2 m_unprojectConstants; float m_bias; + float m_normalShadowBias; float m_esmExponent; float3 m_padding; }; @@ -109,7 +109,7 @@ partial ShaderResourceGroup ViewSrg uint m_shadowmapSize; // width and height of shadowmap uint m_cascadeCount; float m_shadowBias; - uint m_predictionSampleCount; + float m_normalShadowBias; uint m_filteringSampleCount; uint m_debugFlags; uint m_shadowFilterMethod; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h index 2bba1338a1..fa987a7156 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h @@ -160,6 +160,9 @@ namespace AZ //! Reduces acne by applying a small amount of bias along shadow-space z. virtual void SetShadowBias(LightHandle handle, float bias) = 0; + + //! Reduces acne by biasing the shadowmap lookup along the geometric normal. + virtual void SetNormalShadowBias(LightHandle handle, float normalShadowBias) = 0; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h index 46560f435d..1517d655bb 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h @@ -48,11 +48,13 @@ namespace AZ::Render virtual void SetAspectRatio(ShadowId id, float aspectRatio) = 0; //! Sets the field of view for the shadow in radians in the Y direction. virtual void SetFieldOfViewY(ShadowId id, float fieldOfView) = 0; - //! Sets the maximum resolution of the shadow map + //! Sets the maximum resolution of the shadow map. virtual void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) = 0; - //! Sets the shadow bias + //! Sets the shadow bias. virtual void SetShadowBias(ShadowId id, float bias) = 0; - //! Sets the shadow filter method + //! Sets the normal shadow bias. + virtual void SetNormalShadowBias(ShadowId id, float normalShadowBias) = 0; + //! Sets the shadow filter method. virtual void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) = 0; //! Sets the sample count for filtering of the shadow boundary, max 64. virtual void SetFilteringSampleCount(ShadowId id, uint16_t count) = 0; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 4774ac31a1..6c24b7d35b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -598,6 +598,15 @@ namespace AZ m_shadowBufferNeedsUpdate = true; } + void DirectionalLightFeatureProcessor::SetNormalShadowBias(LightHandle handle, float normalShadowBias) + { + for (auto& it : m_shadowData) + { + it.second.GetData(handle.GetIndex()).m_normalShadowBias = normalShadowBias; + } + m_shadowBufferNeedsUpdate = true; + } + void DirectionalLightFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) { PrepareForChangingRenderPipelineAndCameraView(); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h index 2cf5b0b1e6..8d7a9d76e4 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h @@ -92,8 +92,9 @@ namespace AZ uint32_t m_shadowmapSize = 1; // width and height of shadowmap uint32_t m_cascadeCount = 1; // Reduce acne by applying a small amount of bias to apply along shadow-space z. - float m_shadowBias = 0.0f; - uint32_t m_predictionSampleCount = 0; + float m_shadowBias = 0.0f; + // Reduces acne by biasing the shadowmap lookup along the geometric normal. + float m_normalShadowBias; uint32_t m_filteringSampleCount = 0; uint32_t m_debugFlags = 0; uint32_t m_shadowFilterMethod = 0; @@ -101,6 +102,8 @@ namespace AZ float m_padding[3]; }; + static_assert(sizeof(DirectionalLightShadowData) % 16 == 0); // Structured buffers need alignment to be a multiple of 16 bytes. + class DirectionalLightFeatureProcessor final : public DirectionalLightFeatureProcessorInterface { @@ -216,6 +219,7 @@ namespace AZ void SetFilteringSampleCount(LightHandle handle, uint16_t count) override; void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) override; void SetShadowBias(LightHandle handle, float bias) override; + void SetNormalShadowBias(LightHandle handle, float normalShadowBias) override; const Data::Instance GetLightBuffer() const; uint32_t GetLightCount() const; diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index da92cc04e7..7c0b3563c7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -151,6 +151,14 @@ namespace AZ::Render shadowProperty.m_bias = bias; } + void ProjectedShadowFeatureProcessor::SetNormalShadowBias(ShadowId id, float normalShadowBias) + { + AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetNormalShadowBias()."); + + ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); + shadowProperty.m_normalShadowBias = normalShadowBias; + } + void ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution()."); diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index 6269166827..fafcb25a08 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -48,6 +48,7 @@ namespace AZ::Render void SetFieldOfViewY(ShadowId id, float fieldOfViewYRadians) override; void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) override; void SetShadowBias(ShadowId id, float bias) override; + void SetNormalShadowBias(ShadowId id, float normalShadowBias) override; void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) override; void SetFilteringSampleCount(ShadowId id, uint16_t count) override; void SetShadowProperties(ShadowId id, const ProjectedShadowDescriptor& descriptor) override; @@ -64,10 +65,10 @@ namespace AZ::Render uint32_t m_shadowmapArraySlice = 0; // array slice who has shadowmap in the atlas. uint32_t m_shadowFilterMethod = 0; // filtering method of shadows. float m_boundaryScale = 0.f; // the half of boundary of lit/shadowed areas. (in degrees) - uint32_t m_predictionSampleCount = 0; // sample count to judge whether it is on the shadow boundary or not. uint32_t m_filteringSampleCount = 0; AZStd::array m_unprojectConstants = { {0, 0} }; float m_bias; + float m_normalShadowBias; float m_esmExponent = 87.0f; float m_padding[3]; }; @@ -78,6 +79,7 @@ namespace AZ::Render ProjectedShadowDescriptor m_desc; RPI::ViewPtr m_shadowmapView; float m_bias = 0.1f; + float m_normalShadowBias = 0.0f; ShadowId m_shadowId; }; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h index 9ccc4f329b..3cafc183a5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h @@ -176,6 +176,14 @@ namespace AZ //! Shadow bias reduces acne by applying a small amount of offset along shadow-space z. //! @param Sets the amount of bias to apply. virtual void SetShadowBias(float bias) = 0; + + //! Reduces acne by biasing the shadowmap lookup along the geometric normal. + //! @return Returns the amount of bias to apply. + virtual float GetNormalShadowBias() const = 0; + + //! Reduces acne by biasing the shadowmap lookup along the geometric normal. + //! @param normalShadowBias Sets the amount of normal shadow bias to apply. + virtual void SetNormalShadowBias(float normalShadowBias) = 0; }; using DirectionalLightRequestBus = EBus; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h index 20073f437c..92d5cc9ac0 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h @@ -101,6 +101,9 @@ namespace AZ //! Method of shadow's filtering. ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None; + // Reduces acne by biasing the shadowmap lookup along the geometric normal. + float m_normalShadowBias = 0.0f; + //! Sample Count for filtering (from 4 to 64) //! It is used only when the pixel is predicted as on the boundary. uint16_t m_filteringSampleCount = 32; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp index 98d3f838c0..78a9cc21d1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp @@ -39,7 +39,8 @@ namespace AZ ->Field("ShadowFilterMethod", &DirectionalLightComponentConfig::m_shadowFilterMethod) ->Field("PcfFilteringSampleCount", &DirectionalLightComponentConfig::m_filteringSampleCount) ->Field("ShadowReceiverPlaneBiasEnabled", &DirectionalLightComponentConfig::m_receiverPlaneBiasEnabled) - ->Field("Shadow Bias", &DirectionalLightComponentConfig::m_shadowBias); + ->Field("Shadow Bias", &DirectionalLightComponentConfig::m_shadowBias) + ->Field("Normal Shadow Bias", &DirectionalLightComponentConfig::m_normalShadowBias); } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp index c20a9f8e17..e36868c4bb 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp @@ -86,6 +86,8 @@ namespace AZ ->Event("SetShadowReceiverPlaneBiasEnabled", &DirectionalLightRequestBus::Events::SetShadowReceiverPlaneBiasEnabled) ->Event("GetShadowBias", &DirectionalLightRequestBus::Events::GetShadowBias) ->Event("SetShadowBias", &DirectionalLightRequestBus::Events::SetShadowBias) + ->Event("GetNormalShadowBias", &DirectionalLightRequestBus::Events::GetNormalShadowBias) + ->Event("SetNormalShadowBias", &DirectionalLightRequestBus::Events::SetNormalShadowBias) ->VirtualProperty("Color", "GetColor", "SetColor") ->VirtualProperty("Intensity", "GetIntensity", "SetIntensity") ->VirtualProperty("AngularDiameter", "GetAngularDiameter", "SetAngularDiameter") @@ -101,7 +103,8 @@ namespace AZ ->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod") ->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount") ->VirtualProperty("ShadowReceiverPlaneBiasEnabled", "GetShadowReceiverPlaneBiasEnabled", "SetShadowReceiverPlaneBiasEnabled") - ->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias"); + ->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias") + ->VirtualProperty("NormalShadowBias", "GetNormalShadowBias", "SetNormalShadowBias"); ; } } @@ -423,6 +426,20 @@ namespace AZ return m_configuration.m_shadowBias; } + void DirectionalLightComponentController::SetNormalShadowBias(float bias) + { + m_configuration.m_normalShadowBias = bias; + if (m_featureProcessor) + { + m_featureProcessor->SetNormalShadowBias(m_lightHandle, bias); + } + } + + float DirectionalLightComponentController::GetNormalShadowBias() const + { + return m_configuration.m_normalShadowBias; + } + void DirectionalLightComponentController::SetFilteringSampleCount(uint32_t count) { const uint16_t count16 = GetMin(Shadow::MaxPcfSamplingCount, aznumeric_cast(count)); @@ -517,6 +534,7 @@ namespace AZ SetDebugColoringEnabled(m_configuration.m_isDebugColoringEnabled); SetShadowFilterMethod(m_configuration.m_shadowFilterMethod); SetShadowBias(m_configuration.m_shadowBias); + SetNormalShadowBias(m_configuration.m_normalShadowBias); SetFilteringSampleCount(m_configuration.m_filteringSampleCount); SetShadowReceiverPlaneBiasEnabled(m_configuration.m_receiverPlaneBiasEnabled); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h index a0d552cf99..9a6edda666 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h @@ -81,7 +81,9 @@ namespace AZ bool GetShadowReceiverPlaneBiasEnabled() const override; void SetShadowReceiverPlaneBiasEnabled(bool enable) override; float GetShadowBias() const override; - void SetShadowBias(float width) override; + void SetShadowBias(float bias) override; + float GetNormalShadowBias() const override; + void SetNormalShadowBias(float bias) override; private: friend class EditorDirectionalLightComponent; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index 1e5b2580f7..db46434d9a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp @@ -136,7 +136,7 @@ namespace AZ ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 100.0f) ->Attribute(Edit::Attributes::SoftMin, 0.0f) - ->Attribute(Edit::Attributes::SoftMax, 1.0f) + ->Attribute(Edit::Attributes::SoftMax, 2.0f) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp index 99e2cc1485..545064b86f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp @@ -134,7 +134,7 @@ namespace AZ ->EnumAttribute(ShadowFilterMethod::EsmPcf, "ESM+PCF") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_filteringSampleCount, "Filtering sample count\n", - "This is used only when the pixel is predicted as on the boundary.\n" + "This is used only when the pixel is predicted to be on the boundary.\n" "Specific to PCF and ESM+PCF.") ->Attribute(Edit::Attributes::Min, 4) ->Attribute(Edit::Attributes::Max, 64) @@ -154,6 +154,13 @@ namespace AZ ->Attribute(Edit::Attributes::Min, 0.f) ->Attribute(Edit::Attributes::Max, 0.2) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement( + Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_normalShadowBias, "Normal Shadow Bias\n", + "Reduces acne by biasing the shadowmap lookup along the geometric normal.\n" + "If this is 0, no biasing is applied.") + ->Attribute(Edit::Attributes::Min, 0.f) + ->Attribute(Edit::Attributes::Max, 10.0f) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ; } } diff --git a/Gems/AtomTressFX/Assets/Shaders/HairLightTypes.azsli b/Gems/AtomTressFX/Assets/Shaders/HairLightTypes.azsli index 4b0fa654e3..ffabf7c298 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairLightTypes.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairLightTypes.azsli @@ -465,7 +465,7 @@ void ApplyLighting(inout Surface surface, inout LightingData lightingData) const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight; if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount) { - DirectionalLightShadow::GetShadowCoords(shadowIndex, surface.position, lightingData.shadowCoords); + DirectionalLightShadow::GetShadowCoords(shadowIndex, surface.position, surface.vertexNormal, lightingData.shadowCoords); } // Light loops application. diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl index 91e367500b..750cd2fb29 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl @@ -59,6 +59,7 @@ VSOutput TerrainPBR_MainPassVS(VertexInput IN) DirectionalLightShadow::GetShadowCoords( shadowIndex, worldPosition, + OUT.m_normal, OUT.m_shadowCoords); } From f962f3d8164e522c94756f22b6c667d214a724f9 Mon Sep 17 00:00:00 2001 From: hershey5045 <43485729+hershey5045@users.noreply.github.com> Date: Fri, 22 Oct 2021 09:53:48 -0700 Subject: [PATCH 139/152] White Balance Updates (#4887) * Implement simple tint for white balance color grading. Adjust temperature slider to feel linear. Signed-off-by: rbarrand * Change white balance luminance preservation equation and remove unused code. Signed-off-by: rbarrand Co-authored-by: rbarrand --- .../Features/PostProcessing/KelvinToRgb.azsli | 33 ------------------- .../PostProcessing/HDRColorGradingCommon.azsl | 21 +++++++++--- .../Shaders/ColorGrading/LutGeneration.azsl | 1 + .../PostProcessing/HDRColorGrading.azsl | 1 + .../ColorGrading/HDRColorGradingParams.inl | 1 + .../PostProcessing/HDRColorGradingPass.cpp | 3 +- .../PostProcessing/HDRColorGradingPass.h | 1 + .../EditorHDRColorGradingComponent.cpp | 4 +++ 8 files changed, 27 insertions(+), 38 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli index aa57f0f4a1..1c8b77df45 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli @@ -16,39 +16,6 @@ // licensed and released under Creative Commons 3.0 Attribution // https://creativecommons.org/licenses/by/3.0/ -float3 HueToRgb(float hue) -{ - return saturate(float3(abs(hue * 6.0f - 3.0f) - 1.0f, - 2.0f - abs(hue * 6.0f - 2.0f), - 2.0f - abs(hue * 6.0f - 4.0f))); -} - -float3 RgbToHcv(float3 rgb) -{ - // Based on work by Sam Hocevar and Emil Persson - const float4 p = (rgb.g < rgb.b) ? float4(rgb.bg, -1.0f, 2.0f/3.0f) : float4(rgb.gb, 0.0f, -1.0f/3.0f); - const float4 q1 = (rgb.r < p.x) ? float4(p.xyw, rgb.r) : float4(rgb.r, p.yzx); - const float c = q1.x - min(q1.w, q1.y); - const float h = abs((q1.w - q1.y) / (6.0f * c + 0.000001f ) + q1.z); - return float3(h, c, q1.x); -} - -float3 RgbToHsl(float3 rgb) -{ - rgb.xyz = max(rgb.xyz, 0.000001f); - const float3 hcv = RgbToHcv(rgb); - const float L = hcv.z - hcv.y * 0.5f; - const float S = hcv.y / (1.0f - abs(L * 2.0f - 1.0f) + 0.000001f); - return float3(hcv.x, S, L); -} - -float3 HslToRgb(float3 hsl) -{ - const float3 rgb = HueToRgb(hsl.x); - const float c = (1.0f - abs(2.0f * hsl.z - 1.0f)) * hsl.y; - return (rgb - 0.5f) * c + hsl.z; -} - // Color temperature float3 KelvinToRgb(float kelvin) { diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl index a380a0a547..423d447f7b 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl @@ -66,12 +66,21 @@ float3 ColorGradeSaturation (float3 frameColor, float control) return (frameColor - vLuminance) * control + vLuminance; } -float3 ColorGradeKelvinColorTemp(float3 frameColor, float kelvin) +float3 ColorGradeWhiteBalance(float3 frameColor, float kelvin, float tint, float luminancePreservation) { const float3 kColor = TransformColor(KelvinToRgb(kelvin), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); const float luminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg); - const float3 resHsl = RgbToHsl(frameColor.rgb * kColor.rgb); // Apply Kelvin color and convert to HSL - return HslToRgb(float3(resHsl.xy, luminance)); // Preserve luminance + + // Apply Kelvin color and tint and calculate the new luminance + float3 adjustedColor = frameColor.rgb * kColor.rgb; + adjustedColor.g = max(0.0, adjustedColor.g + tint * 0.001); + const float adjustedLuminance = CalculateLuminance(adjustedColor, ColorSpaceId::ACEScg); + + // Adjust the color based on the difference in luminance. + const float luminanceDifferenceRatio = luminance / adjustedLuminance; + const float3 adjustedColorLumPreserved = adjustedColor * luminanceDifferenceRatio; + + return lerp(adjustedColor, adjustedColorLumPreserved, luminancePreservation); } // pow(f, e) won't work if f is negative, or may cause inf/NAN. @@ -132,7 +141,11 @@ float3 ColorGradeShadowsMidtonesHighlights (float3 frameColor, float shadowsStar float3 ColorGrade(float3 frameColor) { frameColor = lerp(frameColor, ColorGradePostExposure(frameColor, PassSrg::m_colorGradingExposure), PassSrg::m_colorAdjustmentWeight); - frameColor = lerp(frameColor, ColorGradeKelvinColorTemp(frameColor, PassSrg::m_whiteBalanceKelvin), PassSrg::m_whiteBalanceWeight); + frameColor = lerp(frameColor, ColorGradeWhiteBalance( + frameColor, PassSrg::m_whiteBalanceKelvin, + PassSrg::m_whiteBalanceTint, + PassSrg::m_whiteBalanceLuminancePreservation), + PassSrg::m_whiteBalanceWeight); frameColor = lerp(frameColor, ColorGradingContrast(frameColor, AcesCcMidGrey, PassSrg::m_colorGradingContrast), PassSrg::m_colorAdjustmentWeight); frameColor = lerp(frameColor, ColorGradeColorFilter(frameColor, PassSrg::m_colorFilterSwatch.rgb, PassSrg::m_colorFilterMultiply, PassSrg::m_colorFilterIntensity), PassSrg::m_colorAdjustmentWeight); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.azsl index 6a76f58519..89655a0e91 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.azsl @@ -57,6 +57,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback float m_whiteBalanceWeight; float m_whiteBalanceKelvin; float m_whiteBalanceTint; + float m_whiteBalanceLuminancePreservation; float m_splitToneBalance; float m_splitToneWeight; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl index bc33a7e920..3167b214dc 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl @@ -40,6 +40,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback float m_whiteBalanceWeight; float m_whiteBalanceKelvin; float m_whiteBalanceTint; + float m_whiteBalanceLuminancePreservation; float m_splitToneBalance; float m_splitToneWeight; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl index c5865b0cfe..f41994fa77 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl @@ -22,6 +22,7 @@ AZ_GFX_VEC3_PARAM(ColorFilterSwatch, m_colorFilterSwatch, AZ::Vector3(1.0f, 0.5f AZ_GFX_FLOAT_PARAM(WhiteBalanceWeight, m_whiteBalanceWeight, 0.0) AZ_GFX_FLOAT_PARAM(WhiteBalanceKelvin, m_whiteBalanceKelvin, 6600.0) AZ_GFX_FLOAT_PARAM(WhiteBalanceTint, m_whiteBalanceTint, 0.0) +AZ_GFX_FLOAT_PARAM(WhiteBalanceLuminancePreservation, m_whiteBalanceLuminancePreservation, 1.0) AZ_GFX_FLOAT_PARAM(SplitToneWeight, m_splitToneWeight, 0.0) AZ_GFX_FLOAT_PARAM(SplitToneBalance, m_splitToneBalance, 0.0) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp index 88266ba789..a3347d0d84 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp @@ -43,6 +43,7 @@ m_whiteBalanceWeightIndex.Reset(); m_whiteBalanceKelvinIndex.Reset(); m_whiteBalanceTintIndex.Reset(); + m_whiteBalanceLuminancePreservationIndex.Reset(); m_splitToneBalanceIndex.Reset(); m_splitToneWeightIndex.Reset(); @@ -96,7 +97,7 @@ m_shaderResourceGroup->SetConstant(m_whiteBalanceWeightIndex, settings->GetWhiteBalanceWeight()); m_shaderResourceGroup->SetConstant(m_whiteBalanceKelvinIndex, settings->GetWhiteBalanceKelvin()); m_shaderResourceGroup->SetConstant(m_whiteBalanceTintIndex, settings->GetWhiteBalanceTint()); - + m_shaderResourceGroup->SetConstant(m_whiteBalanceLuminancePreservationIndex, settings->GetWhiteBalanceLuminancePreservation()); m_shaderResourceGroup->SetConstant(m_splitToneBalanceIndex, settings->GetSplitToneBalance()); m_shaderResourceGroup->SetConstant(m_splitToneWeightIndex, settings->GetSplitToneWeight()); m_shaderResourceGroup->SetConstant(m_splitToneShadowsColorIndex, AZ::Vector4(settings->GetSplitToneShadowsColor())); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h index dc706b8501..b89fa04531 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h @@ -55,6 +55,7 @@ namespace AZ RHI::ShaderInputNameIndex m_whiteBalanceWeightIndex = "m_whiteBalanceWeight"; RHI::ShaderInputNameIndex m_whiteBalanceKelvinIndex = "m_whiteBalanceKelvin"; RHI::ShaderInputNameIndex m_whiteBalanceTintIndex = "m_whiteBalanceTint"; + RHI::ShaderInputNameIndex m_whiteBalanceLuminancePreservationIndex = "m_whiteBalanceLuminancePreservation"; RHI::ShaderInputNameIndex m_splitToneBalanceIndex = "m_splitToneBalance"; RHI::ShaderInputNameIndex m_splitToneWeightIndex = "m_splitToneWeight"; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp index e32b26f0c5..3cc9535d7a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp @@ -100,9 +100,13 @@ namespace AZ ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_whiteBalanceKelvin, "Temperature", "Temperature in Kelvin") ->Attribute(Edit::Attributes::Min, 1000.0f) ->Attribute(Edit::Attributes::Max, 40000.0f) + ->Attribute(AZ::Edit::Attributes::SliderCurveMidpoint, 0.165f) ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_whiteBalanceTint, "Tint", "Tint Value") ->Attribute(Edit::Attributes::Min, -100.0f) ->Attribute(Edit::Attributes::Max, 100.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_whiteBalanceLuminancePreservation, "Luminance Preservation", "Modulate the preservation of luminance") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) ->ClassElement(AZ::Edit::ClassElements::Group, "Split Toning") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) From 0dfa08cac8739dba3b385c543108c80caa05e260 Mon Sep 17 00:00:00 2001 From: jiaweig <51759646+jiaweig-amzn@users.noreply.github.com> Date: Fri, 22 Oct 2021 10:46:21 -0700 Subject: [PATCH 140/152] ATOM-16625 [RHI][Vulkan] Swapchain creation issue on viewports (#4854) * Refactor Vulkan swapchain so it can recreate when error occurs Signed-off-by: jiaweig * revert the workaround Signed-off-by: jiaweig * Move semaphore. Revert some viewport changes. Signed-off-by: jiaweig * Added comments. Moved recreation out of AcquireNewImage. Signed-off-by: jiaweig --- .../RHI/Code/Include/Atom/RHI/SwapChain.h | 91 +++---- Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp | 6 +- .../RHI/Vulkan/Code/Source/RHI/SwapChain.cpp | 238 +++++++++++------- .../RHI/Vulkan/Code/Source/RHI/SwapChain.h | 36 ++- .../Source/Viewport/RenderViewportWidget.cpp | 4 - 5 files changed, 214 insertions(+), 161 deletions(-) diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h index 14e9968e42..97ac3baa90 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h @@ -14,92 +14,86 @@ namespace AZ { namespace RHI { - /** - * The platform-independent swap chain base class. Swap chains contain a "chain" of images which - * map to a platform-specific window, displayed on a physical monitor. The user is allowed - * to adjust the swap chain outside of the current FrameScheduler frame. Doing so within a frame scheduler - * frame results in undefined behavior. - * - * The frame scheduler controls presentation of the swap chain. The user may attach a swap chain to a scope - * in order to render to the current image. - */ + //! The platform-independent swap chain base class. Swap chains contain a "chain" of images which + //! map to a platform-specific window, displayed on a physical monitor. The user is allowed + //! to adjust the swap chain outside of the current FrameScheduler frame. Doing so within a frame scheduler + //! frame results in undefined behavior. + //! + //! The frame scheduler controls presentation of the swap chain. The user may attach a swap chain to a scope + //! in order to render to the current image. class SwapChain : public ImagePoolBase { public: + AZ_RTTI(SwapChain, "{888B64A5-D956-406F-9C33-CF6A54FC41B0}", Object); + virtual ~SwapChain(); - /// Initializes the swap chain, making it ready for attachment. + //! Initializes the swap chain, making it ready for attachment. ResultCode Init(RHI::Device& device, const SwapChainDescriptor& descriptor); - /// Presents the swap chain to the display, and rotates the images. + //! Presents the swap chain to the display, and rotates the images. void Present(); - /** - * Sets the vertical sync interval for the swap chain. - * 0 - No vsync. - * N - Sync to every N vertical refresh. - * - * A value of 1 syncs to the refresh rate of the monitor. - */ + //! Sets the vertical sync interval for the swap chain. + //! 0 - No vsync. + //! N - Sync to every N vertical refresh. + //! + //! A value of 1 syncs to the refresh rate of the monitor. void SetVerticalSyncInterval(uint32_t verticalSyncInterval); - /** - * Resizes the display resolution of the swap chain. Ideally, this matches the platform window - * resolution. Typically, the resize operation will occur in reaction to a platform window size - * change. Takes effect immediately and results in a GPU pipeline flush. - */ + //! Resizes the display resolution of the swap chain. Ideally, this matches the platform window + //! resolution. Typically, the resize operation will occur in reaction to a platform window size + //! change. Takes effect immediately and results in a GPU pipeline flush. ResultCode Resize(const SwapChainDimensions& dimensions); - /// Returns the number of images in the swap chain. + //! Returns the number of images in the swap chain. uint32_t GetImageCount() const; - /// Returns the current image index of the swap chain. + //! Returns the current image index of the swap chain. uint32_t GetCurrentImageIndex() const; - /// Returns the current image of the swap chain. + //! Returns the current image of the swap chain. Image* GetCurrentImage() const; - /// Returns the image associated with the provided index, where the total number of images - /// is given by GetImageCount(). + //! Returns the image associated with the provided index, where the total number of images + //! is given by GetImageCount(). Image* GetImage(uint32_t index) const; - /// Returns the ID used for the SwapChain's attachment + //! Returns the ID used for the SwapChain's attachment const AttachmentId& GetAttachmentId() const; - /// Returns the descriptor provided when initializing the swap chain. + //! Returns the descriptor provided when initializing the swap chain. const RHI::SwapChainDescriptor& GetDescriptor() const override final; - //! \return True if the swap chain prefers to use exclusive full screen mode. + //! Returns True if the swap chain prefers to use exclusive full screen mode. virtual bool IsExclusiveFullScreenPreferred() const { return false; } - //! \return True if the swap chain prefers exclusive full screen mode and it is currently true, false otherwise. + //! Returns True if the swap chain prefers exclusive full screen mode and it is currently true, false otherwise. virtual bool GetExclusiveFullScreenState() const { return false; } - //! \return True if the swap chain prefers exclusive full screen mode and a transition happened, false otherwise. + //! Return True if the swap chain prefers exclusive full screen mode and a transition happened, false otherwise. virtual bool SetExclusiveFullScreenState([[maybe_unused]]bool fullScreenState) { return false; } - AZ_RTTI(SwapChain, "{888B64A5-D956-406F-9C33-CF6A54FC41B0}", Object); - protected: SwapChain(); struct InitImageRequest { - /// Pointer to the image to initialize. + //! Pointer to the image to initialize. Image* m_image = nullptr; - /// Index of the image in the swap chain. + //! Index of the image in the swap chain. uint32_t m_imageIndex = 0; - /// Descriptor for the image. + //! Descriptor for the image. ImageDescriptor m_descriptor; }; ////////////////////////////////////////////////////////////////////////// // ResourcePool Overrides - /// Called when the pool is shutting down. + //! Called when the pool is shutting down. void ShutdownInternal() override; ////////////////////////////////////////////////////////////////////////// @@ -111,32 +105,29 @@ namespace AZ ////////////////////////////////////////////////////////////////////////// // Platform API - /// Called when the swap chain is initializing. + //! Called when the swap chain is initializing. virtual ResultCode InitInternal(RHI::Device& device, const SwapChainDescriptor& descriptor, SwapChainDimensions* nativeDimensions) = 0; - /// called when the swap chain is initializing an image. + //! called when the swap chain is initializing an image. virtual ResultCode InitImageInternal(const InitImageRequest& request) = 0; - /// Called when the swap chain is resizing. + //! Called when the swap chain is resizing. virtual ResultCode ResizeInternal(const SwapChainDimensions& dimensions, SwapChainDimensions* nativeDimensions) = 0; - /// Called when the swap chain is presenting the currently swap image. - /// Returns the index of the current image after the swap. + //! Called when the swap chain is presenting the currently swap image. + //! Returns the index of the current image after the swap. virtual uint32_t PresentInternal() = 0; - virtual void SetVerticalSyncIntervalInternal(uint32_t previousVerticalSyncInterval) - { - AZ_UNUSED(previousVerticalSyncInterval); - } + virtual void SetVerticalSyncIntervalInternal([[maybe_unused]]uint32_t previousVerticalSyncInterval) {} ////////////////////////////////////////////////////////////////////////// SwapChainDescriptor m_descriptor; - /// Images corresponding to each image in the swap chain. + //! Images corresponding to each image in the swap chain. AZStd::vector> m_images; - /// The current image index. + //! The current image index. uint32_t m_currentImageIndex = 0; }; } diff --git a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp index b0501d937d..ff1f0e69a6 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp @@ -55,7 +55,7 @@ namespace AZ if (resultCode == ResultCode::Success) { m_descriptor = descriptor; - // Ovewrite descriptor dimensions with the native ones (the ones assigned by the platform) returned by InitInternal. + // Overwrite descriptor dimensions with the native ones (the ones assigned by the platform) returned by InitInternal. m_descriptor.m_dimensions = nativeDimensions; m_images.reserve(m_descriptor.m_dimensions.m_imageCount); @@ -129,8 +129,8 @@ namespace AZ while (m_images.size() > static_cast(m_descriptor.m_dimensions.m_imageCount)) { m_images.pop_back(); - } - + } + InitImageRequest request; RHI::ImageDescriptor& imageDescriptor = request.m_descriptor; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp index 7040defca8..bef2b154e1 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp @@ -61,13 +61,12 @@ namespace AZ void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval) { - uint32_t verticalSyncInterval = GetDescriptor().m_verticalSyncInterval; - if (verticalSyncInterval == 0 || previousVsyncInterval == 0) + if (GetDescriptor().m_verticalSyncInterval == 0 || previousVsyncInterval == 0) { // The presentation mode may change when transitioning to or from a vsynced presentation mode // In this case, the swapchain must be recreated. InvalidateNativeSwapChain(); - BuildNativeSwapChain(GetDescriptor().m_dimensions, verticalSyncInterval); + CreateSwapchain(); } } @@ -85,46 +84,21 @@ namespace AZ RHI::DeviceObject::Init(baseDevice); auto& device = static_cast(GetDevice()); - RHI::SwapChainDimensions swapchainDimensions = descriptor.m_dimensions; + m_dimensions = descriptor.m_dimensions; + result = BuildSurface(descriptor); RETURN_RESULT_IF_UNSUCCESSFUL(result); - if (!ValidateSurfaceDimensions(swapchainDimensions)) - { - swapchainDimensions.m_imageHeight = AZStd::clamp(swapchainDimensions.m_imageHeight, m_surfaceCapabilities.minImageExtent.height, m_surfaceCapabilities.maxImageExtent.height); - swapchainDimensions.m_imageWidth = AZStd::clamp(swapchainDimensions.m_imageWidth, m_surfaceCapabilities.minImageExtent.width, m_surfaceCapabilities.maxImageExtent.width); - AZ_Printf("Vulkan", "Resizing swapchain from (%d, %d) to (%d, %d).", - static_cast(descriptor.m_dimensions.m_imageWidth), static_cast(descriptor.m_dimensions.m_imageHeight), - static_cast(swapchainDimensions.m_imageWidth), static_cast(swapchainDimensions.m_imageHeight)); - } auto& presentationQueue = device.GetCommandQueueContext().GetOrCreatePresentationCommandQueue(*this); m_presentationQueue = &presentationQueue; - result = BuildNativeSwapChain(swapchainDimensions, descriptor.m_verticalSyncInterval); - RETURN_RESULT_IF_UNSUCCESSFUL(result); - uint32_t imageCount = 0; - VkResult vkResult = vkGetSwapchainImagesKHR(device.GetNativeDevice(), m_nativeSwapChain, &imageCount, nullptr); - AssertSuccess(vkResult); - RETURN_RESULT_IF_UNSUCCESSFUL(ConvertResult(vkResult)); - m_swapchainNativeImages.resize(imageCount); - - // Retrieve the native images of the swapchain so they are - // available when we init the Images in InitImageInternal - vkResult = vkGetSwapchainImagesKHR(device.GetNativeDevice(), m_nativeSwapChain, &imageCount, m_swapchainNativeImages.data()); - AssertSuccess(vkResult); - RETURN_RESULT_IF_UNSUCCESSFUL(ConvertResult(vkResult)); - - // Acquire the first image - uint32_t imageIndex = 0; - result = AcquireNewImage(&imageIndex); + result = CreateSwapchain(); RETURN_RESULT_IF_UNSUCCESSFUL(result); if (nativeDimensions) { // Fill out the real swapchain dimensions to return - nativeDimensions->m_imageCount = imageCount; - nativeDimensions->m_imageHeight = swapchainDimensions.m_imageHeight; - nativeDimensions->m_imageWidth = swapchainDimensions.m_imageWidth; + *nativeDimensions = m_dimensions; nativeDimensions->m_imageFormat = ConvertFormat(m_surfaceFormat.format); } @@ -165,51 +139,24 @@ namespace AZ RHI::ResultCode SwapChain::ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) { auto& device = static_cast(GetDevice()); + m_dimensions = dimensions; InvalidateNativeSwapChain(); - InvalidateSurface(); - RHI::SwapChainDimensions resizeDimensions = dimensions; - BuildSurface(GetDescriptor()); - if (!ValidateSurfaceDimensions(dimensions)) - { - resizeDimensions.m_imageHeight = AZStd::clamp(dimensions.m_imageHeight, m_surfaceCapabilities.minImageExtent.height, m_surfaceCapabilities.maxImageExtent.height); - resizeDimensions.m_imageWidth = AZStd::clamp(dimensions.m_imageWidth, m_surfaceCapabilities.minImageExtent.width, m_surfaceCapabilities.maxImageExtent.width); - AZ_Printf("Vulkan", "Resizing swapchain from (%d, %d) to (%d, %d).", - static_cast(dimensions.m_imageWidth), static_cast(dimensions.m_imageHeight), - static_cast(resizeDimensions.m_imageWidth), static_cast(resizeDimensions.m_imageHeight)); - } auto& presentationQueue = device.GetCommandQueueContext().GetOrCreatePresentationCommandQueue(*this); m_presentationQueue = &presentationQueue; - BuildNativeSwapChain(resizeDimensions, GetDescriptor().m_verticalSyncInterval); - resizeDimensions.m_imageCount = 0; - VkResult vkResult = vkGetSwapchainImagesKHR(device.GetNativeDevice(), m_nativeSwapChain, &resizeDimensions.m_imageCount, nullptr); - RETURN_RESULT_IF_UNSUCCESSFUL(ConvertResult(vkResult)); - - m_swapchainNativeImages.resize(resizeDimensions.m_imageCount); - - // Retrieve the native images of the swapchain so they are - // available when we init the Images in InitImageInternal - vkResult = vkGetSwapchainImagesKHR(device.GetNativeDevice(), m_nativeSwapChain, &resizeDimensions.m_imageCount, m_swapchainNativeImages.data()); - RETURN_RESULT_IF_UNSUCCESSFUL(ConvertResult(vkResult)); - - // Do not recycle the semaphore because they may not ever get signaled and since - // we can't recycle Vulkan semaphores we just delete them. - m_currentFrameContext.m_imageAvailableSemaphore->SetRecycleValue(false); - m_currentFrameContext.m_presentableSemaphore->SetRecycleValue(false); - - // Acquire the first image - uint32_t imageIndex = 0; - AcquireNewImage(&imageIndex); + CreateSwapchain(); if (nativeDimensions) { - *nativeDimensions = resizeDimensions; + *nativeDimensions = m_dimensions; // [ATOM-4840] This is a workaround when the windows is minimized (0x0 size). // Add proper support to handle this case. - nativeDimensions->m_imageHeight = AZStd::max(resizeDimensions.m_imageHeight, 1u); - nativeDimensions->m_imageWidth = AZStd::max(resizeDimensions.m_imageWidth, 1u); + nativeDimensions->m_imageHeight = AZStd::max(m_dimensions.m_imageHeight, 1u); + nativeDimensions->m_imageWidth = AZStd::max(m_dimensions.m_imageWidth, 1u); + + nativeDimensions->m_imageFormat = ConvertFormat(m_surfaceFormat.format); } return RHI::ResultCode::Success; @@ -271,20 +218,48 @@ namespace AZ info.pImageIndices = &imageIndex; info.pResults = nullptr; - [[maybe_unused]] const VkResult result = vkQueuePresentKHR(vulkanQueue->GetNativeQueue(), &info); + const VkResult result = vkQueuePresentKHR(vulkanQueue->GetNativeQueue(), &info); - // Resizing window cause recreation of SwapChain after calling this method, - // so VK_SUBOPTIMAL_KHR or VK_ERROR_OUT_OF_DATE_KHR should not happen at this point. - AZ_Assert(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR, "Failed to present swapchain %s", GetName().GetCStr()); - AZ_Warning("Vulkan", result != VK_SUBOPTIMAL_KHR, "Suboptimal presentation of swapchain %s", GetName().GetCStr()); + // Vulkan's definition of the two types of errors. + // VK_ERROR_OUT_OF_DATE_KHR: "A surface has changed in such a way that it is no longer compatible with the swapchain, + // and further presentation requests using the swapchain will fail. Applications must query the new surface + // properties and recreate their swapchain if they wish to continue presenting to the surface." + // VK_SUBOPTIMAL_KHR: "A swapchain no longer matches the surface properties exactly, but can still be used to + // present to the surface successfully." + // + // These result values may occur after resizing or some window operation. We should update the surface info and recreate the swapchain. + // VK_SUBOPTIMAL_KHR is treated as success, but we better update the surface info as well. + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) + { + InvalidateNativeSwapChain(); + CreateSwapchain(); + } + else + { + // Other errors are: + // VK_ERROR_OUT_OF_HOST_MEMORY + // VK_ERROR_OUT_OF_DEVICE_MEMORY + // VK_ERROR_DEVICE_LOST + // VK_ERROR_SURFACE_LOST_KHR + // VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT + AZ_Assert(result == VK_SUCCESS, "Unhandled error for swapchain presentation."); + } }; m_presentationQueue->QueueCommand(AZStd::move(presentCommand)); uint32_t acquiredImageIndex = GetCurrentImageIndex(); - AcquireNewImage(&acquiredImageIndex); - - return acquiredImageIndex; + RHI::ResultCode result = AcquireNewImage(&acquiredImageIndex); + if (result == RHI::ResultCode::Fail) + { + InvalidateNativeSwapChain(); + CreateSwapchain(); + return 0; + } + else + { + return acquiredImageIndex; + } } RHI::ResultCode SwapChain::BuildSurface(const RHI::SwapChainDescriptor& descriptor) @@ -293,15 +268,8 @@ namespace AZ surfaceDesc.m_windowHandle = descriptor.m_window; RHI::Ptr surface = WSISurface::Create(); const RHI::ResultCode result = surface->Init(surfaceDesc); - if (result == RHI::ResultCode::Success) - { - m_surface = surface; - auto& device = static_cast(GetDevice()); - const auto& physicalDevice = static_cast(device.GetPhysicalDevice()); - VkResult vkResult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice.GetNativePhysicalDevice(), m_surface->GetNativeSurface(), &m_surfaceCapabilities); - AssertSuccess(vkResult); - RETURN_RESULT_IF_UNSUCCESSFUL(ConvertResult(vkResult)); - } + RETURN_RESULT_IF_UNSUCCESSFUL(result); + m_surface = surface; return result; } @@ -373,6 +341,21 @@ namespace AZ return supportedModes[0]; } + VkSurfaceCapabilitiesKHR SwapChain::GetSurfaceCapabilities() + { + AZ_Assert(m_surface, "Surface has not been initialized."); + + auto& device = static_cast(GetDevice()); + const auto& physicalDevice = static_cast(device.GetPhysicalDevice()); + + VkSurfaceCapabilitiesKHR surfaceCapabilities; + VkResult vkResult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + physicalDevice.GetNativePhysicalDevice(), m_surface->GetNativeSurface(), &surfaceCapabilities); + AssertSuccess(vkResult); + + return surfaceCapabilities; + } + VkCompositeAlphaFlagBitsKHR SwapChain::GetSupportedCompositeAlpha() const { VkFlags supportedModesBits = m_surfaceCapabilities.supportedCompositeAlpha; @@ -394,15 +377,9 @@ namespace AZ return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; } - RHI::ResultCode SwapChain::BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions, uint32_t verticalSyncInterval) + RHI::ResultCode SwapChain::BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions) { AZ_Assert(m_nativeSwapChain == VK_NULL_HANDLE, "Vulkan's native SwapChain has been initialized already."); - auto& device = static_cast(GetDevice()); - auto& queueContext = device.GetCommandQueueContext(); - const VkExtent2D extent = { - dimensions.m_imageWidth, - dimensions.m_imageHeight - }; AZ_Assert(m_surface, "Surface is null."); if (!ValidateSurfaceDimensions(dimensions)) @@ -410,7 +387,11 @@ namespace AZ AZ_Assert(false, "Swapchain dimensions are not supported."); return RHI::ResultCode::InvalidArgument; } - m_surfaceFormat = GetSupportedSurfaceFormat(dimensions.m_imageFormat); + + auto& device = static_cast(GetDevice()); + auto& queueContext = device.GetCommandQueueContext(); + const VkExtent2D extent = { dimensions.m_imageWidth, dimensions.m_imageHeight }; + // If the graphic queue is the same as the presentation queue, then we will always acquire // 1 image at the same time. If it's another queue, we will have 2 at the same time (while the other queue // presents the image) @@ -441,11 +422,11 @@ namespace AZ createInfo.imageArrayLayers = 1; // non-stereoscopic createInfo.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.queueFamilyIndexCount = static_cast(familyIndices.size()); + createInfo.queueFamilyIndexCount = aznumeric_cast(familyIndices.size()); createInfo.pQueueFamilyIndices = familyIndices.empty() ? nullptr : familyIndices.data(); createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - createInfo.compositeAlpha = GetSupportedCompositeAlpha(); - createInfo.presentMode = GetSupportedPresentMode(verticalSyncInterval); + createInfo.compositeAlpha = m_compositeAlphaFlagBits; + createInfo.presentMode = m_presentMode; createInfo.clipped = VK_FALSE; createInfo.oldSwapchain = VK_NULL_HANDLE; @@ -467,9 +448,6 @@ namespace AZ VK_NULL_HANDLE, acquiredImageIndex); - // Resizing window cause recreation of SwapChain before calling this method, - // so VK_SUBOPTIMAL_KHR or VK_ERROR_OUT_OF_DATE_KHR should not happen. - AssertSuccess(vkResult); RHI::ResultCode result = ConvertResult(vkResult); RETURN_RESULT_IF_UNSUCCESSFUL(result); @@ -484,6 +462,7 @@ namespace AZ } m_currentFrameContext.m_imageAvailableSemaphore = imageAvailableSemaphore; m_currentFrameContext.m_presentableSemaphore = semaphoreAllocator.Allocate(); + return result; } @@ -502,5 +481,70 @@ namespace AZ m_nativeSwapChain = VK_NULL_HANDLE; } } + + RHI::ResultCode SwapChain::CreateSwapchain() + { + auto& device = static_cast(GetDevice()); + + m_surfaceCapabilities = GetSurfaceCapabilities(); + m_surfaceFormat = GetSupportedSurfaceFormat(GetDescriptor().m_dimensions.m_imageFormat); + m_presentMode = GetSupportedPresentMode(GetDescriptor().m_verticalSyncInterval); + m_compositeAlphaFlagBits = GetSupportedCompositeAlpha(); + + if (!ValidateSurfaceDimensions(m_dimensions)) + { + uint32_t oldHeight = m_dimensions.m_imageHeight; + uint32_t oldWidth = m_dimensions.m_imageWidth; + m_dimensions.m_imageHeight = AZStd::clamp( + m_dimensions.m_imageHeight, + m_surfaceCapabilities.minImageExtent.height, + m_surfaceCapabilities.maxImageExtent.height); + m_dimensions.m_imageWidth = AZStd::clamp( + m_dimensions.m_imageWidth, + m_surfaceCapabilities.minImageExtent.width, + m_surfaceCapabilities.maxImageExtent.width); + AZ_Printf( + "Vulkan", "Resizing swapchain from (%u, %u) to (%u, %u).", + oldWidth, oldHeight, m_dimensions.m_imageWidth, m_dimensions.m_imageHeight); + } + + RHI::ResultCode result = BuildNativeSwapChain(m_dimensions); + RETURN_RESULT_IF_UNSUCCESSFUL(result); + AZ_TracePrintf("Swapchain", "Swapchain created. Width: %u, Height: %u.", m_dimensions.m_imageWidth, m_dimensions.m_imageHeight); + + // Do not recycle the semaphore because they may not ever get signaled and since + // we can't recycle Vulkan semaphores we just delete them. + if (m_currentFrameContext.m_imageAvailableSemaphore) + { + m_currentFrameContext.m_imageAvailableSemaphore->SetRecycleValue(false); + } + if (m_currentFrameContext.m_presentableSemaphore) + { + m_currentFrameContext.m_presentableSemaphore->SetRecycleValue(false); + } + + m_dimensions.m_imageCount = 0; + VkResult vkResult = vkGetSwapchainImagesKHR(device.GetNativeDevice(), m_nativeSwapChain, &m_dimensions.m_imageCount, nullptr); + AssertSuccess(vkResult); + RETURN_RESULT_IF_UNSUCCESSFUL(ConvertResult(vkResult)); + + m_swapchainNativeImages.resize(m_dimensions.m_imageCount); + + // Retrieve the native images of the swapchain so they are + // available when we init the images in InitImageInternal + vkResult = vkGetSwapchainImagesKHR( + device.GetNativeDevice(), m_nativeSwapChain, &m_dimensions.m_imageCount, m_swapchainNativeImages.data()); + AssertSuccess(vkResult); + RETURN_RESULT_IF_UNSUCCESSFUL(ConvertResult(vkResult)); + AZ_TracePrintf("Swapchain", "Obtained presentable images."); + + // Acquire the first image + uint32_t imageIndex = 0; + result = AcquireNewImage(&imageIndex); + RETURN_RESULT_IF_UNSUCCESSFUL(result); + AZ_TracePrintf("Swapchain", "Acquired the first image."); + + return RHI::ResultCode::Success; + } } } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.h index 14dfd34b08..ee2ff3c207 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.h @@ -70,24 +70,48 @@ namespace AZ ////////////////////////////////////////////////////////////////////// RHI::ResultCode BuildSurface(const RHI::SwapChainDescriptor& descriptor); + + //! Returns true is the swapchain dimensions are supported by the current surface. bool ValidateSurfaceDimensions(const RHI::SwapChainDimensions& dimensions); + //! Returns the corresponding Vulkan format that is supported by the surface. + //! If such format is not found, return the first supported format from the surface. VkSurfaceFormatKHR GetSupportedSurfaceFormat(const RHI::Format format) const; + //! Returns the correct presentation mode. + //! If verticalSyncInterval is non-zero, returns VK_PRESENT_MODE_FIFO_KHR. + //! Otherwise, choose preferred mode if they are supported. + //! If not, the first supported present mode is returned. VkPresentModeKHR GetSupportedPresentMode(uint32_t verticalSyncInterval) const; + //! Returns the preferred alpha compositing modes if they are supported. + //! If not, error will be reported. VkCompositeAlphaFlagBitsKHR GetSupportedCompositeAlpha() const; - RHI::ResultCode BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions, uint32_t verticalSyncInterval); + //! Returns the current surface capabilities. + VkSurfaceCapabilitiesKHR GetSurfaceCapabilities(); + //! Create the swapchain when initializing, or + //! swapchain is no longer compatible or is sub-optimal with the surface. + RHI::ResultCode CreateSwapchain(); + //! Build underlying Vulkan swapchain. + RHI::ResultCode BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions); + //! Retrieve the index of the next available presentable image. RHI::ResultCode AcquireNewImage(uint32_t* acquiredImageIndex); + //! Destroy the surface. void InvalidateSurface(); + //! Destroy the old swapchain. void InvalidateNativeSwapChain(); - VkSwapchainKHR m_nativeSwapChain = VK_NULL_HANDLE; RHI::Ptr m_surface; + VkSwapchainKHR m_nativeSwapChain = VK_NULL_HANDLE; CommandQueue* m_presentationQueue = nullptr; - VkSurfaceFormatKHR m_surfaceFormat = {}; - VkSurfaceCapabilitiesKHR m_surfaceCapabilities; - FrameContext m_currentFrameContext; + //! Swapchain data + VkSurfaceFormatKHR m_surfaceFormat = {}; + VkSurfaceCapabilitiesKHR m_surfaceCapabilities = {}; + VkPresentModeKHR m_presentMode = {}; + VkCompositeAlphaFlagBitsKHR m_compositeAlphaFlagBits = {}; + AZStd::vector m_swapchainNativeImages; + RHI::SwapChainDimensions m_dimensions; + struct SwapChainBarrier { VkPipelineStageFlags m_srcPipelineStages = 0; @@ -95,8 +119,6 @@ namespace AZ VkImageMemoryBarrier m_barrier = {}; bool m_isValid = false; } m_swapChainBarrier; - - AZStd::vector m_swapchainNativeImages; }; } } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index e43880827c..9672abfd99 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -196,12 +196,8 @@ namespace AtomToolsFramework bool RenderViewportWidget::event(QEvent* event) { - // On some types of QEvents, a resize event is needed to make sure that the current viewport window - // needs to be updated based on a potential new surface dimensions. switch (event->type()) { - case QEvent::ScreenChangeInternal: - case QEvent::UpdateLater: case QEvent::Resize: SendWindowResizeEvent(); break; From b536abbf4ea93379e174c14c883d90987140a041 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 22 Oct 2021 10:53:29 -0700 Subject: [PATCH 141/152] Add existing gem through Project Manager --- .../Resources/ProjectManager.qss | 8 +++ .../GemCatalog/GemCatalogHeaderWidget.cpp | 12 ++--- .../GemCatalog/GemCatalogHeaderWidget.h | 24 ++++----- .../Source/GemCatalog/GemCatalogScreen.cpp | 52 +++++++++++++++++++ .../Source/GemCatalog/GemCatalogScreen.h | 3 ++ .../ProjectManager/Source/PythonBindings.cpp | 41 +++++++++++++++ .../ProjectManager/Source/PythonBindings.h | 1 + .../Source/PythonBindingsInterface.h | 8 +++ scripts/o3de/o3de/register.py | 2 +- 9 files changed, 130 insertions(+), 21 deletions(-) diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 712c01aa02..6694168f2b 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -523,6 +523,14 @@ QProgressBar::chunk { background-color: #444444; } +#gemCatalogMenuButton { + qproperty-flat: true; + max-width:36px; + min-width:36px; + max-height:24px; + min-height:24px; +} + #GemCatalogHeaderLabel { font-size: 12px; color: #FFFFFF; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 0ecea215bf..5da163bafe 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -8,14 +8,13 @@ #include #include -#include - #include #include #include #include -#include #include +#include +#include namespace O3DE::ProjectManager { @@ -406,7 +405,6 @@ namespace O3DE::ProjectManager CartButton* cartButton = new CartButton(gemModel, downloadController); hLayout->addWidget(cartButton); - hLayout->addSpacing(16); // Separating line @@ -418,9 +416,9 @@ namespace O3DE::ProjectManager hLayout->addSpacing(16); QMenu* gemMenu = new QMenu(this); - m_openGemReposAction = gemMenu->addAction(tr("Show Gem Repos")); - - connect(m_openGemReposAction, &QAction::triggered, this,[this](){ emit OpenGemsRepo(); }); + gemMenu->addAction( tr("Show Gem Repos"), [this]() { emit OpenGemsRepo(); }); + gemMenu->addSeparator(); + gemMenu->addAction( tr("Add Existing Gem"), [this]() { emit AddGem(); }); QPushButton* gemMenuButton = new QPushButton(this); gemMenuButton->setObjectName("gemCatalogMenuButton"); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index fa381e54ae..19adec5607 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -8,24 +8,23 @@ #pragma once -#include - #if !defined(Q_MOC_RUN) +#include #include #include #include #include -#include - #include -#include -#include -#include -#include -#include -#include +#include #endif +QT_FORWARD_DECLARE_CLASS(QPushButton) +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QVBoxLayout) +QT_FORWARD_DECLARE_CLASS(QHBoxLayout) +QT_FORWARD_DECLARE_CLASS(QHideEvent) +QT_FORWARD_DECLARE_CLASS(QMoveEvent) + namespace O3DE::ProjectManager { class CartOverlayWidget @@ -87,12 +86,11 @@ namespace O3DE::ProjectManager void ReinitForProject(); signals: + void AddGem(); void OpenGemsRepo(); - + private: AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr; inline constexpr static int s_height = 60; - - QAction* m_openGemReposAction = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 69c5844e84..b145363460 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -19,6 +19,10 @@ #include #include #include +#include +#include +#include +#include namespace O3DE::ProjectManager { @@ -74,6 +78,7 @@ namespace O3DE::ProjectManager void GemCatalogScreen::ReinitForProject(const QString& projectPath) { m_gemModel->clear(); + m_gemsToRegisterWithProject.clear(); FillModel(projectPath); if (m_filterWidget) @@ -90,6 +95,47 @@ namespace O3DE::ProjectManager connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter); connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); + connect( + m_headerWidget, &GemCatalogHeaderWidget::AddGem, + [&]() + { + EngineInfo engineInfo; + QString defaultPath; + + AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (engineInfoResult.IsSuccess()) + { + engineInfo = engineInfoResult.GetValue(); + defaultPath = engineInfo.m_defaultGemsFolder; + } + + if (defaultPath.isEmpty()) + { + defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + } + + QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath)); + if (!directory.isEmpty()) + { + // register the gem to the o3de_manifest.json and to the project after the user confirms + // project creation/update + auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory); + if(!registerResult) + { + QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str()); + } + else + { + m_gemsToRegisterWithProject.insert(directory); + AZ::Outcome gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory); + if (gemInfoResult) + { + m_gemModel->AddGem(gemInfoResult.GetValue()); + m_gemModel->UpdateGemDependencies(); + } + } + } + }); // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ @@ -251,6 +297,12 @@ namespace O3DE::ProjectManager return EnableDisableGemsResult::Failed; } + + // register external gems that were added with relative paths + if (m_gemsToRegisterWithProject.contains(gemPath)) + { + pythonBindings->RegisterGem(QDir(projectPath).relativeFilePath(gemPath), projectPath); + } } for (const QModelIndex& modelIndex : toBeRemoved) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 62528ba942..8e9f31c710 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -18,6 +18,8 @@ #include #include #include +#include +#include #endif namespace O3DE::ProjectManager @@ -70,5 +72,6 @@ namespace O3DE::ProjectManager GemFilterWidget* m_filterWidget = nullptr; DownloadController* m_downloadController = nullptr; bool m_notificationsEnabled = true; + QSet m_gemsToRegisterWithProject; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index bc8773b0c8..f001195e85 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -556,6 +556,47 @@ namespace O3DE::ProjectManager return AZ::Success(AZStd::move(gemNames)); } + AZ::Outcome PythonBindings::RegisterGem(const QString& gemPath, const QString& projectPath) + { + bool registrationResult = false; + auto result = ExecuteWithLockErrorHandling( + [&] + { + auto externalProjectPath = projectPath.isEmpty() ? pybind11::none() : QString_To_Py_Path(projectPath); + auto pythonRegistrationResult = m_register.attr("register")( + pybind11::none(), // engine_path + pybind11::none(), // project_path + QString_To_Py_Path(gemPath), // gem folder + pybind11::none(), // external subdirectory + pybind11::none(), // template_path + pybind11::none(), // restricted folder + pybind11::none(), // repo uri + pybind11::none(), // default_engines_folder + pybind11::none(), // default_projects_folder + pybind11::none(), // default_gems_folder + pybind11::none(), // default_templates_folder + pybind11::none(), // default_restricted_folder + pybind11::none(), // default_third_party_folder + pybind11::none(), // external_subdir_engine_path + externalProjectPath // external_subdir_project_path + ); + + // Returns an exit code so boolify it then invert result + registrationResult = !pythonRegistrationResult.cast(); + }); + + if (!result.IsSuccess()) + { + return AZ::Failure(result.GetError().c_str()); + } + else if (!registrationResult) + { + return AZ::Failure(AZStd::string::format("Failed to register gem path %s", gemPath.toUtf8().constData())); + } + + return AZ::Success(); + } + bool PythonBindings::AddProject(const QString& path) { bool registrationResult = false; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 638ce6b1d4..9d2c8c850f 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -42,6 +42,7 @@ namespace O3DE::ProjectManager AZ::Outcome, AZStd::string> GetEngineGemInfos() override; AZ::Outcome, AZStd::string> GetAllGemInfos(const QString& projectPath) override; AZ::Outcome, AZStd::string> GetEnabledGemNames(const QString& projectPath) override; + AZ::Outcome RegisterGem(const QString& gemPath, const QString& projectPath = {}) override; // Project AZ::Outcome CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo) override; diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index 19442540a4..6b9ac39213 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -91,6 +91,14 @@ namespace O3DE::ProjectManager */ virtual AZ::Outcome, AZStd::string> GetEnabledGemNames(const QString& projectPath) = 0; + /** + * Registers the gem to the specified project, or to the o3de_manifest.json if no project path is given + * @param gemPath the path to the gem + * @param projectPath the path to the project. If empty, will register the external path in o3de_manifest.json + * @return An outcome with the success flag as well as an error message in case of a failure. + */ + virtual AZ::Outcome RegisterGem(const QString& gemPath, const QString& projectPath = {}) = 0; + // Projects diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index 0de161177c..6ad54a11f3 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -596,7 +596,7 @@ def register(engine_path: pathlib.Path = None, :param default_third_party_folder: default 3rd party cache folder :param external_subdir_engine_path: Path to the engine to use when registering an external subdirectory. The registration occurs in the engine.json file in this case - :param external_subdir_engine_path: Path to the project to use when registering an external subdirectory. + :param external_subdir_project_path: Path to the project to use when registering an external subdirectory. The registrations occurs in the project.json in this case :param remove: add/remove the entries :param force: force update of the engine_path for specified "engine_name" from the engine.json file From 7ba99262022e1b5746f264575f49092ab5722892 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Fri, 22 Oct 2021 11:10:37 -0700 Subject: [PATCH 142/152] The .shader file must now include the .azsl extension when referencing a .azsl file. It will no longer be automatically appended. Fixed an issue where the shader builder would incorrectly succeed when the .azsl file is missing. Also renamed some variables for more consistency. Updated all .shader files accordingly. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../AzslShaderBuilderSystemComponent.cpp | 2 +- .../Code/Source/Editor/ShaderAssetBuilder.cpp | 18 +++++++------- .../Source/Editor/ShaderBuilderUtility.cpp | 24 ++++++++++++++----- .../Code/Source/Editor/ShaderBuilderUtility.h | 4 ++-- .../Shaders/AuxGeom/AuxGeomObject.shader | 2 +- .../Shaders/AuxGeom/AuxGeomObjectLit.shader | 2 +- .../Shaders/AuxGeom/AuxGeomWorld.shader | 2 +- .../Shaders/BRDFTexture/BRDFTextureCS.shader | 2 +- .../CheckerboardColorResolveCS.shader | 2 +- .../Shaders/ColorGrading/LutGeneration.shader | 2 +- .../Assets/Shaders/Depth/DepthPassSkin.shader | 2 +- .../DiffuseComposite.shader | 2 +- .../DiffuseGlobalFullscreen.shader | 2 +- .../DiffuseProbeGridDownsample.shader | 2 +- .../Common/Assets/Shaders/ImGui/ImGui.shader | 2 +- .../Shaders/LightCulling/LightCulling.shader | 2 +- .../LightCulling/LightCullingHeatmap.shader | 2 +- .../LightCulling/LightCullingRemap.shader | 2 +- .../LightCullingTilePrepare.shader | 2 +- .../Shaders/LuxCore/RenderTexture.shader | 2 +- .../Shaders/MorphTargets/MorphTargetCS.shader | 2 +- .../MotionVector/CameraMotionVector.shader | 2 +- .../MotionVector/MeshMotionVector.shader | 2 +- .../MotionVector/MeshMotionVectorSkin.shader | 2 +- .../AcesOutputTransformLut.shader | 2 +- .../ApplyShaperLookupTable.shader | 2 +- .../BakeAcesOutputTransformLutCS.shader | 2 +- .../BlendColorGradingLuts.shader | 2 +- .../Shaders/PostProcessing/BloomBlurCS.shader | 2 +- .../PostProcessing/BloomCompositeCS.shader | 2 +- .../PostProcessing/BloomDownsampleCS.shader | 2 +- .../ContrastAdaptiveSharpening.shader | 2 +- .../PostProcessing/ConvertToAcescg.shader | 2 +- .../PostProcessing/DepthDownsample.shader | 2 +- .../DepthOfFieldBlurBokeh.shader | 2 +- .../DepthOfFieldComposite.shader | 2 +- .../DepthOfFieldDownSample.shader | 2 +- .../PostProcessing/DepthOfFieldMask.shader | 2 +- .../PostProcessing/DepthOfFieldPrepare.shader | 2 +- .../DepthOfFieldWriteFocusDepthFromGpu.shader | 2 +- .../PostProcessing/DepthToLinearDepth.shader | 2 +- .../PostProcessing/DepthUpsample.shader | 2 +- .../DiffuseSpecularMerge.shader | 2 +- .../PostProcessing/DisplayMapper.shader | 2 +- .../DisplayMapperOnlyGammaCorrection.shader | 2 +- .../DownsampleLuminanceMinAvgMaxCS.shader | 2 +- .../DownsampleMinAvgMaxCS.shader | 2 +- .../PostProcessing/EyeAdaptation.shader | 2 +- .../FastDepthAwareBlurHor.shader | 2 +- .../FastDepthAwareBlurVer.shader | 2 +- .../PostProcessing/FullscreenCopy.shader | 2 +- .../PostProcessing/HDRColorGrading.shader | 2 +- .../LookModificationTransform.shader | 2 +- .../PostProcessing/LuminanceHeatmap.shader | 2 +- .../LuminanceHistogramGenerator.shader | 2 +- .../PostProcessing/MSAAResolveCustom.shader | 2 +- .../PostProcessing/MSAAResolveDepth.shader | 2 +- .../PostProcessing/ModulateTexture.shader | 2 +- .../PostProcessing/OutputTransform.shader | 2 +- .../SMAABlendingWeightCalculation.shader | 2 +- .../SMAAConvertToPerceptualColor.shader | 2 +- .../PostProcessing/SMAAEdgeDetection.shader | 2 +- .../SMAANeighborhoodBlending.shader | 2 +- .../ScreenSpaceSubsurfaceScatteringCS.shader | 2 +- .../Shaders/PostProcessing/SsaoCompute.shader | 2 +- .../Assets/Shaders/PostProcessing/Taa.shader | 2 +- .../PostProcessing/UniformColor.shader | 2 +- .../Reflections/ReflectionComposite.shader | 2 +- .../ReflectionGlobalFullscreen.shader | 2 +- .../ReflectionProbeBlendWeight.shader | 2 +- .../ReflectionProbeRenderInner.shader | 2 +- .../ReflectionProbeRenderOuter.shader | 2 +- .../Reflections/ReflectionProbeStencil.shader | 2 +- ...ReflectionScreenSpaceBlurHorizontal.shader | 2 +- .../ReflectionScreenSpaceBlurVertical.shader | 2 +- .../ReflectionScreenSpaceComposite.shader | 2 +- .../ReflectionScreenSpaceTrace.shader | 2 +- .../Shaders/Shadow/DepthExponentiation.shader | 2 +- .../Shaders/Shadow/KawaseShadowBlur.shader | 2 +- .../Assets/Shaders/Shadow/Shadowmap.shader | 2 +- .../Shaders/Shadow/ShadowmapSkin.shader | 2 +- .../SkinnedMesh/LinearSkinningCS.shader | 2 +- .../Assets/Shaders/SkyBox/SkyBox.shader | 2 +- .../Shaders/SkyBox/SkyBox_TwoOutputs.shader | 2 +- .../RPI/Assets/Shader/DecomposeMsImage.shader | 2 +- .../RPI/Assets/Shader/ImagePreview.shader | 2 +- .../Assets/Shaders/LyShineUI.shader | 2 +- .../Assets/Shaders/SimpleTextured.shader | 2 +- .../Assets/Shaders/TexturedIcon.shader | 2 +- .../Assets/Shaders/ImGuiAtom/ImGuiAtom.shader | 2 +- .../Shaders/Terrain/Terrain_Shadowmap.shader | 2 +- 91 files changed, 117 insertions(+), 105 deletions(-) diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp index ffc5f40ef4..56f2fdec62 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp @@ -81,7 +81,7 @@ namespace AZ // Register Shader Asset Builder AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilderDescriptor; shaderAssetBuilderDescriptor.m_name = "Shader Asset Builder"; - shaderAssetBuilderDescriptor.m_version = 105; // [AZSL] Changing inlineConstant to rootConstant keyword work. + shaderAssetBuilderDescriptor.m_version = 107; // Required .azsl extension in .shader file references // .shader file changes trigger rebuilds shaderAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern( AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); shaderAssetBuilderDescriptor.m_busId = azrtti_typeid(); diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp index 3bc63b89a0..a0205efa72 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp @@ -151,11 +151,11 @@ namespace AZ void ShaderAssetBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { - AZStd::string fullPath; - AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, true); + AZStd::string shaderAssetSourceFileFullPath; + AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), shaderAssetSourceFileFullPath, true); ShaderBuilderUtility::IncludedFilesParser includedFilesParser; - AZ_TracePrintf(ShaderAssetBuilderName, "CreateJobs for Shader \"%s\"\n", fullPath.data()); + AZ_TracePrintf(ShaderAssetBuilderName, "CreateJobs for Shader \"%s\"\n", shaderAssetSourceFileFullPath.data()); // Used to synchronize versions of the ShaderAsset and ShaderVariantTreeAsset, especially during hot-reload. // Note it's probably important for this to be set once outside the platform loop so every platform's ShaderAsset @@ -166,7 +166,7 @@ namespace AZ // Need to get the name of the azsl file from the .shader source asset, to be able to declare a dependency to SRG Layout Job. // and the macro options to preprocess. - auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(fullPath); + auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(shaderAssetSourceFileFullPath); if (!descriptorParseOutcome.IsSuccess()) { AZ_Error( @@ -178,7 +178,7 @@ namespace AZ RPI::ShaderSourceData shaderSourceData = descriptorParseOutcome.TakeValue(); AZStd::string azslFullPath; - ShaderBuilderUtility::GetAbsolutePathToAzslFile(fullPath, shaderSourceData.m_source, azslFullPath); + ShaderBuilderUtility::GetAbsolutePathToAzslFile(shaderAssetSourceFileFullPath, shaderSourceData.m_source, azslFullPath); { // Add the AZSL as source dependency @@ -191,9 +191,9 @@ namespace AZ { AZ_Error( ShaderAssetBuilderName, false, "Shader program listed as the source entry does not exist: %s.", azslFullPath.c_str()); - // Treat as success, so when the azsl file shows up the AP will try to recompile. - response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; - return; + // Even though there was an error here, don't stop, because we need to report the SourceFileDependency so when the azsl + // file shows up the AP will try to recompile. We will go ahead and create the job anyway, and then ProcessJob can + // report the failure. } GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderAssetBuilderName); @@ -229,7 +229,7 @@ namespace AZ } // for all request.m_enabledPlatforms AZ_TracePrintf( - ShaderAssetBuilderName, "CreateJobs for %s took %llu microseconds", fullPath.c_str(), + ShaderAssetBuilderName, "CreateJobs for %s took %llu microseconds", shaderAssetSourceFileFullPath.c_str(), AZStd::GetTimeNowMicroSecond() - shaderAssetBuildTimestamp); response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp index 2c87a79068..e6c99630b2 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp @@ -70,11 +70,11 @@ namespace AZ return AZ::Success(shaderSourceData); } - void GetAbsolutePathToAzslFile(const AZStd::string& shaderTemplatePathAndFile, AZStd::string specifiedShaderPathAndName, AZStd::string& absoluteAzslPath) + void GetAbsolutePathToAzslFile(const AZStd::string& shaderSourceFileFullPath, AZStd::string specifiedShaderPathAndName, AZStd::string& absoluteAzslPath) { AZStd::string sourcePath; - AzFramework::StringFunc::Path::GetFullPath(shaderTemplatePathAndFile.data(), sourcePath); + AzFramework::StringFunc::Path::GetFullPath(shaderSourceFileFullPath.c_str(), sourcePath); AzFramework::StringFunc::Path::Normalize(specifiedShaderPathAndName); bool shaderNameHasPath = (specifiedShaderPathAndName.find(AZ_CORRECT_FILESYSTEM_SEPARATOR) != AZStd::string::npos); @@ -82,15 +82,27 @@ namespace AZ // Join will handle overlapping directory structures for us AzFramework::StringFunc::Path::Join(sourcePath.data(), specifiedShaderPathAndName.data(), absoluteAzslPath, shaderNameHasPath /* handle directory overlap? */, false /* be case insensitive? */); - AzFramework::StringFunc::Path::ReplaceExtension(absoluteAzslPath, "azsl"); + // The builders used to automatically set the ".azsl" extension, but no more, because that would make the .shader file confusing to read. + // Here we just detect the issue and instruct the user what to change. + // (There's no need to return a failure code, the builder will eventually fail anyway when it can't find the file). + if (!IO::FileIOBase::GetInstance()->Exists(absoluteAzslPath.c_str())) + { + AZStd::string absoluteAzslPathWithForcedExtension = absoluteAzslPath; + AzFramework::StringFunc::Path::ReplaceExtension(absoluteAzslPathWithForcedExtension, "azsl"); + + if (IO::FileIOBase::GetInstance()->Exists(absoluteAzslPathWithForcedExtension.c_str())) + { + AZ_Error(ShaderBuilderUtilityName, false, "When the .shader file references a .azsl file, it must include the \".azsl\" extension."); + } + } } AZStd::shared_ptr PrepareSourceInput( [[maybe_unused]] const char* builderName, - const AZStd::string& shaderAssetSourcePath, + const AZStd::string& shaderSourceFileFullPath, RPI::ShaderSourceData& sourceAsset) { - auto shaderAssetSourceFileParseOutput = ShaderBuilderUtility::LoadShaderDataJson(shaderAssetSourcePath); + auto shaderAssetSourceFileParseOutput = ShaderBuilderUtility::LoadShaderDataJson(shaderSourceFileFullPath); if (!shaderAssetSourceFileParseOutput.IsSuccess()) { AZ_Error(builderName, false, "Failed to load/parse Shader Descriptor JSON: %s", shaderAssetSourceFileParseOutput.GetError().c_str()); @@ -100,7 +112,7 @@ namespace AZ AZStd::shared_ptr files(new ShaderFiles); const AZStd::string& specifiedAzslName = sourceAsset.m_source; - ShaderBuilderUtility::GetAbsolutePathToAzslFile(shaderAssetSourcePath, specifiedAzslName, files->m_azslSourceFullPath); + ShaderBuilderUtility::GetAbsolutePathToAzslFile(shaderSourceFileFullPath, specifiedAzslName, files->m_azslSourceFullPath); // specifiedAzslName may have a relative path on it so need to strip it AzFramework::StringFunc::Path::GetFileName(specifiedAzslName.c_str(), files->m_azslFileName); diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.h b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.h index 5d45ade9cb..a27eddf2cc 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.h @@ -33,12 +33,12 @@ namespace AZ { Outcome LoadShaderDataJson(const AZStd::string& fullPathToJsonFile); - void GetAbsolutePathToAzslFile(const AZStd::string& shaderTemplatePathAndFile, AZStd::string specifiedShaderPathAndName, AZStd::string& absoluteShaderPath); + void GetAbsolutePathToAzslFile(const AZStd::string& shaderSourceFileFullPath, AZStd::string specifiedShaderPathAndName, AZStd::string& absoluteShaderPath); //! Opens and read the .shader, returns expanded file paths AZStd::shared_ptr PrepareSourceInput( const char* builderName, - const AZStd::string& shaderAssetSourcePath, + const AZStd::string& shaderSourceFileFullPath, RPI::ShaderSourceData& sourceAsset); namespace AzslSubProducts diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomObject.shader b/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomObject.shader index 88120b3a9d..83012fe13c 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomObject.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomObject.shader @@ -1,5 +1,5 @@ { - "Source" : "AuxGeomObject", + "Source" : "AuxGeomObject.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomObjectLit.shader b/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomObjectLit.shader index 98ace1da32..b06a0cd662 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomObjectLit.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomObjectLit.shader @@ -1,5 +1,5 @@ { - "Source" : "AuxGeomObjectLit", + "Source" : "AuxGeomObjectLit.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomWorld.shader b/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomWorld.shader index ad49848f0f..2d7ff39999 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomWorld.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/AuxGeom/AuxGeomWorld.shader @@ -1,5 +1,5 @@ { - "Source" : "AuxGeomWorld", + "Source" : "AuxGeomWorld.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/BRDFTexture/BRDFTextureCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/BRDFTexture/BRDFTextureCS.shader index 50ce945edd..376e2bb5a3 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/BRDFTexture/BRDFTextureCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/BRDFTexture/BRDFTextureCS.shader @@ -1,5 +1,5 @@ { - "Source": "BRDFTextureCS", + "Source": "BRDFTextureCS.azsl", "ProgramSettings": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Checkerboard/CheckerboardColorResolveCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Checkerboard/CheckerboardColorResolveCS.shader index f21f3e5c01..c08065a550 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Checkerboard/CheckerboardColorResolveCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Checkerboard/CheckerboardColorResolveCS.shader @@ -1,5 +1,5 @@ { - "Source": "CheckerboardColorResolveCS", + "Source": "CheckerboardColorResolveCS.azsl", "CompilerHints": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.shader b/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.shader index 4e2c83db35..9c7e66fbfd 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.shader @@ -1,5 +1,5 @@ { - "Source" : "LutGeneration", + "Source" : "LutGeneration.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader index de6c989223..849c7e3441 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader @@ -1,5 +1,5 @@ { - "Source" : "DepthPassSkin", + "Source" : "DepthPassSkin.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.shader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.shader index 1593f0bb66..ba145cd7cf 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.shader @@ -1,5 +1,5 @@ { - "Source" : "DiffuseComposite", + "Source" : "DiffuseComposite.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.shader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.shader index 3d4b711f63..1279775ad0 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.shader @@ -1,5 +1,5 @@ { - "Source" : "DiffuseGlobalFullscreen", + "Source" : "DiffuseGlobalFullscreen.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.shader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.shader index dff0f755d0..c3494e6bea 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.shader @@ -1,5 +1,5 @@ { - "Source" : "DiffuseProbeGridDownsample", + "Source" : "DiffuseProbeGridDownsample.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/ImGui/ImGui.shader b/Gems/Atom/Feature/Common/Assets/Shaders/ImGui/ImGui.shader index 4e742f8194..46729ff7f2 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/ImGui/ImGui.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/ImGui/ImGui.shader @@ -1,6 +1,6 @@ { - "Source" : "ImGui", + "Source" : "ImGui.azsl", "RasterState" : { "CullMode" : "None" }, diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCulling.shader b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCulling.shader index 6a4adcaade..24c1184b53 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCulling.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCulling.shader @@ -1,5 +1,5 @@ { - "Source": "LightCulling", + "Source": "LightCulling.azsl", "CompilerHints": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingHeatmap.shader b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingHeatmap.shader index 99b6c314fb..65e6a205e4 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingHeatmap.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingHeatmap.shader @@ -1,5 +1,5 @@ { - "Source" : "LightCullingHeatmap", + "Source" : "LightCullingHeatmap.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingRemap.shader b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingRemap.shader index d1b52525b6..795f028eba 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingRemap.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingRemap.shader @@ -1,5 +1,5 @@ { - "Source": "LightCullingRemap", + "Source": "LightCullingRemap.azsl", "Compiler": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingTilePrepare.shader b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingTilePrepare.shader index 070bc8e2bc..6a3173da96 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingTilePrepare.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/LightCulling/LightCullingTilePrepare.shader @@ -1,5 +1,5 @@ { - "Source": "LightCullingTilePrepare", + "Source": "LightCullingTilePrepare.azsl", "CompilerHints": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/LuxCore/RenderTexture.shader b/Gems/Atom/Feature/Common/Assets/Shaders/LuxCore/RenderTexture.shader index 32fff758be..6035f98f5d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/LuxCore/RenderTexture.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/LuxCore/RenderTexture.shader @@ -1,5 +1,5 @@ { - "Source" : "RenderTexture", + "Source" : "RenderTexture.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader index 08b1e7c298..38baa9ab87 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader @@ -1,5 +1,5 @@ { - "Source": "MorphTargetCS", + "Source": "MorphTargetCS.azsl", "ProgramSettings": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.shader index 4dee7cc702..f4a92a84d7 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.shader @@ -1,5 +1,5 @@ { - "Source": "CameraMotionVector", + "Source": "CameraMotionVector.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.shader index c585060f3d..2badc21957 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.shader @@ -1,5 +1,5 @@ { - "Source" : "MeshMotionVector", + "Source" : "MeshMotionVector.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader index 9a50e4e2cf..a253c2704b 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader @@ -1,5 +1,5 @@ { - "Source" : "MeshMotionVectorSkin", + "Source" : "MeshMotionVectorSkin.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/AcesOutputTransformLut.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/AcesOutputTransformLut.shader index 69ffb44394..b0f3a81cc9 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/AcesOutputTransformLut.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/AcesOutputTransformLut.shader @@ -1,5 +1,5 @@ { - "Source" : "AcesOutputTransformLut", + "Source" : "AcesOutputTransformLut.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.shader index ec18e33e7d..328b4353d8 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.shader @@ -1,5 +1,5 @@ { - "Source" : "ApplyShaperLookupTable", + "Source" : "ApplyShaperLookupTable.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BakeAcesOutputTransformLutCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BakeAcesOutputTransformLutCS.shader index 9274fa701e..928360b5ab 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BakeAcesOutputTransformLutCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BakeAcesOutputTransformLutCS.shader @@ -1,5 +1,5 @@ { - "Source": "BakeAcesOutputTransformLutCS", + "Source": "BakeAcesOutputTransformLutCS.azsl", "ProgramSettings": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BlendColorGradingLuts.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BlendColorGradingLuts.shader index 10bccf1d42..ed647d8398 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BlendColorGradingLuts.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BlendColorGradingLuts.shader @@ -1,5 +1,5 @@ { - "Source": "BlendColorGradingLuts", + "Source": "BlendColorGradingLuts.azsl", "DrawList" : "forward", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomBlurCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomBlurCS.shader index 467ccbf867..caaf411d29 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomBlurCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomBlurCS.shader @@ -1,5 +1,5 @@ { - "Source": "BloomBlurCS", + "Source": "BloomBlurCS.azsl", "DrawList" : "forward", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomCompositeCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomCompositeCS.shader index 0be9455da1..26ada282ac 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomCompositeCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomCompositeCS.shader @@ -1,5 +1,5 @@ { - "Source": "BloomCompositeCS", + "Source": "BloomCompositeCS.azsl", "DrawList" : "forward", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomDownsampleCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomDownsampleCS.shader index 7f33a041db..f78c4d4837 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomDownsampleCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BloomDownsampleCS.shader @@ -1,5 +1,5 @@ { - "Source": "BloomDownsampleCS", + "Source": "BloomDownsampleCS.azsl", "DrawList" : "forward", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader index 756ce0ec7a..75407d1095 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader @@ -1,5 +1,5 @@ { - "Source": "ContrastAdaptiveSharpening", + "Source": "ContrastAdaptiveSharpening.azsl", "ProgramSettings": { "EntryPoints": [ { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ConvertToAcescg.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ConvertToAcescg.shader index 82a70913d6..f5dae97b16 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ConvertToAcescg.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ConvertToAcescg.shader @@ -1,5 +1,5 @@ { - "Source" : "ConvertToAcescg", + "Source" : "ConvertToAcescg.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthDownsample.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthDownsample.shader index 77ae2b625c..6a66ff875d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthDownsample.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthDownsample.shader @@ -1,5 +1,5 @@ { - "Source": "DepthDownsample", + "Source": "DepthDownsample.azsl", "ProgramSettings" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldBlurBokeh.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldBlurBokeh.shader index 56dc124196..611f0a917c 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldBlurBokeh.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldBlurBokeh.shader @@ -1,5 +1,5 @@ { - "Source" : "DepthOfFieldBlurBokeh", + "Source" : "DepthOfFieldBlurBokeh.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldComposite.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldComposite.shader index a3d5890aa0..581b20d1a6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldComposite.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldComposite.shader @@ -1,5 +1,5 @@ { - "Source" : "DepthOfFieldComposite", + "Source" : "DepthOfFieldComposite.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldDownSample.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldDownSample.shader index 87b0ffc56c..4093ace95b 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldDownSample.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldDownSample.shader @@ -1,5 +1,5 @@ { - "Source" : "DepthOfFieldDownSample", + "Source" : "DepthOfFieldDownSample.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldMask.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldMask.shader index 83c24caa4b..7535e57b5d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldMask.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldMask.shader @@ -1,5 +1,5 @@ { - "Source" : "DepthOfFieldMask", + "Source" : "DepthOfFieldMask.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldPrepare.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldPrepare.shader index 9d7788a6e2..ec33cbf995 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldPrepare.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldPrepare.shader @@ -1,5 +1,5 @@ { - "Source" : "DepthOfFieldPrepare", + "Source" : "DepthOfFieldPrepare.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldWriteFocusDepthFromGpu.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldWriteFocusDepthFromGpu.shader index de6790c675..92243acb87 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldWriteFocusDepthFromGpu.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthOfFieldWriteFocusDepthFromGpu.shader @@ -1,5 +1,5 @@ { - "Source": "DepthOfFieldWriteFocusDepthFromGpu", + "Source": "DepthOfFieldWriteFocusDepthFromGpu.azsl", "ProgramSettings" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthToLinearDepth.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthToLinearDepth.shader index c8f2e6b48a..92e807fea6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthToLinearDepth.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthToLinearDepth.shader @@ -1,5 +1,5 @@ { - "Source" : "DepthToLinearDepth", + "Source" : "DepthToLinearDepth.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthUpsample.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthUpsample.shader index 8ecf28f8d3..80a4fabb3c 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthUpsample.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthUpsample.shader @@ -1,5 +1,5 @@ { - "Source": "DepthUpsample", + "Source": "DepthUpsample.azsl", "ProgramSettings" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DiffuseSpecularMerge.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DiffuseSpecularMerge.shader index 87ee6fcb17..fea445335b 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DiffuseSpecularMerge.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DiffuseSpecularMerge.shader @@ -1,5 +1,5 @@ { - "Source" : "DiffuseSpecularMerge", + "Source" : "DiffuseSpecularMerge.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DisplayMapper.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DisplayMapper.shader index f9fd37efb7..2b5c9998ef 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DisplayMapper.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DisplayMapper.shader @@ -1,5 +1,5 @@ { - "Source" : "DisplayMapper", + "Source" : "DisplayMapper.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DisplayMapperOnlyGammaCorrection.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DisplayMapperOnlyGammaCorrection.shader index fdd4b90ef9..51d3b22d12 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DisplayMapperOnlyGammaCorrection.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DisplayMapperOnlyGammaCorrection.shader @@ -1,5 +1,5 @@ { - "Source" : "DisplayMapperOnlyGammaCorrection", + "Source" : "DisplayMapperOnlyGammaCorrection.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DownsampleLuminanceMinAvgMaxCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DownsampleLuminanceMinAvgMaxCS.shader index 3b541fe132..ba764ae2f1 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DownsampleLuminanceMinAvgMaxCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DownsampleLuminanceMinAvgMaxCS.shader @@ -1,5 +1,5 @@ { - "Source": "DownsampleLuminanceMinAvgMaxCS", + "Source": "DownsampleLuminanceMinAvgMaxCS.azsl", "ProgramSettings": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DownsampleMinAvgMaxCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DownsampleMinAvgMaxCS.shader index a3eb7e6bdb..c7f560c17b 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DownsampleMinAvgMaxCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DownsampleMinAvgMaxCS.shader @@ -1,5 +1,5 @@ { - "Source": "DownsampleMinAvgMaxCS", + "Source": "DownsampleMinAvgMaxCS.azsl", "ProgramSettings": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptation.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptation.shader index e6e81b88fe..596af32317 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptation.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptation.shader @@ -1,5 +1,5 @@ { - "Source": "EyeAdaptation", + "Source": "EyeAdaptation.azsl", "ProgramSettings" : diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FastDepthAwareBlurHor.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FastDepthAwareBlurHor.shader index 74e5eeea42..d649584c44 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FastDepthAwareBlurHor.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FastDepthAwareBlurHor.shader @@ -1,5 +1,5 @@ { - "Source": "FastDepthAwareBlurHor", + "Source": "FastDepthAwareBlurHor.azsl", "ProgramSettings" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FastDepthAwareBlurVer.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FastDepthAwareBlurVer.shader index eb7b4225bb..b78cd6830a 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FastDepthAwareBlurVer.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FastDepthAwareBlurVer.shader @@ -1,5 +1,5 @@ { - "Source": "FastDepthAwareBlurVer", + "Source": "FastDepthAwareBlurVer.azsl", "ProgramSettings" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FullscreenCopy.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FullscreenCopy.shader index b6626cfc87..c82383167b 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FullscreenCopy.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/FullscreenCopy.shader @@ -1,5 +1,5 @@ { - "Source" : "FullscreenCopy", + "Source" : "FullscreenCopy.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.shader index f76f6708b7..8b519b70e7 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.shader @@ -1,5 +1,5 @@ { - "Source" : "HDRColorGrading", + "Source" : "HDRColorGrading.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.shader index bc9dbda122..5b1d53da89 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.shader @@ -1,5 +1,5 @@ { - "Source" : "LookModificationTransform", + "Source" : "LookModificationTransform.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.shader index 9be5f49cc1..06368ff039 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.shader @@ -1,5 +1,5 @@ { - "Source" : "LuminanceHeatmap", + "Source" : "LuminanceHeatmap.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader index f3dd11e11a..23e57b164f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader @@ -1,5 +1,5 @@ { - "Source": "LuminanceHistogramGenerator", + "Source": "LuminanceHistogramGenerator.azsl", "DrawList" : "forward", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/MSAAResolveCustom.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/MSAAResolveCustom.shader index e23dba6d73..6a08dbaaed 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/MSAAResolveCustom.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/MSAAResolveCustom.shader @@ -1,5 +1,5 @@ { - "Source": "MSAAResolveCustom", + "Source": "MSAAResolveCustom.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/MSAAResolveDepth.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/MSAAResolveDepth.shader index c9439d1033..74011db7fa 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/MSAAResolveDepth.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/MSAAResolveDepth.shader @@ -1,5 +1,5 @@ { - "Source" : "MSAAResolveDepth", + "Source" : "MSAAResolveDepth.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "Always" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ModulateTexture.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ModulateTexture.shader index d4cd98a5cd..4341d04390 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ModulateTexture.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ModulateTexture.shader @@ -1,5 +1,5 @@ { - "Source": "ModulateTexture", + "Source": "ModulateTexture.azsl", "ProgramSettings" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransform.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransform.shader index 1a13a21063..5cb652e93d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransform.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransform.shader @@ -1,5 +1,5 @@ { - "Source" : "OutputTransform", + "Source" : "OutputTransform.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAABlendingWeightCalculation.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAABlendingWeightCalculation.shader index 119f10fa47..cbca022dca 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAABlendingWeightCalculation.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAABlendingWeightCalculation.shader @@ -1,5 +1,5 @@ { - "Source" : "SMAABlendingWeightCalculation", + "Source" : "SMAABlendingWeightCalculation.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAAConvertToPerceptualColor.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAAConvertToPerceptualColor.shader index c009c62255..cccccd7418 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAAConvertToPerceptualColor.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAAConvertToPerceptualColor.shader @@ -1,5 +1,5 @@ { - "Source" : "SMAAConvertToPerceptualColor", + "Source" : "SMAAConvertToPerceptualColor.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAAEdgeDetection.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAAEdgeDetection.shader index 43110a1944..cb421a7e63 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAAEdgeDetection.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAAEdgeDetection.shader @@ -1,5 +1,5 @@ { - "Source" : "SMAAEdgeDetection", + "Source" : "SMAAEdgeDetection.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAANeighborhoodBlending.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAANeighborhoodBlending.shader index b93672b961..caeffcff15 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAANeighborhoodBlending.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAANeighborhoodBlending.shader @@ -1,5 +1,5 @@ { - "Source" : "SMAANeighborhoodBlending", + "Source" : "SMAANeighborhoodBlending.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ScreenSpaceSubsurfaceScatteringCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ScreenSpaceSubsurfaceScatteringCS.shader index a54060f093..f6e89db84f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ScreenSpaceSubsurfaceScatteringCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ScreenSpaceSubsurfaceScatteringCS.shader @@ -1,5 +1,5 @@ { - "Source": "ScreenSpaceSubsurfaceScatteringCS", + "Source": "ScreenSpaceSubsurfaceScatteringCS.azsl", "ProgramSettings": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SsaoCompute.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SsaoCompute.shader index 777400ca97..226f995cb6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SsaoCompute.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SsaoCompute.shader @@ -1,5 +1,5 @@ { - "Source": "SsaoCompute", + "Source": "SsaoCompute.azsl", "ProgramSettings" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader index f30ff92f20..fb0d510a8a 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader @@ -1,5 +1,5 @@ { - "Source": "Taa", + "Source": "Taa.azsl", "ProgramSettings": { "EntryPoints": [ { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/UniformColor.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/UniformColor.shader index 422199c420..5db8c97699 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/UniformColor.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/UniformColor.shader @@ -1,5 +1,5 @@ { - "Source" : "UniformColor", + "Source" : "UniformColor.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.shader index 847bf5e300..91bcd9b1b4 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionComposite", + "Source" : "ReflectionComposite.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionGlobalFullscreen.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionGlobalFullscreen.shader index 445a29c74f..f0b962c2eb 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionGlobalFullscreen.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionGlobalFullscreen.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionGlobalFullscreen", + "Source" : "ReflectionGlobalFullscreen.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.shader index 1da653f1e7..0e4cc83413 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionProbeBlendWeight", + "Source" : "ReflectionProbeBlendWeight.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.shader index 76cab60b3c..20dd4bcb56 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionProbeRenderInner", + "Source" : "ReflectionProbeRenderInner.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.shader index 9e60a5faf9..ce7b1c0487 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionProbeRenderOuter", + "Source" : "ReflectionProbeRenderOuter.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeStencil.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeStencil.shader index 538d746fba..7c23487dc1 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeStencil.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeStencil.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionProbeStencil", + "Source" : "ReflectionProbeStencil.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceBlurHorizontal.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceBlurHorizontal.shader index 013c42da57..aea21136dc 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceBlurHorizontal.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceBlurHorizontal.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionScreenSpaceBlurHorizontal", + "Source" : "ReflectionScreenSpaceBlurHorizontal.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceBlurVertical.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceBlurVertical.shader index d5ed94c4a9..dcebb4d2ae 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceBlurVertical.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceBlurVertical.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionScreenSpaceBlurVertical", + "Source" : "ReflectionScreenSpaceBlurVertical.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.shader index d04189b0ba..d23adf898d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionScreenSpaceComposite", + "Source" : "ReflectionScreenSpaceComposite.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceTrace.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceTrace.shader index a6e0b5df4e..563e0e3276 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceTrace.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceTrace.shader @@ -1,5 +1,5 @@ { - "Source" : "ReflectionScreenSpaceTrace", + "Source" : "ReflectionScreenSpaceTrace.azsl", "RasterState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/DepthExponentiation.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/DepthExponentiation.shader index e2f860a169..7d969a491b 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/DepthExponentiation.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/DepthExponentiation.shader @@ -1,5 +1,5 @@ { - "Source" : "DepthExponentiation", + "Source" : "DepthExponentiation.azsl", "DrawList" : "shadow", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/KawaseShadowBlur.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/KawaseShadowBlur.shader index dacacdafff..4f317b31e6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/KawaseShadowBlur.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/KawaseShadowBlur.shader @@ -1,5 +1,5 @@ { - "Source" : "KawaseShadowBlur", + "Source" : "KawaseShadowBlur.azsl", "DrawList" : "shadow", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.shader index 6ef5be872a..d56f40ccdb 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.shader @@ -1,5 +1,5 @@ { - "Source" : "Shadowmap", + "Source" : "Shadowmap.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader index 14c1352c08..40379d9193 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader @@ -1,5 +1,5 @@ { - "Source" : "ShadowmapSkin", + "Source" : "ShadowmapSkin.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningCS.shader index 6bb4f3c289..a853d61dd8 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningCS.shader @@ -1,5 +1,5 @@ { - "Source": "LinearSkinningCS", + "Source": "LinearSkinningCS.azsl", "ProgramSettings": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox.shader b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox.shader index 4edd81d07f..ff91e2f104 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox.shader @@ -1,5 +1,5 @@ { - "Source" : "SkyBox", + "Source" : "SkyBox.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.shader b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.shader index ec80d4a20e..ecd4c5b18e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.shader @@ -1,5 +1,5 @@ { - "Source" : "SkyBox_TwoOutputs", + "Source" : "SkyBox_TwoOutputs.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.shader b/Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.shader index 424c5ad6e3..943de52767 100644 --- a/Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.shader +++ b/Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.shader @@ -1,5 +1,5 @@ { - "Source": "DecomposeMsImage", + "Source": "DecomposeMsImage.azsl", "ProgramSettings": { diff --git a/Gems/Atom/RPI/Assets/Shader/ImagePreview.shader b/Gems/Atom/RPI/Assets/Shader/ImagePreview.shader index d87a4506a2..9a3594e2dd 100644 --- a/Gems/Atom/RPI/Assets/Shader/ImagePreview.shader +++ b/Gems/Atom/RPI/Assets/Shader/ImagePreview.shader @@ -1,5 +1,5 @@ { - "Source" : "ImagePreview", + "Source" : "ImagePreview.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false, "CompareFunc" : "GreaterEqual" } diff --git a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shader b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shader index 9fd3e76813..14088e8c1a 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shader +++ b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shader @@ -1,5 +1,5 @@ { - "Source" : "LyShineUI", + "Source" : "LyShineUI.azsl", "DepthStencilState" : { "Depth" : { diff --git a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/SimpleTextured.shader b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/SimpleTextured.shader index 0f4090c08e..0932ea00b7 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/SimpleTextured.shader +++ b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/SimpleTextured.shader @@ -1,5 +1,5 @@ { - "Source" : "SimpleTextured", + "Source" : "SimpleTextured.azsl", "DepthStencilState" : { "Depth" : { diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader index 601a2664b5..3793d9c8ed 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader @@ -1,5 +1,5 @@ { - "Source" : "TexturedIcon", + "Source" : "TexturedIcon.azsl", "DepthStencilState" : { "Depth" : { diff --git a/Gems/AtomLyIntegration/ImguiAtom/Assets/Shaders/ImGuiAtom/ImGuiAtom.shader b/Gems/AtomLyIntegration/ImguiAtom/Assets/Shaders/ImGuiAtom/ImGuiAtom.shader index db96bfecb7..2f2a81ea3d 100644 --- a/Gems/AtomLyIntegration/ImguiAtom/Assets/Shaders/ImGuiAtom/ImGuiAtom.shader +++ b/Gems/AtomLyIntegration/ImguiAtom/Assets/Shaders/ImGuiAtom/ImGuiAtom.shader @@ -1,6 +1,6 @@ { - "Source" : "ImGuiAtom", + "Source" : "ImGuiAtom.azsl", "RasterState" : { "CullMode" : "None" }, diff --git a/Gems/Terrain/Assets/Shaders/Terrain/Terrain_Shadowmap.shader b/Gems/Terrain/Assets/Shaders/Terrain/Terrain_Shadowmap.shader index 290c83bddf..8f163d25cd 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/Terrain_Shadowmap.shader +++ b/Gems/Terrain/Assets/Shaders/Terrain/Terrain_Shadowmap.shader @@ -1,5 +1,5 @@ { - "Source" : "Terrain_DepthPass", + "Source" : "Terrain_DepthPass.azsl", "DepthStencilState" : { "Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" } From 8e03d6f3065105f53a171345a2cf4a661cec4eb0 Mon Sep 17 00:00:00 2001 From: Yaakuro Date: Fri, 22 Oct 2021 20:10:47 +0200 Subject: [PATCH 143/152] Separate application into platform specific files. (#4799) Signed-off-by: Yaakuro --- Code/Editor/Core/QtEditorApplication.cpp | 132 +------------- Code/Editor/Core/QtEditorApplication.h | 11 +- Code/Editor/CryEdit.cpp | 17 +- .../Core/QtEditorApplication_linux.cpp | 13 +- .../Editor/Core/QtEditorApplication_linux.h | 25 +++ .../Platform/Linux/platform_linux_files.cmake | 2 +- .../Mac/Editor/Core/QtEditorApplication_mac.h | 25 +++ .../Editor}/Core/QtEditorApplication_mac.mm | 9 +- .../Platform/Mac/platform_mac_files.cmake | 2 +- .../Core/QtEditorApplication_windows.cpp | 165 ++++++++++++++++++ .../Editor/Core/QtEditorApplication_windows.h | 27 +++ .../Windows/platform_windows_files.cmake | 1 + 12 files changed, 279 insertions(+), 150 deletions(-) rename Code/Editor/{ => Platform/Linux/Editor}/Core/QtEditorApplication_linux.cpp (73%) create mode 100644 Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h create mode 100644 Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.h rename Code/Editor/{ => Platform/Mac/Editor}/Core/QtEditorApplication_mac.mm (78%) create mode 100644 Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.cpp create mode 100644 Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.h diff --git a/Code/Editor/Core/QtEditorApplication.cpp b/Code/Editor/Core/QtEditorApplication.cpp index a4aab24be4..2439228476 100644 --- a/Code/Editor/Core/QtEditorApplication.cpp +++ b/Code/Editor/Core/QtEditorApplication.cpp @@ -16,18 +16,11 @@ #include #include #include -#if defined(AZ_PLATFORM_WINDOWS) -#include -#include -#endif + #include #include #include -// AzFramework -#if defined(AZ_PLATFORM_WINDOWS) -# include -#endif // defined(AZ_PLATFORM_WINDOWS) // AzQtComponents #include @@ -39,7 +32,6 @@ #include "Settings.h" #include "CryEdit.h" - enum { // in milliseconds @@ -241,7 +233,6 @@ namespace Editor EditorQtApplication::EditorQtApplication(int& argc, char** argv) : AzQtApplication(argc, argv) - , m_inWinEventFilter(false) , m_stylesheet(new AzQtComponents::O3DEStylesheet(this)) , m_idleTimer(new QTimer(this)) { @@ -368,86 +359,10 @@ namespace Editor UninstallEditorTranslators(); } -#if defined(AZ_PLATFORM_WINDOWS) - bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long* result) + EditorQtApplication* EditorQtApplication::instance() { - MSG* msg = (MSG*)message; - - if (msg->message == WM_MOVING || msg->message == WM_SIZING) - { - m_isMovingOrResizing = true; - } - else if (msg->message == WM_EXITSIZEMOVE) - { - m_isMovingOrResizing = false; - } - - // Prevent the user from being able to move the window in game mode. - // This is done during the hit test phase to bypass the native window move messages. If the window - // decoration wrapper title bar contains the cursor, set the result to HTCLIENT instead of - // HTCAPTION. - if (msg->message == WM_NCHITTEST && GetIEditor()->IsInGameMode()) - { - const LRESULT defWinProcResult = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); - if (defWinProcResult == 1) - { - if (QWidget* widget = QWidget::find((WId)msg->hwnd)) - { - if (auto wrapper = qobject_cast(widget)) - { - AzQtComponents::TitleBar* titleBar = wrapper->titleBar(); - const short global_x = static_cast(LOWORD(msg->lParam)); - const short global_y = static_cast(HIWORD(msg->lParam)); - - const QPoint globalPos = QHighDpi::fromNativePixels(QPoint(global_x, global_y), widget->window()->windowHandle()); - const QPoint local = titleBar->mapFromGlobal(globalPos); - if (titleBar->draggableRect().contains(local) && !titleBar->isTopResizeArea(globalPos)) - { - *result = HTCLIENT; - return true; - } - } - } - } - } - - // Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system. - // These events are only broadcast in game mode. In Editor mode, RenderViewportWidget creates synthetic - // keyboard and mouse events via Qt. - if (GetIEditor()->IsInGameMode()) - { - if (msg->message == WM_INPUT) - { - UINT rawInputSize; - const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, nullptr, &rawInputSize, rawInputHeaderSize); - - AZStd::array rawInputBytesArray; - LPBYTE rawInputBytes = rawInputBytesArray.data(); - - [[maybe_unused]] const UINT bytesCopied = GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); - CRY_ASSERT(bytesCopied == rawInputSize); - - RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; - CRY_ASSERT(rawInput); - - AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputEvent, *rawInput); - - return false; - } - else if (msg->message == WM_DEVICECHANGE) - { - if (msg->wParam == 0x0007) // DBT_DEVNODES_CHANGED - { - AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputDeviceChangeEvent); - } - return true; - } - } - - return false; + return static_cast(QApplication::instance()); } -#endif void EditorQtApplication::OnEditorNotifyEvent(EEditorNotifyEvent event) { @@ -505,11 +420,6 @@ namespace Editor return m_stylesheet->GetColorByName(name); } - EditorQtApplication* EditorQtApplication::instance() - { - return static_cast(QApplication::instance()); - } - bool EditorQtApplication::IsActive() { return applicationState() == Qt::ApplicationActive; @@ -613,42 +523,6 @@ namespace Editor case QEvent::KeyRelease: m_pressedKeys.remove(reinterpret_cast(event)->key()); break; -#ifdef AZ_PLATFORM_WINDOWS - case QEvent::Leave: - { - // if we receive a leave event for a toolbar on Windows - // check first whether we really left it. If we didn't: start checking - // for the tool bar under the mouse by timer to check when we really left. - // Synthesize a new leave event then. Workaround for LY-69788 - auto toolBarAt = [](const QPoint& pos) -> QToolBar* { - QWidget* widget = qApp->widgetAt(pos); - while (widget != nullptr) - { - if (QToolBar* tb = qobject_cast(widget)) - { - return tb; - } - widget = widget->parentWidget(); - } - return nullptr; - }; - if (object == toolBarAt(QCursor::pos())) - { - QTimer* t = new QTimer(object); - t->start(100); - connect(t, &QTimer::timeout, object, [t, object, toolBarAt]() { - if (object != toolBarAt(QCursor::pos())) - { - QEvent event(QEvent::Leave); - qApp->sendEvent(object, &event); - t->deleteLater(); - } - }); - return true; - } - break; - } -#endif default: break; } diff --git a/Code/Editor/Core/QtEditorApplication.h b/Code/Editor/Core/QtEditorApplication.h index 2e3612095e..0d702bf647 100644 --- a/Code/Editor/Core/QtEditorApplication.h +++ b/Code/Editor/Core/QtEditorApplication.h @@ -72,14 +72,12 @@ namespace Editor //// static EditorQtApplication* instance(); + static EditorQtApplication* newInstance(int& argc, char** argv); static bool IsActive(); bool isMovingOrResizing() const; - // QAbstractNativeEventFilter: - bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; - // IEditorNotifyListener: void OnEditorNotifyEvent(EEditorNotifyEvent event) override; @@ -100,6 +98,10 @@ namespace Editor signals: void skinChanged(); + protected: + + bool m_isMovingOrResizing = false; + private: enum TimerResetFlag { @@ -116,8 +118,6 @@ namespace Editor AzQtComponents::O3DEStylesheet* m_stylesheet; - bool m_inWinEventFilter = false; - // Translators void InstallEditorTranslators(); void UninstallEditorTranslators(); @@ -127,7 +127,6 @@ namespace Editor QTranslator* m_editorTranslator = nullptr; QTranslator* m_assetBrowserTranslator = nullptr; QTimer* const m_idleTimer = nullptr; - bool m_isMovingOrResizing = false; AZ::UserSettingsProvider m_localUserSettings; diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index ab80e7d23a..e4138da932 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -4135,9 +4135,9 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[]) Editor::EditorQtApplication::InstallQtLogHandler(); AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); - Editor::EditorQtApplication app(argc, argv); + Editor::EditorQtApplication* app = Editor::EditorQtApplication::newInstance(argc, argv); - if (app.arguments().contains("-autotest_mode")) + if (app->arguments().contains("-autotest_mode")) { // Nullroute all stdout to null for automated tests, this way we make sure // that the test result output is not polluted with unrelated output data. @@ -4173,12 +4173,7 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[]) return -1; } - AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::NotifyQtApplicationAvailable, &app); - - #if defined(AZ_PLATFORM_MAC) - // Native menu bars do not work on macOS due to all the tool dialogs - QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); - #endif + AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::NotifyQtApplicationAvailable, app); int exitCode = 0; @@ -4189,9 +4184,9 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[]) if (didCryEditStart) { - app.EnableOnIdle(); + app->EnableOnIdle(); - ret = app.exec(); + ret = app->exec(); } else { @@ -4202,6 +4197,8 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[]) } + delete app; + gSettings.Disconnect(); return ret; diff --git a/Code/Editor/Core/QtEditorApplication_linux.cpp b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp similarity index 73% rename from Code/Editor/Core/QtEditorApplication_linux.cpp rename to Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp index 5491134170..ad5e57479b 100644 --- a/Code/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp @@ -6,7 +6,7 @@ * */ -#include "QtEditorApplication.h" +#include "QtEditorApplication_linux.h" #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB #include @@ -14,7 +14,16 @@ namespace Editor { - bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*) + EditorQtApplication* EditorQtApplication::newInstance(int& argc, char** argv) + { +#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + return new EditorQtApplicationXcb(argc, argv); +#endif + + return nullptr; + } + + bool EditorQtApplicationXcb::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*) { if (GetIEditor()->IsInGameMode()) { diff --git a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h new file mode 100644 index 0000000000..8c145c3aa7 --- /dev/null +++ b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace Editor +{ + class EditorQtApplicationXcb : public EditorQtApplication + { + Q_OBJECT + public: + EditorQtApplicationXcb(int& argc, char** argv) + : EditorQtApplication(argc, argv) + { + } + + // QAbstractNativeEventFilter: + bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; + }; +} // namespace Editor diff --git a/Code/Editor/Platform/Linux/platform_linux_files.cmake b/Code/Editor/Platform/Linux/platform_linux_files.cmake index 3baed702c2..875acad1c3 100644 --- a/Code/Editor/Platform/Linux/platform_linux_files.cmake +++ b/Code/Editor/Platform/Linux/platform_linux_files.cmake @@ -7,6 +7,6 @@ # set(FILES - ../../Core/QtEditorApplication_linux.cpp + Editor/Core/QtEditorApplication_linux.cpp ../Common/Unimplemented/Util/Mailer_Unimplemented.cpp ) diff --git a/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.h b/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.h new file mode 100644 index 0000000000..2de7514bbf --- /dev/null +++ b/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace Editor +{ + class EditorQtApplicationMac : public EditorQtApplication + { + Q_OBJECT + public: + EditorQtApplicationMac(int& argc, char** argv) + : EditorQtApplication(argc, argv) + { + } + + // QAbstractNativeEventFilter: + bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; + }; +} // namespace Editor diff --git a/Code/Editor/Core/QtEditorApplication_mac.mm b/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm similarity index 78% rename from Code/Editor/Core/QtEditorApplication_mac.mm rename to Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm index 17ad8f7ffd..a7f59b7ac8 100644 --- a/Code/Editor/Core/QtEditorApplication_mac.mm +++ b/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm @@ -19,7 +19,14 @@ namespace Editor { - bool EditorQtApplication::nativeEventFilter(const QByteArray& eventType, void* message, long* result) + EditorQtApplication* EditorQtApplication::newInstance(int& argc, char** argv) + { + QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); + + return new EditorQtApplicationMac(argc, argv); + } + + bool EditorQtApplicationMac::nativeEventFilter(const QByteArray& eventType, void* message, long* result) { NSEvent* event = (NSEvent*)message; if (GetIEditor()->IsInGameMode()) diff --git a/Code/Editor/Platform/Mac/platform_mac_files.cmake b/Code/Editor/Platform/Mac/platform_mac_files.cmake index 91a0bed574..5549eaf2f9 100644 --- a/Code/Editor/Platform/Mac/platform_mac_files.cmake +++ b/Code/Editor/Platform/Mac/platform_mac_files.cmake @@ -7,7 +7,7 @@ # set(FILES - ../../Core/QtEditorApplication_mac.mm + Editor/Core/QtEditorApplication_mac.mm ../../LogFile_mac.mm ../../WindowObserver_mac.h ../../WindowObserver_mac.mm diff --git a/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.cpp b/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.cpp new file mode 100644 index 0000000000..f8065af931 --- /dev/null +++ b/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "QtEditorApplication_windows.h" + +// Qt +#include +#include +#include +#include +#include + +#include +#include + +// AzQtComponents +#include +#include + +// AzFramework +#include + +namespace Editor +{ + EditorQtApplication* EditorQtApplication::newInstance(int& argc, char** argv) + { + return new EditorQtApplicationWindows(argc, argv); + } + + bool EditorQtApplicationWindows::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long* result) + { + MSG* msg = (MSG*)message; + + if (msg->message == WM_MOVING || msg->message == WM_SIZING) + { + m_isMovingOrResizing = true; + } + else if (msg->message == WM_EXITSIZEMOVE) + { + m_isMovingOrResizing = false; + } + + // Prevent the user from being able to move the window in game mode. + // This is done during the hit test phase to bypass the native window move messages. If the window + // decoration wrapper title bar contains the cursor, set the result to HTCLIENT instead of + // HTCAPTION. + if (msg->message == WM_NCHITTEST && GetIEditor()->IsInGameMode()) + { + const LRESULT defWinProcResult = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); + if (defWinProcResult == 1) + { + if (QWidget* widget = QWidget::find((WId)msg->hwnd)) + { + if (auto wrapper = qobject_cast(widget)) + { + AzQtComponents::TitleBar* titleBar = wrapper->titleBar(); + const short global_x = static_cast(LOWORD(msg->lParam)); + const short global_y = static_cast(HIWORD(msg->lParam)); + + const QPoint globalPos = QHighDpi::fromNativePixels(QPoint(global_x, global_y), widget->window()->windowHandle()); + const QPoint local = titleBar->mapFromGlobal(globalPos); + if (titleBar->draggableRect().contains(local) && !titleBar->isTopResizeArea(globalPos)) + { + *result = HTCLIENT; + return true; + } + } + } + } + } + + // Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system. + // These events are only broadcast in game mode. In Editor mode, RenderViewportWidget creates synthetic + // keyboard and mouse events via Qt. + if (GetIEditor()->IsInGameMode()) + { + if (msg->message == WM_INPUT) + { + UINT rawInputSize; + const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); + GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, nullptr, &rawInputSize, rawInputHeaderSize); + + AZStd::array rawInputBytesArray; + LPBYTE rawInputBytes = rawInputBytesArray.data(); + + [[maybe_unused]] const UINT bytesCopied = + GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); + CRY_ASSERT(bytesCopied == rawInputSize); + + RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; + CRY_ASSERT(rawInput); + + AzFramework::RawInputNotificationBusWindows::Broadcast( + &AzFramework::RawInputNotificationsWindows::OnRawInputEvent, *rawInput); + + return false; + } + else if (msg->message == WM_DEVICECHANGE) + { + if (msg->wParam == 0x0007) // DBT_DEVNODES_CHANGED + { + AzFramework::RawInputNotificationBusWindows::Broadcast( + &AzFramework::RawInputNotificationsWindows::OnRawInputDeviceChangeEvent); + } + return true; + } + } + + return false; + } + + bool EditorQtApplicationWindows::eventFilter(QObject* object, QEvent* event) + { + switch (event->type()) + { + case QEvent::Leave: + { + // if we receive a leave event for a toolbar on Windows + // check first whether we really left it. If we didn't: start checking + // for the tool bar under the mouse by timer to check when we really left. + // Synthesize a new leave event then. Workaround for LY-69788 + auto toolBarAt = [](const QPoint& pos) -> QToolBar* + { + QWidget* widget = qApp->widgetAt(pos); + while (widget != nullptr) + { + if (QToolBar* tb = qobject_cast(widget)) + { + return tb; + } + widget = widget->parentWidget(); + } + return false; + }; + if (object == toolBarAt(QCursor::pos())) + { + QTimer* t = new QTimer(object); + t->start(100); + connect( + t, &QTimer::timeout, object, + [t, object, toolBarAt]() + { + if (object != toolBarAt(QCursor::pos())) + { + QEvent event(QEvent::Leave); + qApp->sendEvent(object, &event); + t->deleteLater(); + } + }); + return true; + } + break; + } + default: + break; + } + + return EditorQtApplication::eventFilter(object, event); + } +} // namespace Editor diff --git a/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.h b/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.h new file mode 100644 index 0000000000..6967d5a3b0 --- /dev/null +++ b/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace Editor +{ + class EditorQtApplicationWindows : public EditorQtApplication + { + Q_OBJECT + public: + EditorQtApplicationWindows(int& argc, char** argv) + : EditorQtApplication(argc, argv) + { + } + + // QAbstractNativeEventFilter: + bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; + + bool eventFilter(QObject* object, QEvent* event) override; + }; +} // namespace Editor diff --git a/Code/Editor/Platform/Windows/platform_windows_files.cmake b/Code/Editor/Platform/Windows/platform_windows_files.cmake index d7fc96e0a3..df7a3c817f 100644 --- a/Code/Editor/Platform/Windows/platform_windows_files.cmake +++ b/Code/Editor/Platform/Windows/platform_windows_files.cmake @@ -7,5 +7,6 @@ # set(FILES + Editor/Core/QtEditorApplication_windows.cpp Util/Mailer_Windows.cpp ) From ee043f4fed7d3db8d1c559675e8e0d04711abdc0 Mon Sep 17 00:00:00 2001 From: moudgils <47460854+moudgils@users.noreply.github.com> Date: Fri, 22 Oct 2021 12:10:15 -0700 Subject: [PATCH 144/152] =?UTF-8?q?Fix=20RHI/Subpass=20sample=20for=20Vulk?= =?UTF-8?q?an.=20The=20issue=20was=20that=20we=20were=20exectui=E2=80=A6?= =?UTF-8?q?=20(#4904)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix RHI/Subpass sample for Vulkan. The issue was that we were exectuing the sub-pass groups more than once when parallel encoding was used. Signed-off-by: moudgils * Added a minor comment Signed-off-by: moudgils * Minor perf improvement Signed-off-by: moudgils --- .../Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.cpp | 6 ++++++ .../Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.h | 2 ++ Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.cpp index e85c86f3f4..9c1bb896f9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.cpp @@ -27,6 +27,7 @@ namespace AZ { EndInternal(); m_device->GetCommandQueueContext().GetCommandQueue(m_hardwareQueueClass).ExecuteWork(AZStd::move(m_workRequest)); + m_isExecuted = true; } bool FrameGraphExecuteGroupHandlerBase::IsComplete() const @@ -42,6 +43,11 @@ namespace AZ return true; } + bool FrameGraphExecuteGroupHandlerBase::IsExecuted() const + { + return m_isExecuted; + } + template void InsertWorkRequestElements(T& destination, const T& source) { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.h index a0aa6d465a..1aad409667 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupHandlerBase.h @@ -41,6 +41,7 @@ namespace AZ void End(); bool IsComplete() const; + bool IsExecuted() const; protected: virtual RHI::ResultCode InitInternal(Device& device, const AZStd::vector& executeGroups) = 0; @@ -52,6 +53,7 @@ namespace AZ ExecuteWorkRequest m_workRequest; RHI::HardwareQueueClass m_hardwareQueueClass = RHI::HardwareQueueClass::Graphics; AZStd::vector m_executeGroups; + bool m_isExecuted = false; }; } } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp index 7165b5c305..45fe9344a9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp @@ -177,8 +177,8 @@ namespace AZ auto findIter = m_groupHandlers.find(group.GetGroupId()); AZ_Assert(findIter != m_groupHandlers.end(), "Could not find group handler for groupId %d", group.GetGroupId().GetIndex()); FrameGraphExecuteGroupHandlerBase* handler = findIter->second.get(); - // Wait until all execute groups of the handler has finished. - if (handler->IsComplete()) + // Wait until all execute groups of the handler has finished and also make sure that the handler itself hasn't executed already (which is possible for parallel encoding). + if (!handler->IsExecuted() && handler->IsComplete()) { // This will execute the recorded work into the queue. handler->End(); From ea1284c5810f738f400950bebc24a7d3021db807 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Fri, 22 Oct 2021 12:14:35 -0700 Subject: [PATCH 145/152] fixing up atom_feature_common_asset_files.cmake Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- .../Common/Assets/atom_feature_common_asset_files.cmake | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index a33034a1b3..94b711e86c 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -160,8 +160,6 @@ set(FILES Passes/LutGeneration.pass Passes/MainPipeline.pass Passes/MainPipelineRenderToTexture.pass - Passes/ToolsPipeline.pass - Passes/ToolsPipelineRenderToTexture.pass Passes/MeshMotionVector.pass Passes/ModulateTexture.pass Passes/MorphTarget.pass @@ -211,8 +209,8 @@ set(FILES Passes/SsaoParent.pass Passes/SubsurfaceScattering.pass Passes/Taa.pass - Passes/ThumbnailPipeline.pass - Passes/ThumbnailPipelineRenderToTexture.pass + Passes/ToolsPipeline.pass + Passes/ToolsPipelineRenderToTexture.pass Passes/Transparent.pass Passes/TransparentParent.pass Passes/UI.pass @@ -306,6 +304,7 @@ set(FILES ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli ShaderLib/Atom/Features/Shadow/BicubicPcfFilters.azsli ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli + ShaderLib/Atom/Features/Shadow/NormalOffsetShadows.azsli ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli ShaderLib/Atom/Features/Shadow/ReceiverPlaneDepthBias.azsli ShaderLib/Atom/Features/Shadow/Shadow.azsli From 2fe4524458556e717d7cd6d9c98807bbb6a1ee74 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Fri, 22 Oct 2021 14:22:57 -0500 Subject: [PATCH 146/152] Terrain API cleanups (#4914) * Terrain API fixups Moved SurfaceData definitions in AzFramework out of terrain into separate files. Added some missing API calls: Get*FromVector2, GetSurfacePoint* Changed OrderedSurfaceTagWeightSet to SurfaceTagWeightList Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * PR feedback - remove IsClose check. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed PhysX test compile failures by redcoding a bunch of "dummy terrain" implementation that's unused. It was originally added for the PhysX Terrain component, but that component is long gone and has been superceded by the more generic PhysX Heightfield Collider. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed up failing terrain unit tests. Added API changes, and changed the assumption on where the surface weight sort is taking place. The component is no longer expected to provide the sorted list, it only needs to be sorted at the end coming out of the terrain system, so the unit tests have been modified to reflect that. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../AzFramework/Application/Application.cpp | 3 + .../AzFramework/SurfaceData/SurfaceData.cpp | 59 +++++++++ .../AzFramework/SurfaceData/SurfaceData.h | 72 +++++++++++ .../Terrain/TerrainDataRequestBus.cpp | 71 ++++------- .../Terrain/TerrainDataRequestBus.h | 116 +++++++++--------- .../AzFramework/azframework_files.cmake | 2 + .../Benchmarks/PhysXBenchmarksCommon.cpp | 4 - .../Tests/Benchmarks/PhysXBenchmarksCommon.h | 1 - Gems/PhysX/Code/Tests/EditorTestUtilities.cpp | 4 - Gems/PhysX/Code/Tests/EditorTestUtilities.h | 1 - Gems/PhysX/Code/Tests/PhysXTestFixtures.cpp | 4 - Gems/PhysX/Code/Tests/PhysXTestFixtures.h | 1 - Gems/PhysX/Code/Tests/PhysXTestUtil.h | 64 ---------- .../Ebuses/TerrainAreaSurfaceRequestBus.h | 4 +- Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h | 29 +++-- .../MockTerrainAreaSurfaceRequestBus.h | 2 +- .../TerrainSurfaceDataSystemComponent.cpp | 14 ++- .../TerrainSurfaceGradientListComponent.cpp | 7 +- .../TerrainSurfaceGradientListComponent.h | 2 +- .../Source/TerrainSystem/TerrainSystem.cpp | 111 ++++++++++------- .../Code/Source/TerrainSystem/TerrainSystem.h | 41 +++++-- .../Tests/TerrainSurfaceGradientListTests.cpp | 16 +-- Gems/Terrain/Code/Tests/TerrainSystemTest.cpp | 47 ++++--- 23 files changed, 390 insertions(+), 285 deletions(-) create mode 100644 Code/Framework/AzFramework/AzFramework/SurfaceData/SurfaceData.cpp create mode 100644 Code/Framework/AzFramework/AzFramework/SurfaceData/SurfaceData.h diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index 1f99a594fa..f8d0ea8bb1 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -278,6 +279,8 @@ namespace AzFramework AzFramework::RemoteStorageDriveConfig::Reflect(context); Physics::ReflectionUtils::ReflectPhysicsApi(context); + AzFramework::SurfaceData::SurfaceTagWeight::Reflect(context); + AzFramework::SurfaceData::SurfacePoint::Reflect(context); AzFramework::Terrain::TerrainDataRequests::Reflect(context); if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) diff --git a/Code/Framework/AzFramework/AzFramework/SurfaceData/SurfaceData.cpp b/Code/Framework/AzFramework/AzFramework/SurfaceData/SurfaceData.cpp new file mode 100644 index 0000000000..55fec57bb6 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/SurfaceData/SurfaceData.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AzFramework::SurfaceData +{ + void SurfaceTagWeight::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Field("m_surfaceType", &SurfaceTagWeight::m_surfaceType) + ->Field("m_weight", &SurfaceTagWeight::m_weight) + ; + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Category, "SurfaceData") + ->Constructor() + ->Property("surfaceType", BehaviorValueProperty(&SurfaceTagWeight::m_surfaceType)) + ->Property("weight", BehaviorValueProperty(&SurfaceTagWeight::m_weight)) + ; + } + } + + void SurfacePoint::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Field("m_position", &SurfacePoint::m_position) + ->Field("m_normal", &SurfacePoint::m_normal) + ->Field("m_surfaceTags", &SurfacePoint::m_surfaceTags) + ; + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("AzFramework::SurfaceData::SurfacePoint") + ->Attribute(AZ::Script::Attributes::Category, "SurfaceData") + ->Constructor() + ->Property("position", BehaviorValueProperty(&SurfacePoint::m_position)) + ->Property("normal", BehaviorValueProperty(&SurfacePoint::m_normal)) + ->Property("surfaceTags", BehaviorValueProperty(&SurfacePoint::m_surfaceTags)) + ; + } + } + +} // namespace AzFramework::SurfaceData diff --git a/Code/Framework/AzFramework/AzFramework/SurfaceData/SurfaceData.h b/Code/Framework/AzFramework/AzFramework/SurfaceData/SurfaceData.h new file mode 100644 index 0000000000..77d9ae4239 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/SurfaceData/SurfaceData.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include + +namespace AzFramework::SurfaceData +{ + namespace Constants + { + static constexpr const char* s_unassignedTagName = "(unassigned)"; + } + + struct SurfaceTagWeight + { + AZ_TYPE_INFO(SurfaceTagWeight, "{EA14018E-E853-4BF5-8E13-D83BB99A54CC}"); + SurfaceTagWeight() = default; + SurfaceTagWeight(AZ::Crc32 surfaceType, float weight) + : m_surfaceType(surfaceType) + , m_weight(weight) + { + } + + AZ::Crc32 m_surfaceType = AZ::Crc32(Constants::s_unassignedTagName); + float m_weight = 0.0f; //! A Value in the range [0.0f .. 1.0f] + + static void Reflect(AZ::ReflectContext* context); + }; + + struct SurfaceTagWeightComparator + { + bool operator()(const SurfaceTagWeight& tagWeight1, const SurfaceTagWeight& tagWeight2) const + { + // Return a deterministic sort order for surface tags from highest to lowest weight, with the surface types sorted + // in a predictable order when the weights are equal. The surface type sort order is meaningless since it is sorting CRC + // values, it's really just important for it to be stable. + // For the floating-point weight comparisons we use exact instead of IsClose value comparisons for a similar reason - we + // care about being sorted highest to lowest, but there's no inherent meaning in sorting surface types with *similar* weights + // together. + + if (tagWeight1.m_weight != tagWeight2.m_weight) + { + return tagWeight1.m_weight > tagWeight2.m_weight; + } + else + { + return tagWeight1.m_surfaceType > tagWeight2.m_surfaceType; + } + } + }; + + using SurfaceTagWeightList = AZStd::vector; + + struct SurfacePoint final + { + AZ_TYPE_INFO(SurfacePoint, "{331A3D0E-BB1D-47BF-96A2-249FAA0D720D}"); + + AZ::Vector3 m_position; + AZ::Vector3 m_normal; + SurfaceTagWeightList m_surfaceTags; + + static void Reflect(AZ::ReflectContext* context); + }; +} // namespace AzFramework::SurfaceData diff --git a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.cpp b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.cpp index 1fb29cfa30..561408db20 100644 --- a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.cpp +++ b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.cpp @@ -8,56 +8,33 @@ #include "TerrainDataRequestBus.h" #include +#include -namespace AzFramework +namespace AzFramework::Terrain { - namespace SurfaceData + void TerrainDataRequests::Reflect(AZ::ReflectContext* context) { - void SurfaceTagWeight::Reflect(AZ::ReflectContext* context) + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Field("m_surfaceType", &SurfaceTagWeight::m_surfaceType) - ->Field("m_weight", &SurfaceTagWeight::m_weight) - ; - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class("SurfaceTagWeight") - ->Property("m_surfaceType", BehaviorValueProperty(&SurfaceTagWeight::m_surfaceType)) - ->Property("m_weight", BehaviorValueProperty(&SurfaceTagWeight::m_weight)) - ; - } - } - } //namespace SurfaceData - - namespace Terrain - { - void TerrainDataRequests::Reflect(AZ::ReflectContext* context) - { - AzFramework::SurfaceData::SurfaceTagWeight::Reflect(context); - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("TerrainDataRequestBus") - ->Attribute(AZ::Script::Attributes::Category, "Terrain") - ->Event("GetHeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeight) - ->Event("GetHeightFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeightFromFloats) - ->Event("GetMaxSurfaceWeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeight) - ->Event("GetMaxSurfaceWeightFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeightFromFloats) - ->Event("GetIsHoleFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetIsHoleFromFloats) - ->Event("GetNormal", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetNormal) - ->Event("GetNormalFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetNormalFromFloats) - ->Event("GetTerrainAabb", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainAabb) - ->Event("GetTerrainHeightQueryResolution", - &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainHeightQueryResolution) - ; - - } - + behaviorContext->EBus("TerrainDataRequestBus") + ->Attribute(AZ::Script::Attributes::Category, "Terrain") + ->Event("GetHeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeight) + ->Event("GetNormal", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetNormal) + ->Event("GetMaxSurfaceWeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeight) + ->Event("GetMaxSurfaceWeightFromVector2", + &AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeightFromVector2) + ->Event("GetSurfaceWeights", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfaceWeights) + ->Event("GetSurfaceWeightsFromVector2", + &AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfaceWeightsFromVector2) + ->Event("GetIsHoleFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetIsHoleFromFloats) + ->Event("GetSurfacePoint", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfacePoint) + ->Event("GetSurfacePointFromVector2", + &AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfacePointFromVector2) + ->Event("GetTerrainAabb", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainAabb) + ->Event("GetTerrainHeightQueryResolution", + &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainHeightQueryResolution) + ; } - } //namespace Terrain -} // namespace AzFramework + } +} // namespace AzFramework::Terrain diff --git a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h index 3f6a8f0960..30a2f8e044 100644 --- a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h +++ b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h @@ -8,50 +8,13 @@ #pragma once #include -#include #include #include #include -#include +#include namespace AzFramework { - namespace SurfaceData - { - namespace Constants - { - static const char* s_unassignedTagName = "(unassigned)"; - } - - struct SurfaceTagWeight - { - AZ_TYPE_INFO(SurfaceTagWeight, "{EA14018E-E853-4BF5-8E13-D83BB99A54CC}"); - - AZ::Crc32 m_surfaceType = AZ::Crc32(Constants::s_unassignedTagName); - float m_weight = 0.0f; //! A Value in the range [0.0f .. 1.0f] - - //! Don't call this directly. TerrainDataRequests::Reflect is doing it already. - static void Reflect(AZ::ReflectContext* context); - }; - - struct SurfaceTagWeightComparator - { - bool operator()(const SurfaceTagWeight& tagWeight1, const SurfaceTagWeight& tagWeight2) const - { - if (!AZ::IsClose(tagWeight1.m_weight, tagWeight2.m_weight)) - { - return tagWeight1.m_weight > tagWeight2.m_weight; - } - else - { - return tagWeight1.m_surfaceType > tagWeight2.m_surfaceType; - } - } - }; - - using OrderedSurfaceTagWeightSet = AZStd::set; - } //namespace SurfaceData - namespace Terrain { @@ -91,49 +54,82 @@ namespace AzFramework //! Returns terrains height in meters at location x,y. //! @terrainExistsPtr: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain HOLE then *terrainExistsPtr will become false, //! otherwise *terrainExistsPtr will become true. - virtual float GetHeight(AZ::Vector3 position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; - virtual float GetHeightFromFloats(float x, float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual float GetHeight(const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual float GetHeightFromVector2( + const AZ::Vector2& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual float GetHeightFromFloats( + float x, float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + + //! Returns true if there's a hole at location x,y. + //! Also returns true if there's no terrain data at location x,y. + virtual bool GetIsHole(const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR) const = 0; + virtual bool GetIsHoleFromVector2(const AZ::Vector2& position, Sampler sampleFilter = Sampler::BILINEAR) const = 0; + virtual bool GetIsHoleFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR) const = 0; + + // Given an XY coordinate, return the surface normal. + //! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a + //! terrain HOLE then *terrainExistsPtr will be set to false, + //! otherwise *terrainExistsPtr will be set to true. + virtual AZ::Vector3 GetNormal( + const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual AZ::Vector3 GetNormalFromVector2( + const AZ::Vector2& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual AZ::Vector3 GetNormalFromFloats( + float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; //! Given an XY coordinate, return the max surface type and weight. //! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain HOLE then *terrainExistsPtr will be set to false, //! otherwise *terrainExistsPtr will be set to true. - virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight(AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; - virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2(const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0; - virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight( + const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2( + const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0; + virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats( + float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; //! Given an XY coordinate, return the set of surface types and weights. The Vector3 input position version is defined to ignore //! the input Z value. virtual void GetSurfaceWeights( const AZ::Vector3& inPosition, - SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0; virtual void GetSurfaceWeightsFromVector2( const AZ::Vector2& inPosition, - SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0; virtual void GetSurfaceWeightsFromFloats( float x, float y, - SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0; //! Convenience function for low level systems that can't do a reverse lookup from Crc to string. Everyone else should use GetMaxSurfaceWeight or GetMaxSurfaceWeightFromFloats. //! Not available in the behavior context. //! Returns nullptr if the position is inside a hole or outside of the terrain boundaries. - virtual const char * GetMaxSurfaceName(AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual const char* GetMaxSurfaceName( + const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; - //! Returns true if there's a hole at location x,y. - //! Also returns true if there's no terrain data at location x,y. - virtual bool GetIsHoleFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR) const = 0; - - // Given an XY coordinate, return the surface normal. - //! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain HOLE then *terrainExistsPtr will be set to false, - //! otherwise *terrainExistsPtr will be set to true. - virtual AZ::Vector3 GetNormal(AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; - virtual AZ::Vector3 GetNormalFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + //! Given an XY coordinate, return all terrain information at that location. The Vector3 input position version is defined + //! to ignore the input Z value. + virtual void GetSurfacePoint( + const AZ::Vector3& inPosition, + SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const = 0; + virtual void GetSurfacePointFromVector2( + const AZ::Vector2& inPosition, + SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const = 0; + virtual void GetSurfacePointFromFloats( + float x, + float y, + SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const = 0; }; using TerrainDataRequestBus = AZ::EBus; @@ -169,6 +165,10 @@ namespace AzFramework } }; using TerrainDataNotificationBus = AZ::EBus; - - } //namespace Terrain + } // namespace Terrain } // namespace AzFramework + +namespace AZ +{ + AZ_TYPE_INFO_SPECIALIZE(AzFramework::Terrain::TerrainDataRequests::Sampler, "{D29BB6D7-3006-4114-858D-355EAA256B86}"); +} // namespace AZ diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index b338dbb0a8..d3196e8517 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -301,6 +301,8 @@ set(FILES Spawnable/SpawnableMonitor.cpp Spawnable/SpawnableSystemComponent.h Spawnable/SpawnableSystemComponent.cpp + SurfaceData/SurfaceData.h + SurfaceData/SurfaceData.cpp Terrain/TerrainDataRequestBus.h Terrain/TerrainDataRequestBus.cpp Thermal/ThermalInfo.h diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksCommon.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksCommon.cpp index 70b5aefe14..49dc249d25 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksCommon.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksCommon.cpp @@ -61,7 +61,6 @@ namespace PhysX::Benchmarks m_defaultScene = physicsSystem->GetScene(m_testSceneHandle); } - m_dummyTerrainComponentDescriptor = DummyTestTerrainComponent::CreateDescriptor(); Physics::DefaultWorldBus::Handler::BusConnect(); } @@ -80,9 +79,6 @@ namespace PhysX::Benchmarks m_testSceneHandle = AzPhysics::InvalidSceneHandle; TestUtils::ResetPhysXSystem(); - - m_dummyTerrainComponentDescriptor->ReleaseDescriptor(); - m_dummyTerrainComponentDescriptor = nullptr; } AzPhysics::SceneHandle PhysXBaseBenchmarkFixture::CreateDefaultTestScene() diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksCommon.h b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksCommon.h index b32298d484..bde4995e27 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksCommon.h +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksCommon.h @@ -61,7 +61,6 @@ namespace PhysX::Benchmarks AzPhysics::Scene* m_defaultScene = nullptr; AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle; - AZ::ComponentDescriptor* m_dummyTerrainComponentDescriptor = nullptr; }; } // namespace PhysX::Benchmarks #endif //HAVE_BENCHMARK diff --git a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp index 63a689894c..fa794c58a5 100644 --- a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp +++ b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp @@ -52,14 +52,10 @@ namespace PhysXEditorTests m_defaultScene = physicsSystem->GetScene(m_defaultSceneHandle); } Physics::DefaultWorldBus::Handler::BusConnect(); - m_dummyTerrainComponentDescriptor = PhysX::DummyTestTerrainComponent::CreateDescriptor(); } void PhysXEditorFixture::TearDown() { - m_dummyTerrainComponentDescriptor->ReleaseDescriptor(); - m_dummyTerrainComponentDescriptor = nullptr; - Physics::DefaultWorldBus::Handler::BusDisconnect(); // prevents warnings from the undo cache on subsequent tests diff --git a/Gems/PhysX/Code/Tests/EditorTestUtilities.h b/Gems/PhysX/Code/Tests/EditorTestUtilities.h index b05a7561fe..9dfe40f366 100644 --- a/Gems/PhysX/Code/Tests/EditorTestUtilities.h +++ b/Gems/PhysX/Code/Tests/EditorTestUtilities.h @@ -51,7 +51,6 @@ namespace PhysXEditorTests // DefaultWorldBus AzPhysics::SceneHandle GetDefaultSceneHandle() const override; - AZ::ComponentDescriptor* m_dummyTerrainComponentDescriptor = nullptr; AzPhysics::SceneHandle m_defaultSceneHandle = AzPhysics::InvalidSceneHandle; AzPhysics::Scene* m_defaultScene = nullptr; diff --git a/Gems/PhysX/Code/Tests/PhysXTestFixtures.cpp b/Gems/PhysX/Code/Tests/PhysXTestFixtures.cpp index b3f233d388..c5fb2d5092 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestFixtures.cpp +++ b/Gems/PhysX/Code/Tests/PhysXTestFixtures.cpp @@ -41,16 +41,12 @@ namespace PhysX } Physics::DefaultWorldBus::Handler::BusConnect(); - - m_dummyTerrainComponentDescriptor = DummyTestTerrainComponent::CreateDescriptor(); } void PhysXDefaultWorldTest::TearDown() { Physics::DefaultWorldBus::Handler::BusDisconnect(); m_defaultScene = nullptr; - m_dummyTerrainComponentDescriptor->ReleaseDescriptor(); - m_dummyTerrainComponentDescriptor = nullptr; //Clean up the Test scene if (auto* physicsSystem = AZ::Interface::Get()) diff --git a/Gems/PhysX/Code/Tests/PhysXTestFixtures.h b/Gems/PhysX/Code/Tests/PhysXTestFixtures.h index 9b00f7d892..6a8325a66f 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestFixtures.h +++ b/Gems/PhysX/Code/Tests/PhysXTestFixtures.h @@ -33,7 +33,6 @@ namespace PhysX // DefaultWorldBus AzPhysics::SceneHandle GetDefaultSceneHandle() const override; - AZ::ComponentDescriptor* m_dummyTerrainComponentDescriptor = nullptr; AzPhysics::Scene* m_defaultScene = nullptr; AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle; }; diff --git a/Gems/PhysX/Code/Tests/PhysXTestUtil.h b/Gems/PhysX/Code/Tests/PhysXTestUtil.h index 1c86768de7..d821fb146b 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestUtil.h +++ b/Gems/PhysX/Code/Tests/PhysXTestUtil.h @@ -63,68 +63,4 @@ namespace PhysX AzPhysics::SimulatedBodyEvents::OnTriggerExit::Handler m_onTriggerExitHandler; }; - - //! Dummy component emulating presence of terrain by connecting to TerrainDataRequestBus - //! PhysX Terrain Component skips activation if there's no terrain present, - //! so in order to test it we also add the DummyTestTerrainComponent. - class DummyTestTerrainComponent - : public AZ::Component - , private AzFramework::Terrain::TerrainDataRequestBus::Handler - { - public: - AZ_COMPONENT(DummyTestTerrainComponent, "{EE4ECA23-9C27-4D5D-9C6F-271A19C0333E}"); - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC_CE("TerrainService")); - } - - private: - //////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - void Activate() override - { - AzFramework::Terrain::TerrainDataRequestBus::Handler::BusConnect(); - } - void Deactivate() override - { - AzFramework::Terrain::TerrainDataRequestBus::Handler::BusDisconnect(); - } - //////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // TerrainDataRequestBus interface dummy implementation - AZ::Vector2 GetTerrainHeightQueryResolution() const override - { - return {}; - } - void SetTerrainHeightQueryResolution([[maybe_unused]] AZ::Vector2 queryResolution) override - { - } - - AZ::Aabb GetTerrainAabb() const override - { - return {}; - } - void SetTerrainAabb([[maybe_unused]] const AZ::Aabb& worldBounds) override - { - } - - float GetHeight(AZ::Vector3, Sampler, bool*) const override - { - return {}; - } - float GetHeightFromFloats(float, float, Sampler, bool*) const override { return {}; } - AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight(AZ::Vector3, Sampler, bool*) const override { return {}; } - AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2(const AZ::Vector2&, Sampler, bool*) const override { return {}; } - AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats(float, float, Sampler, bool*) const override { return {}; } - void GetSurfaceWeights(const AZ::Vector3&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*) const override {} - void GetSurfaceWeightsFromVector2(const AZ::Vector2&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*) const override{}; - void GetSurfaceWeightsFromFloats(float, float, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*) const override {} - const char* GetMaxSurfaceName(AZ::Vector3, Sampler, bool*) const override { return {}; } - bool GetIsHoleFromFloats(float, float, Sampler) const override { return {}; } - AZ::Vector3 GetNormal(AZ::Vector3, Sampler, bool*) const override { return {}; } - AZ::Vector3 GetNormalFromFloats(float, float, Sampler, bool*) const override { return {}; } - //////////////////////////////////////////////////////////////////////// - }; - } // namespace PhysX diff --git a/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h b/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h index 4c04cf1e42..ed352b55df 100644 --- a/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h +++ b/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h @@ -27,8 +27,8 @@ namespace Terrain virtual ~TerrainAreaSurfaceRequests() = default; - //! Get the surfaces and weights from a gradient at a given position sorted into descending order of weight. - virtual void GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const = 0; + //! Get the surfaces and weights from a gradient at a given position. + virtual void GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights) const = 0; }; using TerrainAreaSurfaceRequestBus = AZ::EBus; diff --git a/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h b/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h index bb1fa5683a..53d93be8ea 100644 --- a/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h +++ b/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h @@ -107,17 +107,28 @@ namespace UnitTest MOCK_METHOD1(SetTerrainHeightQueryResolution, void(AZ::Vector2)); MOCK_CONST_METHOD0(GetTerrainAabb, AZ::Aabb()); MOCK_METHOD1(SetTerrainAabb, void(const AZ::Aabb&)); - MOCK_CONST_METHOD3(GetHeight, float(AZ::Vector3, Sampler, bool*)); + MOCK_CONST_METHOD3(GetHeight, float(const AZ::Vector3&, Sampler, bool*)); + MOCK_CONST_METHOD3(GetHeightFromVector2, float(const AZ::Vector2&, Sampler, bool*)); MOCK_CONST_METHOD4(GetHeightFromFloats, float(float, float, Sampler, bool*)); - MOCK_CONST_METHOD3(GetMaxSurfaceWeight, AzFramework::SurfaceData::SurfaceTagWeight(AZ::Vector3, Sampler, bool*)); + MOCK_CONST_METHOD2(GetIsHole, bool(const AZ::Vector3&, Sampler)); + MOCK_CONST_METHOD2(GetIsHoleFromVector2, bool(const AZ::Vector2&, Sampler)); + MOCK_CONST_METHOD3(GetIsHoleFromFloats, bool(float, float, Sampler)); + MOCK_CONST_METHOD3(GetNormal, AZ::Vector3(const AZ::Vector3&, Sampler, bool*)); + MOCK_CONST_METHOD3(GetNormalFromVector2, AZ::Vector3(const AZ::Vector2&, Sampler, bool*)); + MOCK_CONST_METHOD4(GetNormalFromFloats, AZ::Vector3(float, float, Sampler, bool*)); + MOCK_CONST_METHOD3(GetMaxSurfaceWeight, AzFramework::SurfaceData::SurfaceTagWeight(const AZ::Vector3&, Sampler, bool*)); MOCK_CONST_METHOD3(GetMaxSurfaceWeightFromVector2, AzFramework::SurfaceData::SurfaceTagWeight(const AZ::Vector2&, Sampler, bool*)); MOCK_CONST_METHOD4(GetMaxSurfaceWeightFromFloats, AzFramework::SurfaceData::SurfaceTagWeight(float, float, Sampler, bool*)); - MOCK_CONST_METHOD4(GetSurfaceWeights, void(const AZ::Vector3&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*)); - MOCK_CONST_METHOD4(GetSurfaceWeightsFromVector2, void(const AZ::Vector2&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*)); - MOCK_CONST_METHOD5(GetSurfaceWeightsFromFloats, void(float, float, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*)); - MOCK_CONST_METHOD3(GetMaxSurfaceName, const char*(AZ::Vector3, Sampler, bool*)); - MOCK_CONST_METHOD3(GetIsHoleFromFloats, bool(float, float, Sampler)); - MOCK_CONST_METHOD3(GetNormal, AZ::Vector3(AZ::Vector3, Sampler, bool*)); - MOCK_CONST_METHOD4(GetNormalFromFloats, AZ::Vector3(float, float, Sampler, bool*)); + MOCK_CONST_METHOD4(GetSurfaceWeights, void(const AZ::Vector3&, AzFramework::SurfaceData::SurfaceTagWeightList&, Sampler, bool*)); + MOCK_CONST_METHOD4( + GetSurfaceWeightsFromVector2, void(const AZ::Vector2&, AzFramework::SurfaceData::SurfaceTagWeightList&, Sampler, bool*)); + MOCK_CONST_METHOD5( + GetSurfaceWeightsFromFloats, void(float, float, AzFramework::SurfaceData::SurfaceTagWeightList&, Sampler, bool*)); + MOCK_CONST_METHOD3(GetMaxSurfaceName, const char*(const AZ::Vector3&, Sampler, bool*)); + MOCK_CONST_METHOD4(GetSurfacePoint, void(const AZ::Vector3&, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*)); + MOCK_CONST_METHOD4( + GetSurfacePointFromVector2, void(const AZ::Vector2&, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*)); + MOCK_CONST_METHOD5( + GetSurfacePointFromFloats, void(float, float, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*)); }; } // namespace UnitTest diff --git a/Gems/Terrain/Code/Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h b/Gems/Terrain/Code/Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h index c5a04b4265..665bf581bc 100644 --- a/Gems/Terrain/Code/Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h +++ b/Gems/Terrain/Code/Mocks/Terrain/MockTerrainAreaSurfaceRequestBus.h @@ -28,7 +28,7 @@ namespace UnitTest MOCK_METHOD0(Activate, void()); MOCK_METHOD0(Deactivate, void()); - MOCK_CONST_METHOD2(GetSurfaceWeights, void(const AZ::Vector3&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&)); + MOCK_CONST_METHOD2(GetSurfaceWeights, void(const AZ::Vector3&, AzFramework::SurfaceData::SurfaceTagWeightList&)); }; } // namespace UnitTest diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp index 0346ff7281..7395e66d06 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp @@ -149,13 +149,17 @@ namespace Terrain if (terrain->GetTerrainAabb().Contains(inPosition)) { bool isTerrainValidAtPoint = false; - const float terrainHeight = terrain->GetHeight(inPosition, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, &isTerrainValidAtPoint); + AzFramework::SurfaceData::SurfacePoint terrainSurfacePoint; + terrain->GetSurfacePoint( + inPosition, terrainSurfacePoint, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, + &isTerrainValidAtPoint); + const bool isHole = !isTerrainValidAtPoint; SurfaceData::SurfacePoint point; point.m_entityId = GetEntityId(); - point.m_position = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), terrainHeight); - point.m_normal = terrain->GetNormal(inPosition); + point.m_position = terrainSurfacePoint.m_position; + point.m_normal = terrainSurfacePoint.m_normal; // Always add a "terrain" or "terrainHole" tag. const AZ::Crc32 terrainTag = @@ -163,9 +167,7 @@ namespace Terrain SurfaceData::AddMaxValueForMasks(point.m_masks, terrainTag, 1.0f); // Add all of the surface tags that the terrain has at this point. - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet surfaceWeights; - terrain->GetSurfaceWeights(point.m_position, surfaceWeights); - for (auto& tag : surfaceWeights) + for (auto& tag : terrainSurfacePoint.m_surfaceTags) { SurfaceData::AddMaxValueForMasks(point.m_masks, tag.m_surfaceType, tag.m_weight); } diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp index 958e175cf3..5b76f15d74 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp @@ -166,7 +166,7 @@ namespace Terrain void TerrainSurfaceGradientListComponent::GetSurfaceWeights( const AZ::Vector3& inPosition, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights) const { outSurfaceWeights.clear(); @@ -178,10 +178,7 @@ namespace Terrain GradientSignal::GradientRequestBus::EventResult(weight, mapping.m_gradientEntityId, &GradientSignal::GradientRequestBus::Events::GetValue, params); - AzFramework::SurfaceData::SurfaceTagWeight tagWeight; - tagWeight.m_surfaceType = mapping.m_surfaceTag; - tagWeight.m_weight = weight; - outSurfaceWeights.emplace(tagWeight); + outSurfaceWeights.emplace_back(mapping.m_surfaceTag, weight); } } diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h index bb30029f35..20851c127f 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h @@ -72,7 +72,7 @@ namespace Terrain bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; // TerrainAreaSurfaceRequestBus - void GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const override; + void GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights) const override; private: ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 821e0bd2e2..fa1d483a5d 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -241,7 +241,12 @@ float TerrainSystem::GetTerrainAreaHeight(float x, float y, bool& terrainExists) return height; } -float TerrainSystem::GetHeight(AZ::Vector3 position, Sampler sampler, bool* terrainExistsPtr) const +float TerrainSystem::GetHeight(const AZ::Vector3& position, Sampler sampler, bool* terrainExistsPtr) const +{ + return GetHeightSynchronous(position.GetX(), position.GetY(), sampler, terrainExistsPtr); +} + +float TerrainSystem::GetHeightFromVector2(const AZ::Vector2& position, Sampler sampler, bool* terrainExistsPtr) const { return GetHeightSynchronous(position.GetX(), position.GetY(), sampler, terrainExistsPtr); } @@ -251,6 +256,20 @@ float TerrainSystem::GetHeightFromFloats(float x, float y, Sampler sampler, bool return GetHeightSynchronous(x, y, sampler, terrainExistsPtr); } +bool TerrainSystem::GetIsHole(const AZ::Vector3& position, Sampler sampler) const +{ + bool terrainExists = false; + GetHeightSynchronous(position.GetX(), position.GetY(), sampler, &terrainExists); + return !terrainExists; +} + +bool TerrainSystem::GetIsHoleFromVector2(const AZ::Vector2& position, Sampler sampler) const +{ + bool terrainExists = false; + GetHeightSynchronous(position.GetX(), position.GetY(), sampler, &terrainExists); + return !terrainExists; +} + bool TerrainSystem::GetIsHoleFromFloats(float x, float y, Sampler sampler) const { bool terrainExists = false; @@ -287,7 +306,12 @@ AZ::Vector3 TerrainSystem::GetNormalSynchronous(float x, float y, Sampler sample return outNormal; } -AZ::Vector3 TerrainSystem::GetNormal(AZ::Vector3 position, Sampler sampler, bool* terrainExistsPtr) const +AZ::Vector3 TerrainSystem::GetNormal(const AZ::Vector3& position, Sampler sampler, bool* terrainExistsPtr) const +{ + return GetNormalSynchronous(position.GetX(), position.GetY(), sampler, terrainExistsPtr); +} + +AZ::Vector3 TerrainSystem::GetNormalFromVector2(const AZ::Vector2& position, Sampler sampler, bool* terrainExistsPtr) const { return GetNormalSynchronous(position.GetX(), position.GetY(), sampler, terrainExistsPtr); } @@ -299,7 +323,7 @@ AZ::Vector3 TerrainSystem::GetNormalFromFloats(float x, float y, Sampler sampler AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeight( - const AZ::Vector3 position, Sampler sampleFilter, bool* terrainExistsPtr) const + const AZ::Vector3& position, Sampler sampleFilter, bool* terrainExistsPtr) const { return GetMaxSurfaceWeightFromFloats(position.GetX(), position.GetY(), sampleFilter, terrainExistsPtr); } @@ -317,7 +341,7 @@ AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeightFro *terrainExistsPtr = true; } - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet weightSet; + AzFramework::SurfaceData::SurfaceTagWeightList weightSet; GetOrderedSurfaceWeights(x, y, sampleFilter, weightSet, terrainExistsPtr); @@ -329,6 +353,38 @@ AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeightFro return *weightSet.begin(); } +void TerrainSystem::GetSurfacePoint( + const AZ::Vector3& inPosition, + AzFramework::SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter, + bool* terrainExistsPtr) const +{ + outSurfacePoint.m_position = inPosition; + outSurfacePoint.m_position.SetZ(GetHeightSynchronous(inPosition.GetX(), inPosition.GetY(), sampleFilter, terrainExistsPtr)); + outSurfacePoint.m_normal = GetNormalSynchronous(inPosition.GetX(), inPosition.GetY(), sampleFilter, nullptr); + GetSurfaceWeights(inPosition, outSurfacePoint.m_surfaceTags, sampleFilter, nullptr); +} + +void TerrainSystem::GetSurfacePointFromVector2( + const AZ::Vector2& inPosition, + AzFramework::SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter, + bool* terrainExistsPtr) const +{ + GetSurfacePoint(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f), outSurfacePoint, sampleFilter, terrainExistsPtr); +} + +void TerrainSystem::GetSurfacePointFromFloats( + float x, + float y, + AzFramework::SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter, + bool* terrainExistsPtr) const +{ + GetSurfacePoint(AZ::Vector3(x, y, 0.0f), outSurfacePoint, sampleFilter, terrainExistsPtr); +} + + AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::Aabb& bounds) const { AZ::Vector3 inPosition = AZ::Vector3(x, y, 0); @@ -354,7 +410,7 @@ void TerrainSystem::GetOrderedSurfaceWeights( const float x, const float y, [[maybe_unused]] Sampler sampler, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights, bool* terrainExistsPtr) const { AZ::Aabb bounds; @@ -377,54 +433,40 @@ void TerrainSystem::GetOrderedSurfaceWeights( // Get all the surfaces with weights at the given point. Terrain::TerrainAreaSurfaceRequestBus::Event( bestAreaId, &Terrain::TerrainAreaSurfaceRequestBus::Events::GetSurfaceWeights, inPosition, outSurfaceWeights); + + AZStd::sort(outSurfaceWeights.begin(), outSurfaceWeights.end(), AzFramework::SurfaceData::SurfaceTagWeightComparator()); } void TerrainSystem::GetSurfaceWeights( const AZ::Vector3& inPosition, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter, bool* terrainExistsPtr) const { - if (terrainExistsPtr) - { - *terrainExistsPtr = true; - } - GetOrderedSurfaceWeights(inPosition.GetX(), inPosition.GetY(), sampleFilter, outSurfaceWeights, terrainExistsPtr); } void TerrainSystem::GetSurfaceWeightsFromVector2( const AZ::Vector2& inPosition, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter, bool* terrainExistsPtr) const { - // For now, always set terrainExists to true, as we don't have a way to author data for terrain holes yet. - if (terrainExistsPtr) - { - *terrainExistsPtr = true; - } GetOrderedSurfaceWeights(inPosition.GetX(), inPosition.GetY(), sampleFilter, outSurfaceWeights, terrainExistsPtr); } void TerrainSystem::GetSurfaceWeightsFromFloats( - float x, - float y, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + float x, float y, + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter, bool* terrainExistsPtr) const { - // For now, always set terrainExists to true, as we don't have a way to author data for terrain holes yet. - if (terrainExistsPtr) - { - *terrainExistsPtr = true; - } - GetOrderedSurfaceWeights(x, y, sampleFilter, outSurfaceWeights, terrainExistsPtr); } -const char* TerrainSystem::GetMaxSurfaceName([[maybe_unused]] AZ::Vector3 position, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const +const char* TerrainSystem::GetMaxSurfaceName( + [[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const { // For now, always set terrainExists to true, as we don't have a way to author data for terrain holes yet. if (terrainExistsPtr) @@ -436,21 +478,6 @@ const char* TerrainSystem::GetMaxSurfaceName([[maybe_unused]] AZ::Vector3 positi } /* -void TerrainSystem::GetSurfacePoint( - const AZ::Vector3& inPosition, [[maybe_unused]] Sampler sampleFilter, SurfaceData::SurfacePoint& outSurfacePoint) -{ - // TODO: Handle sampleFilter - - float sampleX = inPosition.GetX(); - float sampleY = inPosition.GetY(); - - GetHeight(inPosition, sampleFilter, outSurfacePoint.m_position); - //outSurfacePoint.m_position = AZ::Vector3(sampleX, sampleY, GetHeightSynchronous(sampleX, sampleY)); - outSurfacePoint.m_normal = GetNormalSynchronous(sampleX, sampleY); -} - - - void TerrainSystem::ProcessHeightsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, Sampler sampleFilter, SurfacePointRegionFillCallback perPositionCallback, TerrainDataReadyCallback onComplete) { diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index 66c48850ef..956424f048 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -62,7 +62,8 @@ namespace Terrain //! @terrainExistsPtr: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain //! HOLE then *terrainExistsPtr will become false, //! otherwise *terrainExistsPtr will become true. - float GetHeight(AZ::Vector3 position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + float GetHeight(const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + float GetHeightFromVector2(const AZ::Vector2& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; float GetHeightFromFloats(float x, float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; //! Given an XY coordinate, return the max surface type and weight. @@ -70,7 +71,7 @@ namespace Terrain //! HOLE then *terrainExistsPtr will be set to false, //! otherwise *terrainExistsPtr will be set to true. AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight( - const AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2( const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const override; AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats( @@ -78,18 +79,18 @@ namespace Terrain void GetSurfaceWeights( const AZ::Vector3& inPosition, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const override; void GetSurfaceWeightsFromVector2( const AZ::Vector2& inPosition, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const override; void GetSurfaceWeightsFromFloats( float x, float y, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const override; @@ -97,10 +98,12 @@ namespace Terrain //! GetMaxSurfaceWeight or GetMaxSurfaceWeightFromFloats. Not available in the behavior context. Returns nullptr if the position is //! inside a hole or outside of the terrain boundaries. const char* GetMaxSurfaceName( - AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; //! Returns true if there's a hole at location x,y. //! Also returns true if there's no terrain data at location x,y. + bool GetIsHole(const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR) const override; + bool GetIsHoleFromVector2(const AZ::Vector2& position, Sampler sampleFilter = Sampler::BILINEAR) const override; bool GetIsHoleFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR) const override; // Given an XY coordinate, return the surface normal. @@ -108,10 +111,30 @@ namespace Terrain //! HOLE then *terrainExistsPtr will be set to false, //! otherwise *terrainExistsPtr will be set to true. AZ::Vector3 GetNormal( - AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + AZ::Vector3 GetNormalFromVector2( + const AZ::Vector2& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; AZ::Vector3 GetNormalFromFloats( float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + void GetSurfacePoint( + const AZ::Vector3& inPosition, + AzFramework::SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const override; + void GetSurfacePointFromVector2( + const AZ::Vector2& inPosition, + AzFramework::SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const override; + void GetSurfacePointFromFloats( + float x, + float y, + AzFramework::SurfaceData::SurfacePoint& outSurfacePoint, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const override; + + private: void ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const; @@ -120,11 +143,11 @@ namespace Terrain const float x, const float y, Sampler sampler, - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights, bool* terrainExistsPtr) const; float GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; float GetTerrainAreaHeight(float x, float y, bool& terrainExists) const; - AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; + AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; // AZ::TickBus::Handler overrides ... void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; diff --git a/Gems/Terrain/Code/Tests/TerrainSurfaceGradientListTests.cpp b/Gems/Terrain/Code/Tests/TerrainSurfaceGradientListTests.cpp index 2a645b3f94..dea861bda5 100644 --- a/Gems/Terrain/Code/Tests/TerrainSurfaceGradientListTests.cpp +++ b/Gems/Terrain/Code/Tests/TerrainSurfaceGradientListTests.cpp @@ -91,10 +91,10 @@ namespace UnitTest } }; - TEST_F(TerrainSurfaceGradientListTest, SurfaceGradientReturnsSurfaceWeightsInOrder) + TEST_F(TerrainSurfaceGradientListTest, SurfaceGradientReturnsSurfaceWeights) { - // When there is more that one surface/weight defined and added to the component, they should all - // be returned in descending weight order. + // When there is more than one surface/weight defined and added to the component, they should all + // be returned. The component isn't required to return them in descending order. AddSurfaceGradientListToEntities(); m_entity->Activate(); @@ -109,15 +109,15 @@ namespace UnitTest NiceMock mockGradientRequests2(m_gradientEntity2->GetId()); ON_CALL(mockGradientRequests2, GetValue).WillByDefault(Return(gradient2Value)); - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet weightSet; + AzFramework::SurfaceData::SurfaceTagWeightList weightList; Terrain::TerrainAreaSurfaceRequestBus::Event( - m_entity->GetId(), &Terrain::TerrainAreaSurfaceRequestBus::Events::GetSurfaceWeights, AZ::Vector3::CreateZero(), weightSet); + m_entity->GetId(), &Terrain::TerrainAreaSurfaceRequestBus::Events::GetSurfaceWeights, AZ::Vector3::CreateZero(), weightList); - AZ::Crc32 expectedCrcList[] = { AZ::Crc32(surfaceTag2), AZ::Crc32(surfaceTag1) }; - const float expectedWeightList[] = { gradient2Value, gradient1Value }; + AZ::Crc32 expectedCrcList[] = { AZ::Crc32(surfaceTag1), AZ::Crc32(surfaceTag2) }; + const float expectedWeightList[] = { gradient1Value, gradient2Value }; int index = 0; - for (const auto& surfaceWeight : weightSet) + for (const auto& surfaceWeight : weightList) { EXPECT_EQ(surfaceWeight.m_surfaceType, expectedCrcList[index]); EXPECT_NEAR(surfaceWeight.m_weight, expectedWeightList[index], 0.01f); diff --git a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp index f273a85e26..4d149e8527 100644 --- a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp +++ b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp @@ -475,8 +475,10 @@ namespace UnitTest } } - TEST_F(TerrainSystemTest, GetSurfaceWeightsReturnsAllValidSurfaceWeights) + TEST_F(TerrainSystemTest, GetSurfaceWeightsReturnsAllValidSurfaceWeightsInOrder) { + // When there is more than one surface/weight defined, they should all be returned in descending weight order. + CreateAndActivateTerrainSystem(); const AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne()); @@ -490,32 +492,41 @@ namespace UnitTest const AZ::Crc32 tag1("tag1"); const AZ::Crc32 tag2("tag2"); + const AZ::Crc32 tag3("tag3"); + const float tag1Weight = 0.8f; + const float tag2Weight = 1.0f; + const float tag3Weight = 0.5f; - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet orderedSurfaceWeights; - - AzFramework::SurfaceData::SurfaceTagWeight tagWeight1; - tagWeight1.m_surfaceType = tag1; - tagWeight1.m_weight = 1.0f; - orderedSurfaceWeights.emplace(tagWeight1); - - AzFramework::SurfaceData::SurfaceTagWeight tagWeight2; - tagWeight2.m_surfaceType = tag2; - tagWeight2.m_weight = 0.8f; - orderedSurfaceWeights.emplace(tagWeight2); + AzFramework::SurfaceData::SurfaceTagWeightList orderedSurfaceWeights + { + { tag1, tag1Weight }, { tag2, tag2Weight }, { tag3, tag3Weight } + }; NiceMock mockSurfaceRequests(entity->GetId()); ON_CALL(mockSurfaceRequests, GetSurfaceWeights).WillByDefault(SetArgReferee<1>(orderedSurfaceWeights)); - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet outSurfaceWeights; + AzFramework::SurfaceData::SurfaceTagWeightList outSurfaceWeights; // Asking for values outside the layer spawner bounds, should result in no results. m_terrainSystem->GetSurfaceWeights(aabb.GetMax() + AZ::Vector3::CreateOne(), outSurfaceWeights); EXPECT_TRUE(outSurfaceWeights.empty()); - // Inside the layer spawner box should give us both the added surface weights. + // Inside the layer spawner box should give us all of the added surface weights. m_terrainSystem->GetSurfaceWeights(aabb.GetCenter(), outSurfaceWeights); - EXPECT_EQ(outSurfaceWeights.size(), 2); + EXPECT_EQ(outSurfaceWeights.size(), 3); + + // The weights should be returned in decreasing order. + AZ::Crc32 expectedCrcList[] = { tag2, tag1, tag3 }; + const float expectedWeightList[] = { tag2Weight, tag1Weight, tag3Weight }; + + int index = 0; + for (const auto& surfaceWeight : outSurfaceWeights) + { + EXPECT_EQ(surfaceWeight.m_surfaceType, expectedCrcList[index]); + EXPECT_NEAR(surfaceWeight.m_weight, expectedWeightList[index], 0.01f); + index++; + } } TEST_F(TerrainSystemTest, GetMaxSurfaceWeightsReturnsBiggestValidSurfaceWeight) @@ -534,17 +545,17 @@ namespace UnitTest const AZ::Crc32 tag1("tag1"); const AZ::Crc32 tag2("tag2"); - AzFramework::SurfaceData::OrderedSurfaceTagWeightSet orderedSurfaceWeights; + AzFramework::SurfaceData::SurfaceTagWeightList orderedSurfaceWeights; AzFramework::SurfaceData::SurfaceTagWeight tagWeight1; tagWeight1.m_surfaceType = tag1; tagWeight1.m_weight = 1.0f; - orderedSurfaceWeights.emplace(tagWeight1); + orderedSurfaceWeights.emplace_back(tagWeight1); AzFramework::SurfaceData::SurfaceTagWeight tagWeight2; tagWeight2.m_surfaceType = tag2; tagWeight2.m_weight = 0.8f; - orderedSurfaceWeights.emplace(tagWeight2); + orderedSurfaceWeights.emplace_back(tagWeight2); NiceMock mockSurfaceRequests(entity->GetId()); ON_CALL(mockSurfaceRequests, GetSurfaceWeights).WillByDefault(SetArgReferee<1>(orderedSurfaceWeights)); From 243532c5debc7c65c20b11cb066eee593cbd8485 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Fri, 22 Oct 2021 14:33:36 -0500 Subject: [PATCH 147/152] Addressed feedback from PR 4874. (#4915) * Addressed feedback from PR 4874. * Removed second copy of HeightfieldProviderBus.h from cmake file * Changed CookedMeshShapeConfiguration and HeightfieldShapeConfiguration to have less messy implementations, instead opting for the slightly less messy const_cast inside of Utils.cpp and DebugDraw.cpp. * Changed InitHeightfieldShapeConfiguraiton to CreateHeightfieldShapeConfiguration with a better API signature. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed indentation Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Physics/ShapeConfiguration.cpp | 21 +++++++++--- .../AzFramework/Physics/ShapeConfiguration.h | 14 ++++---- .../AzFramework/azframework_files.cmake | 1 - Gems/PhysX/Code/Editor/DebugDraw.cpp | 6 +++- .../EditorHeightfieldColliderComponent.cpp | 4 +-- .../Source/HeightfieldColliderComponent.cpp | 2 +- Gems/PhysX/Code/Source/Utils.cpp | 32 +++++++++++++------ Gems/PhysX/Code/Source/Utils.h | 2 +- 8 files changed, 54 insertions(+), 28 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp index 9bf00d9707..db8004d83a 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp @@ -286,12 +286,17 @@ namespace Physics return m_type; } - void* CookedMeshShapeConfiguration::GetCachedNativeMesh() const + const void* CookedMeshShapeConfiguration::GetCachedNativeMesh() const { return m_cachedNativeMesh; } - void CookedMeshShapeConfiguration::SetCachedNativeMesh(void* cachedNativeMesh) const + void* CookedMeshShapeConfiguration::GetCachedNativeMesh() + { + return m_cachedNativeMesh; + } + + void CookedMeshShapeConfiguration::SetCachedNativeMesh(void* cachedNativeMesh) { m_cachedNativeMesh = cachedNativeMesh; } @@ -353,12 +358,17 @@ namespace Physics return *this; } - void* HeightfieldShapeConfiguration::GetCachedNativeHeightfield() const + const void* HeightfieldShapeConfiguration::GetCachedNativeHeightfield() const { return m_cachedNativeHeightfield; } - void HeightfieldShapeConfiguration::SetCachedNativeHeightfield(void* cachedNativeHeightfield) const + void* HeightfieldShapeConfiguration::GetCachedNativeHeightfield() + { + return m_cachedNativeHeightfield; + } + + void HeightfieldShapeConfiguration::SetCachedNativeHeightfield(void* cachedNativeHeightfield) { if (m_cachedNativeHeightfield) { @@ -427,4 +437,5 @@ namespace Physics { m_maxHeightBounds = maxBounds; } -} +} // namespace Physics + diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h index 3bcf336af6..bd9d6a6aa7 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h @@ -187,8 +187,9 @@ namespace Physics MeshType GetMeshType() const; - void* GetCachedNativeMesh() const; - void SetCachedNativeMesh(void* cachedNativeMesh) const; + void* GetCachedNativeMesh(); + const void* GetCachedNativeMesh() const; + void SetCachedNativeMesh(void* cachedNativeMesh); private: void ReleaseCachedNativeMesh(); @@ -197,7 +198,7 @@ namespace Physics MeshType m_type = MeshType::TriangleMesh; //! Cached native mesh object (e.g. PxConvexMesh or PxTriangleMesh). This data is not serialized. - mutable void* m_cachedNativeMesh = nullptr; + void* m_cachedNativeMesh = nullptr; }; class HeightfieldShapeConfiguration @@ -217,8 +218,9 @@ namespace Physics return ShapeType::Heightfield; } - void* GetCachedNativeHeightfield() const; - void SetCachedNativeHeightfield(void* cachedNativeHeightfield) const; + const void* GetCachedNativeHeightfield() const; + void* GetCachedNativeHeightfield(); + void SetCachedNativeHeightfield(void* cachedNativeHeightfield); AZ::Vector2 GetGridResolution() const; void SetGridResolution(const AZ::Vector2& gridSpacing); int32_t GetNumColumns() const; @@ -246,6 +248,6 @@ namespace Physics //! The grid of sample points for the heightfield. AZStd::vector m_samples; //! An optional storage pointer for the physics system to cache its native heightfield representation. - mutable void* m_cachedNativeHeightfield{ nullptr }; + void* m_cachedNativeHeightfield{ nullptr }; }; } // namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index d3196e8517..e03d166cfc 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -252,7 +252,6 @@ set(FILES Physics/Shape.h Physics/ShapeConfiguration.h Physics/ShapeConfiguration.cpp - Physics/HeightfieldProviderBus.h Physics/SystemBus.h Physics/ColliderComponentBus.h Physics/RagdollPhysicsBus.h diff --git a/Gems/PhysX/Code/Editor/DebugDraw.cpp b/Gems/PhysX/Code/Editor/DebugDraw.cpp index 734557205a..90a1eb6004 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.cpp +++ b/Gems/PhysX/Code/Editor/DebugDraw.cpp @@ -264,7 +264,11 @@ namespace PhysX case Physics::ShapeType::CookedMesh: { const auto& cookedMeshConfig = static_cast(shapeConfig); - physx::PxBase* meshData = static_cast(cookedMeshConfig.GetCachedNativeMesh()); + const physx::PxBase* constMeshData = static_cast(cookedMeshConfig.GetCachedNativeMesh()); + + // Specifically removing the const from the meshData pointer because the physx APIs expect this pointer to be non-const. + physx::PxBase* meshData = const_cast(constMeshData); + if (meshData) { if (meshData->is()) diff --git a/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp index 96b1bd51f5..0f09258524 100644 --- a/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp @@ -201,9 +201,7 @@ namespace PhysX void EditorHeightfieldColliderComponent::InitHeightfieldShapeConfiguration() { - Physics::HeightfieldShapeConfiguration& configuration = static_cast(*m_shapeConfig); - - Utils::InitHeightfieldShapeConfiguration(GetEntityId(), configuration); + *m_shapeConfig = Utils::CreateHeightfieldShapeConfiguration(GetEntityId()); } void EditorHeightfieldColliderComponent::RefreshHeightfield() diff --git a/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp index 9bc209982d..c1fa09f21f 100644 --- a/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp @@ -139,7 +139,7 @@ namespace PhysX { Physics::HeightfieldShapeConfiguration& configuration = static_cast(*m_shapeConfig.second); - Utils::InitHeightfieldShapeConfiguration(GetEntityId(), configuration); + configuration = Utils::CreateHeightfieldShapeConfiguration(GetEntityId()); } void HeightfieldColliderComponent::RefreshHeightfield() diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index 72e17aff70..b4e1e768c3 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -67,7 +67,7 @@ namespace PhysX } void CreatePxGeometryFromHeightfield( - const Physics::HeightfieldShapeConfiguration& heightfieldConfig, physx::PxGeometryHolder& pxGeometry) + Physics::HeightfieldShapeConfiguration& heightfieldConfig, physx::PxGeometryHolder& pxGeometry) { physx::PxHeightField* heightfield = nullptr; @@ -264,9 +264,14 @@ namespace PhysX } case Physics::ShapeType::CookedMesh: { - const Physics::CookedMeshShapeConfiguration& cookedMeshShapeConfig = + const Physics::CookedMeshShapeConfiguration& constCookedMeshShapeConfig = static_cast(shapeConfiguration); + // We are deliberately removing the const off of the ShapeConfiguration here because we're going to change the cached + // native mesh pointer that gets stored in the configuration. + Physics::CookedMeshShapeConfiguration& cookedMeshShapeConfig = + const_cast(constCookedMeshShapeConfig); + physx::PxBase* nativeMeshObject = nullptr; // Use the cached mesh object if it is there, otherwise create one and save in the shape configuration @@ -303,13 +308,18 @@ namespace PhysX return false; } case Physics::ShapeType::Heightfield: - { - const Physics::HeightfieldShapeConfiguration& heightfieldConfig = - static_cast(shapeConfiguration); + { + const Physics::HeightfieldShapeConfiguration& constHeightfieldConfig = + static_cast(shapeConfiguration); - CreatePxGeometryFromHeightfield(heightfieldConfig, pxGeometry); - break; - } + // We are deliberately removing the const off of the ShapeConfiguration here because we're going to change the cached + // native heightfield pointer that gets stored in the configuration. + Physics::HeightfieldShapeConfiguration& heightfieldConfig = + const_cast(constHeightfieldConfig); + + CreatePxGeometryFromHeightfield(heightfieldConfig, pxGeometry); + break; + } default: AZ_Warning("PhysX Rigid Body", false, "Shape not supported in PhysX. Shape Type: %d", shapeType); return false; @@ -1518,9 +1528,9 @@ namespace PhysX return entityWorldTransformWithoutScale * jointLocalTransformWithoutScale; } - void InitHeightfieldShapeConfiguration(AZ::EntityId entityId, Physics::HeightfieldShapeConfiguration& configuration) + Physics::HeightfieldShapeConfiguration CreateHeightfieldShapeConfiguration(AZ::EntityId entityId) { - configuration = Physics::HeightfieldShapeConfiguration(); + Physics::HeightfieldShapeConfiguration configuration; AZ::Vector2 gridSpacing(1.0f); Physics::HeightfieldProviderRequestsBus::EventResult( @@ -1549,6 +1559,8 @@ namespace PhysX samples, entityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); configuration.SetSamples(samples); + + return configuration; } } // namespace Utils diff --git a/Gems/PhysX/Code/Source/Utils.h b/Gems/PhysX/Code/Source/Utils.h index 54a81bb28d..525db03315 100644 --- a/Gems/PhysX/Code/Source/Utils.h +++ b/Gems/PhysX/Code/Source/Utils.h @@ -188,7 +188,7 @@ namespace PhysX //! Returns defaultValue if the input is infinite or NaN, otherwise returns the input unchanged. const AZ::Vector3& Sanitize(const AZ::Vector3& input, const AZ::Vector3& defaultValue = AZ::Vector3::CreateZero()); - void InitHeightfieldShapeConfiguration(AZ::EntityId entityId, Physics::HeightfieldShapeConfiguration& configuration); + Physics::HeightfieldShapeConfiguration CreateHeightfieldShapeConfiguration(AZ::EntityId entityId); namespace Geometry { From 45926d0dbddc3bb8a294bb407cb07ade47bdd37b Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Fri, 22 Oct 2021 12:37:43 -0700 Subject: [PATCH 148/152] LYN-7539 + LYN-7541 | Focus Mode - Show prefab names and dirty markers instead of instance names in breadcrumbs (#4850) * Change Prefab Focus breadcrumb widget to display template filename instead of instance container entity name. Also display dirty state for the template (*) and refresh it in real time. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Streamline path creation code; fix stem retrieval to ensure extension is cut correctly; delay refresh one frame when path is clicked to correctly refresh it. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Remove test code. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Simplify code to use Native directly. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Minor variable renaming and comment adjustments to make them clearer. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabFocusHandler.cpp | 57 ++++++++++++++++--- .../Prefab/PrefabFocusHandler.h | 5 +- .../Prefab/PrefabPublicNotificationBus.h | 4 ++ .../Prefab/PrefabSystemComponent.cpp | 11 ++-- .../Prefab/PrefabViewportFocusPathHandler.cpp | 5 ++ 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 8bc258fef9..5ba7382831 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace AzToolsFramework::Prefab { @@ -79,7 +80,7 @@ namespace AzToolsFramework::Prefab auto editUndo = aznew PrefabFocusUndo("Edit Prefab"); editUndo->Capture(entityId); editUndo->SetParent(undoBatch.GetUndoBatch()); - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, editUndo); + FocusOnPrefabInstanceOwningEntityId(entityId); } return AZ::Success(); @@ -94,9 +95,7 @@ namespace AzToolsFramework::Prefab InstanceOptionalReference focusedInstance = m_instanceFocusHierarchy[index]; - FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId()); - - return AZ::Success(); + return FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId()); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) @@ -255,7 +254,7 @@ namespace AzToolsFramework::Prefab void PrefabFocusHandler::OnEntityInfoUpdatedName(AZ::EntityId entityId, [[maybe_unused]]const AZStd::string& name) { - // Determine if the entityId is the container for any of the instances in the vector + // Determine if the entityId is the container for any of the instances in the vector. auto result = AZStd::find_if( m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(), [entityId](const InstanceOptionalReference& instance) @@ -279,6 +278,25 @@ namespace AzToolsFramework::Prefab PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); } + void PrefabFocusHandler::OnPrefabTemplateDirtyFlagUpdated(TemplateId templateId, [[maybe_unused]] bool status) + { + // Determine if the templateId matches any of the instances in the vector. + auto result = AZStd::find_if( + m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(), + [templateId](const InstanceOptionalReference& instance) + { + return (instance->get().GetTemplateId() == templateId); + } + ); + + if (result != m_instanceFocusHierarchy.end()) + { + // Refresh the path and notify changes. + RefreshInstanceFocusPath(); + PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); + } + } + void PrefabFocusHandler::RefreshInstanceFocusList() { m_instanceFocusHierarchy.clear(); @@ -293,17 +311,42 @@ namespace AzToolsFramework::Prefab currentInstance = currentInstance->get().GetParentInstance(); } - // Invert the vector, since we need the top instance to be at index 0 + // Invert the vector, since we need the top instance to be at index 0. AZStd::reverse(m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end()); } void PrefabFocusHandler::RefreshInstanceFocusPath() { + auto prefabSystemComponentInterface = AZ::Interface::Get(); + m_instanceFocusPath.clear(); + size_t index = 0; + size_t maxIndex = m_instanceFocusHierarchy.size() - 1; + for (const InstanceOptionalReference& instance : m_instanceFocusHierarchy) { - m_instanceFocusPath.Append(instance->get().GetContainerEntity()->get().GetName()); + AZStd::string prefabName; + + if (index < maxIndex) + { + // Get the filename without the extension (stem). + prefabName = instance->get().GetTemplateSourcePath().Stem().Native(); + } + else + { + // Get the full filename. + prefabName = instance->get().GetTemplateSourcePath().Filename().Native(); + } + + if (prefabSystemComponentInterface->IsTemplateDirty(instance->get().GetTemplateId())) + { + prefabName += "*"; + } + + m_instanceFocusPath.Append(prefabName); + + ++index; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index cb8165e7f0..9decaed1ec 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -64,8 +64,9 @@ namespace AzToolsFramework::Prefab void OnEntityInfoUpdatedName(AZ::EntityId entityId, const AZStd::string& name) override; // PrefabPublicNotifications overrides ... - void OnPrefabInstancePropagationEnd(); - + void OnPrefabInstancePropagationEnd() override; + void OnPrefabTemplateDirtyFlagUpdated(TemplateId templateId, bool status) override; + private: PrefabFocusOperationResult FocusOnPrefabInstance(InstanceOptionalReference focusedInstance); void RefreshInstanceFocusList(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicNotificationBus.h index 65725b04de..8b0d446f51 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicNotificationBus.h @@ -9,6 +9,7 @@ #pragma once #include +#include namespace AzToolsFramework { @@ -22,6 +23,9 @@ namespace AzToolsFramework virtual void OnPrefabInstancePropagationBegin() {} virtual void OnPrefabInstancePropagationEnd() {} + + virtual void OnPrefabTemplateDirtyFlagUpdated( + [[maybe_unused]] TemplateId templateId, [[maybe_unused]] bool status) {} }; using PrefabPublicNotificationBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index b1fdf9784d..c930c66786 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -185,7 +185,7 @@ namespace AzToolsFramework if (AZ::JsonSerialization::Compare(templateDomToUpdate, updatedDom) != AZ::JsonSerializerCompareResult::Equal) { templateDomToUpdate.CopyFrom(updatedDom, templateDomToUpdate.GetAllocator()); - templateToUpdate->get().MarkAsDirty(true); + SetTemplateDirtyFlag(templateId, true); PropagateTemplateChanges(templateId); } } @@ -813,11 +813,12 @@ namespace AzToolsFramework void PrefabSystemComponent::SetTemplateDirtyFlag(TemplateId templateId, bool dirty) { - auto templateRef = FindTemplate(templateId); - - if (templateRef.has_value()) + if (auto templateReference = FindTemplate(templateId); templateReference.has_value()) { - templateRef->get().MarkAsDirty(dirty); + templateReference->get().MarkAsDirty(dirty); + + PrefabPublicNotificationBus::Broadcast( + &PrefabPublicNotificationBus::Events::OnPrefabTemplateDirtyFlagUpdated, templateId, dirty); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index 21ada94184..52cf3279a4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -10,6 +10,8 @@ #include +#include + namespace AzToolsFramework::Prefab { PrefabViewportFocusPathHandler::PrefabViewportFocusPathHandler() @@ -47,6 +49,9 @@ namespace AzToolsFramework::Prefab [&](const QString&, int linkIndex) { m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex); + + // Manually refresh path + QTimer::singleShot(0, [&]() { OnPrefabFocusChanged(); }); } ); From a4e0d69e8326ec101417db020b49745c48a0ce5a Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Fri, 22 Oct 2021 12:46:06 -0700 Subject: [PATCH 149/152] Update Splashscreen and About Us dialogs for General Availability (#4901) * Add new image for splashscreen. Layout changes incoming. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Adapt layouts to new splashscreen style with transparent background. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Ensure cropping logic works correctly when screen scaling is used. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Remove old image. Replace new image with new version with more readable credits. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- Code/Editor/AboutDialog.cpp | 14 ++++---- Code/Editor/AboutDialog.h | 6 ++-- Code/Editor/AboutDialog.ui | 12 +++---- Code/Editor/StartupLogoDialog.cpp | 5 +-- Code/Editor/StartupLogoDialog.h | 4 +-- Code/Editor/StartupLogoDialog.qrc | 2 +- Code/Editor/StartupLogoDialog.ui | 35 ++++++++++++++----- .../splashscreen_background_2021_11.jpg | 3 ++ ...ashscreen_background_developer_preview.jpg | 3 -- .../Utilities/PixmapScaleUtilities.cpp | 17 +++++++++ .../Utilities/PixmapScaleUtilities.h | 2 ++ 11 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 Code/Editor/splashscreen_background_2021_11.jpg delete mode 100644 Code/Editor/splashscreen_background_developer_preview.jpg diff --git a/Code/Editor/AboutDialog.cpp b/Code/Editor/AboutDialog.cpp index 2c76526731..c0fd39e1ae 100644 --- a/Code/Editor/AboutDialog.cpp +++ b/Code/Editor/AboutDialog.cpp @@ -6,10 +6,7 @@ * */ - - #include "EditorDefs.h" - #include "AboutDialog.h" // Qt @@ -47,14 +44,17 @@ CAboutDialog::CAboutDialog(QString versionText, QString richTextCopyrightNotice, CAboutDialog > QLabel#link { text-decoration: underline; color: #94D2FF; }"); // Prepare background image - m_backgroundImage = AzQtComponents::ScalePixmapForScreenDpi( - QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_developer_preview.jpg")), - screen(), - QSize(m_enforcedWidth, m_enforcedHeight), + QPixmap image = AzQtComponents::ScalePixmapForScreenDpi( + QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_2021_11.jpg")), + screen(), QSize(m_imageWidth, m_imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); + // Crop image to cut out transparent border + QRect cropRect((m_imageWidth - m_enforcedWidth) / 2, (m_imageHeight - m_enforcedHeight) / 2, m_enforcedWidth, m_enforcedHeight); + m_backgroundImage = AzQtComponents::CropPixmapForScreenDpi(image, screen(), cropRect); + // Draw the Open 3D Engine logo from svg m_ui->m_logo->load(QStringLiteral(":/StartupLogoDialog/o3de_logo.svg")); diff --git a/Code/Editor/AboutDialog.h b/Code/Editor/AboutDialog.h index 229675901e..db079a6ec7 100644 --- a/Code/Editor/AboutDialog.h +++ b/Code/Editor/AboutDialog.h @@ -38,7 +38,9 @@ private: QScopedPointer m_ui; QPixmap m_backgroundImage; - int m_enforcedWidth = 600; - int m_enforcedHeight = 400; + const int m_imageWidth = 668; + const int m_imageHeight = 368; + const int m_enforcedWidth = 600; + const int m_enforcedHeight = 300; }; diff --git a/Code/Editor/AboutDialog.ui b/Code/Editor/AboutDialog.ui index 9341a00d48..a6c5bb5d52 100644 --- a/Code/Editor/AboutDialog.ui +++ b/Code/Editor/AboutDialog.ui @@ -7,7 +7,7 @@ 0 0 600 - 360 + 300 @@ -19,13 +19,13 @@ 600 - 360 + 300 - 600 - 360 + 608 + 300 @@ -69,7 +69,7 @@ 11 - 12 + 10 12 @@ -125,7 +125,7 @@ - Developer Preview + General Availability Qt::AutoText diff --git a/Code/Editor/StartupLogoDialog.cpp b/Code/Editor/StartupLogoDialog.cpp index ab251b1564..135c76cf7f 100644 --- a/Code/Editor/StartupLogoDialog.cpp +++ b/Code/Editor/StartupLogoDialog.cpp @@ -34,11 +34,12 @@ CStartupLogoDialog::CStartupLogoDialog(QString versionText, QString richTextCopy m_ui->setupUi(this); s_pLogoWindow = this; - setFixedSize(QSize(600, 300)); + setFixedSize(QSize(m_enforcedWidth, m_enforcedHeight)); + setAttribute(Qt::WA_TranslucentBackground, true); // Prepare background image m_backgroundImage = AzQtComponents::ScalePixmapForScreenDpi( - QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_developer_preview.jpg")), + QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_2021_11.jpg")), screen(), QSize(m_enforcedWidth, m_enforcedHeight), Qt::IgnoreAspectRatio, diff --git a/Code/Editor/StartupLogoDialog.h b/Code/Editor/StartupLogoDialog.h index af80ab7e90..072ad6a905 100644 --- a/Code/Editor/StartupLogoDialog.h +++ b/Code/Editor/StartupLogoDialog.h @@ -50,7 +50,7 @@ private: QScopedPointer m_ui; QPixmap m_backgroundImage; - const int m_enforcedWidth = 600; - const int m_enforcedHeight = 300; + const int m_enforcedWidth = 668; + const int m_enforcedHeight = 368; }; diff --git a/Code/Editor/StartupLogoDialog.qrc b/Code/Editor/StartupLogoDialog.qrc index 38d1d1da2a..2d7dd81e87 100644 --- a/Code/Editor/StartupLogoDialog.qrc +++ b/Code/Editor/StartupLogoDialog.qrc @@ -1,6 +1,6 @@ o3de_logo.svg - splashscreen_background_developer_preview.jpg + splashscreen_background_2021_11.jpg diff --git a/Code/Editor/StartupLogoDialog.ui b/Code/Editor/StartupLogoDialog.ui index 60969f7f7f..c0b8115cb0 100644 --- a/Code/Editor/StartupLogoDialog.ui +++ b/Code/Editor/StartupLogoDialog.ui @@ -6,13 +6,22 @@ 0 0 - 600 - 300 + 668 + 368 + + 50 + + + 42 + + + 42 + - 9 + 42 10 @@ -29,7 +38,7 @@ - 250 + 300 20 @@ -53,7 +62,7 @@ 1 - 28 + 20 24 @@ -94,7 +103,7 @@ - Developer Preview + General Availability @@ -153,20 +162,28 @@ 290 - 0 + 32 290 - 16777215 + 32 + + + 8 + + Starting Editor... + + Qt::PlainText + - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop diff --git a/Code/Editor/splashscreen_background_2021_11.jpg b/Code/Editor/splashscreen_background_2021_11.jpg new file mode 100644 index 0000000000..703884fc53 --- /dev/null +++ b/Code/Editor/splashscreen_background_2021_11.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffcb7614bed0790bf58a2bb7b2d70958289cb2edf562acc8fc841f4c50a55445 +size 2974586 diff --git a/Code/Editor/splashscreen_background_developer_preview.jpg b/Code/Editor/splashscreen_background_developer_preview.jpg deleted file mode 100644 index 59f05a64df..0000000000 --- a/Code/Editor/splashscreen_background_developer_preview.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7105ec99477f124a8ac8d588f2dfc4ee7bb54f39386c8131b7703c86754c0cb8 -size 248690 diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/PixmapScaleUtilities.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Utilities/PixmapScaleUtilities.cpp index fa32b708d7..95c0289f16 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/PixmapScaleUtilities.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Utilities/PixmapScaleUtilities.cpp @@ -28,4 +28,21 @@ namespace AzQtComponents return scaledPixmap; } + + QPixmap CropPixmapForScreenDpi( + QPixmap pixmap, QScreen* screen, QRect rect) + { + qreal screenDpiFactor = QHighDpiScaling::factor(screen); + pixmap.setDevicePixelRatio(screenDpiFactor); + + QRect cropRect( + aznumeric_cast(aznumeric_cast(rect.left()) * screenDpiFactor), + aznumeric_cast(aznumeric_cast(rect.top()) * screenDpiFactor), + aznumeric_cast(aznumeric_cast(rect.width()) * screenDpiFactor), + aznumeric_cast(aznumeric_cast(rect.height()) * screenDpiFactor) + ); + + QPixmap croppedPixmap = pixmap.copy(cropRect); + return croppedPixmap; + } } diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/PixmapScaleUtilities.h b/Code/Framework/AzQtComponents/AzQtComponents/Utilities/PixmapScaleUtilities.h index 4b083855d5..4462e3c9a7 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/PixmapScaleUtilities.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Utilities/PixmapScaleUtilities.h @@ -11,9 +11,11 @@ #include #include +#include #include namespace AzQtComponents { AZ_QT_COMPONENTS_API QPixmap ScalePixmapForScreenDpi(QPixmap pixmap, QScreen* screen, QSize size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode); + AZ_QT_COMPONENTS_API QPixmap CropPixmapForScreenDpi(QPixmap pixmap, QScreen* screen, QRect rect); }; // namespace AzQtComponents From b609dbbbbbb654397a315dc8e28e38703f9853ac Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Fri, 22 Oct 2021 14:07:32 -0700 Subject: [PATCH 150/152] Pass a callback instead of weak linking a module Signed-off-by: AMZN-Phil --- .../ProjectManager/Source/PythonBindings.cpp | 60 ++++--------------- .../ProjectManager/Source/PythonBindings.h | 2 + scripts/o3de/o3de/download.py | 30 ++++++---- scripts/o3de/o3de/utils.py | 20 +++---- 4 files changed, 39 insertions(+), 73 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 0e24d46815..269ddee445 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -223,49 +223,6 @@ namespace RedirectOutput } } // namespace RedirectOutput -namespace O3DEProjectManagerPy -{ - using DownloadProgressFunc = AZStd::function; - DownloadProgressFunc currentProgressCallback; - bool requestCancelDownload = false; - - static PyObject* CLICancelDownload(PyObject* /*self*/, PyObject* /*args*/) - { - if (requestCancelDownload) - { - return Py_True; - } - else - { - return Py_False; - } - } - - static PyObject* CLIDownloadProgress(PyObject* /*self*/, PyObject* args) - { - int progress_percentage = 0; - if (!PyArg_ParseTuple(args, "i", &progress_percentage)) - return NULL; - if (currentProgressCallback) - { - currentProgressCallback(progress_percentage); - } - - Py_RETURN_NONE; - } - - static PyMethodDef O3DEPMPyMethods[] = { { "download_progress", CLIDownloadProgress, METH_VARARGS, "Used to call back to the UI to inform of download progress." }, - { "request_cancel_download", CLICancelDownload, METH_NOARGS, "Returns that the UI is requesting that the current download be cancelled." }, - { NULL, NULL, 0, NULL } }; - - static PyModuleDef O3DEPMPyModule = { PyModuleDef_HEAD_INIT, "o3de_projectmanager", NULL, -1, O3DEPMPyMethods, NULL, NULL, NULL, NULL }; - - static PyObject* PyInit_O3DEPMPy(void) - { - return PyModule_Create(&O3DEPMPyModule); - } -} // namespace O3DEProjectManagerPy - namespace O3DE::ProjectManager { @@ -314,7 +271,6 @@ namespace O3DE::ProjectManager AZ_TracePrintf("python", "Py_GetProgramFullPath=%ls \n", Py_GetProgramFullPath()); PyImport_AppendInittab("azlmbr_redirect", RedirectOutput::PyInit_RedirectOutput); - PyImport_AppendInittab("o3de_projectmanager", &O3DEProjectManagerPy::PyInit_O3DEPMPy); try { @@ -1126,20 +1082,26 @@ namespace O3DE::ProjectManager { // This process is currently limited to download a single gem at a time. bool downloadSucceeded = false; - O3DEProjectManagerPy::currentProgressCallback = gemProgressCallback; - O3DEProjectManagerPy::requestCancelDownload = false; + + m_requestCancelDownload = false; auto result = ExecuteWithLockErrorHandling( [&] { auto downloadResult = m_download.attr("download_gem")( QString_To_Py_String(gemName), // gem name pybind11::none(), // destination path - false// skip auto register + false, // skip auto register + pybind11::cpp_function( + [this, gemProgressCallback](int progress) + { + gemProgressCallback(progress); + + return m_requestCancelDownload; + }) // Callback for download progress and cancelling ); downloadSucceeded = (downloadResult.cast() == 0); }); - O3DEProjectManagerPy::currentProgressCallback = nullptr; if (!result.IsSuccess()) { @@ -1155,6 +1117,6 @@ namespace O3DE::ProjectManager void PythonBindings::CancelDownload() { - O3DEProjectManagerPy::requestCancelDownload = true; + m_requestCancelDownload = true; } } diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 386268b5b2..73970021a6 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -91,5 +91,7 @@ namespace O3DE::ProjectManager pybind11::handle m_editProjectProperties; pybind11::handle m_download; pybind11::handle m_pathlib; + + bool m_requestCancelDownload = false; }; } diff --git a/scripts/o3de/o3de/download.py b/scripts/o3de/o3de/download.py index f8868dd460..98f5d051a4 100644 --- a/scripts/o3de/o3de/download.py +++ b/scripts/o3de/o3de/download.py @@ -90,7 +90,8 @@ def get_downloadable(engine_name: str = None, def download_o3de_object(object_name: str, default_folder_name: str, dest_path: str or pathlib.Path, - object_type: str, downloadable_kwarg_key, skip_auto_register: bool) -> int: + object_type: str, downloadable_kwarg_key, skip_auto_register: bool, + download_progress_callback = None) -> int: download_path = manifest.get_o3de_cache_folder() / default_folder_name / object_name download_path.mkdir(parents=True, exist_ok=True) @@ -104,7 +105,7 @@ def download_o3de_object(object_name: str, default_folder_name: str, dest_path: origin_uri = downloadable_object_data['originuri'] parsed_uri = urllib.parse.urlparse(origin_uri) - download_zip_result = utils.download_zip_file(parsed_uri, download_zip_path) + download_zip_result = utils.download_zip_file(parsed_uri, download_zip_path, download_progress_callback) if download_zip_result != 0: return download_zip_result @@ -147,33 +148,38 @@ def download_o3de_object(object_name: str, default_folder_name: str, dest_path: def download_engine(engine_name: str, dest_path: str or pathlib.Path, - skip_auto_register: bool) -> int: - return download_o3de_object(engine_name, 'engines', dest_path, 'engine', 'engine_name', skip_auto_register) + skip_auto_register: bool, + download_progress_callback = None) -> int: + return download_o3de_object(engine_name, 'engines', dest_path, 'engine', 'engine_name', skip_auto_register, download_progress_callback) def download_project(project_name: str, dest_path: str or pathlib.Path, - skip_auto_register: bool) -> int: - return download_o3de_object(project_name, 'projects', dest_path, 'project', 'project_name', skip_auto_register) + skip_auto_register: bool, + download_progress_callback = None) -> int: + return download_o3de_object(project_name, 'projects', dest_path, 'project', 'project_name', skip_auto_register, download_progress_callback) def download_gem(gem_name: str, dest_path: str or pathlib.Path, - skip_auto_register: bool) -> int: - return download_o3de_object(gem_name, 'gems', dest_path, 'gem', 'gem_name', skip_auto_register) + skip_auto_register: bool, + download_progress_callback = None) -> int: + return download_o3de_object(gem_name, 'gems', dest_path, 'gem', 'gem_name', skip_auto_register, download_progress_callback) def download_template(template_name: str, dest_path: str or pathlib.Path, - skip_auto_register: bool) -> int: - return download_o3de_object(template_name, 'templates', dest_path, 'template', 'template_name', skip_auto_register) + skip_auto_register: bool, + download_progress_callback = None) -> int: + return download_o3de_object(template_name, 'templates', dest_path, 'template', 'template_name', skip_auto_register, download_progress_callback) def download_restricted(restricted_name: str, dest_path: str or pathlib.Path, - skip_auto_register: bool) -> int: - return download_o3de_object(restricted_name, 'restricted', dest_path, 'restricted', 'restricted_name', skip_auto_register) + skip_auto_register: bool, + download_progress_callback = None) -> int: + return download_o3de_object(restricted_name, 'restricted', dest_path, 'restricted', 'restricted_name', skip_auto_register, download_progress_callback) def _run_download(args: argparse) -> int: diff --git a/scripts/o3de/o3de/utils.py b/scripts/o3de/o3de/utils.py index cce51bbd8f..c07793fd1b 100755 --- a/scripts/o3de/o3de/utils.py +++ b/scripts/o3de/o3de/utils.py @@ -16,10 +16,6 @@ import shutil import urllib.request import logging import zipfile -try: - import o3de_projectmanager -except ImportError: - pass logger = logging.getLogger() logging.basicConfig() @@ -38,14 +34,13 @@ def copyfileobj(fsrc, fdst, callback, length=0): copied = 0 while True: - if o3de_projectmanager and o3de_projectmanager.request_cancel_download(): - return 1 buf = fsrc_read(length) if not buf: break fdst_write(buf) copied += len(buf) - callback(copied) + if callback(copied): + return 1 return 0 def validate_identifier(identifier: str) -> bool: @@ -122,11 +117,12 @@ def backup_folder(folder: str or pathlib.Path) -> None: if backup_folder_name.is_dir(): renamed = True -def download_file(parsed_uri, download_path: pathlib.Path) -> int: +def download_file(parsed_uri, download_path: pathlib.Path, download_progress_callback = None) -> int: """ :param parsed_uri: uniform resource identifier to zip file to download :param download_path: location path on disk to download file """ + logger.warn(f'File about to downloaded to {download_path}.') if download_path.is_file(): logger.warn(f'File already downloaded to {download_path}.') elif parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: @@ -137,8 +133,8 @@ def download_file(parsed_uri, download_path: pathlib.Path) -> int: except KeyError: pass def download_progress(blocks): - if o3de_projectmanager and download_file_size: - o3de_projectmanager.download_progress(int(blocks/int(download_file_size) * 100)) + if download_progress_callback and download_file_size: + return download_progress_callback(int(blocks/int(download_file_size) * 100)) with download_path.open('wb') as f: download_cancelled = copyfileobj(s, f, download_progress) if download_cancelled: @@ -152,12 +148,12 @@ def download_file(parsed_uri, download_path: pathlib.Path) -> int: return 0 -def download_zip_file(parsed_uri, download_zip_path: pathlib.Path) -> int: +def download_zip_file(parsed_uri, download_zip_path: pathlib.Path, download_progress_callback = None) -> int: """ :param parsed_uri: uniform resource identifier to zip file to download :param download_zip_path: path to output zip file """ - download_file_result = download_file(parsed_uri, download_zip_path) + download_file_result = download_file(parsed_uri, download_zip_path, download_progress_callback) if download_file_result != 0: return download_file_result From e1b46b7e5c08bb934c82daf8ec909e8389b923bf Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Fri, 22 Oct 2021 14:23:55 -0700 Subject: [PATCH 151/152] Add comment about additional callback function and be explicit on return value. Signed-off-by: AMZN-Phil --- scripts/o3de/o3de/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/o3de/o3de/utils.py b/scripts/o3de/o3de/utils.py index c07793fd1b..8663502a3f 100755 --- a/scripts/o3de/o3de/utils.py +++ b/scripts/o3de/o3de/utils.py @@ -121,8 +121,8 @@ def download_file(parsed_uri, download_path: pathlib.Path, download_progress_cal """ :param parsed_uri: uniform resource identifier to zip file to download :param download_path: location path on disk to download file + :download_progress_callback: callback called with the download progress as a percentage, returns true to request to cancel the download """ - logger.warn(f'File about to downloaded to {download_path}.') if download_path.is_file(): logger.warn(f'File already downloaded to {download_path}.') elif parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: @@ -135,6 +135,7 @@ def download_file(parsed_uri, download_path: pathlib.Path, download_progress_cal def download_progress(blocks): if download_progress_callback and download_file_size: return download_progress_callback(int(blocks/int(download_file_size) * 100)) + return False with download_path.open('wb') as f: download_cancelled = copyfileobj(s, f, download_progress) if download_cancelled: From 45701a997295fb4514fd74dbea28fb4e6de1ad47 Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Fri, 22 Oct 2021 14:48:15 -0700 Subject: [PATCH 152/152] buildfix: adding .aszl to shader references Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- .../Shaders/PostProcessing/NewDepthOfFieldComposite.shader | 2 +- .../Shaders/PostProcessing/NewDepthOfFieldDownsample.shader | 2 +- .../Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader | 2 +- .../Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader | 2 +- .../Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader | 2 +- .../Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.shader index f17837b330..42130d9ba8 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldComposite.shader @@ -1,5 +1,5 @@ { - "Source" : "NewDepthOfFieldComposite", + "Source" : "NewDepthOfFieldComposite.azsl", "DepthStencilState" : { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.shader index 00aa89e6d8..8a9fe62fd7 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldDownsample.shader @@ -1,5 +1,5 @@ { - "Source" : "NewDepthOfFieldDownsample", + "Source" : "NewDepthOfFieldDownsample.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader index 8f6faedb76..f11ac01e5c 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterLarge.shader @@ -1,5 +1,5 @@ { - "Source" : "NewDepthOfFieldFilterLarge", + "Source" : "NewDepthOfFieldFilterLarge.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader index d05e09be8f..17387a4390 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldFilterSmall.shader @@ -1,5 +1,5 @@ { - "Source" : "NewDepthOfFieldFilterSmall", + "Source" : "NewDepthOfFieldFilterSmall.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader index f7d9cc160e..6ee990c5a7 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTile3x3.shader @@ -1,5 +1,5 @@ { - "Source" : "NewDepthOfFieldTile3x3", + "Source" : "NewDepthOfFieldTile3x3.azsl", "DepthStencilState" : { "Depth" : { "Enable" : false } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader index f2da5305e5..06313cdbf2 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/NewDepthOfFieldTileReduce.shader @@ -1,5 +1,5 @@ { - "Source" : "NewDepthOfFieldTileReduce", + "Source" : "NewDepthOfFieldTileReduce.azsl", "ProgramSettings" : {