From d1863c6c5b6e6fad5db498652110a0183919f96a Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 26 May 2021 16:36:54 -0700 Subject: [PATCH] Restore Editor viewport icon rendering (#879) This introduces an EditorViewportIconDisplayInterface that will eventually be used to outright remove CIconManager, it provides a simple interface for loading 2D image assets and rendering them on-screen. It also introduces AtomBridge::PerViewportDynamicDraw for getting a dynamic draw instance on a per-viewport basis --- .../API/EditorViewportIconDisplayInterface.h | 80 +++++ .../EditorEntityIconComponent.cpp | 4 +- .../ViewportSelection/EditorHelpers.cpp | 13 +- .../aztoolsframework_files.cmake | 1 + .../DynamicDraw/DynamicDrawContext.h | 8 + .../Include/Atom/RPI.Public/ViewportContext.h | 25 +- .../DynamicDraw/DynamicDrawContext.cpp | 36 ++ .../Source/RPI.Public/ViewportContext.cpp | 19 + .../AtomBridge/Code/CMakeLists.txt | 1 + .../PerViewportDynamicDrawInterface.h | 44 +++ .../Code/Source/AtomBridgeSystemComponent.cpp | 3 + .../Code/Source/AtomBridgeSystemComponent.h | 2 + .../Source/PerViewportDynamicDrawManager.cpp | 119 ++++++ .../Source/PerViewportDynamicDrawManager.h | 48 +++ .../AtomBridge/Code/atombridge_files.cmake | 7 +- .../Assets/Shaders/TexturedIcon.azsl | 77 ++++ .../Assets/Shaders/TexturedIcon.shader | 39 ++ .../AtomViewportDisplayIcons/CMakeLists.txt | 12 + .../Code/CMakeLists.txt | 35 ++ ...tomViewportDisplayIconsSystemComponent.cpp | 339 ++++++++++++++++++ .../AtomViewportDisplayIconsSystemComponent.h | 82 +++++ .../Code/Source/Module.cpp | 51 +++ .../Code/atomviewportdisplayicons_files.cmake | 16 + .../AtomViewportDisplayInfo/gem.json | 12 - Gems/AtomLyIntegration/CMakeLists.txt | 1 + 25 files changed, 1050 insertions(+), 24 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h create mode 100644 Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h create mode 100644 Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp create mode 100644 Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake delete mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h new file mode 100644 index 0000000000..a2264b5058 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h @@ -0,0 +1,80 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include + +#include + +namespace AzToolsFramework +{ + //! An interface for loading simple icon assets and rendering them to screen on a per-viewport basis. + class EditorViewportIconDisplayInterface + { + public: + AZ_RTTI(EditorViewportIconDisplayInterface, "{D5190B58-2561-4F3F-B793-F1E7D454CDF2}"); + + using IconId = AZ::s32; + static constexpr IconId InvalidIconId = -1; + + enum class CoordinateSpace : AZ::u8 + { + ScreenSpace, + WorldSpace + }; + + //! These draw parameters control rendering for a single icon to a single viewport. + struct DrawParameters + { + //! The ViewportId to render to. + AzFramework::ViewportId m_viewport = AzFramework::InvalidViewportId; + //! The icon ID, retrieved from GetOrLoadIconForPath, to render to screen. + IconId m_icon = InvalidIconId; + //! The color, including opacity, to render the icon with. White will render the icon as opaque in its original color. + AZ::Color m_color = AZ::Colors::White; + //! The position to render the icon to, in world or screen space depending on m_positionSpace. + AZ::Vector3 m_position; + //! The coordinate system to use for m_position. + //! ScreenSpace will accept m_position in the form of [X, Y, Depth], where X & Y are screen coordinates in + //! pixels and Depth is a z-ordering depth value from 0.0f to 1.0f. + //! WorldSpace will accept a 3D vector in world space coordinates that will be translated back into screen + //! space when the icon is rendered. + CoordinateSpace m_positionSpace = CoordinateSpace::ScreenSpace; + //! The size to render the icon as, in pixels. + AZ::Vector2 m_size; + }; + + //! The current load status of an icon retrieved by GetOrLoadIconForPath. + enum class IconLoadStatus : AZ::u8 + { + Unloaded, + Loading, + Loaded, + Error + }; + + //! Draws an icon to a viewport given a set of draw parameters. + //! Requires an IconId retrieved from GetOrLoadIconForPath. + virtual void DrawIcon(const DrawParameters& drawParameters) = 0; + //! Retrieves a reusable IconId for an icon at a given path. + //! This will load the icon, if it has not already been loaded. + //! @param path should be a relative asset path to an icon image asset. + //! png and svg icons are currently supported. + virtual IconId GetOrLoadIconForPath(AZStd::string_view path) = 0; + //! Gets the current load status of an icon retrieved via GetOrLoadIconForPath. + virtual IconLoadStatus GetIconLoadStatus(IconId icon) = 0; + }; + + using EditorViewportIconDisplay = AZ::Interface; +} //namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp index 48a7a37487..c46bb158c6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -313,8 +314,7 @@ namespace AzToolsFramework // if we do not yet have a valid texture id, request it using the entity icon path if (m_entityIconTextureId == 0) { - EditorRequestBus::BroadcastResult( - m_entityIconTextureId, &EditorRequests::GetIconTextureIdFromEntityIconPath, m_entityIconPath); + m_entityIconTextureId = EditorViewportIconDisplay::Get()->GetOrLoadIconForPath(m_entityIconPath); } return m_entityIconTextureId; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 5c62a2997b..31da01fadc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -21,6 +21,7 @@ #include #include #include +#include AZ_CVAR( bool, ed_visibility_showAggregateEntitySelectionBounds, false, nullptr, AZ::ConsoleFunctorFlags::Null, @@ -232,10 +233,14 @@ namespace AzToolsFramework return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f); }(); - debugDisplay.SetColor(iconHighlight); - // debugDisplay.DrawTextureLabel( - // iconTextureId, entityPosition, iconSize, iconSize, - // /*DisplayContext::ETextureIconFlags::TEXICON_ON_TOP=*/ 0x0008); + EditorViewportIconDisplay::Get()->DrawIcon({ + viewportInfo.m_viewportId, + iconTextureId, + iconHighlight, + entityPosition, + EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace, + AZ::Vector2{iconSize, iconSize} + }); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 45f52704bf..aaf5c86d33 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -46,6 +46,7 @@ set(FILES API/EditorWindowRequestBus.h API/EntityCompositionRequestBus.h API/EntityCompositionNotificationBus.h + API/EditorViewportIconDisplayInterface.h API/ViewPaneOptions.h Application/Ticker.h Application/Ticker.cpp diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h index e4b9b91e81..2dd0865688 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h @@ -87,6 +87,14 @@ namespace AZ //! Finalize and validate initialization. Any initialization functions should be called before EndInit is called. void EndInit(); + //! Set up the DynamicDrawContext for the input Scene. + //! This should be called after the last frame is done and before any draw calls. + void SetScene(Scene* scene); + + //! Set up the DynamicDrawContext for the input RenderPipeline. + //! This should be called after the last frame is done and before any draw calls. + void SetRenderPipeline(RenderPipeline* pipeline); + //! Return if this DynamicDrawContext is ready to add draw calls bool IsReady(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h index 4f24a20f35..8075c6fc5d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h @@ -26,9 +26,9 @@ namespace AZ class ViewportContextManager; //! ViewportContext wraps a native window and represents a minimal viewport - //! in which a scene is rendered on-screen + //! in which a scene is rendered on-screen. //! ViewportContexts are registered on creation to allow consumers to listen to notifications - //! and manage the view stack for a given viewport + //! and manage the view stack for a given viewport. class ViewportContext : public SceneNotificationBus::Handler , public AzFramework::WindowNotificationBus::Handler @@ -61,11 +61,11 @@ namespace AZ //! Gets the current name of this ViewportContext. //! This name is used to tie this ViewportContext to its View stack, and ViewportContexts may be - //! renamed via AZ::Interface::Get()->RenameViewportContext. + //! renamed via AZ::RPI::ViewportContextRequests::Get()->RenameViewportContext(...). AZ::Name GetName() const; //! Gets the default view associated with this ViewportContext. - //! Alternatively, use AZ::Interface::Get()->GetCurrentView. + //! Alternatively, use AZ::RPI::ViewportContextRequests::Get()->GetCurrentView(). ViewPtr GetDefaultView(); ConstViewPtr GetDefaultView() const; @@ -99,6 +99,18 @@ namespace AZ //! Notifies consumers when the render scene has changed. void ConnectSceneChangedHandler(SceneChangedEvent::Handler& handler); + using PipelineChangedEvent = AZ::Event; + //! Notifies consumers when the current pipeline associated with our window has changed. + void ConnectCurrentPipelineChangedHandler(PipelineChangedEvent::Handler& handler); + + using ViewChangedEvent = AZ::Event; + //! Notifies consumers when the default view has changed. + void ConnectDefaultViewChangedHandler(ViewChangedEvent::Handler& handler); + + using ViewportIdEvent = AZ::Event; + //! Notifies consumers when this ViewportContext is about to be destroyed. + void ConnectAboutToBeDestroyedHandler(ViewportIdEvent::Handler& handler); + // ViewportRequestBus interface //! Gets the current camera's view matrix. const AZ::Matrix4x4& GetCameraViewMatrix() const override; @@ -123,12 +135,17 @@ namespace AZ WindowContextSharedPtr m_windowContext; ViewPtr m_defaultView; AzFramework::WindowSize m_viewportSize; + SizeChangedEvent m_sizeChangedEvent; MatrixChangedEvent m_viewMatrixChangedEvent; MatrixChangedEvent::Handler m_onViewMatrixChangedHandler; MatrixChangedEvent m_projectionMatrixChangedEvent; MatrixChangedEvent::Handler m_onProjectionMatrixChangedHandler; SceneChangedEvent m_sceneChangedEvent; + PipelineChangedEvent m_currentPipelineChangedEvent; + ViewChangedEvent m_defaultViewChangedEvent; + ViewportIdEvent m_aboutToBeDestroyedEvent; + ViewportContextManager* m_manager; RenderPipelinePtr m_currentPipeline; Name m_name; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp index b7ba7d4a32..4e4a7a5e71 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp @@ -211,6 +211,42 @@ namespace AZ m_rhiPipelineState = m_pipelineState->GetRHIPipelineState(); } + void DynamicDrawContext::SetScene(Scene* scene) + { + AZ_Assert(scene, "SetScene called with an invalid scene"); + if (!scene || m_scene == scene) + { + return; + } + m_scene = scene; + m_drawFilter = RHI::DrawFilterMaskDefaultValue; + // Reinitialize if it was initialized + if (m_initialized) + { + // Report warning if there were some draw data + AZ_Warning( + "DynamicDrawContext", m_cachedDrawItems.size() == 0, + "DynamicDrawContext::SetForScene should be called" + " when there is no cached draw data"); + // Clear some cached data + FrameEnd(); + m_cachedRhiPipelineStates.clear(); + // Reinitialize + EndInit(); + } + } + + void DynamicDrawContext::SetRenderPipeline(RenderPipeline* pipeline) + { + AZ_Assert(pipeline, "SetRenderPipeline called with an invalid pipeline"); + if (!pipeline) + { + return; + } + SetScene(pipeline->GetScene()); + m_drawFilter = pipeline->GetDrawFilterMask(); + } + bool DynamicDrawContext::IsReady() { return m_initialized; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp index 22287238e9..08f8ff0c5e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp @@ -51,6 +51,8 @@ namespace AZ ViewportContext::~ViewportContext() { + m_aboutToBeDestroyedEvent.Signal(m_id); + AzFramework::WindowNotificationBus::Handler::BusDisconnect(); AzFramework::ViewportRequestBus::Handler::BusDisconnect(); @@ -171,6 +173,21 @@ namespace AZ handler.Connect(m_sceneChangedEvent); } + void ViewportContext::ConnectCurrentPipelineChangedHandler(PipelineChangedEvent::Handler& handler) + { + handler.Connect(m_currentPipelineChangedEvent); + } + + void ViewportContext::ConnectDefaultViewChangedHandler(ViewChangedEvent::Handler& handler) + { + handler.Connect(m_defaultViewChangedEvent); + } + + void ViewportContext::ConnectAboutToBeDestroyedHandler(ViewportIdEvent::Handler& handler) + { + handler.Connect(m_aboutToBeDestroyedEvent); + } + const AZ::Matrix4x4& ViewportContext::GetCameraViewMatrix() const { return GetDefaultView()->GetWorldToViewMatrix(); @@ -214,6 +231,7 @@ namespace AZ m_defaultView = view; UpdatePipelineView(); + m_defaultViewChangedEvent.Signal(view); m_viewMatrixChangedEvent.Signal(view->GetWorldToViewMatrix()); m_projectionMatrixChangedEvent.Signal(view->GetViewToClipMatrix()); @@ -232,6 +250,7 @@ namespace AZ if (!m_currentPipeline) { m_currentPipeline = m_rootScene ? m_rootScene->FindRenderPipelineForWindow(m_windowContext->GetWindowHandle()) : nullptr; + m_currentPipelineChangedEvent.Signal(m_currentPipeline); } if (auto pipeline = GetCurrentPipeline()) diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt index c431746b40..0723ca46b7 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt @@ -106,5 +106,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AtomFont Gem::AtomToolsFramework.Editor Gem::AtomViewportDisplayInfo + Gem::AtomViewportDisplayIcons.Editor ) endif() diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h b/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h new file mode 100644 index 0000000000..f77e0b0b88 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h @@ -0,0 +1,44 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ::AtomBridge +{ + //! A simple interface for allocating a DynamicDrawContext on-demand for every viewport, based on + //! a registered initialization function. + class PerViewportDynamicDrawInterface + { + public: + AZ_RTTI(PerViewportDynamicDrawInterface, "{1FF054F5-55FF-4ADB-A86D-640B15FA0395}"); + + using DrawContextFactory = AZStd::function)>; + //! Register a named dynamic draw context that can be retrieved on a per-viewport basis. + //! GetNamedDynamicDraw context can be called on a registered context name to retrieve a + //! valid DynamicDrawContext for a given viewport. + virtual void RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) = 0; + + //! Unregister a previously registered named per-viewport dynamic draw context. + //! This will dispose of all dynamic draw contexts currently associated with this name. + virtual void UnregisterDynamicDrawContext(AZ::Name name) = 0; + + //! Get a dynamic draw context associated with the specified viewport based on a factory registered with + //! RegisterNamedDynamicDrawContext. This dynamic draw context will be created if it does not already exist. + virtual RHI::Ptr GetDynamicDrawContextForViewport(AZ::Name name, AzFramework::ViewportId viewportId) = 0; + }; + + using PerViewportDynamicDraw = AZ::Interface; +} // namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp index 4a19c08174..5116d8a228 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -104,10 +105,12 @@ namespace AZ AzFramework::GameEntityContextRequestBus::BroadcastResult(m_entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId); AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect(); + m_dynamicDrawManager = AZStd::make_unique(); } void AtomBridgeSystemComponent::Deactivate() { + m_dynamicDrawManager.reset(); AZ::RPI::ViewportContextManagerNotificationsBus::Handler::BusDisconnect(); RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); // Check if scene is emptry since scene might be released already when running AtomSampleViewer diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h index da75d3c35c..4d05ba9285 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h @@ -33,6 +33,7 @@ namespace AZ { // forward declares class AtomDebugDisplayViewportInterface; + class PerViewportDynamicDrawManager; class AtomBridgeSystemComponent : public Component @@ -82,6 +83,7 @@ namespace AZ RPI::ViewPtr m_view = nullptr; AZStd::unordered_map > m_activeViewportsList; + AZStd::unique_ptr m_dynamicDrawManager; }; } } // namespace AZ diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp new file mode 100644 index 0000000000..b6ff116a86 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp @@ -0,0 +1,119 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "PerViewportDynamicDrawManager.h" + +#include +#include + +namespace AZ::AtomBridge +{ + PerViewportDynamicDrawManager::PerViewportDynamicDrawManager() + { + PerViewportDynamicDraw::Register(this); + } + + PerViewportDynamicDrawManager::~PerViewportDynamicDrawManager() + { + PerViewportDynamicDraw::Unregister(this); + } + + void PerViewportDynamicDrawManager::RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + const bool alreadyRegistered = m_registeredDrawContexts.find(name) != m_registeredDrawContexts.end(); + AZ_Error("AtomBridge", !alreadyRegistered, "Attempted to call RegisterDynamicDrawContext for already registered name: \"%s\"", name.GetCStr()); + if (alreadyRegistered) + { + return; + } + m_registeredDrawContexts[name] = contextInitializer; + } + + void PerViewportDynamicDrawManager::UnregisterDynamicDrawContext(AZ::Name name) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + auto drawContextFactoryIt = m_registeredDrawContexts.find(name); + const bool registered = drawContextFactoryIt != m_registeredDrawContexts.end(); + AZ_Error("AtomBridge", registered, "Attempted to call UnregisterDynamicDrawContext for unregistered name: \"%s\"", name.GetCStr()); + if (!registered) + { + return; + } + m_registeredDrawContexts.erase(drawContextFactoryIt); + + for (auto& viewportData : m_viewportData) + { + viewportData.second.m_dynamicDrawContexts.erase(name); + } + } + + RHI::Ptr PerViewportDynamicDrawManager::GetDynamicDrawContextForViewport( + AZ::Name name, AzFramework::ViewportId viewportId) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + auto contextFactoryIt = m_registeredDrawContexts.find(name); + if (contextFactoryIt == m_registeredDrawContexts.end()) + { + return nullptr; + } + + auto viewportContextManager = RPI::ViewportContextRequests::Get(); + RPI::ViewportContextPtr viewportContext = viewportContextManager->GetViewportContextById(viewportId); + if (viewportContext == nullptr) + { + return nullptr; + } + + // Get or create a ViewportData if one doesn't already exist + ViewportData& viewportData = m_viewportData[viewportId]; + if (!viewportData.m_initialized) + { + viewportData.m_pipelineChangedHandler = AZ::Event::Handler([this, viewportId](RPI::RenderPipelinePtr pipeline) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + ViewportData& viewportData = m_viewportData[viewportId]; + for (auto& context : viewportData.m_dynamicDrawContexts) + { + context.second->SetRenderPipeline(pipeline.get()); + } + }); + viewportData.m_viewportDestroyedHandler = AZ::Event::Handler([this, viewportId](AzFramework::ViewportId id) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + m_viewportData.erase(id); + }); + + viewportContext->ConnectCurrentPipelineChangedHandler(viewportData.m_pipelineChangedHandler); + viewportContext->ConnectAboutToBeDestroyedHandler(viewportData.m_viewportDestroyedHandler); + + viewportData.m_initialized = true; + } + + RHI::Ptr& context = viewportData.m_dynamicDrawContexts[name]; + if (context == nullptr) + { + auto pipeline = viewportContext->GetCurrentPipeline().get(); + if (pipeline == nullptr) + { + return nullptr; + } + context = RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext(pipeline); + contextFactoryIt->second(context); + } + + return context; + } +} //namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h new file mode 100644 index 0000000000..e2b442915c --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h @@ -0,0 +1,48 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ::AtomBridge +{ + class PerViewportDynamicDrawManager final : public PerViewportDynamicDrawInterface + { + public: + AZ_TYPE_INFO(PerViewportDynamicDrawManager, "{BED66185-00A7-43F7-BD28-C56BC8E4C535}"); + + PerViewportDynamicDrawManager(); + ~PerViewportDynamicDrawManager(); + + // PerViewportDynamicDrawInterface overrides... + void RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) override; + void UnregisterDynamicDrawContext(AZ::Name name) override; + RHI::Ptr GetDynamicDrawContextForViewport(AZ::Name name, AzFramework::ViewportId viewportId) override; + + private: + struct ViewportData + { + AZStd::unordered_map> m_dynamicDrawContexts; + + // Event handlers + AZ::Event::Handler m_pipelineChangedHandler; + AZ::Event::Handler m_viewportDestroyedHandler; + + // Cached state + bool m_initialized = false; + }; + AZStd::map m_viewportData; + AZStd::unordered_map m_registeredDrawContexts; + AZStd::mutex m_mutexDrawContexts; + }; +} //namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake b/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake index f272d323ae..969030f922 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake +++ b/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake @@ -12,10 +12,13 @@ set(FILES Include/AtomBridge/AtomBridgeBus.h Include/AtomBridge/FlyCameraInputBus.h + Include/AtomBridge/PerViewportDynamicDrawInterface.h Source/AtomBridgeSystemComponent.cpp Source/AtomBridgeSystemComponent.h - Source/FlyCameraInputComponent.cpp - Source/FlyCameraInputComponent.h Source/AtomDebugDisplayViewportInterface.cpp Source/AtomDebugDisplayViewportInterface.h + Source/FlyCameraInputComponent.cpp + Source/FlyCameraInputComponent.h + Source/PerViewportDynamicDrawManager.cpp + Source/PerViewportDynamicDrawManager.h ) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl new file mode 100644 index 0000000000..c4367efe6b --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl @@ -0,0 +1,77 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + + #include + +ShaderResourceGroup InstanceSrg : SRG_PerDraw +{ + float2 m_viewportSize; + Texture2D m_texture; + + Sampler m_sampler + { + MaxAnisotropy = 16; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +}; + +struct VSInput +{ + float3 m_position : POSITION; + float4 m_color : COLOR0; + float2 m_uv : TEXCOORD0; +}; + +struct VSOutput +{ + float4 m_position : SV_Position; + float4 m_color : COLOR0; + float2 m_uv : TEXCOORD0; +}; + +VSOutput MainVS(VSInput IN) +{ + // Convert from screen space to clip space + float2 posXY = float2(IN.m_position.xy) / InstanceSrg::m_viewportSize * 2.0f - float2(1.0f, 1.0f); + posXY.y *= -1.0f; + float4 posPS = float4(posXY, IN.m_position.z, 1.0f); + + VSOutput OUT; + OUT.m_position = posPS; + OUT.m_color = IN.m_color; + OUT.m_uv = IN.m_uv; + return OUT; +}; + +struct PSOutput +{ + float4 m_color : SV_Target0; +}; + +PSOutput MainPS(VSOutput IN) +{ + PSOutput OUT; + + float4 tex; + + tex = InstanceSrg::m_texture.Sample(InstanceSrg::m_sampler, IN.m_uv); + float opacity = IN.m_color.a * tex.a; + + // We use pre-multiplied alpha here since it is more flexible. For example, it enables alpha-blended rendering to + // a render target and then alpha blending that render target into another render target + OUT.m_color.rgb = IN.m_color.rgb * tex.rgb * opacity; + + OUT.m_color.a = opacity; + return OUT; +}; diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader new file mode 100644 index 0000000000..601a2664b5 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader @@ -0,0 +1,39 @@ +{ + "Source" : "TexturedIcon", + + "DepthStencilState" : { + "Depth" : { + "Enable" : false, + "CompareFunc" : "Always" + } + }, + + "RasterState" : { + "DepthClipEnable" : false, + "CullMode" : "None" + }, + + "BlendState" : { + "Enable" : true, + "BlendSource" : "One", + "BlendDest" : "AlphaSourceInverse", + "BlendOp" : "Add" + }, + + "DrawList" : "2dpass", + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt new file mode 100644 index 0000000000..20a680bce9 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt new file mode 100644 index 0000000000..b3e176c7a3 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME AtomViewportDisplayIcons.Editor ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + atomviewportdisplayicons_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzFramework + AZ::AzToolsFramework + AZ::AtomCore + 3rdParty::Qt::Core + 3rdParty::Qt::Gui + 3rdParty::Qt::Svg + Gem::Atom_RHI.Reflect + Gem::Atom_RPI.Public + Gem::Atom_Bootstrap.Headers + Gem::Atom_AtomBridge.Static + ) +endif() diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp new file mode 100644 index 0000000000..dce83c3072 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp @@ -0,0 +1,339 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "AtomViewportDisplayIconsSystemComponent.h" + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace AZ::Render +{ + void AtomViewportDisplayIconsSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0) + ; + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class("Viewport Display Icons", "Provides an interface for drawing simple icons to the Editor viewport") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) + ->Attribute(Edit::Attributes::AutoExpand, true) + ; + } + } + } + + void AtomViewportDisplayIconsSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("ViewportDisplayIconsService")); + } + + void AtomViewportDisplayIconsSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("ViewportDisplayIconsService")); + } + + void AtomViewportDisplayIconsSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC("RPISystem", 0xf2add773)); + required.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99)); + } + + void AtomViewportDisplayIconsSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + void AtomViewportDisplayIconsSystemComponent::Activate() + { + AzToolsFramework::EditorViewportIconDisplay::Register(this); + + Bootstrap::NotificationBus::Handler::BusConnect(); + } + + void AtomViewportDisplayIconsSystemComponent::Deactivate() + { + Bootstrap::NotificationBus::Handler::BusDisconnect(); + + auto perViewportDynamicDrawInterface = AtomBridge::PerViewportDynamicDraw::Get(); + if (!perViewportDynamicDrawInterface) + { + return; + } + if (perViewportDynamicDrawInterface) + { + perViewportDynamicDrawInterface->UnregisterDynamicDrawContext(m_drawContextName); + } + + AzToolsFramework::EditorViewportIconDisplay::Unregister(this); + } + + void AtomViewportDisplayIconsSystemComponent::DrawIcon(const DrawParameters& drawParameters) + { + // Ensure we have a valid viewport context & dynamic draw interface + auto viewportContext = RPI::ViewportContextRequests::Get()->GetViewportContextById(drawParameters.m_viewport); + if (viewportContext == nullptr) + { + return; + } + + auto perViewportDynamicDrawInterface = + AtomBridge::PerViewportDynamicDraw::Get(); + if (!perViewportDynamicDrawInterface) + { + return; + } + + RHI::Ptr dynamicDraw = + perViewportDynamicDrawInterface->GetDynamicDrawContextForViewport(m_drawContextName, drawParameters.m_viewport); + if (dynamicDraw == nullptr) + { + return; + } + + // Find our icon, falling back on a grey placeholder if its image is unavailable + AZ::Data::Instance image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Grey); + if (auto iconIt = m_iconData.find(drawParameters.m_icon); iconIt != m_iconData.end()) + { + auto& iconData = iconIt->second; + if (iconData.m_image) + { + image = iconData.m_image; + } + } + else + { + return; + } + + // Initialize our shader + auto viewportSize = viewportContext->GetViewportSize(); + AZ::Data::Instance drawSrg = dynamicDraw->NewDrawSrg(); + drawSrg->SetConstant(m_viewportSizeIndex, AZ::Vector2(aznumeric_cast(viewportSize.m_width), aznumeric_cast(viewportSize.m_height))); + drawSrg->SetImageView(m_textureParameterIndex, image->GetImageView()); + drawSrg->Compile(); + + AZ::Vector3 screenPosition; + if (drawParameters.m_positionSpace == CoordinateSpace::ScreenSpace) + { + screenPosition = drawParameters.m_position; + } + else if (drawParameters.m_positionSpace == CoordinateSpace::WorldSpace) + { + using ViewportRequestBus = AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus; + AzFramework::ScreenPoint position; + ViewportRequestBus::EventResult(position, drawParameters.m_viewport, &ViewportRequestBus::Events::ViewportWorldToScreen, drawParameters.m_position); + screenPosition.SetX(aznumeric_cast(position.m_x)); + screenPosition.SetY(aznumeric_cast(position.m_y)); + } + + struct Vertex + { + float m_position[3]; + AZ::u32 m_color; + float m_uv[2]; + }; + using Indice = AZ::u16; + + // Create a vertex offset from the position to draw from based on the icon size + // Vertex positions are in screen space coordinates + auto createVertex = [&](float offsetX, float offsetY, float u, float v) -> Vertex + { + Vertex vertex; + screenPosition.StoreToFloat3(vertex.m_position); + vertex.m_position[0] += offsetX * drawParameters.m_size.GetX(); + vertex.m_position[1] += offsetY * drawParameters.m_size.GetY(); + vertex.m_color = drawParameters.m_color.ToU32(); + vertex.m_uv[0] = u; + vertex.m_uv[1] = v; + return vertex; + }; + + AZStd::array vertices = { + createVertex(-0.5f, -0.5f, 0.f, 0.f), + createVertex(0.5f, -0.5f, 1.f, 0.f), + createVertex(0.5f, 0.5f, 1.f, 1.f), + createVertex(-0.5f, 0.5f, 0.f, 1.f) + }; + AZStd::array indices = {0, 1, 2, 0, 2, 3}; + dynamicDraw->DrawIndexed(&vertices, vertices.size(), &indices, indices.size(), RHI::IndexFormat::Uint16, drawSrg); + } + + QString AtomViewportDisplayIconsSystemComponent::FindAssetPath(const QString& sourceRelativePath) const + { + bool found = false; + AZStd::vector scanFolders; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + found, &AzToolsFramework::AssetSystemRequestBus::Events::GetScanFolders, scanFolders); + if (!found) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to load asset scan folders"); + return QString(); + } + + for (const auto& folder : scanFolders) + { + QDir dir(folder.data()); + if (dir.exists(sourceRelativePath)) + { + return dir.absoluteFilePath(sourceRelativePath); + } + } + + return QString(); + } + + QImage AtomViewportDisplayIconsSystemComponent::RenderSvgToImage(const QString& svgPath) const + { + // Set up our SVG renderer + QSvgRenderer renderer(svgPath); + renderer.setAspectRatioMode(Qt::KeepAspectRatio); + + // Set up our target image + QSize size = renderer.defaultSize().expandedTo(MinimumRenderedSvgSize); + QImage image(size, QtImageFormat); + image.fill(0x00000000); + + // Render the SVG + QPainter painter(&image); + renderer.render(&painter); + return image; + } + + AZ::Data::Instance AtomViewportDisplayIconsSystemComponent::ConvertToAtomImage(AZ::Uuid assetId, QImage image) const + { + // Ensure our image is in the correct pixel format so we can memcpy it to our renderer image + image.convertTo(QtImageFormat); + Data::Instance streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool(); + return RPI::StreamingImage::CreateFromCpuData( + *streamingImagePool.get(), + RHI::ImageDimension::Image2D, + RHI::Size(image.width(), image.height(), 1), + RHI::Format::R8G8B8A8_UNORM_SRGB, + image.bits(), + image.sizeInBytes(), + assetId); + } + + AzToolsFramework::EditorViewportIconDisplayInterface::IconId AtomViewportDisplayIconsSystemComponent::GetOrLoadIconForPath( + AZStd::string_view path) + { + AZ_Error( + "AtomViewportDisplayIconsSystemComponent", AzFramework::StringFunc::Path::IsRelative(path.data()), + "GetOrLoadIconForPath assumes that it will always be given a relative path, but got '%s'", path.data()); + + // Check our cache to see if the image is already loaded + auto existingEntryIt = AZStd::find_if(m_iconData.begin(), m_iconData.end(), [&path](const auto& iconData) + { + return iconData.second.m_path == path; + }); + if (existingEntryIt != m_iconData.end()) + { + return existingEntryIt->first; + } + + AZ::Uuid assetId = AZ::Uuid::CreateName(path.data()); + + // Find the asset to load on disk + QString assetPath = FindAssetPath(path.data()); + if (assetPath.isEmpty()) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to locate icon on disk: \"%s\"", path.data()); + return InvalidIconId; + } + + QImage loadedImage; + + AZStd::string extension; + AzFramework::StringFunc::Path::GetExtension(path.data(), extension, false); + // For SVGs, we need to actually rasterize to an image + if (extension == "svg") + { + loadedImage = RenderSvgToImage(assetPath); + } + // For everything else, we can just load it through QImage via its image plugins + else + { + const bool loaded = loadedImage.load(assetPath); + if (!loaded) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to load icon: \"%s\"", assetPath.toUtf8().constData()); + return InvalidIconId; + } + } + + // Cache our loaded icon + IconId id = m_currentId++; + IconData& iconData = m_iconData[id]; + iconData.m_path = path; + iconData.m_image = ConvertToAtomImage(assetId, loadedImage); + return id; + } + + AzToolsFramework::EditorViewportIconDisplayInterface::IconLoadStatus AtomViewportDisplayIconsSystemComponent::GetIconLoadStatus( + IconId icon) + { + auto iconIt = m_iconData.find(icon); + if (iconIt == m_iconData.end()) + { + return IconLoadStatus::Unloaded; + } + if (iconIt->second.m_image) + { + return IconLoadStatus::Loaded; + } + return IconLoadStatus::Error; + } + + void AtomViewportDisplayIconsSystemComponent::OnBootstrapSceneReady([[maybe_unused]]AZ::RPI::Scene* bootstrapScene) + { + AtomBridge::PerViewportDynamicDraw::Get()->RegisterDynamicDrawContext(m_drawContextName, [](RPI::Ptr drawContext) + { + auto shader = RPI::LoadShader(DrawContextShaderPath); + drawContext->InitShader(shader); + drawContext->InitVertexFormat( + {{"POSITION", RHI::Format::R32G32B32_FLOAT}, + {"COLOR", RHI::Format::R8G8B8A8_UNORM}, + {"TEXCOORD", RHI::Format::R32G32_FLOAT}}); + drawContext->EndInit(); + }); + } +} // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h new file mode 100644 index 0000000000..0c7366f23b --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h @@ -0,0 +1,82 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace AZ +{ + class TickRequests; + + namespace Render + { + class AtomViewportDisplayIconsSystemComponent + : public AZ::Component + , public AzToolsFramework::EditorViewportIconDisplayInterface + , public AZ::Render::Bootstrap::NotificationBus::Handler + { + public: + AZ_COMPONENT(AtomViewportDisplayIconsSystemComponent, "{AEC1D3E1-1D9A-437A-B4C6-CFAEE620C160}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + protected: + // AZ::Component overrides... + void Activate() override; + void Deactivate() override; + + // AzToolsFramework::EditorViewportIconDisplayInterface overrides... + void DrawIcon(const DrawParameters& drawParameters) override; + IconId GetOrLoadIconForPath(AZStd::string_view path) override; + IconLoadStatus GetIconLoadStatus(IconId icon) override; + + // AZ::Render::Bootstrap::NotificationBus::Handler overrides... + void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) override; + + private: + static constexpr const char* DrawContextShaderPath = "Shaders/TexturedIcon.azshader"; + static constexpr QSize MinimumRenderedSvgSize = QSize(128, 128); + static constexpr QImage::Format QtImageFormat = QImage::Format_RGBA8888; + + QString FindAssetPath(const QString& sourceRelativePath) const; + QImage RenderSvgToImage(const QString& svgPath) const; + AZ::Data::Instance ConvertToAtomImage(AZ::Uuid assetId, QImage image) const; + + Name m_drawContextName = Name("ViewportIconDisplay"); + bool m_shaderIndexesInitialized = false; + RHI::ShaderInputNameIndex m_textureParameterIndex = "m_texture"; + RHI::ShaderInputNameIndex m_viewportSizeIndex = "m_viewportSize"; + + struct IconData + { + AZStd::string m_path; + AZ::Data::Instance m_image = nullptr; + }; + AZStd::unordered_map m_iconData; + IconId m_currentId = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp new file mode 100644 index 0000000000..db7672186a --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp @@ -0,0 +1,51 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +#include "AtomViewportDisplayIconsSystemComponent.h" + +namespace AZ +{ + namespace Render + { + class AtomViewportDisplayInfoModule + : public AZ::Module + { + public: + AZ_RTTI(AtomViewportDisplayInfoModule, "{8D72F14E-958D-4225-B3BC-C5C87BDDD426}", AZ::Module); + AZ_CLASS_ALLOCATOR(AtomViewportDisplayInfoModule, AZ::SystemAllocator, 0); + + AtomViewportDisplayInfoModule() + : AZ::Module() + { + m_descriptors.insert(m_descriptors.end(), { + AtomViewportDisplayIconsSystemComponent::CreateDescriptor(), + }); + } + + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; + } // namespace Render +} // namespace AZ + +// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM +// The first parameter should be GemName_GemIdLower +// The second should be the fully qualified name of the class above +AZ_DECLARE_MODULE_CLASS(Gem_AtomViewportDisplayInfo, AZ::Render::AtomViewportDisplayInfoModule) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake new file mode 100644 index 0000000000..f02aed0856 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake @@ -0,0 +1,16 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Source/AtomViewportDisplayIconsSystemComponent.cpp + Source/AtomViewportDisplayIconsSystemComponent.h + Source/Module.cpp +) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json deleted file mode 100644 index dd92a99ea9..0000000000 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "gem_name": "AtomLyIntegration_AtomViewportDisplayInfo", - "display_name": "Atom Viewport Display Info Overlay", - "summary": "Provides a diagnostic viewport overlay for the default O3DE Atom viewport.", - "canonical_tags": [ - "Gem" - ], - "user_tags": [ - "AtomLyIntegration", - "AtomViewportDisplayInfo" - ] -} \ No newline at end of file diff --git a/Gems/AtomLyIntegration/CMakeLists.txt b/Gems/AtomLyIntegration/CMakeLists.txt index 35022e643b..ff6800a7ff 100644 --- a/Gems/AtomLyIntegration/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(AtomFont) add_subdirectory(TechnicalArt) add_subdirectory(AtomBridge) add_subdirectory(AtomViewportDisplayInfo) +add_subdirectory(AtomViewportDisplayIcons)