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>
This commit is contained in:
@@ -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; }
|
||||
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) { 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<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
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<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)
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
@@ -886,6 +948,6 @@ namespace AZ
|
||||
return Failure();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
+97
-59
@@ -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<AZ::RPI::MaterialFunctorSourceDataHolder> functorData : m_editData.m_materialTypeSourceData.m_materialFunctorSourceData)
|
||||
{
|
||||
AZ::RPI::MaterialFunctorSourceData::FunctorResult createResult = functorData->CreateFunctor(editorContext);
|
||||
|
||||
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,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<AZ::RPI::MaterialTypeSourceData::PropertyGroup>& 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;
|
||||
|
||||
// Assign id before conversion so it can be used in dynamic description
|
||||
propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupName, propertyDefinition->GetName());
|
||||
|
||||
AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, *propertyDefinition.get());
|
||||
|
||||
const auto& propertyIndex =
|
||||
m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id);
|
||||
const MaterialTypeSourceData::PropertyGroup* propertyGroupDefinition = propertyGroupStack.back();
|
||||
|
||||
MaterialNameContext groupNameContext = MaterialTypeSourceData::MakeMaterialNameContext(propertyGroupStack);
|
||||
|
||||
propertyConfig.m_groupName = groupDisplayName;
|
||||
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);
|
||||
}
|
||||
AddEditorMaterialFunctors(propertyGroupDefinition->GetFunctors(), groupNameContext);
|
||||
|
||||
// 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);
|
||||
}
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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<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()
|
||||
{
|
||||
|
||||
+4
@@ -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;
|
||||
|
||||
+1
-3
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user