#version 330 core
uniform float u_smooth_delta = 0.5;
uniform sampler2D u_images[16];
in vec2 v_uv;
in vec2 v_local_uv;
in vec2 v_size;
flat in int v_control;
in float v_box_blur;
in vec4 v_color;
in vec4 v_border;
in vec4 v_radius;
out vec4 FragColor;
// Modified based on https://iquilezles.org/articles/distfunctions2d/
// That website is very handy
float sdRoundBox(vec2 point, vec2 size, float radius) {
vec2 q = abs(point) - size + radius;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}
// Modified based on https://iquilezles.org/articles/distfunctions2d/
float sdEllipse(in vec2 p, in vec2 ab) {
if (ab.x == ab.y) return length(p) - ab.x;
p = abs(p);
if (p.x > p.y) {
p = p.yx;
ab = ab.yx;
}
float l = ab.y * ab.y - ab.x * ab.x;
float m = ab.x * p.x / l;
float m2 = m * m;
float n = ab.y * p.y / l;
float n2 = n * n;
float c = (m2 + n2 - 1.0) / 3.0;
float c3 = c * c * c;
float q = c3 + m2 * n2 * 2.0;
float d = c3 + m2 * n2;
float g = m + m * n2;
float co;
if (d < 0.0) {
float h = acos(q / c3) / 3.0;
float s = cos(h);
float t = sin(h) * sqrt(3.0);
float rx = sqrt(-c * (s + t + 2.0) + m2);
float ry = sqrt(-c * (s - t + 2.0) + m2);
co = (ry + sign(l) * rx + abs(g) / (rx * ry) - m) / 2.0;
} else {
float h = 2.0 * m * n * sqrt(d);
float s = sign(q + h) * pow(abs(q + h), 1.0 / 3.0);
float u = sign(q - h) * pow(abs(q - h), 1.0 / 3.0);
float rx = -s - u - c * 4.0 + 2.0 * m2;
float ry = (s - u) * sqrt(3.0);
float rm = sqrt(rx * rx + ry * ry);
co = (ry / sqrt(rm - rx) + 2.0 * g / rm - m) / 2.0;
}
vec2 r = ab * vec2(co, sqrt(1.0 - co * co));
return length(r - p) * sign(p.y - r.y);
}
// I basically brute-forced my way into math to come up with a SDF for this
// https://www.shadertoy.com/view/w3ByzG
float sdRoundBorderInner44(in vec2 point, in vec2 size, in float radius, in vec4 borders) {
// corner ellipse computations
float dTRBL = -100000.0;
{
vec2 ellipseCenter;
vec2 ellipsePoint;
vec2 ab;
// top right corner
ellipseCenter = vec2(1.0, 1.0) * (size - radius);
ellipsePoint = point - ellipseCenter;
ab = radius - borders.yx;
if (ellipsePoint.x > 0.0 && ellipsePoint.y > 0.0 && min(ab.x, ab.y) > 0.0)
dTRBL = max(dTRBL, -sdEllipse(ellipsePoint, ab));
// top left corner
ellipseCenter = vec2(-1.0, 1.0) * (size - radius);
ellipsePoint = point - ellipseCenter;
ab = radius - borders.wx;
if (ellipsePoint.x < 0.0 && ellipsePoint.y > 0.0 && min(ab.x, ab.y) > 0.0)
dTRBL = max(dTRBL, -sdEllipse(ellipsePoint, ab));
// bottom right corner
ellipseCenter = vec2(1.0, -1.0) * (size - radius);
ellipsePoint = point - ellipseCenter;
ab = radius - borders.yz;
if (ellipsePoint.x > 0.0 && ellipsePoint.y < 0.0 && min(ab.x, ab.y) > 0.0)
dTRBL = max(dTRBL, -sdEllipse(ellipsePoint, ab));
// top left corner
ellipseCenter = vec2(-1.0, -1.0) * (size - radius);
ellipsePoint = point - ellipseCenter;
ab = radius - borders.wz;
if (ellipsePoint.x < 0.0 && ellipsePoint.y < 0.0 && min(ab.x, ab.y) > 0.0)
dTRBL = max(dTRBL, -sdEllipse(ellipsePoint, ab));
}
// interior edges
vec2 borderPosOffset = vec2(borders.y - borders.w, borders.x - borders.z);
vec2 borderSizeOffset = vec2(borders.y + borders.w, borders.x + borders.z);
vec2 innerCenterPoint = point + borderPosOffset / 2.0;
float dX = -sdRoundBox(innerCenterPoint, size - abs(borderSizeOffset) / 2.0, 0.0);
float dInner = dX - 10000.0 > dTRBL ? dX : dTRBL;
return dInner;
}
void main() {
vec2 size = v_size;
int texture_index = v_control & 15;
bool has_texture = (v_control & (1 << 4)) > 0;
bool has_borders = (v_control & (1 << 5)) > 0;
vec2 rect_uv = vec2(v_local_uv.x - 0.5, -(v_local_uv.y - 0.5));
vec4 texture_color = vec4(1.0);
if (has_texture) {
// YandereDev would be proud
switch (texture_index) {
case 0: texture_color = texture(u_images[0], v_uv); break;
case 1: texture_color = texture(u_images[1], v_uv); break;
case 2: texture_color = texture(u_images[2], v_uv); break;
case 3: texture_color = texture(u_images[3], v_uv); break;
case 4: texture_color = texture(u_images[4], v_uv); break;
case 5: texture_color = texture(u_images[5], v_uv); break;
case 6: texture_color = texture(u_images[6], v_uv); break;
case 7: texture_color = texture(u_images[7], v_uv); break;
case 8: texture_color = texture(u_images[8], v_uv); break;
case 9: texture_color = texture(u_images[9], v_uv); break;
case 10: texture_color = texture(u_images[10], v_uv); break;
case 11: texture_color = texture(u_images[11], v_uv); break;
case 12: texture_color = texture(u_images[12], v_uv); break;
case 13: texture_color = texture(u_images[13], v_uv); break;
case 14: texture_color = texture(u_images[14], v_uv); break;
case 15: texture_color = texture(u_images[15], v_uv); break;
}
}
vec2 pos = rect_uv * size;
vec2 hsize = size / 2.0;
vec4 radius = v_radius;
vec4 color = v_color * texture_color;
radius.xy = (rect_uv.x > 0.0) ? radius.xy : radius.wz;
radius.x = (rect_uv.y > 0.0) ? radius.x : radius.y;
float collapsed_radius = radius.x;
float d_outer = sdRoundBox(pos, hsize, collapsed_radius);
float dist = d_outer;
if (has_borders) {
float d_inner = sdRoundBorderInner44(pos, hsize, collapsed_radius, v_border);
dist = max(d_outer, d_inner);
}
float delta = u_smooth_delta;
if (dist > delta) {
discard;
}
FragColor = mix(color, vec4(color.rgb, 0.0), smoothstep(-delta - v_box_blur, delta, dist));
}