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+
510namespace 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}
0 commit comments