diff --git a/osu.Game.Rulesets.Sentakki.Tests/Graphics/TestSceneTouchTriangle.cs b/osu.Game.Rulesets.Sentakki.Tests/Graphics/TestSceneTouchTriangle.cs new file mode 100644 index 000000000..3df2ecb8e --- /dev/null +++ b/osu.Game.Rulesets.Sentakki.Tests/Graphics/TestSceneTouchTriangle.cs @@ -0,0 +1,42 @@ +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Sentakki.Objects; +using osu.Game.Rulesets.Sentakki.Objects.Drawables; +using osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.Slides; +using osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.Touches; +using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Sentakki.Tests.Graphics +{ + [TestFixture] + public partial class TestSceneTouchTriangle : OsuGridTestScene + { + protected override Ruleset CreateRuleset() => new SentakkiRuleset(); + public TestSceneTouchTriangle() : base(1, 2) + { + Cell(0).Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White + }); + Cell(1).Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White + }); + Cell(0).Add(new TouchPiece()); + Cell(1).Add(new TouchPiece() + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + } + } +} diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs index 0d315e1c3..d379717b6 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs @@ -141,15 +141,5 @@ protected override void UpdateHitStateTransforms(ArmedState state) } public bool OnNewPointInteraction() => UpdateResult(true); - - private struct TouchEasingFunction : IEasingFunction - { - public readonly double ApplyEasing(double t) - { - double result = (3.5 * Math.Pow(t, 4)) - (3.75 * Math.Pow(t, 3)) + (1.45 * Math.Pow(t, 2)) - (0.05 * t) + 0.005; - - return Math.Min(1, result); - } - } } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs index 67e69ae55..20a7f8f13 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs @@ -51,7 +51,6 @@ private void load() Colour = Color4.SlateGray; Anchor = Anchor.Centre; Origin = Anchor.Centre; - Scale = new Vector2(0f); Alpha = 0; AddRangeInternal(new Drawable[] { @@ -100,11 +99,19 @@ protected override void OnFree() protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); - double fadeIn = AnimationDuration.Value; - this.FadeInFromZero(fadeIn).ScaleTo(1, fadeIn); + double animTime = AnimationDuration.Value * 0.8; + double fadeTime = AnimationDuration.Value * 0.2; - using (BeginDelayedSequence(fadeIn)) + this.FadeInFromZero(fadeTime).ScaleTo(1); + + using (BeginAbsoluteSequence(HitObject.StartTime - animTime)) + { + TouchHoldBody.ResizeTo(80, animTime, Easing.InCirc); + } + using (BeginDelayedSequence(fadeTime + animTime)) { + TouchHoldBody.centrePiece.FadeOut(); + TouchHoldBody.CompletedCentre.FadeIn(); TouchHoldBody.ProgressPiece.TransformBindableTo(TouchHoldBody.ProgressPiece.ProgressBindable, 1, ((IHasDuration)HitObject).Duration); } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldBody.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldBody.cs index ec423dd8e..41a9046ec 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldBody.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldBody.cs @@ -1,29 +1,31 @@ -using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects.Drawables; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.TouchHolds { public partial class TouchHoldBody : CircularContainer { public readonly TouchHoldProgressPiece ProgressPiece; - private readonly TouchHoldCentrePiece centrePiece; + public readonly TouchHoldCentrePiece centrePiece; + + public readonly TouchHoldCompletedCentre CompletedCentre; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => ProgressPiece.ReceivePositionalInputAt(screenSpacePos); public TouchHoldBody() { - Size = new Vector2(110); + Size = new Vector2(130); Anchor = Anchor.Centre; Origin = Anchor.Centre; InternalChildren = new Drawable[] { ProgressPiece = new TouchHoldProgressPiece(), centrePiece = new TouchHoldCentrePiece(), + // We swap the centre piece with this other drawable to make it look better with the progress bar + // Otherwise we'd need to add a thick border in between the centre and the progress + CompletedCentre = new TouchHoldCompletedCentre(), + new DotPiece(), }; } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldCentrePiece.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldCentrePiece.cs index d28eddb58..72a1eae53 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldCentrePiece.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldCentrePiece.cs @@ -1,8 +1,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; +using osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.Touches; using osuTK; using osuTK.Graphics; @@ -11,76 +13,64 @@ namespace osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.TouchHolds public partial class TouchHoldCentrePiece : CompositeDrawable { private readonly OsuColour colours = new OsuColour(); + public Container PieceContainer; public TouchHoldCentrePiece() { Origin = Anchor.Centre; Anchor = Anchor.Centre; - Size = new Vector2(80); - Masking = true; - CornerRadius = 20f; + RelativeSizeAxes = Axes.Both; Rotation = 45; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black, - Radius = 10f, - }; + Scale = new Vector2(80 / 90f); + InternalChildren = new Drawable[] { - new Container + PieceContainer = new Container { - Origin = Anchor.Centre, Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Size = new Vector2(2), - Rotation = -45f, Children = new Drawable[] { - new CircularProgress - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - InnerRadius = 1, - Size = Vector2.One, - RelativeSizeAxes = Axes.Both, - Progress = 1, - Colour = colours.Blue - }, - new CircularProgress - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - InnerRadius = 1, - Size = Vector2.One, - RelativeSizeAxes = Axes.Both, - Progress = 0.75 , - Colour = colours.Green - }, - new CircularProgress - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - InnerRadius = 1, - Size = Vector2.One, - RelativeSizeAxes = Axes.Both, - Progress = 0.5 , - Colour = colours.Yellow, - }, - new CircularProgress - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - InnerRadius = 1, - Size = Vector2.One, - RelativeSizeAxes = Axes.Both, - Progress = 0.25 , - Colour = colours.Red, - }, + createTouchShapeWith(), + createTouchShapeWith(), } }, - new DotPiece() }; } + + // Creates the touch shape using the provided drawable as each of the 4 quarters + private Drawable createTouchShapeWith() where T : Drawable, new() + => new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[]{ + new T + { + Anchor = Anchor.TopCentre, + Colour = colours.Red + }, + new T + { + Anchor = Anchor.CentreRight, + Rotation = 90, + Colour = colours.Yellow, + }, + new T + { + Anchor = Anchor.BottomCentre, + Rotation = 180, + Colour = colours.Green + }, + new T + { + Anchor = Anchor.CentreLeft, + Rotation = 270, + Colour = colours.Blue, + }, + } + }; } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldCompletedCentrePiece.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldCompletedCentrePiece.cs new file mode 100644 index 000000000..cea1e9d75 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldCompletedCentrePiece.cs @@ -0,0 +1,86 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.TouchHolds +{ + public partial class TouchHoldCompletedCentre : CompositeDrawable + { + private readonly OsuColour colours = new OsuColour(); + + public TouchHoldCompletedCentre() + { + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + Size = new Vector2(80); + Masking = true; + CornerRadius = 20; + Rotation = 45; + Alpha = 0; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black, + Radius = 10f, + }; + InternalChildren = new Drawable[] + { + new Container + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(2), + Rotation = -45f, + Children = new Drawable[] + { + new CircularProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + InnerRadius = 1, + Size = Vector2.One, + RelativeSizeAxes = Axes.Both, + Progress = 1, + Colour = colours.Blue + }, + new CircularProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + InnerRadius = 1, + Size = Vector2.One, + RelativeSizeAxes = Axes.Both, + Progress = 0.75 , + Colour = colours.Green + }, + new CircularProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + InnerRadius = 1, + Size = Vector2.One, + RelativeSizeAxes = Axes.Both, + Progress = 0.5 , + Colour = colours.Yellow, + }, + new CircularProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + InnerRadius = 1, + Size = Vector2.One, + RelativeSizeAxes = Axes.Both, + Progress = 0.25 , + Colour = colours.Red, + }, + } + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldTrianglePiece.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldTrianglePiece.cs new file mode 100644 index 000000000..ccc54982e --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/TouchHolds/TouchHoldTrianglePiece.cs @@ -0,0 +1,26 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.Touches; +using osuTK; + +namespace osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.TouchHolds +{ + public partial class TouchHoldPiece : CompositeDrawable + { + public TouchHoldPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + AddInternal(new DrawableTouchTriangle + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(73, 45f), + Thickness = 8f, + ShadowRadius = 0f, + FillTriangle = true, + }); + } + } +} diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/DrawableTouchTriangle.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/DrawableTouchTriangle.cs new file mode 100644 index 000000000..3af1a1b94 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/DrawableTouchTriangle.cs @@ -0,0 +1,186 @@ +using System.Runtime.InteropServices; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; +using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; + +namespace osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.Touches; + +public partial class DrawableTouchTriangle : Sprite, ITexturedShaderDrawable +{ + public NoteShape Shape { get; init; } = NoteShape.Ring; + private float thickness = 7f; + public float Thickness + { + get => thickness; + set + { + if (thickness == value) + return; + thickness = value; + Invalidate(Invalidation.DrawNode); + } + } + + // Special behavior, any non-zero value will hide the main body + // This is because touch notes shadow should exist *under* all triangles, not just a single one + private float shadowRadius = 15; + public float ShadowRadius + { + get => shadowRadius; + set + { + if (shadowRadius == value) + return; + shadowRadius = value; + Invalidate(Invalidation.DrawNode); + } + } + + private bool glow; + public bool Glow + { + get => glow; + set + { + if (glow == value) + return; + glow = value; + Invalidate(Invalidation.DrawNode); + } + } + + private bool fillTriangle; + public bool FillTriangle + { + get => fillTriangle; + set + { + if (fillTriangle == value) + return; + fillTriangle = value; + Invalidate(Invalidation.DrawNode); + } + } + + private bool shadowOnly; + public bool ShadowOnly + { + get => shadowOnly; + set + { + if (shadowOnly == value) + return; + shadowOnly = value; + Invalidate(Invalidation.DrawNode); + } + } + + public new IShader TextureShader { get; private set; } = null!; + + protected override DrawNode CreateDrawNode() => new TouchTriangleDrawNode(this); + + private BindableBool exBindable = new BindableBool(); + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders, IRenderer renderer, DrawableHitObject? hitObject) + { + TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "touchTriangle"); + Texture = renderer.WhitePixel; + + if (hitObject is null) + return; + + // Bind exnote + exBindable.BindTo(((DrawableSentakkiHitObject)hitObject).ExBindable); + exBindable.BindValueChanged(b => Glow = b.NewValue, true); + } + + private partial class TouchTriangleDrawNode : SpriteDrawNode + { + protected new DrawableTouchTriangle Source => (DrawableTouchTriangle)base.Source; + protected override bool CanDrawOpaqueInterior => false; + private IUniformBuffer? shapeParameters; + private IUniformBuffer? triangleParameters; + + private float thickness; + private Vector2 size; + private bool glow; + private float shadowRadius; + private bool fillTriangle; + private bool shadowOnly; + + public TouchTriangleDrawNode(DrawableTouchTriangle source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + thickness = Source.Thickness; + size = Source.DrawSize; + shadowRadius = Source.shadowRadius; + glow = Source.Glow; + fillTriangle = Source.FillTriangle; + shadowOnly = Source.ShadowOnly; + } + + protected override void BindUniformResources(IShader shader, IRenderer renderer) + { + base.BindUniformResources(shader, renderer); + + shapeParameters ??= renderer.CreateUniformBuffer(); + + shapeParameters.Data = shapeParameters.Data with + { + Thickness = thickness, + Size = size, + ShadowRadius = shadowRadius, + Glow = glow, + }; + + triangleParameters ??= renderer.CreateUniformBuffer(); + + triangleParameters.Data = triangleParameters.Data with + { + fillTriangle = fillTriangle, + shadowOnly = shadowOnly + }; + + shader.BindUniformBlock("m_shapeParameters", shapeParameters); + shader.BindUniformBlock("m_triangleParameters", triangleParameters); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + shapeParameters?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct ShapeParameters + { + public UniformFloat Thickness; + public UniformPadding4 _; + public UniformVector2 Size; + public UniformFloat ShadowRadius; + public UniformBool Glow; + + public UniformPadding8 __; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct TriangleParameters + { + public UniformBool fillTriangle; + public UniformBool shadowOnly; + public UniformPadding8 __; + } + } +} diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchBody.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchBody.cs index 43cda5546..7eb6a7f1d 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchBody.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchBody.cs @@ -16,7 +16,7 @@ public partial class TouchBody : Container public TouchBody() { - Size = new Vector2(130); + Size = new Vector2(value: 130); Anchor = Anchor.Centre; Origin = Anchor.Centre; Alpha = 0; @@ -30,8 +30,8 @@ public TouchBody() RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - createTouchShapeWith(), // Meant for the drop shadow/glow - createTouchShapeWith(), + createTouchShape(), + createTouchShape(), new DotPiece() } }, @@ -40,10 +40,10 @@ public TouchBody() Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(100), - CornerRadius = 25f, + CornerRadius = 25, CornerExponent = 2.5f, Masking = true, - BorderThickness = 10, + BorderThickness = 12, BorderColour = Color4.White, Alpha = 0, Child = new Box @@ -66,28 +66,28 @@ private void load(DrawableHitObject drawableObject) } // Creates the touch shape using the provided drawable as each of the 4 quarters - private Drawable createTouchShapeWith() where T : Drawable, new() + private Drawable createTouchShape(bool shadow = false) where T : Drawable, new() => new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Children = new Drawable[]{ - new T + new T() { Anchor = Anchor.TopCentre, }, - new T + new T() { Anchor = Anchor.BottomCentre, Rotation = 180 }, - new T + new T() { Anchor = Anchor.CentreLeft, Rotation = 270 }, - new T + new T() { Anchor = Anchor.CentreRight, Rotation = 90 diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchGlowPiece.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchGlowPiece.cs deleted file mode 100644 index b94f52888..000000000 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchGlowPiece.cs +++ /dev/null @@ -1,49 +0,0 @@ -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Objects.Drawables; -using osuTK.Graphics; - -namespace osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.Touches -{ - public partial class TouchGlowPiece : CompositeDrawable - { - private Bindable ExBindable = new Bindable(); - - public TouchGlowPiece() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures, DrawableHitObject? hitObject) - { - var tex = textures.Get("touchGlow"); - - // We must shift the texture down by this amount to align the sprite to the top edge of the triangle - const float triangle_half_height = 43f; - - // The textures are potentially scaled down when in use, so let's account for that (osu does supersampling by default) - float yShift = triangle_half_height / tex.ScaleAdjust; - - AddInternal(new Sprite - { - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Y = yShift, - Texture = tex, - }); - - if (hitObject is null) - return; - - // Bind exnote - ExBindable.BindTo(((DrawableSentakkiHitObject)hitObject).ExBindable); - ExBindable.BindValueChanged(v => Colour = v.NewValue ? Color4.White : Color4.Black, true); - } - } -} diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchPiece.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchPiece.cs index d4543de20..886acd201 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchPiece.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchPiece.cs @@ -1,8 +1,6 @@ -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; +using osuTK; namespace osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.Touches { @@ -12,16 +10,14 @@ public TouchPiece() { Anchor = Anchor.Centre; Origin = Anchor.Centre; - } - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - AddInternal(new Sprite + AddInternal(new DrawableTouchTriangle { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Texture = textures.Get("touch"), + Size = new Vector2(73, 45f), + Thickness = 8f, + ShadowRadius = 0f, }); } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchPieceShadow.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchPieceShadow.cs new file mode 100644 index 000000000..be2f014b6 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/Touches/TouchPieceShadow.cs @@ -0,0 +1,26 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osuTK; + +namespace osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces.Touches +{ + public partial class TouchPieceShadow : CompositeDrawable + { + public TouchPieceShadow() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + AddInternal(new DrawableTouchTriangle + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(103, 75f), + Thickness = 8f, + ShadowRadius = 15, + ShadowOnly = true, + Y = -15 + }); + } + } +} diff --git a/osu.Game.Rulesets.Sentakki/Resources/Shaders/sh_touchTriangle.fs b/osu.Game.Rulesets.Sentakki/Resources/Shaders/sh_touchTriangle.fs new file mode 100644 index 000000000..3ed2b28ff --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/Resources/Shaders/sh_touchTriangle.fs @@ -0,0 +1,101 @@ +#ifndef SENTAKKI_CHEVRON_FS +#define SENTAKKI_CHEVRON_FS + +#include "sh_noteBase.fs" + +layout(std140, set = 1, binding = 0) uniform m_triangleParameters +{ + bool fillTriangle; + bool shadowOnly; +}; + + +float sdTriangle( in vec2 p, in vec2 p0, in vec2 p1, in vec2 p2 ) +{ + vec2 e0 = p1-p0, e1 = p2-p1, e2 = p0-p2; + vec2 v0 = p -p0, v1 = p -p1, v2 = p -p2; + vec2 pq0 = v0 - e0*clamp( dot(v0,e0)/dot(e0,e0), 0.0, 1.0 ); + vec2 pq1 = v1 - e1*clamp( dot(v1,e1)/dot(e1,e1), 0.0, 1.0 ); + vec2 pq2 = v2 - e2*clamp( dot(v2,e2)/dot(e2,e2), 0.0, 1.0 ); + float s = sign( e0.x*e2.y - e0.y*e2.x ); + vec2 d = min(min(vec2(dot(pq0,pq0), s*(v0.x*e0.y-v0.y*e0.x)), + vec2(dot(pq1,pq1), s*(v1.x*e1.y-v1.y*e1.x))), + vec2(dot(pq2,pq2), s*(v2.x*e2.y-v2.y*e2.x))); + return -sqrt(d.x)*sign(d.y); +} + +float triangle(in vec2 p, in vec2 centre, in vec2 size){ + + p -= centre; + + float w = size.x * 0.5; + float h = size.y * 0.5; + vec2 p0 = vec2(-w, -h); + vec2 p1 = vec2(0.0, h); + vec2 p2 = vec2(w, -h); + float dist = sdTriangle(p, p0,p1,p2); + + return dist; +} + +vec4 sdfShadow(in float dist, in float borderThickness, in float shadowThickness){ + vec3 shadowColor = glow ? vec3(1) : vec3(0); + float shadowAlpha = glow ? 0.75: 0.6; + + float shadowDist = dist - borderThickness; + + float shadow = pow((1 - clamp(((1 / shadowThickness) * shadowDist), 0.0 , 1.0)) * shadowAlpha, 2.0); + float exclusion = smoothstep(borderThickness, borderThickness - 1.0, dist); // Inner cutout for shadow + + vec4 shadowPart = vec4(shadowColor,shadow) * (1 - exclusion) * v_Colour; + return shadowPart; +} + +vec4 sdfFill(in float dist, in float borderThickness, in float shadowThickness){ + vec3 shadowColor = glow ? vec3(1) : vec3(0); + float shadowAlpha = glow ? 0.75: 0.6; + + float base = smoothstep(borderThickness - 2.0, borderThickness - 3.0, dist); + float outline = smoothstep(borderThickness, borderThickness - 1.0, dist); + + if(shadowThickness < 1) + return vec4(vec3(max(outline * 0.5, base)), outline) * v_Colour; + + float shadowDist = dist - borderThickness; + + float shadow = pow((1 - clamp(((1 / shadowThickness) * shadowDist), 0.0 , 1.0)) * shadowAlpha, 2.0); + float exclusion = smoothstep(borderThickness, borderThickness - 1.0, dist); // Inner cutout for shadow + + float innerShading = smoothstep(borderThickness -2.0, 0.0 , dist); + + vec4 shadowPart = vec4(shadowColor,shadow) * (1 - exclusion) * v_Colour; + vec4 fillPart = vec4(vec3(max(outline * 0.5, base)), outline) * v_Colour; + + //vec4 stylizedFill = mix(fillPart, v_Colour * 0.85, innerShading); + + return shadowPart + fillPart; +} + +void main(void) { + vec2 resolution = v_TexRect.zw - v_TexRect.xy; + vec2 pixelPos = (v_TexCoord - v_TexRect.xy) / resolution; + + vec2 p = pixelPos * size; + vec2 c = 0.5 * size; + + float shadeRadius = shadowRadius; + float borderThickness = thickness; + float paddingAmount = - borderThickness - shadeRadius; + + vec2 newSize= size + paddingAmount * 2; + + float ringSDF = triangle(p, c, newSize); + + if(shadowOnly) + o_Colour = sdfShadow(ringSDF, borderThickness, shadeRadius); + else if(fillTriangle) + o_Colour = sdfFill(ringSDF, borderThickness, shadeRadius); + else + o_Colour = sdfToShape(ringSDF, borderThickness, shadeRadius); +} +#endif \ No newline at end of file