From e8142fa403a7e932e361ef03bfcce5652051fa43 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Tue, 12 Oct 2021 13:19:38 -0500 Subject: [PATCH] Updated editor material component slots to support dynamic previews rendered with property overrides applied Fixed scaling issues with labels used for images Reduced updating previews and material component inspector to only apply after a value is committed Storing cache of previously rendered material previews Signed-off-by: Guthrie Adams --- ...orMaterialSystemComponentNotificationBus.h | 5 +- .../EditorMaterialSystemComponentRequestBus.h | 7 ++- .../Material/EditorMaterialComponent.cpp | 14 +++++ .../Source/Material/EditorMaterialComponent.h | 10 ++- .../EditorMaterialComponentInspector.cpp | 14 +++-- .../EditorMaterialComponentInspector.h | 2 - .../Material/EditorMaterialComponentSlot.cpp | 61 +++++++++++++------ .../Material/EditorMaterialComponentSlot.h | 38 ++++++++---- .../EditorMaterialSystemComponent.cpp | 29 +++++++++ .../Material/EditorMaterialSystemComponent.h | 12 +++- 10 files changed, 148 insertions(+), 44 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h index 4e655c7835..30762a60ba 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h @@ -10,7 +10,6 @@ #include #include #include -#include class QPixmap; @@ -18,11 +17,11 @@ namespace AZ { namespace Render { - //! EditorMaterialSystemComponentNotifications provides an interface to communicate with EditorMaterialSystemComponent + //! EditorMaterialSystemComponentNotifications is an interface for handling notifications from EditorMaterialSystemComponent, like + //! being informed that material preview images are available class EditorMaterialSystemComponentNotifications : public AZ::EBusTraits { public: - // Only a single handler is allowed static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h index 059e6dec8e..0bf93fc56f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h @@ -17,7 +17,8 @@ namespace AZ { namespace Render { - //! EditorMaterialSystemComponentRequests provides an interface to communicate with MaterialEditor + //! EditorMaterialSystemComponentRequests provides an interface for interacting with EditorMaterialSystemComponent, performing + //! different operations like opening the material editor, the material instance inspector, and managing material preview images class EditorMaterialSystemComponentRequests : public AZ::EBusTraits { public: @@ -35,6 +36,10 @@ namespace AZ //! Generate a material preview image virtual void RenderMaterialPreview( const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) = 0; + + //! Get recently rendered material preview image + virtual QPixmap GetRenderedMaterialPreview( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const = 0; }; using EditorMaterialSystemComponentRequestBus = AZ::EBus; } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp index 8e5e7e2345..a3ae61b14b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp @@ -148,11 +148,13 @@ namespace AZ BaseClass::Activate(); MaterialReceiverNotificationBus::Handler::BusConnect(GetEntityId()); MaterialComponentNotificationBus::Handler::BusConnect(GetEntityId()); + EditorMaterialSystemComponentNotificationBus::Handler::BusConnect(); UpdateMaterialSlots(); } void EditorMaterialComponent::Deactivate() { + EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect(); MaterialReceiverNotificationBus::Handler::BusDisconnect(); MaterialComponentNotificationBus::Handler::BusDisconnect(); BaseClass::Deactivate(); @@ -260,6 +262,18 @@ namespace AZ } } + void EditorMaterialComponent::OnRenderMaterialPreviewComplete( + [[maybe_unused]] const AZ::EntityId& entityId, + [[maybe_unused]] const AZ::Render::MaterialAssignmentId& materialAssignmentId, + [[maybe_unused]] const QPixmap& pixmap) + { + if (entityId == GetEntityId()) + { + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues); + } + } + AZ::u32 EditorMaterialComponent::OnConfigurationChanged() { return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h index f8895d994f..ce21ae82ac 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -21,8 +22,9 @@ namespace AZ //! In-editor material component for displaying and editing material assignments. class EditorMaterialComponent final : public EditorRenderComponentAdapter - , private MaterialReceiverNotificationBus::Handler - , private MaterialComponentNotificationBus::Handler + , public MaterialReceiverNotificationBus::Handler + , public MaterialComponentNotificationBus::Handler + , public EditorMaterialSystemComponentNotificationBus::Handler { public: using BaseClass = EditorRenderComponentAdapter; @@ -52,6 +54,10 @@ namespace AZ //! MaterialComponentNotificationBus::Handler overrides... void OnMaterialInstanceCreated(const MaterialAssignment& materialAssignment) override; + //! EditorMaterialSystemComponentNotificationBus::Handler overrides... + void OnRenderMaterialPreviewComplete( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap) override; + // Regenerates the editor component material slots based on the material and // LOD mapping from the model or other consumer of materials. // If any corresponding material assignments are found in the component diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 7562049031..9cf39483fe 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -170,6 +170,7 @@ namespace AZ m_overviewImage = new QLabel(this); m_overviewImage->setFixedSize(QSize(120, 120)); + m_overviewImage->setScaledContents(true); m_overviewImage->setVisible(false); m_overviewText = new QLabel(this); @@ -241,8 +242,14 @@ namespace AZ m_overviewText->setText(materialInfo); m_overviewText->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); + + QPixmap pixmap; + EditorMaterialSystemComponentRequestBus::BroadcastResult( + pixmap, &EditorMaterialSystemComponentRequestBus::Events::GetRenderedMaterialPreview, m_entityId, + m_materialAssignmentId); + m_overviewImage->setPixmap(pixmap); m_overviewImage->setVisible(true); - m_updatePreview = true; + m_updatePreview |= pixmap.isNull(); } void MaterialPropertyInspector::AddUvNamesGroup() @@ -400,7 +407,8 @@ namespace AZ m_internalEditNotification = false; } - m_updatePreview = true; + // m_updatePreview should be set to true here for continuous preview updates as slider/color properties change but needs + // throttling } void MaterialPropertyInspector::RunEditorMaterialFunctors() @@ -779,5 +787,3 @@ namespace AZ } // namespace EditorMaterialComponentInspector } // namespace Render } // namespace AZ - -//#include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h index 6199fba179..82f46ebc90 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h @@ -32,8 +32,6 @@ namespace AZ { namespace EditorMaterialComponentInspector { - using PropertyChangedCallback = AZStd::function; - class MaterialPropertyInspector : public AtomToolsFramework::InspectorWidget , public AzToolsFramework::IPropertyEditorNotify diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index 21d045a072..d65fd0f2af 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -6,23 +6,25 @@ * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include +#include +#include #include +#include +#include AZ_POP_DISABLE_WARNING namespace AZ @@ -100,6 +102,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &EditorMaterialComponentSlot::GetLabel) ->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true) ->Attribute("ThumbnailCallback", &EditorMaterialComponentSlot::OpenPopupMenu) + ->Attribute("ThumbnailIcon", &EditorMaterialComponentSlot::GetPreviewPixmapData) ; } } @@ -118,6 +121,33 @@ namespace AZ } }; + AZStd::vector EditorMaterialComponentSlot::GetPreviewPixmapData() const + { + if (!GetActiveAssetId().IsValid()) + { + return {}; + } + + QPixmap pixmap; + EditorMaterialSystemComponentRequestBus::BroadcastResult( + pixmap, &EditorMaterialSystemComponentRequestBus::Events::GetRenderedMaterialPreview, m_entityId, m_id); + if (pixmap.isNull()) + { + if (m_updatePreview) + { + EditorMaterialSystemComponentRequestBus::Broadcast( + &EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_id); + m_updatePreview = false; + } + return {}; + } + + QByteArray pixmapBytes; + QDataStream stream(&pixmapBytes, QIODevice::WriteOnly); + stream << pixmap; + return AZStd::vector(pixmapBytes.begin(), pixmapBytes.end()); + } + AZ::Data::AssetId EditorMaterialComponentSlot::GetActiveAssetId() const { return m_materialAsset.GetId().IsValid() ? m_materialAsset.GetId() : GetDefaultAssetId(); @@ -169,14 +199,6 @@ namespace AZ ClearOverrides(); } - void EditorMaterialComponentSlot::ClearToDefaultAsset() - { - m_materialAsset = AZ::Data::Asset(GetDefaultAssetId(), AZ::AzTypeInfo::Uuid()); - MaterialComponentRequestBus::Event( - m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); - ClearOverrides(); - } - void EditorMaterialComponentSlot::ClearOverrides() { MaterialComponentRequestBus::Event( @@ -317,6 +339,7 @@ namespace AZ EditorMaterialSystemComponentRequestBus::Broadcast( &EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_id); + m_updatePreview = false; MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h index 377fe9a18b..78fc7449de 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h @@ -8,37 +8,52 @@ #pragma once -#include -#include - -#include -#include #include +#include +#include +#include +#include +#include namespace AZ { namespace Render { - static const size_t DefaultMaterialSlotIndex = std::numeric_limits::max(); - //! Details for a single editable material assignment struct EditorMaterialComponentSlot final { AZ_RTTI(EditorMaterialComponentSlot, "{344066EB-7C3D-4E92-B53D-3C9EBD546488}"); AZ_CLASS_ALLOCATOR(EditorMaterialComponentSlot, SystemAllocator, 0); - static void Reflect(ReflectContext* context); static bool ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); + static void Reflect(ReflectContext* context); + //! Get cached preview image as a buffer to use as an RPE attribute + //! If a cached image isn't avalible then a request will be made to render one + AZStd::vector GetPreviewPixmapData() const; + + //! Returns the overridden asset id if it's valid, otherwise gets the default asseet id AZ::Data::AssetId GetActiveAssetId() const; + + //! Returns the default asseet id of the material provded by the model AZ::Data::AssetId GetDefaultAssetId() const; + + //! Returns the display name of the material slot AZStd::string GetLabel() const; + + //! Returns true if the active material asset has a source material bool HasSourceData() const; + //! Assign a new material override asset void SetAsset(const Data::AssetId& assetId); + + //! Assign a new material override asset void SetAsset(const Data::Asset& asset); + + //! Remove material and prperty overrides void Clear(); - void ClearToDefaultAsset(); + + //! Remove prperty overrides void ClearOverrides(); void OpenMaterialExporter(); @@ -54,6 +69,7 @@ namespace AZ void OpenPopupMenu(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType); void OnMaterialChanged() const; void OnDataChanged() const; + mutable bool m_updatePreview = true; }; // Vector of slots for assignable or overridable material data. @@ -62,8 +78,8 @@ namespace AZ // Table containing all editable material data that is displayed in the edit context and inspector // The vector represents all the LODs that can have material overrides. // The container will be populated with every potential material slot on an associated model, using its default values. - // Whenever changes are made to this container, the modified values are copied into the controller configuration material assignment map - // as overrides that will be applied to material instances + // Whenever changes are made to this container, the modified values are copied into the controller configuration material assignment + // map as overrides that will be applied to material instances using EditorMaterialComponentSlotsByLodContainer = AZStd::vector; } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp index 96a8e3e8d4..0939c8548b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp @@ -88,6 +88,7 @@ namespace AZ void EditorMaterialSystemComponent::Activate() { + EditorMaterialSystemComponentNotificationBus::Handler::BusConnect(); EditorMaterialSystemComponentRequestBus::Handler::BusConnect(); AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusConnect(); AzToolsFramework::EditorMenuNotificationBus::Handler::BusConnect(); @@ -100,6 +101,7 @@ namespace AZ { AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect(); EditorMaterialSystemComponentRequestBus::Handler::BusDisconnect(); AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorMenuNotificationBus::Handler::BusDisconnect(); @@ -167,6 +169,10 @@ namespace AZ { MaterialComponentRequestBus::EventResult( materialAssetId, entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, materialAssignmentId); + if (!materialAssetId.IsValid()) + { + return; + } } AZ::Render::MaterialPropertyOverrideMap propertyOverrides; @@ -193,6 +199,29 @@ namespace AZ } } + QPixmap EditorMaterialSystemComponent::GetRenderedMaterialPreview( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const + { + const auto& itr1 = m_materialPreviews.find(entityId); + if (itr1 != m_materialPreviews.end()) + { + const auto& itr2 = itr1->second.find(materialAssignmentId); + if (itr2 != itr1->second.end()) + { + return itr2->second; + } + } + return QPixmap(); + } + + void EditorMaterialSystemComponent::OnRenderMaterialPreviewComplete( + [[maybe_unused]] const AZ::EntityId& entityId, + [[maybe_unused]] const AZ::Render::MaterialAssignmentId& materialAssignmentId, + [[maybe_unused]] const QPixmap& pixmap) + { + m_materialPreviews[entityId][materialAssignmentId] = pixmap; + } + void EditorMaterialSystemComponent::OnPopulateToolMenuItems() { if (!m_openMaterialEditorAction) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h index f2ccbdc435..e62c9b64dc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace AZ { @@ -24,6 +25,7 @@ namespace AZ //! System component that manages launching and maintaining connections with the material editor. class EditorMaterialSystemComponent final : public AZ::Component + , public EditorMaterialSystemComponentNotificationBus::Handler , public EditorMaterialSystemComponentRequestBus::Handler , public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler , public AzToolsFramework::EditorMenuNotificationBus::Handler @@ -50,8 +52,13 @@ namespace AZ //! EditorMaterialSystemComponentRequestBus::Handler overrides... void OpenMaterialEditor(const AZStd::string& sourcePath) override; void OpenMaterialInspector(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override; - void RenderMaterialPreview( - const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override; + void RenderMaterialPreview(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override; + QPixmap GetRenderedMaterialPreview( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const override; + + //! EditorMaterialSystemComponentNotificationBus::Handler overrides... + void OnRenderMaterialPreviewComplete( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap)override; //! AssetBrowserInteractionNotificationBus::Handler overrides... AzToolsFramework::AssetBrowser::SourceFileDetails GetSourceFileDetails(const char* fullSourceFileName) override; @@ -73,6 +80,7 @@ namespace AZ QAction* m_openMaterialEditorAction = nullptr; AZStd::unique_ptr m_materialBrowserInteractions; AZStd::unique_ptr m_previewRenderer; + AZStd::unordered_map> m_materialPreviews; }; } // namespace Render } // namespace AZ