Tied up a few loose ends to support deeply nested property groups.

Simplified the call back for MaterialTypeSourceData::EnumeratePropertyGroups while also providing more data.
Made the Material Inspector join nested property group display names to be like "Layer 1 | Base Color", since the leaf property groups are shown as a flat list in the inspector.
Fixed CreateMaterialAssetFromSourceData to include the imported json files in the list of sourceDependencies. This triggers the Material Editor to hot-reload when one of these json files changes.
Updated a few places that were still assuming only one level of property group.
Updated EditorMaterialComponentInspector to apply the per-property-group material functors, before it was still only applying the top-level onces.
Moved some accessor function implementations to the cpp files, per feedback on another already-merged PR.

Testing:
Made changes to MinimalMultilayerPbr (in AtomSampleViewer) to use nested property groups, and saw the correct behavior in the Material Editor's property inspector.
Used MaterialComponent's property inspector to edit a StandardPbr material instance. Confrimed that functors were correctly controlling property visibility by enabling and disabling things like emissive and clear coat.
Used MaterialComponent's property inspector to edit a MinimalMultilayerPbr material instance. Saw all the expected groups and properties show up. Confirmed that per-group functors were correctly controlling property visibility.
Used MaterialComponent's property inspector to export a material instance and confirmed the .material file included the expected properties.

Signed-off-by: santorac <55155825+santorac@users.noreply.github.com>
monroegm-disable-blank-issue-2
santorac 4 years ago
parent 2a3c5b38e5
commit 8fbd2aaaf5

@ -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<AZStd::unique_ptr<PropertyGroup>>& GetPropertyGroups() const { return m_propertyGroups; }
const AZStd::vector<Ptr<MaterialFunctorSourceDataHolder>>& GetFunctors() const { return m_materialFunctorSourceData; }
void SetDisplayName(AZStd::string_view displayName) { m_displayName = displayName; }
void SetDescription(AZStd::string_view description) { m_description = description; }
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<AZStd::unique_ptr<PropertyGroup>>& GetPropertyGroups() const;
const AZStd::vector<Ptr<MaterialFunctorSourceDataHolder>>& GetFunctors() const;
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<AZStd::string_view> 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<const PropertyGroup*>;
//! 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<bool(
const PropertyGroup*, // the next property group in the tree
const MaterialNameContext&, // The name context defined by the PropertyGroup, used to scope the contents of the property group
const MaterialNameContext& // The name context that the PropertyGroup is in, used to scope the property group name
)>;
using EnumeratePropertyGroupsCallback = AZStd::function<bool(const PropertyGroupStack&)>;
//! 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<Data::Asset<MaterialTypeAsset>> 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<AZStd::string_view> parsedPropertyId, AZStd::span<AZStd::unique_ptr<PropertyGroup>> inPropertyGroupList);
// Function overloads for recursion, returns false to indicate that recursion should end.
bool EnumeratePropertyGroups(const EnumeratePropertyGroupsCallback& callback, MaterialNameContext nameContext, const AZStd::vector<AZStd::unique_ptr<PropertyGroup>>& inPropertyGroupList) const;
bool EnumeratePropertyGroups(const EnumeratePropertyGroupsCallback& callback, PropertyGroupStack* propertyGroupStack, const AZStd::vector<AZStd::unique_ptr<PropertyGroup>>& inPropertyGroupList) const;
bool EnumerateProperties(const EnumeratePropertiesCallback& callback, MaterialNameContext nameContext, const AZStd::vector<AZStd::unique_ptr<PropertyGroup>>& 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

@ -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;

@ -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());

@ -158,6 +158,56 @@ namespace AZ
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<AZStd::unique_ptr<MaterialTypeSourceData::PropertyGroup>>& MaterialTypeSourceData::PropertyGroup::GetPropertyGroups() const
{
return m_propertyGroups;
}
const AZStd::vector<Ptr<MaterialFunctorSourceDataHolder>>& 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)
{
if (!MaterialPropertyId::CheckIsValidName(name))
@ -377,21 +427,23 @@ namespace AZ
return parts;
}
bool MaterialTypeSourceData::EnumeratePropertyGroups(const EnumeratePropertyGroupsCallback& callback, MaterialNameContext nameContext, const AZStd::vector<AZStd::unique_ptr<PropertyGroup>>& inPropertyGroupList) const
bool MaterialTypeSourceData::EnumeratePropertyGroups(const EnumeratePropertyGroupsCallback& callback, PropertyGroupStack* propertyGroupStack, const AZStd::vector<AZStd::unique_ptr<PropertyGroup>>& 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<AZStd::unique_ptr<PropertyGroup>>& 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<PropertyDefinition>& property : propertyGroup->m_properties)
{

@ -118,5 +118,19 @@ namespace AZ
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

@ -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<AZStd::string> groupNameVector;
AZStd::vector<AZStd::string> 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);
@ -588,8 +597,7 @@ namespace MaterialEditor
{
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;

@ -103,28 +103,9 @@ namespace AZ
return false;
}
// 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<AZ::RPI::MaterialFunctorSourceDataHolder> functorData : m_editData.m_materialTypeSourceData.m_materialFunctorSourceData)
{
AZ::RPI::MaterialFunctorSourceData::FunctorResult createResult = functorData->CreateFunctor(editorContext);
// 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{});
if (createResult.IsSuccess())
{
AZ::RPI::Ptr<AZ::RPI::MaterialFunctor>& 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,29 +276,53 @@ 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<AZ::RPI::MaterialTypeSourceData::PropertyGroup>& propertyGroup : m_editData.m_materialTypeSourceData.GetPropertyLayout().m_propertyGroups)
m_editData.m_materialTypeSourceData.EnumeratePropertyGroups(
[this](const AZ::RPI::MaterialTypeSourceData::PropertyGroupStack& propertyGroupStack)
{
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];
using namespace AZ::RPI;
const MaterialTypeSourceData::PropertyGroup* propertyGroupDefinition = propertyGroupStack.back();
group.m_properties.reserve(propertyGroup->GetProperties().size());
for (const auto& propertyDefinition : propertyGroup->GetProperties())
MaterialNameContext groupNameContext = MaterialTypeSourceData::MakeMaterialNameContext(propertyGroupStack);
AddEditorMaterialFunctors(propertyGroupDefinition->GetFunctors(), groupNameContext);
AZStd::vector<AZStd::string> groupNameVector;
AZStd::vector<AZStd::string> groupDisplayNameVector;
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(), ".");
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;
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 = AZ::RPI::MaterialPropertyId(groupName, propertyDefinition->GetName());
propertyConfig.m_id = propertyDefinition->GetName();
groupNameContext.ContextualizeProperty(propertyConfig.m_id);
AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, *propertyDefinition.get());
AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, *propertyDefinition);
const auto& propertyIndex =
m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id);
propertyConfig.m_groupName = groupDisplayName;
// (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()]);
@ -332,10 +337,12 @@ namespace AZ
// 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), {},
&group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(group.m_name), {},
[this](const auto node) { return GetInstanceNodePropertyIndicator(node); }, 0);
AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget);
}
AddGroup(group.m_name, group.m_displayName, group.m_description, propertyGroupWidget);
return true;
});
}
void MaterialPropertyInspector::Populate()
@ -436,6 +443,37 @@ namespace AZ
// throttling
}
bool MaterialPropertyInspector::AddEditorMaterialFunctors(
const AZStd::vector<AZ::RPI::Ptr<AZ::RPI::MaterialFunctorSourceDataHolder>>& 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<AZ::RPI::MaterialFunctorSourceDataHolder> functorData : functorSourceDataHolders)
{
AZ::RPI::MaterialFunctorSourceData::FunctorResult result = functorData->CreateFunctor(editorContext);
if (result.IsSuccess())
{
AZ::RPI::Ptr<AZ::RPI::MaterialFunctor>& 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()
{
if (!IsLoaded())

@ -108,6 +108,10 @@ namespace AZ
void RunEditorMaterialFunctors();
void UpdateMaterialInstanceProperty(const AtomToolsFramework::DynamicProperty& property);
bool AddEditorMaterialFunctors(
const AZStd::vector<AZ::RPI::Ptr<AZ::RPI::MaterialFunctorSourceDataHolder>>& 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;

@ -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;
});

Loading…
Cancel
Save