3
0

Draw2d.cpp 38 KB

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