123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "Draw2d.h"
- #include <LyShine/UiRenderFormats.h>
- #include "LyShinePassDataBus.h"
- #include <AzCore/Math/Matrix3x3.h>
- #include <AzCore/Math/MatrixUtils.h>
- #include <AzFramework/Font/FontInterface.h>
- #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
- #include <Atom/RPI.Public/RPISystemInterface.h>
- #include <Atom/RHI/RHISystemInterface.h>
- #include <Atom/RPI.Public/Pass/RasterPass.h>
- #include <Atom/RPI.Public/Shader/Shader.h>
- #include <Atom/RPI.Public/Image/StreamingImage.h>
- #include <Atom/RPI.Public/RPIUtils.h>
- #include <Atom/RPI.Public/ViewportContextBus.h>
- namespace
- {
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // Color to u32 => 0xAARRGGBB
- AZ::u32 PackARGB8888(const AZ::Color& color)
- {
- return (color.GetA8() << 24) | (color.GetR8() << 16) | (color.GetG8() << 8) | color.GetB8();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // Vertex format for Dynamic Draw Context
- struct Draw2dVertex
- {
- Vec3 xyz;
- LyShine::UCol color;
- Vec2 st;
- };
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // PUBLIC MEMBER FUNCTIONS
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- CDraw2d::CDraw2d(AZ::RPI::ViewportContextPtr viewportContext)
- : m_deferCalls(false)
- , m_viewportContext(viewportContext)
- {
- // These default options are set here and never change. They are stored so that if a null options
- // structure is passed into the draw functions then this default one can be used instead
- m_defaultTextOptions.fontName = "default";
- m_defaultTextOptions.effectIndex = 0;
- m_defaultTextOptions.color.Set(1.0f, 1.0f, 1.0f);
- m_defaultTextOptions.horizontalAlignment = HAlign::Left;
- m_defaultTextOptions.verticalAlignment = VAlign::Top;
- m_defaultTextOptions.dropShadowOffset.Set(0.0f, 0.0f);
- m_defaultTextOptions.dropShadowColor.Set(0.0f, 0.0f, 0.0f, 0.0f);
- m_defaultTextOptions.rotation = 0.0f;
- m_defaultTextOptions.depthTestEnabled = false;
- AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- CDraw2d::~CDraw2d()
- {
- AZ::Render::Bootstrap::NotificationBus::Handler::BusDisconnect();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene)
- {
- // At this point the RPI is ready for use
- // Load the shader to be used for 2d drawing
- const char* shaderFilepath = "Shaders/SimpleTextured.azshader";
- AZ::Data::Instance<AZ::RPI::Shader> shader = AZ::RPI::LoadCriticalShader(shaderFilepath);
- // Set scene to be associated with the dynamic draw context
- AZ::RPI::Scene* scene = nullptr;
- if (m_viewportContext)
- {
- // Use scene associated with the specified viewport context
- scene = m_viewportContext->GetRenderScene().get();
- }
- else
- {
- // No viewport context specified, use main scene
- scene = bootstrapScene;
- }
- AZ_Assert(scene != nullptr, "Attempting to create a DynamicDrawContext for a viewport context that has not been associated with a scene yet.");
- // Create and initialize a DynamicDrawContext for 2d drawing
- // Get the pass for the dynamic draw context to render to
- AZ::RPI::RasterPass* uiCanvasPass = nullptr;
- AZ::RPI::SceneId sceneId = scene->GetId();
- LyShinePassRequestBus::EventResult(uiCanvasPass, sceneId, &LyShinePassRequestBus::Events::GetUiCanvasPass);
- if (!uiCanvasPass)
- {
- AZ_Error("Draw2d", false, "No UiCanvasPass found in Render-Pipeline.");
- return;
- }
- m_dynamicDraw = AZ::RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext();
- m_dynamicDraw->InitShader(shader);
- m_dynamicDraw->InitVertexFormat(
- { {"POSITION", AZ::RHI::Format::R32G32B32_FLOAT},
- {"COLOR", AZ::RHI::Format::B8G8R8A8_UNORM},
- {"TEXCOORD0", AZ::RHI::Format::R32G32_FLOAT} });
- m_dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::PrimitiveType
- | AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode
- | AZ::RPI::DynamicDrawContext::DrawStateOptions::DepthState
- | AZ::RPI::DynamicDrawContext::DrawStateOptions::ShaderVariant);
- // Use scene as output scope (will render to the UiCanvas child pass of the LyShine pass)
- m_dynamicDraw->SetOutputScope(scene);
- m_dynamicDraw->InitDrawListTag(uiCanvasPass->GetDrawListTag());
- m_dynamicDraw->EndInit();
- // Check that the dynamic draw context has been initialized appropriately
- if (m_dynamicDraw->IsReady())
- {
- // Cache draw srg input indices for later use
- static const char textureIndexName[] = "m_texture";
- static const char worldToProjIndexName[] = "m_worldToProj";
- AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = m_dynamicDraw->NewDrawSrg();
- if (drawSrg)
- {
- const AZ::RHI::ShaderResourceGroupLayout* layout = drawSrg->GetLayout();
- m_shaderData.m_imageInputIndex = layout->FindShaderInputImageIndex(AZ::Name(textureIndexName));
- AZ_Error("Draw2d", m_shaderData.m_imageInputIndex.IsValid(), "Failed to find shader input constant %s.",
- textureIndexName);
- m_shaderData.m_viewProjInputIndex = layout->FindShaderInputConstantIndex(AZ::Name(worldToProjIndexName));
- AZ_Error("Draw2d", m_shaderData.m_viewProjInputIndex.IsValid(), "Failed to find shader input constant %s.",
- worldToProjIndexName);
- }
- // Cache shader variants that will be used
- AZ::RPI::ShaderOptionList shaderOptionsClamp;
- shaderOptionsClamp.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true")));
- shaderOptionsClamp.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true")));
- m_shaderData.m_shaderOptionsClamp = m_dynamicDraw->UseShaderVariant(shaderOptionsClamp);
- AZ::RPI::ShaderOptionList shaderOptionsWrap;
- shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("false")));
- shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true")));
- m_shaderData.m_shaderOptionsWrap = m_dynamicDraw->UseShaderVariant(shaderOptionsWrap);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // Draw a textured quad with the top left corner at the given position.
- void CDraw2d::DrawImage(AZ::Data::Instance<AZ::RPI::Image> image, AZ::Vector2 position, AZ::Vector2 size, float opacity,
- float rotation, const AZ::Vector2* pivotPoint, const AZ::Vector2* minMaxTexCoords,
- ImageOptions* imageOptions)
- {
- ImageOptions* actualImageOptions = (imageOptions) ? imageOptions : &m_defaultImageOptions;
- AZ::Color color = AZ::Color::CreateFromVector3AndFloat(actualImageOptions->color, opacity);
- AZ::u32 packedColor = PackARGB8888(color);
- // Depending on the requested pixel rounding setting we may round position to an exact pixel
- AZ::Vector2 pos = Draw2dHelper::RoundXY(position, actualImageOptions->pixelRounding);
- // define quad (in clockwise order)
- DeferredQuad quad;
- quad.m_points[0].Set(pos.GetX(), pos.GetY());
- quad.m_points[1].Set(pos.GetX() + size.GetX(), pos.GetY());
- quad.m_points[2].Set(pos.GetX() + size.GetX(), pos.GetY() + size.GetY());
- quad.m_points[3].Set(pos.GetX(), pos.GetY() + size.GetY());
- quad.m_packedColors[0] = packedColor;
- quad.m_packedColors[1] = packedColor;
- quad.m_packedColors[2] = packedColor;
- quad.m_packedColors[3] = packedColor;
- if (minMaxTexCoords)
- {
- quad.m_texCoords[0] = AZ::Vector2(minMaxTexCoords[0].GetX(), minMaxTexCoords[0].GetY());
- quad.m_texCoords[1] = AZ::Vector2(minMaxTexCoords[1].GetX(), minMaxTexCoords[0].GetY());
- quad.m_texCoords[2] = AZ::Vector2(minMaxTexCoords[1].GetX(), minMaxTexCoords[1].GetY());
- quad.m_texCoords[3] = AZ::Vector2(minMaxTexCoords[0].GetX(), minMaxTexCoords[1].GetY());
- }
- else
- {
- quad.m_texCoords[0].Set(0.0f, 0.0f);
- quad.m_texCoords[1].Set(1.0f, 0.0f);
- quad.m_texCoords[2].Set(1.0f, 1.0f);
- quad.m_texCoords[3].Set(0.0f, 1.0f);
- }
- quad.m_image = image;
- quad.m_clamp = actualImageOptions->m_clamp;
- // add the blendMode flags to the base state
- quad.m_renderState = actualImageOptions->m_renderState;
- // apply rotation if requested
- if (rotation != 0.0f)
- {
- AZ::Vector2 pivot = (pivotPoint) ? *pivotPoint : quad.m_points[0];
- RotatePointsAboutPivot(quad.m_points, 4, pivot, rotation);
- }
- DrawOrDeferQuad(&quad);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DrawImageAligned(AZ::Data::Instance<AZ::RPI::Image> image, AZ::Vector2 position, AZ::Vector2 size,
- HAlign horizontalAlignment, VAlign verticalAlignment,
- float opacity, float rotation, const AZ::Vector2* minMaxTexCoords, ImageOptions* imageOptions)
- {
- AZ::Vector2 alignedPosition = Draw2dHelper::Align(position, size, horizontalAlignment, verticalAlignment);
- DrawImage(image, alignedPosition, size, opacity, rotation, &position, minMaxTexCoords, imageOptions);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DrawQuad(AZ::Data::Instance<AZ::RPI::Image> image, VertexPosColUV* verts, Rounding pixelRounding,
- bool clamp, const CDraw2d::RenderState& renderState)
- {
- // define quad
- DeferredQuad quad;
- for (int i = 0; i < 4; ++i)
- {
- quad.m_points[i] = Draw2dHelper::RoundXY(verts[i].position, pixelRounding);
- quad.m_texCoords[i] = verts[i].uv;
- quad.m_packedColors[i] = PackARGB8888(verts[i].color);
- }
- quad.m_image = image;
- quad.m_clamp = clamp;
- // add the blendMode flags to the base state
- quad.m_renderState = renderState;
- DrawOrDeferQuad(&quad);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DrawLine(AZ::Vector2 start, AZ::Vector2 end, AZ::Color color, Rounding pixelRounding,
- const CDraw2d::RenderState& renderState)
- {
- auto image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
- // define line
- uint32 packedColor = PackARGB8888(color);
- DeferredLine line;
- line.m_image = image;
- line.m_points[0] = Draw2dHelper::RoundXY(start, pixelRounding);
- line.m_points[1] = Draw2dHelper::RoundXY(end, pixelRounding);
- line.m_texCoords[0] = AZ::Vector2(0, 0);
- line.m_texCoords[1] = AZ::Vector2(1, 1);
- line.m_packedColors[0] = packedColor;
- line.m_packedColors[1] = packedColor;
- // add the blendMode flags to the base state
- line.m_renderState = renderState;
- DrawOrDeferLine(&line);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DrawLineTextured(AZ::Data::Instance<AZ::RPI::Image> image, VertexPosColUV* verts, Rounding pixelRounding,
- const CDraw2d::RenderState& renderState)
- {
- // define line
- DeferredLine line;
- line.m_image = image;
- for (int i = 0; i < 2; ++i)
- {
- line.m_points[i] = Draw2dHelper::RoundXY(verts[i].position, pixelRounding);
- line.m_texCoords[i] = verts[i].uv;
- line.m_packedColors[i] = PackARGB8888(verts[i].color);
- }
- // add the blendMode flags to the base state
- line.m_renderState = renderState;
- DrawOrDeferLine(&line);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // NOTE: The alignment does not take account of the drop shadow. i.e. the non-shadow text is centered
- // and the drop shadow text is offset from that.
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DrawText(const char* textString, AZ::Vector2 position, float pointSize, float opacity, TextOptions* textOptions)
- {
- TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions;
- AzFramework::FontId fontId = AzFramework::InvalidFontId;
- AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get();
- if (fontQueryInterface)
- {
- fontId = fontQueryInterface->GetFontId(actualTextOptions->fontName);
- }
- // render the drop shadow, if needed
- if ((actualTextOptions->dropShadowColor.GetA() > 0.0f) &&
- (actualTextOptions->dropShadowOffset.GetX() || actualTextOptions->dropShadowOffset.GetY()))
- {
- // calculate the drop shadow pos and render it
- AZ::Vector2 dropShadowPosition(position + actualTextOptions->dropShadowOffset);
- DrawTextInternal(textString, fontId, actualTextOptions->effectIndex,
- dropShadowPosition, pointSize, actualTextOptions->dropShadowColor,
- actualTextOptions->rotation,
- actualTextOptions->horizontalAlignment, actualTextOptions->verticalAlignment,
- actualTextOptions->depthTestEnabled);
- }
- // draw the text string
- AZ::Color textColor = AZ::Color::CreateFromVector3AndFloat(actualTextOptions->color, opacity);
- DrawTextInternal(textString, fontId, actualTextOptions->effectIndex,
- position, pointSize, textColor,
- actualTextOptions->rotation,
- actualTextOptions->horizontalAlignment, actualTextOptions->verticalAlignment,
- actualTextOptions->depthTestEnabled);
- }
- void CDraw2d::DrawRectOutlineTextured(AZ::Data::Instance<AZ::RPI::Image> image,
- UiTransformInterface::RectPoints points,
- AZ::Vector2 rightVec,
- AZ::Vector2 downVec,
- AZ::Color color,
- uint32_t lineThickness)
- {
- // since the rect can be transformed we have to add the offsets by multiplying them
- // by unit vectors parallel with the edges of the rect. However, the rect could be
- // zero width and/or height so we can't use "points" to compute these unit vectors.
- // So we instead get two transformed unit vectors and then normalize them
- rightVec.NormalizeSafe();
- downVec.NormalizeSafe();
- // calculate the transformed width and height of the rect
- // (in case it is smaller than the texture height)
- AZ::Vector2 widthVec = points.TopRight() - points.TopLeft();
- AZ::Vector2 heightVec = points.BottomLeft() - points.TopLeft();
- float rectWidth = widthVec.GetLength();
- float rectHeight = heightVec.GetLength();
- if (lineThickness == 0 && image)
- {
- lineThickness = image->GetDescriptor().m_size.m_height;
- }
- if (lineThickness == 0)
- {
- AZ_Assert(false, "Attempting to draw a rect outline with of zero thickness.");
- return;
- }
- // the outline is centered on the element rect so half the outline is outside
- // the rect and half is inside the rect
- float offset = aznumeric_cast<float>(lineThickness);
- float outerOffset = -offset * 0.5f;
- float innerOffset = offset * 0.5f;
- float outerV = 0.0f;
- float innerV = 1.0f;
- // if the rect is small there may not be space for the half of the outline that
- // is inside the rect. If this is the case reduce the innerOffset so the inner
- // points are coincident. Adjust the UVs according to keep a 1-1 texel to pixel ratio.
- float minDimension = min(rectWidth, rectHeight);
- if (innerOffset > minDimension * 0.5f)
- {
- float oldInnerOffset = innerOffset;
- innerOffset = minDimension * 0.5f;
- // note oldInnerOffset can't be zero because of early return if lineThickness is zero
- innerV = 0.5f + 0.5f * innerOffset / oldInnerOffset;
- }
- DeferredRectOutline rectOutline;
- // fill out the 8 verts to define the 2 rectangles - outer and inner
- // The vertices are in the order of outer rect then inner rect. e.g.:
- // 0 1
- // 4 5
- // 6 7
- // 2 3
- //
- // four verts of outer rect
- rectOutline.m_verts2d[0] = points.pt[0] + rightVec * outerOffset + downVec * outerOffset;
- rectOutline.m_verts2d[1] = points.pt[1] - rightVec * outerOffset + downVec * outerOffset;
- rectOutline.m_verts2d[2] = points.pt[3] + rightVec * outerOffset - downVec * outerOffset;
- rectOutline.m_verts2d[3] = points.pt[2] - rightVec * outerOffset - downVec * outerOffset;
- // four verts of inner rect
- rectOutline.m_verts2d[4] = points.pt[0] + rightVec * innerOffset + downVec * innerOffset;
- rectOutline.m_verts2d[5] = points.pt[1] - rightVec * innerOffset + downVec * innerOffset;
- rectOutline.m_verts2d[6] = points.pt[3] + rightVec * innerOffset - downVec * innerOffset;
- rectOutline.m_verts2d[7] = points.pt[2] - rightVec * innerOffset - downVec * innerOffset;
- // and define the UV coordinates for these 8 verts
- rectOutline.m_uvs[0] = AZ::Vector2(0.0f, outerV);
- rectOutline.m_uvs[1] = AZ::Vector2(1.0f, outerV);
- rectOutline.m_uvs[2] = AZ::Vector2(1.0f, outerV);
- rectOutline.m_uvs[3] = AZ::Vector2(0.0f, outerV);
- rectOutline.m_uvs[4] = AZ::Vector2(0.0f, innerV);
- rectOutline.m_uvs[5] = AZ::Vector2(1.0f, innerV);
- rectOutline.m_uvs[6] = AZ::Vector2(1.0f, innerV);
- rectOutline.m_uvs[7] = AZ::Vector2(0.0f, innerV);
- rectOutline.m_image = image;
- rectOutline.m_color = color;
- DrawOrDeferRectOutline(&rectOutline);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // TBD should the size include the offset of the drop shadow (if any)?
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Vector2 CDraw2d::GetTextSize(const char* textString, float pointSize, TextOptions* textOptions)
- {
- AzFramework::FontDrawInterface* fontDrawInterface = nullptr;
- AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get();
- if (fontQueryInterface)
- {
- TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions;
- AzFramework::FontId fontId = fontQueryInterface->GetFontId(actualTextOptions->fontName);
- fontDrawInterface = fontQueryInterface->GetFontDrawInterface(fontId);
- }
- if (!fontDrawInterface)
- {
- return AZ::Vector2(0.0f, 0.0f);
- }
- // Set up draw parameters
- AzFramework::TextDrawParameters drawParams;
- drawParams.m_drawViewportId = GetViewportContext()->GetId();
- drawParams.m_position = AZ::Vector3(0.0f, 0.0f, 1.0f);
- drawParams.m_effectIndex = 0;
- drawParams.m_textSizeFactor = pointSize;
- drawParams.m_scale = AZ::Vector2(1.0f, 1.0f);
- drawParams.m_lineSpacing = 1.0f;
- drawParams.m_monospace = false;
- drawParams.m_depthTest = false;
- drawParams.m_virtual800x600ScreenSize = false;
- drawParams.m_scaleWithWindow = false;
- drawParams.m_multiline = true;
- AZ::Vector2 textSize = fontDrawInterface->GetTextSize(drawParams, textString);
- return textSize;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float CDraw2d::GetViewportWidth() const
- {
- auto windowContext = GetViewportContext()->GetWindowContext();
- const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
- const float viewWidth = viewport.m_maxX - viewport.m_minX;
- return viewWidth;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float CDraw2d::GetViewportHeight() const
- {
- auto windowContext = GetViewportContext()->GetWindowContext();
- const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
- const float viewHeight = viewport.m_maxY - viewport.m_minY;
- return viewHeight;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float CDraw2d::GetViewportDpiScalingFactor() const
- {
- return GetViewportContext()->GetDpiScalingFactor();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- const CDraw2d::ImageOptions& CDraw2d::GetDefaultImageOptions() const
- {
- return m_defaultImageOptions;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- const CDraw2d::TextOptions& CDraw2d::GetDefaultTextOptions() const
- {
- return m_defaultTextOptions;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::RenderDeferredPrimitives()
- {
- // Draw and delete the deferred primitives
- AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext();
- for (auto primIter : m_deferredPrimitives)
- {
- primIter->Draw(m_dynamicDraw, m_shaderData, viewportContext);
- delete primIter;
- }
- // clear the list of deferred primitives
- m_deferredPrimitives.clear();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::SetDeferPrimitives(bool deferPrimitives)
- {
- m_deferCalls = deferPrimitives;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool CDraw2d::GetDeferPrimitives()
- {
- return m_deferCalls;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::SetSortKey(int64_t key)
- {
- m_dynamicDraw->SetSortKey(key);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // PUBLIC STATIC FUNCTIONS
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Data::Instance<AZ::RPI::Image> CDraw2d::LoadTexture(const AZStd::string& pathName)
- {
- // The file may not be in the AssetCatalog at this point if it is still processing or doesn't exist on disk.
- // Use GenerateAssetIdTEMP instead of GetAssetIdByPath so that it will return a valid AssetId anyways
- AZ::Data::AssetId streamingImageAssetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(
- streamingImageAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GenerateAssetIdTEMP,
- pathName.c_str());
- streamingImageAssetId.m_subId = AZ::RPI::StreamingImageAsset::GetImageAssetSubId();
- auto streamingImageAsset = AZ::Data::AssetManager::Instance().FindOrCreateAsset<AZ::RPI::StreamingImageAsset>(streamingImageAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
- AZ::Data::Instance<AZ::RPI::Image> image = AZ::RPI::StreamingImage::FindOrCreate(streamingImageAsset);
- if (!image)
- {
- AZ_Error("Draw2d", false, "Failed to find or create an image instance from image asset '%s'", pathName.c_str());
- }
- return image;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // PROTECTED MEMBER FUNCTIONS
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::RotatePointsAboutPivot(AZ::Vector2* points, [[maybe_unused]] int numPoints, AZ::Vector2 pivot, float angle) const
- {
- float angleRadians = DEG2RAD(angle);
- AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateRotationZ(angleRadians);
- for (int i = 0; i < 4; ++i)
- {
- AZ::Vector2 offset = points[i] - pivot;
- AZ::Vector3 offset3(offset.GetX(), offset.GetY(), 0.0f);
- offset3 = offset3 * rotationMatrix;
- offset.Set(offset3.GetX(), offset3.GetY());
- points[i] = pivot + offset;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DrawTextInternal(const char* textString, AzFramework::FontId fontId, unsigned int effectIndex,
- AZ::Vector2 position, float pointSize, AZ::Color color, float rotation,
- HAlign horizontalAlignment, VAlign verticalAlignment, bool depthTestEnabled)
- {
- // FFont.cpp uses the alpha value of the color to decide whether to use the color, if the alpha value is zero
- // (in a ColorB format) then the color set via SetColor is ignored and it usually ends up drawing with an alpha of 1.
- // This is not what we want so in this case do not draw at all.
- if (AZ::IsClose(color.GetA(), 0.0f))
- {
- return;
- }
- // Convert Draw2d alignment to text alignment
- AzFramework::TextHorizontalAlignment hAlignment = AzFramework::TextHorizontalAlignment::Left;
- switch (horizontalAlignment)
- {
- case HAlign::Left:
- hAlignment = AzFramework::TextHorizontalAlignment::Left;
- break;
- case HAlign::Center:
- hAlignment = AzFramework::TextHorizontalAlignment::Center;
- break;
- case HAlign::Right:
- hAlignment = AzFramework::TextHorizontalAlignment::Right;
- break;
- default:
- AZ_Assert(false, "Attempting to draw text with unsupported horizontal alignment.");
- break;
- }
- AzFramework::TextVerticalAlignment vAlignment = AzFramework::TextVerticalAlignment::Top;
- switch (verticalAlignment)
- {
- case VAlign::Top:
- vAlignment = AzFramework::TextVerticalAlignment::Top;
- break;
- case VAlign::Center:
- vAlignment = AzFramework::TextVerticalAlignment::Center;
- break;
- case VAlign::Bottom:
- vAlignment = AzFramework::TextVerticalAlignment::Bottom;
- break;
- default:
- AZ_Assert(false, "Attempting to draw text with unsupported vertical alignment.");
- break;
- }
- // Set up draw parameters for font interface
- AzFramework::TextDrawParameters drawParams;
- drawParams.m_drawViewportId = GetViewportContext()->GetId();
- drawParams.m_position = AZ::Vector3(position.GetX(), position.GetY(), 1.0f);
- drawParams.m_color = color;
- drawParams.m_effectIndex = effectIndex;
- drawParams.m_textSizeFactor = pointSize;
- drawParams.m_scale = AZ::Vector2(1.0f, 1.0f);
- drawParams.m_lineSpacing = 1.0f; //!< Spacing between new lines, as a percentage of m_scale.
- drawParams.m_hAlign = hAlignment;
- drawParams.m_vAlign = vAlignment;
- drawParams.m_monospace = false;
- drawParams.m_depthTest = depthTestEnabled;
- drawParams.m_virtual800x600ScreenSize = false;
- drawParams.m_scaleWithWindow = false;
- drawParams.m_multiline = true;
- if (rotation != 0.0f)
- {
- // rotate around the position (if aligned to center will rotate about center etc)
- float rotRad = DEG2RAD(rotation);
- AZ::Vector3 pivot(position.GetX(), position.GetY(), 0.0f);
- AZ::Matrix3x4 moveToPivotSpaceMat = AZ::Matrix3x4::CreateTranslation(-pivot);
- AZ::Matrix3x4 rotMat = AZ::Matrix3x4::CreateRotationZ(rotRad);
- AZ::Matrix3x4 moveFromPivotSpaceMat = AZ::Matrix3x4::CreateTranslation(pivot);
- drawParams.m_transform = moveFromPivotSpaceMat * rotMat * moveToPivotSpaceMat;
- drawParams.m_useTransform = true;
- }
- DeferredText newText;
- newText.m_drawParameters = drawParams;
- newText.m_fontId = fontId;
- newText.m_string = textString;
- DrawOrDeferTextString(&newText);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DrawOrDeferQuad(const DeferredQuad* quad)
- {
- if (m_deferCalls)
- {
- DeferredQuad* newQuad = new DeferredQuad;
- *newQuad = *quad;
- m_deferredPrimitives.push_back(newQuad);
- }
- else
- {
- quad->Draw(m_dynamicDraw, m_shaderData, GetViewportContext());
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DrawOrDeferLine(const DeferredLine* line)
- {
- if (m_deferCalls)
- {
- DeferredLine* newLine = new DeferredLine;
- *newLine = *line;
- m_deferredPrimitives.push_back(newLine);
- }
- else
- {
- line->Draw(m_dynamicDraw, m_shaderData, GetViewportContext());
- }
- }
- void CDraw2d::DrawOrDeferTextString(const DeferredText* text)
- {
- if (m_deferCalls)
- {
- DeferredText* newText = new DeferredText;
- *newText = *text;
- m_deferredPrimitives.push_back(newText);
- }
- else
- {
- text->Draw(m_dynamicDraw, m_shaderData, GetViewportContext());
- }
- }
- void CDraw2d::DrawOrDeferRectOutline(const DeferredRectOutline* rectOutline)
- {
- if (m_deferCalls)
- {
- DeferredRectOutline* newRectOutline = new DeferredRectOutline;
- *newRectOutline = *rectOutline;
- m_deferredPrimitives.push_back(newRectOutline);
- }
- else
- {
- rectOutline->Draw(m_dynamicDraw, m_shaderData, GetViewportContext());
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::RPI::ViewportContextPtr CDraw2d::GetViewportContext() const
- {
- if (!m_viewportContext)
- {
- // Return the default viewport context
- auto viewContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
- return viewContextManager->GetDefaultViewportContext();
- }
- // Return the user specified viewport context
- return m_viewportContext;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // CDraw2d::DeferredQuad
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DeferredQuad::Draw(AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
- const Draw2dShaderData& shaderData,
- AZ::RPI::ViewportContextPtr viewportContext) const
- {
- const int32 NUM_VERTS = 6;
- const float z = 1.0f; // depth test disabled, if writing Z this will write at far plane
- Draw2dVertex vertices[NUM_VERTS];
- const int vertIndex[NUM_VERTS] = {
- 0, 1, 3, 3, 1, 2
- };
- for (int i = 0; i < NUM_VERTS; ++i)
- {
- int j = vertIndex[i];
- vertices[i].xyz = Vec3(m_points[j].GetX(), m_points[j].GetY(), z);
- vertices[i].color.dcolor = m_packedColors[j];
- vertices[i].st = Vec2(m_texCoords[j].GetX(), m_texCoords[j].GetY());
- }
- dynamicDraw->SetShaderVariant(m_clamp ? shaderData.m_shaderOptionsClamp : shaderData.m_shaderOptionsWrap);
- // Set up per draw SRG
- AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
- // Set texture
- const auto* imageView = m_image ? m_image->GetImageView() : nullptr;
- if (!imageView)
- {
- // Default to white texture
- auto image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
- imageView = image->GetImageView();
- }
- if (imageView)
- {
- drawSrg->SetImageView(shaderData.m_imageInputIndex, imageView, 0);
- }
- // Set projection matrix
- auto windowContext = viewportContext->GetWindowContext();
- const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
- const float viewX = viewport.m_minX;
- const float viewY = viewport.m_minY;
- const float viewWidth = viewport.m_maxX - viewport.m_minX;
- const float viewHeight = viewport.m_maxY - viewport.m_minY;
- const float zf = viewport.m_minZ;
- const float zn = viewport.m_maxZ;
- AZ::Matrix4x4 modelViewProjMat;
- AZ::MakeOrthographicMatrixRH(modelViewProjMat, viewX, viewX + viewWidth, viewY + viewHeight, viewY, zn, zf);
- drawSrg->SetConstant(shaderData.m_viewProjInputIndex, modelViewProjMat);
- drawSrg->Compile();
- // Add the primitive to the dynamic draw context for drawing
- dynamicDraw->SetPrimitiveType(AZ::RHI::PrimitiveTopology::TriangleList);
- dynamicDraw->SetDepthState(m_renderState.m_depthState);
- dynamicDraw->SetTarget0BlendState(m_renderState.m_blendState);
- dynamicDraw->DrawLinear(vertices, NUM_VERTS, drawSrg);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // CDraw2d::DeferredLine
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DeferredLine::Draw(AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
- const Draw2dShaderData& shaderData,
- AZ::RPI::ViewportContextPtr viewportContext) const
- {
- const float z = 1.0f; // depth test disabled, if writing Z this will write at far plane
- const int32 NUM_VERTS = 2;
- Draw2dVertex vertices[NUM_VERTS];
- for (int i = 0; i < NUM_VERTS; ++i)
- {
- vertices[i].xyz = Vec3(m_points[i].GetX(), m_points[i].GetY(), z);
- vertices[i].color.dcolor = m_packedColors[i];
- vertices[i].st = Vec2(m_texCoords[i].GetX(), m_texCoords[i].GetY());
- }
- // Set up per draw SRG
- AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
- // Set texture
- const auto* imageView = m_image ? m_image->GetImageView() : nullptr;
- if (!imageView)
- {
- // Default to white texture
- auto image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
- imageView = image->GetImageView();
- }
- if (imageView)
- {
- drawSrg->SetImageView(shaderData.m_imageInputIndex, imageView, 0);
- }
- // Set projection matrix
- auto windowContext = viewportContext->GetWindowContext();
- const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
- const float viewX = viewport.m_minX;
- const float viewY = viewport.m_minY;
- const float viewWidth = viewport.m_maxX - viewport.m_minX;
- const float viewHeight = viewport.m_maxY - viewport.m_minY;
- const float zf = viewport.m_minZ;
- const float zn = viewport.m_maxZ;
- AZ::Matrix4x4 modelViewProjMat;
- AZ::MakeOrthographicMatrixRH(modelViewProjMat, viewX, viewX + viewWidth, viewY + viewHeight, viewY, zn, zf);
- drawSrg->SetConstant(shaderData.m_viewProjInputIndex, modelViewProjMat);
- drawSrg->Compile();
- // Add the primitive to the dynamic draw context for drawing
- dynamicDraw->SetPrimitiveType(AZ::RHI::PrimitiveTopology::LineList);
- dynamicDraw->SetDepthState(m_renderState.m_depthState);
- dynamicDraw->SetTarget0BlendState(m_renderState.m_blendState);
- dynamicDraw->DrawLinear(vertices, NUM_VERTS, drawSrg);
- }
- void CDraw2d::DeferredRectOutline::Draw(AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
- const Draw2dShaderData& shaderData,
- AZ::RPI::ViewportContextPtr viewportContext) const
- {
- // Create the 8 verts in the right vertex format for the dynamic draw context
- Draw2dVertex vertices[NUM_VERTS];
- const float z = 1.0f; // depth test disabled, if writing Z this will write at far plane
- uint32 packedColor = PackARGB8888(m_color);
- for (int i = 0; i < NUM_VERTS; ++i)
- {
- vertices[i].xyz = Vec3(m_verts2d[i].GetX(), m_verts2d[i].GetY(), z);
- vertices[i].color.dcolor = packedColor;
- vertices[i].st = Vec2(m_uvs[i].GetX(), m_uvs[i].GetY());
- }
- // The indices are for four quads (one for each side of the rect).
- // The quads are drawn using a triangle list (simpler than a tri-strip)
- // We draw each quad in the same order that the image component draws quads to
- // maximize chances of things lining up so each quad is drawn as two triangles:
- // top-left, top-right, bottom-left / bottom-left, top-right, bottom-right
- // e.g. for a quad like this:
- //
- // 0 1
- // |/|
- // 2 3
- //
- // The two triangles would be 0,1,2 and 2,1,3
- //
- static constexpr int32 NUM_INDICES = 24;
- uint16 indices[NUM_INDICES] =
- {
- 0, 1, 4, 4, 1, 5, // top quad
- 6, 7, 2, 2, 7, 3, // bottom quad
- 0, 4, 2, 2, 4, 6, // left quad
- 5, 1, 7, 1, 7, 3, // right quad
- };
- // Set up per draw SRG
- AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
- // Set texture
- const auto* imageView = m_image ? m_image->GetImageView() : nullptr;
- if (!imageView)
- {
- // Default to white texture
- auto image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
- imageView = image->GetImageView();
- }
- if (imageView)
- {
- drawSrg->SetImageView(shaderData.m_imageInputIndex, imageView, 0);
- }
- // Set projection matrix
- auto windowContext = viewportContext->GetWindowContext();
- const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
- const float viewX = viewport.m_minX;
- const float viewY = viewport.m_minY;
- const float viewWidth = viewport.m_maxX - viewport.m_minX;
- const float viewHeight = viewport.m_maxY - viewport.m_minY;
- const float zf = viewport.m_minZ;
- const float zn = viewport.m_maxZ;
- AZ::Matrix4x4 modelViewProjMat;
- AZ::MakeOrthographicMatrixRH(modelViewProjMat, viewX, viewX + viewWidth, viewY + viewHeight, viewY, zn, zf);
- drawSrg->SetConstant(shaderData.m_viewProjInputIndex, modelViewProjMat);
- drawSrg->Compile();
- // Add the primitive to the dynamic draw context for drawing
- dynamicDraw->SetPrimitiveType(AZ::RHI::PrimitiveTopology::TriangleList);
- dynamicDraw->DrawIndexed(vertices, NUM_VERTS, indices, NUM_INDICES, AZ::RHI::IndexFormat::Uint16, drawSrg);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // CDraw2d::DeferredText
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void CDraw2d::DeferredText::Draw([[maybe_unused]] AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
- [[maybe_unused]] const Draw2dShaderData& shaderData,
- [[maybe_unused]] AZ::RPI::ViewportContextPtr viewportContext) const
- {
- AzFramework::FontDrawInterface* fontDrawInterface = nullptr;
- AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get();
- if (fontQueryInterface)
- {
- fontDrawInterface = fontQueryInterface->GetFontDrawInterface(m_fontId);
- if (fontDrawInterface)
- {
- fontDrawInterface->DrawScreenAlignedText2d(m_drawParameters, m_string.c_str());
- }
- }
- }
|