Changing material component property inspector to dockable view pane
• Inspector is locked to a specific entity and material assignment ID • All modifications are made via the material component request bus • Removed complicated configuration management in editor material component • Multiple material property inspectors can be opened • Multiple materials across different entities can be edited simultaneously • No longer blocks the viewport or other interactions • Added functions to material component request bus for retrieving material slot labels, default materials, getting and setting property and UV overrides • Added more asset related types to material property value conversion from any • Added support for static heading widget on top of atom tools inspector, currently used for menus and messages WIP: Still investigating intermittent crash because of corrupt asset property Signed-off-by: Guthrie Adams <guthadam@amazon.com>
This commit is contained in:
+286
-154
@@ -27,19 +27,16 @@
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailWidget.h>
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
|
||||
|
||||
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConfig.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QApplication>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileInfo>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
@@ -49,26 +46,59 @@ namespace AZ
|
||||
{
|
||||
namespace EditorMaterialComponentInspector
|
||||
{
|
||||
MaterialPropertyInspector::MaterialPropertyInspector(
|
||||
const AZStd::string& slotName, const AZ::Data::AssetId& assetId, PropertyChangedCallback propertyChangedCallback,
|
||||
QWidget* parent)
|
||||
MaterialPropertyInspector::MaterialPropertyInspector(QWidget* parent)
|
||||
: AtomToolsFramework::InspectorWidget(parent)
|
||||
, m_slotName(slotName)
|
||||
, m_materialAssetId(assetId)
|
||||
, m_propertyChangedCallback(propertyChangedCallback)
|
||||
{
|
||||
// Create the menu button
|
||||
QToolButton* menuButton = new QToolButton(this);
|
||||
menuButton->setAutoRaise(true);
|
||||
menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg"));
|
||||
menuButton->setVisible(true);
|
||||
QObject::connect(menuButton, &QToolButton::clicked, this, [this]() { OpenMenu(); });
|
||||
AddHeading(menuButton);
|
||||
|
||||
m_messageLabel = new QLabel(this);
|
||||
m_messageLabel->setWordWrap(true);
|
||||
m_messageLabel->setVisible(true);
|
||||
m_messageLabel->setAlignment(Qt::AlignCenter);
|
||||
m_messageLabel->setText(tr("Material not available"));
|
||||
AddHeading(m_messageLabel);
|
||||
|
||||
AZ::EntitySystemBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
MaterialPropertyInspector::~MaterialPropertyInspector()
|
||||
{
|
||||
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
|
||||
AZ::EntitySystemBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
MaterialComponentNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
bool MaterialPropertyInspector::LoadMaterial()
|
||||
bool MaterialPropertyInspector::LoadMaterial(
|
||||
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId)
|
||||
{
|
||||
if (!EditorMaterialComponentUtil::LoadMaterialEditDataFromAssetId(m_materialAssetId, m_editData))
|
||||
UnloadMaterial();
|
||||
|
||||
m_entityId = entityId;
|
||||
m_materialAssignmentId = materialAssignmentId;
|
||||
MaterialComponentNotificationBus::Handler::BusDisconnect();
|
||||
MaterialComponentNotificationBus::Handler::BusConnect(m_entityId);
|
||||
|
||||
AZ::Data::AssetId materialAssetId = {};
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId);
|
||||
|
||||
if (!materialAssetId.IsValid())
|
||||
{
|
||||
UnloadMaterial();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EditorMaterialComponentUtil::LoadMaterialEditDataFromAssetId(materialAssetId, m_editData))
|
||||
{
|
||||
AZ_Warning("AZ::Render::EditorMaterialComponentInspector", false, "Failed to load material data.");
|
||||
UnloadMaterial();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -77,6 +107,7 @@ namespace AZ
|
||||
if (!m_materialInstance)
|
||||
{
|
||||
AZ_Error("AZ::Render::EditorMaterialComponentInspector", false, "Material instance could not be created.");
|
||||
UnloadMaterial();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -102,15 +133,36 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
Populate();
|
||||
m_messageLabel->setVisible(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MaterialPropertyInspector::UnloadMaterial()
|
||||
{
|
||||
Reset();
|
||||
m_editData = EditorMaterialComponentUtil::MaterialEditData();
|
||||
m_materialInstance = {};
|
||||
m_dirtyPropertyFlags.set();
|
||||
m_editorFunctors = {};
|
||||
m_internalEditNotification = {};
|
||||
m_messageLabel->setVisible(true);
|
||||
m_messageLabel->setText(tr("Material not available"));
|
||||
}
|
||||
|
||||
bool MaterialPropertyInspector::IsLoaded() const
|
||||
{
|
||||
return m_entityId.IsValid() && m_materialInstance && m_editData.m_materialAsset.IsReady();
|
||||
}
|
||||
|
||||
void MaterialPropertyInspector::Reset()
|
||||
{
|
||||
m_activeProperty = {};
|
||||
m_groups = {};
|
||||
m_dirtyPropertyFlags.set();
|
||||
m_internalEditNotification = {};
|
||||
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
|
||||
AtomToolsFramework::InspectorWidget::Reset();
|
||||
}
|
||||
@@ -150,9 +202,18 @@ namespace AZ
|
||||
QFileInfo materialTypeSourceFileInfo(m_editData.m_materialTypeSourcePath.c_str());
|
||||
QFileInfo materialParentSourceFileInfo(AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_editData.m_materialParentAsset.GetId()).c_str());
|
||||
|
||||
AZStd::string entityName;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(
|
||||
entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_entityId);
|
||||
|
||||
AZStd::string slotName;
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
slotName, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialSlotLabel, m_materialAssignmentId);
|
||||
|
||||
QString materialInfo;
|
||||
materialInfo += tr("<table>");
|
||||
materialInfo += tr("<tr><td><b>Material Slot </b></td><td>%1</td></tr>").arg(m_slotName.c_str());
|
||||
materialInfo += tr("<tr><td><b>Entity </b></td><td>%1</td></tr>").arg(entityName.c_str());
|
||||
materialInfo += tr("<tr><td><b>Material Slot </b></td><td>%1</td></tr>").arg(slotName.c_str());
|
||||
if (!materialFileInfo.fileName().isEmpty())
|
||||
{
|
||||
materialInfo += tr("<tr><td><b>Material </b></td><td>%1</td></tr>").arg(materialFileInfo.fileName());
|
||||
@@ -209,16 +270,8 @@ namespace AZ
|
||||
}
|
||||
|
||||
// Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties
|
||||
const AZ::Crc32 saveStateKey(AZStd::string::format(
|
||||
"MaterialPropertyInspector::PropertyGroup::%s::%s", m_materialAssetId.ToString<AZStd::string>().c_str(),
|
||||
groupNameId.c_str()));
|
||||
auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget(
|
||||
&group, &group, group.TYPEINFO_Uuid(), this, this, saveStateKey,
|
||||
[](const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target) {
|
||||
AZ_UNUSED(source);
|
||||
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(target);
|
||||
return property && AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue);
|
||||
});
|
||||
&group, nullptr, group.TYPEINFO_Uuid(), this, this, GetSaveStateKeyForGroup(groupNameId));
|
||||
AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget);
|
||||
}
|
||||
|
||||
@@ -262,35 +315,90 @@ namespace AZ
|
||||
}
|
||||
|
||||
// Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties
|
||||
const AZ::Crc32 saveStateKey(AZStd::string::format(
|
||||
"MaterialPropertyInspector::PropertyGroup::%s::%s", m_materialAssetId.ToString<AZStd::string>().c_str(),
|
||||
groupNameId.c_str()));
|
||||
auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget(
|
||||
&group, &group, group.TYPEINFO_Uuid(), this, this, saveStateKey,
|
||||
[](const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target) {
|
||||
AZ_UNUSED(source);
|
||||
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(target);
|
||||
return property && AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue);
|
||||
});
|
||||
&group, nullptr, group.TYPEINFO_Uuid(), this, this, GetSaveStateKeyForGroup(groupNameId));
|
||||
AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget);
|
||||
}
|
||||
|
||||
AddGroupsEnd();
|
||||
|
||||
m_dirtyPropertyFlags.set();
|
||||
RunEditorMaterialFunctors();
|
||||
LoadOverridesFromEntity();
|
||||
}
|
||||
|
||||
void MaterialPropertyInspector::RunPropertyChangedCallback()
|
||||
void MaterialPropertyInspector::LoadOverridesFromEntity()
|
||||
{
|
||||
if (m_propertyChangedCallback)
|
||||
if (!IsLoaded())
|
||||
{
|
||||
m_propertyChangedCallback(m_editData.m_materialPropertyOverrideMap);
|
||||
return;
|
||||
}
|
||||
|
||||
m_editData.m_materialPropertyOverrideMap.clear();
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
m_editData.m_materialPropertyOverrideMap, m_entityId, &MaterialComponentRequestBus::Events::GetPropertyOverrides,
|
||||
m_materialAssignmentId);
|
||||
|
||||
for (auto& group : m_groups)
|
||||
{
|
||||
for (auto& property : group.second.m_properties)
|
||||
{
|
||||
const AtomToolsFramework::DynamicPropertyConfig& propertyConfig = property.GetConfig();
|
||||
const auto overrideItr = m_editData.m_materialPropertyOverrideMap.find(propertyConfig.m_id);
|
||||
const auto& editValue = overrideItr != m_editData.m_materialPropertyOverrideMap.end() ? overrideItr->second : propertyConfig.m_originalValue;
|
||||
|
||||
// This first converts to an acceptable runtime type in case the value came from script
|
||||
const auto propertyIndex = m_materialInstance->FindPropertyIndex(property.GetId());
|
||||
if (!propertyIndex.IsNull())
|
||||
{
|
||||
const auto runtimeValue = AtomToolsFramework::ConvertToRuntimeType(editValue);
|
||||
if (runtimeValue.IsValid())
|
||||
{
|
||||
property.SetValue(AtomToolsFramework::ConvertToEditableType(runtimeValue));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
property.SetValue(editValue);
|
||||
}
|
||||
|
||||
UpdateMaterialInstanceProperty(property);
|
||||
}
|
||||
}
|
||||
|
||||
m_dirtyPropertyFlags.set();
|
||||
RunEditorMaterialFunctors();
|
||||
RefreshAll();
|
||||
}
|
||||
|
||||
void MaterialPropertyInspector::SaveOverridesToEntity(bool commitChanges)
|
||||
{
|
||||
if (!IsLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialComponentRequestBus::Event(
|
||||
m_entityId, &MaterialComponentRequestBus::Events::SetPropertyOverrides, m_materialAssignmentId,
|
||||
m_editData.m_materialPropertyOverrideMap);
|
||||
|
||||
if (commitChanges)
|
||||
{
|
||||
AzToolsFramework::ScopedUndoBatch undoBatch("Material slot changed.");
|
||||
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, m_entityId);
|
||||
|
||||
m_internalEditNotification = true;
|
||||
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited);
|
||||
m_internalEditNotification = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialPropertyInspector::RunEditorMaterialFunctors()
|
||||
{
|
||||
if (!IsLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::unordered_set<AZ::Name> changedPropertyNames;
|
||||
AZStd::unordered_set<AZ::Name> changedPropertyGroupNames;
|
||||
|
||||
@@ -337,11 +445,13 @@ namespace AZ
|
||||
// Apply any changes to material property meta data back to the editor property configurations
|
||||
for (auto& groupPair : m_groups)
|
||||
{
|
||||
AZ::Name groupName{groupPair.first};
|
||||
AZ::Name groupName{ groupPair.first };
|
||||
|
||||
if (changedPropertyGroupNames.find(groupName) != changedPropertyGroupNames.end())
|
||||
{
|
||||
SetGroupVisible(groupPair.first, propertyGroupDynamicMetadata[groupName].m_visibility == AZ::RPI::MaterialPropertyGroupVisibility::Enabled);
|
||||
SetGroupVisible(
|
||||
groupPair.first,
|
||||
propertyGroupDynamicMetadata[groupName].m_visibility == AZ::RPI::MaterialPropertyGroupVisibility::Enabled);
|
||||
}
|
||||
|
||||
for (auto& property : groupPair.second.m_properties)
|
||||
@@ -355,12 +465,12 @@ namespace AZ
|
||||
|
||||
if (oldReadOnly != propertyConfig.m_readOnly)
|
||||
{
|
||||
RefreshAll();
|
||||
RefreshGroup(groupPair.first);
|
||||
}
|
||||
|
||||
if (oldVisible != propertyConfig.m_visible)
|
||||
{
|
||||
RebuildAll();
|
||||
RebuildGroup(groupPair.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,57 +478,37 @@ namespace AZ
|
||||
|
||||
void MaterialPropertyInspector::UpdateMaterialInstanceProperty(const AtomToolsFramework::DynamicProperty& property)
|
||||
{
|
||||
if (m_materialInstance)
|
||||
if (!IsLoaded())
|
||||
{
|
||||
const auto propertyIndex = m_materialInstance->FindPropertyIndex(property.GetId());
|
||||
if (!propertyIndex.IsNull())
|
||||
{
|
||||
m_dirtyPropertyFlags.set(propertyIndex.GetIndex());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto runtimeValue = AtomToolsFramework::ConvertToRuntimeType(property.GetValue());
|
||||
if (runtimeValue.IsValid())
|
||||
{
|
||||
m_materialInstance->SetPropertyValue(propertyIndex, runtimeValue);
|
||||
}
|
||||
const auto propertyIndex = m_materialInstance->FindPropertyIndex(property.GetId());
|
||||
if (!propertyIndex.IsNull())
|
||||
{
|
||||
m_dirtyPropertyFlags.set(propertyIndex.GetIndex());
|
||||
|
||||
const auto runtimeValue = AtomToolsFramework::ConvertToRuntimeType(property.GetValue());
|
||||
if (runtimeValue.IsValid())
|
||||
{
|
||||
m_materialInstance->SetPropertyValue(propertyIndex, runtimeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialPropertyInspector::SetOverrides(const MaterialPropertyOverrideMap& propertyOverrideMap)
|
||||
AZ::Crc32 MaterialPropertyInspector::GetSaveStateKeyForGroup(const AZStd::string& groupNameId) const
|
||||
{
|
||||
m_editData.m_materialPropertyOverrideMap = propertyOverrideMap;
|
||||
return AZ::Crc32(AZStd::string::format(
|
||||
"MaterialPropertyInspector::PropertyGroup::%s::%s", m_editData.m_materialAssetId.ToString<AZStd::string>().c_str(),
|
||||
groupNameId.c_str()));
|
||||
}
|
||||
|
||||
for (auto& group : m_groups)
|
||||
{
|
||||
for (auto& property : group.second.m_properties)
|
||||
{
|
||||
const AtomToolsFramework::DynamicPropertyConfig& propertyConfig = property.GetConfig();
|
||||
const auto overrideItr = m_editData.m_materialPropertyOverrideMap.find(propertyConfig.m_id);
|
||||
const auto& editValue = overrideItr != m_editData.m_materialPropertyOverrideMap.end() ? overrideItr->second : propertyConfig.m_originalValue;
|
||||
|
||||
// This first converts to an acceptable runtime type in case the value came from script
|
||||
const auto propertyIndex = m_materialInstance->FindPropertyIndex(property.GetId());
|
||||
if (!propertyIndex.IsNull())
|
||||
{
|
||||
const auto runtimeValue = AtomToolsFramework::ConvertToRuntimeType(editValue);
|
||||
if (runtimeValue.IsValid())
|
||||
{
|
||||
property.SetValue(AtomToolsFramework::ConvertToEditableType(runtimeValue));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
property.SetValue(editValue);
|
||||
}
|
||||
|
||||
UpdateMaterialInstanceProperty(property);
|
||||
}
|
||||
}
|
||||
|
||||
m_dirtyPropertyFlags.set();
|
||||
RunPropertyChangedCallback();
|
||||
RunEditorMaterialFunctors();
|
||||
RebuildAll();
|
||||
bool MaterialPropertyInspector::AreNodePropertyValuesEqual(
|
||||
const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target)
|
||||
{
|
||||
AZ_UNUSED(source);
|
||||
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(target);
|
||||
return property && AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue);
|
||||
}
|
||||
|
||||
bool MaterialPropertyInspector::SaveMaterial() const
|
||||
@@ -446,7 +536,8 @@ namespace AZ
|
||||
|
||||
bool MaterialPropertyInspector::SaveMaterialToSource() const
|
||||
{
|
||||
const QString saveFilePath = AtomToolsFramework::GetSaveFileInfo(m_editData.m_materialSourcePath.c_str()).absoluteFilePath();
|
||||
const QString saveFilePath =
|
||||
AtomToolsFramework::GetSaveFileInfo(m_editData.m_materialSourcePath.c_str()).absoluteFilePath();
|
||||
if (saveFilePath.isEmpty())
|
||||
{
|
||||
return false;
|
||||
@@ -463,13 +554,13 @@ namespace AZ
|
||||
|
||||
bool MaterialPropertyInspector::HasMaterialSource() const
|
||||
{
|
||||
return !m_editData.m_materialSourcePath.empty() &&
|
||||
return IsLoaded() && !m_editData.m_materialSourcePath.empty() &&
|
||||
AZ::StringFunc::Path::IsExtension(m_editData.m_materialSourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension);
|
||||
}
|
||||
|
||||
bool MaterialPropertyInspector::HasMaterialParentSource() const
|
||||
{
|
||||
return !m_editData.m_materialParentSourcePath.empty() &&
|
||||
return IsLoaded() && !m_editData.m_materialParentSourcePath.empty() &&
|
||||
AZ::StringFunc::Path::IsExtension(
|
||||
m_editData.m_materialParentSourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension);
|
||||
}
|
||||
@@ -479,7 +570,7 @@ namespace AZ
|
||||
if (HasMaterialSource())
|
||||
{
|
||||
EditorMaterialSystemComponentRequestBus::Broadcast(
|
||||
&EditorMaterialSystemComponentRequestBus::Events::OpenInMaterialEditor, m_editData.m_materialSourcePath);
|
||||
&EditorMaterialSystemComponentRequestBus::Events::OpenMaterialEditor, m_editData.m_materialSourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,10 +579,42 @@ namespace AZ
|
||||
if (HasMaterialParentSource())
|
||||
{
|
||||
EditorMaterialSystemComponentRequestBus::Broadcast(
|
||||
&EditorMaterialSystemComponentRequestBus::Events::OpenInMaterialEditor, m_editData.m_materialParentSourcePath);
|
||||
&EditorMaterialSystemComponentRequestBus::Events::OpenMaterialEditor, m_editData.m_materialParentSourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialPropertyInspector::OpenMenu()
|
||||
{
|
||||
QAction* action = nullptr;
|
||||
|
||||
QMenu menu(this);
|
||||
action = menu.addAction("Clear Overrides", [this] {
|
||||
MaterialComponentRequestBus::Event(
|
||||
m_entityId, &MaterialComponentRequestBus::Events::SetPropertyOverrides, m_materialAssignmentId,
|
||||
MaterialPropertyOverrideMap());
|
||||
QueueUpdateUI();
|
||||
});
|
||||
action->setEnabled(IsLoaded());
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
action = menu.addAction("Save Material", [this] { SaveMaterial(); });
|
||||
action->setEnabled(IsLoaded());
|
||||
|
||||
action = menu.addAction("Save Material To Source", [this] { SaveMaterialToSource(); });
|
||||
action->setEnabled(HasMaterialSource());
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
action = menu.addAction("Open Source Material In Editor", [this] { OpenMaterialSourceInEditor(); });
|
||||
action->setEnabled(HasMaterialSource());
|
||||
|
||||
action = menu.addAction("Open Parent Material In Editor", [this] { OpenMaterialParentSourceInEditor(); });
|
||||
action->setEnabled(HasMaterialParentSource());
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
const EditorMaterialComponentUtil::MaterialEditData& MaterialPropertyInspector::GetEditData() const
|
||||
{
|
||||
return m_editData;
|
||||
@@ -501,7 +624,8 @@ namespace AZ
|
||||
{
|
||||
// For some reason the reflected property editor notifications are not symmetrical
|
||||
// This function is called continuously anytime a property changes until the edit has completed
|
||||
// Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has started and ended
|
||||
// Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has
|
||||
// started and ended
|
||||
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
|
||||
if (property)
|
||||
{
|
||||
@@ -521,15 +645,16 @@ namespace AZ
|
||||
{
|
||||
m_editData.m_materialPropertyOverrideMap[m_activeProperty->GetId()] = m_activeProperty->GetValue();
|
||||
UpdateMaterialInstanceProperty(*m_activeProperty);
|
||||
RunPropertyChangedCallback();
|
||||
SaveOverridesToEntity(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialPropertyInspector::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode)
|
||||
{
|
||||
// As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been completed but they are not being called following that pattern.
|
||||
// when this function executes the changes to the property are ready to be committed or reverted
|
||||
// As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been
|
||||
// completed but they are not being called following that pattern. when this function executes the changes to the property
|
||||
// are ready to be committed or reverted
|
||||
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
|
||||
if (property)
|
||||
{
|
||||
@@ -537,85 +662,92 @@ namespace AZ
|
||||
{
|
||||
m_editData.m_materialPropertyOverrideMap[m_activeProperty->GetId()] = m_activeProperty->GetValue();
|
||||
UpdateMaterialInstanceProperty(*m_activeProperty);
|
||||
RunPropertyChangedCallback();
|
||||
SaveOverridesToEntity(true);
|
||||
RunEditorMaterialFunctors();
|
||||
m_activeProperty = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenInspectorDialog(
|
||||
const AZStd::string& slotName, const AZ::Data::AssetId& assetId, MaterialPropertyOverrideMap propertyOverrideMap,
|
||||
PropertyChangedCallback propertyChangedCallback)
|
||||
void MaterialPropertyInspector::OnEntityInitialized(const AZ::EntityId& entityId)
|
||||
{
|
||||
QWidget* activeWindow = nullptr;
|
||||
AzToolsFramework::EditorWindowRequestBus::BroadcastResult(activeWindow, &AzToolsFramework::EditorWindowRequests::GetAppMainWindow);
|
||||
|
||||
// Constructing a dialog with a table to display all configurable material export items
|
||||
QDialog dialog(activeWindow);
|
||||
dialog.setWindowTitle("Material Inspector");
|
||||
|
||||
MaterialPropertyInspector* inspector = new MaterialPropertyInspector(slotName, assetId, propertyChangedCallback, &dialog);
|
||||
if (!inspector->LoadMaterial())
|
||||
if (m_entityId == entityId)
|
||||
{
|
||||
return false;
|
||||
UnloadMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
inspector->Populate();
|
||||
inspector->SetOverrides(propertyOverrideMap);
|
||||
void MaterialPropertyInspector::OnEntityDestroyed(const AZ::EntityId& entityId)
|
||||
{
|
||||
if (m_entityId == entityId)
|
||||
{
|
||||
UnloadMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
// Create the menu button
|
||||
QToolButton* menuButton = new QToolButton(&dialog);
|
||||
menuButton->setAutoRaise(true);
|
||||
menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg"));
|
||||
menuButton->setVisible(true);
|
||||
QObject::connect(menuButton, &QToolButton::clicked, &dialog, [&]() {
|
||||
QAction* action = nullptr;
|
||||
void MaterialPropertyInspector::OnEntityActivated(const AZ::EntityId& entityId)
|
||||
{
|
||||
if (m_entityId == entityId)
|
||||
{
|
||||
QueueUpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
QMenu menu(&dialog);
|
||||
action = menu.addAction("Clear Overrides", [&] { inspector->SetOverrides(MaterialPropertyOverrideMap()); });
|
||||
action = menu.addAction("Revert Changes", [&] { inspector->SetOverrides(propertyOverrideMap); });
|
||||
void MaterialPropertyInspector::OnEntityDeactivated(const AZ::EntityId& entityId)
|
||||
{
|
||||
if (m_entityId == entityId)
|
||||
{
|
||||
UnloadMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
action = menu.addAction("Save Material", [&] { inspector->SaveMaterial(); });
|
||||
action = menu.addAction("Save Material To Source", [&] { inspector->SaveMaterialToSource(); });
|
||||
action->setEnabled(inspector->HasMaterialSource());
|
||||
void MaterialPropertyInspector::OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name)
|
||||
{
|
||||
AZ_UNUSED(name);
|
||||
if (m_entityId == entityId)
|
||||
{
|
||||
QueueUpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
action = menu.addAction("Open Source Material In Editor", [&] { inspector->OpenMaterialSourceInEditor(); });
|
||||
action->setEnabled(inspector->HasMaterialSource());
|
||||
action = menu.addAction("Open Parent Material In Editor", [&] { inspector->OpenMaterialParentSourceInEditor(); });
|
||||
action->setEnabled(inspector->HasMaterialParentSource());
|
||||
menu.exec(QCursor::pos());
|
||||
});
|
||||
void MaterialPropertyInspector::OnTick(float deltaTime, ScriptTimePoint time)
|
||||
{
|
||||
AZ_UNUSED(time);
|
||||
AZ_UNUSED(deltaTime);
|
||||
UpdateUI();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(&dialog);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
|
||||
QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
|
||||
void MaterialPropertyInspector::OnMaterialsEdited()
|
||||
{
|
||||
if (!m_internalEditNotification)
|
||||
{
|
||||
QueueUpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
QObject::connect(&dialog, &QDialog::rejected, &dialog, [&] { inspector->SetOverrides(propertyOverrideMap); });
|
||||
void MaterialPropertyInspector::UpdateUI()
|
||||
{
|
||||
AZ::Data::AssetId assetId;
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
assetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId);
|
||||
|
||||
QVBoxLayout* dialogLayout = new QVBoxLayout(&dialog);
|
||||
dialogLayout->addWidget(menuButton);
|
||||
dialogLayout->addWidget(inspector);
|
||||
dialogLayout->addWidget(buttonBox);
|
||||
dialog.setLayout(dialogLayout);
|
||||
dialog.setModal(true);
|
||||
if (IsLoaded() && m_editData.m_materialAssetId == assetId)
|
||||
{
|
||||
LoadOverridesFromEntity();
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadMaterial(m_entityId, m_materialAssignmentId);
|
||||
}
|
||||
}
|
||||
|
||||
// Forcing the initial dialog size to accomodate typical content.
|
||||
// Temporarily settng fixed size because dialog.show/exec invokes WindowDecorationWrapper::showEvent.
|
||||
// This forces the dialog to be centered and sized based on the layout of content.
|
||||
// Resizing the dialog after show will not be centered and moving the dialog programatically doesn't m0ve the custmk frame.
|
||||
dialog.setFixedSize(500, 800);
|
||||
dialog.show();
|
||||
|
||||
// Removing fixed size to allow drag resizing
|
||||
dialog.setMinimumSize(0, 0);
|
||||
dialog.setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
|
||||
// Return true if the user press the export button
|
||||
return dialog.exec() == QDialog::Accepted;
|
||||
void MaterialPropertyInspector::QueueUpdateUI()
|
||||
{
|
||||
if (!AZ::TickBus::Handler::BusIsConnected())
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
}
|
||||
}
|
||||
} // namespace EditorMaterialComponentInspector
|
||||
} // namespace Render
|
||||
|
||||
Reference in New Issue
Block a user