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:
+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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user