This commit is contained in:
Jean-Baptiste Perrier
2021-02-18 19:35:21 +01:00
10 changed files with 503 additions and 54 deletions

View File

@@ -20,6 +20,8 @@
#include "Engine/Animations/AnimationUtils.h"
#include "Engine/Profiler/Profiler.h"
#include "Engine/Debug/DebugLog.h"
#include "Engine/Render2D/Render2D.h"
#include "Engine/Render2D/FontAsset.h"
// Debug draw service configuration
#define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024)
@@ -54,6 +56,25 @@ struct DebugTriangle
float TimeLeft;
};
struct DebugText2D
{
Array<Char, InlinedAllocation<64>> Text;
Vector2 Position;
int32 Size;
Color Color;
float TimeLeft;
};
struct DebugText3D
{
Array<Char, InlinedAllocation<64>> Text;
Transform Transform;
bool FaceCamera;
int32 Size;
Color Color;
float TimeLeft;
};
PACK_STRUCT(struct Vertex {
Vector3 Position;
Color32 Color;
@@ -133,10 +154,14 @@ struct DebugDrawData
Array<DebugTriangle> OneFrameTriangles;
Array<DebugTriangle> DefaultWireTriangles;
Array<DebugTriangle> OneFrameWireTriangles;
Array<DebugText2D> DefaultText2D;
Array<DebugText2D> OneFrameText2D;
Array<DebugText3D> DefaultText3D;
Array<DebugText3D> OneFrameText3D;
inline int32 Count() const
{
return LinesCount() + TrianglesCount();
return LinesCount() + TrianglesCount() + TextCount();
}
inline int32 LinesCount() const
@@ -149,6 +174,11 @@ struct DebugDrawData
return DefaultTriangles.Count() + OneFrameTriangles.Count() + DefaultWireTriangles.Count() + OneFrameWireTriangles.Count();
}
inline int32 TextCount() const
{
return DefaultText2D.Count() + OneFrameText2D.Count() + DefaultText3D.Count() + OneFrameText3D.Count();
}
inline void Add(const DebugLine& l)
{
if (l.TimeLeft > 0)
@@ -178,10 +208,14 @@ struct DebugDrawData
UpdateList(deltaTime, DefaultLines);
UpdateList(deltaTime, DefaultTriangles);
UpdateList(deltaTime, DefaultWireTriangles);
UpdateList(deltaTime, DefaultText2D);
UpdateList(deltaTime, DefaultText3D);
OneFrameLines.Clear();
OneFrameTriangles.Clear();
OneFrameWireTriangles.Clear();
OneFrameText2D.Clear();
OneFrameText3D.Clear();
}
inline void Clear()
@@ -192,6 +226,10 @@ struct DebugDrawData
OneFrameTriangles.Clear();
DefaultWireTriangles.Clear();
OneFrameWireTriangles.Clear();
DefaultText2D.Clear();
OneFrameText2D.Clear();
DefaultText3D.Clear();
OneFrameText3D.Clear();
}
inline void Release()
@@ -202,6 +240,10 @@ struct DebugDrawData
OneFrameTriangles.Resize(0);
DefaultWireTriangles.Resize(0);
OneFrameWireTriangles.Resize(0);
DefaultText2D.Resize(0);
OneFrameText2D.Resize(0);
DefaultText3D.Resize(0);
OneFrameText3D.Resize(0);
}
};
@@ -216,6 +258,7 @@ namespace
DebugDrawContext GlobalContext;
DebugDrawContext* Context;
AssetReference<Shader> DebugDrawShader;
AssetReference<FontAsset> DebugDrawFont;
PsData DebugDrawPsLinesDefault;
PsData DebugDrawPsLinesDepthTest;
PsData DebugDrawPsWireTrianglesDefault;
@@ -299,6 +342,21 @@ DebugDrawCall WriteLists(int32& vertexCounter, const Array<T>& listA, const Arra
return drawCall;
}
inline void DrawText3D(const DebugText3D& t, const RenderContext& renderContext, const Vector3& viewUp, const Matrix& f, const Matrix& vp, const Viewport& viewport, GPUContext* context, GPUTextureView* target, GPUTextureView* depthBuffer)
{
Matrix w, fw, m;
if (t.FaceCamera)
Matrix::CreateWorld(t.Transform.Translation, renderContext.View.Direction, viewUp, w);
else
t.Transform.GetWorld(w);
Matrix::Multiply(f, w, fw);
Matrix::Multiply(fw, vp, m);
Render2D::Begin(context, target, depthBuffer, viewport, m);
const StringView text(t.Text.Get(), t.Text.Count() - 1);
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, Vector2::Zero);
Render2D::End();
}
class DebugDrawService : public EngineService
{
public:
@@ -569,7 +627,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
return;
auto context = GPUDevice::Instance->GetMainContext();
// Fallback to task backbuffer
// Fallback to task buffers
if (target == nullptr && renderContext.Task)
target = renderContext.Task->GetOutputView();
@@ -601,8 +659,6 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
context->BindCB(0, cb);
auto vb = DebugDrawVB->GetBuffer();
#define DRAW(drawCall) if (drawCall.VertexCount)
// Draw with depth test
if (depthTestLines.VertexCount + depthTestTriangles.VertexCount + depthTestWireTriangles.VertexCount > 0)
{
@@ -673,7 +729,50 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
}
}
#undef DRAW
// Text
if (Context->DebugDrawDefault.TextCount() + Context->DebugDrawDepthTest.TextCount())
{
PROFILE_GPU_CPU_NAMED("Text");
auto features = Render2D::Features;
Render2D::Features = (Render2D::RenderingFeatures)((uint32)features & ~(uint32)Render2D::RenderingFeatures::VertexSnapping);
if (!DebugDrawFont)
DebugDrawFont = Content::LoadAsyncInternal<FontAsset>(TEXT("Editor/Fonts/Roboto-Regular"));
if (DebugDrawFont && DebugDrawFont->IsLoaded())
{
Viewport viewport = renderContext.Task->GetViewport();
if (Context->DebugDrawDefault.DefaultText2D.Count() + Context->DebugDrawDefault.OneFrameText2D.Count())
{
Render2D::Begin(context, target, nullptr, viewport);
for (auto& t : Context->DebugDrawDefault.DefaultText2D)
{
const StringView text(t.Text.Get(), t.Text.Count() - 1);
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, t.Position);
}
for (auto& t : Context->DebugDrawDefault.OneFrameText2D)
{
const StringView text(t.Text.Get(), t.Text.Count() - 1);
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, t.Position);
}
Render2D::End();
}
if (Context->DebugDrawDefault.DefaultText3D.Count() + Context->DebugDrawDefault.OneFrameText3D.Count())
{
Matrix f;
Matrix::RotationZ(PI, f);
Vector3 viewUp;
Vector3::Transform(Vector3::Up, Quaternion::LookRotation(renderContext.View.Direction, Vector3::Up), viewUp);
for (auto& t : Context->DebugDrawDefault.DefaultText3D)
DrawText3D(t, renderContext, viewUp, f, vp, viewport, context, target, nullptr);
for (auto& t : Context->DebugDrawDefault.OneFrameText3D)
DrawText3D(t, renderContext, viewUp, f, vp, viewport, context, target, nullptr);
}
}
Render2D::Features = features;
}
}
void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes)
@@ -1276,4 +1375,51 @@ void DebugDraw::DrawBox(const OrientedBoundingBox& box, const Color& color, floa
}
}
void DebugDraw::DrawText(const StringView& text, const Vector2& position, const Color& color, int32 size, float duration)
{
if (text.Length() == 0 || size < 4)
return;
Array<DebugText2D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText2D : &Context->DebugDrawDefault.OneFrameText2D;
auto& t = list->AddOne();
t.Text.Resize(text.Length() + 1);
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
t.Text[text.Length()] = 0;
t.Position = position;
t.Size = size;
t.Color = color;
t.TimeLeft = duration;
}
void DebugDraw::DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size, float duration)
{
if (text.Length() == 0 || size < 4)
return;
Array<DebugText3D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText3D : &Context->DebugDrawDefault.OneFrameText3D;
auto& t = list->AddOne();
t.Text.Resize(text.Length() + 1);
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
t.Text[text.Length()] = 0;
t.Transform = position;
t.FaceCamera = true;
t.Size = size;
t.Color = color;
t.TimeLeft = duration;
}
void DebugDraw::DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size, float duration)
{
if (text.Length() == 0 || size < 4)
return;
Array<DebugText3D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText3D : &Context->DebugDrawDefault.OneFrameText3D;
auto& t = list->AddOne();
t.Text.Resize(text.Length() + 1);
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
t.Text[text.Length()] = 0;
t.Transform = transform;
t.FaceCamera = false;
t.Size = size;
t.Color = color;
t.TimeLeft = duration;
}
#endif

View File

@@ -15,6 +15,7 @@ class GPUContext;
class RenderTask;
class SceneRenderTask;
class Actor;
struct Transform;
/// <summary>
/// The debug shapes rendering service. Not available in final game. For use only in the editor.
@@ -307,6 +308,36 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawBox(const OrientedBoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the text on a screen (2D).
/// </summary>
/// <param name="text">The text.</param>
/// <param name="position">The position of the text on the screen (in screen-space coordinates).</param>
/// <param name="color">The color.</param>
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Vector2& position, const Color& color, int32 size = 20, float duration = 0.0f);
/// <summary>
/// Draws the text (3D) that automatically faces the camera.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="position">The position of the text (world-space).</param>
/// <param name="color">The color.</param>
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size = 32, float duration = 0.0f);
/// <summary>
/// Draws the text (3D).
/// </summary>
/// <param name="text">The text.</param>
/// <param name="transform">The transformation of the text (world-space).</param>
/// <param name="color">The color.</param>
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size = 32, float duration = 0.0f);
};
#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest)
@@ -326,7 +357,8 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawWireSphere(sphere, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, length, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireArrow(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_TEXT(text, position, color, size, duration) DebugDraw::DrawText(text, position, color, size, duration)
#else
@@ -348,5 +380,6 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_TEXT(text, position, color, size, duration)
#endif

View File

@@ -270,6 +270,34 @@ FORCE_INLINE Render2DVertex MakeVertex(const Vector2& point, const Vector2& uv,
};
}
void WriteTri(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& uv0, const Vector2& uv1, const Vector2& uv2, const Color& color0, const Color& color1, const Color& color2)
{
Render2DVertex tris[3];
tris[0] = MakeVertex(p0, uv0, color0);
tris[1] = MakeVertex(p1, uv1, color1);
tris[2] = MakeVertex(p2, uv2, color2);
VB.Write(tris, sizeof(tris));
uint32 indices[3];
indices[0] = VBIndex + 0;
indices[1] = VBIndex + 1;
indices[2] = VBIndex + 2;
IB.Write(indices, sizeof(indices));
VBIndex += 3;
IBIndex += 3;
}
void WriteTri(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Color& color0, const Color& color1, const Color& color2)
{
WriteTri(p0, p1, p2, Vector2::Zero, Vector2::Zero, Vector2::Zero, color0, color1, color2);
}
void WriteTri(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& uv0, const Vector2& uv1, const Vector2& uv2)
{
WriteTri(p0, p1, p2, uv0, uv1, uv2, Color::Black, Color::Black, Color::Black);
}
void WriteRect(const Rectangle& rect, const Color& color1, const Color& color2, const Color& color3, const Color& color4)
{
const Vector2 uvUpperLeft = Vector2::Zero;
@@ -1716,3 +1744,45 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength)
drawCall.AsBlur.BottomRightY = p.Y;
WriteRect(rect, Color::White);
}
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Vector2>& vertices, const Span<Vector2>& uvs)
{
CHECK(vertices.Length() == uvs.Length())
RENDER2D_CHECK_RENDERING_STATE;
Render2DDrawCall& drawCall = DrawCalls.AddOne();
drawCall.Type = DrawCallType::FillTexture;
drawCall.StartIB = IBIndex;
drawCall.CountIB = vertices.Length();
drawCall.AsTexture.Ptr = t;
for (int32 i = 0; i < vertices.Length(); i += 3)
WriteTri(vertices[i], vertices[i + 1], vertices[i + 2], uvs[i], uvs[i + 1], uvs[i + 2]);
}
void Render2D::FillTriangles(const Span<Vector2>& vertices, const Span<Color>& colors, bool useAlpha)
{
CHECK(vertices.Length() == colors.Length());
RENDER2D_CHECK_RENDERING_STATE;
Render2DDrawCall& drawCall = DrawCalls.AddOne();
drawCall.Type = useAlpha ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
drawCall.StartIB = IBIndex;
drawCall.CountIB = vertices.Length();
for (int32 i = 0; i < vertices.Length(); i += 3)
WriteTri(vertices[i], vertices[i + 1], vertices[i + 2], colors[i], colors[i + 1], colors[i + 2]);
}
void Render2D::FillTriangle(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Color& color)
{
RENDER2D_CHECK_RENDERING_STATE;
Render2DDrawCall& drawCall = DrawCalls.AddOne();
drawCall.Type = color.A < 1.0f ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
drawCall.StartIB = IBIndex;
drawCall.CountIB = 3;
WriteTri(p0, p1, p2, color, color, color);
}

View File

@@ -4,6 +4,7 @@
#include "Engine/Core/Math/Color.h"
#include "Engine/Scripting/ScriptingType.h"
#include "Engine/Core/Types/Span.h"
struct SpriteHandle;
struct TextLayoutOptions;
@@ -339,4 +340,29 @@ public:
/// <param name="rect">The target rectangle to draw (blurs its background).</param>
/// <param name="blurStrength">The blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.</param>
API_FUNCTION() static void DrawBlur(const Rectangle& rect, float blurStrength);
/// <summary>
/// Draws vertices array.
/// </summary>
/// <param name="t">The texture.</param>
/// <param name="vertices">The vertices array.</param>
/// <param name="uvs">The uvs array.</param>
API_FUNCTION() static void DrawTexturedTriangles(GPUTexture* t, const Span<Vector2>& vertices, const Span<Vector2>& uvs);
/// <summary>
/// Draws vertices array.
/// </summary>
/// <param name="vertices">The vertices array.</param>
/// <param name="colors">The colors array.</param>
/// <param name="useAlpha">If true alpha blending will be enabled.</param>
API_FUNCTION() static void FillTriangles(const Span<Vector2>& vertices, const Span<Color>& colors, bool useAlpha);
/// <summary>
/// Fills a triangular area.
/// </summary>
/// <param name="p0">The first point.</param>
/// <param name="p1">The second point.</param>
/// <param name="p2">The third point.</param>
/// <param name="color">The color.</param>
API_FUNCTION() static void FillTriangle(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Color& color);
};