// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System; using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Sprites; using osuTK; using osuTK.Graphics; using osuTK.Graphics.ES30; namespace osu.Game.Graphics.Sprites { public partial class LogoAnimation : Sprite { [BackgroundDependencyLoader] private void load(ShaderManager shaders) { TextureShader = shaders.Load(@"LogoAnimation", @"LogoAnimation"); } private float animationProgress; public float AnimationProgress { get => animationProgress; set { if (animationProgress == value) return; animationProgress = value; Invalidate(Invalidation.DrawInfo); } } public override bool IsPresent => true; protected override DrawNode CreateDrawNode() => new LogoAnimationDrawNode(this); private class LogoAnimationDrawNode : SpriteDrawNode { private LogoAnimation source => (LogoAnimation)Source; private readonly Action addVertexAction; private float progress; public LogoAnimationDrawNode(LogoAnimation source) : base(source) { addVertexAction = v => { animationVertexBatch!.Add(new LogoAnimationVertex { Position = v.Position, Colour = v.Colour, TexturePosition = v.TexturePosition, }); }; } public override void ApplyState() { base.ApplyState(); progress = source.animationProgress; } private IUniformBuffer animationDataBuffer; private IVertexBatch animationVertexBatch; protected override void BindUniformResources(IShader shader, IRenderer renderer) { base.BindUniformResources(shader, renderer); animationDataBuffer ??= renderer.CreateUniformBuffer(); animationVertexBatch ??= renderer.CreateQuadBatch(1, 2); animationDataBuffer.Data = animationDataBuffer.Data with { Progress = progress }; shader.BindUniformBlock(@"m_AnimationData", animationDataBuffer); } protected override void Blit(IRenderer renderer) { if (DrawRectangle.Width == 0 || DrawRectangle.Height == 0) return; base.Blit(renderer); renderer.DrawQuad( Texture, ScreenSpaceDrawQuad, DrawColourInfo.Colour, inflationPercentage: new Vector2(InflationAmount.X / DrawRectangle.Width, InflationAmount.Y / DrawRectangle.Height), textureCoords: TextureCoords, vertexAction: addVertexAction); } protected override bool CanDrawOpaqueInterior => false; protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); animationDataBuffer?.Dispose(); } [StructLayout(LayoutKind.Sequential, Pack = 1)] private record struct AnimationData { public UniformFloat Progress; private readonly UniformPadding12 pad1; } [StructLayout(LayoutKind.Sequential)] private struct LogoAnimationVertex : IEquatable, IVertex { [VertexMember(2, VertexAttribPointerType.Float)] public Vector2 Position; [VertexMember(4, VertexAttribPointerType.Float)] public Color4 Colour; [VertexMember(2, VertexAttribPointerType.Float)] public Vector2 TexturePosition; public readonly bool Equals(LogoAnimationVertex other) => Position.Equals(other.Position) && TexturePosition.Equals(other.TexturePosition) && Colour.Equals(other.Colour); } } } }