Merge branch 'master' into argon-spinner-sides

This commit is contained in:
Bartłomiej Dach
2022-10-23 18:44:04 +02:00
committed by GitHub
62 changed files with 520 additions and 297 deletions

View File

@ -108,18 +108,23 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
{
base.LoadComplete();
accentColour.BindValueChanged(colour =>
{
outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4);
outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f));
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
flash.Colour = colour.NewValue;
}, true);
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
accentColour.BindValueChanged(colour =>
{
// A colour transform is applied.
// Without removing transforms first, when it is rewound it may apply an old colour.
outerGradient.ClearTransforms(targetMember: nameof(Colour));
outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f));
outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4);
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
flash.Colour = colour.NewValue;
updateStateTransforms(drawableObject, drawableObject.State.Value);
}, true);
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
updateStateTransforms(drawableObject, drawableObject.State.Value);
}
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
@ -173,11 +178,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
.FadeOut(flash_in_duration);
}
// The flash layer starts white to give the wanted brightness, but is almost immediately
// recoloured to the accent colour. This would more correctly be done with two layers (one for the initial flash)
// but works well enough with the colour fade.
flash.FadeTo(1, flash_in_duration, Easing.OutQuint);
flash.FlashColour(accentColour.Value, fade_out_time, Easing.OutQuint);
this.FadeOut(fade_out_time, Easing.OutQuad);
break;

View File

@ -134,10 +134,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
switch (state)
{
case ArmedState.Hit:
CircleSprite.FadeOut(legacy_fade_duration, Easing.Out);
CircleSprite.FadeOut(legacy_fade_duration);
CircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
OverlaySprite.FadeOut(legacy_fade_duration, Easing.Out);
OverlaySprite.FadeOut(legacy_fade_duration);
OverlaySprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
if (hasNumber)
@ -146,11 +146,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
if (legacyVersion >= 2.0m)
// legacy skins of version 2.0 and newer only apply very short fade out to the number piece.
hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out);
hitCircleText.FadeOut(legacy_fade_duration / 4);
else
{
// old skins scale and fade it normally along other pieces.
hitCircleText.FadeOut(legacy_fade_duration, Easing.Out);
hitCircleText.FadeOut(legacy_fade_duration);
hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
}
}

View File

@ -107,8 +107,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
this.FadeOut();
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2))
this.FadeInFromZero(spinner.TimeFadeIn / 2);
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn))
this.FadeInFromZero(spinner.TimeFadeIn);
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
{

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
[Resolved(canBeNull: true)]
private DrawableHitObject? parentObject { get; set; }
public Color4 BallColour => animationContent.Colour;
private Sprite layerNd = null!;
private Sprite layerSpec = null!;
@ -61,6 +64,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
};
}
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
protected override void LoadComplete()
{
base.LoadComplete();
@ -69,6 +74,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
parentObject.ApplyCustomUpdateState += updateStateTransforms;
updateStateTransforms(parentObject, parentObject.State.Value);
if (skin.GetConfig<SkinConfiguration.LegacySetting, bool>(SkinConfiguration.LegacySetting.AllowSliderBallTint)?.Value == true)
{
accentColour.BindTo(parentObject.AccentColour);
accentColour.BindValueChanged(a => animationContent.Colour = a.NewValue, true);
}
}
}

View File

@ -65,6 +65,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
spin = new Sprite
{
Alpha = 0,
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-spin"),
@ -82,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
},
bonusCounter = new LegacySpriteText(LegacyFont.Score)
{
Alpha = 0f,
Alpha = 0,
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
Scale = new Vector2(SPRITE_SCALE),
@ -179,6 +180,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
spmCounter.MoveToOffset(new Vector2(0, -spm_hide_offset), d.HitObject.TimeFadeIn, Easing.Out);
}
using (BeginAbsoluteSequence(d.HitObject.StartTime - d.HitObject.TimeFadeIn / 2))
spin.FadeInFromZero(d.HitObject.TimeFadeIn / 2);
using (BeginAbsoluteSequence(d.HitObject.StartTime))
ApproachCircle?.ScaleTo(SPRITE_SCALE * 0.1f, d.HitObject.Duration);

View File

@ -9,7 +9,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
SliderBorderSize,
SliderPathRadius,
AllowSliderBallTint,
CursorCentre,
CursorExpand,
CursorRotate,

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@ -22,8 +23,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
public abstract class SmokeSegment : Drawable, ITexturedShaderDrawable
{
private const int max_point_count = 18_000;
// fade anim values
private const double initial_fade_out_duration = 4000;
@ -85,12 +84,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
totalDistance = pointInterval;
}
private Vector2 nextPointDirection()
{
float angle = RNG.NextSingle(0, 2 * MathF.PI);
return new Vector2(MathF.Sin(angle), -MathF.Cos(angle));
}
public void AddPosition(Vector2 position, double time)
{
lastPosition ??= position;
@ -107,33 +100,27 @@ namespace osu.Game.Rulesets.Osu.Skinning
Vector2 pointPos = (pointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition;
increment *= pointInterval;
if (SmokePoints.Count > 0 && SmokePoints[^1].Time > time)
{
int index = ~SmokePoints.BinarySearch(new SmokePoint { Time = time }, new SmokePoint.UpperBoundComparer());
SmokePoints.RemoveRange(index, SmokePoints.Count - index);
}
totalDistance %= pointInterval;
for (int i = 0; i < count; i++)
if (SmokePoints.Count == 0 || SmokePoints[^1].Time <= time)
{
SmokePoints.Add(new SmokePoint
for (int i = 0; i < count; i++)
{
Position = pointPos,
Time = time,
Direction = nextPointDirection(),
});
SmokePoints.Add(new SmokePoint
{
Position = pointPos,
Time = time,
Angle = RNG.NextSingle(0, 2 * MathF.PI),
});
pointPos += increment;
pointPos += increment;
}
}
Invalidate(Invalidation.DrawNode);
}
lastPosition = position;
if (SmokePoints.Count >= max_point_count)
FinishDrawing(time);
}
public void FinishDrawing(double time)
@ -157,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
public Vector2 Position;
public double Time;
public Vector2 Direction;
public float Angle;
public struct UpperBoundComparer : IComparer<SmokePoint>
{
@ -171,6 +158,17 @@ namespace osu.Game.Rulesets.Osu.Skinning
return x.Time > target.Time ? 1 : -1;
}
}
public struct LowerBoundComparer : IComparer<SmokePoint>
{
public int Compare(SmokePoint x, SmokePoint target)
{
// Similar logic as UpperBoundComparer, except returned index will always be
// the first element larger or equal
return x.Time < target.Time ? -1 : 1;
}
}
}
protected class SmokeDrawNode : TexturedShaderDrawNode
@ -187,11 +185,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
private Vector2 drawSize;
private Texture? texture;
private int rotationSeed;
private int rotationIndex;
private int firstVisiblePointIndex;
// anim calculation vars (color, scale, direction)
private double initialFadeOutDurationTrunc;
private double firstVisiblePointTime;
private double firstVisiblePointTimeAfterSmokeEnded;
private double initialFadeOutTime;
private double reFadeInTime;
@ -206,9 +204,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
base.ApplyState();
points.Clear();
points.AddRange(Source.SmokePoints);
radius = Source.radius;
drawSize = Source.DrawSize;
texture = Source.Texture;
@ -220,11 +215,18 @@ namespace osu.Game.Rulesets.Osu.Skinning
rotationSeed = Source.rotationSeed;
initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime);
firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc;
firstVisiblePointTimeAfterSmokeEnded = SmokeEndTime - initialFadeOutDurationTrunc;
initialFadeOutTime = CurrentTime;
reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / re_fade_in_speed);
finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / final_fade_out_speed);
initialFadeOutTime = Math.Min(CurrentTime, SmokeEndTime);
reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTimeAfterSmokeEnded * (1 - 1 / re_fade_in_speed);
finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTimeAfterSmokeEnded * (1 - 1 / final_fade_out_speed);
double firstVisiblePointTime = Math.Min(SmokeEndTime, CurrentTime) - initialFadeOutDurationTrunc;
firstVisiblePointIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = firstVisiblePointTime }, new SmokePoint.LowerBoundComparer());
int futurePointIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = CurrentTime }, new SmokePoint.UpperBoundComparer());
points.Clear();
points.AddRange(Source.SmokePoints.Skip(firstVisiblePointIndex).Take(futurePointIndex - firstVisiblePointIndex));
}
public sealed override void Draw(IRenderer renderer)
@ -234,9 +236,14 @@ namespace osu.Game.Rulesets.Osu.Skinning
if (points.Count == 0)
return;
rotationIndex = 0;
quadBatch ??= renderer.CreateQuadBatch<TexturedVertex2D>(200, 4);
if (points.Count > quadBatch.Size && quadBatch.Size != IRenderer.MAX_QUADS)
{
int batchSize = Math.Min(quadBatch.Size * 2, IRenderer.MAX_QUADS);
quadBatch = renderer.CreateQuadBatch<TexturedVertex2D>(batchSize, 4);
}
quadBatch ??= renderer.CreateQuadBatch<TexturedVertex2D>(max_point_count / 10, 10);
texture ??= renderer.WhitePixel;
RectangleF textureRect = texture.GetTextureRect();
@ -248,8 +255,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
shader.Bind();
texture.Bind();
foreach (var point in points)
drawPointQuad(point, textureRect);
for (int i = 0; i < points.Count; i++)
drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex);
shader.Unbind();
renderer.PopLocalMatrix();
@ -263,30 +270,34 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
var color = Color4.White;
double timeDoingInitialFadeOut = Math.Min(initialFadeOutTime, SmokeEndTime) - point.Time;
double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed;
if (timeDoingInitialFadeOut > 0)
if (timeDoingFinalFadeOut > 0 && point.Time >= firstVisiblePointTimeAfterSmokeEnded)
{
float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1);
color.A = (1 - fraction) * initial_alpha;
float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1);
fraction = MathF.Pow(fraction, 5);
color.A = (1 - fraction) * re_fade_in_alpha;
}
if (color.A > 0)
else
{
double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed;
double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed;
double timeDoingInitialFadeOut = initialFadeOutTime - point.Time;
if (timeDoingFinalFadeOut > 0)
if (timeDoingInitialFadeOut > 0)
{
float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1);
fraction = MathF.Pow(fraction, 5);
color.A = (1 - fraction) * re_fade_in_alpha;
float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1);
color.A = (1 - fraction) * initial_alpha;
}
else if (timeDoingReFadeIn > 0)
if (point.Time > firstVisiblePointTimeAfterSmokeEnded)
{
float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1);
fraction = 1 - MathF.Pow(1 - fraction, 5);
color.A = fraction * (re_fade_in_alpha - color.A) + color.A;
double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed;
if (timeDoingReFadeIn > 0)
{
float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1);
fraction = 1 - MathF.Pow(1 - fraction, 5);
color.A = fraction * (re_fade_in_alpha - color.A) + color.A;
}
}
}
@ -301,33 +312,33 @@ namespace osu.Game.Rulesets.Osu.Skinning
return fraction * (final_scale - initial_scale) + initial_scale;
}
protected virtual Vector2 PointDirection(SmokePoint point)
protected virtual Vector2 PointDirection(SmokePoint point, int index)
{
float initialAngle = MathF.Atan2(point.Direction.Y, point.Direction.X);
float finalAngle = initialAngle + nextRotation();
double timeDoingRotation = CurrentTime - point.Time;
float fraction = Math.Clamp((float)(timeDoingRotation / rotation_duration), 0, 1);
fraction = 1 - MathF.Pow(1 - fraction, 5);
float angle = fraction * (finalAngle - initialAngle) + initialAngle;
float angle = fraction * getRotation(index) + point.Angle;
return new Vector2(MathF.Sin(angle), -MathF.Cos(angle));
}
private float nextRotation() => max_rotation * (StatelessRNG.NextSingle(rotationSeed, rotationIndex++) * 2 - 1);
private float getRotation(int index) => max_rotation * (StatelessRNG.NextSingle(rotationSeed, index) * 2 - 1);
private void drawPointQuad(SmokePoint point, RectangleF textureRect)
private void drawPointQuad(SmokePoint point, RectangleF textureRect, int index)
{
Debug.Assert(quadBatch != null);
var colour = PointColour(point);
float scale = PointScale(point);
var dir = PointDirection(point);
var ortho = dir.PerpendicularLeft;
if (colour.A == 0 || scale == 0)
if (colour.A == 0)
return;
float scale = PointScale(point);
if (scale == 0)
return;
var dir = PointDirection(point, index);
var ortho = dir.PerpendicularLeft;
var localTopLeft = point.Position + (radius * scale * (-ortho - dir));
var localTopRight = point.Position + (radius * scale * (-ortho + dir));
var localBotLeft = point.Position + (radius * scale * (ortho - dir));