UiImageComponent.cpp 115 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685
  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 "UiImageComponent.h"
  9. #include <AzCore/Math/Crc.h>
  10. #include <AzCore/Asset/AssetSerializer.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/Serialization/EditContext.h>
  13. #include <AzCore/RTTI/BehaviorContext.h>
  14. #include <AzCore/Component/ComponentApplicationBus.h>
  15. #include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
  16. #include <LyShine/IDraw2d.h>
  17. #include <LyShine/UiSerializeHelpers.h>
  18. #include <LyShine/Bus/UiElementBus.h>
  19. #include <LyShine/Bus/UiCanvasBus.h>
  20. #include <LyShine/Bus/UiTransformBus.h>
  21. #include <LyShine/Bus/UiTransform2dBus.h>
  22. #include <LyShine/Bus/UiLayoutManagerBus.h>
  23. #include <LyShine/Bus/UiLayoutControllerBus.h>
  24. #include <LyShine/Bus/UiEditorChangeNotificationBus.h>
  25. #include <LyShine/Bus/UiIndexableImageBus.h>
  26. #include <LyShine/ISprite.h>
  27. #include <LyShine/IRenderGraph.h>
  28. #include "UiSerialize.h"
  29. #include "UiLayoutHelpers.h"
  30. #include "Sprite.h"
  31. #include "RenderGraph.h"
  32. namespace
  33. {
  34. //! Given a sprite with a cell index, populates the UV/ST coords array for traditional (stretched) 9-sliced image types.
  35. //!
  36. //! It's assumed that the given left/right/top/bottom border values have
  37. //! already had a "correction scaling" applied. This scaling gets applied
  38. //! when the left/right and/or top/bottom border sizes are greater than
  39. //! the image element's width or height (respectively).
  40. //!
  41. //! \param sValues Output array of U/S texture coords for 9-sliced image.
  42. //!
  43. //! \param tValues Output array of V/T texture coords for 9-sliced image.
  44. //!
  45. //! \param sprite Pointer to sprite object info.
  46. //!
  47. //! \param cellIndex Index of a particular cell in sprite-sheet, if applicable.
  48. //!
  49. //! \param leftBorder The "left" border info for the cell, with "correction scaling" applied.
  50. //! \param rightBorder One minus the "right" border info for the cell, with "correction scaling" applied.
  51. //!
  52. //! \param topBorder Same as leftBorder, but for top cell border value.
  53. //! \param bottomBorder Same as rightBorder, but for bottom cell border value.
  54. void GetSlicedStValuesFromCorrectionalScaleBorders(
  55. float sValues[4],
  56. float tValues[4],
  57. const ISprite* sprite,
  58. const int cellIndex,
  59. const float leftBorder,
  60. const float rightBorder,
  61. const float topBorder,
  62. const float bottomBorder)
  63. {
  64. const float cellMinUCoord = sprite->GetCellUvCoords(cellIndex).TopLeft().GetX();
  65. const float cellMaxUCoord = sprite->GetCellUvCoords(cellIndex).TopRight().GetX();
  66. const float cellMinVCoord = sprite->GetCellUvCoords(cellIndex).TopLeft().GetY();
  67. const float cellMaxVCoord = sprite->GetCellUvCoords(cellIndex).BottomLeft().GetY();
  68. // Transform border values from cell space to texture space
  69. const AZ::Vector2 cellUvSize = sprite->GetCellUvSize(cellIndex);
  70. const float leftBorderTextureSpace = leftBorder * cellUvSize.GetX();
  71. const float rightBorderTextureSpace = (1.0f - rightBorder) * cellUvSize.GetX();
  72. const float topBorderTextureSpace = topBorder * cellUvSize.GetY();
  73. const float bottomBorderTextureSpace = (1.0f - bottomBorder) * cellUvSize.GetY();
  74. // The texture coords are just based on the border values
  75. int uIndex = 0;
  76. sValues[uIndex++] = cellMinUCoord;
  77. sValues[uIndex++] = cellMinUCoord + leftBorderTextureSpace;
  78. sValues[uIndex++] = cellMinUCoord + rightBorderTextureSpace;
  79. sValues[uIndex++] = cellMaxUCoord;
  80. int vIndex = 0;
  81. tValues[vIndex++] = cellMinVCoord;
  82. tValues[vIndex++] = cellMinVCoord + topBorderTextureSpace;
  83. tValues[vIndex++] = cellMinVCoord + bottomBorderTextureSpace;
  84. tValues[vIndex++] = cellMaxVCoord;
  85. }
  86. //! Given a sprite with a cell index, populates the UV/ST coords array for "fixed" 9-sliced image types.
  87. //!
  88. //! It's assumed that the given left/right/top/bottom border values have
  89. //! already had a "correction scaling" applied. This scaling gets applied
  90. //! when the left/right and/or top/bottom border sizes are greater than
  91. //! the image element's width or height (respectively).
  92. //!
  93. //! \param sValues Output array of U/S texture coords for 9-sliced image.
  94. //!
  95. //! \param tValues Output array of V/T texture coords for 9-sliced image.
  96. //!
  97. //! \param sprite Pointer to sprite object info.
  98. //!
  99. //! \param cellIndex Index of a particular cell in sprite-sheet, if applicable.
  100. //!
  101. //! \param leftBorder The "left" border info for the cell, with "correction scaling" applied.
  102. //! \param rightBorder One minus the "right" border info for the cell, with "correction scaling" applied.
  103. //!
  104. //! \param topBorder Same as leftBorder, but for top cell border value.
  105. //! \param bottomBorder Same as rightBorder, but for bottom cell border value.
  106. //!
  107. //! \param pivot The pivot value for the element, this controls how the central part of the texture moves as the element resizes
  108. //!
  109. //! \param centerUvWidth The width of the central quad of the 9-slice in cell UV coords, with "correction scaling" applied.
  110. //! \param centerUvHeight The height of the central quad of the 9-slice in cell UV coords, with "correction scaling" applied.
  111. void GetSlicedFixedStValuesFromCorrectionalScaleBorders(
  112. float sValues[6],
  113. float tValues[6],
  114. const ISprite* sprite,
  115. const int cellIndex,
  116. const float leftBorder,
  117. const float rightBorder,
  118. const float topBorder,
  119. const float bottomBorder,
  120. const AZ::Vector2& pivot,
  121. const float centerUvWidth,
  122. const float centerUvHeight)
  123. {
  124. const float cellMinUCoord = sprite->GetCellUvCoords(cellIndex).TopLeft().GetX();
  125. const float cellMaxUCoord = sprite->GetCellUvCoords(cellIndex).TopRight().GetX();
  126. const float cellMinVCoord = sprite->GetCellUvCoords(cellIndex).TopLeft().GetY();
  127. const float cellMaxVCoord = sprite->GetCellUvCoords(cellIndex).BottomLeft().GetY();
  128. // Transform border values from cell space to texture space
  129. const AZ::Vector2 cellUvSize = sprite->GetCellUvSize(cellIndex);
  130. const float leftBorderTextureSpace = leftBorder * cellUvSize.GetX();
  131. const float rightBorderTextureSpace = (1.0f - rightBorder) * cellUvSize.GetX();
  132. const float topBorderTextureSpace = topBorder * cellUvSize.GetY();
  133. const float bottomBorderTextureSpace = (1.0f - bottomBorder) * cellUvSize.GetY();
  134. // This width and height take into account the size of the rect in pixels
  135. const float centerUvWidthTextureSpace = centerUvWidth * cellUvSize.GetX();
  136. const float centerUvHeightTextureSpace = centerUvHeight * cellUvSize.GetY();
  137. // This width and height is what would happen if we were stretching, if the centerUv... equals the betweenBordersUv...
  138. // the the rect is sized to fit the texture perfectly and stretched and fixed would look the same
  139. const float betweenBordersUvWidthTextureSpace = rightBorderTextureSpace - leftBorderTextureSpace;
  140. const float betweenBordersUvHeightTextureSpace = bottomBorderTextureSpace - topBorderTextureSpace;
  141. // compute the four UV values for the internal verts, the pivot controls where the texture is fixed, i.e. if pivot.x is zero
  142. // the left edge of the center quad is fixed and the right edge reveals more texture as the size of the rect increases.
  143. float centerLeftTextureSpace = leftBorderTextureSpace + (betweenBordersUvWidthTextureSpace - centerUvWidthTextureSpace) * pivot.GetX();
  144. float centerRightTextureSpace = centerLeftTextureSpace + centerUvWidthTextureSpace;
  145. // clamp the values so they never go into the borders, eventually we might want to support tiling in this case
  146. centerLeftTextureSpace = AZ::GetClamp(centerLeftTextureSpace, leftBorderTextureSpace, rightBorderTextureSpace);
  147. centerRightTextureSpace = AZ::GetClamp(centerRightTextureSpace, leftBorderTextureSpace, rightBorderTextureSpace);
  148. float centerTopTextureSpace = topBorderTextureSpace + (betweenBordersUvHeightTextureSpace - centerUvHeightTextureSpace) * pivot.GetY();
  149. float centerBottomTextureSpace = centerTopTextureSpace + centerUvHeightTextureSpace;
  150. // clamp the values so they never go into the borders, eventually we might want to support tiling in this case
  151. centerTopTextureSpace = AZ::GetClamp(centerTopTextureSpace, topBorderTextureSpace, bottomBorderTextureSpace);
  152. centerBottomTextureSpace = AZ::GetClamp(centerBottomTextureSpace, topBorderTextureSpace, bottomBorderTextureSpace);
  153. // The texture coords 0,1,4,5 are just based on the border values while coords 2 & 3 are the internal ones that handle the sliced/fixed behavior
  154. int uIndex = 0;
  155. sValues[uIndex++] = cellMinUCoord;
  156. sValues[uIndex++] = cellMinUCoord + leftBorderTextureSpace;
  157. sValues[uIndex++] = cellMinUCoord + centerLeftTextureSpace;
  158. sValues[uIndex++] = cellMinUCoord + centerRightTextureSpace;
  159. sValues[uIndex++] = cellMinUCoord + rightBorderTextureSpace;
  160. sValues[uIndex++] = cellMaxUCoord;
  161. int vIndex = 0;
  162. tValues[vIndex++] = cellMinVCoord;
  163. tValues[vIndex++] = cellMinVCoord + topBorderTextureSpace;
  164. tValues[vIndex++] = cellMinVCoord + centerTopTextureSpace;
  165. tValues[vIndex++] = cellMinVCoord + centerBottomTextureSpace;
  166. tValues[vIndex++] = cellMinVCoord + bottomBorderTextureSpace;
  167. tValues[vIndex++] = cellMaxVCoord;
  168. }
  169. //! Set the values for an image vertex
  170. //! This helper function is used so that we only have to initialize textIndex and texHasColorChannel in one place
  171. void SetVertex(LyShine::UiPrimitiveVertex& vert, const Vec2& pos, uint32 color, const Vec2& uv)
  172. {
  173. vert.xy = pos;
  174. vert.color.dcolor = color;
  175. vert.st = uv;
  176. vert.texIndex = 0;
  177. vert.texHasColorChannel = 1;
  178. vert.texIndex2 = 0;
  179. vert.pad = 0;
  180. }
  181. //! Set the values for an image vertex
  182. //! This version of the helper function takes AZ vectors
  183. void SetVertex(LyShine::UiPrimitiveVertex& vert, const AZ::Vector2& pos, uint32 color, const AZ::Vector2& uv)
  184. {
  185. SetVertex(vert, Vec2(pos.GetX(), pos.GetY()), color, Vec2(uv.GetX(), uv.GetY()));
  186. }
  187. //! Given the xValues, yValues, sValues and tValues, fill out the verts array with transformed points.
  188. //!
  189. //! \param verts The vertex array to be filled, must be the correct size for "numVerts" verts
  190. //! \param numVerts The number of vertices to be generated
  191. //! \param numX The size of the xValues and sValues input arrays
  192. //! \param numY The size of the yValues and tValues input arrays
  193. //! \param packedColor The color value to be put in every vertex
  194. //! \param transform The transform to be applied to the points
  195. //! \param xValues The x-values for the edges and borders
  196. void FillVerts(LyShine::UiPrimitiveVertex* verts, [[maybe_unused]] uint32 numVerts, uint32 numX, uint32 numY, uint32 packedColor, const AZ::Matrix4x4& transform,
  197. float* xValues, float* yValues, float* sValues, float* tValues,
  198. bool isPixelAligned)
  199. {
  200. AZ_Assert(numVerts == numX * numY, "Error: array size does not match dimensions");
  201. IDraw2d::Rounding pixelRounding = isPixelAligned ? IDraw2d::Rounding::Nearest : IDraw2d::Rounding::None;
  202. float z = 1.0f;
  203. int i = 0;
  204. for (uint32 y = 0; y < numY; ++y)
  205. {
  206. for (uint32 x = 0; x < numX; x += 1)
  207. {
  208. AZ::Vector3 point3(xValues[x], yValues[y], z);
  209. point3 = transform * point3;
  210. point3 = Draw2dHelper::RoundXY(point3, pixelRounding);
  211. AZ::Vector2 point2(point3.GetX(), point3.GetY());
  212. AZ::Vector2 uv(sValues[x], tValues[y]);
  213. SetVertex(verts[i], point2, packedColor, uv);
  214. ++i;
  215. }
  216. }
  217. }
  218. const uint32 numQuadsIn9Slice = 9;
  219. const uint32 numIndicesIn9Slice = numQuadsIn9Slice * 6;
  220. const uint32 numQuadsIn9SliceExcludingCenter = 8;
  221. const uint32 numIndicesIn9SliceExcludingCenter = numQuadsIn9SliceExcludingCenter * 6;
  222. // The vertices are in the order of top row left->right, then next row left->right etc
  223. // 0 1 2 3
  224. // 4 5 6 7
  225. // 8 9 10 11
  226. // 12 13 14 15
  227. const uint16 indicesFor9SliceWhenVertsAre4x4[numIndicesIn9Slice] = {
  228. 0, 1, 4, 1, 5, 4, 1, 2, 5, 2, 6, 5, 2, 3, 6, 3, 7, 6,
  229. 4, 5, 8, 5, 9, 8, 6, 7, 10, 7, 11, 10,
  230. 8, 9, 12, 9, 13, 12, 9, 10, 13, 10, 14, 13, 10, 11, 14, 11, 15, 14,
  231. 5, 6, 9, 6, 10, 9, // center quad
  232. };
  233. // The vertices are in the order of top row left->right, then next row left->right etc
  234. // 0 1 2 3 4 5
  235. // 6 7 8 9 10 11
  236. //
  237. // 12 13 14 15 16 17
  238. // 18 19 20 21 22 23
  239. //
  240. // 24 25 26 27 28 29
  241. // 30 31 32 33 34 35
  242. const uint16 indicesFor9SliceWhenVertsAre6x6[numIndicesIn9Slice] =
  243. {
  244. 0, 1, 7, 7, 6, 0, 2, 3, 9, 9, 8, 2, 4, 5, 11, 11, 10, 4,
  245. 12, 13, 19, 19, 18, 12, 16, 17, 23, 23, 22, 16,
  246. 24, 25, 31, 31, 30, 24, 26, 27, 33, 33, 32, 26, 28, 29, 35, 35, 34, 28,
  247. 14, 15, 21, 21, 20, 14, // center quad
  248. };
  249. AZ::Data::Instance<AZ::RPI::Image> GetSpriteImage(ISprite* sprite)
  250. {
  251. AZ::Data::Instance<AZ::RPI::Image> image;
  252. if (sprite)
  253. {
  254. image = sprite->GetImage();
  255. }
  256. return image;
  257. }
  258. }
  259. ////////////////////////////////////////////////////////////////////////////////////////////////////
  260. // PUBLIC MEMBER FUNCTIONS
  261. ////////////////////////////////////////////////////////////////////////////////////////////////////
  262. ////////////////////////////////////////////////////////////////////////////////////////////////////
  263. UiImageComponent::UiImageComponent()
  264. : m_isColorOverridden(false)
  265. , m_isAlphaOverridden(false)
  266. {
  267. }
  268. ////////////////////////////////////////////////////////////////////////////////////////////////////
  269. UiImageComponent::~UiImageComponent()
  270. {
  271. SAFE_RELEASE(m_sprite);
  272. SAFE_RELEASE(m_overrideSprite);
  273. ClearCachedVertices();
  274. ClearCachedIndices();
  275. }
  276. ////////////////////////////////////////////////////////////////////////////////////////////////////
  277. void UiImageComponent::ResetOverrides()
  278. {
  279. m_overrideColor = m_color;
  280. m_overrideAlpha = m_alpha;
  281. SAFE_RELEASE(m_overrideSprite);
  282. m_isColorOverridden = false;
  283. m_isAlphaOverridden = false;
  284. MarkRenderCacheDirty();
  285. }
  286. ////////////////////////////////////////////////////////////////////////////////////////////////////
  287. void UiImageComponent::SetOverrideColor(const AZ::Color& color)
  288. {
  289. m_overrideColor.Set(color.GetAsVector3());
  290. m_isColorOverridden = true;
  291. MarkRenderCacheDirty();
  292. }
  293. ////////////////////////////////////////////////////////////////////////////////////////////////////
  294. void UiImageComponent::SetOverrideAlpha(float alpha)
  295. {
  296. m_overrideAlpha = alpha;
  297. m_isAlphaOverridden = true;
  298. MarkRenderCacheDirty();
  299. }
  300. ////////////////////////////////////////////////////////////////////////////////////////////////////
  301. void UiImageComponent::SetOverrideSprite(ISprite* sprite, AZ::u32 cellIndex)
  302. {
  303. SAFE_RELEASE(m_overrideSprite);
  304. m_overrideSprite = sprite;
  305. if (m_overrideSprite)
  306. {
  307. m_overrideSprite->AddRef();
  308. m_overrideSpriteCellIndex = cellIndex;
  309. }
  310. MarkRenderCacheDirty();
  311. }
  312. ////////////////////////////////////////////////////////////////////////////////////////////////////
  313. void UiImageComponent::Render(LyShine::IRenderGraph* renderGraph)
  314. {
  315. // get fade value (tracked by UiRenderer) and compute the desired alpha for the image
  316. float fade = renderGraph->GetAlphaFade();
  317. float desiredAlpha = m_overrideAlpha * fade;
  318. uint8 desiredPackedAlpha = static_cast<uint8>(desiredAlpha * 255.0f);
  319. ISprite* sprite = (m_overrideSprite) ? m_overrideSprite : m_sprite;
  320. if (m_isRenderCacheDirty)
  321. {
  322. int cellIndex = (m_overrideSprite) ? m_overrideSpriteCellIndex : m_spriteSheetCellIndex;
  323. bool isTextureSRGB = IsSpriteTypeRenderTarget() && m_isRenderTargetSRGB;
  324. AZ::Color color = AZ::Color::CreateFromVector3AndFloat(m_overrideColor.GetAsVector3(), 1.0f);
  325. if (!isTextureSRGB)
  326. {
  327. color = color.GammaToLinear(); // the colors are specified in sRGB but we want linear colors in the shader
  328. }
  329. uint32 packedColor = (desiredPackedAlpha << 24) | (color.GetR8() << 16) | (color.GetG8() << 8) | color.GetB8();
  330. ImageType imageType = m_imageType;
  331. // if there is no texture we will just use a white texture and want to stretch it
  332. const bool spriteOrTextureIsNull = sprite == nullptr || sprite->GetImage() == nullptr;
  333. // Zero texture size may occur even if the UiImageComponent has a valid non-zero-sized texture,
  334. // because a canvas can be requested to Render() before the texture asset is done loading.
  335. if (!spriteOrTextureIsNull)
  336. {
  337. const AZ::Vector2 textureSize = sprite->GetSize();
  338. if (textureSize.GetX() == 0 || textureSize.GetY() == 0)
  339. {
  340. // don't render to cache and leave m_isRenderCacheDirty set to true
  341. return;
  342. }
  343. }
  344. // if the borders are zero width then sliced is the same as stretched and stretched is simpler to render
  345. const bool spriteIsSlicedAndBordersAreZeroWidth = imageType == ImageType::Sliced && sprite && sprite->AreCellBordersZeroWidth(cellIndex);
  346. if (spriteOrTextureIsNull || spriteIsSlicedAndBordersAreZeroWidth)
  347. {
  348. imageType = ImageType::Stretched;
  349. }
  350. switch (imageType)
  351. {
  352. case ImageType::Stretched:
  353. RenderStretchedSprite(sprite, cellIndex, packedColor);
  354. break;
  355. case ImageType::Sliced:
  356. AZ_Assert(sprite, "Should not get here if no sprite path is specified");
  357. RenderSlicedSprite(sprite, cellIndex, packedColor); // will not get here if sprite is null since we change type in that case above
  358. break;
  359. case ImageType::Fixed:
  360. AZ_Assert(sprite, "Should not get here if no sprite path is specified");
  361. RenderFixedSprite(sprite, cellIndex, packedColor);
  362. break;
  363. case ImageType::Tiled:
  364. AZ_Assert(sprite, "Should not get here if no sprite path is specified");
  365. RenderTiledSprite(sprite, packedColor);
  366. break;
  367. case ImageType::StretchedToFit:
  368. AZ_Assert(sprite, "Should not get here if no sprite path is specified");
  369. RenderStretchedToFitOrFillSprite(sprite, cellIndex, packedColor, true);
  370. break;
  371. case ImageType::StretchedToFill:
  372. AZ_Assert(sprite, "Should not get here if no sprite path is specified");
  373. RenderStretchedToFitOrFillSprite(sprite, cellIndex, packedColor, false);
  374. break;
  375. }
  376. if (!UiCanvasPixelAlignmentNotificationBus::Handler::BusIsConnected())
  377. {
  378. AZ::EntityId canvasEntityId;
  379. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  380. UiCanvasPixelAlignmentNotificationBus::Handler::BusConnect(canvasEntityId);
  381. }
  382. }
  383. // if desired alpha is zero then no need to do any more
  384. if (desiredPackedAlpha == 0)
  385. {
  386. return;
  387. }
  388. // Render cache is now valid - render using the cache
  389. // this should always be true but test to be safe
  390. if (m_cachedPrimitive.m_numVertices > 0)
  391. {
  392. // If the fade value has changed we need to update the alpha values in the vertex colors but we do
  393. // not want to touch or recompute the RGB values
  394. if (m_cachedPrimitive.m_vertices[0].color.a != desiredPackedAlpha)
  395. {
  396. // go through all the cached vertices and update the alpha values
  397. LyShine::UCol desiredPackedColor = m_cachedPrimitive.m_vertices[0].color;
  398. desiredPackedColor.a = desiredPackedAlpha;
  399. for (int i = 0; i < m_cachedPrimitive.m_numVertices; ++i)
  400. {
  401. m_cachedPrimitive.m_vertices[i].color = desiredPackedColor;
  402. }
  403. }
  404. AZ::Data::Instance<AZ::RPI::Image> image = GetSpriteImage(sprite);
  405. bool isClampTextureMode = m_imageType == ImageType::Tiled ? false : true;
  406. bool isTextureSRGB = IsSpriteTypeRenderTarget() && m_isRenderTargetSRGB;
  407. bool isTexturePremultipliedAlpha = false; // we are not rendering from a render target with alpha in it
  408. renderGraph->AddPrimitive(&m_cachedPrimitive, image, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, m_blendMode);
  409. }
  410. }
  411. ////////////////////////////////////////////////////////////////////////////////////////////////////
  412. AZ::Color UiImageComponent::GetColor()
  413. {
  414. return AZ::Color::CreateFromVector3AndFloat(m_color.GetAsVector3(), m_alpha);
  415. }
  416. ////////////////////////////////////////////////////////////////////////////////////////////////////
  417. void UiImageComponent::SetColor(const AZ::Color& color)
  418. {
  419. m_color.Set(color.GetAsVector3());
  420. m_alpha = color.GetA();
  421. AZ::Color oldOverrideColor = m_overrideColor;
  422. float oldOverrideAlpha = m_overrideAlpha;
  423. if (!m_isColorOverridden)
  424. {
  425. m_overrideColor = m_color;
  426. }
  427. if (!m_isAlphaOverridden)
  428. {
  429. m_overrideAlpha = m_alpha;
  430. }
  431. if (oldOverrideColor != m_overrideColor)
  432. {
  433. MarkRenderCacheDirty();
  434. }
  435. else if (oldOverrideAlpha != m_overrideAlpha)
  436. {
  437. // alpha changed so we need RenderGraph to be rebuilt but not render cache
  438. MarkRenderGraphDirty();
  439. }
  440. }
  441. ////////////////////////////////////////////////////////////////////////////////////////////////////
  442. float UiImageComponent::GetAlpha()
  443. {
  444. return m_alpha;
  445. }
  446. ////////////////////////////////////////////////////////////////////////////////////////////////////
  447. void UiImageComponent::SetAlpha(float alpha)
  448. {
  449. float oldOverrideAlpha = m_overrideAlpha;
  450. m_alpha = alpha;
  451. if (!m_isAlphaOverridden)
  452. {
  453. m_overrideAlpha = m_alpha;
  454. }
  455. if (oldOverrideAlpha != m_overrideAlpha)
  456. {
  457. // alpha changed so we need RenderGraph to be rebuilt but not render cache
  458. MarkRenderGraphDirty();
  459. }
  460. }
  461. ////////////////////////////////////////////////////////////////////////////////////////////////////
  462. ISprite* UiImageComponent::GetSprite()
  463. {
  464. return m_sprite;
  465. }
  466. ////////////////////////////////////////////////////////////////////////////////////////////////////
  467. void UiImageComponent::SetSprite(ISprite* sprite)
  468. {
  469. if (m_sprite)
  470. {
  471. if (UiSpriteSettingsChangeNotificationBus::Handler::BusIsConnected())
  472. {
  473. UiSpriteSettingsChangeNotificationBus::Handler::BusDisconnect();
  474. }
  475. m_sprite->Release();
  476. m_spritePathname.SetAssetPath("");
  477. }
  478. m_sprite = sprite;
  479. if (m_sprite)
  480. {
  481. m_sprite->AddRef();
  482. m_spritePathname.SetAssetPath(m_sprite->GetPathname().c_str());
  483. UiSpriteSettingsChangeNotificationBus::Handler::BusConnect(m_sprite);
  484. }
  485. InvalidateLayouts();
  486. UiSpriteSourceNotificationBus::Event(GetEntityId(), &UiSpriteSourceNotificationBus::Events::OnSpriteSourceChanged);
  487. }
  488. ////////////////////////////////////////////////////////////////////////////////////////////////////
  489. AZStd::string UiImageComponent::GetSpritePathname()
  490. {
  491. return m_spritePathname.GetAssetPath();
  492. }
  493. ////////////////////////////////////////////////////////////////////////////////////////////////////
  494. void UiImageComponent::SetSpritePathname(AZStd::string spritePath)
  495. {
  496. m_spritePathname.SetAssetPath(spritePath.c_str());
  497. if (IsSpriteTypeAsset())
  498. {
  499. OnSpritePathnameChange();
  500. }
  501. }
  502. ////////////////////////////////////////////////////////////////////////////////////////////////////
  503. bool UiImageComponent::SetSpritePathnameIfExists(AZStd::string spritePath)
  504. {
  505. if (AZ::Interface<ILyShine>::Get()->DoesSpriteTextureAssetExist(spritePath))
  506. {
  507. SetSpritePathname(spritePath);
  508. return true;
  509. }
  510. return false;
  511. }
  512. ////////////////////////////////////////////////////////////////////////////////////////////////////
  513. AZ::Data::Asset<AZ::RPI::AttachmentImageAsset> UiImageComponent::GetAttachmentImageAsset()
  514. {
  515. return m_attachmentImageAsset;
  516. }
  517. ////////////////////////////////////////////////////////////////////////////////////////////////////
  518. void UiImageComponent::SetAttachmentImageAsset(const AZ::Data::Asset<AZ::RPI::AttachmentImageAsset>& attachmentImageAsset)
  519. {
  520. m_attachmentImageAsset = attachmentImageAsset;
  521. if (m_spriteType == UiImageInterface::SpriteType::RenderTarget)
  522. {
  523. OnSpriteAttachmentImageAssetChange();
  524. }
  525. }
  526. ////////////////////////////////////////////////////////////////////////////////////////////////////
  527. bool UiImageComponent::GetIsRenderTargetSRGB()
  528. {
  529. return m_isRenderTargetSRGB;
  530. }
  531. ////////////////////////////////////////////////////////////////////////////////////////////////////
  532. void UiImageComponent::SetIsRenderTargetSRGB(bool isSRGB)
  533. {
  534. if (m_isRenderTargetSRGB != isSRGB)
  535. {
  536. m_isRenderTargetSRGB = isSRGB;
  537. MarkRenderCacheDirty();
  538. }
  539. }
  540. ////////////////////////////////////////////////////////////////////////////////////////////////////
  541. UiImageInterface::SpriteType UiImageComponent::GetSpriteType()
  542. {
  543. return m_spriteType;
  544. }
  545. ////////////////////////////////////////////////////////////////////////////////////////////////////
  546. void UiImageComponent::SetSpriteType(SpriteType spriteType)
  547. {
  548. m_spriteType = spriteType;
  549. OnSpriteTypeChange();
  550. }
  551. ////////////////////////////////////////////////////////////////////////////////////////////////////
  552. UiImageInterface::ImageType UiImageComponent::GetImageType()
  553. {
  554. return m_imageType;
  555. }
  556. ////////////////////////////////////////////////////////////////////////////////////////////////////
  557. void UiImageComponent::SetImageType(ImageType imageType)
  558. {
  559. if (m_imageType != imageType)
  560. {
  561. m_imageType = imageType;
  562. MarkRenderCacheDirty();
  563. }
  564. }
  565. ////////////////////////////////////////////////////////////////////////////////////////////////////
  566. void UiImageComponent::SetImageIndex(AZ::u32 index)
  567. {
  568. if (m_spriteSheetCellIndex != index)
  569. {
  570. m_spriteSheetCellIndex = index;
  571. MarkRenderCacheDirty();
  572. }
  573. }
  574. ////////////////////////////////////////////////////////////////////////////////////////////////////
  575. const AZ::u32 UiImageComponent::GetImageIndex()
  576. {
  577. return m_spriteSheetCellIndex;
  578. }
  579. ////////////////////////////////////////////////////////////////////////////////////////////////////
  580. const AZ::u32 UiImageComponent::GetImageIndexCount()
  581. {
  582. if (m_sprite)
  583. {
  584. return static_cast<AZ::u32>(m_sprite->GetSpriteSheetCells().size());
  585. }
  586. return 0;
  587. }
  588. ////////////////////////////////////////////////////////////////////////////////////////////////////
  589. AZStd::string UiImageComponent::GetImageIndexAlias(AZ::u32 index)
  590. {
  591. return m_sprite ? m_sprite->GetCellAlias(index) : AZStd::string();
  592. }
  593. ////////////////////////////////////////////////////////////////////////////////////////////////////
  594. void UiImageComponent::SetImageIndexAlias(AZ::u32 index, const AZStd::string& alias)
  595. {
  596. m_sprite ? m_sprite->SetCellAlias(index, alias) : AZ_UNUSED(0);
  597. MarkRenderCacheDirty();
  598. }
  599. ////////////////////////////////////////////////////////////////////////////////////////////////////
  600. AZ::u32 UiImageComponent::GetImageIndexFromAlias(const AZStd::string& alias)
  601. {
  602. return m_sprite ? m_sprite->GetCellIndexFromAlias(alias) : 0;
  603. }
  604. ////////////////////////////////////////////////////////////////////////////////////////////////////
  605. UiImageInterface::FillType UiImageComponent::GetFillType()
  606. {
  607. return m_fillType;
  608. }
  609. ////////////////////////////////////////////////////////////////////////////////////////////////////
  610. void UiImageComponent::SetFillType(UiImageInterface::FillType fillType)
  611. {
  612. if (m_fillType != fillType)
  613. {
  614. m_fillType = fillType;
  615. MarkRenderCacheDirty();
  616. }
  617. }
  618. ////////////////////////////////////////////////////////////////////////////////////////////////////
  619. float UiImageComponent::GetFillAmount()
  620. {
  621. return m_fillAmount;
  622. }
  623. ////////////////////////////////////////////////////////////////////////////////////////////////////
  624. void UiImageComponent::SetFillAmount(float fillAmount)
  625. {
  626. float clampedFillAmount = AZ::GetClamp(fillAmount, 0.0f, 1.0f);
  627. if (m_fillAmount != clampedFillAmount)
  628. {
  629. m_fillAmount = clampedFillAmount;
  630. MarkRenderCacheDirty();
  631. }
  632. }
  633. ////////////////////////////////////////////////////////////////////////////////////////////////////
  634. float UiImageComponent::GetRadialFillStartAngle()
  635. {
  636. return m_fillStartAngle;
  637. }
  638. ////////////////////////////////////////////////////////////////////////////////////////////////////
  639. void UiImageComponent::SetRadialFillStartAngle(float radialFillStartAngle)
  640. {
  641. if (m_fillStartAngle != radialFillStartAngle)
  642. {
  643. m_fillStartAngle = radialFillStartAngle;
  644. MarkRenderCacheDirty();
  645. }
  646. }
  647. ////////////////////////////////////////////////////////////////////////////////////////////////////
  648. UiImageInterface::FillCornerOrigin UiImageComponent::GetCornerFillOrigin()
  649. {
  650. return m_fillCornerOrigin;
  651. }
  652. ////////////////////////////////////////////////////////////////////////////////////////////////////
  653. void UiImageComponent::SetCornerFillOrigin(UiImageInterface::FillCornerOrigin cornerOrigin)
  654. {
  655. if (m_fillCornerOrigin != cornerOrigin)
  656. {
  657. m_fillCornerOrigin = cornerOrigin;
  658. MarkRenderCacheDirty();
  659. }
  660. }
  661. ////////////////////////////////////////////////////////////////////////////////////////////////////
  662. UiImageInterface::FillEdgeOrigin UiImageComponent::GetEdgeFillOrigin()
  663. {
  664. return m_fillEdgeOrigin;
  665. }
  666. ////////////////////////////////////////////////////////////////////////////////////////////////////
  667. void UiImageComponent::SetEdgeFillOrigin(UiImageInterface::FillEdgeOrigin edgeOrigin)
  668. {
  669. if (m_fillEdgeOrigin != edgeOrigin)
  670. {
  671. m_fillEdgeOrigin = edgeOrigin;
  672. MarkRenderCacheDirty();
  673. }
  674. }
  675. ////////////////////////////////////////////////////////////////////////////////////////////////////
  676. bool UiImageComponent::GetFillClockwise()
  677. {
  678. return m_fillClockwise;
  679. }
  680. ////////////////////////////////////////////////////////////////////////////////////////////////////
  681. void UiImageComponent::SetFillClockwise(bool fillClockwise)
  682. {
  683. if (m_fillClockwise != fillClockwise)
  684. {
  685. m_fillClockwise = fillClockwise;
  686. MarkRenderCacheDirty();
  687. }
  688. }
  689. ////////////////////////////////////////////////////////////////////////////////////////////////////
  690. bool UiImageComponent::GetFillCenter()
  691. {
  692. return m_fillCenter;
  693. }
  694. ////////////////////////////////////////////////////////////////////////////////////////////////////
  695. void UiImageComponent::SetFillCenter(bool fillCenter)
  696. {
  697. if (m_fillCenter != fillCenter)
  698. {
  699. m_fillCenter = fillCenter;
  700. MarkRenderCacheDirty();
  701. }
  702. }
  703. ////////////////////////////////////////////////////////////////////////////////////////////////////
  704. void UiImageComponent::PropertyValuesChanged()
  705. {
  706. if (!m_isColorOverridden)
  707. {
  708. m_overrideColor = m_color;
  709. }
  710. if (!m_isAlphaOverridden)
  711. {
  712. m_overrideAlpha = m_alpha;
  713. }
  714. MarkRenderCacheDirty();
  715. }
  716. ////////////////////////////////////////////////////////////////////////////////////////////////////
  717. void UiImageComponent::OnCanvasSpaceRectChanged(AZ::EntityId /*entityId*/, const UiTransformInterface::Rect& /*oldRect*/, const UiTransformInterface::Rect& /*newRect*/)
  718. {
  719. MarkRenderCacheDirty();
  720. }
  721. ////////////////////////////////////////////////////////////////////////////////////////////////////
  722. void UiImageComponent::OnTransformToViewportChanged()
  723. {
  724. MarkRenderCacheDirty();
  725. }
  726. ////////////////////////////////////////////////////////////////////////////////////////////////////
  727. float UiImageComponent::GetMinWidth()
  728. {
  729. return 0.0f;
  730. }
  731. ////////////////////////////////////////////////////////////////////////////////////////////////////
  732. float UiImageComponent::GetMinHeight()
  733. {
  734. return 0.0f;
  735. }
  736. ////////////////////////////////////////////////////////////////////////////////////////////////////
  737. float UiImageComponent::GetTargetWidth(float /*maxWidth*/)
  738. {
  739. float targetWidth = 0.0f;
  740. if (m_sprite)
  741. {
  742. switch (m_imageType)
  743. {
  744. case ImageType::Fixed:
  745. {
  746. AZ::Vector2 textureSize = m_sprite->GetCellSize(m_spriteSheetCellIndex);
  747. targetWidth = textureSize.GetX();
  748. }
  749. break;
  750. case ImageType::Sliced:
  751. {
  752. AZ::Vector2 textureSize = m_sprite->GetCellSize(m_spriteSheetCellIndex);
  753. targetWidth = (m_sprite->GetBorders().m_left * textureSize.GetX()) + ((1.0f - m_sprite->GetBorders().m_right) * textureSize.GetX());
  754. }
  755. break;
  756. default:
  757. {
  758. targetWidth = 0.0f;
  759. }
  760. break;
  761. }
  762. }
  763. return targetWidth;
  764. }
  765. ////////////////////////////////////////////////////////////////////////////////////////////////////
  766. float UiImageComponent::GetTargetHeight(float /*maxHeight*/)
  767. {
  768. float targetHeight = 0.0f;
  769. if (m_sprite)
  770. {
  771. switch (m_imageType)
  772. {
  773. case ImageType::Fixed:
  774. {
  775. AZ::Vector2 textureSize = m_sprite->GetCellSize(m_spriteSheetCellIndex);
  776. targetHeight = textureSize.GetY();
  777. }
  778. break;
  779. case ImageType::Sliced:
  780. {
  781. AZ::Vector2 textureSize = m_sprite->GetCellSize(m_spriteSheetCellIndex);
  782. targetHeight = (m_sprite->GetBorders().m_top * textureSize.GetY()) + ((1.0f - m_sprite->GetBorders().m_bottom) * textureSize.GetY());
  783. }
  784. break;
  785. case ImageType::StretchedToFit:
  786. {
  787. AZ::Vector2 textureSize = m_sprite->GetCellSize(m_spriteSheetCellIndex);
  788. if (textureSize.GetX() > 0.0f)
  789. {
  790. // Get element size
  791. AZ::Vector2 size(0.0f, 0.0f);
  792. UiTransformBus::EventResult(size, GetEntityId(), &UiTransformBus::Events::GetCanvasSpaceSizeNoScaleRotate);
  793. targetHeight = textureSize.GetY() * (size.GetX() / textureSize.GetX());
  794. }
  795. else
  796. {
  797. targetHeight = 0.0f;
  798. }
  799. }
  800. break;
  801. default:
  802. {
  803. targetHeight = 0.0f;
  804. }
  805. break;
  806. }
  807. }
  808. return targetHeight;
  809. }
  810. ////////////////////////////////////////////////////////////////////////////////////////////////////
  811. float UiImageComponent::GetExtraWidthRatio()
  812. {
  813. return 1.0f;
  814. }
  815. ////////////////////////////////////////////////////////////////////////////////////////////////////
  816. float UiImageComponent::GetExtraHeightRatio()
  817. {
  818. return 1.0f;
  819. }
  820. ////////////////////////////////////////////////////////////////////////////////////////////////////
  821. void UiImageComponent::OnCanvasPixelAlignmentChange()
  822. {
  823. MarkRenderCacheDirty();
  824. }
  825. ////////////////////////////////////////////////////////////////////////////////////////////////////
  826. void UiImageComponent::OnSpriteSettingsChanged()
  827. {
  828. MarkRenderCacheDirty();
  829. }
  830. ////////////////////////////////////////////////////////////////////////////////////////////////////
  831. // PUBLIC STATIC MEMBER FUNCTIONS
  832. ////////////////////////////////////////////////////////////////////////////////////////////////////
  833. ////////////////////////////////////////////////////////////////////////////////////////////////////
  834. void UiImageComponent::Reflect(AZ::ReflectContext* context)
  835. {
  836. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  837. if (serializeContext)
  838. {
  839. serializeContext->Class<UiImageComponent, AZ::Component>()
  840. ->Version(8, &VersionConverter)
  841. ->Field("SpriteType", &UiImageComponent::m_spriteType)
  842. ->Field("SpritePath", &UiImageComponent::m_spritePathname)
  843. ->Field("Index", &UiImageComponent::m_spriteSheetCellIndex)
  844. ->Field("AttachmentImageAsset", &UiImageComponent::m_attachmentImageAsset)
  845. ->Field("IsRenderTargetSRGB", &UiImageComponent::m_isRenderTargetSRGB)
  846. ->Field("Color", &UiImageComponent::m_color)
  847. ->Field("Alpha", &UiImageComponent::m_alpha)
  848. ->Field("ImageType", &UiImageComponent::m_imageType)
  849. ->Field("FillCenter", &UiImageComponent::m_fillCenter)
  850. ->Field("StretchSliced", &UiImageComponent::m_isSlicingStretched)
  851. ->Field("BlendMode", &UiImageComponent::m_blendMode)
  852. ->Field("FillType", &UiImageComponent::m_fillType)
  853. ->Field("FillAmount", &UiImageComponent::m_fillAmount)
  854. ->Field("FillStartAngle", &UiImageComponent::m_fillStartAngle)
  855. ->Field("FillCornerOrigin", &UiImageComponent::m_fillCornerOrigin)
  856. ->Field("FillEdgeOrigin", &UiImageComponent::m_fillEdgeOrigin)
  857. ->Field("FillClockwise", &UiImageComponent::m_fillClockwise);
  858. AZ::EditContext* ec = serializeContext->GetEditContext();
  859. if (ec)
  860. {
  861. auto editInfo = ec->Class<UiImageComponent>("Image", "A visual component to draw a rectangle with an optional sprite/texture");
  862. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  863. ->Attribute(AZ::Edit::Attributes::Category, "UI")
  864. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiImage.png")
  865. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiImage.png")
  866. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
  867. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  868. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiImageComponent::m_spriteType, "SpriteType", "The sprite type.")
  869. ->EnumAttribute(UiImageInterface::SpriteType::SpriteAsset, "Sprite/Texture asset")
  870. ->EnumAttribute(UiImageInterface::SpriteType::RenderTarget, "Render target")
  871. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorSpriteTypeChange)
  872. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshEntireTree", 0xefbc823c));
  873. editInfo->DataElement("Sprite", &UiImageComponent::m_spritePathname, "Sprite path", "The sprite path. Can be overridden by another component such as an interactable.")
  874. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsSpriteTypeAsset)
  875. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorSpritePathnameChange);
  876. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiImageComponent::m_spriteSheetCellIndex, "Index", "Sprite-sheet index. Defines which cell in a sprite-sheet is displayed.")
  877. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsSpriteTypeSpriteSheet)
  878. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnIndexChange)
  879. ->Attribute("EnumValues", &UiImageComponent::PopulateIndexStringList);
  880. editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiImageComponent::m_attachmentImageAsset, "Attachment Image Asset", "The render target associated with the sprite.")
  881. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsSpriteTypeRenderTarget)
  882. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnSpriteAttachmentImageAssetChange);
  883. editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiImageComponent::m_isRenderTargetSRGB, "Render Target sRGB", "Check this box if the render target is in sRGB space instead of linear RGB space.")
  884. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsSpriteTypeRenderTarget)
  885. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  886. editInfo->DataElement(AZ::Edit::UIHandlers::Color, &UiImageComponent::m_color, "Color", "The color tint for the image. Can be overridden by another component such as an interactable.")
  887. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnColorChange);
  888. editInfo->DataElement(AZ::Edit::UIHandlers::Slider, &UiImageComponent::m_alpha, "Alpha", "The transparency. Can be overridden by another component such as an interactable.")
  889. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnColorChange)
  890. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  891. ->Attribute(AZ::Edit::Attributes::Max, 1.0f);
  892. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiImageComponent::m_imageType, "ImageType", "The image type. Affects how the texture/sprite is mapped to the image rectangle.")
  893. ->EnumAttribute(UiImageInterface::ImageType::Stretched, "Stretched")
  894. ->EnumAttribute(UiImageInterface::ImageType::Sliced, "Sliced")
  895. ->EnumAttribute(UiImageInterface::ImageType::Fixed, "Fixed")
  896. ->EnumAttribute(UiImageInterface::ImageType::Tiled, "Tiled")
  897. ->EnumAttribute(UiImageInterface::ImageType::StretchedToFit, "Stretched To Fit")
  898. ->EnumAttribute(UiImageInterface::ImageType::StretchedToFill, "Stretched To Fill")
  899. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshEntireTree", 0xefbc823c))
  900. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorImageTypeChange);
  901. editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiImageComponent::m_fillCenter, "Fill Center", "Sliced image center is filled.")
  902. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsSliced)
  903. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  904. editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiImageComponent::m_isSlicingStretched, "Stretch Center/Edges",
  905. "If true, sliced image center and edges are stretched. If false, they act as fixed in the same way as the corners and the pivot controls how they are anchored.")
  906. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsSliced)
  907. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  908. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiImageComponent::m_blendMode, "BlendMode", "The blend mode used to draw the image")
  909. ->EnumAttribute(LyShine::BlendMode::Normal, "Normal")
  910. ->EnumAttribute(LyShine::BlendMode::Add, "Add")
  911. ->EnumAttribute(LyShine::BlendMode::Screen, "Screen")
  912. ->EnumAttribute(LyShine::BlendMode::Darken, "Darken")
  913. ->EnumAttribute(LyShine::BlendMode::Lighten, "Lighten")
  914. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  915. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiImageComponent::m_fillType, "Fill Type", "The fill style used to draw the image.")
  916. ->EnumAttribute(UiImageComponent::FillType::None, "None")
  917. ->EnumAttribute(UiImageComponent::FillType::Linear, "Linear")
  918. ->EnumAttribute(UiImageComponent::FillType::Radial, "Radial")
  919. ->EnumAttribute(UiImageComponent::FillType::RadialCorner, "RadialCorner")
  920. ->EnumAttribute(UiImageComponent::FillType::RadialEdge, "RadialEdge")
  921. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshEntireTree", 0xefbc823c))
  922. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  923. editInfo->DataElement(AZ::Edit::UIHandlers::Slider, &UiImageComponent::m_fillAmount, "Fill Amount", "The amount of the image to be filled.")
  924. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsFilled)
  925. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  926. ->Attribute(AZ::Edit::Attributes::Max, 1.0f)
  927. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  928. editInfo->DataElement(AZ::Edit::UIHandlers::Slider, &UiImageComponent::m_fillStartAngle, "Fill Start Angle", "The start angle for the fill in degrees measured clockwise from straight up.")
  929. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsRadialFilled)
  930. ->Attribute(AZ::Edit::Attributes::Step, 0.1f)
  931. ->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
  932. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  933. ->Attribute(AZ::Edit::Attributes::Max, 360.0f)
  934. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  935. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiImageComponent::m_fillCornerOrigin, "Corner Fill Origin", "The corner from which the image is filled.")
  936. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsCornerFilled)
  937. ->EnumAttribute(UiImageComponent::FillCornerOrigin::TopLeft, "TopLeft")
  938. ->EnumAttribute(UiImageComponent::FillCornerOrigin::TopRight, "TopRight")
  939. ->EnumAttribute(UiImageComponent::FillCornerOrigin::BottomRight, "BottomRight")
  940. ->EnumAttribute(UiImageComponent::FillCornerOrigin::BottomLeft, "BottomLeft")
  941. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  942. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiImageComponent::m_fillEdgeOrigin, "Edge Fill Origin", "The edge from which the image is filled.")
  943. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsEdgeFilled)
  944. ->EnumAttribute(UiImageComponent::FillEdgeOrigin::Left, "Left")
  945. ->EnumAttribute(UiImageComponent::FillEdgeOrigin::Top, "Top")
  946. ->EnumAttribute(UiImageComponent::FillEdgeOrigin::Right, "Right")
  947. ->EnumAttribute(UiImageComponent::FillEdgeOrigin::Bottom, "Bottom")
  948. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  949. editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiImageComponent::m_fillClockwise, "Fill Clockwise", "Image is filled clockwise about the origin.")
  950. ->Attribute(AZ::Edit::Attributes::Visibility, &UiImageComponent::IsRadialAnyFilled)
  951. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiImageComponent::OnEditorRenderSettingChange);
  952. }
  953. }
  954. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  955. if (behaviorContext)
  956. {
  957. behaviorContext->Enum<(int)UiImageInterface::ImageType::Stretched>("eUiImageType_Stretched")
  958. ->Enum<(int)UiImageInterface::ImageType::Sliced>("eUiImageType_Sliced")
  959. ->Enum<(int)UiImageInterface::ImageType::Fixed>("eUiImageType_Fixed")
  960. ->Enum<(int)UiImageInterface::ImageType::Tiled>("eUiImageType_Tiled")
  961. ->Enum<(int)UiImageInterface::ImageType::StretchedToFit>("eUiImageType_StretchedToFit")
  962. ->Enum<(int)UiImageInterface::ImageType::StretchedToFill>("eUiImageType_StretchedToFill")
  963. ->Enum<(int)UiImageInterface::SpriteType::SpriteAsset>("eUiSpriteType_SpriteAsset")
  964. ->Enum<(int)UiImageInterface::SpriteType::RenderTarget>("eUiSpriteType_RenderTarget")
  965. ->Enum<(int)UiImageInterface::FillType::None>("eUiFillType_None")
  966. ->Enum<(int)UiImageInterface::FillType::Linear>("eUiFillType_Linear")
  967. ->Enum<(int)UiImageInterface::FillType::Radial>("eUiFillType_Radial")
  968. ->Enum<(int)UiImageInterface::FillType::RadialCorner>("eUiFillType_RadialCorner")
  969. ->Enum<(int)UiImageInterface::FillType::RadialEdge>("eUiFillType_RadialEdge")
  970. ->Enum<(int)UiImageInterface::FillCornerOrigin::TopLeft>("eUiFillCornerOrigin_TopLeft")
  971. ->Enum<(int)UiImageInterface::FillCornerOrigin::TopRight>("eUiFillCornerOrigin_TopRight")
  972. ->Enum<(int)UiImageInterface::FillCornerOrigin::BottomRight>("eUiFillCornerOrigin_BottomRight")
  973. ->Enum<(int)UiImageInterface::FillCornerOrigin::BottomLeft>("eUiFillCornerOrigin_BottomLeft")
  974. ->Enum<(int)UiImageInterface::FillEdgeOrigin::Left>("eUiFillEdgeOrigin_Left")
  975. ->Enum<(int)UiImageInterface::FillEdgeOrigin::Top>("eUiFillEdgeOrigin_Top")
  976. ->Enum<(int)UiImageInterface::FillEdgeOrigin::Right>("eUiFillEdgeOrigin_Right")
  977. ->Enum<(int)UiImageInterface::FillEdgeOrigin::Bottom>("eUiFillEdgeOrigin_Bottom");
  978. behaviorContext->EBus<UiImageBus>("UiImageBus")
  979. ->Event("GetColor", &UiImageBus::Events::GetColor)
  980. ->Event("SetColor", &UiImageBus::Events::SetColor)
  981. ->Event("GetAlpha", &UiImageBus::Events::GetAlpha)
  982. ->Event("SetAlpha", &UiImageBus::Events::SetAlpha)
  983. ->Event("GetSpritePathname", &UiImageBus::Events::GetSpritePathname)
  984. ->Event("SetSpritePathname", &UiImageBus::Events::SetSpritePathname)
  985. ->Event("SetSpritePathnameIfExists", &UiImageBus::Events::SetSpritePathnameIfExists)
  986. ->Event("GetAttachmentImageAsset", &UiImageBus::Events::GetAttachmentImageAsset)
  987. ->Event("SetAttachmentImageAsset", &UiImageBus::Events::SetAttachmentImageAsset)
  988. ->Event("GetIsRenderTargetSRGB", &UiImageBus::Events::GetIsRenderTargetSRGB)
  989. ->Event("SetIsRenderTargetSRGB", &UiImageBus::Events::SetIsRenderTargetSRGB)
  990. ->Event("GetSpriteType", &UiImageBus::Events::GetSpriteType)
  991. ->Event("SetSpriteType", &UiImageBus::Events::SetSpriteType)
  992. ->Event("GetImageType", &UiImageBus::Events::GetImageType)
  993. ->Event("SetImageType", &UiImageBus::Events::SetImageType)
  994. ->Event("GetFillType", &UiImageBus::Events::GetFillType)
  995. ->Event("SetFillType", &UiImageBus::Events::SetFillType)
  996. ->Event("GetFillAmount", &UiImageBus::Events::GetFillAmount)
  997. ->Event("SetFillAmount", &UiImageBus::Events::SetFillAmount)
  998. ->Event("GetRadialFillStartAngle", &UiImageBus::Events::GetRadialFillStartAngle)
  999. ->Event("SetRadialFillStartAngle", &UiImageBus::Events::SetRadialFillStartAngle)
  1000. ->Event("GetCornerFillOrigin", &UiImageBus::Events::GetCornerFillOrigin)
  1001. ->Event("SetCornerFillOrigin", &UiImageBus::Events::SetCornerFillOrigin)
  1002. ->Event("GetEdgeFillOrigin", &UiImageBus::Events::GetEdgeFillOrigin)
  1003. ->Event("SetEdgeFillOrigin", &UiImageBus::Events::SetEdgeFillOrigin)
  1004. ->Event("GetFillClockwise", &UiImageBus::Events::GetFillClockwise)
  1005. ->Event("SetFillClockwise", &UiImageBus::Events::SetFillClockwise)
  1006. ->Event("GetFillCenter", &UiImageBus::Events::GetFillCenter)
  1007. ->Event("SetFillCenter", &UiImageBus::Events::SetFillCenter)
  1008. ->VirtualProperty("Color", "GetColor", "SetColor")
  1009. ->VirtualProperty("Alpha", "GetAlpha", "SetAlpha")
  1010. ->VirtualProperty("FillAmount", "GetFillAmount", "SetFillAmount")
  1011. ->VirtualProperty("RadialFillStartAngle", "GetRadialFillStartAngle", "SetRadialFillStartAngle")
  1012. ;
  1013. behaviorContext->Class<UiImageComponent>()->RequestBus("UiImageBus");
  1014. behaviorContext->EBus<UiIndexableImageBus>("UiIndexableImageBus")
  1015. ->Event("GetImageIndex", &UiIndexableImageBus::Events::GetImageIndex)
  1016. ->Event("SetImageIndex", &UiIndexableImageBus::Events::SetImageIndex)
  1017. ->Event("GetImageIndexCount", &UiIndexableImageBus::Events::GetImageIndexCount)
  1018. ->Event("GetImageIndexAlias", &UiIndexableImageBus::Events::GetImageIndexAlias)
  1019. ->Event("SetImageIndexAlias", &UiIndexableImageBus::Events::SetImageIndexAlias)
  1020. ->Event("GetImageIndexFromAlias", &UiIndexableImageBus::Events::GetImageIndexFromAlias);
  1021. }
  1022. }
  1023. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1024. // PROTECTED MEMBER FUNCTIONS
  1025. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1026. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1027. void UiImageComponent::Init()
  1028. {
  1029. // If this is called from RC.exe for example these pointers will not be set. In that case
  1030. // we only need to be able to load, init and save the component. It will never be
  1031. // activated.
  1032. if (!AZ::Interface<ILyShine>::Get())
  1033. {
  1034. return;
  1035. }
  1036. // This supports serialization. If we have a sprite pathname but no sprite is loaded
  1037. // then load the sprite
  1038. if (!m_sprite)
  1039. {
  1040. if (IsSpriteTypeAsset())
  1041. {
  1042. if (!m_spritePathname.GetAssetPath().empty())
  1043. {
  1044. m_sprite = AZ::Interface<ILyShine>::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str());
  1045. }
  1046. }
  1047. else if (m_spriteType == UiImageInterface::SpriteType::RenderTarget)
  1048. {
  1049. if (m_attachmentImageAsset)
  1050. {
  1051. m_sprite = AZ::Interface<ILyShine>::Get()->CreateSprite(m_attachmentImageAsset);
  1052. }
  1053. }
  1054. else
  1055. {
  1056. AZ_Assert(false, "unhandled sprite type");
  1057. }
  1058. }
  1059. m_overrideColor = m_color;
  1060. m_overrideAlpha = m_alpha;
  1061. }
  1062. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1063. void UiImageComponent::Activate()
  1064. {
  1065. UiVisualBus::Handler::BusConnect(m_entity->GetId());
  1066. UiRenderBus::Handler::BusConnect(m_entity->GetId());
  1067. UiImageBus::Handler::BusConnect(m_entity->GetId());
  1068. UiIndexableImageBus::Handler::BusConnect(m_entity->GetId());
  1069. UiAnimateEntityBus::Handler::BusConnect(m_entity->GetId());
  1070. UiTransformChangeNotificationBus::Handler::BusConnect(m_entity->GetId());
  1071. UiLayoutCellDefaultBus::Handler::BusConnect(m_entity->GetId());
  1072. if (m_sprite)
  1073. {
  1074. UiSpriteSettingsChangeNotificationBus::Handler::BusConnect(m_sprite);
  1075. }
  1076. // If this is the first time the entity has been activated this is just the same as calling
  1077. // MarkRenderCacheDirty since the canvas is not known. But if an image component has just been
  1078. // added to an existing entity we need to invalidate the layout in case that affects things.
  1079. InvalidateLayouts();
  1080. }
  1081. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1082. void UiImageComponent::Deactivate()
  1083. {
  1084. UiVisualBus::Handler::BusDisconnect();
  1085. UiRenderBus::Handler::BusDisconnect();
  1086. UiImageBus::Handler::BusDisconnect();
  1087. UiIndexableImageBus::Handler::BusDisconnect();
  1088. UiAnimateEntityBus::Handler::BusDisconnect();
  1089. UiTransformChangeNotificationBus::Handler::BusDisconnect();
  1090. UiLayoutCellDefaultBus::Handler::BusDisconnect();
  1091. if (UiCanvasPixelAlignmentNotificationBus::Handler::BusIsConnected())
  1092. {
  1093. UiCanvasPixelAlignmentNotificationBus::Handler::BusDisconnect();
  1094. }
  1095. if (UiSpriteSettingsChangeNotificationBus::Handler::BusIsConnected())
  1096. {
  1097. UiSpriteSettingsChangeNotificationBus::Handler::BusDisconnect();
  1098. }
  1099. // We could be about to remove this component and then reactivate the entity
  1100. // which could affect the layout if there is a parent layout component
  1101. InvalidateLayouts();
  1102. // reduce memory use on deactivate
  1103. ClearCachedVertices();
  1104. ClearCachedIndices();
  1105. }
  1106. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1107. void UiImageComponent::ResetSpriteSheetCellIndex()
  1108. {
  1109. m_spriteSheetCellIndex = 0;
  1110. }
  1111. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1112. // PRIVATE MEMBER FUNCTIONS
  1113. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1114. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1115. void UiImageComponent::RenderStretchedSprite(ISprite* sprite, int cellIndex, uint32 packedColor)
  1116. {
  1117. UiTransformInterface::RectPoints points;
  1118. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetViewportSpacePoints, points);
  1119. if (sprite)
  1120. {
  1121. const UiTransformInterface::RectPoints& uvCoords = sprite->GetCellUvCoords(cellIndex);
  1122. const AZ::Vector2 uvs[4] =
  1123. {
  1124. uvCoords.TopLeft(),
  1125. uvCoords.TopRight(),
  1126. uvCoords.BottomRight(),
  1127. uvCoords.BottomLeft(),
  1128. };
  1129. if (m_fillType != FillType::None)
  1130. {
  1131. RenderFilledQuad(points.pt, uvs, packedColor);
  1132. }
  1133. else
  1134. {
  1135. RenderSingleQuad(points.pt, uvs, packedColor);
  1136. }
  1137. }
  1138. else
  1139. {
  1140. // points are a clockwise quad
  1141. static const AZ::Vector2 uvs[4] = { AZ::Vector2(0, 0), AZ::Vector2(1, 0), AZ::Vector2(1, 1), AZ::Vector2(0, 1) };
  1142. if (m_fillType != FillType::None)
  1143. {
  1144. RenderFilledQuad(points.pt, uvs, packedColor);
  1145. }
  1146. else
  1147. {
  1148. RenderSingleQuad(points.pt, uvs, packedColor);
  1149. }
  1150. }
  1151. }
  1152. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1153. void UiImageComponent::RenderSlicedSprite(ISprite* sprite, int cellIndex, uint32 packedColor)
  1154. {
  1155. // get the details of the texture
  1156. AZ::Vector2 textureSize(sprite->GetCellSize(cellIndex));
  1157. // get the untransformed rect for the element plus it's transform matrix
  1158. UiTransformInterface::RectPoints points;
  1159. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetCanvasSpacePointsNoScaleRotate, points);
  1160. ISprite::Borders cellUvBorders(sprite->GetCellUvBorders(cellIndex));
  1161. float leftBorder = cellUvBorders.m_left;
  1162. float rightBorder = 1.0f - cellUvBorders.m_right;
  1163. float rectWidth = points.TopRight().GetX() - points.TopLeft().GetX();
  1164. float leftPlusRightBorderWidth = (leftBorder + rightBorder) * textureSize.GetX();
  1165. if (leftPlusRightBorderWidth > rectWidth)
  1166. {
  1167. // the width of the element rect is less that the right and left borders combined
  1168. // so we need to adjust so that they don't get drawn overlapping. We adjust them
  1169. // proportionally
  1170. float correctionScale = rectWidth / leftPlusRightBorderWidth;
  1171. leftBorder *= correctionScale;
  1172. rightBorder *= correctionScale;
  1173. }
  1174. float topBorder = cellUvBorders.m_top;
  1175. float bottomBorder = 1.0f - cellUvBorders.m_bottom;
  1176. float rectHeight = points.BottomLeft().GetY() - points.TopLeft().GetY();
  1177. float topPlusBottomBorderHeight = (topBorder + bottomBorder) * textureSize.GetY();
  1178. if (topPlusBottomBorderHeight > rectHeight)
  1179. {
  1180. // the height of the element rect is less that the top and bottom borders combined
  1181. // so we need to adjust so that they don't get drawn overlapping. We adjust them
  1182. // proportionally
  1183. float correctionScale = rectHeight / topPlusBottomBorderHeight;
  1184. topBorder *= correctionScale;
  1185. bottomBorder *= correctionScale;
  1186. }
  1187. AZ::Matrix4x4 transform;
  1188. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetTransformToViewport, transform);
  1189. if (m_isSlicingStretched)
  1190. {
  1191. RenderSlicedStretchedSprite(sprite, cellIndex, packedColor, transform,
  1192. textureSize, points, leftBorder, rightBorder, topBorder, bottomBorder);
  1193. }
  1194. else
  1195. {
  1196. float centerUvWidth = 0.0f;
  1197. if (leftPlusRightBorderWidth < rectWidth)
  1198. {
  1199. // Only used for the SlicedFixed case - compute the width of the unstretched center
  1200. float centerWidthInTexels = rectWidth - leftPlusRightBorderWidth;
  1201. centerUvWidth = centerWidthInTexels / textureSize.GetX();
  1202. }
  1203. float centerUvHeight = 0.0f;
  1204. if (topPlusBottomBorderHeight < rectHeight)
  1205. {
  1206. // Only used for the SlicedFixed case - compute the height of the unstretched center
  1207. float centerHeightInTexels = rectHeight - topPlusBottomBorderHeight;
  1208. centerUvHeight = centerHeightInTexels / textureSize.GetY();
  1209. }
  1210. RenderSlicedFixedSprite(sprite, cellIndex, packedColor, transform,
  1211. textureSize, points, leftBorder, rightBorder, topBorder, bottomBorder,
  1212. rectWidth, rectHeight, centerUvWidth, centerUvHeight);
  1213. }
  1214. }
  1215. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1216. void UiImageComponent::RenderFixedSprite(ISprite* sprite, int cellIndex, uint32 packedColor)
  1217. {
  1218. AZ::Vector2 textureSize(sprite->GetCellSize(cellIndex));
  1219. UiTransformInterface::RectPoints points;
  1220. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetCanvasSpacePointsNoScaleRotate, points);
  1221. AZ::Vector2 pivot;
  1222. UiTransformBus::EventResult(pivot, GetEntityId(), &UiTransformBus::Events::GetPivot);
  1223. // change width and height to match texture
  1224. AZ::Vector2 rectSize = points.GetAxisAlignedSize();
  1225. AZ::Vector2 sizeDiff = textureSize - rectSize;
  1226. AZ::Vector2 topLeftOffset(sizeDiff.GetX() * pivot.GetX(), sizeDiff.GetY() * pivot.GetY());
  1227. AZ::Vector2 bottomRightOffset(sizeDiff.GetX() * (1.0f - pivot.GetX()), sizeDiff.GetY() * (1.0f - pivot.GetY()));
  1228. points.TopLeft() -= topLeftOffset;
  1229. points.BottomRight() += bottomRightOffset;
  1230. points.TopRight() = AZ::Vector2(points.BottomRight().GetX(), points.TopLeft().GetY());
  1231. points.BottomLeft() = AZ::Vector2(points.TopLeft().GetX(), points.BottomRight().GetY());
  1232. // now apply scale and rotation
  1233. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::RotateAndScalePoints, points);
  1234. // now draw the same as Stretched
  1235. const UiTransformInterface::RectPoints& uvCoords = sprite->GetCellUvCoords(cellIndex);
  1236. const AZ::Vector2 uvs[4] =
  1237. {
  1238. uvCoords.TopLeft(),
  1239. uvCoords.TopRight(),
  1240. uvCoords.BottomRight(),
  1241. uvCoords.BottomLeft(),
  1242. };
  1243. if (m_fillType == FillType::None)
  1244. {
  1245. RenderSingleQuad(points.pt, uvs, packedColor);
  1246. }
  1247. else
  1248. {
  1249. RenderFilledQuad(points.pt, uvs, packedColor);
  1250. }
  1251. }
  1252. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1253. void UiImageComponent::RenderTiledSprite(ISprite* sprite, uint32 packedColor)
  1254. {
  1255. AZ::Vector2 textureSize = sprite->GetSize();
  1256. UiTransformInterface::RectPoints points;
  1257. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetCanvasSpacePointsNoScaleRotate, points);
  1258. // scale UV's so that one texel is one pixel on screen
  1259. AZ::Vector2 rectSize = points.GetAxisAlignedSize();
  1260. AZ::Vector2 uvScale(rectSize.GetX() / textureSize.GetX(), rectSize.GetY() / textureSize.GetY());
  1261. // now apply scale and rotation to points
  1262. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::RotateAndScalePoints, points);
  1263. // now draw the same as Stretched but with UV's adjusted
  1264. const AZ::Vector2 uvs[4] = { AZ::Vector2(0, 0), AZ::Vector2(uvScale.GetX(), 0), AZ::Vector2(uvScale.GetX(), uvScale.GetY()), AZ::Vector2(0, uvScale.GetY()) };
  1265. if (m_fillType == FillType::None)
  1266. {
  1267. RenderSingleQuad(points.pt, uvs, packedColor);
  1268. }
  1269. else
  1270. {
  1271. RenderFilledQuad(points.pt, uvs, packedColor);
  1272. }
  1273. }
  1274. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1275. void UiImageComponent::RenderStretchedToFitOrFillSprite(ISprite* sprite, int cellIndex, uint32 packedColor, bool toFit)
  1276. {
  1277. AZ::Vector2 textureSize = sprite->GetCellSize(cellIndex);
  1278. UiTransformInterface::RectPoints points;
  1279. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetCanvasSpacePointsNoScaleRotate, points);
  1280. AZ::Vector2 pivot;
  1281. UiTransformBus::EventResult(pivot, GetEntityId(), &UiTransformBus::Events::GetPivot);
  1282. // scale the texture so it either fits or fills the enclosing rect
  1283. AZ::Vector2 rectSize = points.GetAxisAlignedSize();
  1284. const float scaleFactorX = rectSize.GetX() / textureSize.GetX();
  1285. const float scaleFactorY = rectSize.GetY() / textureSize.GetY();
  1286. const float scaleFactor = toFit ?
  1287. AZ::GetMin(scaleFactorX, scaleFactorY) :
  1288. AZ::GetMax(scaleFactorX, scaleFactorY);
  1289. AZ::Vector2 scaledTextureSize = textureSize * scaleFactor;
  1290. AZ::Vector2 sizeDiff = scaledTextureSize - rectSize;
  1291. AZ::Vector2 topLeftOffset(sizeDiff.GetX() * pivot.GetX(), sizeDiff.GetY() * pivot.GetY());
  1292. AZ::Vector2 bottomRightOffset(sizeDiff.GetX() * (1.0f - pivot.GetX()), sizeDiff.GetY() * (1.0f - pivot.GetY()));
  1293. points.TopLeft() -= topLeftOffset;
  1294. points.BottomRight() += bottomRightOffset;
  1295. points.TopRight() = AZ::Vector2(points.BottomRight().GetX(), points.TopLeft().GetY());
  1296. points.BottomLeft() = AZ::Vector2(points.TopLeft().GetX(), points.BottomRight().GetY());
  1297. // now apply scale and rotation
  1298. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::RotateAndScalePoints, points);
  1299. // now draw the same as Stretched
  1300. const UiTransformInterface::RectPoints& uvCoords = sprite->GetCellUvCoords(cellIndex);
  1301. const AZ::Vector2 uvs[4] =
  1302. {
  1303. uvCoords.TopLeft(),
  1304. uvCoords.TopRight(),
  1305. uvCoords.BottomRight(),
  1306. uvCoords.BottomLeft(),
  1307. };
  1308. if (m_fillType == FillType::None)
  1309. {
  1310. RenderSingleQuad(points.pt, uvs, packedColor);
  1311. }
  1312. else
  1313. {
  1314. RenderFilledQuad(points.pt, uvs, packedColor);
  1315. }
  1316. }
  1317. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1318. void UiImageComponent::RenderSingleQuad(const AZ::Vector2* positions, const AZ::Vector2* uvs, uint32 packedColor)
  1319. {
  1320. // points are a clockwise quad
  1321. IDraw2d::Rounding pixelRounding = IsPixelAligned() ? IDraw2d::Rounding::Nearest : IDraw2d::Rounding::None;
  1322. const uint32 numVertices = 4;
  1323. LyShine::UiPrimitiveVertex vertices[numVertices];
  1324. for (int i = 0; i < numVertices; ++i)
  1325. {
  1326. AZ::Vector2 roundedPoint = Draw2dHelper::RoundXY(positions[i], pixelRounding);
  1327. SetVertex(vertices[i], roundedPoint, packedColor, uvs[i]);
  1328. }
  1329. const uint32 numIndices = 6;
  1330. uint16 indices[numIndices] = { 0, 1, 2, 2, 3, 0 };
  1331. RenderTriangleList(vertices, indices, numVertices, numIndices);
  1332. }
  1333. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1334. void UiImageComponent::RenderFilledQuad(const AZ::Vector2* positions, const AZ::Vector2* uvs, uint32 packedColor)
  1335. {
  1336. if (m_fillType == FillType::Linear)
  1337. {
  1338. RenderLinearFilledQuad(positions, uvs, packedColor);
  1339. }
  1340. else if (m_fillType == FillType::Radial)
  1341. {
  1342. RenderRadialFilledQuad(positions, uvs, packedColor);
  1343. }
  1344. else if (m_fillType == FillType::RadialCorner)
  1345. {
  1346. RenderRadialCornerFilledQuad(positions, uvs, packedColor);
  1347. }
  1348. else if (m_fillType == FillType::RadialEdge)
  1349. {
  1350. RenderRadialEdgeFilledQuad(positions, uvs, packedColor);
  1351. }
  1352. }
  1353. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1354. void UiImageComponent::RenderLinearFilledQuad(const AZ::Vector2* positions, const AZ::Vector2* uvs, uint32 packedColor)
  1355. {
  1356. // This fills the vertices (rotating them based on the origin edge) similar to RenderSingleQuad but then edits 2 vertices based on m_fillAmount.
  1357. int vertexOffset = 0;
  1358. if (m_fillEdgeOrigin == FillEdgeOrigin::Left)
  1359. {
  1360. vertexOffset = 0;
  1361. }
  1362. else if (m_fillEdgeOrigin == FillEdgeOrigin::Top)
  1363. {
  1364. vertexOffset = 1;
  1365. }
  1366. else if (m_fillEdgeOrigin == FillEdgeOrigin::Right)
  1367. {
  1368. vertexOffset = 2;
  1369. }
  1370. else
  1371. {
  1372. vertexOffset = 3;
  1373. }
  1374. // points are a clockwise quad
  1375. IDraw2d::Rounding pixelRounding = IsPixelAligned() ? IDraw2d::Rounding::Nearest : IDraw2d::Rounding::None;
  1376. const uint32 numVertices = 4;
  1377. LyShine::UiPrimitiveVertex vertices[numVertices];
  1378. for (int i = 0; i < numVertices; ++i)
  1379. {
  1380. int index = (i + vertexOffset) % 4;
  1381. AZ::Vector2 roundedPoint = Draw2dHelper::RoundXY(positions[index], pixelRounding);
  1382. SetVertex(vertices[i], roundedPoint, packedColor, uvs[index]);
  1383. }
  1384. vertices[1].xy = vertices[0].xy + m_fillAmount * (vertices[1].xy - vertices[0].xy);
  1385. vertices[2].xy = vertices[3].xy + m_fillAmount * (vertices[2].xy - vertices[3].xy);
  1386. vertices[1].st = vertices[0].st + m_fillAmount * (vertices[1].st - vertices[0].st);
  1387. vertices[2].st = vertices[3].st + m_fillAmount * (vertices[2].st - vertices[3].st);
  1388. const uint32 numIndices = 6;
  1389. uint16 indices[numIndices] = { 0, 1, 2, 2, 3, 0 };
  1390. RenderTriangleList(vertices, indices, numVertices, numIndices);
  1391. }
  1392. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1393. void UiImageComponent::RenderRadialFilledQuad(const AZ::Vector2* positions, const AZ::Vector2* uvs, uint32 packedColor)
  1394. {
  1395. // 1. Fill all vertices/indices as if m_fillAmount is 1.0f
  1396. // 2. Calculate which vertex needs to be moved based on the current value of m_fillAmount and set it's new position/UVs accordingly.
  1397. // 3. Submit only the required amount of vertices/indices
  1398. // The vertices are in the following order. If the calculated fillOffset does not lie on the top edge, the indices are rotated to keep the fill algorithm the same.
  1399. // 5 1/6 2
  1400. // 0
  1401. // 4 3
  1402. int firstIndexOffset = 0;
  1403. int secondIndexOffset = 1;
  1404. int currentVertexToFill = 0;
  1405. int windingDirection = 1;
  1406. float fillOffset = (fmod(m_fillStartAngle, 360.0f) / 360.0f) + 1.125f; // Offsets below are calculated from vertex 5, keep the value is above 0
  1407. // and offset by 1/8 here so it behaves as if calculated from vertex 1.
  1408. if (!m_fillClockwise)
  1409. {
  1410. fillOffset = 1 - (fmod(m_fillStartAngle, 360.0f) / 360.0f) + 1.875f; // Start angle should be clockwise and offsets below are now measured
  1411. // counter-clockwise so offset further here back into vertex 1 position.
  1412. }
  1413. int startingEdge = static_cast<int>((fillOffset) / 0.25f) % 4;
  1414. if (!m_fillClockwise)
  1415. {
  1416. // Flip vertices and direction so that the fill algorithm is unchanged.
  1417. firstIndexOffset = 1;
  1418. secondIndexOffset = 0;
  1419. currentVertexToFill = 6;
  1420. windingDirection = -1;
  1421. startingEdge *= -1;
  1422. }
  1423. // Fill vertices (rotated based on startingEdge).
  1424. const int numVertices = 7; // The maximum amount of vertices that can be used
  1425. LyShine::UiPrimitiveVertex verts[numVertices];
  1426. for (int i = 1; i < 5; ++i)
  1427. {
  1428. int srcIndex = (4 + i + startingEdge) % 4;
  1429. int dstIndex = currentVertexToFill % 4 + 2;
  1430. SetVertex(verts[dstIndex], positions[srcIndex], packedColor, uvs[srcIndex]);
  1431. currentVertexToFill += windingDirection;
  1432. }
  1433. const int numIndices = 15;
  1434. uint16 indices[numIndices];
  1435. for (uint16 ix = 0; ix < 5; ++ix)
  1436. {
  1437. indices[ix * 3 + firstIndexOffset] = ix + 1;
  1438. indices[ix * 3 + secondIndexOffset] = ix + 2;
  1439. indices[ix * 3 + 2] = 0;
  1440. }
  1441. float startingEdgeRemainder = fmod(fillOffset, 0.25f);
  1442. float startingEdgePercentage = startingEdgeRemainder * 4;
  1443. // Set start/end vertices
  1444. SetVertex(verts[1], verts[5].xy + startingEdgePercentage * (verts[2].xy - verts[5].xy),
  1445. packedColor, verts[5].st + startingEdgePercentage * (verts[2].st - verts[5].st));
  1446. verts[6] = verts[1];
  1447. // Set center vertex
  1448. SetVertex(verts[0], (verts[5].xy + verts[3].xy) * 0.5f,
  1449. packedColor, (verts[5].st + verts[3].st) * 0.5f);
  1450. int finalEdge = static_cast<int>((startingEdgeRemainder + m_fillAmount) / 0.25f);
  1451. float finalEdgePercentage = fmod(fillOffset + m_fillAmount, 0.25f) * 4;
  1452. // Calculate which vertex should be moved for the current m_fillAmount value and set it's new position/UV.
  1453. int editedVertexIndex = finalEdge + 2;
  1454. int previousVertexIndex = ((3 + editedVertexIndex - 2) % 4) + 2;
  1455. int nextVertexIndex = ((editedVertexIndex - 2) % 4) + 2;
  1456. verts[editedVertexIndex].xy = verts[previousVertexIndex].xy + finalEdgePercentage * (verts[nextVertexIndex].xy - verts[previousVertexIndex].xy);
  1457. verts[editedVertexIndex].st = verts[previousVertexIndex].st + finalEdgePercentage * (verts[nextVertexIndex].st - verts[previousVertexIndex].st);
  1458. RenderTriangleList(verts, indices, editedVertexIndex + 1, 3 * (editedVertexIndex - 1));
  1459. }
  1460. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1461. void UiImageComponent::RenderRadialCornerFilledQuad(const AZ::Vector2* positions, const AZ::Vector2* uvs, uint32 packedColor)
  1462. {
  1463. // This fills the vertices (rotating them based on the origin edge) similar to RenderSingleQuad, then edits a vertex based on m_fillAmount.
  1464. const uint32 numVerts = 4;
  1465. LyShine::UiPrimitiveVertex verts[numVerts];
  1466. int vertexOffset = 0;
  1467. if (m_fillCornerOrigin == FillCornerOrigin::TopLeft)
  1468. {
  1469. vertexOffset = 0;
  1470. }
  1471. else if (m_fillCornerOrigin == FillCornerOrigin::TopRight)
  1472. {
  1473. vertexOffset = 1;
  1474. }
  1475. else if (m_fillCornerOrigin == FillCornerOrigin::BottomRight)
  1476. {
  1477. vertexOffset = 2;
  1478. }
  1479. else
  1480. {
  1481. vertexOffset = 3;
  1482. }
  1483. for (int i = 0; i < 4; ++i)
  1484. {
  1485. int srcIndex = (i + vertexOffset) % 4;
  1486. SetVertex(verts[i], positions[srcIndex], packedColor, uvs[srcIndex]);
  1487. }
  1488. const uint32 numIndices = 6;
  1489. uint16 indicesCW[numIndices] = { 1, 2, 0, 2, 0, 3 };
  1490. uint16 indicesCCW[numIndices] = { 3, 0, 2, 0, 2, 1 };
  1491. uint16* indices = indicesCW;
  1492. if (!m_fillClockwise)
  1493. {
  1494. // Change index order as we're now filling from the end edge back to the start.
  1495. indices = indicesCCW;
  1496. }
  1497. // Calculate which vertex needs to be moved based on m_fillAmount and set its new position and UV.
  1498. int half = static_cast<int>(floor(m_fillAmount + 0.5f));
  1499. float s = (m_fillAmount - (0.5f * half)) * 2;
  1500. int order = m_fillClockwise ? 1 : -1;
  1501. int vertexToEdit = (half * order) + 2;
  1502. verts[vertexToEdit].xy = verts[vertexToEdit - order].xy + (verts[vertexToEdit].xy - verts[vertexToEdit - order].xy) * s;
  1503. verts[vertexToEdit].st = verts[vertexToEdit - order].st + (verts[vertexToEdit].st - verts[vertexToEdit - order].st) * s;
  1504. int numIndicesToDraw = 3 + half * 3;
  1505. RenderTriangleList(verts, indices, numVerts, numIndicesToDraw);
  1506. }
  1507. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1508. void UiImageComponent::RenderRadialEdgeFilledQuad(const AZ::Vector2* positions, const AZ::Vector2* uvs, uint32 packedColor)
  1509. {
  1510. // This fills the vertices (rotating them based on the origin edge) similar to RenderSingleQuad, then edits a vertex based on m_fillAmount.
  1511. const uint32 numVertices = 5; // Need an extra vertex for the origin.
  1512. LyShine::UiPrimitiveVertex verts[numVertices];
  1513. int vertexOffset = 0;
  1514. if (m_fillEdgeOrigin == FillEdgeOrigin::Left)
  1515. {
  1516. vertexOffset = 0;
  1517. }
  1518. else if (m_fillEdgeOrigin == FillEdgeOrigin::Top)
  1519. {
  1520. vertexOffset = 1;
  1521. }
  1522. else if (m_fillEdgeOrigin == FillEdgeOrigin::Right)
  1523. {
  1524. vertexOffset = 2;
  1525. }
  1526. else
  1527. {
  1528. vertexOffset = 3;
  1529. }
  1530. // Generate the vertex on the edge.
  1531. AZ::Vector2 calculatedPosition = (positions[(0 + vertexOffset) % 4] + positions[(3 + vertexOffset) % 4]) * 0.5f;
  1532. AZ::Vector2 calculatedUV = (uvs[(0 + vertexOffset) % 4] + uvs[(3 + vertexOffset) % 4]) * 0.5f;
  1533. SetVertex(verts[0], calculatedPosition, packedColor, calculatedUV);
  1534. // Fill other vertices
  1535. for (int i = 1; i < 5; ++i)
  1536. {
  1537. calculatedPosition = positions[(i - 1 + vertexOffset) % 4];
  1538. calculatedUV = uvs[(i - 1 + vertexOffset) % 4];
  1539. SetVertex(verts[i], calculatedPosition, packedColor, calculatedUV);
  1540. }
  1541. const uint32 numIndices = 9;
  1542. uint16 indicesCW[numIndices] = { 0, 1, 2, 0, 2, 3, 0, 3, 4 };
  1543. uint16 indicesCCW[numIndices] = { 0, 3, 4, 0, 2, 3, 0, 1, 2 };
  1544. uint16* indices = indicesCW;
  1545. int segment = static_cast<int>(fmin(m_fillAmount * 3, 2.0f));
  1546. float s = (m_fillAmount - (0.3333f * segment)) * 3;
  1547. int order = 1;
  1548. int firstVertex = 2;
  1549. if (!m_fillClockwise)
  1550. {
  1551. // Change order as we're now filling from the end back to the start.
  1552. order = -1;
  1553. firstVertex = 3;
  1554. indices = indicesCCW;
  1555. }
  1556. // Calculate which vertex needs to be moved based on m_fillAmount and set its new position and UV.
  1557. int vertexToEdit = (segment * order) + firstVertex;
  1558. verts[vertexToEdit].xy = verts[vertexToEdit - order].xy + (verts[vertexToEdit].xy - verts[vertexToEdit - order].xy) * s;
  1559. verts[vertexToEdit].st = verts[vertexToEdit - order].st + (verts[vertexToEdit].st - verts[vertexToEdit - order].st) * s;
  1560. int numIndicesToDraw = 3 * (segment + 1);
  1561. RenderTriangleList(verts, indices, numVertices, numIndicesToDraw);
  1562. }
  1563. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1564. void UiImageComponent::RenderSlicedStretchedSprite(ISprite* sprite, int cellIndex, uint32 packedColor, const AZ::Matrix4x4& transform,
  1565. const AZ::Vector2& textureSize, const UiTransformInterface::RectPoints& points,
  1566. float leftBorder, float rightBorder, float topBorder, float bottomBorder)
  1567. {
  1568. const uint32 numValues = 4; // the number of values in the xValues, yValues, sValues and tValues arrays
  1569. // compute the values for the vertex positions, the mid positions are a fixed number of pixels in from the
  1570. // edges. This is based on the border percentage of the texture size
  1571. float xValues[] = {
  1572. points.TopLeft().GetX(),
  1573. points.TopLeft().GetX() + textureSize.GetX() * leftBorder,
  1574. points.BottomRight().GetX() - textureSize.GetX() * rightBorder,
  1575. points.BottomRight().GetX(),
  1576. };
  1577. float yValues[] = {
  1578. points.TopLeft().GetY(),
  1579. points.TopLeft().GetY() + textureSize.GetY() * topBorder,
  1580. points.BottomRight().GetY() - textureSize.GetY() * bottomBorder,
  1581. points.BottomRight().GetY(),
  1582. };
  1583. float sValues[numValues];
  1584. float tValues[numValues];
  1585. GetSlicedStValuesFromCorrectionalScaleBorders(sValues, tValues, sprite, cellIndex, leftBorder, rightBorder, topBorder, bottomBorder);
  1586. if (m_fillType == FillType::None)
  1587. {
  1588. RenderSlicedFillModeNoneSprite<numValues>(packedColor, transform, xValues, yValues, sValues, tValues);
  1589. }
  1590. else if (m_fillType == FillType::Linear)
  1591. {
  1592. RenderSlicedLinearFilledSprite<numValues>(packedColor, transform, xValues, yValues, sValues, tValues);
  1593. }
  1594. else if (m_fillType == FillType::Radial)
  1595. {
  1596. RenderSlicedRadialFilledSprite<numValues>(packedColor, transform, xValues, yValues, sValues, tValues);
  1597. }
  1598. else if (m_fillType == FillType::RadialCorner || m_fillType == FillType::RadialEdge)
  1599. {
  1600. RenderSlicedRadialCornerOrEdgeFilledSprite<numValues>(packedColor, transform, xValues, yValues, sValues, tValues);
  1601. }
  1602. }
  1603. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1604. void UiImageComponent::RenderSlicedFixedSprite(ISprite* sprite, int cellIndex, uint32 packedColor, const AZ::Matrix4x4& transform,
  1605. const AZ::Vector2& textureSize, const UiTransformInterface::RectPoints& points,
  1606. float leftBorder, float rightBorder, float topBorder, float bottomBorder,
  1607. [[maybe_unused]] float rectWidth, [[maybe_unused]] float rectHeight, float centerUvWidth, float centerUvHeight)
  1608. {
  1609. const uint32 numValues = 6; // the number of values in the xValues, yValues, sValues and tValues arrays
  1610. // compute the values for the vertex positions, the mid positions are a fixed number of pixels in from the
  1611. // edges. This is based on the border percentage of the texture size
  1612. float xValues[] = {
  1613. points.TopLeft().GetX(),
  1614. points.TopLeft().GetX() + textureSize.GetX() * leftBorder,
  1615. points.TopLeft().GetX() + textureSize.GetX() * leftBorder,
  1616. points.BottomRight().GetX() - textureSize.GetX() * rightBorder,
  1617. points.BottomRight().GetX() - textureSize.GetX() * rightBorder,
  1618. points.BottomRight().GetX(),
  1619. };
  1620. float yValues[] = {
  1621. points.TopLeft().GetY(),
  1622. points.TopLeft().GetY() + textureSize.GetY() * topBorder,
  1623. points.TopLeft().GetY() + textureSize.GetY() * topBorder,
  1624. points.BottomRight().GetY() - textureSize.GetY() * bottomBorder,
  1625. points.BottomRight().GetY() - textureSize.GetY() * bottomBorder,
  1626. points.BottomRight().GetY(),
  1627. };
  1628. AZ::Vector2 pivot;
  1629. UiTransformBus::EventResult(pivot, GetEntityId(), &UiTransformBus::Events::GetPivot);
  1630. float sValues[numValues];
  1631. float tValues[numValues];
  1632. GetSlicedFixedStValuesFromCorrectionalScaleBorders(sValues, tValues, sprite, cellIndex, leftBorder, rightBorder, topBorder, bottomBorder,
  1633. pivot, centerUvWidth, centerUvHeight);
  1634. if (m_fillType == FillType::None)
  1635. {
  1636. RenderSlicedFillModeNoneSprite<numValues>(packedColor, transform, xValues, yValues, sValues, tValues);
  1637. }
  1638. else if (m_fillType == FillType::Linear)
  1639. {
  1640. RenderSlicedLinearFilledSprite<numValues>(packedColor, transform, xValues, yValues, sValues, tValues);
  1641. }
  1642. else if (m_fillType == FillType::Radial)
  1643. {
  1644. RenderSlicedRadialFilledSprite<numValues>(packedColor, transform, xValues, yValues, sValues, tValues);
  1645. }
  1646. else if (m_fillType == FillType::RadialCorner || m_fillType == FillType::RadialEdge)
  1647. {
  1648. RenderSlicedRadialCornerOrEdgeFilledSprite<numValues>(packedColor, transform, xValues, yValues, sValues, tValues);
  1649. }
  1650. }
  1651. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1652. template<uint32 numValues> void UiImageComponent::RenderSlicedFillModeNoneSprite(uint32 packedColor, const AZ::Matrix4x4& transform,
  1653. float* xValues, float* yValues, float* sValues, float* tValues)
  1654. {
  1655. // fill out the verts
  1656. const uint32 numVertices = numValues * numValues;
  1657. LyShine::UiPrimitiveVertex vertices[numVertices];
  1658. FillVerts(vertices, numVertices, numValues, numValues, packedColor, transform, xValues, yValues, sValues, tValues, IsPixelAligned());
  1659. int totalIndices = m_fillCenter ? numIndicesIn9Slice : numIndicesIn9SliceExcludingCenter;
  1660. const uint16* indices = (numValues == 4) ? indicesFor9SliceWhenVertsAre4x4 : indicesFor9SliceWhenVertsAre6x6;
  1661. RenderTriangleList(vertices, indices, numVertices, totalIndices);
  1662. }
  1663. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1664. template<uint32 numValues> void UiImageComponent::RenderSlicedLinearFilledSprite(uint32 packedColor, const AZ::Matrix4x4& transform,
  1665. float* xValues, float* yValues, float* sValues, float* tValues)
  1666. {
  1667. // 1. Clamp x and y and corresponding s and t values based on m_fillAmount.
  1668. // 2. Fill vertices in the same way as a standard sliced sprite
  1669. const uint32 numVertices = numValues * numValues;
  1670. LyShine::UiPrimitiveVertex vertices[numVertices];
  1671. ClipValuesForSlicedLinearFill(numValues, xValues, yValues, sValues, tValues);
  1672. // Fill the vertices with the generated xy and st values.
  1673. FillVerts(vertices, numVertices, numValues, numValues, packedColor, transform, xValues, yValues, sValues, tValues, IsPixelAligned());
  1674. int totalIndices = m_fillCenter ? numIndicesIn9Slice : numIndicesIn9SliceExcludingCenter;
  1675. const uint16* indices = (numValues == 4) ? indicesFor9SliceWhenVertsAre4x4 : indicesFor9SliceWhenVertsAre6x6;
  1676. RenderTriangleList(vertices, indices, numVertices, totalIndices);
  1677. }
  1678. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1679. template<uint32 numValues> void UiImageComponent::RenderSlicedRadialFilledSprite(uint32 packedColor, const AZ::Matrix4x4& transform,
  1680. float* xValues, float* yValues, float* sValues, float* tValues)
  1681. {
  1682. // build the verts on the stack
  1683. const uint32 numVertices = numValues * numValues;
  1684. LyShine::UiPrimitiveVertex verts[numVertices];
  1685. // Fill the vertices with the generated xy and st values.
  1686. FillVerts(verts, numVertices, numValues, numValues, packedColor, transform, xValues, yValues, sValues, tValues, IsPixelAligned());
  1687. int totalIndices = (m_fillCenter ? numIndicesIn9Slice : numIndicesIn9SliceExcludingCenter);
  1688. const uint16* indices = (numValues == 4) ? indicesFor9SliceWhenVertsAre4x4 : indicesFor9SliceWhenVertsAre6x6;
  1689. // clip the quads generating new verts and indices and render them to the cache
  1690. ClipAndRenderForSlicedRadialFill(numValues, numVertices, verts, totalIndices, indices);
  1691. }
  1692. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1693. template<uint32 numValues> void UiImageComponent::RenderSlicedRadialCornerOrEdgeFilledSprite(uint32 packedColor, const AZ::Matrix4x4& transform,
  1694. float* xValues, float* yValues, float* sValues, float* tValues)
  1695. {
  1696. // build the verts on the stack
  1697. const uint32 numVertices = numValues * numValues;
  1698. LyShine::UiPrimitiveVertex verts[numVertices];
  1699. // Fill the vertices with the generated xy and st values.
  1700. FillVerts(verts, numVertices, numValues, numValues, packedColor, transform, xValues, yValues, sValues, tValues, IsPixelAligned());
  1701. int totalIndices = (m_fillCenter ? numIndicesIn9Slice : numIndicesIn9SliceExcludingCenter);
  1702. const uint16* indices = (numValues == 4) ? indicesFor9SliceWhenVertsAre4x4 : indicesFor9SliceWhenVertsAre6x6;
  1703. // clip the quads generating new verts and indices and render them to the cache
  1704. ClipAndRenderForSlicedRadialCornerOrEdgeFill(numValues, numVertices, verts, totalIndices, indices);
  1705. }
  1706. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1707. void UiImageComponent::ClipValuesForSlicedLinearFill(uint32 numValues, float* xValues, float* yValues, float* sValues, float* tValues)
  1708. {
  1709. // 1. Clamp x and y and corresponding s and t values based on m_fillAmount.
  1710. // 2. Fill vertices in the same way as a standard sliced sprite
  1711. float* clipPosition = xValues;
  1712. float* clipSTs = sValues;
  1713. int startClip = 0;
  1714. int clipInc = 1;
  1715. if (m_fillEdgeOrigin == FillEdgeOrigin::Left)
  1716. {
  1717. clipPosition = xValues;
  1718. clipSTs = sValues;
  1719. startClip = 0;
  1720. clipInc = 1;
  1721. }
  1722. else if (m_fillEdgeOrigin == FillEdgeOrigin::Top)
  1723. {
  1724. clipPosition = yValues;
  1725. clipSTs = tValues;
  1726. startClip = 0;
  1727. clipInc = 1;
  1728. }
  1729. else if (m_fillEdgeOrigin == FillEdgeOrigin::Right)
  1730. {
  1731. clipPosition = xValues;
  1732. clipSTs = sValues;
  1733. // Start from the end of the array and work back.
  1734. startClip = numValues-1;
  1735. clipInc = -1;
  1736. }
  1737. else
  1738. {
  1739. clipPosition = yValues;
  1740. clipSTs = tValues;
  1741. // Start from the end of the array and work back.
  1742. startClip = numValues-1;
  1743. clipInc = -1;
  1744. }
  1745. // If a segment ends before m_fillAmount then it is fully displayed.
  1746. // If m_fillAmount lies in this segment, change the x/y position and clamp all remaining values to this.
  1747. float totalLength = (clipPosition[startClip + ((numValues-1) * clipInc)] - clipPosition[startClip]);
  1748. float previousPercentage = 0;
  1749. int previousIndex = startClip;
  1750. int clampIndex = -1; // to clamp all values greater than m_fillAmount in specified direction.
  1751. for (uint32 arrayPos = 1; arrayPos < numValues; ++arrayPos)
  1752. {
  1753. int currentIndex = startClip + arrayPos * clipInc;
  1754. float thisPercentage = (clipPosition[currentIndex] - clipPosition[startClip]) / totalLength;
  1755. if (clampIndex != -1) // we've already passed m_fillAmount.
  1756. {
  1757. clipPosition[currentIndex] = clipPosition[clampIndex];
  1758. clipSTs[currentIndex] = clipSTs[clampIndex];
  1759. }
  1760. else if (thisPercentage > m_fillAmount)
  1761. {
  1762. // this index is greater than m_fillAmount but the previous one was not, so calculate our new position and UV.
  1763. float segmentPercentFilled = (m_fillAmount - previousPercentage) / (thisPercentage - previousPercentage);
  1764. clipPosition[currentIndex] = clipPosition[previousIndex] + segmentPercentFilled * (clipPosition[currentIndex] - clipPosition[previousIndex]);
  1765. clipSTs[currentIndex] = clipSTs[previousIndex] + segmentPercentFilled * (clipSTs[currentIndex] - clipSTs[previousIndex]);
  1766. clampIndex = currentIndex; // clamp remaining values to this one to generate degenerate triangles.
  1767. }
  1768. previousPercentage = thisPercentage;
  1769. previousIndex = currentIndex;
  1770. }
  1771. }
  1772. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1773. void UiImageComponent::ClipAndRenderForSlicedRadialFill(uint32 numVertsPerSide, uint32 numVerts, const LyShine::UiPrimitiveVertex* verts, uint32 totalIndices, const uint16* indices)
  1774. {
  1775. // 1. Calculate two points of lines from the center to a point based on m_fillAmount and m_fillOrigin.
  1776. // 2. Clip the triangles of the sprite against those lines based on the fill amount.
  1777. LyShine::UiPrimitiveVertex renderVerts[numIndicesIn9Slice * 4]; // ClipToLine doesn't check for duplicate vertices for speed, so this is the maximum we'll need.
  1778. uint16 renderIndices[numIndicesIn9Slice * 4] = { 0 };
  1779. float fillOffset = AZ::DegToRad(m_fillStartAngle);
  1780. Vec2 lineOrigin = (verts[0].xy + verts[numVerts-1].xy) * 0.5f;
  1781. Vec2 rotatingLineEnd = ((verts[0].xy + verts[numVertsPerSide-1].xy) * 0.5f) - lineOrigin;
  1782. Vec2 firstHalfFixedLineEnd = (rotatingLineEnd * -1.0f);
  1783. Vec2 secondHalfFixedLineEnd = rotatingLineEnd;
  1784. float startAngle = 0;
  1785. float endAngle = -AZ::Constants::TwoPi;
  1786. if (!m_fillClockwise)
  1787. {
  1788. // Clip from the opposite side of the line and rotate the line in the opposite direction.
  1789. float temp = startAngle;
  1790. startAngle = endAngle;
  1791. endAngle = temp;
  1792. rotatingLineEnd = rotatingLineEnd * -1.0f;
  1793. firstHalfFixedLineEnd = firstHalfFixedLineEnd * -1.0f;
  1794. secondHalfFixedLineEnd = secondHalfFixedLineEnd * -1.0f;
  1795. }
  1796. Matrix33 lineRotationMatrix;
  1797. lineRotationMatrix.SetRotationZ(startAngle - fillOffset);
  1798. firstHalfFixedLineEnd = firstHalfFixedLineEnd * lineRotationMatrix;
  1799. firstHalfFixedLineEnd = lineOrigin + firstHalfFixedLineEnd;
  1800. secondHalfFixedLineEnd = secondHalfFixedLineEnd * lineRotationMatrix;
  1801. secondHalfFixedLineEnd = lineOrigin + secondHalfFixedLineEnd;
  1802. lineRotationMatrix.SetRotationZ(startAngle - fillOffset + (endAngle - startAngle) * m_fillAmount);
  1803. rotatingLineEnd = rotatingLineEnd * lineRotationMatrix;
  1804. rotatingLineEnd = lineOrigin + rotatingLineEnd;
  1805. int numIndicesToRender = 0;
  1806. int vertexOffset = 0;
  1807. int indicesUsed = 0;
  1808. const int maxTemporaryVerts = 4;
  1809. const int maxTemporaryIndices = 6;
  1810. if (m_fillAmount < 0.5f)
  1811. {
  1812. // Clips against first half line and then rotating line and adds results to render list.
  1813. for (uint32 currentIndex = 0; currentIndex < totalIndices; currentIndex += 3)
  1814. {
  1815. LyShine::UiPrimitiveVertex intermediateVerts[maxTemporaryVerts];
  1816. uint16 intermediateIndices[maxTemporaryIndices];
  1817. int intermedateVertexOffset = 0;
  1818. int intermediateIndicesUsed = ClipToLine(verts, &indices[currentIndex], intermediateVerts, intermediateIndices, intermedateVertexOffset, 0, lineOrigin, firstHalfFixedLineEnd);
  1819. for (int currentIntermediateIndex = 0; currentIntermediateIndex < intermediateIndicesUsed; currentIntermediateIndex += 3)
  1820. {
  1821. indicesUsed = ClipToLine(intermediateVerts, &intermediateIndices[currentIntermediateIndex], renderVerts, renderIndices, vertexOffset, numIndicesToRender, lineOrigin, rotatingLineEnd);
  1822. numIndicesToRender += indicesUsed;
  1823. }
  1824. }
  1825. }
  1826. else
  1827. {
  1828. // Clips against first half line and adds results to render list then clips against the second half line and rotating line and also adds those results to render list.
  1829. for (uint32 currentIndex = 0; currentIndex < totalIndices; currentIndex += 3)
  1830. {
  1831. LyShine::UiPrimitiveVertex intermediateVerts[maxTemporaryVerts];
  1832. uint16 intermediateIndices[maxTemporaryIndices];
  1833. indicesUsed = ClipToLine(verts, &indices[currentIndex], renderVerts, renderIndices, vertexOffset, numIndicesToRender, lineOrigin, firstHalfFixedLineEnd);
  1834. numIndicesToRender += indicesUsed;
  1835. int intermedateVertexOffset = 0;
  1836. int intermediateIndicesUsed = ClipToLine(verts, &indices[currentIndex], intermediateVerts, intermediateIndices, intermedateVertexOffset, 0, lineOrigin, secondHalfFixedLineEnd);
  1837. for (int currentIntermediateIndex = 0; currentIntermediateIndex < intermediateIndicesUsed; currentIntermediateIndex += 3)
  1838. {
  1839. indicesUsed = ClipToLine(intermediateVerts, &intermediateIndices[currentIntermediateIndex], renderVerts, renderIndices, vertexOffset, numIndicesToRender, lineOrigin, rotatingLineEnd);
  1840. numIndicesToRender += indicesUsed;
  1841. }
  1842. }
  1843. }
  1844. RenderTriangleList(renderVerts, renderIndices, vertexOffset, numIndicesToRender);
  1845. }
  1846. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1847. void UiImageComponent::ClipAndRenderForSlicedRadialCornerOrEdgeFill(uint32 numVertsPerSide, uint32 numVerts, const LyShine::UiPrimitiveVertex* verts, uint32 totalIndices, const uint16* indices)
  1848. {
  1849. // 1. Calculate two points of a line from either the corner or center of an edge to a point based on m_fillAmount.
  1850. // 2. Clip the triangles of the sprite against that line.
  1851. LyShine::UiPrimitiveVertex renderVerts[numIndicesIn9Slice * 2]; // ClipToLine doesn't check for duplicate vertices for speed, so this is the maximum we'll need.
  1852. uint16 renderIndices[numIndicesIn9Slice * 2] = { 0 };
  1853. // Generate the start and direction of the line to clip against based on the fill origin and fill amount.
  1854. int originVertex = 0;
  1855. int targetVertex = 0;
  1856. if ((m_fillType == FillType::RadialCorner && m_fillCornerOrigin == FillCornerOrigin::TopLeft) ||
  1857. (m_fillType == FillType::RadialEdge && m_fillEdgeOrigin == FillEdgeOrigin::Top))
  1858. {
  1859. originVertex = 0;
  1860. targetVertex = numVertsPerSide-1;
  1861. }
  1862. else if ((m_fillType == FillType::RadialCorner && m_fillCornerOrigin == FillCornerOrigin::TopRight) ||
  1863. (m_fillType == FillType::RadialEdge && m_fillEdgeOrigin == FillEdgeOrigin::Right))
  1864. {
  1865. originVertex = numVertsPerSide-1;
  1866. targetVertex = numVerts-1;
  1867. }
  1868. else if ((m_fillType == FillType::RadialCorner && m_fillCornerOrigin == FillCornerOrigin::BottomRight) ||
  1869. (m_fillType == FillType::RadialEdge && m_fillEdgeOrigin == FillEdgeOrigin::Bottom))
  1870. {
  1871. originVertex = numVerts-1;
  1872. targetVertex = numVertsPerSide * (numVertsPerSide-1);
  1873. }
  1874. else if ((m_fillType == FillType::RadialCorner && m_fillCornerOrigin == FillCornerOrigin::BottomLeft) ||
  1875. (m_fillType == FillType::RadialEdge && m_fillEdgeOrigin == FillEdgeOrigin::Left))
  1876. {
  1877. originVertex = numVertsPerSide * (numVertsPerSide-1);
  1878. targetVertex = 0;
  1879. }
  1880. Vec2 lineOrigin(verts[originVertex].xy);
  1881. if (m_fillType == FillType::RadialEdge)
  1882. {
  1883. lineOrigin = (verts[originVertex].xy + verts[targetVertex].xy) * 0.5f;
  1884. }
  1885. Vec2 lineEnd = verts[targetVertex].xy - verts[originVertex].xy;
  1886. float startAngle = 0;
  1887. float endAngle = m_fillType == FillType::RadialCorner ? -AZ::Constants::HalfPi : -AZ::Constants::Pi;
  1888. if (!m_fillClockwise)
  1889. {
  1890. // Clip from the opposite side of the line and rotate the line in the opposite direction.
  1891. float temp = startAngle;
  1892. startAngle = endAngle;
  1893. endAngle = temp;
  1894. lineEnd = lineEnd * -1.0f;
  1895. }
  1896. Matrix33 lineRotationMatrix;
  1897. lineRotationMatrix.SetRotationZ(startAngle + (endAngle - startAngle) * m_fillAmount);
  1898. lineEnd = lineEnd * lineRotationMatrix;
  1899. lineEnd = lineOrigin + lineEnd;
  1900. int numIndicesToRender = 0;
  1901. int vertexOffset = 0;
  1902. for (uint32 ix = 0; ix < totalIndices; ix += 3)
  1903. {
  1904. int indicesUsed = ClipToLine(verts, &indices[ix], renderVerts, renderIndices, vertexOffset, numIndicesToRender, lineOrigin, lineEnd);
  1905. numIndicesToRender += indicesUsed;
  1906. }
  1907. RenderTriangleList(renderVerts, renderIndices, vertexOffset, numIndicesToRender);
  1908. }
  1909. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1910. int UiImageComponent::ClipToLine(const LyShine::UiPrimitiveVertex* vertices, const uint16* indices, LyShine::UiPrimitiveVertex* renderVertices, uint16* renderIndices, int& vertexOffset, int renderIndexOffset, const Vec2& lineOrigin, const Vec2& lineEnd)
  1911. {
  1912. Vec2 lineVector = lineEnd - lineOrigin;
  1913. LyShine::UiPrimitiveVertex lastVertex = vertices[indices[2]];
  1914. LyShine::UiPrimitiveVertex currentVertex;
  1915. int verticesAdded = 0;
  1916. for (int i = 0; i < 3; ++i)
  1917. {
  1918. currentVertex = vertices[indices[i]];
  1919. Vec2 triangleEdgeDirection = currentVertex.xy - lastVertex.xy;
  1920. Vec2 currentPointVector = (currentVertex.xy - lineOrigin);
  1921. Vec2 lastPointVector = (lastVertex.xy - lineOrigin);
  1922. float currentPointDeterminant = (lineVector.x * currentPointVector.y) - (lineVector.y * currentPointVector.x);
  1923. float lastPointDeterminant = (lineVector.x * lastPointVector.y) - (lineVector.y * lastPointVector.x);
  1924. const float epsilon = 0.001f;
  1925. Vec2 perpendicularLineVector(-lineVector.y, lineVector.x);
  1926. Vec2 vertexToLine = lineOrigin - lastVertex.xy;
  1927. if (currentPointDeterminant < epsilon)
  1928. {
  1929. if (lastPointDeterminant > -epsilon && fabs(currentPointDeterminant) > epsilon && fabs(lastPointDeterminant) > epsilon)
  1930. {
  1931. //add calculated intersection
  1932. float intersectionDistance = (vertexToLine.x * perpendicularLineVector.x + vertexToLine.y * perpendicularLineVector.y) / (triangleEdgeDirection.x * perpendicularLineVector.x + triangleEdgeDirection.y * perpendicularLineVector.y);
  1933. LyShine::UiPrimitiveVertex intersectPoint;
  1934. SetVertex(intersectPoint, lastVertex.xy + triangleEdgeDirection * intersectionDistance,
  1935. lastVertex.color.dcolor, lastVertex.st + (currentVertex.st - lastVertex.st) * intersectionDistance);
  1936. renderVertices[vertexOffset] = intersectPoint;
  1937. vertexOffset++;
  1938. verticesAdded++;
  1939. }
  1940. //add currentvertex
  1941. renderVertices[vertexOffset] = currentVertex;
  1942. vertexOffset++;
  1943. verticesAdded++;
  1944. }
  1945. else if (lastPointDeterminant < epsilon)
  1946. {
  1947. //add calculated intersection
  1948. float intersectionDistance = (vertexToLine.x * perpendicularLineVector.x + vertexToLine.y * perpendicularLineVector.y) / (triangleEdgeDirection.x * perpendicularLineVector.x + triangleEdgeDirection.y * perpendicularLineVector.y);
  1949. LyShine::UiPrimitiveVertex intersectPoint;
  1950. SetVertex(intersectPoint, lastVertex.xy + triangleEdgeDirection * intersectionDistance,
  1951. lastVertex.color.dcolor, lastVertex.st + (currentVertex.st - lastVertex.st) * intersectionDistance);
  1952. renderVertices[vertexOffset] = intersectPoint;
  1953. vertexOffset++;
  1954. verticesAdded++;
  1955. }
  1956. lastVertex = currentVertex;
  1957. }
  1958. int indicesAdded = 0;
  1959. if (verticesAdded == 3)
  1960. {
  1961. renderIndices[renderIndexOffset] = static_cast<uint16>(vertexOffset - 3);
  1962. renderIndices[renderIndexOffset + 1] = static_cast<uint16>(vertexOffset - 2);
  1963. renderIndices[renderIndexOffset + 2] = static_cast<uint16>(vertexOffset - 1);
  1964. indicesAdded = 3;
  1965. }
  1966. else if (verticesAdded == 4)
  1967. {
  1968. renderIndices[renderIndexOffset] = static_cast<uint16>(vertexOffset - 4);
  1969. renderIndices[renderIndexOffset + 1] = static_cast<uint16>(vertexOffset - 3);
  1970. renderIndices[renderIndexOffset + 2] = static_cast<uint16>(vertexOffset - 2);
  1971. renderIndices[renderIndexOffset + 3] = static_cast<uint16>(vertexOffset - 4);
  1972. renderIndices[renderIndexOffset + 4] = static_cast<uint16>(vertexOffset - 2);
  1973. renderIndices[renderIndexOffset + 5] = static_cast<uint16>(vertexOffset - 1);
  1974. indicesAdded = 6;
  1975. }
  1976. return indicesAdded;
  1977. }
  1978. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1979. void UiImageComponent::RenderTriangleList(const LyShine::UiPrimitiveVertex* vertices, const uint16* indices, int numVertices, int numIndices)
  1980. {
  1981. if (numVertices != m_cachedPrimitive.m_numVertices)
  1982. {
  1983. ClearCachedVertices();
  1984. m_cachedPrimitive.m_vertices = new LyShine::UiPrimitiveVertex[numVertices];
  1985. m_cachedPrimitive.m_numVertices = numVertices;
  1986. }
  1987. if (numIndices != m_cachedPrimitive.m_numIndices)
  1988. {
  1989. ClearCachedIndices();
  1990. m_cachedPrimitive.m_indices = new uint16[numIndices];
  1991. m_cachedPrimitive.m_numIndices = numIndices;
  1992. }
  1993. memcpy(m_cachedPrimitive.m_vertices, vertices, sizeof(LyShine::UiPrimitiveVertex) * numVertices);
  1994. memcpy(m_cachedPrimitive.m_indices, indices, sizeof(uint16) * numIndices);
  1995. m_isRenderCacheDirty = false;
  1996. }
  1997. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1998. void UiImageComponent::ClearCachedVertices()
  1999. {
  2000. if (m_cachedPrimitive.m_vertices)
  2001. {
  2002. delete [] m_cachedPrimitive.m_vertices;
  2003. }
  2004. m_cachedPrimitive.m_vertices = nullptr;
  2005. m_cachedPrimitive.m_numVertices = 0;
  2006. }
  2007. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2008. void UiImageComponent::ClearCachedIndices()
  2009. {
  2010. if (m_cachedPrimitive.m_indices)
  2011. {
  2012. delete [] m_cachedPrimitive.m_indices;
  2013. }
  2014. m_cachedPrimitive.m_indices = nullptr;
  2015. m_cachedPrimitive.m_numIndices = 0;
  2016. }
  2017. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2018. void UiImageComponent::MarkRenderCacheDirty()
  2019. {
  2020. m_isRenderCacheDirty = true;
  2021. MarkRenderGraphDirty();
  2022. }
  2023. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2024. void UiImageComponent::MarkRenderGraphDirty()
  2025. {
  2026. // tell the canvas to invalidate the render graph (never want to do this while rendering)
  2027. AZ::EntityId canvasEntityId;
  2028. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  2029. UiCanvasComponentImplementationBus::Event(canvasEntityId, &UiCanvasComponentImplementationBus::Events::MarkRenderGraphDirty);
  2030. }
  2031. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2032. void UiImageComponent::SnapOffsetsToFixedImage()
  2033. {
  2034. // check that the element is using transform2d - if not then can't adjust the offsets
  2035. if (!UiTransform2dBus::FindFirstHandler(GetEntityId()))
  2036. {
  2037. return;
  2038. }
  2039. // if the image has no texture it will not use Fixed rendering so do nothing
  2040. AZ::Data::Instance<AZ::RPI::Image> image = GetSpriteImage(m_sprite);
  2041. if (!image)
  2042. {
  2043. return;
  2044. }
  2045. // Check that this element is not controlled by a parent layout component
  2046. AZ::EntityId parentElementId;
  2047. UiElementBus::EventResult(parentElementId, GetEntityId(), &UiElementBus::Events::GetParentEntityId);
  2048. bool isControlledByParent = false;
  2049. UiLayoutBus::EventResult(isControlledByParent, parentElementId, &UiLayoutBus::Events::IsControllingChild, GetEntityId());
  2050. if (isControlledByParent)
  2051. {
  2052. return;
  2053. }
  2054. // get the anchors and offsets from the element's transform component
  2055. UiTransform2dInterface::Anchors anchors;
  2056. UiTransform2dInterface::Offsets offsets;
  2057. UiTransform2dBus::EventResult(anchors, GetEntityId(), &UiTransform2dBus::Events::GetAnchors);
  2058. UiTransform2dBus::EventResult(offsets, GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  2059. // Get the size of the element rect before scale/rotate
  2060. UiTransformInterface::RectPoints points;
  2061. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetCanvasSpacePointsNoScaleRotate, points);
  2062. AZ::Vector2 rectSize = points.GetAxisAlignedSize();
  2063. // get the texture size
  2064. AZ::Vector2 textureSize = m_sprite->GetCellSize(m_spriteSheetCellIndex);
  2065. // calculate difference in the current rect size and the texture size
  2066. AZ::Vector2 sizeDiff = textureSize - rectSize;
  2067. // get the pivot of the element, the fixed image will render the texture aligned with the pivot
  2068. AZ::Vector2 pivot;
  2069. UiTransformBus::EventResult(pivot, GetEntityId(), &UiTransformBus::Events::GetPivot);
  2070. // if the anchors are together (no stretching) in either dimension
  2071. // and that dimension is not controlled by a LayoutFitter
  2072. // then adjust the offsets in that dimension to fit the texture
  2073. bool offsetsChanged = false;
  2074. if (anchors.m_left == anchors.m_right && !UiLayoutHelpers::IsControlledByHorizontalFit(GetEntityId()))
  2075. {
  2076. offsets.m_left -= sizeDiff.GetX() * pivot.GetX();
  2077. offsets.m_right += sizeDiff.GetX() * (1.0f - pivot.GetX());
  2078. offsetsChanged = true;
  2079. }
  2080. if (anchors.m_top == anchors.m_bottom && !UiLayoutHelpers::IsControlledByVerticalFit(GetEntityId()))
  2081. {
  2082. offsets.m_top -= sizeDiff.GetY() * pivot.GetY();
  2083. offsets.m_bottom += sizeDiff.GetY() * (1.0f - pivot.GetY());
  2084. offsetsChanged = true;
  2085. }
  2086. if (offsetsChanged)
  2087. {
  2088. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetOffsets, offsets);
  2089. UiEditorChangeNotificationBus::Broadcast(&UiEditorChangeNotificationBus::Events::OnEditorTransformPropertiesNeedRefresh);
  2090. }
  2091. }
  2092. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2093. bool UiImageComponent::IsPixelAligned()
  2094. {
  2095. AZ::EntityId canvasEntityId;
  2096. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  2097. bool isPixelAligned = true;
  2098. UiCanvasBus::EventResult(isPixelAligned, canvasEntityId, &UiCanvasBus::Events::GetIsPixelAligned);
  2099. return isPixelAligned;
  2100. }
  2101. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2102. bool UiImageComponent::IsSpriteTypeAsset()
  2103. {
  2104. return m_spriteType == UiImageInterface::SpriteType::SpriteAsset;
  2105. }
  2106. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2107. bool UiImageComponent::IsSpriteTypeSpriteSheet()
  2108. {
  2109. return GetImageIndexCount() > 1;
  2110. }
  2111. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2112. bool UiImageComponent::IsSpriteTypeRenderTarget()
  2113. {
  2114. return (m_spriteType == UiImageInterface::SpriteType::RenderTarget);
  2115. }
  2116. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2117. bool UiImageComponent::IsFilled()
  2118. {
  2119. return (m_fillType != FillType::None);
  2120. }
  2121. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2122. bool UiImageComponent::IsLinearFilled()
  2123. {
  2124. return (m_fillType == FillType::Linear);
  2125. }
  2126. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2127. bool UiImageComponent::IsRadialFilled()
  2128. {
  2129. return (m_fillType == FillType::Radial);
  2130. }
  2131. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2132. bool UiImageComponent::IsRadialAnyFilled()
  2133. {
  2134. return (m_fillType == FillType::Radial) || (m_fillType == FillType::RadialCorner) || (m_fillType == FillType::RadialEdge);
  2135. }
  2136. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2137. bool UiImageComponent::IsCornerFilled()
  2138. {
  2139. return (m_fillType == FillType::RadialCorner);
  2140. }
  2141. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2142. bool UiImageComponent::IsEdgeFilled()
  2143. {
  2144. return (m_fillType == FillType::RadialEdge) || (m_fillType == FillType::Linear);
  2145. }
  2146. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2147. bool UiImageComponent::IsSliced()
  2148. {
  2149. return m_imageType == ImageType::Sliced;
  2150. }
  2151. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2152. void UiImageComponent::OnEditorSpritePathnameChange()
  2153. {
  2154. OnSpritePathnameChange();
  2155. if (m_imageType == ImageType::Fixed)
  2156. {
  2157. SnapOffsetsToFixedImage();
  2158. }
  2159. UiEditorChangeNotificationBus::Broadcast(&UiEditorChangeNotificationBus::Events::OnEditorPropertiesRefreshEntireTree);
  2160. CheckLayoutFitterAndRefreshEditorTransformProperties();
  2161. }
  2162. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2163. void UiImageComponent::OnEditorSpriteTypeChange()
  2164. {
  2165. OnSpriteTypeChange();
  2166. if (m_imageType == ImageType::Fixed)
  2167. {
  2168. SnapOffsetsToFixedImage();
  2169. }
  2170. CheckLayoutFitterAndRefreshEditorTransformProperties();
  2171. }
  2172. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2173. void UiImageComponent::OnEditorImageTypeChange()
  2174. {
  2175. if (m_imageType == ImageType::Fixed)
  2176. {
  2177. SnapOffsetsToFixedImage();
  2178. }
  2179. InvalidateLayouts();
  2180. CheckLayoutFitterAndRefreshEditorTransformProperties();
  2181. }
  2182. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2183. void UiImageComponent::OnEditorRenderSettingChange()
  2184. {
  2185. // something changed in the properties that requires re-rendering
  2186. MarkRenderCacheDirty();
  2187. }
  2188. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2189. void UiImageComponent::OnSpritePathnameChange()
  2190. {
  2191. ISprite* newSprite = nullptr;
  2192. if (!m_spritePathname.GetAssetPath().empty())
  2193. {
  2194. // Load the new texture.
  2195. newSprite = AZ::Interface<ILyShine>::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str());
  2196. }
  2197. // if listening for notifications from a current sprite then disconnect
  2198. if (UiSpriteSettingsChangeNotificationBus::Handler::BusIsConnected())
  2199. {
  2200. UiSpriteSettingsChangeNotificationBus::Handler::BusDisconnect();
  2201. }
  2202. SAFE_RELEASE(m_sprite);
  2203. m_sprite = newSprite;
  2204. // Listen for change notifications from the new sprite
  2205. if (m_sprite)
  2206. {
  2207. UiSpriteSettingsChangeNotificationBus::Handler::BusConnect(m_sprite);
  2208. }
  2209. InvalidateLayouts();
  2210. ResetSpriteSheetCellIndex();
  2211. UiSpriteSourceNotificationBus::Event(GetEntityId(), &UiSpriteSourceNotificationBus::Events::OnSpriteSourceChanged);
  2212. }
  2213. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2214. void UiImageComponent::OnSpriteAttachmentImageAssetChange()
  2215. {
  2216. ISprite* newSprite = nullptr;
  2217. if (m_attachmentImageAsset)
  2218. {
  2219. newSprite = AZ::Interface<ILyShine>::Get()->CreateSprite(m_attachmentImageAsset);
  2220. }
  2221. SAFE_RELEASE(m_sprite);
  2222. m_sprite = newSprite;
  2223. InvalidateLayouts();
  2224. }
  2225. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2226. void UiImageComponent::OnSpriteTypeChange()
  2227. {
  2228. if (IsSpriteTypeAsset())
  2229. {
  2230. OnSpritePathnameChange();
  2231. }
  2232. else if (m_spriteType == UiImageInterface::SpriteType::RenderTarget)
  2233. {
  2234. OnSpriteAttachmentImageAssetChange();
  2235. }
  2236. else
  2237. {
  2238. AZ_Assert(false, "unhandled sprite type");
  2239. }
  2240. }
  2241. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2242. void UiImageComponent::OnColorChange()
  2243. {
  2244. m_overrideColor = m_color;
  2245. m_overrideAlpha = m_alpha;
  2246. MarkRenderCacheDirty();
  2247. }
  2248. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2249. void UiImageComponent::InvalidateLayouts()
  2250. {
  2251. AZ::EntityId canvasEntityId;
  2252. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  2253. UiLayoutManagerBus::Event(
  2254. canvasEntityId, &UiLayoutManagerBus::Events::MarkToRecomputeLayoutsAffectedByLayoutCellChange, GetEntityId(), true);
  2255. UiLayoutManagerBus::Event(canvasEntityId, &UiLayoutManagerBus::Events::MarkToRecomputeLayout, GetEntityId());
  2256. MarkRenderCacheDirty();
  2257. }
  2258. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2259. void UiImageComponent::OnIndexChange()
  2260. {
  2261. // Index update logic will go here
  2262. MarkRenderCacheDirty();
  2263. }
  2264. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2265. LyShine::AZu32ComboBoxVec UiImageComponent::PopulateIndexStringList() const
  2266. {
  2267. // There may not be a sprite loaded for this component
  2268. if (m_sprite)
  2269. {
  2270. const AZ::u32 numCells = static_cast<AZ::u32>(m_sprite->GetSpriteSheetCells().size());
  2271. if (numCells != 0)
  2272. {
  2273. return LyShine::GetEnumSpriteIndexList(GetEntityId(), 0, numCells - 1);
  2274. }
  2275. }
  2276. return LyShine::AZu32ComboBoxVec();
  2277. }
  2278. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2279. void UiImageComponent::CheckLayoutFitterAndRefreshEditorTransformProperties() const
  2280. {
  2281. UiLayoutHelpers::CheckFitterAndRefreshEditorTransformProperties(GetEntityId());
  2282. }
  2283. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2284. // PRIVATE STATIC MEMBER FUNCTIONS
  2285. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2286. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2287. bool UiImageComponent::VersionConverter(AZ::SerializeContext& context,
  2288. AZ::SerializeContext::DataElementNode& classElement)
  2289. {
  2290. // conversion from version 1:
  2291. // - Need to convert CryString elements to AZStd::string
  2292. // - Need to convert Color to Color and Alpha
  2293. AZ_Assert(classElement.GetVersion() > 1, "Unsupported UiImageComponent version: %d", classElement.GetVersion());
  2294. // conversion from version 1 or 2 to current:
  2295. // - Need to convert AZStd::string sprites to AzFramework::SimpleAssetReference<LmbrCentral::TextureAsset>
  2296. if (classElement.GetVersion() <= 2)
  2297. {
  2298. if (!LyShine::ConvertSubElementFromAzStringToAssetRef<LmbrCentral::TextureAsset>(context, classElement, "SpritePath"))
  2299. {
  2300. return false;
  2301. }
  2302. }
  2303. // conversion from version 3 to current:
  2304. // - Strip off any leading forward slashes from sprite path
  2305. if (classElement.GetVersion() <= 3)
  2306. {
  2307. if (!LyShine::RemoveLeadingForwardSlashesFromAssetPath(context, classElement, "SpritePath"))
  2308. {
  2309. return false;
  2310. }
  2311. }
  2312. // conversion from version 4 to current:
  2313. // - Need to convert AZ::Vector3 to AZ::Color
  2314. if (classElement.GetVersion() <= 4)
  2315. {
  2316. if (!LyShine::ConvertSubElementFromVector3ToAzColor(context, classElement, "Color"))
  2317. {
  2318. return false;
  2319. }
  2320. }
  2321. // conversion to version 8:
  2322. // - Need to remove render target name as it was replaced with attachment image asset
  2323. if (classElement.GetVersion() <= 7)
  2324. {
  2325. if (!LyShine::RemoveRenderTargetAsString(context, classElement, "RenderTargetName"))
  2326. {
  2327. return false;
  2328. }
  2329. }
  2330. return true;
  2331. }