UiFaderComponent.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  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 "UiFaderComponent.h"
  9. #include "RenderGraph.h"
  10. #include <LyShine/IDraw2d.h>
  11. #include <AzCore/Math/Crc.h>
  12. #include <AzCore/Math/MathUtils.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <AzCore/Serialization/EditContext.h>
  15. #include <AzCore/RTTI/BehaviorContext.h>
  16. #include <Atom/RPI.Public/Image/AttachmentImage.h>
  17. #include <AtomCore/Instance/Instance.h>
  18. #include <LyShine/Bus/UiElementBus.h>
  19. #include <LyShine/Bus/UiRenderBus.h>
  20. #include <LyShine/Bus/UiCanvasBus.h>
  21. #include <LyShine/IRenderGraph.h>
  22. #include "UiSerialize.h"
  23. #include "RenderToTextureBus.h"
  24. // BehaviorContext UiFaderNotificationBus forwarder
  25. class BehaviorUiFaderNotificationBusHandler
  26. : public UiFaderNotificationBus::Handler
  27. , public AZ::BehaviorEBusHandler
  28. {
  29. public:
  30. AZ_EBUS_BEHAVIOR_BINDER(BehaviorUiFaderNotificationBusHandler, "{CAD44770-3D5E-4E67-8F05-D2A89E8C501A}", AZ::SystemAllocator,
  31. OnFadeComplete, OnFadeInterrupted, OnFaderDestroyed);
  32. void OnFadeComplete() override
  33. {
  34. Call(FN_OnFadeComplete);
  35. }
  36. void OnFadeInterrupted() override
  37. {
  38. Call(FN_OnFadeInterrupted);
  39. }
  40. void OnFaderDestroyed() override
  41. {
  42. Call(FN_OnFaderDestroyed);
  43. }
  44. };
  45. ////////////////////////////////////////////////////////////////////////////////////////////////////
  46. // PUBLIC MEMBER FUNCTIONS
  47. ////////////////////////////////////////////////////////////////////////////////////////////////////
  48. ////////////////////////////////////////////////////////////////////////////////////////////////////
  49. UiFaderComponent::UiFaderComponent()
  50. : m_fade(1.0f)
  51. , m_isFading(false)
  52. , m_fadeTarget(1.0f)
  53. , m_fadeSpeedInSeconds(1.0f)
  54. {
  55. m_cachedPrimitive.m_vertices = nullptr;
  56. m_cachedPrimitive.m_numVertices = 0;
  57. m_cachedPrimitive.m_indices = nullptr;
  58. m_cachedPrimitive.m_numIndices = 0;
  59. }
  60. ////////////////////////////////////////////////////////////////////////////////////////////////////
  61. UiFaderComponent::~UiFaderComponent()
  62. {
  63. if (m_isFading && m_entity)
  64. {
  65. UiFaderNotificationBus::Event(GetEntityId(), &UiFaderNotificationBus::Events::OnFaderDestroyed);
  66. }
  67. DestroyRenderTarget();
  68. // We only deallocate the vertices on destruction rather than every time we recreate the render
  69. // target. Changing the size of the element requires recreating render target but doesn't change
  70. // the number of vertices. Note this may be nullptr which is fine for delete.
  71. delete [] m_cachedPrimitive.m_vertices;
  72. }
  73. ////////////////////////////////////////////////////////////////////////////////////////////////////
  74. void UiFaderComponent::Update(float deltaTime)
  75. {
  76. if (!m_isFading)
  77. {
  78. return;
  79. }
  80. // Update fade
  81. SetFadeValueInternal(m_fade + m_fadeSpeedInSeconds * deltaTime);
  82. // Check for completion
  83. if (m_fadeSpeedInSeconds == 0 ||
  84. m_fadeSpeedInSeconds > 0 && m_fade >= m_fadeTarget ||
  85. m_fadeSpeedInSeconds < 0 && m_fade <= m_fadeTarget)
  86. {
  87. CompleteFade();
  88. }
  89. }
  90. ////////////////////////////////////////////////////////////////////////////////////////////////////
  91. void UiFaderComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInterface* elementInterface,
  92. UiRenderInterface* renderInterface, int numChildren, bool isInGame)
  93. {
  94. static const float epsilon = 1.0f / 255.0f; // less than this value means alpha will be zero when converted to a uint8
  95. // if the fader is at (or close to) zero then do not render this element or its children at all
  96. if (m_fade < epsilon)
  97. {
  98. return;
  99. }
  100. if (GetUseRenderToTexture())
  101. {
  102. AZ::Vector2 pixelAlignedTopLeft, pixelAlignedBottomRight;
  103. ComputePixelAlignedBounds(pixelAlignedTopLeft, pixelAlignedBottomRight);
  104. AZ::Vector2 renderTargetSize = pixelAlignedBottomRight - pixelAlignedTopLeft;
  105. bool needsResize = static_cast<int>(renderTargetSize.GetX()) != m_renderTargetWidth || static_cast<int>(renderTargetSize.GetY()) != m_renderTargetHeight;
  106. if (m_attachmentImageId.IsEmpty() || needsResize)
  107. {
  108. // We delay first creation of the render target until render time since size is not known in Activate
  109. // We also call this if the size has changed
  110. CreateOrResizeRenderTarget(pixelAlignedTopLeft, pixelAlignedBottomRight);
  111. }
  112. // if the render target failed to be created (zero size for example) we don't render the element at all
  113. if (m_attachmentImageId.IsEmpty())
  114. {
  115. return;
  116. }
  117. // Do render-to-texture fade, this renders this element and its children to a render target, then renders that
  118. RenderRttFader(renderGraph, elementInterface, renderInterface, numChildren, isInGame);
  119. }
  120. else
  121. {
  122. // destroy previous render target, if exists
  123. if (!m_attachmentImageId.IsEmpty())
  124. {
  125. DestroyRenderTarget();
  126. }
  127. // do standard (non-render-to-texture) fade, this renders this element and its children
  128. RenderStandardFader(renderGraph, elementInterface, renderInterface, numChildren, isInGame);
  129. }
  130. }
  131. ////////////////////////////////////////////////////////////////////////////////////////////////////
  132. float UiFaderComponent::GetFadeValue()
  133. {
  134. return m_fade;
  135. }
  136. ////////////////////////////////////////////////////////////////////////////////////////////////////
  137. void UiFaderComponent::SetFadeValue(float fade)
  138. {
  139. if (m_isFading)
  140. {
  141. UiFaderNotificationBus::Event(GetEntityId(), &UiFaderNotificationBus::Events::OnFadeInterrupted);
  142. m_isFading = false;
  143. }
  144. SetFadeValueInternal(fade);
  145. }
  146. ////////////////////////////////////////////////////////////////////////////////////////////////////
  147. void UiFaderComponent::Fade(float targetValue, float speed)
  148. {
  149. if (m_isFading)
  150. {
  151. UiFaderNotificationBus::Event(GetEntityId(), &UiFaderNotificationBus::Events::OnFadeInterrupted);
  152. }
  153. // Connect to UpdateBus for updates while fading
  154. if (!UiCanvasUpdateNotificationBus::Handler::BusIsConnected())
  155. {
  156. AZ::EntityId canvasEntityId;
  157. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  158. // if this element has not been fixed up then canvasEntityId will be invalid. We handle this
  159. // in OnUiElementFixup
  160. if (canvasEntityId.IsValid())
  161. {
  162. UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
  163. }
  164. }
  165. m_isFading = true;
  166. m_fadeTarget = clamp_tpl(targetValue, 0.0f, 1.0f);
  167. // Give speed a direction
  168. float fadeChange = m_fadeTarget - m_fade;
  169. float fadeDirection = fadeChange >= 0.0f ? 1.0f : -1.0f;
  170. m_fadeSpeedInSeconds = fadeDirection * speed;
  171. }
  172. ////////////////////////////////////////////////////////////////////////////////////////////////////
  173. bool UiFaderComponent::IsFading()
  174. {
  175. return m_isFading;
  176. }
  177. ////////////////////////////////////////////////////////////////////////////////////////////////////
  178. bool UiFaderComponent::GetUseRenderToTexture()
  179. {
  180. return m_useRenderToTexture;
  181. }
  182. ////////////////////////////////////////////////////////////////////////////////////////////////////
  183. void UiFaderComponent::SetUseRenderToTexture(bool useRenderToTexture)
  184. {
  185. if (GetUseRenderToTexture() != useRenderToTexture)
  186. {
  187. m_useRenderToTexture = useRenderToTexture;
  188. OnRenderTargetChange();
  189. }
  190. }
  191. ////////////////////////////////////////////////////////////////////////////////////////////////////
  192. void UiFaderComponent::PropertyValuesChanged()
  193. {
  194. MarkRenderGraphDirty();
  195. }
  196. ////////////////////////////////////////////////////////////////////////////////////////////////////
  197. void UiFaderComponent::OnUiElementFixup(AZ::EntityId canvasEntityId, [[maybe_unused]] AZ::EntityId parentEntityId)
  198. {
  199. // If we are fading but not already connected to UpdateBus for updates then connect
  200. // This would only happen if Fade was called during activate (before fixup)
  201. if (m_isFading && !UiCanvasUpdateNotificationBus::Handler::BusIsConnected())
  202. {
  203. UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
  204. }
  205. }
  206. ////////////////////////////////////////////////////////////////////////////////////////////////////
  207. void UiFaderComponent::OnCanvasSpaceRectChanged(AZ::EntityId /*entityId*/, const UiTransformInterface::Rect& /*oldRect*/, const UiTransformInterface::Rect& /*newRect*/)
  208. {
  209. // we only listen for this if using render target, if rect changed recreate render target
  210. OnRenderTargetChange();
  211. }
  212. ////////////////////////////////////////////////////////////////////////////////////////////////////
  213. void UiFaderComponent::OnTransformToViewportChanged()
  214. {
  215. // we only listen for this if using render target, if transform changed recreate render target
  216. OnRenderTargetChange();
  217. }
  218. ////////////////////////////////////////////////////////////////////////////////////////////////////
  219. // PUBLIC STATIC MEMBER FUNCTIONS
  220. ////////////////////////////////////////////////////////////////////////////////////////////////////
  221. void UiFaderComponent::Reflect(AZ::ReflectContext* context)
  222. {
  223. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  224. if (serializeContext)
  225. {
  226. serializeContext->Class<UiFaderComponent, AZ::Component>()
  227. ->Version(1)
  228. ->Field("Fade", &UiFaderComponent::m_fade)
  229. ->Field("UseRenderToTexture", &UiFaderComponent::m_useRenderToTexture);
  230. AZ::EditContext* ec = serializeContext->GetEditContext();
  231. if (ec)
  232. {
  233. auto editInfo = ec->Class<UiFaderComponent>("Fader", "A component that can fade its element and all its child elements");
  234. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  235. ->Attribute(AZ::Edit::Attributes::Category, "UI")
  236. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiFader.png")
  237. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiFader.png")
  238. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("UI"))
  239. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  240. editInfo->DataElement(AZ::Edit::UIHandlers::Slider, &UiFaderComponent::m_fade, "Fade", "The initial fade value")
  241. ->Attribute(AZ::Edit::Attributes::Step, 0.01f)
  242. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  243. ->Attribute(AZ::Edit::Attributes::Max, 1.0f)
  244. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiFaderComponent::OnFadeValueChanged);
  245. editInfo->DataElement(0, &UiFaderComponent::m_useRenderToTexture, "Use render to texture",
  246. "If true, this element and all children are rendered to a separate render target\n"
  247. "and then that target is rendered to the screen. This avoids child elements\n"
  248. "blending with each other as they fade. But it is more expensive.")
  249. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiFaderComponent::OnRenderTargetChange);
  250. }
  251. }
  252. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  253. if (behaviorContext)
  254. {
  255. behaviorContext->EBus<UiFaderBus>("UiFaderBus")
  256. ->Event("GetFadeValue", &UiFaderBus::Events::GetFadeValue)
  257. ->Event("SetFadeValue", &UiFaderBus::Events::SetFadeValue)
  258. ->Event("Fade", &UiFaderBus::Events::Fade)
  259. ->Event("IsFading", &UiFaderBus::Events::IsFading)
  260. ->Event("GetUseRenderToTexture", &UiFaderBus::Events::GetUseRenderToTexture)
  261. ->Event("SetUseRenderToTexture", &UiFaderBus::Events::SetUseRenderToTexture)
  262. ->VirtualProperty("Fade", "GetFadeValue", "SetFadeValue");
  263. behaviorContext->Class<UiFaderComponent>()->RequestBus("UiFaderBus");
  264. behaviorContext->EBus<UiFaderNotificationBus>("UiFaderNotificationBus")
  265. ->Handler<BehaviorUiFaderNotificationBusHandler>();
  266. }
  267. }
  268. ////////////////////////////////////////////////////////////////////////////////////////////////////
  269. // PROTECTED MEMBER FUNCTIONS
  270. ////////////////////////////////////////////////////////////////////////////////////////////////////
  271. ////////////////////////////////////////////////////////////////////////////////////////////////////
  272. void UiFaderComponent::Activate()
  273. {
  274. UiRenderControlBus::Handler::BusConnect(GetEntityId());
  275. UiFaderBus::Handler::BusConnect(GetEntityId());
  276. UiAnimateEntityBus::Handler::BusConnect(GetEntityId());
  277. UiElementNotificationBus::Handler::BusConnect(GetEntityId());
  278. if (GetUseRenderToTexture())
  279. {
  280. UiTransformChangeNotificationBus::Handler::BusConnect(m_entity->GetId());
  281. }
  282. // The first time the component is activated we don't want to connect to the update bus. However if
  283. // the element starts a fade and then we deactivate and reactivate we need to reconnect to the
  284. // update bus.
  285. if (m_isFading)
  286. {
  287. AZ::EntityId canvasEntityId;
  288. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  289. if (canvasEntityId.IsValid())
  290. {
  291. UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
  292. }
  293. }
  294. // set the render target name to an automatically generated name based on entity Id
  295. m_renderTargetName = "FaderTarget_";
  296. m_renderTargetName += GetEntityId().ToString();
  297. }
  298. ////////////////////////////////////////////////////////////////////////////////////////////////////
  299. void UiFaderComponent::Deactivate()
  300. {
  301. UiRenderControlBus::Handler::BusDisconnect();
  302. UiFaderBus::Handler::BusDisconnect();
  303. UiAnimateEntityBus::Handler::BusDisconnect();
  304. UiElementNotificationBus::Handler::BusDisconnect();
  305. if (UiTransformChangeNotificationBus::Handler::BusIsConnected())
  306. {
  307. UiTransformChangeNotificationBus::Handler::BusDisconnect();
  308. }
  309. // if deactivated during a fade we either have to cancel the fade or rely on activate reconnecting
  310. // to the UpdateBus. We do the latter.
  311. if (UiCanvasUpdateNotificationBus::Handler::BusIsConnected())
  312. {
  313. UiCanvasUpdateNotificationBus::Handler::BusDisconnect();
  314. }
  315. }
  316. ////////////////////////////////////////////////////////////////////////////////////////////////////
  317. void UiFaderComponent::CompleteFade()
  318. {
  319. SetFadeValueInternal(m_fadeTarget);
  320. // Queue the OnFadeComplete event to prevent deletions during the canvas update
  321. UiFaderNotificationBus::QueueEvent(GetEntityId(), &UiFaderNotificationBus::Events::OnFadeComplete);
  322. m_isFading = false;
  323. // Disconnect from UpdateBus
  324. if (UiCanvasUpdateNotificationBus::Handler::BusIsConnected())
  325. {
  326. UiCanvasUpdateNotificationBus::Handler::BusDisconnect();
  327. }
  328. }
  329. ////////////////////////////////////////////////////////////////////////////////////////////////////
  330. void UiFaderComponent::SetFadeValueInternal(float fade)
  331. {
  332. if (m_fade != fade)
  333. {
  334. m_fade = fade;
  335. MarkRenderGraphDirty();
  336. }
  337. }
  338. ////////////////////////////////////////////////////////////////////////////////////////////////////
  339. void UiFaderComponent::OnFadeValueChanged()
  340. {
  341. MarkRenderGraphDirty();
  342. }
  343. ////////////////////////////////////////////////////////////////////////////////////////////////////
  344. void UiFaderComponent::OnRenderTargetChange()
  345. {
  346. // mark render graph dirty so next render will recreate render targets if necessary
  347. MarkRenderGraphDirty();
  348. // update cached primitive to reflect new transforms
  349. AZ::Vector2 pixelAlignedTopLeft, pixelAlignedBottomRight;
  350. ComputePixelAlignedBounds(pixelAlignedTopLeft, pixelAlignedBottomRight);
  351. UpdateCachedPrimitive(pixelAlignedTopLeft, pixelAlignedBottomRight);
  352. // if using a render target we need to know if the element size or position changes since that
  353. // affects the render target and the viewport
  354. if (GetUseRenderToTexture())
  355. {
  356. if (!UiTransformChangeNotificationBus::Handler::BusIsConnected())
  357. {
  358. UiTransformChangeNotificationBus::Handler::BusConnect(m_entity->GetId());
  359. }
  360. }
  361. else
  362. {
  363. if (UiTransformChangeNotificationBus::Handler::BusIsConnected())
  364. {
  365. UiTransformChangeNotificationBus::Handler::BusDisconnect();
  366. }
  367. }
  368. }
  369. ////////////////////////////////////////////////////////////////////////////////////////////////////
  370. void UiFaderComponent::MarkRenderGraphDirty()
  371. {
  372. // tell the canvas to invalidate the render graph
  373. AZ::EntityId canvasEntityId;
  374. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  375. UiCanvasComponentImplementationBus::Event(canvasEntityId, &UiCanvasComponentImplementationBus::Events::MarkRenderGraphDirty);
  376. }
  377. ////////////////////////////////////////////////////////////////////////////////////////////////////
  378. void UiFaderComponent::CreateOrResizeRenderTarget(const AZ::Vector2& pixelAlignedTopLeft, const AZ::Vector2& pixelAlignedBottomRight)
  379. {
  380. // The render target size is the pixel aligned element size.
  381. AZ::Vector2 renderTargetSize = pixelAlignedBottomRight - pixelAlignedTopLeft;
  382. if (renderTargetSize.GetX() <= 0 || renderTargetSize.GetY() <= 0)
  383. {
  384. // if render targets exist then destroy them (just to be in a consistent state)
  385. DestroyRenderTarget();
  386. return;
  387. }
  388. m_viewportTopLeft = pixelAlignedTopLeft;
  389. m_viewportSize = renderTargetSize;
  390. // [LYSHINE_ATOM_TODO][GHI #6271] Optimize by reusing existing render targets
  391. DestroyRenderTarget();
  392. // Create a render target that this element and its children will be rendered to
  393. AZ::EntityId canvasEntityId;
  394. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  395. AZ::RHI::Size imageSize(static_cast<uint32_t>(renderTargetSize.GetX()), static_cast<uint32_t>(renderTargetSize.GetY()), 1);
  396. LyShine::RenderToTextureRequestBus::EventResult(
  397. m_attachmentImageId,
  398. canvasEntityId,
  399. &LyShine::RenderToTextureRequestBus::Events::UseRenderTarget,
  400. AZ::Name(m_renderTargetName.c_str()),
  401. imageSize);
  402. if (m_attachmentImageId.IsEmpty())
  403. {
  404. AZ_Warning("UI", false, "Failed to create render target for UiFaderComponent");
  405. }
  406. // at this point either all render targets and depth surfaces are created or none are.
  407. // If all succeeded then update the render target size
  408. if (!m_attachmentImageId.IsEmpty())
  409. {
  410. m_renderTargetWidth = static_cast<int>(renderTargetSize.GetX());
  411. m_renderTargetHeight = static_cast<int>(renderTargetSize.GetY());
  412. }
  413. UpdateCachedPrimitive(pixelAlignedTopLeft, pixelAlignedBottomRight);
  414. }
  415. ////////////////////////////////////////////////////////////////////////////////////////////////////
  416. void UiFaderComponent::DestroyRenderTarget()
  417. {
  418. if (!m_attachmentImageId.IsEmpty())
  419. {
  420. AZ::EntityId canvasEntityId;
  421. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  422. LyShine::RenderToTextureRequestBus::Event(
  423. canvasEntityId, &LyShine::RenderToTextureRequestBus::Events::ReleaseRenderTarget, m_attachmentImageId);
  424. m_attachmentImageId = AZ::RHI::AttachmentId{};
  425. }
  426. }
  427. ////////////////////////////////////////////////////////////////////////////////////////////////////
  428. void UiFaderComponent::UpdateCachedPrimitive(const AZ::Vector2& pixelAlignedTopLeft, const AZ::Vector2& pixelAlignedBottomRight)
  429. {
  430. // update viewport position
  431. m_viewportTopLeft = pixelAlignedTopLeft;
  432. // now create the verts to render the texture to the screen, we cache these in m_cachedPrimitive
  433. const int numVertices = 4;
  434. if (!m_cachedPrimitive.m_vertices)
  435. {
  436. // verts not yet allocated, allocate them now
  437. const int numIndices = 6;
  438. m_cachedPrimitive.m_vertices = new LyShine::UiPrimitiveVertex[numVertices];
  439. m_cachedPrimitive.m_numVertices = numVertices;
  440. static uint16 indices[numIndices] = { 0, 1, 2, 2, 3, 0 };
  441. m_cachedPrimitive.m_indices = indices;
  442. m_cachedPrimitive.m_numIndices = numIndices;
  443. }
  444. float left = pixelAlignedTopLeft.GetX();
  445. float right = pixelAlignedBottomRight.GetX();
  446. float top = pixelAlignedTopLeft.GetY();
  447. float bottom = pixelAlignedBottomRight.GetY();
  448. Vec2 positions[numVertices] = { Vec2(left, top), Vec2(right, top), Vec2(right, bottom), Vec2(left, bottom) };
  449. static const Vec2 uvs[numVertices] = { {0, 0}, {1, 0}, {1, 1}, {0, 1} };
  450. for (int i = 0; i < numVertices; ++i)
  451. {
  452. m_cachedPrimitive.m_vertices[i].xy = positions[i];
  453. m_cachedPrimitive.m_vertices[i].color.dcolor = 0xFFFFFFFF;
  454. m_cachedPrimitive.m_vertices[i].st = uvs[i];
  455. m_cachedPrimitive.m_vertices[i].texIndex = 0; // this will be set later by render graph
  456. m_cachedPrimitive.m_vertices[i].texHasColorChannel = 1;
  457. m_cachedPrimitive.m_vertices[i].texIndex2 = 0;
  458. m_cachedPrimitive.m_vertices[i].pad = 0;
  459. }
  460. }
  461. ////////////////////////////////////////////////////////////////////////////////////////////////////
  462. void UiFaderComponent::ComputePixelAlignedBounds(AZ::Vector2& pixelAlignedTopLeft, AZ::Vector2& pixelAlignedBottomRight)
  463. {
  464. // The viewport has to be axis aligned so we get the axis-aligned top-left and bottom-right of the element
  465. // in main viewport space. We then snap them to the nearest pixel since the render target has to be an exact number
  466. // of pixels.
  467. UiTransformInterface::RectPoints points;
  468. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetViewportSpacePoints, points);
  469. pixelAlignedTopLeft = Draw2dHelper::RoundXY(points.GetAxisAlignedTopLeft(), IDraw2d::Rounding::Nearest);
  470. pixelAlignedBottomRight = Draw2dHelper::RoundXY(points.GetAxisAlignedBottomRight(), IDraw2d::Rounding::Nearest);
  471. }
  472. ////////////////////////////////////////////////////////////////////////////////////////////////////
  473. void UiFaderComponent::RenderStandardFader(LyShine::IRenderGraph* renderGraph, UiElementInterface* elementInterface,
  474. UiRenderInterface* renderInterface, int numChildren, bool isInGame)
  475. {
  476. // Push the fade value that is used for this element and children
  477. renderGraph->PushAlphaFade(m_fade);
  478. // Render this element and its children
  479. RenderElementAndChildren(renderGraph, elementInterface, renderInterface, numChildren, isInGame);
  480. // Pop off the fade from this fader
  481. renderGraph->PopAlphaFade();
  482. }
  483. ////////////////////////////////////////////////////////////////////////////////////////////////////
  484. void UiFaderComponent::RenderRttFader(LyShine::IRenderGraph* renderGraph, UiElementInterface* elementInterface,
  485. UiRenderInterface* renderInterface, int numChildren, bool isInGame)
  486. {
  487. // Get the render target
  488. AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage;
  489. AZ::EntityId canvasEntityId;
  490. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  491. LyShine::RenderToTextureRequestBus::EventResult(
  492. attachmentImage, canvasEntityId, &LyShine::RenderToTextureRequestBus::Events::GetRenderTarget, m_attachmentImageId);
  493. // Render the element and its children to a render target
  494. {
  495. // we always clear to transparent black - the accumulation of alpha in the render target requires it
  496. AZ::Color clearColor(0.0f, 0.0f, 0.0f, 0.0f);
  497. // Start building the render to texture node in the render graph
  498. renderGraph->BeginRenderToTexture(attachmentImage, m_viewportTopLeft, m_viewportSize, clearColor);
  499. // We don't want this fader or parent faders to affect what is rendered to the render target since we will
  500. // apply those fades when we render from the render target.
  501. renderGraph->PushOverrideAlphaFade(1.0f);
  502. // Render this element and its children
  503. RenderElementAndChildren(renderGraph, elementInterface, renderInterface, numChildren, isInGame);
  504. // finish building the render to texture node in the render graph
  505. renderGraph->EndRenderToTexture();
  506. // pop off the override alpha fade
  507. renderGraph->PopAlphaFade();
  508. }
  509. // render from the render target to the screen (or a parent render target) with the fade value
  510. {
  511. // set the alpha in the verts
  512. {
  513. float desiredAlpha = renderGraph->GetAlphaFade() * m_fade;
  514. uint8 desiredPackedAlpha = static_cast<uint8>(desiredAlpha * 255.0f);
  515. // If the fade value has changed we need to update the alpha values in the vertex colors but we do
  516. // not want to touch or recompute the RGB values
  517. if (m_cachedPrimitive.m_vertices[0].color.a != desiredPackedAlpha)
  518. {
  519. // go through all the cached vertices and update the alpha values
  520. LyShine::UCol desiredPackedColor = m_cachedPrimitive.m_vertices[0].color;
  521. desiredPackedColor.a = desiredPackedAlpha;
  522. for (int i = 0; i < m_cachedPrimitive.m_numVertices; ++i)
  523. {
  524. m_cachedPrimitive.m_vertices[i].color = desiredPackedColor;
  525. }
  526. }
  527. }
  528. // Add a primitive to render a quad using the render target we have created
  529. {
  530. // Set the texture and other render state required
  531. AZ::Data::Instance<AZ::RPI::Image> image = attachmentImage;
  532. bool isClampTextureMode = true;
  533. bool isTextureSRGB = true;
  534. bool isTexturePremultipliedAlpha = true;
  535. LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
  536. renderGraph->AddPrimitive(&m_cachedPrimitive, image, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
  537. }
  538. }
  539. }
  540. ////////////////////////////////////////////////////////////////////////////////////////////////////
  541. void UiFaderComponent::RenderElementAndChildren(LyShine::IRenderGraph* renderGraph, UiElementInterface* elementInterface,
  542. UiRenderInterface* renderInterface, int numChildren, bool isInGame)
  543. {
  544. // Render the visual component for this element (if there is one)
  545. if (renderInterface)
  546. {
  547. renderInterface->Render(renderGraph);
  548. }
  549. // Render the child elements
  550. for (int childIndex = 0; childIndex < numChildren; ++childIndex)
  551. {
  552. UiElementInterface* childElementInterface = elementInterface->GetChildElementInterface(childIndex);
  553. // childElementInterface should never be nullptr but check just to be safe
  554. if (childElementInterface)
  555. {
  556. childElementInterface->RenderElement(renderGraph, isInGame);
  557. }
  558. }
  559. }