Skip to content

Commit d5dfbbf

Browse files
committed
Added rounded rect to the engine
1 parent 52ef169 commit d5dfbbf

8 files changed

Lines changed: 388 additions & 2 deletions

File tree

res/setupRenderer.pg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ loadShader("2DShapes", "shader/simpleshapes.vs", "shader/simpleshapes.fs");
1313
loadShader("progressBar", "shader/progressbar.vs", "shader/progressbar.fs");
1414
loadShader("lineShader", "shader/lineshader.vs", "shader/lineshader.fs");
1515
loadShader("polygonShader", "shader/polygonshader.vs", "shader/polygonshader.fs");
16+
loadShader("RoundedRect", "shader/roundedrect.vs", "shader/roundedrect.fs");
1617

1718
print("Shaders loaded successfully.")
1819

shader/roundedrect.fs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
out vec4 FragColor;
2+
3+
in vec4 ourColor;
4+
in vec2 fragUV;
5+
in vec2 fragSize;
6+
in float fragRadius;
7+
8+
// Signed distance function for a rounded rectangle.
9+
// p : fragment position relative to rect center (same units as halfSize/radius)
10+
// halfSize : half-extents of the rectangle
11+
// radius : corner radius
12+
float roundedBoxSDF(vec2 p, vec2 halfSize, float radius)
13+
{
14+
vec2 q = abs(p) - halfSize + radius;
15+
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
16+
}
17+
18+
void main()
19+
{
20+
// Map UV [0,1] to coordinates centered on the rectangle, in pixel units.
21+
vec2 p = (fragUV - 0.5) * fragSize;
22+
vec2 halfSize = fragSize * 0.5;
23+
24+
// Clamp radius so it never exceeds half the smallest dimension.
25+
float radius = min(fragRadius, min(halfSize.x, halfSize.y));
26+
27+
float d = roundedBoxSDF(p, halfSize, radius);
28+
29+
// Smooth the edge over ~1.5 pixels for anti-aliasing.
30+
float alpha = 1.0 - smoothstep(0.0, 1.5, d);
31+
32+
vec4 color = ourColor / 255.0;
33+
FragColor = vec4(color.rgb, color.a * alpha);
34+
}

shader/roundedrect.vs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
layout (location = 0) in vec3 aPos;
2+
layout (location = 1) in vec2 aTexCoord;
3+
4+
layout (location = 2) in vec3 aWorldPos;
5+
layout (location = 3) in vec2 aSize;
6+
layout (location = 4) in float rotation;
7+
layout (location = 5) in vec4 aColors;
8+
layout (location = 6) in float aRadius;
9+
10+
uniform mat4 model;
11+
uniform mat4 scale;
12+
uniform mat4 view;
13+
uniform mat4 projection;
14+
15+
uniform float sWidth;
16+
uniform float sHeight;
17+
18+
out vec4 ourColor;
19+
out vec2 fragUV;
20+
out vec2 fragSize;
21+
out float fragRadius;
22+
23+
float M_PI = 3.1415926535897932384626433832795;
24+
25+
float rad(float degree)
26+
{
27+
return degree * M_PI / 180.0f;
28+
}
29+
30+
void main()
31+
{
32+
mat4 posMat = mat4(
33+
vec4( 1.0, 0.0, 0.0, 0.0),
34+
vec4( 0.0, 1.0, 0.0, 0.0),
35+
vec4( 0.0, 0.0, 1.0, 0.0),
36+
vec4( -1.0f + 2.0f * aWorldPos.x * (1.0f / sWidth), 1.0f + 2.0f * -(aWorldPos.y) * (1.0f / sHeight), aWorldPos.z / 100.0f, 1.0f) );
37+
38+
mat4 scaleMat = mat4(
39+
vec4( aSize.x, 0.0f, 0.0f, 0.0f),
40+
vec4( 0.0f, aSize.y, 0.0f, 0.0f),
41+
vec4( 0.0f, 0.0f, 1.0f, 0.0f),
42+
vec4( 0.0f, 0.0f, 0.0f, 1.0f) );
43+
44+
float radRotation = rad(rotation);
45+
46+
mat4 rotateMat = mat4(
47+
vec4(cos(radRotation), -sin(radRotation), 0.0, 0.0),
48+
vec4(sin(radRotation), cos(radRotation), 0.0, 0.0),
49+
vec4(0.0, 0.0, 1.0, 0.0),
50+
vec4(0.0, 0.0, 0.0, 1.0));
51+
52+
vec2 centerNDC = vec2(0.5*aSize.x, -0.5*aSize.y);
53+
54+
mat4 Tneg = mat4(1.0);
55+
Tneg[3].xy = -centerNDC;
56+
57+
mat4 Tpos = mat4(1.0);
58+
Tpos[3].xy = centerNDC;
59+
60+
gl_Position = projection * posMat * view * scale * Tpos * rotateMat * Tneg * scaleMat * model * vec4(aPos.x, aPos.y, aPos.z, 1.0f);
61+
62+
ourColor = aColors;
63+
fragUV = aTexCoord;
64+
fragSize = aSize;
65+
fragRadius = aRadius;
66+
}

src/Editor/Gui/contextmenu.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ namespace editor
105105
"Add Button", makeCallable<CreateElement>(UiComponentType::BUTTON),
106106
"Add TextInput", makeCallable<CreateElement>(UiComponentType::TEXTINPUT),
107107
"Add List", makeCallable<CreateElement>(UiComponentType::LIST),
108-
"Add Prefab", makeCallable<CreateElement>(UiComponentType::PREFAB));
108+
// "Add Prefab", makeCallable<CreateElement>(UiComponentType::PREFAB),
109+
"Add RoundRect", makeCallable<CreateElement>(UiComponentType::ROUNDEDRECT));
109110

110111
hide();
111112
}
@@ -237,6 +238,20 @@ namespace editor
237238
break;
238239
}
239240

241+
case UiComponentType::ROUNDEDRECT:
242+
{
243+
LOG_INFO(DOM, "Create rounded rect at (" << cX << ", " << cY << ")");
244+
ecsRef->sendEvent(CreateInspectorEntityEvent{[cX, cY](EntitySystem* ecsRef) -> EntityRef {
245+
auto newElement = makeRoundedRect2DShape(ecsRef, 10, 50, 50);
246+
newElement.get<PositionComponent>()->setX(cX);
247+
newElement.get<PositionComponent>()->setY(cY);
248+
249+
return newElement;
250+
}});
251+
252+
break;
253+
}
254+
240255
default:
241256
break;
242257
}

src/Editor/Gui/contextmenu.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ namespace pg
2020
TTFTEXT,
2121
TEXTINPUT,
2222
LIST,
23-
PREFAB // Todo to implement !
23+
PREFAB, // Todo to implement !
24+
ROUNDEDRECT
2425
};
2526

2627
class Input;

src/Engine/2D/simple2dobject.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,172 @@ namespace pg
264264

265265
changed = true;
266266
}
267+
268+
// ---------------------------------------------------------------------------
269+
// RoundedRect2DObject serialize / deserialize
270+
// ---------------------------------------------------------------------------
271+
272+
template <>
273+
void serialize(Archive& archive, const RoundedRect2DObject& value)
274+
{
275+
LOG_THIS(DOM);
276+
277+
archive.startSerialization(RoundedRect2DObject::getType());
278+
279+
serialize(archive, "cornerRadius", value.cornerRadius);
280+
serialize(archive, "colors", value.colors);
281+
282+
archive.endSerialization();
283+
}
284+
285+
template <>
286+
RoundedRect2DObject deserialize(const UnserializedObject& serializedString)
287+
{
288+
LOG_THIS(DOM);
289+
290+
if (serializedString.isNull())
291+
{
292+
LOG_ERROR(DOM, "Element is null");
293+
return RoundedRect2DObject{};
294+
}
295+
296+
LOG_INFO(DOM, "Deserializing a RoundedRect2DObject");
297+
298+
auto cornerRadius = deserialize<float>(serializedString["cornerRadius"]);
299+
auto colors = deserialize<constant::Vector4D>(serializedString["colors"]);
300+
301+
return RoundedRect2DObject{cornerRadius, colors};
302+
}
303+
304+
// ---------------------------------------------------------------------------
305+
// RoundedRect2DObjectSystem
306+
// ---------------------------------------------------------------------------
307+
308+
void RoundedRect2DObjectSystem::init()
309+
{
310+
LOG_THIS_MEMBER(DOM);
311+
312+
Material mat;
313+
314+
mat.shader = masterRenderer->getShader("RoundedRect");
315+
316+
mat.nbTextures = 0;
317+
318+
mat.uniformMap.emplace("sWidth", "ScreenWidth");
319+
mat.uniformMap.emplace("sHeight", "ScreenHeight");
320+
321+
// Instance layout: worldPos(3), size(2), rotation(1), color(4), cornerRadius(1) = 11 floats
322+
mat.setSimpleMesh({3, 2, 1, 4, 1});
323+
324+
materialId = masterRenderer->registerMaterial(mat);
325+
326+
auto group = registerGroup<PositionComponent, RoundedRect2DObject>();
327+
328+
group->addOnGroup([this](EntityRef entity) {
329+
LOG_MILE("Rounded Rect 2D System", "Add entity " << entity->id << " to rounded rect group!");
330+
updateQueue.push(entity->id);
331+
changed = true;
332+
});
333+
334+
group->removeOfGroup([this](EntitySystem* ecsRef, _unique_id id) {
335+
LOG_MILE("Rounded Rect 2D System", "Remove entity " << id << " from rounded rect group!");
336+
auto entity = ecsRef->getEntity(id);
337+
ecsRef->detach<RoundedRect2DRenderCall>(entity);
338+
changed = true;
339+
});
340+
}
341+
342+
void RoundedRect2DObjectSystem::execute()
343+
{
344+
if (not changed)
345+
return;
346+
347+
while (not updateQueue.empty())
348+
{
349+
auto entityId = updateQueue.front();
350+
351+
auto entity = ecsRef->getEntity(entityId);
352+
353+
if (not entity)
354+
{
355+
updateQueue.pop();
356+
continue;
357+
}
358+
359+
auto ui = entity->get<PositionComponent>();
360+
auto obj = entity->get<RoundedRect2DObject>();
361+
362+
if (entity->has<RoundedRect2DRenderCall>())
363+
{
364+
entity->get<RoundedRect2DRenderCall>()->call = createRenderCall(ui, obj);
365+
}
366+
else
367+
{
368+
ecsRef->_attach<RoundedRect2DRenderCall>(entity, createRenderCall(ui, obj));
369+
}
370+
371+
updateQueue.pop();
372+
}
373+
374+
renderCallList.clear();
375+
376+
const auto& renderCallView = view<RoundedRect2DRenderCall>();
377+
378+
renderCallList.reserve(renderCallView.nbComponents());
379+
380+
for (const auto& renderCall : renderCallView)
381+
{
382+
renderCallList.push_back(renderCall->call);
383+
}
384+
385+
finishChanges();
386+
}
387+
388+
RenderCall RoundedRect2DObjectSystem::createRenderCall(CompRef<PositionComponent> ui, CompRef<RoundedRect2DObject> obj)
389+
{
390+
LOG_THIS_MEMBER(DOM);
391+
392+
RenderCall call;
393+
394+
call.processPositionComponent(ui);
395+
396+
call.setOpacity(OpacityType::Additive);
397+
398+
call.setRenderStage(renderStage);
399+
400+
call.setMaterial(materialId);
401+
402+
call.setViewport(obj->viewport);
403+
404+
// 11 floats: x, y, z, width, height, rotation, r, g, b, a, cornerRadius
405+
call.data.resize(11);
406+
407+
call.data[0] = ui->x;
408+
call.data[1] = ui->y;
409+
call.data[2] = ui->z;
410+
call.data[3] = ui->width;
411+
call.data[4] = ui->height;
412+
call.data[5] = ui->rotation;
413+
call.data[6] = obj->colors.x;
414+
call.data[7] = obj->colors.y;
415+
call.data[8] = obj->colors.z;
416+
call.data[9] = obj->colors.w;
417+
call.data[10] = obj->cornerRadius;
418+
419+
return call;
420+
}
421+
422+
void RoundedRect2DObjectSystem::onEvent(const EntityChangedEvent& event)
423+
{
424+
LOG_THIS_MEMBER(DOM);
425+
426+
auto entity = ecsRef->getEntity(event.id);
427+
428+
if (not entity or not entity->has<RoundedRect2DObject>())
429+
return;
430+
431+
updateQueue.push(event.id);
432+
433+
changed = true;
434+
}
267435
}

0 commit comments

Comments
 (0)