diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs index 31cdc0dc0f..b719138d33 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs @@ -2,10 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; +using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; @@ -19,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon private const float arc_fill = 0.15f; private const float arc_radius = 0.12f; - private CircularProgress fill = null!; + private ProgressFill fill = null!; private DrawableSpinner spinner = null!; @@ -45,13 +52,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon InnerRadius = arc_radius, RoundedCaps = true, }, - fill = new CircularProgress + fill = new ProgressFill { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, InnerRadius = arc_radius, RoundedCaps = true, + GlowColour = new Color4(171, 255, 255, 255) } }; } @@ -67,5 +75,115 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon fill.Rotation = (float)(90 - fill.Current.Value * 180); } + + private partial class ProgressFill : CircularProgress + { + private Color4 glowColour = Color4.White; + + public Color4 GlowColour + { + get => glowColour; + set + { + glowColour = value; + Invalidate(Invalidation.DrawNode); + } + } + + private Texture glowTexture = null!; + private IShader glowShader = null!; + private float glowSize; + + [BackgroundDependencyLoader] + private void load(TextureStore textures, ShaderManager shaders) + { + glowTexture = textures.Get("Gameplay/osu/spinner-glow"); + glowShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "SpinnerGlow"); + glowSize = Blur.KernelSize(50); // Half of the maximum blur sigma in the design (which is 100) + } + + protected override DrawNode CreateDrawNode() => new ProgressFillDrawNode(this); + + private class ProgressFillDrawNode : CircularProgressDrawNode + { + protected new ProgressFill Source => (ProgressFill)base.Source; + + public ProgressFillDrawNode(CircularProgress source) + : base(source) + { + } + + private Texture glowTexture = null!; + private IShader glowShader = null!; + private Quad glowQuad; + private float relativeGlowSize; + private Color4 glowColour; + + public override void ApplyState() + { + base.ApplyState(); + + glowTexture = Source.glowTexture; + glowShader = Source.glowShader; + glowColour = Source.glowColour; + + // Inflated draw quad by the size of the blur. + glowQuad = Source.ToScreenSpace(DrawRectangle.Inflate(Source.glowSize)); + relativeGlowSize = Source.glowSize / Source.DrawSize.X; + } + + public override void Draw(IRenderer renderer) + { + base.Draw(renderer); + drawGlow(renderer); + } + + private void drawGlow(IRenderer renderer) + { + renderer.SetBlend(BlendingParameters.Additive); + + glowShader.Bind(); + bindGlowUniformResources(glowShader, renderer); + + ColourInfo col = DrawColourInfo.Colour; + col.ApplyChild(glowColour); + + renderer.DrawQuad(glowTexture, glowQuad, col); + + glowShader.Unbind(); + } + + private IUniformBuffer? progressGlowParametersBuffer; + + private void bindGlowUniformResources(IShader shader, IRenderer renderer) + { + progressGlowParametersBuffer ??= renderer.CreateUniformBuffer(); + progressGlowParametersBuffer.Data = new ProgressGlowParameters + { + InnerRadius = InnerRadius, + Progress = Progress, + TexelSize = TexelSize, + GlowSize = relativeGlowSize + }; + + shader.BindUniformBlock("m_ProgressGlowParameters", progressGlowParametersBuffer); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + progressGlowParametersBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct ProgressGlowParameters + { + public UniformFloat InnerRadius; + public UniformFloat Progress; + public UniformFloat TexelSize; + public UniformFloat GlowSize; + } + } + } } }