UiCustomImageComponent.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "UiCustomImageComponent.h"
  9. #include <LyShineExamples/UiCustomImageBus.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <LyShine/IDraw2d.h>
  14. #include <LyShine/ISprite.h>
  15. #include <LyShine/Bus/UiElementBus.h>
  16. #include <LyShine/Bus/UiCanvasBus.h>
  17. #include <LyShine/Bus/UiTransformBus.h>
  18. namespace LyShineExamples
  19. {
  20. ////////////////////////////////////////////////////////////////////////////////////////////////////
  21. // PUBLIC MEMBER FUNCTIONS
  22. ////////////////////////////////////////////////////////////////////////////////////////////////////
  23. ////////////////////////////////////////////////////////////////////////////////////////////////////
  24. UiCustomImageComponent::UiCustomImageComponent()
  25. : m_color(1.f, 1.f, 1.f, 1.f)
  26. , m_alpha(1.f)
  27. , m_sprite(nullptr)
  28. , m_uvs(0, 0, 1, 1)
  29. , m_clamp(true)
  30. , m_overrideColor(m_color)
  31. , m_overrideAlpha(m_alpha)
  32. , m_overrideSprite(nullptr)
  33. {
  34. }
  35. ////////////////////////////////////////////////////////////////////////////////////////////////////
  36. UiCustomImageComponent::~UiCustomImageComponent()
  37. {
  38. SAFE_RELEASE(m_sprite);
  39. }
  40. ////////////////////////////////////////////////////////////////////////////////////////////////////
  41. void UiCustomImageComponent::ResetOverrides()
  42. {
  43. m_overrideColor = m_color;
  44. m_overrideAlpha = m_alpha;
  45. m_overrideSprite = nullptr;
  46. }
  47. ////////////////////////////////////////////////////////////////////////////////////////////////////
  48. void UiCustomImageComponent::SetOverrideColor(const AZ::Color& color)
  49. {
  50. m_overrideColor.Set(color.GetAsVector3());
  51. }
  52. ////////////////////////////////////////////////////////////////////////////////////////////////////
  53. void UiCustomImageComponent::SetOverrideAlpha(float alpha)
  54. {
  55. m_overrideAlpha = alpha;
  56. }
  57. ////////////////////////////////////////////////////////////////////////////////////////////////////
  58. void UiCustomImageComponent::SetOverrideSprite(ISprite* sprite, AZ::u32 /* cellIndex */)
  59. {
  60. m_overrideSprite = sprite;
  61. }
  62. ////////////////////////////////////////////////////////////////////////////////////////////////////
  63. void UiCustomImageComponent::Render(LyShine::IRenderGraph* renderGraph)
  64. {
  65. // get fade value (tracked by UiRenderer) and compute the desired alpha for the image
  66. float fade = renderGraph->GetAlphaFade();
  67. float desiredAlpha = m_overrideAlpha * fade;
  68. uint8 desiredPackedAlpha = static_cast<uint8>(desiredAlpha * 255.0f);
  69. if (m_isRenderCacheDirty)
  70. {
  71. RenderToCache(renderGraph);
  72. m_isRenderCacheDirty = false;
  73. }
  74. // if desired alpha is zero then no need to do any more
  75. if (desiredPackedAlpha == 0)
  76. {
  77. return;
  78. }
  79. // Render cache is now valid - render using the cache
  80. // If the fade value has changed we need to update the alpha values in the vertex colors but we do
  81. // not want to touch or recompute the RGB values
  82. if (m_cachedPrimitive.m_vertices[0].color.a != desiredPackedAlpha)
  83. {
  84. // go through all the cached vertices and update the alpha values
  85. LyShine::UCol desiredPackedColor = m_cachedPrimitive.m_vertices[0].color;
  86. desiredPackedColor.a = desiredPackedAlpha;
  87. for (int i = 0; i < m_cachedPrimitive.m_numVertices; ++i)
  88. {
  89. m_cachedPrimitive.m_vertices[i].color = desiredPackedColor;
  90. }
  91. }
  92. ISprite* sprite = (m_overrideSprite) ? m_overrideSprite : m_sprite;
  93. AZ::Data::Instance<AZ::RPI::Image> image;
  94. if (sprite != nullptr)
  95. {
  96. image = sprite->GetImage();
  97. }
  98. bool isTextureSRGB = false;
  99. bool isTexturePremultipliedAlpha = false; // we are not rendering from a render target with alpha in it
  100. LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
  101. renderGraph->AddPrimitive(&m_cachedPrimitive, image, m_clamp, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
  102. }
  103. ////////////////////////////////////////////////////////////////////////////////////////////////////
  104. AZ::Color UiCustomImageComponent::GetColor()
  105. {
  106. return AZ::Color::CreateFromVector3AndFloat(m_color.GetAsVector3(), m_alpha);
  107. }
  108. ////////////////////////////////////////////////////////////////////////////////////////////////////
  109. void UiCustomImageComponent::SetColor(const AZ::Color& color)
  110. {
  111. m_color.Set(color.GetAsVector3());
  112. m_alpha = color.GetA();
  113. m_overrideColor = m_color;
  114. m_overrideAlpha = m_alpha;
  115. MarkRenderCacheDirty();
  116. }
  117. ////////////////////////////////////////////////////////////////////////////////////////////////////
  118. ISprite* UiCustomImageComponent::GetSprite()
  119. {
  120. return m_sprite;
  121. }
  122. ////////////////////////////////////////////////////////////////////////////////////////////////////
  123. void UiCustomImageComponent::SetSprite(ISprite* sprite)
  124. {
  125. if (m_sprite)
  126. {
  127. m_sprite->Release();
  128. m_spritePathname.SetAssetPath("");
  129. }
  130. m_sprite = sprite;
  131. if (m_sprite)
  132. {
  133. m_sprite->AddRef();
  134. m_spritePathname.SetAssetPath(m_sprite->GetPathname().c_str());
  135. }
  136. MarkRenderGraphDirty();
  137. }
  138. ////////////////////////////////////////////////////////////////////////////////////////////////////
  139. AZStd::string UiCustomImageComponent::GetSpritePathname()
  140. {
  141. return m_spritePathname.GetAssetPath();
  142. }
  143. ////////////////////////////////////////////////////////////////////////////////////////////////////
  144. void UiCustomImageComponent::SetSpritePathname(AZStd::string spritePath)
  145. {
  146. m_spritePathname.SetAssetPath(spritePath.c_str());
  147. MarkRenderGraphDirty();
  148. }
  149. ////////////////////////////////////////////////////////////////////////////////////////////////////
  150. UiCustomImageInterface::UVRect UiCustomImageComponent::GetUVs()
  151. {
  152. return m_uvs;
  153. }
  154. ////////////////////////////////////////////////////////////////////////////////////////////////////
  155. void UiCustomImageComponent::SetUVs(UiCustomImageInterface::UVRect uvs)
  156. {
  157. m_uvs = uvs;
  158. MarkRenderCacheDirty();
  159. }
  160. ////////////////////////////////////////////////////////////////////////////////////////////////////
  161. bool UiCustomImageComponent::GetClamp()
  162. {
  163. return m_clamp;
  164. }
  165. ////////////////////////////////////////////////////////////////////////////////////////////////////
  166. void UiCustomImageComponent::SetClamp(bool clamp)
  167. {
  168. m_clamp = clamp;
  169. MarkRenderGraphDirty();
  170. }
  171. ////////////////////////////////////////////////////////////////////////////////////////////////////
  172. void UiCustomImageComponent::OnCanvasSpaceRectChanged(AZ::EntityId /*entityId*/, const UiTransformInterface::Rect& /*oldRect*/, const UiTransformInterface::Rect& /*newRect*/)
  173. {
  174. MarkRenderCacheDirty();
  175. }
  176. ////////////////////////////////////////////////////////////////////////////////////////////////////
  177. void UiCustomImageComponent::OnTransformToViewportChanged()
  178. {
  179. MarkRenderCacheDirty();
  180. }
  181. ////////////////////////////////////////////////////////////////////////////////////////////////////
  182. // PUBLIC STATIC MEMBER FUNCTIONS
  183. ////////////////////////////////////////////////////////////////////////////////////////////////////
  184. ////////////////////////////////////////////////////////////////////////////////////////////////////
  185. void UiCustomImageComponent::Reflect(AZ::ReflectContext* context)
  186. {
  187. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  188. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  189. // Serialize this component
  190. if (serializeContext)
  191. {
  192. serializeContext->Class<UiCustomImageComponent, AZ::Component>()
  193. ->Field("SpritePath", &UiCustomImageComponent::m_spritePathname)
  194. ->Field("Color", &UiCustomImageComponent::m_color)
  195. ->Field("Alpha", &UiCustomImageComponent::m_alpha)
  196. ->Field("UVCoords", &UiCustomImageComponent::m_uvs)
  197. ->Field("Clamp", &UiCustomImageComponent::m_clamp);
  198. AZ::EditContext* ec = serializeContext->GetEditContext();
  199. if (ec)
  200. {
  201. auto editInfo = ec->Class<UiCustomImageComponent>("Custom Image", "A visual component to draw a rectangle with an optional sprite/texture");
  202. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  203. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiImage.png")
  204. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiImage.png")
  205. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("UI"))
  206. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  207. editInfo->DataElement("Sprite", &UiCustomImageComponent::m_spritePathname, "Sprite path", "The sprite path. Can be overridden by another component such as an interactable.")
  208. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiCustomImageComponent::OnSpritePathnameChange);
  209. editInfo->DataElement(AZ::Edit::UIHandlers::Color, &UiCustomImageComponent::m_color, "Color", "The color tint for the image. Can be overridden by another component such as an interactable.")
  210. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiCustomImageComponent::OnColorChange);
  211. editInfo->DataElement(AZ::Edit::UIHandlers::Slider, &UiCustomImageComponent::m_alpha, "Alpha", "The transparency. Can be overridden by another component such as an interactable.")
  212. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiCustomImageComponent::OnColorChange)
  213. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  214. ->Attribute(AZ::Edit::Attributes::Max, 1.0f);
  215. editInfo->DataElement(0, &UiCustomImageComponent::m_uvs, "UV Rect", "The UV coordinates of the rectangle for rendering the texture.")
  216. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiCustomImageComponent::OnRenderSettingChange)
  217. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshValues"))
  218. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show); // needed because sub-elements are hidden
  219. editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiCustomImageComponent::m_clamp, "Clamp", "Whether the image should be clamped or not.")
  220. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiCustomImageComponent::OnRenderSettingChange)
  221. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshValues"));
  222. }
  223. }
  224. if (behaviorContext)
  225. {
  226. behaviorContext->EBus<UiCustomImageBus>("UiCustomImageBus")
  227. ->Event("GetColor", &UiCustomImageBus::Events::GetColor)
  228. ->Event("SetColor", &UiCustomImageBus::Events::SetColor)
  229. ->Event("GetSpritePathname", &UiCustomImageBus::Events::GetSpritePathname)
  230. ->Event("SetSpritePathname", &UiCustomImageBus::Events::SetSpritePathname)
  231. ->Event("GetUVs", &UiCustomImageBus::Events::GetUVs)
  232. ->Event("SetUVs", &UiCustomImageBus::Events::SetUVs)
  233. ->Event("GetClamp", &UiCustomImageBus::Events::GetClamp)
  234. ->Event("SetClamp", &UiCustomImageBus::Events::SetClamp);
  235. }
  236. }
  237. ////////////////////////////////////////////////////////////////////////////////////////////////////
  238. // PROTECTED MEMBER FUNCTIONS
  239. ////////////////////////////////////////////////////////////////////////////////////////////////////
  240. ////////////////////////////////////////////////////////////////////////////////////////////////////
  241. void UiCustomImageComponent::Init()
  242. {
  243. // If this is called from RC.exe for example these pointers will not be set. In that case
  244. // we only need to be able to load, init and save the component. It will never be
  245. // activated.
  246. if (!AZ::Interface<ILyShine>::Get())
  247. {
  248. return;
  249. }
  250. // Load our sprite from the path at the beginning of the game
  251. if (!m_sprite)
  252. {
  253. if (!m_spritePathname.GetAssetPath().empty())
  254. {
  255. m_sprite = AZ::Interface<ILyShine>::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str());
  256. }
  257. }
  258. m_overrideColor = m_color;
  259. m_overrideAlpha = m_alpha;
  260. }
  261. ////////////////////////////////////////////////////////////////////////////////////////////////////
  262. void UiCustomImageComponent::Activate()
  263. {
  264. UiVisualBus::Handler::BusConnect(m_entity->GetId());
  265. UiRenderBus::Handler::BusConnect(m_entity->GetId());
  266. UiCustomImageBus::Handler::BusConnect(m_entity->GetId());
  267. UiTransformChangeNotificationBus::Handler::BusConnect(m_entity->GetId());
  268. }
  269. ////////////////////////////////////////////////////////////////////////////////////////////////////
  270. void UiCustomImageComponent::Deactivate()
  271. {
  272. UiVisualBus::Handler::BusDisconnect();
  273. UiRenderBus::Handler::BusDisconnect();
  274. UiCustomImageBus::Handler::BusDisconnect();
  275. UiTransformChangeNotificationBus::Handler::BusDisconnect();
  276. }
  277. ////////////////////////////////////////////////////////////////////////////////////////////////////
  278. // PRIVATE MEMBER FUNCTIONS
  279. ////////////////////////////////////////////////////////////////////////////////////////////////////
  280. ////////////////////////////////////////////////////////////////////////////////////////////////////
  281. void UiCustomImageComponent::RenderToCache(LyShine::IRenderGraph* renderGraph)
  282. {
  283. UiTransformInterface::RectPoints points;
  284. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetViewportSpacePoints, points);
  285. // points are a clockwise quad
  286. const AZ::Vector2 uvs[4] = {
  287. AZ::Vector2(m_uvs.m_left, m_uvs.m_top), AZ::Vector2(m_uvs.m_right, m_uvs.m_top)
  288. , AZ::Vector2(m_uvs.m_right, m_uvs.m_bottom), AZ::Vector2(m_uvs.m_left, m_uvs.m_bottom)
  289. };
  290. RenderSingleQuad(renderGraph, points.pt, uvs);
  291. }
  292. ////////////////////////////////////////////////////////////////////////////////////////////////////
  293. void UiCustomImageComponent::RenderSingleQuad(LyShine::IRenderGraph* renderGraph, const AZ::Vector2* positions, const AZ::Vector2* uvs)
  294. {
  295. float fade = renderGraph->GetAlphaFade();
  296. float desiredAlpha = m_overrideAlpha * fade;
  297. AZ::Color color = AZ::Color::CreateFromVector3AndFloat(m_overrideColor.GetAsVector3(), desiredAlpha);
  298. color = color.GammaToLinear(); // the colors are specified in sRGB but we want linear colors in the shader
  299. uint32 packedColor = (color.GetA8() << 24) | (color.GetR8() << 16) | (color.GetG8() << 8) | color.GetB8();
  300. IDraw2d::Rounding pixelRounding = IsPixelAligned() ? IDraw2d::Rounding::Nearest : IDraw2d::Rounding::None;
  301. const int numVertices = 4;
  302. if (numVertices != m_cachedPrimitive.m_numVertices)
  303. {
  304. if (m_cachedPrimitive.m_vertices)
  305. {
  306. delete [] m_cachedPrimitive.m_vertices;
  307. }
  308. m_cachedPrimitive.m_vertices = new LyShine::UiPrimitiveVertex[numVertices];
  309. m_cachedPrimitive.m_numVertices = numVertices;
  310. }
  311. // points are a clockwise quad
  312. for (int i = 0; i < numVertices; ++i)
  313. {
  314. AZ::Vector2 pos = Draw2dHelper::RoundXY(positions[i], pixelRounding);
  315. m_cachedPrimitive.m_vertices[i].xy = Vec2(pos.GetX(), pos.GetY());
  316. m_cachedPrimitive.m_vertices[i].color.dcolor = packedColor;
  317. m_cachedPrimitive.m_vertices[i].st = Vec2(uvs[i].GetX(), uvs[i].GetY());
  318. m_cachedPrimitive.m_vertices[i].texIndex = 0;
  319. m_cachedPrimitive.m_vertices[i].texHasColorChannel = 1;
  320. m_cachedPrimitive.m_vertices[i].texIndex2 = 0;
  321. m_cachedPrimitive.m_vertices[i].pad = 0;
  322. }
  323. static uint16 indices[6] = { 0, 1, 2, 2, 3, 0 };
  324. m_cachedPrimitive.m_numIndices = 6;
  325. m_cachedPrimitive.m_indices = indices;
  326. }
  327. ////////////////////////////////////////////////////////////////////////////////////////////////////
  328. bool UiCustomImageComponent::IsPixelAligned()
  329. {
  330. AZ::EntityId canvasEntityId;
  331. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  332. bool isPixelAligned = true;
  333. UiCanvasBus::EventResult(isPixelAligned, canvasEntityId, &UiCanvasBus::Events::GetIsPixelAligned);
  334. return isPixelAligned;
  335. }
  336. ////////////////////////////////////////////////////////////////////////////////////////////////////
  337. void UiCustomImageComponent::OnSpritePathnameChange()
  338. {
  339. ISprite* newSprite = nullptr;
  340. if (!m_spritePathname.GetAssetPath().empty())
  341. {
  342. // Load the new texture.
  343. newSprite = AZ::Interface<ILyShine>::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str());
  344. }
  345. SAFE_RELEASE(m_sprite);
  346. m_sprite = newSprite;
  347. MarkRenderGraphDirty();
  348. }
  349. ////////////////////////////////////////////////////////////////////////////////////////////////////
  350. void UiCustomImageComponent::OnColorChange()
  351. {
  352. m_overrideColor = m_color;
  353. m_overrideAlpha = m_alpha;
  354. MarkRenderCacheDirty();
  355. }
  356. ////////////////////////////////////////////////////////////////////////////////////////////////////
  357. void UiCustomImageComponent::OnRenderSettingChange()
  358. {
  359. MarkRenderCacheDirty();
  360. }
  361. ////////////////////////////////////////////////////////////////////////////////////////////////////
  362. void UiCustomImageComponent::MarkRenderCacheDirty()
  363. {
  364. if (!m_isRenderCacheDirty)
  365. {
  366. m_isRenderCacheDirty = true;
  367. MarkRenderGraphDirty();
  368. }
  369. }
  370. ////////////////////////////////////////////////////////////////////////////////////////////////////
  371. void UiCustomImageComponent::MarkRenderGraphDirty()
  372. {
  373. // tell the canvas to invalidate the render graph (never want to do this while rendering)
  374. AZ::EntityId canvasEntityId;
  375. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  376. UiCanvasComponentImplementationBus::Event(canvasEntityId, &UiCanvasComponentImplementationBus::Events::MarkRenderGraphDirty);
  377. }
  378. }