A game framework written with osu! in mind.
at master 189 lines 7.0 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using osu.Framework.Allocation; 6using osu.Framework.Graphics.Colour; 7using osu.Framework.Graphics.Containers; 8using osu.Framework.Graphics.OpenGL; 9using osu.Framework.Graphics.OpenGL.Vertices; 10using osu.Framework.Graphics.Primitives; 11using osu.Framework.Graphics.Shaders; 12 13namespace osu.Framework.Graphics.Sprites 14{ 15 /// <summary> 16 /// A view that displays the contents of a <see cref="BufferedContainer{T}"/>. 17 /// </summary> 18 public class BufferedContainerView<T> : Drawable, ITexturedShaderDrawable 19 where T : Drawable 20 { 21 public IShader TextureShader { get; private set; } 22 public IShader RoundedTextureShader { get; private set; } 23 24 private BufferedContainer<T> container; 25 private BufferedDrawNodeSharedData sharedData; 26 27 internal BufferedContainerView(BufferedContainer<T> container, BufferedDrawNodeSharedData sharedData) 28 { 29 this.container = container; 30 this.sharedData = sharedData; 31 32 container.OnDispose += removeContainer; 33 } 34 35 [BackgroundDependencyLoader] 36 private void load(ShaderManager shaders) 37 { 38 TextureShader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE); 39 RoundedTextureShader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); 40 } 41 42 protected override DrawNode CreateDrawNode() => new BufferSpriteDrawNode(this); 43 44 private bool synchronisedDrawQuad; 45 46 /// <summary> 47 /// Whether this <see cref="BufferedContainerView{T}"/> should be drawn using the original <see cref="BufferedContainer{T}"/>'s draw quad. 48 /// </summary> 49 /// <remarks> 50 /// This can be useful to display the <see cref="BufferedContainer{T}"/> as an overlay on top of itself. 51 /// </remarks> 52 public bool SynchronisedDrawQuad 53 { 54 get => synchronisedDrawQuad; 55 set 56 { 57 if (value == synchronisedDrawQuad) 58 return; 59 60 synchronisedDrawQuad = value; 61 62 Invalidate(Invalidation.DrawNode); 63 } 64 } 65 66 private bool displayOriginalEffects; 67 68 /// <summary> 69 /// Whether the effects drawn by the <see cref="BufferedContainer{T}"/> should also be drawn for this view. 70 /// </summary> 71 public bool DisplayOriginalEffects 72 { 73 get => displayOriginalEffects; 74 set 75 { 76 if (displayOriginalEffects == value) 77 return; 78 79 displayOriginalEffects = value; 80 81 Invalidate(Invalidation.DrawNode); 82 } 83 } 84 85 private void removeContainer() 86 { 87 if (container == null) 88 return; 89 90 container.OnDispose -= removeContainer; 91 92 container = null; 93 sharedData = null; 94 95 Invalidate(Invalidation.DrawNode); 96 } 97 98 protected override void Dispose(bool isDisposing) 99 { 100 base.Dispose(isDisposing); 101 102 removeContainer(); 103 } 104 105 private class BufferSpriteDrawNode : TexturedShaderDrawNode 106 { 107 protected new BufferedContainerView<T> Source => (BufferedContainerView<T>)base.Source; 108 109 private Quad screenSpaceDrawQuad; 110 private BufferedDrawNodeSharedData shared; 111 private bool displayOriginalEffects; 112 113 private bool sourceDrawsOriginal; 114 private ColourInfo sourceEffectColour; 115 private BlendingParameters sourceEffectBlending; 116 private EffectPlacement sourceEffectPlacement; 117 118 public BufferSpriteDrawNode(BufferedContainerView<T> source) 119 : base(source) 120 { 121 } 122 123 public override void ApplyState() 124 { 125 base.ApplyState(); 126 127 screenSpaceDrawQuad = Source.synchronisedDrawQuad ? Source.container.ScreenSpaceDrawQuad : Source.ScreenSpaceDrawQuad; 128 shared = Source.sharedData; 129 130 displayOriginalEffects = Source.displayOriginalEffects; 131 sourceDrawsOriginal = Source.container.DrawOriginal; 132 sourceEffectColour = Source.container.EffectColour; 133 sourceEffectBlending = Source.container.DrawEffectBlending; 134 sourceEffectPlacement = Source.container.EffectPlacement; 135 } 136 137 public override void Draw(Action<TexturedVertex2D> vertexAction) 138 { 139 base.Draw(vertexAction); 140 141 if (shared?.MainBuffer?.Texture?.Available != true || shared.DrawVersion == -1) 142 return; 143 144 Shader.Bind(); 145 146 if (sourceEffectPlacement == EffectPlacement.InFront) 147 drawMainBuffer(vertexAction); 148 149 drawEffectBuffer(vertexAction); 150 151 if (sourceEffectPlacement == EffectPlacement.Behind) 152 drawMainBuffer(vertexAction); 153 154 Shader.Unbind(); 155 } 156 157 private void drawMainBuffer(Action<TexturedVertex2D> vertexAction) 158 { 159 // If the original was drawn, draw it. 160 // Otherwise, if an effect will also not be drawn then we still need to display something - the original. 161 // Keep in mind that the effect MAY be the original itself, but is drawn through drawEffectBuffer(). 162 if (!sourceDrawsOriginal && shouldDrawEffectBuffer) 163 return; 164 165 GLWrapper.SetBlend(DrawColourInfo.Blending); 166 DrawFrameBuffer(shared.MainBuffer, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction); 167 } 168 169 private void drawEffectBuffer(Action<TexturedVertex2D> vertexAction) 170 { 171 if (!shouldDrawEffectBuffer) 172 return; 173 174 GLWrapper.SetBlend(sourceEffectBlending); 175 ColourInfo finalEffectColour = DrawColourInfo.Colour; 176 finalEffectColour.ApplyChild(sourceEffectColour); 177 178 DrawFrameBuffer(shared.CurrentEffectBuffer, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction); 179 } 180 181 /// <summary> 182 /// Whether the source's current effect buffer should be drawn. 183 /// This is true if we explicitly want to draw it or if no effects were drawn by the source. In the case that no effects were drawn by the source, 184 /// the current effect buffer will be the main buffer, and what will be drawn is the main buffer with the effect blending applied. 185 /// </summary> 186 private bool shouldDrawEffectBuffer => displayOriginalEffects || shared.CurrentEffectBuffer == shared.MainBuffer; 187 } 188 } 189}