Draw2d.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  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 "Draw2d.h"
  9. #include <LyShine/UiRenderFormats.h>
  10. #include "LyShinePassDataBus.h"
  11. #include <AzCore/Math/Matrix3x3.h>
  12. #include <AzCore/Math/MatrixUtils.h>
  13. #include <AzFramework/Font/FontInterface.h>
  14. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  15. #include <Atom/RPI.Public/RPISystemInterface.h>
  16. #include <Atom/RHI/RHISystemInterface.h>
  17. #include <Atom/RPI.Public/Pass/RasterPass.h>
  18. #include <Atom/RPI.Public/Shader/Shader.h>
  19. #include <Atom/RPI.Public/Image/StreamingImage.h>
  20. #include <Atom/RPI.Public/RPIUtils.h>
  21. #include <Atom/RPI.Public/ViewportContextBus.h>
  22. namespace
  23. {
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////
  25. // Color to u32 => 0xAARRGGBB
  26. AZ::u32 PackARGB8888(const AZ::Color& color)
  27. {
  28. return (color.GetA8() << 24) | (color.GetR8() << 16) | (color.GetG8() << 8) | color.GetB8();
  29. }
  30. ////////////////////////////////////////////////////////////////////////////////////////////////////
  31. // Vertex format for Dynamic Draw Context
  32. struct Draw2dVertex
  33. {
  34. Vec3 xyz;
  35. LyShine::UCol color;
  36. Vec2 st;
  37. };
  38. }
  39. ////////////////////////////////////////////////////////////////////////////////////////////////////
  40. // PUBLIC MEMBER FUNCTIONS
  41. ////////////////////////////////////////////////////////////////////////////////////////////////////
  42. ////////////////////////////////////////////////////////////////////////////////////////////////////
  43. CDraw2d::CDraw2d(AZ::RPI::ViewportContextPtr viewportContext)
  44. : m_deferCalls(false)
  45. , m_viewportContext(viewportContext)
  46. {
  47. // These default options are set here and never change. They are stored so that if a null options
  48. // structure is passed into the draw functions then this default one can be used instead
  49. m_defaultTextOptions.fontName = "default";
  50. m_defaultTextOptions.effectIndex = 0;
  51. m_defaultTextOptions.color.Set(1.0f, 1.0f, 1.0f);
  52. m_defaultTextOptions.horizontalAlignment = HAlign::Left;
  53. m_defaultTextOptions.verticalAlignment = VAlign::Top;
  54. m_defaultTextOptions.dropShadowOffset.Set(0.0f, 0.0f);
  55. m_defaultTextOptions.dropShadowColor.Set(0.0f, 0.0f, 0.0f, 0.0f);
  56. m_defaultTextOptions.rotation = 0.0f;
  57. m_defaultTextOptions.depthTestEnabled = false;
  58. AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect();
  59. }
  60. ////////////////////////////////////////////////////////////////////////////////////////////////////
  61. CDraw2d::~CDraw2d()
  62. {
  63. AZ::Render::Bootstrap::NotificationBus::Handler::BusDisconnect();
  64. }
  65. ////////////////////////////////////////////////////////////////////////////////////////////////////
  66. void CDraw2d::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene)
  67. {
  68. // At this point the RPI is ready for use
  69. // Load the shader to be used for 2d drawing
  70. const char* shaderFilepath = "Shaders/SimpleTextured.azshader";
  71. AZ::Data::Instance<AZ::RPI::Shader> shader = AZ::RPI::LoadCriticalShader(shaderFilepath);
  72. // Set scene to be associated with the dynamic draw context
  73. AZ::RPI::Scene* scene = nullptr;
  74. if (m_viewportContext)
  75. {
  76. // Use scene associated with the specified viewport context
  77. scene = m_viewportContext->GetRenderScene().get();
  78. }
  79. else
  80. {
  81. // No viewport context specified, use main scene
  82. scene = bootstrapScene;
  83. }
  84. AZ_Assert(scene != nullptr, "Attempting to create a DynamicDrawContext for a viewport context that has not been associated with a scene yet.");
  85. // Create and initialize a DynamicDrawContext for 2d drawing
  86. // Get the pass for the dynamic draw context to render to
  87. AZ::RPI::RasterPass* uiCanvasPass = nullptr;
  88. AZ::RPI::SceneId sceneId = scene->GetId();
  89. LyShinePassRequestBus::EventResult(uiCanvasPass, sceneId, &LyShinePassRequestBus::Events::GetUiCanvasPass);
  90. if (!uiCanvasPass)
  91. {
  92. AZ_Error("Draw2d", false, "No UiCanvasPass found in Render-Pipeline.");
  93. return;
  94. }
  95. m_dynamicDraw = AZ::RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext();
  96. m_dynamicDraw->InitShader(shader);
  97. m_dynamicDraw->InitVertexFormat(
  98. { {"POSITION", AZ::RHI::Format::R32G32B32_FLOAT},
  99. {"COLOR", AZ::RHI::Format::B8G8R8A8_UNORM},
  100. {"TEXCOORD0", AZ::RHI::Format::R32G32_FLOAT} });
  101. m_dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::PrimitiveType
  102. | AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode
  103. | AZ::RPI::DynamicDrawContext::DrawStateOptions::DepthState
  104. | AZ::RPI::DynamicDrawContext::DrawStateOptions::ShaderVariant);
  105. // Use scene as output scope (will render to the UiCanvas child pass of the LyShine pass)
  106. m_dynamicDraw->SetOutputScope(scene);
  107. m_dynamicDraw->InitDrawListTag(uiCanvasPass->GetDrawListTag());
  108. m_dynamicDraw->EndInit();
  109. // Check that the dynamic draw context has been initialized appropriately
  110. if (m_dynamicDraw->IsReady())
  111. {
  112. // Cache draw srg input indices for later use
  113. static const char textureIndexName[] = "m_texture";
  114. static const char worldToProjIndexName[] = "m_worldToProj";
  115. AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = m_dynamicDraw->NewDrawSrg();
  116. if (drawSrg)
  117. {
  118. const AZ::RHI::ShaderResourceGroupLayout* layout = drawSrg->GetLayout();
  119. m_shaderData.m_imageInputIndex = layout->FindShaderInputImageIndex(AZ::Name(textureIndexName));
  120. AZ_Error("Draw2d", m_shaderData.m_imageInputIndex.IsValid(), "Failed to find shader input constant %s.",
  121. textureIndexName);
  122. m_shaderData.m_viewProjInputIndex = layout->FindShaderInputConstantIndex(AZ::Name(worldToProjIndexName));
  123. AZ_Error("Draw2d", m_shaderData.m_viewProjInputIndex.IsValid(), "Failed to find shader input constant %s.",
  124. worldToProjIndexName);
  125. }
  126. // Cache shader variants that will be used
  127. AZ::RPI::ShaderOptionList shaderOptionsClamp;
  128. shaderOptionsClamp.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true")));
  129. shaderOptionsClamp.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true")));
  130. m_shaderData.m_shaderOptionsClamp = m_dynamicDraw->UseShaderVariant(shaderOptionsClamp);
  131. AZ::RPI::ShaderOptionList shaderOptionsWrap;
  132. shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("false")));
  133. shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true")));
  134. m_shaderData.m_shaderOptionsWrap = m_dynamicDraw->UseShaderVariant(shaderOptionsWrap);
  135. }
  136. }
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////
  138. // Draw a textured quad with the top left corner at the given position.
  139. void CDraw2d::DrawImage(AZ::Data::Instance<AZ::RPI::Image> image, AZ::Vector2 position, AZ::Vector2 size, float opacity,
  140. float rotation, const AZ::Vector2* pivotPoint, const AZ::Vector2* minMaxTexCoords,
  141. ImageOptions* imageOptions)
  142. {
  143. ImageOptions* actualImageOptions = (imageOptions) ? imageOptions : &m_defaultImageOptions;
  144. AZ::Color color = AZ::Color::CreateFromVector3AndFloat(actualImageOptions->color, opacity);
  145. AZ::u32 packedColor = PackARGB8888(color);
  146. // Depending on the requested pixel rounding setting we may round position to an exact pixel
  147. AZ::Vector2 pos = Draw2dHelper::RoundXY(position, actualImageOptions->pixelRounding);
  148. // define quad (in clockwise order)
  149. DeferredQuad quad;
  150. quad.m_points[0].Set(pos.GetX(), pos.GetY());
  151. quad.m_points[1].Set(pos.GetX() + size.GetX(), pos.GetY());
  152. quad.m_points[2].Set(pos.GetX() + size.GetX(), pos.GetY() + size.GetY());
  153. quad.m_points[3].Set(pos.GetX(), pos.GetY() + size.GetY());
  154. quad.m_packedColors[0] = packedColor;
  155. quad.m_packedColors[1] = packedColor;
  156. quad.m_packedColors[2] = packedColor;
  157. quad.m_packedColors[3] = packedColor;
  158. if (minMaxTexCoords)
  159. {
  160. quad.m_texCoords[0] = AZ::Vector2(minMaxTexCoords[0].GetX(), minMaxTexCoords[0].GetY());
  161. quad.m_texCoords[1] = AZ::Vector2(minMaxTexCoords[1].GetX(), minMaxTexCoords[0].GetY());
  162. quad.m_texCoords[2] = AZ::Vector2(minMaxTexCoords[1].GetX(), minMaxTexCoords[1].GetY());
  163. quad.m_texCoords[3] = AZ::Vector2(minMaxTexCoords[0].GetX(), minMaxTexCoords[1].GetY());
  164. }
  165. else
  166. {
  167. quad.m_texCoords[0].Set(0.0f, 0.0f);
  168. quad.m_texCoords[1].Set(1.0f, 0.0f);
  169. quad.m_texCoords[2].Set(1.0f, 1.0f);
  170. quad.m_texCoords[3].Set(0.0f, 1.0f);
  171. }
  172. quad.m_image = image;
  173. quad.m_clamp = actualImageOptions->m_clamp;
  174. // add the blendMode flags to the base state
  175. quad.m_renderState = actualImageOptions->m_renderState;
  176. // apply rotation if requested
  177. if (rotation != 0.0f)
  178. {
  179. AZ::Vector2 pivot = (pivotPoint) ? *pivotPoint : quad.m_points[0];
  180. RotatePointsAboutPivot(quad.m_points, 4, pivot, rotation);
  181. }
  182. DrawOrDeferQuad(&quad);
  183. }
  184. ////////////////////////////////////////////////////////////////////////////////////////////////////
  185. void CDraw2d::DrawImageAligned(AZ::Data::Instance<AZ::RPI::Image> image, AZ::Vector2 position, AZ::Vector2 size,
  186. HAlign horizontalAlignment, VAlign verticalAlignment,
  187. float opacity, float rotation, const AZ::Vector2* minMaxTexCoords, ImageOptions* imageOptions)
  188. {
  189. AZ::Vector2 alignedPosition = Draw2dHelper::Align(position, size, horizontalAlignment, verticalAlignment);
  190. DrawImage(image, alignedPosition, size, opacity, rotation, &position, minMaxTexCoords, imageOptions);
  191. }
  192. ////////////////////////////////////////////////////////////////////////////////////////////////////
  193. void CDraw2d::DrawQuad(AZ::Data::Instance<AZ::RPI::Image> image, VertexPosColUV* verts, Rounding pixelRounding,
  194. bool clamp, const CDraw2d::RenderState& renderState)
  195. {
  196. // define quad
  197. DeferredQuad quad;
  198. for (int i = 0; i < 4; ++i)
  199. {
  200. quad.m_points[i] = Draw2dHelper::RoundXY(verts[i].position, pixelRounding);
  201. quad.m_texCoords[i] = verts[i].uv;
  202. quad.m_packedColors[i] = PackARGB8888(verts[i].color);
  203. }
  204. quad.m_image = image;
  205. quad.m_clamp = clamp;
  206. // add the blendMode flags to the base state
  207. quad.m_renderState = renderState;
  208. DrawOrDeferQuad(&quad);
  209. }
  210. ////////////////////////////////////////////////////////////////////////////////////////////////////
  211. void CDraw2d::DrawLine(AZ::Vector2 start, AZ::Vector2 end, AZ::Color color, Rounding pixelRounding,
  212. const CDraw2d::RenderState& renderState)
  213. {
  214. auto image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
  215. // define line
  216. uint32 packedColor = PackARGB8888(color);
  217. DeferredLine line;
  218. line.m_image = image;
  219. line.m_points[0] = Draw2dHelper::RoundXY(start, pixelRounding);
  220. line.m_points[1] = Draw2dHelper::RoundXY(end, pixelRounding);
  221. line.m_texCoords[0] = AZ::Vector2(0, 0);
  222. line.m_texCoords[1] = AZ::Vector2(1, 1);
  223. line.m_packedColors[0] = packedColor;
  224. line.m_packedColors[1] = packedColor;
  225. // add the blendMode flags to the base state
  226. line.m_renderState = renderState;
  227. DrawOrDeferLine(&line);
  228. }
  229. ////////////////////////////////////////////////////////////////////////////////////////////////////
  230. ////////////////////////////////////////////////////////////////////////////////////////////////////
  231. void CDraw2d::DrawLineTextured(AZ::Data::Instance<AZ::RPI::Image> image, VertexPosColUV* verts, Rounding pixelRounding,
  232. const CDraw2d::RenderState& renderState)
  233. {
  234. // define line
  235. DeferredLine line;
  236. line.m_image = image;
  237. for (int i = 0; i < 2; ++i)
  238. {
  239. line.m_points[i] = Draw2dHelper::RoundXY(verts[i].position, pixelRounding);
  240. line.m_texCoords[i] = verts[i].uv;
  241. line.m_packedColors[i] = PackARGB8888(verts[i].color);
  242. }
  243. // add the blendMode flags to the base state
  244. line.m_renderState = renderState;
  245. DrawOrDeferLine(&line);
  246. }
  247. ////////////////////////////////////////////////////////////////////////////////////////////////////
  248. // NOTE: The alignment does not take account of the drop shadow. i.e. the non-shadow text is centered
  249. // and the drop shadow text is offset from that.
  250. ////////////////////////////////////////////////////////////////////////////////////////////////////
  251. void CDraw2d::DrawText(const char* textString, AZ::Vector2 position, float pointSize, float opacity, TextOptions* textOptions)
  252. {
  253. TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions;
  254. AzFramework::FontId fontId = AzFramework::InvalidFontId;
  255. AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get();
  256. if (fontQueryInterface)
  257. {
  258. fontId = fontQueryInterface->GetFontId(actualTextOptions->fontName);
  259. }
  260. // render the drop shadow, if needed
  261. if ((actualTextOptions->dropShadowColor.GetA() > 0.0f) &&
  262. (actualTextOptions->dropShadowOffset.GetX() || actualTextOptions->dropShadowOffset.GetY()))
  263. {
  264. // calculate the drop shadow pos and render it
  265. AZ::Vector2 dropShadowPosition(position + actualTextOptions->dropShadowOffset);
  266. DrawTextInternal(textString, fontId, actualTextOptions->effectIndex,
  267. dropShadowPosition, pointSize, actualTextOptions->dropShadowColor,
  268. actualTextOptions->rotation,
  269. actualTextOptions->horizontalAlignment, actualTextOptions->verticalAlignment,
  270. actualTextOptions->depthTestEnabled);
  271. }
  272. // draw the text string
  273. AZ::Color textColor = AZ::Color::CreateFromVector3AndFloat(actualTextOptions->color, opacity);
  274. DrawTextInternal(textString, fontId, actualTextOptions->effectIndex,
  275. position, pointSize, textColor,
  276. actualTextOptions->rotation,
  277. actualTextOptions->horizontalAlignment, actualTextOptions->verticalAlignment,
  278. actualTextOptions->depthTestEnabled);
  279. }
  280. void CDraw2d::DrawRectOutlineTextured(AZ::Data::Instance<AZ::RPI::Image> image,
  281. UiTransformInterface::RectPoints points,
  282. AZ::Vector2 rightVec,
  283. AZ::Vector2 downVec,
  284. AZ::Color color,
  285. uint32_t lineThickness)
  286. {
  287. // since the rect can be transformed we have to add the offsets by multiplying them
  288. // by unit vectors parallel with the edges of the rect. However, the rect could be
  289. // zero width and/or height so we can't use "points" to compute these unit vectors.
  290. // So we instead get two transformed unit vectors and then normalize them
  291. rightVec.NormalizeSafe();
  292. downVec.NormalizeSafe();
  293. // calculate the transformed width and height of the rect
  294. // (in case it is smaller than the texture height)
  295. AZ::Vector2 widthVec = points.TopRight() - points.TopLeft();
  296. AZ::Vector2 heightVec = points.BottomLeft() - points.TopLeft();
  297. float rectWidth = widthVec.GetLength();
  298. float rectHeight = heightVec.GetLength();
  299. if (lineThickness == 0 && image)
  300. {
  301. lineThickness = image->GetDescriptor().m_size.m_height;
  302. }
  303. if (lineThickness == 0)
  304. {
  305. AZ_Assert(false, "Attempting to draw a rect outline with of zero thickness.");
  306. return;
  307. }
  308. // the outline is centered on the element rect so half the outline is outside
  309. // the rect and half is inside the rect
  310. float offset = aznumeric_cast<float>(lineThickness);
  311. float outerOffset = -offset * 0.5f;
  312. float innerOffset = offset * 0.5f;
  313. float outerV = 0.0f;
  314. float innerV = 1.0f;
  315. // if the rect is small there may not be space for the half of the outline that
  316. // is inside the rect. If this is the case reduce the innerOffset so the inner
  317. // points are coincident. Adjust the UVs according to keep a 1-1 texel to pixel ratio.
  318. float minDimension = min(rectWidth, rectHeight);
  319. if (innerOffset > minDimension * 0.5f)
  320. {
  321. float oldInnerOffset = innerOffset;
  322. innerOffset = minDimension * 0.5f;
  323. // note oldInnerOffset can't be zero because of early return if lineThickness is zero
  324. innerV = 0.5f + 0.5f * innerOffset / oldInnerOffset;
  325. }
  326. DeferredRectOutline rectOutline;
  327. // fill out the 8 verts to define the 2 rectangles - outer and inner
  328. // The vertices are in the order of outer rect then inner rect. e.g.:
  329. // 0 1
  330. // 4 5
  331. // 6 7
  332. // 2 3
  333. //
  334. // four verts of outer rect
  335. rectOutline.m_verts2d[0] = points.pt[0] + rightVec * outerOffset + downVec * outerOffset;
  336. rectOutline.m_verts2d[1] = points.pt[1] - rightVec * outerOffset + downVec * outerOffset;
  337. rectOutline.m_verts2d[2] = points.pt[3] + rightVec * outerOffset - downVec * outerOffset;
  338. rectOutline.m_verts2d[3] = points.pt[2] - rightVec * outerOffset - downVec * outerOffset;
  339. // four verts of inner rect
  340. rectOutline.m_verts2d[4] = points.pt[0] + rightVec * innerOffset + downVec * innerOffset;
  341. rectOutline.m_verts2d[5] = points.pt[1] - rightVec * innerOffset + downVec * innerOffset;
  342. rectOutline.m_verts2d[6] = points.pt[3] + rightVec * innerOffset - downVec * innerOffset;
  343. rectOutline.m_verts2d[7] = points.pt[2] - rightVec * innerOffset - downVec * innerOffset;
  344. // and define the UV coordinates for these 8 verts
  345. rectOutline.m_uvs[0] = AZ::Vector2(0.0f, outerV);
  346. rectOutline.m_uvs[1] = AZ::Vector2(1.0f, outerV);
  347. rectOutline.m_uvs[2] = AZ::Vector2(1.0f, outerV);
  348. rectOutline.m_uvs[3] = AZ::Vector2(0.0f, outerV);
  349. rectOutline.m_uvs[4] = AZ::Vector2(0.0f, innerV);
  350. rectOutline.m_uvs[5] = AZ::Vector2(1.0f, innerV);
  351. rectOutline.m_uvs[6] = AZ::Vector2(1.0f, innerV);
  352. rectOutline.m_uvs[7] = AZ::Vector2(0.0f, innerV);
  353. rectOutline.m_image = image;
  354. rectOutline.m_color = color;
  355. DrawOrDeferRectOutline(&rectOutline);
  356. }
  357. ////////////////////////////////////////////////////////////////////////////////////////////////////
  358. // TBD should the size include the offset of the drop shadow (if any)?
  359. ////////////////////////////////////////////////////////////////////////////////////////////////////
  360. AZ::Vector2 CDraw2d::GetTextSize(const char* textString, float pointSize, TextOptions* textOptions)
  361. {
  362. AzFramework::FontDrawInterface* fontDrawInterface = nullptr;
  363. AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get();
  364. if (fontQueryInterface)
  365. {
  366. TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions;
  367. AzFramework::FontId fontId = fontQueryInterface->GetFontId(actualTextOptions->fontName);
  368. fontDrawInterface = fontQueryInterface->GetFontDrawInterface(fontId);
  369. }
  370. if (!fontDrawInterface)
  371. {
  372. return AZ::Vector2(0.0f, 0.0f);
  373. }
  374. // Set up draw parameters
  375. AzFramework::TextDrawParameters drawParams;
  376. drawParams.m_drawViewportId = GetViewportContext()->GetId();
  377. drawParams.m_position = AZ::Vector3(0.0f, 0.0f, 1.0f);
  378. drawParams.m_effectIndex = 0;
  379. drawParams.m_textSizeFactor = pointSize;
  380. drawParams.m_scale = AZ::Vector2(1.0f, 1.0f);
  381. drawParams.m_lineSpacing = 1.0f;
  382. drawParams.m_monospace = false;
  383. drawParams.m_depthTest = false;
  384. drawParams.m_virtual800x600ScreenSize = false;
  385. drawParams.m_scaleWithWindow = false;
  386. drawParams.m_multiline = true;
  387. AZ::Vector2 textSize = fontDrawInterface->GetTextSize(drawParams, textString);
  388. return textSize;
  389. }
  390. ////////////////////////////////////////////////////////////////////////////////////////////////////
  391. float CDraw2d::GetViewportWidth() const
  392. {
  393. auto windowContext = GetViewportContext()->GetWindowContext();
  394. const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
  395. const float viewWidth = viewport.m_maxX - viewport.m_minX;
  396. return viewWidth;
  397. }
  398. ////////////////////////////////////////////////////////////////////////////////////////////////////
  399. float CDraw2d::GetViewportHeight() const
  400. {
  401. auto windowContext = GetViewportContext()->GetWindowContext();
  402. const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
  403. const float viewHeight = viewport.m_maxY - viewport.m_minY;
  404. return viewHeight;
  405. }
  406. ////////////////////////////////////////////////////////////////////////////////////////////////////
  407. float CDraw2d::GetViewportDpiScalingFactor() const
  408. {
  409. return GetViewportContext()->GetDpiScalingFactor();
  410. }
  411. ////////////////////////////////////////////////////////////////////////////////////////////////////
  412. const CDraw2d::ImageOptions& CDraw2d::GetDefaultImageOptions() const
  413. {
  414. return m_defaultImageOptions;
  415. }
  416. ////////////////////////////////////////////////////////////////////////////////////////////////////
  417. const CDraw2d::TextOptions& CDraw2d::GetDefaultTextOptions() const
  418. {
  419. return m_defaultTextOptions;
  420. }
  421. ////////////////////////////////////////////////////////////////////////////////////////////////////
  422. void CDraw2d::RenderDeferredPrimitives()
  423. {
  424. // Draw and delete the deferred primitives
  425. AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext();
  426. for (auto primIter : m_deferredPrimitives)
  427. {
  428. primIter->Draw(m_dynamicDraw, m_shaderData, viewportContext);
  429. delete primIter;
  430. }
  431. // clear the list of deferred primitives
  432. m_deferredPrimitives.clear();
  433. }
  434. ////////////////////////////////////////////////////////////////////////////////////////////////////
  435. void CDraw2d::SetDeferPrimitives(bool deferPrimitives)
  436. {
  437. m_deferCalls = deferPrimitives;
  438. }
  439. ////////////////////////////////////////////////////////////////////////////////////////////////////
  440. bool CDraw2d::GetDeferPrimitives()
  441. {
  442. return m_deferCalls;
  443. }
  444. ////////////////////////////////////////////////////////////////////////////////////////////////////
  445. void CDraw2d::SetSortKey(int64_t key)
  446. {
  447. m_dynamicDraw->SetSortKey(key);
  448. }
  449. ////////////////////////////////////////////////////////////////////////////////////////////////////
  450. // PUBLIC STATIC FUNCTIONS
  451. ////////////////////////////////////////////////////////////////////////////////////////////////////
  452. ////////////////////////////////////////////////////////////////////////////////////////////////////
  453. AZ::Data::Instance<AZ::RPI::Image> CDraw2d::LoadTexture(const AZStd::string& pathName)
  454. {
  455. // The file may not be in the AssetCatalog at this point if it is still processing or doesn't exist on disk.
  456. // Use GenerateAssetIdTEMP instead of GetAssetIdByPath so that it will return a valid AssetId anyways
  457. AZ::Data::AssetId streamingImageAssetId;
  458. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  459. streamingImageAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GenerateAssetIdTEMP,
  460. pathName.c_str());
  461. streamingImageAssetId.m_subId = AZ::RPI::StreamingImageAsset::GetImageAssetSubId();
  462. auto streamingImageAsset = AZ::Data::AssetManager::Instance().FindOrCreateAsset<AZ::RPI::StreamingImageAsset>(streamingImageAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
  463. AZ::Data::Instance<AZ::RPI::Image> image = AZ::RPI::StreamingImage::FindOrCreate(streamingImageAsset);
  464. if (!image)
  465. {
  466. AZ_Error("Draw2d", false, "Failed to find or create an image instance from image asset '%s'", pathName.c_str());
  467. }
  468. return image;
  469. }
  470. ////////////////////////////////////////////////////////////////////////////////////////////////////
  471. // PROTECTED MEMBER FUNCTIONS
  472. ////////////////////////////////////////////////////////////////////////////////////////////////////
  473. ////////////////////////////////////////////////////////////////////////////////////////////////////
  474. void CDraw2d::RotatePointsAboutPivot(AZ::Vector2* points, [[maybe_unused]] int numPoints, AZ::Vector2 pivot, float angle) const
  475. {
  476. float angleRadians = DEG2RAD(angle);
  477. AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateRotationZ(angleRadians);
  478. for (int i = 0; i < 4; ++i)
  479. {
  480. AZ::Vector2 offset = points[i] - pivot;
  481. AZ::Vector3 offset3(offset.GetX(), offset.GetY(), 0.0f);
  482. offset3 = offset3 * rotationMatrix;
  483. offset.Set(offset3.GetX(), offset3.GetY());
  484. points[i] = pivot + offset;
  485. }
  486. }
  487. ////////////////////////////////////////////////////////////////////////////////////////////////////
  488. void CDraw2d::DrawTextInternal(const char* textString, AzFramework::FontId fontId, unsigned int effectIndex,
  489. AZ::Vector2 position, float pointSize, AZ::Color color, float rotation,
  490. HAlign horizontalAlignment, VAlign verticalAlignment, bool depthTestEnabled)
  491. {
  492. // FFont.cpp uses the alpha value of the color to decide whether to use the color, if the alpha value is zero
  493. // (in a ColorB format) then the color set via SetColor is ignored and it usually ends up drawing with an alpha of 1.
  494. // This is not what we want so in this case do not draw at all.
  495. if (AZ::IsClose(color.GetA(), 0.0f))
  496. {
  497. return;
  498. }
  499. // Convert Draw2d alignment to text alignment
  500. AzFramework::TextHorizontalAlignment hAlignment = AzFramework::TextHorizontalAlignment::Left;
  501. switch (horizontalAlignment)
  502. {
  503. case HAlign::Left:
  504. hAlignment = AzFramework::TextHorizontalAlignment::Left;
  505. break;
  506. case HAlign::Center:
  507. hAlignment = AzFramework::TextHorizontalAlignment::Center;
  508. break;
  509. case HAlign::Right:
  510. hAlignment = AzFramework::TextHorizontalAlignment::Right;
  511. break;
  512. default:
  513. AZ_Assert(false, "Attempting to draw text with unsupported horizontal alignment.");
  514. break;
  515. }
  516. AzFramework::TextVerticalAlignment vAlignment = AzFramework::TextVerticalAlignment::Top;
  517. switch (verticalAlignment)
  518. {
  519. case VAlign::Top:
  520. vAlignment = AzFramework::TextVerticalAlignment::Top;
  521. break;
  522. case VAlign::Center:
  523. vAlignment = AzFramework::TextVerticalAlignment::Center;
  524. break;
  525. case VAlign::Bottom:
  526. vAlignment = AzFramework::TextVerticalAlignment::Bottom;
  527. break;
  528. default:
  529. AZ_Assert(false, "Attempting to draw text with unsupported vertical alignment.");
  530. break;
  531. }
  532. // Set up draw parameters for font interface
  533. AzFramework::TextDrawParameters drawParams;
  534. drawParams.m_drawViewportId = GetViewportContext()->GetId();
  535. drawParams.m_position = AZ::Vector3(position.GetX(), position.GetY(), 1.0f);
  536. drawParams.m_color = color;
  537. drawParams.m_effectIndex = effectIndex;
  538. drawParams.m_textSizeFactor = pointSize;
  539. drawParams.m_scale = AZ::Vector2(1.0f, 1.0f);
  540. drawParams.m_lineSpacing = 1.0f; //!< Spacing between new lines, as a percentage of m_scale.
  541. drawParams.m_hAlign = hAlignment;
  542. drawParams.m_vAlign = vAlignment;
  543. drawParams.m_monospace = false;
  544. drawParams.m_depthTest = depthTestEnabled;
  545. drawParams.m_virtual800x600ScreenSize = false;
  546. drawParams.m_scaleWithWindow = false;
  547. drawParams.m_multiline = true;
  548. if (rotation != 0.0f)
  549. {
  550. // rotate around the position (if aligned to center will rotate about center etc)
  551. float rotRad = DEG2RAD(rotation);
  552. AZ::Vector3 pivot(position.GetX(), position.GetY(), 0.0f);
  553. AZ::Matrix3x4 moveToPivotSpaceMat = AZ::Matrix3x4::CreateTranslation(-pivot);
  554. AZ::Matrix3x4 rotMat = AZ::Matrix3x4::CreateRotationZ(rotRad);
  555. AZ::Matrix3x4 moveFromPivotSpaceMat = AZ::Matrix3x4::CreateTranslation(pivot);
  556. drawParams.m_transform = moveFromPivotSpaceMat * rotMat * moveToPivotSpaceMat;
  557. drawParams.m_useTransform = true;
  558. }
  559. DeferredText newText;
  560. newText.m_drawParameters = drawParams;
  561. newText.m_fontId = fontId;
  562. newText.m_string = textString;
  563. DrawOrDeferTextString(&newText);
  564. }
  565. ////////////////////////////////////////////////////////////////////////////////////////////////////
  566. void CDraw2d::DrawOrDeferQuad(const DeferredQuad* quad)
  567. {
  568. if (m_deferCalls)
  569. {
  570. DeferredQuad* newQuad = new DeferredQuad;
  571. *newQuad = *quad;
  572. m_deferredPrimitives.push_back(newQuad);
  573. }
  574. else
  575. {
  576. quad->Draw(m_dynamicDraw, m_shaderData, GetViewportContext());
  577. }
  578. }
  579. ////////////////////////////////////////////////////////////////////////////////////////////////////
  580. void CDraw2d::DrawOrDeferLine(const DeferredLine* line)
  581. {
  582. if (m_deferCalls)
  583. {
  584. DeferredLine* newLine = new DeferredLine;
  585. *newLine = *line;
  586. m_deferredPrimitives.push_back(newLine);
  587. }
  588. else
  589. {
  590. line->Draw(m_dynamicDraw, m_shaderData, GetViewportContext());
  591. }
  592. }
  593. void CDraw2d::DrawOrDeferTextString(const DeferredText* text)
  594. {
  595. if (m_deferCalls)
  596. {
  597. DeferredText* newText = new DeferredText;
  598. *newText = *text;
  599. m_deferredPrimitives.push_back(newText);
  600. }
  601. else
  602. {
  603. text->Draw(m_dynamicDraw, m_shaderData, GetViewportContext());
  604. }
  605. }
  606. void CDraw2d::DrawOrDeferRectOutline(const DeferredRectOutline* rectOutline)
  607. {
  608. if (m_deferCalls)
  609. {
  610. DeferredRectOutline* newRectOutline = new DeferredRectOutline;
  611. *newRectOutline = *rectOutline;
  612. m_deferredPrimitives.push_back(newRectOutline);
  613. }
  614. else
  615. {
  616. rectOutline->Draw(m_dynamicDraw, m_shaderData, GetViewportContext());
  617. }
  618. }
  619. ////////////////////////////////////////////////////////////////////////////////////////////////////
  620. AZ::RPI::ViewportContextPtr CDraw2d::GetViewportContext() const
  621. {
  622. if (!m_viewportContext)
  623. {
  624. // Return the default viewport context
  625. auto viewContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
  626. return viewContextManager->GetDefaultViewportContext();
  627. }
  628. // Return the user specified viewport context
  629. return m_viewportContext;
  630. }
  631. ////////////////////////////////////////////////////////////////////////////////////////////////////
  632. // CDraw2d::DeferredQuad
  633. ////////////////////////////////////////////////////////////////////////////////////////////////////
  634. ////////////////////////////////////////////////////////////////////////////////////////////////////
  635. void CDraw2d::DeferredQuad::Draw(AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
  636. const Draw2dShaderData& shaderData,
  637. AZ::RPI::ViewportContextPtr viewportContext) const
  638. {
  639. const int32 NUM_VERTS = 6;
  640. const float z = 1.0f; // depth test disabled, if writing Z this will write at far plane
  641. Draw2dVertex vertices[NUM_VERTS];
  642. const int vertIndex[NUM_VERTS] = {
  643. 0, 1, 3, 3, 1, 2
  644. };
  645. for (int i = 0; i < NUM_VERTS; ++i)
  646. {
  647. int j = vertIndex[i];
  648. vertices[i].xyz = Vec3(m_points[j].GetX(), m_points[j].GetY(), z);
  649. vertices[i].color.dcolor = m_packedColors[j];
  650. vertices[i].st = Vec2(m_texCoords[j].GetX(), m_texCoords[j].GetY());
  651. }
  652. dynamicDraw->SetShaderVariant(m_clamp ? shaderData.m_shaderOptionsClamp : shaderData.m_shaderOptionsWrap);
  653. // Set up per draw SRG
  654. AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
  655. // Set texture
  656. const auto* imageView = m_image ? m_image->GetImageView() : nullptr;
  657. if (!imageView)
  658. {
  659. // Default to white texture
  660. auto image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
  661. imageView = image->GetImageView();
  662. }
  663. if (imageView)
  664. {
  665. drawSrg->SetImageView(shaderData.m_imageInputIndex, imageView, 0);
  666. }
  667. // Set projection matrix
  668. auto windowContext = viewportContext->GetWindowContext();
  669. const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
  670. const float viewX = viewport.m_minX;
  671. const float viewY = viewport.m_minY;
  672. const float viewWidth = viewport.m_maxX - viewport.m_minX;
  673. const float viewHeight = viewport.m_maxY - viewport.m_minY;
  674. const float zf = viewport.m_minZ;
  675. const float zn = viewport.m_maxZ;
  676. AZ::Matrix4x4 modelViewProjMat;
  677. AZ::MakeOrthographicMatrixRH(modelViewProjMat, viewX, viewX + viewWidth, viewY + viewHeight, viewY, zn, zf);
  678. drawSrg->SetConstant(shaderData.m_viewProjInputIndex, modelViewProjMat);
  679. drawSrg->Compile();
  680. // Add the primitive to the dynamic draw context for drawing
  681. dynamicDraw->SetPrimitiveType(AZ::RHI::PrimitiveTopology::TriangleList);
  682. dynamicDraw->SetDepthState(m_renderState.m_depthState);
  683. dynamicDraw->SetTarget0BlendState(m_renderState.m_blendState);
  684. dynamicDraw->DrawLinear(vertices, NUM_VERTS, drawSrg);
  685. }
  686. ////////////////////////////////////////////////////////////////////////////////////////////////////
  687. // CDraw2d::DeferredLine
  688. ////////////////////////////////////////////////////////////////////////////////////////////////////
  689. ////////////////////////////////////////////////////////////////////////////////////////////////////
  690. void CDraw2d::DeferredLine::Draw(AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
  691. const Draw2dShaderData& shaderData,
  692. AZ::RPI::ViewportContextPtr viewportContext) const
  693. {
  694. const float z = 1.0f; // depth test disabled, if writing Z this will write at far plane
  695. const int32 NUM_VERTS = 2;
  696. Draw2dVertex vertices[NUM_VERTS];
  697. for (int i = 0; i < NUM_VERTS; ++i)
  698. {
  699. vertices[i].xyz = Vec3(m_points[i].GetX(), m_points[i].GetY(), z);
  700. vertices[i].color.dcolor = m_packedColors[i];
  701. vertices[i].st = Vec2(m_texCoords[i].GetX(), m_texCoords[i].GetY());
  702. }
  703. // Set up per draw SRG
  704. AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
  705. // Set texture
  706. const auto* imageView = m_image ? m_image->GetImageView() : nullptr;
  707. if (!imageView)
  708. {
  709. // Default to white texture
  710. auto image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
  711. imageView = image->GetImageView();
  712. }
  713. if (imageView)
  714. {
  715. drawSrg->SetImageView(shaderData.m_imageInputIndex, imageView, 0);
  716. }
  717. // Set projection matrix
  718. auto windowContext = viewportContext->GetWindowContext();
  719. const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
  720. const float viewX = viewport.m_minX;
  721. const float viewY = viewport.m_minY;
  722. const float viewWidth = viewport.m_maxX - viewport.m_minX;
  723. const float viewHeight = viewport.m_maxY - viewport.m_minY;
  724. const float zf = viewport.m_minZ;
  725. const float zn = viewport.m_maxZ;
  726. AZ::Matrix4x4 modelViewProjMat;
  727. AZ::MakeOrthographicMatrixRH(modelViewProjMat, viewX, viewX + viewWidth, viewY + viewHeight, viewY, zn, zf);
  728. drawSrg->SetConstant(shaderData.m_viewProjInputIndex, modelViewProjMat);
  729. drawSrg->Compile();
  730. // Add the primitive to the dynamic draw context for drawing
  731. dynamicDraw->SetPrimitiveType(AZ::RHI::PrimitiveTopology::LineList);
  732. dynamicDraw->SetDepthState(m_renderState.m_depthState);
  733. dynamicDraw->SetTarget0BlendState(m_renderState.m_blendState);
  734. dynamicDraw->DrawLinear(vertices, NUM_VERTS, drawSrg);
  735. }
  736. void CDraw2d::DeferredRectOutline::Draw(AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
  737. const Draw2dShaderData& shaderData,
  738. AZ::RPI::ViewportContextPtr viewportContext) const
  739. {
  740. // Create the 8 verts in the right vertex format for the dynamic draw context
  741. Draw2dVertex vertices[NUM_VERTS];
  742. const float z = 1.0f; // depth test disabled, if writing Z this will write at far plane
  743. uint32 packedColor = PackARGB8888(m_color);
  744. for (int i = 0; i < NUM_VERTS; ++i)
  745. {
  746. vertices[i].xyz = Vec3(m_verts2d[i].GetX(), m_verts2d[i].GetY(), z);
  747. vertices[i].color.dcolor = packedColor;
  748. vertices[i].st = Vec2(m_uvs[i].GetX(), m_uvs[i].GetY());
  749. }
  750. // The indices are for four quads (one for each side of the rect).
  751. // The quads are drawn using a triangle list (simpler than a tri-strip)
  752. // We draw each quad in the same order that the image component draws quads to
  753. // maximize chances of things lining up so each quad is drawn as two triangles:
  754. // top-left, top-right, bottom-left / bottom-left, top-right, bottom-right
  755. // e.g. for a quad like this:
  756. //
  757. // 0 1
  758. // |/|
  759. // 2 3
  760. //
  761. // The two triangles would be 0,1,2 and 2,1,3
  762. //
  763. static constexpr int32 NUM_INDICES = 24;
  764. uint16 indices[NUM_INDICES] =
  765. {
  766. 0, 1, 4, 4, 1, 5, // top quad
  767. 6, 7, 2, 2, 7, 3, // bottom quad
  768. 0, 4, 2, 2, 4, 6, // left quad
  769. 5, 1, 7, 1, 7, 3, // right quad
  770. };
  771. // Set up per draw SRG
  772. AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
  773. // Set texture
  774. const auto* imageView = m_image ? m_image->GetImageView() : nullptr;
  775. if (!imageView)
  776. {
  777. // Default to white texture
  778. auto image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
  779. imageView = image->GetImageView();
  780. }
  781. if (imageView)
  782. {
  783. drawSrg->SetImageView(shaderData.m_imageInputIndex, imageView, 0);
  784. }
  785. // Set projection matrix
  786. auto windowContext = viewportContext->GetWindowContext();
  787. const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
  788. const float viewX = viewport.m_minX;
  789. const float viewY = viewport.m_minY;
  790. const float viewWidth = viewport.m_maxX - viewport.m_minX;
  791. const float viewHeight = viewport.m_maxY - viewport.m_minY;
  792. const float zf = viewport.m_minZ;
  793. const float zn = viewport.m_maxZ;
  794. AZ::Matrix4x4 modelViewProjMat;
  795. AZ::MakeOrthographicMatrixRH(modelViewProjMat, viewX, viewX + viewWidth, viewY + viewHeight, viewY, zn, zf);
  796. drawSrg->SetConstant(shaderData.m_viewProjInputIndex, modelViewProjMat);
  797. drawSrg->Compile();
  798. // Add the primitive to the dynamic draw context for drawing
  799. dynamicDraw->SetPrimitiveType(AZ::RHI::PrimitiveTopology::TriangleList);
  800. dynamicDraw->DrawIndexed(vertices, NUM_VERTS, indices, NUM_INDICES, AZ::RHI::IndexFormat::Uint16, drawSrg);
  801. }
  802. ////////////////////////////////////////////////////////////////////////////////////////////////////
  803. // CDraw2d::DeferredText
  804. ////////////////////////////////////////////////////////////////////////////////////////////////////
  805. ////////////////////////////////////////////////////////////////////////////////////////////////////
  806. void CDraw2d::DeferredText::Draw([[maybe_unused]] AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
  807. [[maybe_unused]] const Draw2dShaderData& shaderData,
  808. [[maybe_unused]] AZ::RPI::ViewportContextPtr viewportContext) const
  809. {
  810. AzFramework::FontDrawInterface* fontDrawInterface = nullptr;
  811. AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get();
  812. if (fontQueryInterface)
  813. {
  814. fontDrawInterface = fontQueryInterface->GetFontDrawInterface(m_fontId);
  815. if (fontDrawInterface)
  816. {
  817. fontDrawInterface->DrawScreenAlignedText2d(m_drawParameters, m_string.c_str());
  818. }
  819. }
  820. }