Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement shaders for ArgonHealthDisplay #302

Merged
merged 13 commits into from Jan 16, 2024
61 changes: 61 additions & 0 deletions osu.Game.Resources/Shaders/sh_ArgonBarPath.fs
@@ -0,0 +1,61 @@
#ifndef ARGON_BAR_PATH_FS
#define ARGON_BAR_PATH_FS

#undef HIGH_PRECISION_VERTEX
#define HIGH_PRECISION_VERTEX

#include "sh_Utils.h"
#include "sh_Masking.h"
#include "sh_TextureWrapping.h"
#include "sh_ArgonBarPathUtils.h"

layout(location = 2) in highp vec2 v_TexCoord;

layout(std140, set = 0, binding = 0) uniform m_ArgonBarPathParameters
{
lowp vec4 barColour;
lowp vec4 glowColour;
mediump vec2 size;
highp vec2 progressRange;
mediump float pathRadius;
mediump float glowPortion;
};

layout(location = 0) out vec4 o_Colour;

lowp vec4 glowColAt(highp float absoluteTexturePos, highp float absoluteGlowPortion)
{
highp float mixValue = 1.0 - (absoluteTexturePos - pathRadius + absoluteGlowPortion) / absoluteGlowPortion;
// 5 multiplications should match InQuint easing, however it still doesn't look quite the same.
// Instead let it be 8 as it looks the closest.
mixValue *= mixValue;
mixValue *= mixValue;
mixValue *= mixValue;
return vec4(glowColour.rgb, glowColour.a * mixValue);
}

lowp vec4 getColour(highp float absoluteTexturePos)
{
highp float absoluteGlowPortion = pathRadius * glowPortion;
absoluteTexturePos = clamp(absoluteTexturePos, 0.0, pathRadius);

if (absoluteTexturePos < pathRadius - absoluteGlowPortion - 1.0)
return barColour;

if (absoluteTexturePos < pathRadius - absoluteGlowPortion)
return mix(glowColour, barColour, pathRadius - absoluteGlowPortion - absoluteTexturePos);

return glowColAt(absoluteTexturePos, absoluteGlowPortion);
}

void main(void)
{
highp vec2 resolution = v_TexRect.zw - v_TexRect.xy;
highp vec2 pixelPos = (v_TexCoord - v_TexRect.xy) / resolution;

mediump vec2 absolutePos = size * pixelPos;
highp float absoluteTexturePos = getBarTexturePosition(size, progressRange, pathRadius, absolutePos);
o_Colour = getRoundedColor(getColour(absoluteTexturePos), v_TexCoord);
}

#endif
51 changes: 51 additions & 0 deletions osu.Game.Resources/Shaders/sh_ArgonBarPathBackground.fs
@@ -0,0 +1,51 @@
#ifndef ARGON_BAR_PATH_BACKGROUND_FS
#define ARGON_BAR_PATH_BACKGROUND_FS
peppy marked this conversation as resolved.
Show resolved Hide resolved

#undef HIGH_PRECISION_VERTEX
#define HIGH_PRECISION_VERTEX

#include "sh_Utils.h"
#include "sh_Masking.h"
#include "sh_TextureWrapping.h"
#include "sh_ArgonBarPathUtils.h"

layout(location = 2) in highp vec2 v_TexCoord;

layout(std140, set = 0, binding = 0) uniform m_ArgonBarPathBackgroundParameters
{
mediump vec2 size;
};

layout(location = 0) out vec4 o_Colour;

lowp vec4 bgColAt(highp float absoluteTexturePos, mediump float pathRadius)
{
highp float relativeTexturePos = clamp(absoluteTexturePos / pathRadius, 0.0, 1.5);
return mix(vec4(vec3(0.0), 0.2), vec4(vec3(1.0), 0.8), relativeTexturePos / 1.5);
}

lowp vec4 getColour(highp float absoluteTexturePos, mediump float pathRadius)
{
if (absoluteTexturePos > pathRadius - 1.0)
return mix(vec4(1.0), vec4(vec3(1.0), 0.0), absoluteTexturePos - (pathRadius - 1.0));

if (absoluteTexturePos > pathRadius - 2.0)
return mix(bgColAt(absoluteTexturePos, pathRadius), vec4(1.0), absoluteTexturePos - (pathRadius - 2.0));

return bgColAt(absoluteTexturePos, pathRadius);
}

void main(void)
{
highp vec2 resolution = v_TexRect.zw - v_TexRect.xy;
highp vec2 pixelPos = (v_TexCoord - v_TexRect.xy) / resolution;

mediump vec2 absolutePos = size * pixelPos;

const float pathRadius = 10.0;

highp float absoluteTexturePos = getBarTexturePosition(size, vec2(0.0, 1.0), pathRadius, absolutePos);
o_Colour = getRoundedColor(getColour(absoluteTexturePos, pathRadius), v_TexCoord);
}

#endif
214 changes: 214 additions & 0 deletions osu.Game.Resources/Shaders/sh_ArgonBarPathUtils.h
@@ -0,0 +1,214 @@
#ifndef ARGON_BAR_PATH_UTILS_H
#define ARGON_BAR_PATH_UTILS_H

#undef PI
#define PI 3.1415926536

#undef HALF_PI
#define HALF_PI 1.57079632679

#undef TWO_PI
#define TWO_PI 6.28318530718

highp float dstToLine(highp vec2 start, highp vec2 end, highp vec2 pixelPos)
{
highp float lineLength = distance(end, start);

if (lineLength < 0.001)
return distance(pixelPos, start);

highp vec2 a = (end - start) / lineLength;
highp vec2 closest = clamp(dot(a, pixelPos - start), 0.0, distance(end, start)) * a + start; // closest point on a line from given position
return distance(closest, pixelPos);
}

highp float dstToArc(highp vec2 arcPos, highp float radius, highp float angle, highp vec2 pixelPos, highp float rotation)
{
pixelPos -= arcPos;

highp float pixelAngle = atan(-pixelPos.y, -pixelPos.x) - HALF_PI + rotation;
pixelAngle += float(pixelAngle < 0.0) * TWO_PI;
highp vec2 cs = vec2(cos(pixelAngle - HALF_PI), sin(pixelAngle - HALF_PI));
pixelPos = cs * vec2(distance(pixelPos, vec2(0.0)));

pixelPos.x = abs(pixelPos.x);
highp vec2 sc = vec2(sin(angle * 0.5), cos(angle * 0.5));
return (sc.y * pixelPos.x > sc.x * pixelPos.y) ? length(pixelPos - sc * radius) : abs(length(pixelPos) - radius);
}

highp float dstToTopLine(
highp vec2 progressRange,
mediump vec2 p1,
mediump vec2 arc1Start,
highp float progressArc1Start,
mediump vec2 pixelAbsolutePos)
{
if (progressRange.x > progressArc1Start)
return 1000.0;

if (progressArc1Start < 0.001)
return distance(pixelAbsolutePos, arc1Start);

progressRange.y = min(progressRange.y, progressArc1Start);

mediump vec2 startPos = mix(p1, arc1Start, progressRange.x / progressArc1Start);
mediump vec2 endPos = mix(p1, arc1Start, progressRange.y / progressArc1Start);

return dstToLine(startPos, endPos, pixelAbsolutePos);
}

highp float dstToTopArc(
highp vec2 progressRange,
mediump float radius,
highp float progressArc1Start,
highp float progressArc1End,
mediump vec2 arcCentre,
highp float slashAngle,
highp float fullArcAngle,
mediump vec2 pixelAbsolutePos)
{
if (progressRange.x > progressArc1End || progressRange.y < progressArc1Start)
return 1000.0;

if (progressArc1End - progressArc1Start < 0.001)
return distance(pixelAbsolutePos, vec2(arcCentre.x, arcCentre.y - radius));

progressRange.x = max(progressRange.x, progressArc1Start);
progressRange.y = min(progressRange.y, progressArc1End);

highp float currentArcAngle = fullArcAngle * (progressRange.y - progressRange.x) / (progressArc1End - progressArc1Start);
highp float rotationOffset = fullArcAngle * (progressRange.x - progressArc1Start) / (progressArc1End - progressArc1Start);
return dstToArc(arcCentre, radius, currentArcAngle, pixelAbsolutePos, HALF_PI + (slashAngle + fullArcAngle - currentArcAngle) * 0.5 - rotationOffset);
}

highp float dstToSlash(
highp vec2 progressRange,
mediump vec2 arc1End,
mediump vec2 arc2Start,
highp float progressArc1End,
highp float progressArc2Start,
mediump vec2 pixelAbsolutePos)
{
if (progressRange.x > progressArc2Start || progressRange.y < progressArc1End)
return 1000.0;

progressRange.x = max(progressRange.x, progressArc1End);
progressRange.y = min(progressRange.y, progressArc2Start);

mediump vec2 startPos = mix(arc1End, arc2Start, (progressRange.x - progressArc1End) / (progressArc2Start - progressArc1End));
mediump vec2 endPos = mix(arc1End, arc2Start, (progressRange.y - progressArc1End) / (progressArc2Start - progressArc1End));

return dstToLine(startPos, endPos, pixelAbsolutePos);
}

highp float dstToBottomArc(
highp vec2 progressRange,
mediump float radius,
highp float progressArc2Start,
highp float progressArc2End,
mediump vec2 arcCentre,
highp float slashAngle,
highp float fullArcAngle,
mediump vec2 pixelAbsolutePos)
{
if (progressRange.x > progressArc2End || progressRange.y < progressArc2Start)
return 1000.0;

if (progressArc2End - progressArc2Start < 0.001)
return distance(pixelAbsolutePos, vec2(arcCentre.x, arcCentre.y + radius));

progressRange.x = max(progressRange.x, progressArc2Start);
progressRange.y = min(progressRange.y, progressArc2End);

highp float currentArcAngle = fullArcAngle * (progressRange.y - progressRange.x) / (progressArc2End - progressArc2Start);
highp float rotationOffset = fullArcAngle * (progressRange.x - progressArc2Start) / (progressArc2End - progressArc2Start);
return dstToArc(arcCentre, radius, currentArcAngle, pixelAbsolutePos, PI + HALF_PI + (slashAngle - fullArcAngle + currentArcAngle) * 0.5 + rotationOffset);
}

highp float dstToBottomLine(
highp vec2 progressRange,
mediump vec2 arc2End,
mediump vec2 p4,
highp float progressArc2End,
mediump vec2 pixelAbsolutePos)
{
if (progressRange.y < progressArc2End)
return 1000.0;

if (1.0 - progressArc2End < 0.001)
return distance(pixelAbsolutePos, arc2End);

progressRange.x = max(progressRange.x, progressArc2End);

mediump vec2 startPos = mix(arc2End, p4, (progressRange.x - progressArc2End) / (1.0 - progressArc2End));
mediump vec2 endPos = mix(arc2End, p4, (progressRange.y - progressArc2End) / (1.0 - progressArc2End));

return dstToLine(startPos, endPos, pixelAbsolutePos);
}

highp float getBarTexturePosition(mediump vec2 size, highp vec2 progressRange, mediump float pathRadius, mediump vec2 pixelAbsolutePos)
{
mediump vec2 p1 = vec2(min(pathRadius, size.x * 0.5), min(pathRadius, size.y * 0.5));
mediump vec2 p4 = vec2(max(size.x - pathRadius, size.x * 0.5), max(size.y - pathRadius, size.y * 0.5));

if (p4.y == p1.y)
{
return dstToLine(vec2(p1.x + progressRange.x * (p4.x - p1.x), p1.y), vec2(p1.x + progressRange.y * (p4.x - p1.x), p1.y), pixelAbsolutePos);
}

const float curve_start_offset = 70.0;
const float curve_end_offset = 40.0;
const float curve_smoothness = 10.0;

mediump float topWidth = max(size.x - pathRadius - curve_start_offset, p1.x) - p1.x;
mediump float bottomWidth = p4.x - max(size.x - pathRadius - curve_end_offset, p1.x);

if (topWidth < bottomWidth)
{
mediump float newWidth = (topWidth + bottomWidth) * 0.5;
topWidth = newWidth;
bottomWidth = newWidth;
}

mediump vec2 p2 = vec2(p1.x + topWidth, p1.y);
mediump vec2 p3 = vec2(p4.x - bottomWidth, p4.y);

mediump float slashLength = distance(p2, p3);
mediump float roundOffset = min(min(topWidth, slashLength * 0.5), curve_smoothness);

mediump vec2 arc1Start = vec2(p2.x - roundOffset, p2.y);
mediump vec2 arc1End = mix(p2, p3, roundOffset / slashLength);
mediump vec2 arc2Start = mix(p2, p3, 1.0 - roundOffset / slashLength);
mediump vec2 arc2End = vec2(p3.x + roundOffset, p3.y);

highp float slashAngle = atan(p2.y - p3.y, p2.x - p3.x) - HALF_PI;
slashAngle += float(slashAngle < 0.0) * TWO_PI;
slashAngle = TWO_PI - HALF_PI - slashAngle;

highp float arcAngle = (HALF_PI - slashAngle * 0.5) * 2.0;
mediump float arcRadius = roundOffset * tan(slashAngle * 0.5);
mediump float arcLength = arcAngle * arcRadius;

mediump float length1 = arc1Start.x - p1.x;
mediump float length2 = distance(arc1End, arc2Start);
mediump float length3 = p4.x - arc2End.x;

mediump float totalLength = length1 + length2 + length3 + arcLength * 2.0;

highp float progressArc1Start = length1 / totalLength;
highp float progressArc1End = (length1 + arcLength) / totalLength;
highp float progressArc2Start = (length1 + arcLength + length2) / totalLength;
highp float progressArc2End = (length1 + length2 + arcLength * 2.0) / totalLength;
mediump vec2 arc1Centre = vec2(p2.x - roundOffset, p2.y + arcRadius);
mediump vec2 arc2Centre = vec2(p3.x + roundOffset, p3.y - arcRadius);

mediump float minDst = dstToTopLine(progressRange, p1, arc1Start, progressArc1Start, pixelAbsolutePos);
minDst = min(minDst, dstToTopArc(progressRange, arcRadius, progressArc1Start, progressArc1End, arc1Centre, slashAngle, arcAngle, pixelAbsolutePos));
minDst = min(minDst, dstToSlash(progressRange, arc1End, arc2Start, progressArc1End, progressArc2Start, pixelAbsolutePos));
minDst = min(minDst, dstToBottomArc(progressRange, arcRadius, progressArc2Start, progressArc2End, arc2Centre, slashAngle, arcAngle, pixelAbsolutePos));
minDst = min(minDst, dstToBottomLine(progressRange, arc2End, p4, progressArc2End, pixelAbsolutePos));
peppy marked this conversation as resolved.
Show resolved Hide resolved

return minDst;
}

#endif