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 900a57746a..507652b9fa 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 @@ -131,15 +131,17 @@ namespace AZ PropertyGroup() = default; AZ_DISABLE_COPY(PropertyGroup) - const AZStd::string& GetName() const { return m_name; } - const AZStd::string& GetDisplayName() const { return m_displayName; } - const AZStd::string& GetDescription() const { return m_description; } - const PropertyList& GetProperties() const { return m_properties; } - const AZStd::vector>& GetPropertyGroups() const { return m_propertyGroups; } - const AZStd::vector>& GetFunctors() const { return m_materialFunctorSourceData; } + const AZStd::string& GetName() const; + const AZStd::string& GetDisplayName() const; + const AZStd::string& GetDescription() const; + const PropertyList& GetProperties() const; + const AZStd::string& GetShaderInputsPrefix() const; + const AZStd::string& GetShaderOptionsPrefix() const; + const AZStd::vector>& GetPropertyGroups() const; + const AZStd::vector>& GetFunctors() const; - void SetDisplayName(AZStd::string_view displayName) { m_displayName = displayName; } - void SetDescription(AZStd::string_view description) { m_description = description; } + void SetDisplayName(AZStd::string_view displayName); + void SetDescription(AZStd::string_view description); //! Add a new property to this PropertyGroup. //! @param name a unique for the property. Must be a C-style identifier. @@ -281,13 +283,13 @@ namespace AZ //! Splits an ID string like "itemA.itemB.itemC" into a vector like ["itemA.itemB", "itemC"]. static AZStd::vector SplitId(AZStd::string_view id); + //! Describes a path in the hierarchy of property groups, with the top level group at the beginning and a leaf-most group at the end. + using PropertyGroupStack = AZStd::vector; + //! Call back function type used with the enumeration functions. + //! The PropertyGroupStack contains the stack of property groups at the current point in the traversal. //! Return false to terminate the traversal. - using EnumeratePropertyGroupsCallback = AZStd::function; + using EnumeratePropertyGroupsCallback = AZStd::function; //! Recursively traverses all of the property groups contained in the material type, executing a callback function for each. //! @return false if the enumeration was terminated early by the callback returning false. @@ -304,6 +306,9 @@ namespace AZ //! @return false if the enumeration was terminated early by the callback returning false. bool EnumerateProperties(const EnumeratePropertiesCallback& callback) const; + //! Returns a MaterialNameContext for a specific path through the property group hierarchy. + static MaterialNameContext MakeMaterialNameContext(const MaterialTypeSourceData::PropertyGroupStack& propertyGroupStack); + Outcome> CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath = "", bool elevateWarnings = true) const; //! If the data was loaded from an old format file (i.e. where "groups" and "properties" were separate sections), @@ -319,10 +324,10 @@ namespace AZ PropertyDefinition* FindProperty(AZStd::span parsedPropertyId, AZStd::span> inPropertyGroupList); // Function overloads for recursion, returns false to indicate that recursion should end. - bool EnumeratePropertyGroups(const EnumeratePropertyGroupsCallback& callback, MaterialNameContext nameContext, const AZStd::vector>& inPropertyGroupList) const; + bool EnumeratePropertyGroups(const EnumeratePropertyGroupsCallback& callback, PropertyGroupStack* propertyGroupStack, const AZStd::vector>& inPropertyGroupList) const; bool EnumerateProperties(const EnumeratePropertiesCallback& callback, MaterialNameContext nameContext, const AZStd::vector>& inPropertyGroupList) const; - static MaterialNameContext ExtendNameContext(MaterialNameContext nameContext, const MaterialTypeSourceData::PropertyGroup& propertyGroup); + static void ExtendNameContext(MaterialNameContext& nameContext, const MaterialTypeSourceData::PropertyGroup& propertyGroup); //! Recursively populates a material type asset with properties from the tree of material property groups. //! @param materialTypeSourceFilePath path to the material type file that is being processed, used to look up relative paths @@ -342,7 +347,7 @@ namespace AZ PropertyLayout m_propertyLayout; }; - + //! The wrapper class for derived material functors. //! It is used in deserialization so that derived material functors can be deserialized by name. class MaterialFunctorSourceDataHolder final diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialNameContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialNameContext.h index 124d73ad7a..ac6adffe2b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialNameContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialNameContext.h @@ -45,9 +45,9 @@ namespace AZ bool ContextualizeShaderOption(AZStd::string& shaderOptionName) const; //! Returns true if there is some non-default name context. - bool HasContextForProperties() const { return !m_propertyIdContext.empty(); } - bool HasContextForSrgInputs() const { return !m_srgInputNameContext.empty(); } - bool HasContextForShaderOptions() const { return !m_shaderOptionNameContext.empty(); } + bool HasContextForProperties() const; + bool HasContextForSrgInputs() const; + bool HasContextForShaderOptions() const; //! Returns true if the name context is empty. bool IsDefault() 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 7f2c13515a..91a7092362 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -262,7 +262,7 @@ namespace AZ return Failure(); } - auto materialTypeLoadOutcome = MaterialUtils::LoadMaterialTypeSourceData(materialTypeSourcePath); + auto materialTypeLoadOutcome = MaterialUtils::LoadMaterialTypeSourceData(materialTypeSourcePath, nullptr, sourceDependencies); if (!materialTypeLoadOutcome) { AZ_Error("MaterialSourceData", false, "Failed to load MaterialTypeSourceData: '%s'.", materialTypeSourcePath.c_str()); 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 6bcaa5d519..9153120732 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -157,6 +157,56 @@ namespace AZ toPropertyGroupList.back()->m_name = name; return toPropertyGroupList.back().get(); } + + const AZStd::string& MaterialTypeSourceData::PropertyGroup::GetName() const + { + return m_name; + } + + const AZStd::string& MaterialTypeSourceData::PropertyGroup::GetDisplayName() const + { + return m_displayName; + } + + const AZStd::string& MaterialTypeSourceData::PropertyGroup::GetDescription() const + { + return m_description; + } + + const MaterialTypeSourceData::PropertyList& MaterialTypeSourceData::PropertyGroup::GetProperties() const + { + return m_properties; + } + + const AZStd::string& MaterialTypeSourceData::PropertyGroup::GetShaderInputsPrefix() const + { + return m_shaderInputsPrefix; + } + + const AZStd::string& MaterialTypeSourceData::PropertyGroup::GetShaderOptionsPrefix() const + { + return m_shaderOptionsPrefix; + } + + const AZStd::vector>& MaterialTypeSourceData::PropertyGroup::GetPropertyGroups() const + { + return m_propertyGroups; + } + + const AZStd::vector>& MaterialTypeSourceData::PropertyGroup::GetFunctors() const + { + return m_materialFunctorSourceData; + } + + void MaterialTypeSourceData::PropertyGroup::SetDisplayName(AZStd::string_view displayName) + { + m_displayName = displayName; + } + + void MaterialTypeSourceData::PropertyGroup::SetDescription(AZStd::string_view description) + { + m_description = description; + } MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::PropertyGroup::AddProperty(AZStd::string_view name) { @@ -377,21 +427,23 @@ namespace AZ return parts; } - bool MaterialTypeSourceData::EnumeratePropertyGroups(const EnumeratePropertyGroupsCallback& callback, MaterialNameContext nameContext, const AZStd::vector>& inPropertyGroupList) const + bool MaterialTypeSourceData::EnumeratePropertyGroups(const EnumeratePropertyGroupsCallback& callback, PropertyGroupStack* propertyGroupStack, const AZStd::vector>& inPropertyGroupList) const { for (auto& propertyGroup : inPropertyGroupList) { - MaterialNameContext groupNameContext = ExtendNameContext(nameContext, *propertyGroup); + propertyGroupStack->push_back(propertyGroup.get()); - if (!callback(propertyGroup.get(), groupNameContext, nameContext)) + if (!callback(*propertyGroupStack)) { return false; // Stop processing } - if (!EnumeratePropertyGroups(callback, groupNameContext, propertyGroup->m_propertyGroups)) + if (!EnumeratePropertyGroups(callback, propertyGroupStack, propertyGroup->m_propertyGroups)) { return false; // Stop processing } + + propertyGroupStack->pop_back(); } return true; @@ -404,14 +456,16 @@ namespace AZ return false; } - return EnumeratePropertyGroups(callback, {}, m_propertyLayout.m_propertyGroups); + PropertyGroupStack propertyGroupStack; + return EnumeratePropertyGroups(callback, &propertyGroupStack, m_propertyLayout.m_propertyGroups); } bool MaterialTypeSourceData::EnumerateProperties(const EnumeratePropertiesCallback& callback, MaterialNameContext nameContext, const AZStd::vector>& inPropertyGroupList) const { for (auto& propertyGroup : inPropertyGroupList) { - MaterialNameContext groupNameContext = ExtendNameContext(nameContext, *propertyGroup); + MaterialNameContext groupNameContext = nameContext; + ExtendNameContext(groupNameContext, *propertyGroup); for (auto& property : propertyGroup->m_properties) { @@ -529,13 +583,21 @@ namespace AZ return groupDefinitions; } - MaterialNameContext MaterialTypeSourceData::ExtendNameContext(MaterialNameContext nameContext, const MaterialTypeSourceData::PropertyGroup& propertyGroup) + void MaterialTypeSourceData::ExtendNameContext(MaterialNameContext& nameContext, const MaterialTypeSourceData::PropertyGroup& propertyGroup) { - MaterialNameContext materialNameContext2 = nameContext; - materialNameContext2.ExtendPropertyIdContext(propertyGroup.m_name); - materialNameContext2.ExtendShaderOptionContext(propertyGroup.m_shaderOptionsPrefix); - materialNameContext2.ExtendSrgInputContext(propertyGroup.m_shaderInputsPrefix); - return materialNameContext2; + nameContext.ExtendPropertyIdContext(propertyGroup.m_name); + nameContext.ExtendShaderOptionContext(propertyGroup.m_shaderOptionsPrefix); + nameContext.ExtendSrgInputContext(propertyGroup.m_shaderInputsPrefix); + } + + /*static*/ MaterialNameContext MaterialTypeSourceData::MakeMaterialNameContext(const MaterialTypeSourceData::PropertyGroupStack& propertyGroupStack) + { + MaterialNameContext nameContext; + for (auto& group : propertyGroupStack) + { + ExtendNameContext(nameContext, *group); + } + return nameContext; } bool MaterialTypeSourceData::BuildPropertyList( @@ -549,7 +611,7 @@ namespace AZ return false; } - materialNameContext = ExtendNameContext(materialNameContext, *propertyGroup); + ExtendNameContext(materialNameContext, *propertyGroup); for (const AZStd::unique_ptr& property : propertyGroup->m_properties) { @@ -886,6 +948,6 @@ namespace AZ return Failure(); } } - + } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialNameContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialNameContext.cpp index 5846c60e05..9ef97c8785 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialNameContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialNameContext.cpp @@ -117,6 +117,20 @@ namespace AZ shaderOptionName = m_shaderOptionNameContext + shaderOptionName; return true; } + + bool MaterialNameContext::HasContextForProperties() const + { + return !m_propertyIdContext.empty(); + } + + bool MaterialNameContext::HasContextForSrgInputs() const + { + return !m_srgInputNameContext.empty(); + } + bool MaterialNameContext::HasContextForShaderOptions() const + { + return !m_shaderOptionNameContext.empty(); + } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 5605917e42..d122a7fff4 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -354,9 +354,7 @@ namespace MaterialEditor return false; } - // TODO: Support populating the Material Editor with nested property groups, not just the top level. - AZStd::string_view groupName = propertyId.GetStringView().substr(0, propertyId.GetStringView().size() - propertyDefinition->GetName().size() - 1); - sourceData.SetPropertyValue(AZ::RPI::MaterialPropertyId{groupName, propertyDefinition->GetName()}, propertyValue); + sourceData.SetPropertyValue(propertyId, propertyValue); } } return true; @@ -553,27 +551,38 @@ namespace MaterialEditor // Assets must still be used for now because they contain the final accumulated value after all other materials // in the hierarchy are applied bool enumerateResult = m_materialTypeSourceData.EnumeratePropertyGroups( - [this, &parentPropertyValues]( - const AZ::RPI::MaterialTypeSourceData::PropertyGroup* propertyGroup, - const AZ::RPI::MaterialNameContext& groupNameContext, - const AZ::RPI::MaterialNameContext& parentNameContext) + [this, &parentPropertyValues](const AZ::RPI::MaterialTypeSourceData::PropertyGroupStack& propertyGroupStack) { - // Add any material functors that are located inside each property group. + using namespace AZ::RPI; + + const MaterialTypeSourceData::PropertyGroup* propertyGroup = propertyGroupStack.back(); + + MaterialNameContext groupNameContext = MaterialTypeSourceData::MakeMaterialNameContext(propertyGroupStack); + if (!AddEditorMaterialFunctors(propertyGroup->GetFunctors(), groupNameContext)) { return false; } - + + AZStd::vector groupNameVector; + AZStd::vector groupDisplayNameVector; + + for (auto& group : propertyGroupStack) + { + groupNameVector.push_back(group->GetName()); + groupDisplayNameVector.push_back(group->GetDisplayName()); + } + m_groups.emplace_back(aznew AtomToolsFramework::DynamicPropertyGroup); - m_groups.back()->m_name = propertyGroup->GetName(); - parentNameContext.ContextualizeProperty(m_groups.back()->m_name); - m_groups.back()->m_displayName = propertyGroup->GetDisplayName(); m_groups.back()->m_description = propertyGroup->GetDescription(); + AzFramework::StringFunc::Join(m_groups.back()->m_name, groupNameVector.begin(), groupNameVector.end(), "."); + AzFramework::StringFunc::Join(m_groups.back()->m_displayName, groupDisplayNameVector.begin(), groupDisplayNameVector.end(), " | "); for (const auto& propertyDefinition : propertyGroup->GetProperties()) { - // Assign id before conversion so it can be used in dynamic description AtomToolsFramework::DynamicPropertyConfig propertyConfig; + + // Assign id before conversion so it can be used in dynamic description propertyConfig.m_id = propertyDefinition->GetName(); groupNameContext.ContextualizeProperty(propertyConfig.m_id); @@ -587,9 +596,8 @@ namespace MaterialEditor if (propertyIndexInBounds) { AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, *propertyDefinition); - - // TODO: Support populating the Material Editor with nested property groups, not just the top level. - // (Does DynamicPropertyConfig really even need m_groupDisplayName?) + + // (Does DynamicPropertyConfig really even need m_groupName? It doesn't seem to be used anywhere) propertyConfig.m_groupName = m_groups.back()->m_name; propertyConfig.m_groupDisplayName = m_groups.back()->m_displayName; propertyConfig.m_showThumbnail = true; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 376aba2208..1c6410f8c3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -102,29 +102,10 @@ namespace AZ UnloadMaterial(); return false; } + + // Add material functors that are in the top-level functors list. Other functors are also added per-property-group elsewhere. + AddEditorMaterialFunctors(m_editData.m_materialTypeSourceData.m_materialFunctorSourceData, AZ::RPI::MaterialNameContext{}); - // Get a list of all the editor functors to be used for property editor states - auto propertyLayout = m_editData.m_materialAsset->GetMaterialPropertiesLayout(); - AZ::RPI::MaterialNameContext materialNameContext; // There is no name context for top-level functors, only functors inside PropertyGroups - const AZ::RPI::MaterialFunctorSourceData::EditorContext editorContext = - AZ::RPI::MaterialFunctorSourceData::EditorContext(m_editData.m_materialTypeSourcePath, propertyLayout, &materialNameContext); - for (AZ::RPI::Ptr functorData : m_editData.m_materialTypeSourceData.m_materialFunctorSourceData) - { - AZ::RPI::MaterialFunctorSourceData::FunctorResult createResult = functorData->CreateFunctor(editorContext); - - if (createResult.IsSuccess()) - { - AZ::RPI::Ptr& functor = createResult.GetValue(); - if (functor != nullptr) - { - m_editorFunctors.push_back(functor); - } - } - else - { - AZ_Error("AZ::Render::EditorMaterialComponentInspector", false, "Material functors were not created: '%s'.", m_editData.m_materialTypeSourcePath.c_str()); - } - } Populate(); LoadOverridesFromEntity(); @@ -295,47 +276,73 @@ namespace AZ void MaterialPropertyInspector::AddPropertiesGroup() { // Copy all of the properties from the material asset to the source data that will be exported - // TODO: Support populating the Material Editor with nested property groups, not just the top level. - for (const AZStd::unique_ptr& propertyGroup : m_editData.m_materialTypeSourceData.GetPropertyLayout().m_propertyGroups) - { - const AZStd::string& groupName = propertyGroup->GetName(); - const AZStd::string& groupDisplayName = !propertyGroup->GetDisplayName().empty() ? propertyGroup->GetDisplayName() : groupName; - const AZStd::string& groupDescription = !propertyGroup->GetDescription().empty() ? propertyGroup->GetDescription() : groupDisplayName; - auto& group = m_groups[groupName]; - - group.m_properties.reserve(propertyGroup->GetProperties().size()); - for (const auto& propertyDefinition : propertyGroup->GetProperties()) + m_editData.m_materialTypeSourceData.EnumeratePropertyGroups( + [this](const AZ::RPI::MaterialTypeSourceData::PropertyGroupStack& propertyGroupStack) { - AtomToolsFramework::DynamicPropertyConfig propertyConfig; + using namespace AZ::RPI; + + const MaterialTypeSourceData::PropertyGroup* propertyGroupDefinition = propertyGroupStack.back(); + + MaterialNameContext groupNameContext = MaterialTypeSourceData::MakeMaterialNameContext(propertyGroupStack); + + AddEditorMaterialFunctors(propertyGroupDefinition->GetFunctors(), groupNameContext); - // Assign id before conversion so it can be used in dynamic description - propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupName, propertyDefinition->GetName()); + AZStd::vector groupNameVector; + AZStd::vector groupDisplayNameVector; - AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, *propertyDefinition.get()); + for (auto& group : propertyGroupStack) + { + groupNameVector.push_back(group->GetName()); + groupDisplayNameVector.push_back(!group->GetDisplayName().empty() ? group->GetDisplayName() : group->GetName()); + } + + AZStd::string groupId; + AzFramework::StringFunc::Join(groupId, groupNameVector.begin(), groupNameVector.end(), "."); - const auto& propertyIndex = - m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id); + auto& group = m_groups[groupId]; + group.m_name = groupId; + AzFramework::StringFunc::Join(group.m_displayName, groupDisplayNameVector.begin(), groupDisplayNameVector.end(), " | "); + group.m_description = !propertyGroupDefinition->GetDescription().empty() ? propertyGroupDefinition->GetDescription() : group.m_displayName; - propertyConfig.m_groupName = groupDisplayName; - propertyConfig.m_showThumbnail = true; - propertyConfig.m_defaultValue = AtomToolsFramework::ConvertToEditableType( - m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]); + group.m_properties.reserve(propertyGroupDefinition->GetProperties().size()); + for (const auto& propertyDefinition : propertyGroupDefinition->GetProperties()) + { + AtomToolsFramework::DynamicPropertyConfig propertyConfig; + + // Assign id before conversion so it can be used in dynamic description + propertyConfig.m_id = propertyDefinition->GetName(); + groupNameContext.ContextualizeProperty(propertyConfig.m_id); + + AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, *propertyDefinition); + + const auto& propertyIndex = + m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id); + + // (Does DynamicPropertyConfig really even need m_groupName? It doesn't seem to be used anywhere) + propertyConfig.m_groupName = group.m_name; + propertyConfig.m_groupDisplayName = group.m_displayName; + propertyConfig.m_showThumbnail = true; + + propertyConfig.m_defaultValue = AtomToolsFramework::ConvertToEditableType( + m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]); + + // There is no explicit parent material here. Material instance property overrides replace the values from the + // assigned material asset. Its values should be treated as parent, for comparison, in this case. + propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType( + m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]); + propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType( + m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]); + group.m_properties.emplace_back(propertyConfig); + } - // There is no explicit parent material here. Material instance property overrides replace the values from the - // assigned material asset. Its values should be treated as parent, for comparison, in this case. - propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType( - m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]); - propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType( - m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]); - group.m_properties.emplace_back(propertyConfig); - } + // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties + auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( + &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(group.m_name), {}, + [this](const auto node) { return GetInstanceNodePropertyIndicator(node); }, 0); + AddGroup(group.m_name, group.m_displayName, group.m_description, propertyGroupWidget); - // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties - auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupName), {}, - [this](const auto node) { return GetInstanceNodePropertyIndicator(node); }, 0); - AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget); - } + return true; + }); } void MaterialPropertyInspector::Populate() @@ -435,6 +442,37 @@ namespace AZ // m_updatePreview should be set to true here for continuous preview updates as slider/color properties change but needs // throttling } + + bool MaterialPropertyInspector::AddEditorMaterialFunctors( + const AZStd::vector>& functorSourceDataHolders, + const AZ::RPI::MaterialNameContext& nameContext) + { + // Copied from MaterialDocument::AddEditorMaterialFunctors, should be refactored at some point + + const AZ::RPI::MaterialFunctorSourceData::EditorContext editorContext = AZ::RPI::MaterialFunctorSourceData::EditorContext( + m_editData.m_materialTypeSourcePath, m_editData.m_materialAsset->GetMaterialPropertiesLayout(), &nameContext); + + for (AZ::RPI::Ptr functorData : functorSourceDataHolders) + { + AZ::RPI::MaterialFunctorSourceData::FunctorResult result = functorData->CreateFunctor(editorContext); + + if (result.IsSuccess()) + { + AZ::RPI::Ptr& functor = result.GetValue(); + if (functor != nullptr) + { + m_editorFunctors.push_back(functor); + } + } + else + { + AZ_Error("MaterialDocument", false, "Material functors were not created: '%s'.", m_editData.m_materialTypeSourcePath.c_str()); + return false; + } + } + + return true; + } void MaterialPropertyInspector::RunEditorMaterialFunctors() { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h index 82f46ebc90..44f1fb3491 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h @@ -108,6 +108,10 @@ namespace AZ void RunEditorMaterialFunctors(); void UpdateMaterialInstanceProperty(const AtomToolsFramework::DynamicProperty& property); + bool AddEditorMaterialFunctors( + const AZStd::vector>& functorSourceDataHolders, + const AZ::RPI::MaterialNameContext& nameContext); + AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupName) const; bool IsInstanceNodePropertyModifed(const AzToolsFramework::InstanceDataNode* node) const; const char* GetInstanceNodePropertyIndicator(const AzToolsFramework::InstanceDataNode* node) const; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp index 35633068d5..88cbbba87b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp @@ -151,9 +151,7 @@ namespace AZ return true; } - // TODO: Support populating the Material Editor with nested property groups, not just the top level. - AZStd::string_view groupName = propertyId.GetStringView().substr(0, propertyId.GetStringView().size() - propertyDefinition->GetName().size() - 1); - exportData.SetPropertyValue(RPI::MaterialPropertyId{groupName, propertyDefinition->GetName()}, propertyValue); + exportData.SetPropertyValue(propertyId, propertyValue); return true; });