123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <Atom/RPI.Edit/Common/AssetUtils.h>
- #include <Atom/RPI.Edit/Common/JsonUtils.h>
- #include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
- #include <Atom/RPI.Edit/Material/MaterialSourceData.h>
- #include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
- #include <Atom/RPI.Edit/Material/MaterialUtils.h>
- #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
- #include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
- #include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
- #include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
- #include <AtomToolsFramework/Util/Util.h>
- #include <AzFramework/API/ApplicationAPI.h>
- #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
- #include <AzToolsFramework/API/EditorWindowRequestBus.h>
- #include <AzToolsFramework/API/ToolsApplicationAPI.h>
- #include <Material/EditorMaterialModelUvNameMapInspector.h>
- AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
- #include <QApplication>
- #include <QDialog>
- #include <QDialogButtonBox>
- #include <QHBoxLayout>
- #include <QMenu>
- #include <QPushButton>
- #include <QToolButton>
- #include <QVBoxLayout>
- AZ_POP_DISABLE_WARNING
- namespace AZ
- {
- namespace Render
- {
- namespace EditorMaterialComponentInspector
- {
- MaterialModelUvNameMapInspector::MaterialModelUvNameMapInspector(
- const AZ::Data::AssetId& assetId,
- const RPI::MaterialModelUvOverrideMap& matModUvOverrides,
- const AZStd::unordered_set<AZ::Name>& modelUvNames,
- MaterialModelUvOverrideMapChangedCallBack matModUvOverrideMapChangedCallBack,
- QWidget* parent)
- : AtomToolsFramework::InspectorWidget(parent)
- , m_matModUvOverrideMapChangedCallBack(matModUvOverrideMapChangedCallBack)
- , m_matModUvOverrides(matModUvOverrides)
- {
- // Load the originating product asset from which the new source has set will be generated
- auto materialAssetOutcome = AZ::RPI::AssetUtils::LoadAsset<AZ::RPI::MaterialAsset>(assetId);
- AZ_Error(
- "AZ::Render::EditorMaterialComponentInspector", materialAssetOutcome, "Failed to load material asset: %s",
- assetId.ToString<AZStd::string>().c_str());
- auto materialAsset = materialAssetOutcome.GetValue();
- // Get material UV names
- m_materialUvNames = materialAsset->GetMaterialTypeAsset()->GetUvNameMap();
- SetModelUvNames(modelUvNames);
- ResetModelUvNameIndices();
- }
- MaterialModelUvNameMapInspector::~MaterialModelUvNameMapInspector()
- {
- }
- void MaterialModelUvNameMapInspector::Reset()
- {
- m_activeProperty = {};
- m_group = {};
- AtomToolsFramework::InspectorWidget::Reset();
- }
- void MaterialModelUvNameMapInspector::Populate()
- {
- AddGroupsBegin();
- const AZStd::string groupName = "ModelUvMap";
- const AZStd::string groupDisplayName = "Material to Model UV Map";
- const AZStd::string groupDescription = "Custom map that maps a UV name from the material to one from the model.";
- const size_t uvSize = m_materialUvNames.size();
- m_group.m_properties.reserve(uvSize);
- for (size_t i = 0; i < uvSize; ++i)
- {
- AtomToolsFramework::DynamicPropertyConfig propertyConfig;
- const AZStd::string shaderInput = m_materialUvNames[i].m_shaderInput.ToString();
- const AZStd::string materialUvName = m_materialUvNames[i].m_uvName.GetStringView();
- propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupName, shaderInput);
- propertyConfig.m_name = shaderInput;
- propertyConfig.m_displayName = materialUvName;
- propertyConfig.m_description = shaderInput;
- propertyConfig.m_defaultValue = 0u;
- propertyConfig.m_originalValue = 0u;
- propertyConfig.m_parentValue = 0u;
- propertyConfig.m_enumValues = m_modelUvNames;
- m_group.m_properties.emplace_back(propertyConfig);
- m_group.m_properties.back().SetValue(AZStd::any(m_modelUvNameIndices[i]));
- }
- AddGroup(groupName, groupDisplayName, groupDescription,
- new AtomToolsFramework::InspectorPropertyGroupWidget(&m_group, nullptr, m_group.TYPEINFO_Uuid(), this));
- AddGroupsEnd();
- }
- void MaterialModelUvNameMapInspector::BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode)
- {
- // 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
- const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
- if (property && m_activeProperty != property)
- {
- m_activeProperty = property;
- }
- }
- void MaterialModelUvNameMapInspector::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
- {
- const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
- if (property && m_activeProperty == property)
- {
- uint32_t index = 0;
- while (&(m_group.m_properties[index]) != property)
- {
- ++index;
- }
- AZ_Assert(index < m_group.m_properties.size(), "The property doesn't exist in the group.");
- uint32_t modelUvIndex = AZStd::any_cast<uint32_t>(m_activeProperty->GetValue());
- if (modelUvIndex == 0u)
- {
- m_matModUvOverrides[m_materialUvNames[index].m_shaderInput] = Name();
- }
- else
- {
- m_matModUvOverrides[m_materialUvNames[index].m_shaderInput] = Name(m_modelUvNames[modelUvIndex]);
- }
- if (m_matModUvOverrideMapChangedCallBack)
- {
- m_matModUvOverrideMapChangedCallBack(m_matModUvOverrides);
- }
- }
- }
- void MaterialModelUvNameMapInspector::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
- const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
- if (property && m_activeProperty == property)
- {
- uint32_t index = 0;
- while (&(m_group.m_properties[index]) != property)
- {
- ++index;
- }
- AZ_Assert(index < m_group.m_properties.size(), "The property doesn't exist in the group.");
- uint32_t modelUvIndex = AZStd::any_cast<uint32_t>(m_activeProperty->GetValue());
- if (modelUvIndex == 0u)
- {
- m_matModUvOverrides[m_materialUvNames[index].m_shaderInput] = Name();
- }
- else
- {
- m_matModUvOverrides[m_materialUvNames[index].m_shaderInput] = Name(m_modelUvNames[modelUvIndex]);
- }
- if (m_matModUvOverrideMapChangedCallBack)
- {
- m_matModUvOverrideMapChangedCallBack(m_matModUvOverrides);
- }
- m_activeProperty = nullptr;
- }
- }
- void MaterialModelUvNameMapInspector::ResetModelUvNameIndices()
- {
- m_modelUvNameIndices.clear();
- const uint32_t uvSize = aznumeric_cast<uint32_t>(m_materialUvNames.size());
- m_modelUvNameIndices.resize(uvSize, 0u);
- AZStd::unordered_map<AZ::Name, uint32_t> tempModelUvIndexLookup;
- uint32_t index = 0u;
- for (const AZStd::string& modelUvName : m_modelUvNames)
- {
- tempModelUvIndexLookup[AZ::Name(modelUvName)] = index++;
- }
- index = 0u;
- for (const auto& materialUvNamePair : m_materialUvNames)
- {
- const auto overrideIter = m_matModUvOverrides.find(materialUvNamePair.m_shaderInput);
- if (overrideIter != m_matModUvOverrides.end())
- {
- const auto modelUvIndexIter = tempModelUvIndexLookup.find(overrideIter->second);
- if (modelUvIndexIter != tempModelUvIndexLookup.end())
- {
- m_modelUvNameIndices[index++] = modelUvIndexIter->second;
- }
- }
- }
- }
- void MaterialModelUvNameMapInspector::SetModelUvNames(const AZStd::unordered_set<AZ::Name>& modelUvNames)
- {
- static constexpr const char DefaultModelUvName[] = "[Same as in the material]";
- m_modelUvNames.clear();
- // Plus the default name
- m_modelUvNames.reserve(modelUvNames.size() + 1u);
- m_modelUvNames.push_back(DefaultModelUvName);
- for (const AZ::Name& modelUvName : modelUvNames)
- {
- m_modelUvNames.push_back(modelUvName.GetStringView());
- }
- }
- void MaterialModelUvNameMapInspector::SetUvNameMap(const RPI::MaterialModelUvOverrideMap& matModUvOverrides)
- {
- m_matModUvOverrides = matModUvOverrides;
- ResetModelUvNameIndices();
- const AZStd::string groupName = "ModelUvMap";
- size_t uvSize = m_materialUvNames.size();
- for (size_t i = 0u; i < uvSize; ++i)
- {
- AtomToolsFramework::DynamicPropertyConfig propertyConfig;
- const AZStd::string shaderInput = m_materialUvNames[i].m_shaderInput.ToString();
- const AZStd::string materialUvName = m_materialUvNames[i].m_uvName.GetStringView();
- propertyConfig.m_id = AZ::RPI::MaterialPropertyId(groupName, shaderInput);
- propertyConfig.m_name = shaderInput;
- propertyConfig.m_displayName = materialUvName;
- propertyConfig.m_description = shaderInput;
- propertyConfig.m_defaultValue = 0u;
- propertyConfig.m_originalValue = 0u;
- propertyConfig.m_parentValue = 0u;
- propertyConfig.m_enumValues = m_modelUvNames;
- m_group.m_properties[i].SetConfig(propertyConfig);
- m_group.m_properties[i].SetValue(AZStd::any(m_modelUvNameIndices[i]));
- }
- if (m_matModUvOverrideMapChangedCallBack)
- {
- m_matModUvOverrideMapChangedCallBack(matModUvOverrides);
- }
- RebuildAll();
- }
- bool OpenInspectorDialog(
- const AZ::Data::AssetId& assetId,
- const RPI::MaterialModelUvOverrideMap& matModUvOverrides,
- const AZStd::unordered_set<AZ::Name>& modelUvNames,
- MaterialModelUvOverrideMapChangedCallBack matModUvOverrideMapChangedCallBack)
- {
- 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");
- MaterialModelUvNameMapInspector* inspector = new MaterialModelUvNameMapInspector(assetId, matModUvOverrides, modelUvNames, matModUvOverrideMapChangedCallBack, &dialog);
- inspector->Populate();
- // 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, [&]()
- {
- QMenu menu(&dialog);
- menu.addAction(
- "Clear", [&]
- {
- inspector->SetUvNameMap(RPI::MaterialModelUvOverrideMap());
- });
- menu.addAction(
- "Revert", [&]
- {
- inspector->SetUvNameMap(matModUvOverrides);
- });
- menu.exec(QCursor::pos());
- });
- 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);
- QVBoxLayout* dialogLayout = new QVBoxLayout(&dialog);
- dialogLayout->addWidget(menuButton);
- dialogLayout->addWidget(inspector);
- dialogLayout->addWidget(buttonBox);
- dialog.setLayout(dialogLayout);
- dialog.setModal(true);
- // 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(300, 300);
- 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;
- }
- } // namespace EditorMaterialComponentInspector
- } // namespace Render
- } // namespace AZ
|