From 905bdf9627f5c6226624d3343fb2c45f0d707440 Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Mon, 3 May 2021 16:32:18 -0700 Subject: [PATCH 1/3] Fix sprite asset selection in property editor (#384) * Fix sprite asset selection in property editor * Linux compile fix * More fixes for the custom Sprite property handler * PR feedback to use existing constant image extension --- .../Code/Editor/PropertyHandlerSprite.cpp | 26 +++- Gems/LyShine/Code/Source/Sprite.cpp | 125 +++++++++++------- Gems/LyShine/Code/Source/Sprite.h | 8 ++ Gems/LyShine/Code/Source/UiImageComponent.cpp | 3 +- 4 files changed, 106 insertions(+), 56 deletions(-) diff --git a/Gems/LyShine/Code/Editor/PropertyHandlerSprite.cpp b/Gems/LyShine/Code/Editor/PropertyHandlerSprite.cpp index 20721d4624..9263210088 100644 --- a/Gems/LyShine/Code/Editor/PropertyHandlerSprite.cpp +++ b/Gems/LyShine/Code/Editor/PropertyHandlerSprite.cpp @@ -11,6 +11,7 @@ */ #include "UiCanvasEditor_precompiled.h" #include "EditorCommon.h" +#include "Sprite.h" #include "PropertyHandlerSprite.h" @@ -31,6 +32,8 @@ #include +#include + #include #include @@ -44,6 +47,7 @@ PropertySpriteCtrl::PropertySpriteCtrl(QWidget* parent) [ this ]([[maybe_unused]] AZ::Data::AssetId newAssetID) { EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, this); + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler::OnEditingFinished, m_propertyAssetCtrl); }); setAcceptDrops(true); @@ -150,7 +154,9 @@ void PropertyHandlerSprite::WriteGUIValuesIntoProperty(size_t index, PropertySpr AZStd::string assetPath; EBUS_EVENT_RESULT(assetPath, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, GUI->GetPropertyAssetCtrl()->GetCurrentAssetID()); - instance.SetAssetPath(assetPath.c_str()); + // Convert streaming image's product path to relative source path to assign to the SimpleAssetReference + AZStd::string sourcePath = CSprite::GetImageSourcePathFromProductPath(assetPath); + instance.SetAssetPath(sourcePath.c_str()); } bool PropertyHandlerSprite::ReadValuesIntoGUI(size_t index, PropertySpriteCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) @@ -162,12 +168,26 @@ bool PropertyHandlerSprite::ReadValuesIntoGUI(size_t index, PropertySpriteCtrl* ctrl->blockSignals(true); { - ctrl->SetCurrentAssetType(instance.GetAssetType()); + // Set the asset type for the PropertyAssetCtrl. + // Use the hardcoded streaming image asset type instead of the passed in instance's asset type + // since the passed in type is the legacy SimpleAssetReference, and the asset picker + // does not associate this type with streaming images + AZ::Data::AssetType assetType = AZ::AzTypeInfo::Uuid(); + ctrl->SetCurrentAssetType(assetType); AZ::Data::AssetId assetId; if (!instance.GetAssetPath().empty()) { - EBUS_EVENT_RESULT(assetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, instance.GetAssetPath().c_str(), instance.GetAssetType(), false); + // Get the image path from the SimpleAssetReference and fix it up since CSprite still + // allows user specified paths that have the .sprite extension or the deprecated .dds extension + AZStd::string sourcePath = CSprite::GetImageSourcePathFromProductPath(instance.GetAssetPath()); + AZStd::string fixedUpSourcePath; + CSprite::FixUpSourceImagePathFromUserDefinedPath(sourcePath, fixedUpSourcePath); + + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, &AZ::Data::AssetCatalogRequestBus::Events::GenerateAssetIdTEMP, + fixedUpSourcePath.c_str()); + assetId.m_subId = AZ::RPI::StreamingImageAsset::GetImageAssetSubId(); } ctrl->SetSelectedAssetID(assetId); } diff --git a/Gems/LyShine/Code/Source/Sprite.cpp b/Gems/LyShine/Code/Source/Sprite.cpp index c574a16c0c..26e4056a41 100644 --- a/Gems/LyShine/Code/Source/Sprite.cpp +++ b/Gems/LyShine/Code/Source/Sprite.cpp @@ -26,6 +26,7 @@ namespace { const char* const spriteExtension = "sprite"; + const char* const streamingImageExtension = "streamingimage"; // Increment this when the Sprite Serialize(TSerialize) function // changes to be incompatible with previous data @@ -37,7 +38,7 @@ namespace }; const int numAllowedSpriteTextureExtensions = AZ_ARRAY_SIZE(allowedSpriteTextureExtensions); - bool IsValidSpriteTextureExtension(const AZStd::string& extension) + bool IsValidImageExtension(const AZStd::string& extension) { for (int i = 0; i < numAllowedSpriteTextureExtensions; ++i) { @@ -50,6 +51,13 @@ namespace return false; } + bool IsImageProductPath(const AZStd::string& pathname) + { + AZStd::string extension; + AzFramework::StringFunc::Path::GetExtension(pathname.c_str(), extension, false); + return (extension.compare(streamingImageExtension) == 0); + } + // Check if a file exists. This does not go through the AssetCatalog so that it can identify files that exist but aren't processed yet, // and so that it will work before the AssetCatalog has loaded bool CheckIfFileExists(const AZStd::string& sourceRelativePath, const AZStd::string& cacheRelativePath) @@ -88,61 +96,49 @@ namespace return fileExists; } - bool ReplaceSpriteExtensionWithTextureExtension(const AZStd::string& spritePath, AZStd::string& texturePath) + bool GetSourceAssetPaths(const AZStd::string& pathname, AZStd::string& spritePath, AZStd::string& texturePath) { - for (int i = 0; i < numAllowedSpriteTextureExtensions; ++i) + // Remove product extension from the texture path if it exists + AZStd::string sourcePathname(pathname); + if (IsImageProductPath(pathname)) { - AZStd::string sourceRelativePath(spritePath); - AzFramework::StringFunc::Path::ReplaceExtension(sourceRelativePath, allowedSpriteTextureExtensions[i]); - AZStd::string cacheRelativePath = sourceRelativePath + ".streamingimage"; - - bool textureExists = CheckIfFileExists(sourceRelativePath, cacheRelativePath); - if (textureExists) - { - texturePath = sourceRelativePath; - return true; - } + sourcePathname = CSprite::GetImageSourcePathFromProductPath(pathname); } - return false; - } - - bool GetAssetPaths(const AZStd::string& pathname, AZStd::string& spritePath, AZStd::string& texturePath) - { - // the input string could be in any form. So make it normalized + // the input string could be in any form. So make it normalized (forward slashes and lower case) // NOTE: it should not be a full path at this point. If called from the UI editor it will // have been transformed to a game path. If being called with a hard coded path it should be a // game path already - it is not good for code to be using full paths. - AZStd::string assetPath(pathname); - EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, assetPath); + EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, sourcePathname); // check the extension and work out the pathname of the sprite file and the texture file // currently it works if the input path is either a sprite file or a texture file AZStd::string extension; - AzFramework::StringFunc::Path::GetExtension(assetPath.c_str(), extension, false); + AzFramework::StringFunc::Path::GetExtension(sourcePathname.c_str(), extension, false); if (extension.compare(spriteExtension) == 0) { - spritePath = assetPath; + // The .sprite file has been specified + spritePath = sourcePathname; // look for a texture file with the same name - if (!ReplaceSpriteExtensionWithTextureExtension(spritePath, texturePath)) + if (!CSprite::FixUpSourceImagePathFromUserDefinedPath(spritePath, texturePath)) { gEnv->pSystem->Warning(VALIDATOR_MODULE_SHINE, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, - assetPath.c_str(), "No texture file found for sprite: %s, no sprite will be used", assetPath.c_str()); + spritePath.c_str(), "No texture file found for sprite: %s, no sprite will be used", spritePath.c_str()); return false; } } - else if (IsValidSpriteTextureExtension(extension)) + else if (IsValidImageExtension(extension)) { - texturePath = assetPath; - spritePath = assetPath; + texturePath = sourcePathname; + spritePath = sourcePathname; AzFramework::StringFunc::Path::ReplaceExtension(spritePath, spriteExtension); } else { gEnv->pSystem->Warning(VALIDATOR_MODULE_SHINE, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, - assetPath.c_str(), "Invalid file extension for sprite: %s, no sprite will be used", assetPath.c_str()); + pathname.c_str(), "Invalid file extension for sprite: %s, no sprite will be used", pathname.c_str()); return false; } @@ -665,7 +661,7 @@ CSprite* CSprite::LoadSprite(const string& pathname) { AZStd::string spritePath; AZStd::string texturePath; - bool validAssetPaths = GetAssetPaths(pathname.c_str(), spritePath, texturePath); + bool validAssetPaths = GetSourceAssetPaths(pathname.c_str(), spritePath, texturePath); if (!validAssetPaths) { @@ -760,7 +756,7 @@ bool CSprite::DoesSpriteTextureAssetExist(const AZStd::string& pathname) { AZStd::string spritePath; AZStd::string texturePath; - bool validAssetPaths = GetAssetPaths(pathname, spritePath, texturePath); + bool validAssetPaths = GetSourceAssetPaths(pathname.c_str(), spritePath, texturePath); if (!validAssetPaths) { @@ -785,8 +781,7 @@ bool CSprite::DoesSpriteTextureAssetExist(const AZStd::string& pathname) } // Check if the texture asset exists - AZStd::string cacheRelativePath = texturePath + ".streamingimage"; - bool textureExists = CheckIfFileExists(texturePath, cacheRelativePath); + bool textureExists = CheckIfFileExists(spritePath, texturePath); return textureExists; } @@ -806,6 +801,48 @@ void CSprite::ReplaceSprite(ISprite** baseSprite, ISprite* newSprite) } } +//////////////////////////////////////////////////////////////////////////////////////////////////// +bool CSprite::FixUpSourceImagePathFromUserDefinedPath(const AZStd::string& userDefinedPath, AZStd::string& sourceImagePath) +{ + static const char* textureExtensions[] = { "png", "tif", "tiff", "tga", "jpg", "jpeg", "bmp", "gif" }; + + AZStd::string sourceRelativePath(userDefinedPath); + AZStd::string cacheRelativePath = AZStd::string::format("%s.%s", sourceRelativePath.c_str(), streamingImageExtension); + bool textureExists = CheckIfFileExists(sourceRelativePath, cacheRelativePath); + + if (textureExists) + { + sourceImagePath = userDefinedPath; + return true; + } + + AZStd::string curSourceImagePath(userDefinedPath); + for (const char* extensionReplacement : textureExtensions) + { + AzFramework::StringFunc::Path::ReplaceExtension(curSourceImagePath, extensionReplacement); + cacheRelativePath = AZStd::string::format("%s.%s", curSourceImagePath.c_str(), streamingImageExtension); + textureExists = CheckIfFileExists(curSourceImagePath, cacheRelativePath); + + if (textureExists) + { + sourceImagePath = curSourceImagePath; + return true; + } + } + + return false; +} + +AZStd::string CSprite::GetImageSourcePathFromProductPath(const AZStd::string& productPathname) +{ + AZStd::string sourcePathname(productPathname); + if (IsImageProductPath(sourcePathname)) + { + AzFramework::StringFunc::Path::StripExtension(sourcePathname); + } + return sourcePathname; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// bool CSprite::LoadTexture(const string& texturePathname, const string& pathname, ITexture*& texture) { @@ -850,7 +887,7 @@ void CSprite::ReleaseTexture(ITexture*& texture) bool CSprite::LoadImage(const AZStd::string& nameTex, AZ::Data::Instance& image) { AZStd::string sourceRelativePath(nameTex); - AZStd::string cacheRelativePath = sourceRelativePath + ".streamingimage"; + AZStd::string cacheRelativePath = AZStd::string::format("%s.%s", sourceRelativePath.c_str(), streamingImageExtension); bool textureExists = CheckIfFileExists(sourceRelativePath, cacheRelativePath); if (!textureExists) @@ -859,27 +896,13 @@ bool CSprite::LoadImage(const AZStd::string& nameTex, AZ::Data::InstanceAttribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshEntireTree", 0xefbc823c)); editInfo->DataElement("Sprite", &UiImageComponent::m_spritePathname, "Sprite path", "The sprite path. Can be overridden by another component such as an interactable.") ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsSpriteTypeAsset) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorSpritePathnameChange) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshEntireTree", 0xefbc823c)); + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorSpritePathnameChange); editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiImageComponent::m_spriteSheetCellIndex, "Index", "Sprite-sheet index. Defines which cell in a sprite-sheet is displayed.") ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsSpriteTypeSpriteSheet) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnIndexChange) From 3219c787ac66bf19dea7040581a27e2a80fa2d77 Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Thu, 6 May 2021 13:27:46 -0700 Subject: [PATCH 2/3] UI cursor Atom conversion (#607) --- Gems/LyShine/Code/CMakeLists.txt | 1 + Gems/LyShine/Code/Source/LyShine.cpp | 69 ++++++++++++++++++---------- Gems/LyShine/Code/Source/LyShine.h | 12 ++++- 3 files changed, 56 insertions(+), 26 deletions(-) diff --git a/Gems/LyShine/Code/CMakeLists.txt b/Gems/LyShine/Code/CMakeLists.txt index 9c46a61236..732bd1cfd4 100644 --- a/Gems/LyShine/Code/CMakeLists.txt +++ b/Gems/LyShine/Code/CMakeLists.txt @@ -93,6 +93,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) PUBLIC Gem::Atom_RPI.Public Gem::Atom_Utils.Static + Gem::Atom_Bootstrap.Headers ) ly_add_target( diff --git a/Gems/LyShine/Code/Source/LyShine.cpp b/Gems/LyShine/Code/Source/LyShine.cpp index bc6a27dac4..679cae3421 100644 --- a/Gems/LyShine/Code/Source/LyShine.cpp +++ b/Gems/LyShine/Code/Source/LyShine.cpp @@ -137,7 +137,6 @@ CLyShine::CLyShine(ISystem* system) , m_draw2d(new CDraw2d) , m_uiRenderer(new UiRenderer) , m_uiCanvasManager(new UiCanvasManager) - , m_uiCursorTexture(nullptr) , m_uiCursorVisibleCounter(0) { // Reflect the Deprecated Lua buses using the behavior context. @@ -170,6 +169,7 @@ CLyShine::CLyShine(ISystem* system) AzFramework::InputTextEventListener::Connect(); UiCursorBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect(); + AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect(); // These are internal Amazon components, so register them so that we can send back their names to our metrics collection // IF YOU ARE A THIRDPARTY WRITING A GEM, DO NOT REGISTER YOUR COMPONENTS WITH EditorMetricsComponentRegistrationBus @@ -248,17 +248,12 @@ CLyShine::~CLyShine() AZ::TickBus::Handler::BusDisconnect(); AzFramework::InputTextEventListener::Disconnect(); AzFramework::InputChannelEventListener::Disconnect(); + AZ::Render::Bootstrap::NotificationBus::Handler::BusDisconnect(); UiCanvasComponent::Shutdown(); // must be done after UiCanvasComponent::Shutdown CSprite::Shutdown(); - - if (m_uiCursorTexture) - { - m_uiCursorTexture->Release(); - m_uiCursorTexture = nullptr; - } } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -443,6 +438,9 @@ void CLyShine::Render() // Render all the canvases loaded in game m_uiCanvasManager->RenderLoadedCanvases(); + // Set sort key for draw2d layer to ensure it renders in front of the canvases + static const int64_t topLayerKey = 0x1000000; + m_draw2d->SetSortKey(topLayerKey); m_draw2d->RenderDeferredPrimitives(); // Don't render the UI cursor when in edit mode. For example during UI Preview mode a script could turn on the @@ -558,18 +556,20 @@ bool CLyShine::IsUiCursorVisible() //////////////////////////////////////////////////////////////////////////////////////////////////// void CLyShine::SetUiCursor(const char* cursorImagePath) { - if (m_uiCursorTexture) - { - m_uiCursorTexture->Release(); - m_uiCursorTexture = nullptr; - } + m_uiCursorTexture.reset(); + m_cursorImagePathToLoad.clear(); - if (cursorImagePath && *cursorImagePath && gEnv && gEnv->pRenderer) + if (cursorImagePath && *cursorImagePath) { - m_uiCursorTexture = gEnv->pRenderer->EF_LoadTexture(cursorImagePath, FT_DONT_RELEASE | FT_DONT_STREAM); - if (m_uiCursorTexture) + m_cursorImagePathToLoad = cursorImagePath; + // The cursor image can only be loaded after the RPI has been initialized. + // Note: this check could be avoided if LyShineSystemComponent included the RPISystem + // as a required service. However, LyShineSystempComponent is currently activated for + // tools as well as game and RPIService is not available with all tools such as AP. An + // enhancement would be to break LyShineSystemComponent into a game only component + if (m_uiRenderer->IsReady()) { - m_uiCursorTexture->SetClamp(true); + LoadUiCursor(); } } } @@ -581,8 +581,11 @@ AZ::Vector2 CLyShine::GetUiCursorPosition() AzFramework::InputSystemCursorRequestBus::EventResult(systemCursorPositionNormalized, AzFramework::InputDeviceMouse::Id, &AzFramework::InputSystemCursorRequests::GetSystemCursorPositionNormalized); - return AZ::Vector2(systemCursorPositionNormalized.GetX() * static_cast(gEnv->pRenderer->GetOverlayWidth()), - systemCursorPositionNormalized.GetY() * static_cast(gEnv->pRenderer->GetOverlayHeight())); + + AZ::Vector2 viewportSize = m_uiRenderer->GetViewportSize(); + + return AZ::Vector2(systemCursorPositionNormalized.GetX() * viewportSize.GetX(), + systemCursorPositionNormalized.GetY() * viewportSize.GetY()); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -642,6 +645,7 @@ bool CLyShine::OnInputTextEventFiltered(const AZStd::string& textUTF8) return result; } +//////////////////////////////////////////////////////////////////////////////////////////////////// void CLyShine::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { // Update the loaded UI canvases @@ -651,15 +655,33 @@ void CLyShine::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time Render(); } +//////////////////////////////////////////////////////////////////////////////////////////////////// int CLyShine::GetTickOrder() { return AZ::TICK_UI; } +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CLyShine::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene) +{ + // Load cursor if its path was set before RPI was initialized + LoadUiCursor(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CLyShine::LoadUiCursor() +{ + if (!m_cursorImagePathToLoad.empty()) + { + m_uiCursorTexture = CDraw2d::LoadTexture(m_cursorImagePathToLoad); // LYSHINE_ATOM_TODO - add clamp option to draw2d and set cursor to clamp + m_cursorImagePathToLoad.clear(); + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void CLyShine::RenderUiCursor() { - if (!gEnv || !gEnv->pRenderer || !m_uiCursorTexture || !IsUiCursorVisible()) + if (!m_uiCursorTexture || !IsUiCursorVisible()) { return; } @@ -671,13 +693,10 @@ void CLyShine::RenderUiCursor() } const AZ::Vector2 position = GetUiCursorPosition(); - const AZ::Vector2 dimensions(static_cast(m_uiCursorTexture->GetWidth()), static_cast(m_uiCursorTexture->GetHeight())); + AZ::RHI::Size cursorSize = m_uiCursorTexture->GetDescriptor().m_size; + const AZ::Vector2 dimensions(aznumeric_cast(cursorSize.m_width), aznumeric_cast(cursorSize.m_height)); -#ifdef LYSHINE_ATOM_TODO // Convert cursor to Atom image - m_draw2d->BeginDraw2d(); - m_draw2d->DrawImage(m_uiCursorTexture->GetTextureID(), position, dimensions); - m_draw2d->EndDraw2d(); -#endif + m_draw2d->DrawImage(m_uiCursorTexture, position, dimensions); } #ifndef _RELEASE diff --git a/Gems/LyShine/Code/Source/LyShine.h b/Gems/LyShine/Code/Source/LyShine.h index 943f5fa537..4c46b0db19 100644 --- a/Gems/LyShine/Code/Source/LyShine.h +++ b/Gems/LyShine/Code/Source/LyShine.h @@ -19,6 +19,9 @@ #include #include +#include +#include + #if !defined(_RELEASE) #define LYSHINE_INTERNAL_UNIT_TEST #endif @@ -41,6 +44,7 @@ class CLyShine , public AzFramework::InputChannelEventListener , public AzFramework::InputTextEventListener , public AZ::TickBus::Handler + , protected AZ::Render::Bootstrap::NotificationBus::Handler { public: @@ -111,6 +115,10 @@ public: int GetTickOrder() override; // ~TickEvents + // AZ::Render::Bootstrap::NotificationBus + void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) override; + // ~AZ::Render::Bootstrap::NotificationBus + // Get the UIRenderer for the game (which is owned by CLyShine). This is not exposed outside the gem. UiRenderer* GetUiRenderer(); @@ -128,6 +136,7 @@ private: // member functions AZ_DISABLE_COPY_MOVE(CLyShine); + void LoadUiCursor(); void RenderUiCursor(); private: // static member functions @@ -146,7 +155,8 @@ private: // data std::unique_ptr m_uiCanvasManager; - ITexture* m_uiCursorTexture; + AZStd::string m_cursorImagePathToLoad; + AZ::Data::Instance m_uiCursorTexture; int m_uiCursorVisibleCounter; bool m_updatingLoadedCanvases = false; // guard against nested updates From d9cb61575e5c9afbc1d4f3f10cea3202e85db6b3 Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Fri, 7 May 2021 11:25:57 -0700 Subject: [PATCH 3/3] Fix up particle emitter to work with Atom (#626) * Fix up particle emitter component to work with Atom * PR feedback and disable render target tests until supported --- Code/CryEngine/CryCommon/LyShine/ISprite.h | 6 -- Gems/LyShine/Code/Source/Sprite.cpp | 84 +++---------------- Gems/LyShine/Code/Source/Sprite.h | 7 +- Gems/LyShine/Code/Source/UiImageComponent.cpp | 38 +++++---- .../Code/Source/UiImageSequenceComponent.cpp | 19 ++++- .../Source/UiParticleEmitterComponent.cpp | 27 ++++-- Gems/LyShine/Code/Tests/SpriteTest.cpp | 2 + .../Code/Source/UiCustomImageComponent.cpp | 4 +- 8 files changed, 76 insertions(+), 111 deletions(-) diff --git a/Code/CryEngine/CryCommon/LyShine/ISprite.h b/Code/CryEngine/CryCommon/LyShine/ISprite.h index d2e9a6e039..b9adda6901 100644 --- a/Code/CryEngine/CryCommon/LyShine/ISprite.h +++ b/Code/CryEngine/CryCommon/LyShine/ISprite.h @@ -16,9 +16,6 @@ #include #include -// forward declarations -class ITexture; - //////////////////////////////////////////////////////////////////////////////////////////////////// //! A sprite is a texture with extra information about how it behaves for 2D drawing //! Currently a sprite exists on disk as a side car file next to the texture file. @@ -80,9 +77,6 @@ public: // member functions //! Set the borders of a given cell within the sprite-sheet. virtual void SetCellBorders(int cellIndex, Borders borders) = 0; - //! Get the texture for this sprite - virtual ITexture* GetTexture() = 0; - //! Serialize this object for save/load virtual void Serialize(TSerialize ser) = 0; diff --git a/Gems/LyShine/Code/Source/Sprite.cpp b/Gems/LyShine/Code/Source/Sprite.cpp index 26e4056a41..7abbba4ea2 100644 --- a/Gems/LyShine/Code/Source/Sprite.cpp +++ b/Gems/LyShine/Code/Source/Sprite.cpp @@ -11,7 +11,6 @@ */ #include "LyShine_precompiled.h" #include "Sprite.h" -#include #include #include #include @@ -193,8 +192,7 @@ AZStd::string CSprite::s_emptyString; //////////////////////////////////////////////////////////////////////////////////////////////////// CSprite::CSprite() - : m_texture(nullptr) - , m_numSpriteSheetCellTags(0) + : m_numSpriteSheetCellTags(0) , m_atlas(nullptr) { AddRef(); @@ -204,8 +202,6 @@ CSprite::CSprite() //////////////////////////////////////////////////////////////////////////////////////////////////// CSprite::~CSprite() { - ReleaseTexture(m_texture); - s_loadedSprites->erase(m_pathname); TextureAtlasNamespace::TextureAtlasNotificationBus::Handler::BusDisconnect(); } @@ -250,26 +246,17 @@ void CSprite::SetCellBorders(int cellIndex, Borders borders) } //////////////////////////////////////////////////////////////////////////////////////////////////// -ITexture* CSprite::GetTexture() +AZ::Data::Instance CSprite::GetImage() { // Prioritize usage of an atlas +#ifdef LYSHINE_ATOM_TODO // texture atlas conversion to use Atom if (m_atlas) { return m_atlas->GetTexture(); } +#endif - if (!m_texture && !m_pathname.empty()) - { - // the render target texture may not have existed when the sprite was created - m_texture = gEnv->pRenderer->EF_GetTextureByName(m_pathname.c_str()); - if (m_texture) - { - // increase the reference count to this texture so it doesn't get removed while - // we are using it - m_texture->AddRef(); - } - } - return m_texture; + return m_image; } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -377,31 +364,21 @@ bool CSprite::AreCellBordersZeroWidth(int cellIndex) const //////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Vector2 CSprite::GetSize() { -#ifdef LYSHINE_ATOM_TODO // Convert texture atlases to use Atom - ITexture* texture = GetTexture(); - if (texture) + AZ::Data::Instance image = GetImage(); + if (image) { if (m_atlas) { return AZ::Vector2(static_cast(m_atlasCoordinates.GetWidth()), static_cast(m_atlasCoordinates.GetHeight())); } - return AZ::Vector2(static_cast(texture->GetWidth()), static_cast(texture->GetHeight())); - } - else - { - return AZ::Vector2(0.0f, 0.0f); - } -#else - if (m_image) - { - AZ::RHI::Size size = m_image->GetRHIImage()->GetDescriptor().m_size; + + AZ::RHI::Size size = image->GetRHIImage()->GetDescriptor().m_size; return AZ::Vector2(size.m_width, size.m_height); } else { return AZ::Vector2(0.0f, 0.0f); } -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -734,6 +711,7 @@ CSprite* CSprite::CreateSprite(const string& renderTargetName) // create Sprite object CSprite* sprite = new CSprite; +#ifdef LYSHINE_ATOM_TODO // render target converstion to use ATom // the render target texture may not exist yet in which case we will need to load it later sprite->m_texture = gEnv->pRenderer->EF_GetTextureByName(renderTargetName.c_str()); if (sprite->m_texture) @@ -742,6 +720,7 @@ CSprite* CSprite::CreateSprite(const string& renderTargetName) // while we are using it sprite->m_texture->AddRef(); } +#endif sprite->m_pathname = renderTargetName; sprite->m_texturePathname.clear(); @@ -833,6 +812,7 @@ bool CSprite::FixUpSourceImagePathFromUserDefinedPath(const AZStd::string& userD return false; } +//////////////////////////////////////////////////////////////////////////////////////////////////// AZStd::string CSprite::GetImageSourcePathFromProductPath(const AZStd::string& productPathname) { AZStd::string sourcePathname(productPathname); @@ -843,46 +823,6 @@ AZStd::string CSprite::GetImageSourcePathFromProductPath(const AZStd::string& pr return sourcePathname; } -//////////////////////////////////////////////////////////////////////////////////////////////////// -bool CSprite::LoadTexture(const string& texturePathname, const string& pathname, ITexture*& texture) -{ - uint32 loadTextureFlags = (FT_USAGE_ALLOWREADSRGB | FT_DONT_STREAM); - texture = gEnv->pRenderer->EF_LoadTexture(texturePathname.c_str(), loadTextureFlags); - - if (!texture || !texture->IsTextureLoaded()) - { - gEnv->pSystem->Warning( - VALIDATOR_MODULE_SHINE, - VALIDATOR_WARNING, - VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, - texturePathname.c_str(), - "No texture file found for sprite: %s, no sprite will be used. " - "NOTE: File must be in current project or a gem.", - pathname.c_str()); - texture = nullptr; - return false; - } - texture->SetFilter(FILTER_LINEAR); - return true; -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -void CSprite::ReleaseTexture(ITexture*& texture) -{ - if (texture) - { - // In order to avoid the texture being deleted while there are still commands on the render - // thread command queue that use it, we queue a command to delete the texture onto the - // command queue. - auto pInfo = AZStd::make_unique(); - pInfo->eClassName = eRCN_Texture; - pInfo->pResource = texture; - gEnv->pRenderer->ReleaseResourceAsync(AZStd::move(pInfo)); - texture = nullptr; - } -} - //////////////////////////////////////////////////////////////////////////////////////////////////// bool CSprite::LoadImage(const AZStd::string& nameTex, AZ::Data::Instance& image) { diff --git a/Gems/LyShine/Code/Source/Sprite.h b/Gems/LyShine/Code/Source/Sprite.h index b3037bf242..026646013e 100644 --- a/Gems/LyShine/Code/Source/Sprite.h +++ b/Gems/LyShine/Code/Source/Sprite.h @@ -41,7 +41,6 @@ public: // member functions Borders GetBorders() const override; void SetBorders(Borders borders) override; void SetCellBorders(int cellIndex, Borders borders) override; - ITexture* GetTexture() override; void Serialize(TSerialize ser) override; bool SaveToXml(const string& pathname) override; bool AreBordersZeroWidth() const override; @@ -71,7 +70,7 @@ public: // member functions // ~TextureAtlasNotifications - AZ::Data::Instance GetImage() { return m_image; } + AZ::Data::Instance GetImage(); public: // static member functions @@ -93,9 +92,6 @@ public: // static member functions static AZStd::string GetImageSourcePathFromProductPath(const AZStd::string& productPathname); private: - static bool LoadTexture(const string& texturePathname, const string& pathname, ITexture*& texture); - static void ReleaseTexture(ITexture*& texture); - static bool LoadImage(const AZStd::string& nameTex, AZ::Data::Instance& image); static void ReleaseImage(AZ::Data::Instance& image); @@ -120,7 +116,6 @@ private: // data string m_pathname; string m_texturePathname; Borders m_borders; - ITexture* m_texture; AZ::Data::Instance m_image; int m_numSpriteSheetCellTags; //!< Number of Cell child-tags in sprite XML; unfortunately needed to help with serialization. diff --git a/Gems/LyShine/Code/Source/UiImageComponent.cpp b/Gems/LyShine/Code/Source/UiImageComponent.cpp index c2294b8a93..9cf923a460 100644 --- a/Gems/LyShine/Code/Source/UiImageComponent.cpp +++ b/Gems/LyShine/Code/Source/UiImageComponent.cpp @@ -34,9 +34,8 @@ #include #include "UiSerialize.h" -#include "Sprite.h" #include "UiLayoutHelpers.h" - +#include "Sprite.h" #include "RenderGraph.h" namespace @@ -281,6 +280,20 @@ namespace 14, 15, 21, 21, 20, 14, // center quad }; + AZ::Data::Instance GetSpriteImage(ISprite* sprite) + { + AZ::Data::Instance image; + if (sprite) + { + CSprite* cSprite = dynamic_cast(sprite); // LYSHINE_ATOM_TODO - find a different solution from downcasting + if (cSprite) + { + image = cSprite->GetImage(); + } + } + + return image; + } } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -471,20 +484,12 @@ void UiImageComponent::Render(LyShine::IRenderGraph* renderGraph) renderGraph->AddPrimitive(&m_cachedPrimitive, texture, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, m_blendMode); #else - AZ::Data::Instance image; - if (sprite) - { - CSprite* cSprite = static_cast(sprite); // LYSHINE_ATOM_TODO - find a different solution from downcasting - if (cSprite) - { - image = cSprite->GetImage(); - } - } + AZ::Data::Instance image = GetSpriteImage(sprite); bool isClampTextureMode = m_imageType == ImageType::Tiled ? false : true; bool isTextureSRGB = IsSpriteTypeRenderTarget() && m_isRenderTargetSRGB; bool isTexturePremultipliedAlpha = false; // we are not rendering from a render target with alpha in it - LyShine::RenderGraph* lyRenderGraph = static_cast(renderGraph); // LYSHINE_ATOM_TODO - find a different solution from downcasting + LyShine::RenderGraph* lyRenderGraph = dynamic_cast(renderGraph); // LYSHINE_ATOM_TODO - find a different solution from downcasting if (lyRenderGraph) { lyRenderGraph->AddPrimitiveAtom(&m_cachedPrimitive, image, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, m_blendMode); @@ -880,8 +885,7 @@ float UiImageComponent::GetTargetWidth(float /*maxWidth*/) { float targetWidth = 0.0f; - ITexture* texture = (m_sprite) ? m_sprite->GetTexture() : nullptr; - if (texture) + if (m_sprite) { switch (m_imageType) { @@ -915,8 +919,7 @@ float UiImageComponent::GetTargetHeight(float /*maxHeight*/) { float targetHeight = 0.0f; - ITexture* texture = (m_sprite) ? m_sprite->GetTexture() : nullptr; - if (texture) + if (m_sprite) { switch (m_imageType) { @@ -2363,7 +2366,8 @@ void UiImageComponent::SnapOffsetsToFixedImage() } // if the image has no texture it will not use Fixed rendering so do nothing - if (!m_sprite || !m_sprite->GetTexture()) + AZ::Data::Instance image = GetSpriteImage(m_sprite); + if (!image) { return; } diff --git a/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp b/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp index e6cbef8386..df77a003ae 100644 --- a/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp +++ b/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp @@ -12,6 +12,9 @@ #include "LyShine_precompiled.h" #include "UiImageSequenceComponent.h" +#include "Sprite.h" +#include "RenderGraph.h" + #include #include #include @@ -100,7 +103,7 @@ void UiImageSequenceComponent::Render(LyShine::IRenderGraph* renderGraph) return; } - ISprite* sprite = m_spriteList[m_sequenceIndex]; + CSprite* sprite = dynamic_cast(m_spriteList[m_sequenceIndex]); // get fade value (tracked by UiRenderer) and compute the desired alpha for the image float fade = renderGraph->GetAlphaFade(); @@ -158,15 +161,23 @@ void UiImageSequenceComponent::Render(LyShine::IRenderGraph* renderGraph) } } - ITexture* texture = (sprite) ? sprite->GetTexture() : nullptr; + AZ::Data::Instance image; + if (sprite) + { + image = sprite->GetImage(); + } bool isClampTextureMode = false; bool isTextureSRGB = false; bool isTexturePremultipliedAlpha = false; LyShine::BlendMode blendMode = LyShine::BlendMode::Normal; // Add the quad to the render graph - renderGraph->AddPrimitive(&m_cachedPrimitive, texture, - isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode); + LyShine::RenderGraph* lyRenderGraph = dynamic_cast(renderGraph); + if (lyRenderGraph) + { + lyRenderGraph->AddPrimitiveAtom(&m_cachedPrimitive, image, + isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode); + } } } diff --git a/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp b/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp index e54f61fbfa..3c9a871847 100644 --- a/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp +++ b/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp @@ -13,6 +13,8 @@ #include "UiParticleEmitterComponent.h" #include "EditorPropertyTypes.h" +#include "Sprite.h" +#include "RenderGraph.h" #include #include @@ -21,7 +23,6 @@ #include #include -#include #include #include @@ -764,6 +765,12 @@ void UiParticleEmitterComponent::InGamePostActivate() //////////////////////////////////////////////////////////////////////////////////////////////////// void UiParticleEmitterComponent::Render(LyShine::IRenderGraph* renderGraph) { + AZ::u32 particlesToRender = AZ::GetMin(m_particleContainer.size(), m_particleBufferSize); + if (particlesToRender == 0) + { + return; + } + AZ::Matrix4x4 transform = AZ::Matrix4x4::CreateIdentity(); AZ::Vector2 emitterOffset = AZ::Vector2::CreateZero(); @@ -781,9 +788,15 @@ void UiParticleEmitterComponent::Render(LyShine::IRenderGraph* renderGraph) EBUS_EVENT_ID_RESULT(transform, canvasID, UiCanvasBus, GetCanvasToViewportMatrix); } - AZ::u32 particlesToRender = AZ::GetMin(m_particleContainer.size(), m_particleBufferSize); - - ITexture* texture = (m_sprite) ? m_sprite->GetTexture() : nullptr; + AZ::Data::Instance image; + if (m_sprite) + { + CSprite* sprite = dynamic_cast(m_sprite); + if (sprite) + { + image = sprite->GetImage(); + } + } bool isClampTextureMode = true; bool isTextureSRGB = false; @@ -836,7 +849,11 @@ void UiParticleEmitterComponent::Render(LyShine::IRenderGraph* renderGraph) m_cachedPrimitive.m_numVertices = totalVerticesInserted; m_cachedPrimitive.m_numIndices = totalParticlesInserted * indicesPerParticle; - renderGraph->AddPrimitive(&m_cachedPrimitive, texture, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, m_blendMode); + LyShine::RenderGraph* lyRenderGraph = dynamic_cast(renderGraph); + if (lyRenderGraph) + { + lyRenderGraph->AddPrimitiveAtom(&m_cachedPrimitive, image, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, m_blendMode); + } } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/LyShine/Code/Tests/SpriteTest.cpp b/Gems/LyShine/Code/Tests/SpriteTest.cpp index 1d45a6113c..29ef1eb7f2 100644 --- a/Gems/LyShine/Code/Tests/SpriteTest.cpp +++ b/Gems/LyShine/Code/Tests/SpriteTest.cpp @@ -68,6 +68,7 @@ namespace UnitTest AZStd::unique_ptr m_data; }; +#ifdef LYSHINE_ATOM_TODO // [LYN-3359] - render target support using Atom TEST_F(LyShineSpriteTest, Sprite_CanAcquireRenderTarget) { // initialize to create the static sprite cache @@ -130,6 +131,7 @@ namespace UnitTest CSprite::Shutdown(); delete mockTexture; } +#endif } //namespace UnitTest AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp b/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp index 1bd2bd10be..b2055f2a0c 100644 --- a/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp +++ b/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp @@ -78,8 +78,9 @@ namespace LyShineExamples } //////////////////////////////////////////////////////////////////////////////////////////////////// - void UiCustomImageComponent::Render(LyShine::IRenderGraph* renderGraph) + void UiCustomImageComponent::Render([[maybe_unused]] LyShine::IRenderGraph* renderGraph) { +#ifdef LYSHINE_ATOM_TODO // [LYN-3635] convert to use Atom // get fade value (tracked by UiRenderer) and compute the desired alpha for the image float fade = renderGraph->GetAlphaFade(); float desiredAlpha = m_overrideAlpha * fade; @@ -126,6 +127,7 @@ namespace LyShineExamples bool isTexturePremultipliedAlpha = false; // we are not rendering from a render target with alpha in it LyShine::BlendMode blendMode = LyShine::BlendMode::Normal; renderGraph->AddPrimitive(&m_cachedPrimitive, texture, m_clamp, isTextureSRGB, isTexturePremultipliedAlpha, blendMode); +#endif } ////////////////////////////////////////////////////////////////////////////////////////////////////