From ccac2e9a7511b72e7d60a0b635ec5ae51ccd7222 Mon Sep 17 00:00:00 2001 From: ColdVolcano Date: Sun, 18 Jun 2017 19:33:50 -0500 Subject: [PATCH] Add a visualizer around the logo --- osu.Game/Screens/Menu/LogoVisualisation.cs | 198 +++++++++++++++++++++ osu.Game/Screens/Menu/MenuVisualisation.cs | 11 -- osu.Game/Screens/Menu/OsuLogo.cs | 21 ++- osu.Game/osu.Game.csproj | 4 +- 4 files changed, 213 insertions(+), 21 deletions(-) create mode 100644 osu.Game/Screens/Menu/LogoVisualisation.cs delete mode 100644 osu.Game/Screens/Menu/MenuVisualisation.cs diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs new file mode 100644 index 0000000000..36b5a672a3 --- /dev/null +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -0,0 +1,198 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Graphics.ES30; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using System; + +namespace osu.Game.Screens.Menu +{ + internal class LogoVisualisation : Drawable, IHasAccentColour + { + private Bindable beatmap = new Bindable(); + private Color4 barColour; + + private const int index_change = 5; + private const int bar_length = 1200; + private const int bars_per_visualizer = 250; + private const int visualizers = 5; + + /// + /// How much should each bar go down each milisecond (based on a full bar) + /// + private const float decay_per_milisecond = 0.0024f; + + private int indexOffset; + + public Color4 AccentColour + { + get + { + return barColour; + } + set + { + //Half alpha makes it look really good! + value.A = 0.5f; + barColour = value; + } + } + + private float[] fftData = new float[256]; + + public override bool HandleInput => false; + + private Shader shader; + private readonly Texture texture; + + public LogoVisualisation() + { + texture = Texture.WhitePixel; + AccentColour = Color4.White; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders, OsuGame game) + { + beatmap.BindTo(game.Beatmap); + shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } + + private void ensureFFTData() + { + float[] temporalFFTData = beatmap?.Value?.Track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256]; + + for (int i = 0; i < bars_per_visualizer; i++) + { + int index = (i + indexOffset) % bars_per_visualizer; + if (beatmap?.Value?.Track?.IsRunning ?? false) + { + if (temporalFFTData[index] > fftData[i]) + fftData[i] = temporalFFTData[index]; + } + else + { + if (fftData[(i + index_change) % bars_per_visualizer] > fftData[i]) + fftData[i] = fftData[(i + index_change) % bars_per_visualizer]; + } + } + indexOffset = (indexOffset + index_change) % bars_per_visualizer; + Scheduler.AddDelayed(ensureFFTData, 50); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + ensureFFTData(); + } + + protected override void Update() + { + base.Update(); + + float decayFactor = (float)Time.Elapsed * decay_per_milisecond; + for (int i = 0; i < bars_per_visualizer; i++) + { + //0.03% of extra bar length to make it a little faster when bar is almost at it's minimum + fftData[i] -= decayFactor * (fftData[i] + 0.03f); + if (fftData[i] < 0) + fftData[i] = 0; + } + + Invalidate(Invalidation.DrawNode, shallPropagate: false); + } + + protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(); + + private readonly VisualizerSharedData sharedData = new VisualizerSharedData(); + protected override void ApplyDrawNode(DrawNode node) + { + base.ApplyDrawNode(node); + + var visNode = (VisualisationDrawNode)node; + + visNode.Shader = shader; + visNode.Texture = texture; + visNode.Size = DrawSize.X; + visNode.Shared = sharedData; + visNode.Colour = barColour; + visNode.AudioData = fftData; + } + + private class VisualizerSharedData + { + public readonly LinearBatch VertexBatch = new LinearBatch(100 * 4, 10, PrimitiveType.Quads); + } + + private class VisualisationDrawNode : DrawNode + { + public Shader Shader; + public Texture Texture; + public VisualizerSharedData Shared; + //Asuming the logo is a circle, we don't need a second dimension. + public float Size; + + public Color4 Colour; + public float[] AudioData; + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + Shader.Bind(); + Texture.TextureGL.Bind(); + + Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; + + ColourInfo colourInfo = DrawInfo.Colour; + colourInfo.ApplyChild(Colour); + + if (AudioData != null) + { + for (int i = 0; i < bars_per_visualizer * visualizers; i++) + { + float rotation = MathHelper.DegreesToRadians(i / (float)bars_per_visualizer * 360 + (i / bars_per_visualizer * (360 / visualizers))); + float rotationCos = (float)Math.Cos(rotation); + float rotationSin = (float)Math.Sin(rotation); + //taking the cos and sin to the 0..1 range + var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * Size; + + var barSize = new Vector2(Size * (float)Math.Sqrt(2 * (1 - Math.Cos(MathHelper.DegreesToRadians(360f / bars_per_visualizer)))) / 2f, bar_length * AudioData[i % bars_per_visualizer]); + //The distance between the position and the sides of the bar. + var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); + //The distance between the bottom side of the bar and the top side. + var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); + + var rectangle = new Quad( + (barPosition - bottomOffset) * DrawInfo.Matrix, + (barPosition - bottomOffset + amplitudeOffset) * DrawInfo.Matrix, + (barPosition + bottomOffset) * DrawInfo.Matrix, + (barPosition + bottomOffset + amplitudeOffset) * DrawInfo.Matrix + ); + + Texture.DrawQuad( + rectangle, + colourInfo, + null, + Shared.VertexBatch.Add, + //barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. + Vector2.Divide(inflation, barSize.Yx)); + } + } + Shader.Unbind(); + } + } + } +} diff --git a/osu.Game/Screens/Menu/MenuVisualisation.cs b/osu.Game/Screens/Menu/MenuVisualisation.cs deleted file mode 100644 index 85c65b460d..0000000000 --- a/osu.Game/Screens/Menu/MenuVisualisation.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; - -namespace osu.Game.Screens.Menu -{ - internal class MenuVisualisation : Drawable - { - } -} diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index db1f008dcf..8459993967 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -34,6 +34,7 @@ public class OsuLogo : BeatSyncedContainer private readonly Container logoBeatContainer; private readonly Container logoAmplitudeContainer; private readonly Container logoHoverContainer; + private readonly LogoVisualisation visualizer; private SampleChannel sampleClick; @@ -120,6 +121,14 @@ public OsuLogo() AutoSizeAxes = Axes.Both, Children = new Drawable[] { + visualizer = new LogoVisualisation + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Alpha = 0.5f, + Size = new Vector2(0.96f) + }, new BufferedContainer { AutoSizeAxes = Axes.Both, @@ -189,14 +198,6 @@ public OsuLogo() Alpha = 0, } } - }, - new MenuVisualisation - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - BlendingMode = BlendingMode.Additive, - Alpha = 0.2f, } } } @@ -246,10 +247,14 @@ protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, if (effectPoint.KiaiMode && flashLayer.Alpha < 0.4f) { flashLayer.ClearTransforms(); + visualizer.ClearTransforms(); flashLayer.FadeTo(0.2f * amplitudeAdjust, beat_in_time, EasingTypes.Out); + visualizer.FadeTo(0.9f * amplitudeAdjust, beat_in_time, EasingTypes.Out); using (flashLayer.BeginDelayedSequence(beat_in_time)) flashLayer.FadeOut(beatLength); + using (visualizer.BeginDelayedSequence(beat_in_time)) + visualizer.FadeTo(0.5f, beatLength); } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 36f358801e..4b1985e3d4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -45,7 +45,7 @@ True - $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll + $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll @@ -276,7 +276,7 @@ - +