Merge remote-tracking branch 'upstream/master' into nightcore-beats

This commit is contained in:
Dean Herbert
2019-12-16 18:03:32 +09:00
11 changed files with 160 additions and 137 deletions

View File

@ -53,7 +53,7 @@
<Reference Include="Java.Interop" /> <Reference Include="Java.Interop" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1215.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2019.1215.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Catch.Objects
if (value != null) if (value != null)
{ {
path.ControlPoints.AddRange(value.ControlPoints); path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value; path.ExpectedDistance.Value = value.ExpectedDistance.Value;
} }
} }

View File

@ -0,0 +1,75 @@
// 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.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
/// <summary>
/// A visualisation of the line between two <see cref="PathControlPointPiece"/>s.
/// </summary>
public class PathControlPointConnectionPiece : CompositeDrawable
{
public PathControlPoint ControlPoint;
private readonly Path path;
private readonly Slider slider;
private IBindable<Vector2> sliderPosition;
private IBindable<int> pathVersion;
public PathControlPointConnectionPiece(Slider slider, PathControlPoint controlPoint)
{
this.slider = slider;
ControlPoint = controlPoint;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
InternalChild = path = new SmoothPath
{
Anchor = Anchor.Centre,
PathRadius = 1
};
}
protected override void LoadComplete()
{
base.LoadComplete();
sliderPosition = slider.PositionBindable.GetBoundCopy();
sliderPosition.BindValueChanged(_ => updateConnectingPath());
pathVersion = slider.Path.Version.GetBoundCopy();
pathVersion.BindValueChanged(_ => updateConnectingPath());
updateConnectingPath();
}
/// <summary>
/// Updates the path connecting this control point to the next one.
/// </summary>
private void updateConnectingPath()
{
Position = slider.StackedPosition + ControlPoint.Position.Value;
path.ClearVertices();
int index = slider.Path.ControlPoints.IndexOf(ControlPoint) + 1;
if (index == 0 || index == slider.Path.ControlPoints.Count)
return;
path.AddVertex(Vector2.Zero);
path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -19,6 +18,9 @@ using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{ {
/// <summary>
/// A visualisation of a single <see cref="PathControlPoint"/> in a <see cref="Slider"/>.
/// </summary>
public class PathControlPointPiece : BlueprintPiece<Slider> public class PathControlPointPiece : BlueprintPiece<Slider>
{ {
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection; public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
@ -28,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
public readonly PathControlPoint ControlPoint; public readonly PathControlPoint ControlPoint;
private readonly Slider slider; private readonly Slider slider;
private readonly Path path;
private readonly Container marker; private readonly Container marker;
private readonly Drawable markerRing; private readonly Drawable markerRing;
@ -39,12 +40,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
private OsuColour colours { get; set; } private OsuColour colours { get; set; }
private IBindable<Vector2> sliderPosition; private IBindable<Vector2> sliderPosition;
private IBindable<int> pathVersion; private IBindable<Vector2> controlPointPosition;
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint) public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
{ {
this.slider = slider; this.slider = slider;
ControlPoint = controlPoint; ControlPoint = controlPoint;
Origin = Anchor.Centre; Origin = Anchor.Centre;
@ -52,11 +52,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
path = new SmoothPath
{
Anchor = Anchor.Centre,
PathRadius = 1
},
marker = new Container marker = new Container
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -96,20 +91,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
base.LoadComplete(); base.LoadComplete();
sliderPosition = slider.PositionBindable.GetBoundCopy(); sliderPosition = slider.PositionBindable.GetBoundCopy();
sliderPosition.BindValueChanged(_ => updateDisplay()); sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
pathVersion = slider.Path.Version.GetBoundCopy(); controlPointPosition = ControlPoint.Position.GetBoundCopy();
pathVersion.BindValueChanged(_ => updateDisplay()); controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
IsSelected.BindValueChanged(_ => updateMarkerDisplay()); IsSelected.BindValueChanged(_ => updateMarkerDisplay());
updateDisplay();
}
private void updateDisplay()
{
updateMarkerDisplay(); updateMarkerDisplay();
updateConnectingPath();
} }
// The connecting path is excluded from positional input // The connecting path is excluded from positional input
@ -189,26 +178,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
colour = Color4.White; colour = Color4.White;
marker.Colour = colour; marker.Colour = colour;
} }
/// <summary>
/// Updates the path connecting this control point to the previous one.
/// </summary>
private void updateConnectingPath()
{
path.ClearVertices();
int index = slider.Path.ControlPoints.IndexOf(ControlPoint);
if (index == -1)
return;
if (++index != slider.Path.ControlPoints.Count)
{
path.AddVertex(Vector2.Zero);
path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
}
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
}
} }
} }

View File

@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{ {
internal readonly Container<PathControlPointPiece> Pieces; internal readonly Container<PathControlPointPiece> Pieces;
private readonly Container<PathControlPointConnectionPiece> connections;
private readonly Slider slider; private readonly Slider slider;
private readonly bool allowSelection; private readonly bool allowSelection;
@ -42,7 +44,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
InternalChild = Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both }; InternalChildren = new Drawable[]
{
connections = new Container<PathControlPointConnectionPiece> { RelativeSizeAxes = Axes.Both },
Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both }
};
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -62,19 +68,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{ {
foreach (var point in controlPoints) foreach (var point in controlPoints)
{ {
var piece = new PathControlPointPiece(slider, point); Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
{
if (allowSelection) if (allowSelection)
piece.RequestSelection = selectPiece; d.RequestSelection = selectPiece;
}));
Pieces.Add(piece); connections.Add(new PathControlPointConnectionPiece(slider, point));
} }
} }
private void removeControlPoints(IEnumerable<PathControlPoint> controlPoints) private void removeControlPoints(IEnumerable<PathControlPoint> controlPoints)
{ {
foreach (var point in controlPoints) foreach (var point in controlPoints)
{
Pieces.RemoveAll(p => p.ControlPoint == point); Pieces.RemoveAll(p => p.ControlPoint == point);
connections.RemoveAll(c => c.ControlPoint == point);
}
} }
protected override bool OnClick(ClickEvent e) protected override bool OnClick(ClickEvent e)

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects
if (value != null) if (value != null)
{ {
path.ControlPoints.AddRange(value.ControlPoints); path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value; path.ExpectedDistance.Value = value.ExpectedDistance.Value;
} }
} }

View File

@ -28,18 +28,18 @@ namespace osu.Game.Rulesets.Osu.Skinning
InternalChildren = new[] InternalChildren = new[]
{ {
ExpandTarget = new NonPlayfieldSprite
{
Texture = skin.GetTexture("cursor"),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new NonPlayfieldSprite new NonPlayfieldSprite
{ {
Texture = skin.GetTexture("cursormiddle"), Texture = skin.GetTexture("cursormiddle"),
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
}, },
ExpandTarget = new NonPlayfieldSprite
{
Texture = skin.GetTexture("cursor"),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}; };
} }

View File

@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private void addPart(Vector2 screenSpacePosition) private void addPart(Vector2 screenSpacePosition)
{ {
parts[currentIndex].Position = screenSpacePosition; parts[currentIndex].Position = screenSpacePosition;
parts[currentIndex].Time = time; parts[currentIndex].Time = time + 1;
++parts[currentIndex].InvalidationID; ++parts[currentIndex].InvalidationID;
currentIndex = (currentIndex + 1) % max_sprites; currentIndex = (currentIndex + 1) % max_sprites;
@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly TrailPart[] parts = new TrailPart[max_sprites]; private readonly TrailPart[] parts = new TrailPart[max_sprites];
private Vector2 size; private Vector2 size;
private readonly TrailBatch vertexBatch = new TrailBatch(max_sprites, 1); private readonly QuadBatch<TexturedTrailVertex> vertexBatch = new QuadBatch<TexturedTrailVertex>(max_sprites, 1);
public TrailDrawNode(CursorTrail source) public TrailDrawNode(CursorTrail source)
: base(source) : base(source)
@ -227,23 +227,50 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
shader.Bind(); shader.Bind();
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time); shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
for (int i = 0; i < parts.Length; ++i) RectangleF textureRect = texture.GetTextureRect();
foreach (var part in parts)
{ {
if (parts[i].InvalidationID == -1) if (part.InvalidationID == -1)
continue; continue;
vertexBatch.DrawTime = parts[i].Time; if (time - part.Time >= 1)
continue;
Vector2 pos = parts[i].Position; vertexBatch.Add(new TexturedTrailVertex
{
Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y + size.Y / 2),
TexturePosition = textureRect.BottomLeft,
Colour = DrawColourInfo.Colour.BottomLeft.Linear,
Time = part.Time
});
DrawQuad( vertexBatch.Add(new TexturedTrailVertex
texture, {
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y + size.Y / 2),
DrawColourInfo.Colour, TexturePosition = textureRect.BottomRight,
null, Colour = DrawColourInfo.Colour.BottomRight.Linear,
vertexBatch.AddAction); Time = part.Time
});
vertexBatch.Add(new TexturedTrailVertex
{
Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y - size.Y / 2),
TexturePosition = textureRect.TopRight,
Colour = DrawColourInfo.Colour.TopRight.Linear,
Time = part.Time
});
vertexBatch.Add(new TexturedTrailVertex
{
Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y - size.Y / 2),
TexturePosition = textureRect.TopLeft,
Colour = DrawColourInfo.Colour.TopLeft.Linear,
Time = part.Time
});
} }
vertexBatch.Draw();
shader.Unbind(); shader.Unbind();
} }
@ -253,25 +280,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
vertexBatch.Dispose(); vertexBatch.Dispose();
} }
// Todo: This shouldn't exist, but is currently used to reduce allocations by caching variable-capturing closures.
private class TrailBatch : QuadBatch<TexturedTrailVertex>
{
public new readonly Action<TexturedVertex2D> AddAction;
public float DrawTime;
public TrailBatch(int size, int maxBuffers)
: base(size, maxBuffers)
{
AddAction = v => Add(new TexturedTrailVertex
{
Position = v.Position,
TexturePosition = v.TexturePosition,
Time = DrawTime + 1,
Colour = v.Colour,
});
}
}
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]

View File

@ -3,77 +3,39 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Game.Database; using osu.Game.Database;
namespace osu.Game.Skinning namespace osu.Game.Skinning
{ {
public class LegacySkinResourceStore<T> : IResourceStore<byte[]> public class LegacySkinResourceStore<T> : ResourceStore<byte[]>
where T : INamedFileInfo where T : INamedFileInfo
{ {
private readonly IHasFiles<T> source; private readonly IHasFiles<T> source;
private readonly IResourceStore<byte[]> underlyingStore;
private string getPathForFile(string filename)
{
if (source.Files == null)
return null;
bool hasExtension = filename.Contains('.');
var file = source.Files.Find(f =>
string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), filename, StringComparison.InvariantCultureIgnoreCase));
return file?.FileInfo.StoragePath;
}
public LegacySkinResourceStore(IHasFiles<T> source, IResourceStore<byte[]> underlyingStore) public LegacySkinResourceStore(IHasFiles<T> source, IResourceStore<byte[]> underlyingStore)
: base(underlyingStore)
{ {
this.source = source; this.source = source;
this.underlyingStore = underlyingStore;
} }
public Stream GetStream(string name) protected override IEnumerable<string> GetFilenames(string name)
{ {
string path = getPathForFile(name); if (source.Files == null)
return path == null ? null : underlyingStore.GetStream(path); yield break;
}
public IEnumerable<string> GetAvailableResources() => source.Files.Select(f => f.Filename); foreach (var filename in base.GetFilenames(name))
byte[] IResourceStore<byte[]>.Get(string name) => GetAsync(name).Result;
public Task<byte[]> GetAsync(string name)
{ {
string path = getPathForFile(name); var path = getPathForFile(filename);
return path == null ? Task.FromResult<byte[]>(null) : underlyingStore.GetAsync(path); if (path != null)
} yield return path;
#region IDisposable Support
private bool isDisposed;
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
isDisposed = true;
} }
} }
~LegacySkinResourceStore() private string getPathForFile(string filename) =>
{ source.Files.Find(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath;
Dispose(false);
}
public void Dispose() public override IEnumerable<string> GetAvailableResources() => source.Files.Select(f => f.Filename);
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
} }
} }

View File

@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1215.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.1215.0" />
<PackageReference Include="Sentry" Version="1.2.0" /> <PackageReference Include="Sentry" Version="1.2.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />

View File

@ -73,7 +73,7 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1215.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1215.0" />
</ItemGroup> </ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. --> <!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->