diff --git a/osu-framework b/osu-framework index 777996fb97..8baad1b948 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 777996fb9731ba1895a5ab1323cbbc97259ff741 +Subproject commit 8baad1b9484b9f35724e2f965c18cfe710907d80 diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 5cca57be8a..9a19819af8 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -1,20 +1,29 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using OpenTK; using OpenTK.Graphics; using System; +using osu.Framework.Graphics.OpenGL; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using OpenTK.Graphics.ES30; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Allocation; +using System.Collections.Generic; +using osu.Framework.Graphics.Batches; +using osu.Framework.Lists; namespace osu.Game.Graphics.Backgrounds { - public class Triangles : Container + public class Triangles : Drawable { + private const float triangle_size = 100; + public override bool HandleInput => false; public Color4 ColourLight = Color4.White; @@ -49,6 +58,28 @@ namespace osu.Game.Graphics.Backgrounds /// public float Velocity = 1; + private readonly SortedList parts = new SortedList(Comparer.Default); + + private Shader shader; + private readonly Texture texture; + + public Triangles() + { + texture = Texture.WhitePixel; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders) + { + shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + addTriangles(true); + } + public float TriangleScale { get { return triangleScale; } @@ -57,42 +88,71 @@ namespace osu.Game.Graphics.Backgrounds float change = value / triangleScale; triangleScale = value; - if (change != 1) - Children.ForEach(t => t.Scale *= change); + for (int i = 0; i < parts.Count; i++) + { + TriangleParticle newParticle = parts[i]; + newParticle.Scale *= change; + parts[i] = newParticle; + } } } - protected override void LoadComplete() - { - base.LoadComplete(); - - addTriangles(true); - } - - private int aimTriangleCount => (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); - protected override void Update() { base.Update(); - float adjustedAlpha = HideAlphaDiscrepancies ? - // Cubically scale alpha to make it drop off more sharply. - (float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) : - 1; + Invalidate(Invalidation.DrawNode, shallPropagate: false); - foreach (var t in Children) + for (int i = 0; i < parts.Count; i++) { - t.Alpha = adjustedAlpha; - t.Position -= new Vector2(0, (float)(t.Scale.X * (50 / DrawHeight) * (Time.Elapsed / 950)) / triangleScale * Velocity); - if (ExpireOffScreenTriangles && t.DrawPosition.Y + t.DrawSize.Y * t.Scale.Y < 0) - t.Expire(); + TriangleParticle newParticle = parts[i]; + + float adjustedAlpha = HideAlphaDiscrepancies ? + // Cubically scale alpha to make it drop off more sharply. + (float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) : + 1; + + + newParticle.Position += new Vector2(0, -(parts[i].Scale * (50 / DrawHeight)) / triangleScale * Velocity) * ((float)Time.Elapsed / 950); + newParticle.Colour.A = adjustedAlpha; + + parts[i] = newParticle; + + if (!CreateNewTriangles) + continue; + + float bottomPos = parts[i].Position.Y + triangle_size * parts[i].Scale * 0.866f / DrawHeight; + + if (bottomPos < 0) + parts.RemoveAt(i); } - if (CreateNewTriangles) - addTriangles(false); + addTriangles(false); } - protected virtual Triangle CreateTriangle() + private void addTriangles(bool randomY) + { + int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); + + for (int i = 0; i < aimTriangleCount - parts.Count; i++) + parts.Add(createTriangle(randomY)); + } + + private TriangleParticle createTriangle(bool randomY) + { + TriangleParticle particle = CreateTriangle(); + + particle.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() : 1); + particle.Colour = CreateTriangleShade(); + + return particle; + } + + /// + /// Creates a triangle particle with a random scale. + /// + /// The triangle particle. + protected virtual TriangleParticle CreateTriangle() { const float std_dev = 0.16f; const float mean = 0.5f; @@ -102,32 +162,100 @@ namespace osu.Game.Graphics.Backgrounds float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) - const float size = 100; - - return new EquilateralTriangle - { - Origin = Anchor.TopCentre, - RelativePositionAxes = Axes.Both, - Size = new Vector2(size), - Scale = new Vector2(scale), - EdgeSmoothness = new Vector2(1), - Colour = GetTriangleShade(), - Depth = scale, - }; + return new TriangleParticle { Scale = scale }; } - protected virtual Color4 GetTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); + /// + /// Creates a shade of colour for the triangles. + /// + /// The colour. + protected virtual Color4 CreateTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); - private void addTriangles(bool randomY) + protected override DrawNode CreateDrawNode() => new TrianglesDrawNode(); + + private readonly TrianglesDrawNodeSharedData sharedData = new TrianglesDrawNodeSharedData(); + protected override void ApplyDrawNode(DrawNode node) { - int addCount = aimTriangleCount - Children.Count(); - for (int i = 0; i < addCount; i++) + base.ApplyDrawNode(node); + + var trianglesNode = (TrianglesDrawNode)node; + + trianglesNode.Shader = shader; + trianglesNode.Texture = texture; + trianglesNode.Size = DrawSize; + trianglesNode.Shared = sharedData; + + trianglesNode.Parts.Clear(); + trianglesNode.Parts.AddRange(parts); + } + + private class TrianglesDrawNodeSharedData + { + public readonly LinearBatch VertexBatch = new LinearBatch(100 * 3, 10, PrimitiveType.Triangles); + } + + private class TrianglesDrawNode : DrawNode + { + public Shader Shader; + public Texture Texture; + + public TrianglesDrawNodeSharedData Shared; + + public readonly List Parts = new List(); + public Vector2 Size; + + public override void Draw(Action vertexAction) { - var sprite = CreateTriangle(); - float triangleHeight = sprite.DrawHeight / DrawHeight; - sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1); - Add(sprite); + base.Draw(vertexAction); + + Shader.Bind(); + Texture.TextureGL.Bind(); + + foreach (TriangleParticle particle in Parts) + { + var offset = new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f); + + var triangle = new Triangle( + particle.Position * Size * DrawInfo.Matrix, + (particle.Position * Size + offset * triangle_size) * DrawInfo.Matrix, + (particle.Position * Size + new Vector2(-offset.X, offset.Y) * triangle_size) * DrawInfo.Matrix + ); + + ColourInfo colourInfo = DrawInfo.Colour; + colourInfo.ApplyChild(particle.Colour); + + Texture.DrawTriangle(triangle, colourInfo, null, Shared.VertexBatch.Add); + } + + Shader.Unbind(); } } + + protected struct TriangleParticle : IComparable + { + /// + /// The position of the top vertex of the triangle. + /// + public Vector2 Position; + + /// + /// The colour of the triangle. + /// + public Color4 Colour; + + /// + /// The scale of the triangle. + /// + public float Scale; + + /// + /// Compares two s. This is a reverse comparer because when the + /// triangles are added to the particles list, they should be drawn from largest to smallest + /// such that the smaller triangles appear on top. + /// + /// + /// + public int CompareTo(TriangleParticle other) => other.Scale.CompareTo(Scale); + } } }