Skip to content

Commit 8de2e73

Browse files
committed
[ECSCircuit] Add more paint options
1 parent 75d2ee0 commit 8de2e73

11 files changed

Lines changed: 403 additions & 22 deletions

File tree

Engine/Engine/Shaders/Source/Utility/2DSDF.hlsli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ float SDF_Rectangle(float2 pixelPos, float2 halfSize, float rounding)
1818

1919
float SDF_Line(float2 pixelPos, float2 posA, float2 posB, float radius)
2020
{
21-
float2 ba = posA - posB;
21+
float2 ba = posB - posA;
2222
float2 pa = pixelPos - posA;
2323

2424
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.f, 1.f);

Engine/Source/ECSCircuit/Private/ECSCircuit/Core/Painter.cpp

Lines changed: 265 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@
22

33
#include "ECSCircuit/Core/Painter.h"
44

5+
#include <RHIModule/RHIFeatures.h>
6+
#include <RHIModule/Images/ImageView.h>
7+
8+
#include <CoreUtilities/VoltAssert.h>
9+
510
namespace UI
611
{
7-
void Painter::AddRect(float x, float y, float width, float height, Color color, float rounding, RectCorners roundingCorners)
12+
void Painter::AddRect(float x, float y, float width, float height, Color color, float rounding, RectCorners roundingCorners, float rotation, float scale)
813
{
914
VT_UNUSED(roundingCorners); // TODO: per-corner rounding
1015

1116
DrawCommand command = DrawCommand::Initialize();
1217
command.type = PrimitiveType::Rect;
1318

14-
command.rotation = 0.f;
15-
command.scale = 1.f;
19+
command.rotation = rotation;
20+
command.scale = scale;
1621

1722
command.position = { x, y };
1823
command.halfSize = { width * 0.5f, height * 0.5f };
@@ -32,8 +37,263 @@ namespace UI
3237
m_commands.push_back(command);
3338
}
3439

35-
void Painter::AddRect(const Volt::Rect& rect, Color color, float rounding, RectCorners roundingCorners)
40+
void Painter::AddRect(const Volt::Rect& rect, Color color, float rounding, RectCorners roundingCorners, float rotation, float scale)
41+
{
42+
AddRect(rect.GetPosition().x, rect.GetPosition().y, rect.GetSize().x, rect.GetSize().y, color, rounding, roundingCorners, rotation, scale);
43+
}
44+
45+
void Painter::AddRectOutline(float x, float y, float width, float height, Color color, float lineThickness, float rounding, RectCorners roundingCorners, float rotation, float scale)
46+
{
47+
VT_UNUSED(roundingCorners); // TODO: per-corner rounding
48+
49+
DrawCommand command = DrawCommand::Initialize();
50+
command.type = PrimitiveType::Rect;
51+
52+
command.rotation = rotation;
53+
command.scale = scale;
54+
55+
command.position = { x, y };
56+
command.halfSize = { width * 0.5f, height * 0.5f };
57+
command.rounding = rounding;
58+
59+
// position stores the rect centre; radiusInner repurposed as outline thickness
60+
command.position += command.halfSize;
61+
command.radiusInner = lineThickness;
62+
63+
command.bounds =
64+
{
65+
command.position - command.halfSize,
66+
command.position + command.halfSize
67+
};
68+
69+
command.color = color;
70+
71+
m_commands.push_back(command);
72+
}
73+
74+
void Painter::AddRectOutline(const Volt::Rect& rect, Color color, float lineThickness, float rounding, RectCorners roundingCorners, float rotation, float scale)
75+
{
76+
AddRectOutline(rect.GetPosition().x, rect.GetPosition().y, rect.GetSize().x, rect.GetSize().y, color, lineThickness, rounding, roundingCorners, rotation, scale);
77+
}
78+
79+
void Painter::AddCircle(float x, float y, float radius, Color color, float scale)
80+
{
81+
DrawCommand command = DrawCommand::Initialize();
82+
command.type = PrimitiveType::Circle;
83+
84+
command.rotation = 0.f;
85+
command.scale = scale;
86+
87+
command.position = { x, y };
88+
command.radius = radius;
89+
90+
command.bounds =
91+
{
92+
command.position - radius,
93+
command.position + radius
94+
};
95+
96+
command.color = color;
97+
98+
m_commands.push_back(command);
99+
}
100+
101+
void Painter::AddCircleSegment(float x, float y, float innerRadius, float outerRadius, float angleDegrees, Color color, float scale)
102+
{
103+
DrawCommand command = DrawCommand::Initialize();
104+
command.type = PrimitiveType::CircleSegment;
105+
106+
command.rotation = 0.f;
107+
command.scale = scale;
108+
109+
command.position = { x, y };
110+
command.radius = outerRadius;
111+
command.radiusInner = innerRadius;
112+
// shader expects the half-angle in radians
113+
command.angle = glm::radians(angleDegrees) * 0.5f;
114+
115+
command.bounds =
116+
{
117+
command.position - outerRadius,
118+
command.position + outerRadius
119+
};
120+
121+
command.color = color;
122+
123+
m_commands.push_back(command);
124+
}
125+
126+
void Painter::AddLine(float x0, float y0, float x1, float y1, float radius, Color color)
36127
{
37-
AddRect(rect.GetPosition().x, rect.GetPosition().y, rect.GetSize().x, rect.GetSize().y, color, rounding, roundingCorners);
128+
DrawCommand command = DrawCommand::Initialize();
129+
command.type = PrimitiveType::Line;
130+
131+
command.rotation = 0.f;
132+
command.scale = 1.f;
133+
134+
command.radius = radius;
135+
command.lineA = { x0, y0 };
136+
command.lineB = { x1, y1 };
137+
command.position = (command.lineA + command.lineB) * 0.5f;
138+
139+
command.bounds =
140+
{
141+
glm::min(command.lineA, command.lineB) - radius,
142+
glm::max(command.lineA, command.lineB) + radius
143+
};
144+
145+
command.color = color;
146+
147+
m_commands.push_back(command);
148+
}
149+
150+
void Painter::AddText(float x, float y, const String& text, AssetReference<Volt::FontAsset> font, float maxWidth, Color color, float scale)
151+
{
152+
VT_UNUSED(maxWidth); // TODO: word wrap (old Circuit computed line splits but never applied them)
153+
154+
using namespace Volt;
155+
156+
if (text.empty())
157+
{
158+
return;
159+
}
160+
161+
U32String utf32string(U32String::CtorConvert(), text);
162+
163+
const FontMetrics& fontMetrics = font->GetMetrics();
164+
const FontGeometry& fontGeometry = font->GetGeometry();
165+
166+
IntRef<RHI::Image> atlas = font->GetAtlas();
167+
const uint32_t atlasTextureIndex = ResolveTextureIndex(atlas);
168+
169+
double sX = 0.0;
170+
double fsScale = 1.0 / (fontMetrics.ascenderY - fontMetrics.descenderY);
171+
double sY = -fsScale * fontMetrics.ascenderY;
172+
173+
for (int32_t i = 0; i < static_cast<int32_t>(utf32string.size()); i++)
174+
{
175+
char32_t character = utf32string[i];
176+
if (character == '\n')
177+
{
178+
sX = 0.0;
179+
sY += fsScale * fontMetrics.lineHeight;
180+
continue;
181+
}
182+
183+
const GlyphGeometry* glyph = fontGeometry.GetGlyph(character);
184+
if (!glyph)
185+
{
186+
glyph = fontGeometry.GetGlyph('?');
187+
}
188+
189+
VT_ENSURE(glyph);
190+
191+
GlyphGeometry::Bounds atlasBounds = glyph->GetAtlasBounds();
192+
GlyphGeometry::Bounds planeBounds = glyph->GetPlaneBounds();
193+
194+
planeBounds.top = fontMetrics.ascenderY - planeBounds.top;
195+
planeBounds.bottom = fontMetrics.ascenderY - planeBounds.bottom;
196+
197+
planeBounds.top += fontMetrics.ascenderY + fontMetrics.descenderY;
198+
planeBounds.bottom += fontMetrics.ascenderY + fontMetrics.descenderY;
199+
200+
planeBounds.left *= fsScale;
201+
planeBounds.bottom *= fsScale;
202+
planeBounds.right *= fsScale;
203+
planeBounds.top *= fsScale;
204+
205+
planeBounds.left += sX;
206+
planeBounds.bottom += sY;
207+
planeBounds.right += sX;
208+
planeBounds.top += sY;
209+
210+
double texelWidth = 1.0 / atlas->GetWidth();
211+
double texelHeight = 1.0 / atlas->GetHeight();
212+
213+
atlasBounds.left *= texelWidth;
214+
atlasBounds.bottom *= texelHeight;
215+
atlasBounds.right *= texelWidth;
216+
atlasBounds.top *= texelHeight;
217+
218+
DrawCommand command = DrawCommand::Initialize();
219+
command.type = PrimitiveType::TextCharacter;
220+
command.position = { x, y };
221+
command.color = color;
222+
command.scale = scale;
223+
224+
command.textureIndex = atlasTextureIndex;
225+
command.dimensions = { atlas->GetWidth(), atlas->GetHeight() };
226+
227+
command.minMaxPx.x = static_cast<float>(planeBounds.left) * scale + x;
228+
command.minMaxPx.y = static_cast<float>(planeBounds.bottom) * scale + y;
229+
command.minMaxPx.z = static_cast<float>(planeBounds.right) * scale + x;
230+
command.minMaxPx.w = static_cast<float>(planeBounds.top) * scale + y;
231+
232+
command.minMaxUV.x = static_cast<float>(atlasBounds.left);
233+
command.minMaxUV.y = static_cast<float>(atlasBounds.bottom);
234+
command.minMaxUV.z = static_cast<float>(atlasBounds.right);
235+
command.minMaxUV.w = static_cast<float>(atlasBounds.top);
236+
237+
command.bounds =
238+
{
239+
command.minMaxPx.x, command.minMaxPx.w,
240+
command.minMaxPx.z, command.minMaxPx.y
241+
};
242+
243+
m_commands.push_back(command);
244+
245+
double advance = glyph->GetAdvance();
246+
fontGeometry.GetAdvance(advance, character, utf32string[i + 1]);
247+
sX += fsScale * advance;
248+
}
249+
}
250+
251+
void Painter::AddImage(float x, float y, float width, float height, IntRef<Volt::RHI::Image> image, float scale)
252+
{
253+
AddImage(x, y, width, height, image, 0.f, 0.f, 1.f, 1.f, scale);
254+
}
255+
256+
void Painter::AddImage(float x, float y, float width, float height, IntRef<Volt::RHI::Image> image, float uv0x, float uv0y, float uv1x, float uv1y, float scale)
257+
{
258+
DrawCommand command = DrawCommand::Initialize();
259+
command.type = PrimitiveType::Image;
260+
261+
command.rotation = 0.f;
262+
command.scale = scale;
263+
264+
command.position = { x, y };
265+
266+
command.minMaxPx.x = x;
267+
command.minMaxPx.y = y;
268+
command.minMaxPx.z = x + width;
269+
command.minMaxPx.w = y + height;
270+
271+
command.minMaxUV.x = uv0x;
272+
command.minMaxUV.y = uv0y;
273+
command.minMaxUV.z = uv1x;
274+
command.minMaxUV.w = uv1y;
275+
276+
command.bounds = command.minMaxPx;
277+
278+
command.textureIndex = ResolveTextureIndex(image);
279+
command.dimensions = { image->GetWidth(), image->GetHeight() };
280+
281+
m_commands.push_back(command);
282+
}
283+
284+
void Painter::AddImage(const Volt::Rect& rect, IntRef<Volt::RHI::Image> image, float scale)
285+
{
286+
AddImage(rect.GetPosition().x, rect.GetPosition().y, rect.GetSize().x, rect.GetSize().y, image, scale);
287+
}
288+
289+
uint32_t Painter::ResolveTextureIndex(const IntRef<Volt::RHI::Image>& image)
290+
{
291+
if (Volt::RHI::RHICanUseBindless())
292+
{
293+
return image->GetView()->GetSRVBindlessIndex().Get();
294+
}
295+
296+
VT_ENSURE(m_resourceTable);
297+
return m_resourceTable->GetOrAddTextureSlotIndex(image);
38298
}
39299
}

Engine/Source/ECSCircuit/Private/ECSCircuit/Window/CircuitWindow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ namespace UI
184184
});
185185
registry.sort<PainterC, RelationshipC>();
186186

187-
Painter painter;
187+
Painter painter(m_resourceTable);
188188
registry.view<PainterC>().each([&](entt::entity entity, PainterC& painterC)
189189
{
190190
if (!painterC.paint)

Engine/Source/ECSCircuit/Public/ECSCircuit/Core/Painter.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@
44
#include "ECSCircuit/Core/DrawCommand.h"
55
#include "ECSCircuit/Core/RectCorners.h"
66

7+
#include <Volt-Assets/FontAsset.h>
8+
9+
#include <RHIModule/Descriptors/ResourceTable.h>
10+
#include <RHIModule/Images/Image.h>
11+
12+
#include <AssetSystem/AssetReference.h>
13+
714
#include <CoreUtilities/Math/2DShapes/Rect.h>
815
#include <CoreUtilities/Containers/Vector.h>
16+
#include <CoreUtilities/String/VoltString.h>
917

1018
namespace UI
1119
{
@@ -14,13 +22,34 @@ namespace UI
1422
class Painter
1523
{
1624
public:
17-
ECSCIRCUIT_API void AddRect(float x, float y, float width, float height, Color color, float rounding = 0, RectCorners roundingCorners = RectCorners::All);
18-
ECSCIRCUIT_API void AddRect(const Volt::Rect& rect, Color color, float rounding = 0, RectCorners roundingCorners = RectCorners::All);
25+
explicit Painter(IntRef<Volt::RHI::ResourceTable> resourceTable)
26+
: m_resourceTable(resourceTable)
27+
{
28+
}
29+
30+
ECSCIRCUIT_API void AddRect(float x, float y, float width, float height, Color color, float rounding = 0, RectCorners roundingCorners = RectCorners::All, float rotation = 0, float scale = 1);
31+
ECSCIRCUIT_API void AddRect(const Volt::Rect& rect, Color color, float rounding = 0, RectCorners roundingCorners = RectCorners::All, float rotation = 0, float scale = 1);
32+
33+
ECSCIRCUIT_API void AddRectOutline(float x, float y, float width, float height, Color color, float lineThickness, float rounding = 0, RectCorners roundingCorners = RectCorners::All, float rotation = 0, float scale = 1);
34+
ECSCIRCUIT_API void AddRectOutline(const Volt::Rect& rect, Color color, float lineThickness, float rounding = 0, RectCorners roundingCorners = RectCorners::All, float rotation = 0, float scale = 1);
35+
36+
ECSCIRCUIT_API void AddCircle(float x, float y, float radius, Color color, float scale = 1);
37+
ECSCIRCUIT_API void AddCircleSegment(float x, float y, float innerRadius, float outerRadius, float angleDegrees, Color color, float scale = 1);
38+
ECSCIRCUIT_API void AddLine(float x0, float y0, float x1, float y1, float radius, Color color);
39+
40+
ECSCIRCUIT_API void AddText(float x, float y, const String& text, AssetReference<Volt::FontAsset> font, float maxWidth, Color color, float scale = 1.f);
41+
42+
ECSCIRCUIT_API void AddImage(float x, float y, float width, float height, IntRef<Volt::RHI::Image> image, float scale = 1.f);
43+
ECSCIRCUIT_API void AddImage(float x, float y, float width, float height, IntRef<Volt::RHI::Image> image, float uv0x, float uv0y, float uv1x, float uv1y, float scale = 1.f);
44+
ECSCIRCUIT_API void AddImage(const Volt::Rect& rect, IntRef<Volt::RHI::Image> image, float scale = 1.f);
1945

2046
const Vector<DrawCommand>& GetCommands() const { return m_commands; }
2147
void Clear() { m_commands.clear(); }
2248

2349
private:
50+
uint32_t ResolveTextureIndex(const IntRef<Volt::RHI::Image>& image);
51+
2452
Vector<DrawCommand> m_commands;
53+
IntRef<Volt::RHI::ResourceTable> m_resourceTable;
2554
};
2655
}

Engine/Source/ECSCircuit/Public/ECSCircuit/Core/UIScene.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
#include "ECSCircuit/Core/Reflection.h"
55
#include "ECSCircuit/Core/Widget.h"
66

7+
#include "ECSCircuit/Config.h"
8+
79
#include <entt.hpp>
810

911
namespace UI
1012
{
11-
class UIScene
13+
class ECSCIRCUIT_API UIScene
1214
{
1315
public:
1416

0 commit comments

Comments
 (0)