diff --git a/engine/Sandbox.Engine/Resources/Textures/Texture.cs b/engine/Sandbox.Engine/Resources/Textures/Texture.cs index 848fe311..a04be719 100644 --- a/engine/Sandbox.Engine/Resources/Textures/Texture.cs +++ b/engine/Sandbox.Engine/Resources/Textures/Texture.cs @@ -32,6 +32,11 @@ public partial class Texture : Resource, IDisposable public override bool IsValid => native.IsValid; + /// + /// Flags providing hints about this texture + /// + public TextureFlags Flags { get; set; } = TextureFlags.None; + /// /// Private constructor, use /// @@ -304,3 +309,16 @@ public partial class Texture : Resource, IDisposable IsLoaded = true; } } + +/// +/// Flags providing hints about a texture +/// +public enum TextureFlags +{ + None = 0, + + /// + /// Hint that this texture has pre-multiplied alpha + /// + PremultipliedAlpha = 1 << 0, +} diff --git a/engine/Sandbox.Engine/Systems/Render/BlendMode.cs b/engine/Sandbox.Engine/Systems/Render/BlendMode.cs index bac55285..da7a159a 100644 --- a/engine/Sandbox.Engine/Systems/Render/BlendMode.cs +++ b/engine/Sandbox.Engine/Systems/Render/BlendMode.cs @@ -13,13 +13,18 @@ // Screen = 2, // Overlay = 3, // Darken = 4, - Lighten = 2, // 5 - // ColorDodge = 6, - // ColorBurn = 7, - // HardLight = 8, - // SoftLight = 9, - // Difference = 10, - // Exclusion = 11, + Lighten = 2, + PremultipliedAlpha = 3, + // 5 + // ColorDodge = 6, + // ColorBurn = 7, + // HardLight = 8, + // SoftLight = 9, + // Difference = 10, + // Exclusion = 11, + + + } } diff --git a/engine/Sandbox.Engine/Systems/UI/Controls/WebPanel.cs b/engine/Sandbox.Engine/Systems/UI/Controls/WebPanel.cs index 827b6cb8..74aa6c95 100644 --- a/engine/Sandbox.Engine/Systems/UI/Controls/WebPanel.cs +++ b/engine/Sandbox.Engine/Systems/UI/Controls/WebPanel.cs @@ -1,4 +1,5 @@ -using System.Web; +using Microsoft.AspNetCore.Components; +using System.Web; namespace Sandbox.UI; @@ -12,7 +13,7 @@ public class WebPanel : Panel /// public WebSurface Surface { get; private set; } - + [Parameter] public string Url { get => Surface.Url; @@ -52,6 +53,8 @@ public class WebPanel : Panel .WithName( "WebPanel" ) .Finish(); + sufaceTexture.Flags |= TextureFlags.PremultipliedAlpha; + Style.SetBackgroundImage( sufaceTexture ); } diff --git a/engine/Sandbox.Engine/Systems/UI/Engine/TextBlock.cs b/engine/Sandbox.Engine/Systems/UI/Engine/TextBlock.cs index 308deed5..fe315f38 100644 --- a/engine/Sandbox.Engine/Systems/UI/Engine/TextBlock.cs +++ b/engine/Sandbox.Engine/Systems/UI/Engine/TextBlock.cs @@ -157,12 +157,17 @@ internal sealed class TextBlock : IDisposable if ( color.a <= 0 ) return; + var bm = renderer.OverrideBlendMode; + + if ( bm == BlendMode.Normal && Texture.Flags.Contains( TextureFlags.PremultipliedAlpha ) ) + bm = BlendMode.PremultipliedAlpha; + textAttr.Set( "BoxPosition", textrect.Position ); textAttr.Set( "BoxSize", textrect.Size ); textAttr.Set( "TextureIndex", Texture.Index ); textAttr.Set( "SamplerIndex", SamplerState.GetBindlessIndex( new SamplerState() { Filter = TextFilter } ) ); - textAttr.SetComboEnum( "D_BLENDMODE", renderer.OverrideBlendMode ); + textAttr.SetComboEnum( "D_BLENDMODE", bm ); Graphics.DrawQuad( textrect.Floor(), Material.UI.Text, color, textAttr ); } @@ -543,7 +548,7 @@ internal sealed class TextBlock : IDisposable using var perfScope = Performance.Scope( "TextBlock.RebuildTexture" ); - using ( var bitmap = new SKBitmap( width, height, SKColorType.Bgra8888, SKAlphaType.Unpremul ) ) + using ( var bitmap = new SKBitmap( width, height, SKColorType.Bgra8888, SKAlphaType.Premul ) ) using ( var canvas = new SKCanvas( bitmap ) ) { var o = new Topten.RichTextKit.TextPaintOptions @@ -599,6 +604,8 @@ internal sealed class TextBlock : IDisposable .WithData( bitmap.GetPixels(), width * height * bitmap.BytesPerPixel ) .WithDynamicUsage() .Finish(); + + Texture.Flags |= TextureFlags.PremultipliedAlpha; } } diff --git a/engine/Sandbox.Engine/Systems/UI/Panel/Panel.Render.cs b/engine/Sandbox.Engine/Systems/UI/Panel/Panel.Render.cs index 00d19c65..52128bee 100644 --- a/engine/Sandbox.Engine/Systems/UI/Panel/Panel.Render.cs +++ b/engine/Sandbox.Engine/Systems/UI/Panel/Panel.Render.cs @@ -1,5 +1,4 @@ using Sandbox.Rendering; -using System.Runtime.InteropServices; namespace Sandbox.UI; @@ -70,7 +69,12 @@ public partial class Panel if ( bgBlendMode == BlendMode.Normal || ComputedStyle.BackgroundImage == null ) { - bgAttribs.SetComboEnum( "D_BLENDMODE", renderer.OverrideBlendMode ); + BlendMode bm = bgBlendMode; + + if ( bm == BlendMode.Normal && (ComputedStyle.BackgroundImage?.Flags.Contains( TextureFlags.PremultipliedAlpha ) ?? false) ) + bm = BlendMode.PremultipliedAlpha; + + bgAttribs.SetComboEnum( "D_BLENDMODE", bm ); bgAttribs.Set( "Texture", ComputedStyle.BackgroundImage ); Graphics.DrawQuad( rect, Material.UI.Box, color, bgAttribs ); } diff --git a/game/addons/base/Assets/shaders/common/blendmode.hlsl b/game/addons/base/Assets/shaders/common/blendmode.hlsl index 5c685b49..61f7d789 100644 --- a/game/addons/base/Assets/shaders/common/blendmode.hlsl +++ b/game/addons/base/Assets/shaders/common/blendmode.hlsl @@ -4,9 +4,9 @@ // Blend Modes (https://web.dev/learn/css/blend-modes/) // I only filled in what I needed. A job for someone else - garry // -DynamicCombo( D_BLENDMODE, 0..2, Sys( ALL ) ); +DynamicCombo( D_BLENDMODE, 0..3, Sys( ALL ) ); -// Alpha Blend +// Alpha Blend (standard) #if D_BLENDMODE == 0 RenderState( BlendEnable, true ); RenderState( SrcBlend, SRC_ALPHA ); @@ -15,18 +15,34 @@ DynamicCombo( D_BLENDMODE, 0..2, Sys( ALL ) ); RenderState( SrcBlendAlpha, ONE ); RenderState( DstBlendAlpha, INV_SRC_ALPHA ); RenderState( BlendOpAlpha, ADD ); + // Multiply #elif D_BLENDMODE == 1 RenderState( BlendEnable, true ); RenderState( SrcBlend, DEST_COLOR ); - RenderState( DstBlend, INV_SRC_ALPHA ); + RenderState( DstBlend, ZERO ); + RenderState( BlendOp, ADD ); RenderState( SrcBlendAlpha, ONE ); - RenderState( DstBlendAlpha, ONE ); -// Lighten + RenderState( DstBlendAlpha, ZERO ); + RenderState( BlendOpAlpha, ADD ); + +// Lighten / Additive #elif D_BLENDMODE == 2 RenderState( BlendEnable, true ); RenderState( SrcBlend, SRC_ALPHA ); RenderState( DstBlend, ONE ); + RenderState( BlendOp, ADD ); RenderState( SrcBlendAlpha, ONE ); RenderState( DstBlendAlpha, ONE ); -#endif + RenderState( BlendOpAlpha, ADD ); + +// Premultiplied Alpha +#elif D_BLENDMODE == 3 + RenderState( BlendEnable, true ); + RenderState( SrcBlend, ONE ); + RenderState( DstBlend, INV_SRC_ALPHA ); + RenderState( BlendOp, ADD ); + RenderState( SrcBlendAlpha, ONE ); + RenderState( DstBlendAlpha, INV_SRC_ALPHA ); + RenderState( BlendOpAlpha, ADD ); +#endif \ No newline at end of file diff --git a/game/addons/base/Assets/shaders/ui_cssbox.shader b/game/addons/base/Assets/shaders/ui_cssbox.shader index 8cf6d3a7..54cecc45 100644 --- a/game/addons/base/Assets/shaders/ui_cssbox.shader +++ b/game/addons/base/Assets/shaders/ui_cssbox.shader @@ -329,10 +329,18 @@ PS } vImage.xyz = SrgbGammaToLinear( vImage.xyz ); - vImage *= bgTint; + + #if ( D_BLENDMODE == 3 ) // PREMULIPLIED + vImage.rgb *= bgTint.rgb; + vImage *= bgTint.a; + #else + vImage *= bgTint; + #endif vBox.rgb = lerp( vBox.rgb, vImage.rgb, saturate( vImage.a + ( 1 - vBox.a ) ) ); vBox.a = max( vBox.a, vImage.a ); + + } o.vColor = vBox; diff --git a/game/addons/base/Assets/shaders/ui_text.shader b/game/addons/base/Assets/shaders/ui_text.shader index a320d030..ae2a40f2 100644 --- a/game/addons/base/Assets/shaders/ui_text.shader +++ b/game/addons/base/Assets/shaders/ui_text.shader @@ -96,16 +96,13 @@ PS Texture2D tex = GetBindlessTexture2D( TextureIndex + 1 ); float4 vColor = tex.SampleBias( sampler, vTexCoord, mipBias ); - float flAlphaScale = 1.0f; - - float lum = saturate(dot( float3(0.30, 0.59, 0.11), vColor.rgb ) - 0.2); - float alpha = vColor.a; - alpha = pow(alpha, 0.6 + (lum) * 3 ); - - vColor.a = alpha * flAlphaScale; - o.vColor = vColor; - o.vColor.a *= i.vColor.a; + + #if ( D_BLENDMODE == 3 ) + o.vColor *= i.vColor.a; + #else + o.vColor.a *= i.vColor.a; + #endif // Apply fog only on world panels #if D_WORLDPANEL diff --git a/game/addons/menu/Code/ModalSystem.cs b/game/addons/menu/Code/ModalSystem.cs index c20466ec..468d58eb 100644 --- a/game/addons/menu/Code/ModalSystem.cs +++ b/game/addons/menu/Code/ModalSystem.cs @@ -10,6 +10,8 @@ public class ModalSystem : IModalSystem List OpenModals = new List(); + PauseModal _pauseModal; + public ModalSystem() { Instance = this; @@ -17,6 +19,9 @@ public class ModalSystem : IModalSystem public bool HasModalsOpen() { + if ( IsPauseMenuOpen ) + return true; + return OpenModals.Any( x => x.WantsMouseInput() ); } @@ -167,8 +172,16 @@ public class ModalSystem : IModalSystem return; } - var modal = new PauseModal(); - Push( modal ); + _pauseModal = MenuOverlay.Instance.Children.OfType().FirstOrDefault(); + + if ( _pauseModal != null ) + { + _pauseModal.ToggleClass( "hidden" ); + return; + } + + _pauseModal = new PauseModal(); + MenuOverlay.Instance.AddChild( _pauseModal ); } public void Player( SteamId steamid, string page = "" ) @@ -190,6 +203,6 @@ public class ModalSystem : IModalSystem Push( new WorkshopPublishModal { Options = options } ); } - public bool IsModalOpen => OpenModals.Any(); - public bool IsPauseMenuOpen => OpenModals.OfType().Any(); + public bool IsModalOpen => HasModalsOpen(); + public bool IsPauseMenuOpen => _pauseModal.IsValid() && _pauseModal.IsPauseMenuOpen(); } diff --git a/game/addons/menu/code/Modals/PauseMenuModal/PauseModal.razor b/game/addons/menu/code/Modals/PauseMenuModal/PauseModal.razor index 4acb699d..ee7a8d8c 100644 --- a/game/addons/menu/code/Modals/PauseMenuModal/PauseModal.razor +++ b/game/addons/menu/code/Modals/PauseMenuModal/PauseModal.razor @@ -20,7 +20,14 @@
- + + @{ + var ident = game?.FullIdent.Split('#')[0] ?? "unknown/unknown"; + var url = $"https://sbox.game/game/pause/{ident}"; + //var url = $"https://localhost:44338/game/pause/{ident}"; + + } +
@@ -108,4 +115,36 @@ StateHasChanged(); } } + + public override void Tick() + { + base.Tick(); + + CheckForDelete(); + } + + bool CheckForDelete() + { + if (MenuUtility.GamePackage == null) + { + Delete(); + return true; + } + + if (MenuUtility.GamePackage?.Ident != game?.Ident) + { + Delete(); + return true; + } + + return false; + } + + public bool IsPauseMenuOpen() + { + if (CheckForDelete() ) + return false; + + return !HasClass( "hidden" ); + } } diff --git a/game/addons/menu/code/Modals/PauseMenuModal/PauseModal.razor.scss b/game/addons/menu/code/Modals/PauseMenuModal/PauseModal.razor.scss index 535c028b..4811a9ba 100644 --- a/game/addons/menu/code/Modals/PauseMenuModal/PauseModal.razor.scss +++ b/game/addons/menu/code/Modals/PauseMenuModal/PauseModal.razor.scss @@ -17,6 +17,12 @@ PauseModal.modal padding: 4rem; gap: 2rem; } + + &.hidden + { + opacity: 0; + pointer-events: none; + } } @@ -151,4 +157,11 @@ PauseModal .tiles-container flex-grow: 1; overflow: hidden; } +} + +.browser +{ + flex-grow: 1; + width: 100%; + height: 100%; } \ No newline at end of file