mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 23:23:52 +08:00
Full legacy smoke implementation and temp default smoke
This commit is contained in:
parent
493efd84a3
commit
613564b5b9
@ -1,14 +1,61 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Rendering;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||||
{
|
{
|
||||||
public class DefaultSmoke : Smoke
|
public class DefaultSmoke : Smoke
|
||||||
{
|
{
|
||||||
|
private const double fade_out_delay = 8000;
|
||||||
|
private const double fade_out_speed = 3;
|
||||||
|
private const double fade_out_duration = 50;
|
||||||
|
private const float alpha = 0.5f;
|
||||||
|
|
||||||
|
protected override double LifetimeAfterSmokeEnd => fade_out_delay + fade_out_duration + (SmokeEndTime - SmokeStartTime) / fade_out_speed;
|
||||||
|
|
||||||
public DefaultSmoke()
|
public DefaultSmoke()
|
||||||
{
|
{
|
||||||
Texture = null;
|
Radius = 2;
|
||||||
PathRadius = 2;
|
}
|
||||||
|
|
||||||
|
protected override DrawNode CreateDrawNode() => new DefaultSmokeDrawNode(this);
|
||||||
|
|
||||||
|
private class DefaultSmokeDrawNode : SmokeDrawNode
|
||||||
|
{
|
||||||
|
private double fadeOutTime;
|
||||||
|
|
||||||
|
public DefaultSmokeDrawNode(ITexturedShaderDrawable source)
|
||||||
|
: base(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateDrawVariables(IRenderer renderer)
|
||||||
|
{
|
||||||
|
base.UpdateDrawVariables(renderer);
|
||||||
|
|
||||||
|
fadeOutTime = SmokeStartTime + fade_out_speed * (CurrentTime - (SmokeEndTime + fade_out_delay));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Color4 ColorAtTime(double pointTime)
|
||||||
|
{
|
||||||
|
var color = Color4.White;
|
||||||
|
color.A = alpha;
|
||||||
|
|
||||||
|
double timeDoingFadeOut = fadeOutTime - pointTime;
|
||||||
|
if (timeDoingFadeOut > 0)
|
||||||
|
{
|
||||||
|
float fraction = Math.Clamp((float)(1 - (timeDoingFadeOut / fade_out_duration)), 0, 1);
|
||||||
|
fraction = MathF.Pow(fraction, 5);
|
||||||
|
color.A *= fraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,42 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Rendering;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
{
|
{
|
||||||
public class LegacySmoke : Smoke
|
public class LegacySmoke : Smoke
|
||||||
{
|
{
|
||||||
|
private const double initial_fade_out_duration = 2500;
|
||||||
|
|
||||||
|
private const double re_fade_in_speed = 3;
|
||||||
|
private const double re_fade_in_duration = 50;
|
||||||
|
|
||||||
|
private const double final_fade_out_duration = 7500;
|
||||||
|
|
||||||
|
private const float initial_alpha = 0.8f;
|
||||||
|
private const float re_fade_in_alpha = 1.4f;
|
||||||
|
|
||||||
|
protected override double LifetimeAfterSmokeEnd
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime);
|
||||||
|
return final_fade_out_duration + initialFadeOutDurationTrunc * (1 + re_fade_in_speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ISkin skin;
|
private ISkin skin;
|
||||||
|
|
||||||
public LegacySmoke(ISkin skin)
|
public LegacySmoke(ISkin skin)
|
||||||
{
|
{
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
|
Radius = 3;
|
||||||
PathRadius = 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -22,5 +45,71 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
Texture = skin.GetTexture("cursor-smoke");
|
Texture = skin.GetTexture("cursor-smoke");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override DrawNode CreateDrawNode() => new LegacySmokeDrawNode(this);
|
||||||
|
|
||||||
|
protected class LegacySmokeDrawNode : SmokeDrawNode
|
||||||
|
{
|
||||||
|
private double initialFadeOutDurationTrunc;
|
||||||
|
private double initialFadeOutTime;
|
||||||
|
private double reFadeInTime;
|
||||||
|
private double finalFadeOutTime;
|
||||||
|
|
||||||
|
public LegacySmokeDrawNode(ITexturedShaderDrawable source)
|
||||||
|
: base(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ApplyState()
|
||||||
|
{
|
||||||
|
base.ApplyState();
|
||||||
|
|
||||||
|
initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateDrawVariables(IRenderer renderer)
|
||||||
|
{
|
||||||
|
base.UpdateDrawVariables(renderer);
|
||||||
|
|
||||||
|
initialFadeOutTime = Math.Min(CurrentTime, SmokeEndTime);
|
||||||
|
reFadeInTime = re_fade_in_speed * (CurrentTime - SmokeEndTime) + SmokeEndTime - initialFadeOutDurationTrunc;
|
||||||
|
finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc * (1 + 1 / re_fade_in_speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Color4 ColorAtTime(double pointTime)
|
||||||
|
{
|
||||||
|
var color = Color4.White;
|
||||||
|
|
||||||
|
double timeDoingInitialFadeOut = initialFadeOutTime - pointTime;
|
||||||
|
|
||||||
|
if (timeDoingInitialFadeOut > 0)
|
||||||
|
{
|
||||||
|
float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1);
|
||||||
|
fraction = MathF.Pow(fraction, 5);
|
||||||
|
color.A = (1 - fraction) * initial_alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color.A > 0)
|
||||||
|
{
|
||||||
|
double timeDoingReFadeIn = reFadeInTime - pointTime;
|
||||||
|
double timeDoingFinalFadeOut = finalFadeOutTime - pointTime;
|
||||||
|
|
||||||
|
if (timeDoingFinalFadeOut > 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;
|
||||||
|
}
|
||||||
|
else 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,175 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Lines;
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Framework.Graphics.Rendering;
|
||||||
|
using osu.Framework.Graphics.Rendering.Vertices;
|
||||||
|
using osu.Framework.Graphics.Shaders;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning
|
namespace osu.Game.Rulesets.Osu.Skinning
|
||||||
{
|
{
|
||||||
public abstract class Smoke : TexturedPath
|
public abstract class Smoke : Drawable, ITexturedShaderDrawable
|
||||||
{
|
{
|
||||||
protected bool IsActive { get; private set; }
|
public IShader? TextureShader { get; private set; }
|
||||||
protected double SmokeTimeStart { get; private set; } = double.MinValue;
|
public IShader? RoundedTextureShader { get; private set; }
|
||||||
protected double SmokeTimeEnd { get; private set; } = double.MinValue;
|
|
||||||
|
|
||||||
protected readonly List<Vector2> SmokeVertexPositions = new List<Vector2>();
|
private float radius = 1;
|
||||||
protected readonly List<double> SmokeVertexTimes = new List<double>();
|
protected float Radius
|
||||||
|
{
|
||||||
|
get => radius;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (radius == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
radius = value;
|
||||||
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int rotationSeed = RNG.Next();
|
||||||
|
protected int RotationSeed
|
||||||
|
{
|
||||||
|
get => rotationSeed;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (rotationSeed == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rotationSeed = value;
|
||||||
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Texture? texture;
|
||||||
|
protected Texture? Texture
|
||||||
|
{
|
||||||
|
get => texture;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
texture = value;
|
||||||
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double smokeTimeStart = double.MinValue;
|
||||||
|
protected double SmokeStartTime
|
||||||
|
{
|
||||||
|
get => smokeTimeStart;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (smokeTimeStart == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
smokeTimeStart = value;
|
||||||
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double smokeTimeEnd = double.MaxValue;
|
||||||
|
protected double SmokeEndTime
|
||||||
|
{
|
||||||
|
get => smokeTimeEnd;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (smokeTimeEnd == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
smokeTimeEnd = value;
|
||||||
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IFrameBasedClock Clock
|
||||||
|
{
|
||||||
|
get => base.Clock;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.Clock = value;
|
||||||
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 topLeft;
|
||||||
|
protected Vector2 TopLeft
|
||||||
|
{
|
||||||
|
get => topLeft;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (topLeft == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
topLeft = value;
|
||||||
|
Invalidate(Invalidation.All);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 bottomRight;
|
||||||
|
protected Vector2 BottomRight
|
||||||
|
{
|
||||||
|
get => bottomRight;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (bottomRight == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bottomRight = value;
|
||||||
|
Invalidate(Invalidation.Layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract double LifetimeAfterSmokeEnd { get; }
|
||||||
|
protected virtual float PointInterval => Radius * 7f / 8;
|
||||||
|
protected bool IsActive { get; private set; }
|
||||||
|
|
||||||
|
protected readonly List<SmokePoint> SmokePoints = new List<SmokePoint>();
|
||||||
|
|
||||||
|
private float totalDistance;
|
||||||
|
private Vector2? lastPosition = null;
|
||||||
|
|
||||||
|
private const double max_duration = 60_000;
|
||||||
|
|
||||||
|
public override float Height
|
||||||
|
{
|
||||||
|
get => base.Height = BottomRight.Y - TopLeft.Y;
|
||||||
|
set => throw new InvalidOperationException($"Cannot manually set {nameof(Height)} of {nameof(Smoke)}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float Width
|
||||||
|
{
|
||||||
|
get => base.Width = BottomRight.X - TopLeft.X;
|
||||||
|
set => throw new InvalidOperationException($"Cannot manually set {nameof(Width)} of {nameof(Smoke)}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Vector2 Size
|
||||||
|
{
|
||||||
|
get => base.Size = BottomRight - TopLeft;
|
||||||
|
set => throw new InvalidOperationException($"Cannot manually set {nameof(Size)} of {nameof(Smoke)}.");
|
||||||
|
}
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private SmokeContainer? smokeContainer { get; set; }
|
private SmokeContainer? smokeContainer { get; set; }
|
||||||
|
|
||||||
protected struct SmokePoint
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ShaderManager shaders)
|
||||||
{
|
{
|
||||||
public Vector2 Position;
|
RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
|
||||||
public double Time;
|
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -36,72 +178,107 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
if (smokeContainer != null)
|
if (smokeContainer != null)
|
||||||
{
|
{
|
||||||
smokeContainer.SmokeMoved += guardOnSmokeMoved;
|
smokeContainer.SmokeMoved += onSmokeMoved;
|
||||||
smokeContainer.SmokeEnded += guardOnSmokeEnded;
|
smokeContainer.SmokeEnded += onSmokeEnded;
|
||||||
IsActive = true;
|
IsActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Anchor = Anchor.TopLeft;
|
Anchor = Anchor.TopLeft;
|
||||||
Origin = Anchor.TopLeft;
|
Origin = Anchor.TopLeft;
|
||||||
|
|
||||||
SmokeTimeStart = Time.Current;
|
SmokeStartTime = Time.Current;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void guardOnSmokeMoved(Vector2 position, double time)
|
private void onSmokeMoved(Vector2 position, double time)
|
||||||
{
|
{
|
||||||
if (IsActive)
|
if (!IsActive)
|
||||||
OnSmokeMoved(position, time);
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
private void guardOnSmokeEnded(double time)
|
lastPosition ??= position;
|
||||||
{
|
|
||||||
if (IsActive)
|
|
||||||
OnSmokeEnded(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnSmokeMoved(Vector2 position, double time)
|
float delta = (position - (Vector2)lastPosition).LengthFast;
|
||||||
{
|
totalDistance += delta;
|
||||||
addSmokeVertex(position, time);
|
int count = (int)(totalDistance / PointInterval);
|
||||||
}
|
|
||||||
|
|
||||||
private void addSmokeVertex(Vector2 position, double time)
|
if (count > 0)
|
||||||
{
|
|
||||||
Debug.Assert(SmokeVertexTimes.Count == SmokeVertexPositions.Count);
|
|
||||||
|
|
||||||
if (SmokeVertexTimes.Count > 0 && SmokeVertexTimes.Last() > time)
|
|
||||||
{
|
{
|
||||||
int index = ~SmokeVertexTimes.BinarySearch(time, new UpperBoundComparer());
|
Vector2 increment = position - (Vector2)lastPosition;
|
||||||
|
increment.NormalizeFast();
|
||||||
|
|
||||||
SmokeVertexTimes.RemoveRange(index, SmokeVertexTimes.Count - index);
|
Vector2 pointPos = (PointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition;
|
||||||
SmokeVertexPositions.RemoveRange(index, SmokeVertexPositions.Count - index);
|
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);
|
||||||
|
recalculateBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
totalDistance %= PointInterval;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
SmokePoints.Add(new SmokePoint
|
||||||
|
{
|
||||||
|
Position = pointPos,
|
||||||
|
Time = time,
|
||||||
|
});
|
||||||
|
|
||||||
|
pointPos += increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
adaptBounds(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
SmokeVertexTimes.Add(time);
|
lastPosition = position;
|
||||||
SmokeVertexPositions.Add(position);
|
|
||||||
|
if (time - SmokeStartTime > max_duration)
|
||||||
|
onSmokeEnded(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnSmokeEnded(double time)
|
private void recalculateBounds()
|
||||||
{
|
{
|
||||||
IsActive = false;
|
TopLeft = BottomRight = Vector2.Zero;
|
||||||
SmokeTimeEnd = time;
|
|
||||||
|
foreach (var point in SmokePoints)
|
||||||
|
adaptBounds(point.Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void adaptBounds(Vector2 position)
|
||||||
|
{
|
||||||
|
if (position.X < TopLeft.X)
|
||||||
|
TopLeft = new Vector2(position.X, TopLeft.Y);
|
||||||
|
else if (position.X > BottomRight.X)
|
||||||
|
BottomRight = new Vector2(position.X, BottomRight.Y);
|
||||||
|
|
||||||
|
if (position.Y < TopLeft.Y)
|
||||||
|
TopLeft = new Vector2(TopLeft.X, position.Y);
|
||||||
|
else if (position.Y > BottomRight.Y)
|
||||||
|
BottomRight = new Vector2(BottomRight.X, position.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSmokeEnded(double time)
|
||||||
|
{
|
||||||
|
if (!IsActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsActive = false;
|
||||||
|
SmokeEndTime = time;
|
||||||
|
LifetimeEnd = time + LifetimeAfterSmokeEnd + 100;
|
||||||
|
|
||||||
|
// TODO: HYPER MEGA JANK WTF??
|
||||||
|
if (Parent is SkinnableDrawable)
|
||||||
|
Parent.LifetimeEnd = LifetimeEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract override DrawNode CreateDrawNode();
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
const double visible_duration = 8000;
|
Position = TopLeft;
|
||||||
const float disappear_speed = 3;
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
if (!IsActive)
|
|
||||||
{
|
|
||||||
double cutoffTime = SmokeTimeStart + disappear_speed * (Time.Current - (SmokeTimeEnd + visible_duration));
|
|
||||||
index = ~SmokeVertexTimes.BinarySearch(cutoffTime, new UpperBoundComparer());
|
|
||||||
}
|
|
||||||
Vertices = new List<Vector2>(SmokeVertexPositions.Skip(index));
|
|
||||||
|
|
||||||
Position = -PositionInBoundingBox(Vector2.Zero);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
@ -110,21 +287,163 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
if (smokeContainer != null)
|
if (smokeContainer != null)
|
||||||
{
|
{
|
||||||
smokeContainer.SmokeMoved -= guardOnSmokeMoved;
|
smokeContainer.SmokeMoved -= onSmokeMoved;
|
||||||
smokeContainer.SmokeEnded -= guardOnSmokeEnded;
|
smokeContainer.SmokeEnded -= onSmokeEnded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct UpperBoundComparer : IComparer<double>
|
protected struct SmokePoint
|
||||||
{
|
{
|
||||||
public int Compare(double x, double target)
|
public Vector2 Position;
|
||||||
{
|
public double Time;
|
||||||
// By returning -1 when the target value is equal to x, guarantees that the
|
|
||||||
// element at BinarySearch's returned index will always be the first element
|
|
||||||
// larger. Since 0 is never returned, the target is never "found", so the return
|
|
||||||
// value will be the index's complement.
|
|
||||||
|
|
||||||
return x > target ? 1 : -1;
|
public struct UpperBoundComparer : IComparer<SmokePoint>
|
||||||
|
{
|
||||||
|
public int Compare(SmokePoint x, SmokePoint target)
|
||||||
|
{
|
||||||
|
// By returning -1 when the target value is equal to x, guarantees that the
|
||||||
|
// element at BinarySearch's returned index will always be the first element
|
||||||
|
// larger. Since 0 is never returned, the target is never "found", so the return
|
||||||
|
// value will be the index's complement.
|
||||||
|
|
||||||
|
return x.Time > target.Time ? 1 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract class SmokeDrawNode : TexturedShaderDrawNode
|
||||||
|
{
|
||||||
|
protected new Smoke Source => (Smoke)base.Source;
|
||||||
|
|
||||||
|
protected IVertexBatch<TexturedVertex2D>? QuadBatch;
|
||||||
|
|
||||||
|
protected readonly List<SmokePoint> Points = new List<SmokePoint>();
|
||||||
|
|
||||||
|
protected float Radius;
|
||||||
|
protected Vector2 DrawSize;
|
||||||
|
protected Vector2 PositionOffset;
|
||||||
|
protected Texture? Texture;
|
||||||
|
|
||||||
|
protected double SmokeStartTime;
|
||||||
|
protected double SmokeEndTime;
|
||||||
|
protected double CurrentTime;
|
||||||
|
|
||||||
|
protected RectangleF TextureRect;
|
||||||
|
|
||||||
|
private IFrameBasedClock? clock;
|
||||||
|
|
||||||
|
private int rotationSeed;
|
||||||
|
private Random rotationRNG = new Random();
|
||||||
|
|
||||||
|
public SmokeDrawNode(ITexturedShaderDrawable source)
|
||||||
|
: base(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ApplyState()
|
||||||
|
{
|
||||||
|
base.ApplyState();
|
||||||
|
|
||||||
|
Points.Clear();
|
||||||
|
Points.AddRange(Source.SmokePoints);
|
||||||
|
|
||||||
|
Radius = Source.Radius;
|
||||||
|
DrawSize = Source.DrawSize;
|
||||||
|
PositionOffset = Source.TopLeft;
|
||||||
|
Texture = Source.Texture;
|
||||||
|
clock = Source.Clock;
|
||||||
|
|
||||||
|
SmokeStartTime = Source.SmokeStartTime;
|
||||||
|
SmokeEndTime = Source.SmokeEndTime;
|
||||||
|
|
||||||
|
rotationSeed = Source.RotationSeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed override void Draw(IRenderer renderer)
|
||||||
|
{
|
||||||
|
base.Draw(renderer);
|
||||||
|
|
||||||
|
if (Points.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QuadBatch ??= renderer.CreateQuadBatch<TexturedVertex2D>(7200, 10);
|
||||||
|
Texture ??= renderer.WhitePixel;
|
||||||
|
|
||||||
|
var shader = GetAppropriateShader(renderer);
|
||||||
|
shader.Bind();
|
||||||
|
Texture.Bind();
|
||||||
|
|
||||||
|
UpdateDrawVariables(renderer);
|
||||||
|
UpdateVertexBuffer();
|
||||||
|
|
||||||
|
shader.Unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color4 ColorAtPosition(Vector2 localPos) => DrawColourInfo.Colour.HasSingleColour
|
||||||
|
? ((SRGBColour)DrawColourInfo.Colour).Linear
|
||||||
|
: DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, DrawSize)).Linear;
|
||||||
|
|
||||||
|
protected abstract Color4 ColorAtTime(double pointTime);
|
||||||
|
|
||||||
|
protected virtual void UpdateDrawVariables(IRenderer renderer)
|
||||||
|
{
|
||||||
|
Debug.Assert(clock != null);
|
||||||
|
Debug.Assert(Texture != null);
|
||||||
|
|
||||||
|
CurrentTime = clock.CurrentTime;
|
||||||
|
TextureRect = Texture.GetTextureRect();
|
||||||
|
rotationRNG = new Random(rotationSeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateVertexBuffer()
|
||||||
|
{
|
||||||
|
foreach (var point in Points)
|
||||||
|
drawPointQuad(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 nextTextureDirection()
|
||||||
|
{
|
||||||
|
float angle = (float)rotationRNG.NextDouble() * 2 * MathF.PI;
|
||||||
|
return new Vector2(MathF.Sin(angle), -MathF.Cos(angle));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawPointQuad(SmokePoint point)
|
||||||
|
{
|
||||||
|
Debug.Assert(QuadBatch != null);
|
||||||
|
|
||||||
|
var color = ColorAtTime(point.Time);
|
||||||
|
var dir = nextTextureDirection();
|
||||||
|
var ortho = dir.PerpendicularLeft;
|
||||||
|
|
||||||
|
var localTopLeft = point.Position + (Radius * (-ortho - dir)) - PositionOffset;
|
||||||
|
var localTopRight = point.Position + (Radius * (-ortho + dir)) - PositionOffset;
|
||||||
|
var localBotLeft = point.Position + (Radius * (ortho - dir)) - PositionOffset;
|
||||||
|
var localBotRight = point.Position + (Radius * (ortho + dir)) - PositionOffset;
|
||||||
|
|
||||||
|
QuadBatch.Add(new TexturedVertex2D
|
||||||
|
{
|
||||||
|
Position = Vector2Extensions.Transform(localTopLeft, DrawInfo.Matrix),
|
||||||
|
TexturePosition = TextureRect.TopLeft,
|
||||||
|
Colour = Color4Extensions.Multiply(ColorAtPosition(localTopLeft), color),
|
||||||
|
});
|
||||||
|
QuadBatch.Add(new TexturedVertex2D
|
||||||
|
{
|
||||||
|
Position = Vector2Extensions.Transform(localTopRight, DrawInfo.Matrix),
|
||||||
|
TexturePosition = TextureRect.TopRight,
|
||||||
|
Colour = Color4Extensions.Multiply(ColorAtPosition(localTopRight), color),
|
||||||
|
});
|
||||||
|
QuadBatch.Add(new TexturedVertex2D
|
||||||
|
{
|
||||||
|
Position = Vector2Extensions.Transform(localBotRight, DrawInfo.Matrix),
|
||||||
|
TexturePosition = TextureRect.BottomRight,
|
||||||
|
Colour = Color4Extensions.Multiply(ColorAtPosition(localBotRight), color),
|
||||||
|
});
|
||||||
|
QuadBatch.Add(new TexturedVertex2D
|
||||||
|
{
|
||||||
|
Position = Vector2Extensions.Transform(localBotLeft, DrawInfo.Matrix),
|
||||||
|
TexturePosition = TextureRect.BottomLeft,
|
||||||
|
Colour = Color4Extensions.Multiply(ColorAtPosition(localBotLeft), color),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
|
Loading…
Reference in New Issue
Block a user