Pause menu 3 (#3663)

* Add TextureFlags
* Add TextureFlags.PremultipliedAlpha in text block and webpanel textures
* Add BlendMode.PremultipliedAlpha
* Add panel to PauseModal
This commit is contained in:
Garry Newman
2025-12-23 15:11:25 +00:00
committed by GitHub
parent 677282f1cd
commit 675f55005e
11 changed files with 157 additions and 34 deletions

View File

@@ -32,6 +32,11 @@ public partial class Texture : Resource, IDisposable
public override bool IsValid => native.IsValid; public override bool IsValid => native.IsValid;
/// <summary>
/// Flags providing hints about this texture
/// </summary>
public TextureFlags Flags { get; set; } = TextureFlags.None;
/// <summary> /// <summary>
/// Private constructor, use <see cref="FromNative(ITexture)"/> /// Private constructor, use <see cref="FromNative(ITexture)"/>
/// </summary> /// </summary>
@@ -304,3 +309,16 @@ public partial class Texture : Resource, IDisposable
IsLoaded = true; IsLoaded = true;
} }
} }
/// <summary>
/// Flags providing hints about a texture
/// </summary>
public enum TextureFlags
{
None = 0,
/// <summary>
/// Hint that this texture has pre-multiplied alpha
/// </summary>
PremultipliedAlpha = 1 << 0,
}

View File

@@ -13,13 +13,18 @@
// Screen = 2, // Screen = 2,
// Overlay = 3, // Overlay = 3,
// Darken = 4, // Darken = 4,
Lighten = 2, // 5 Lighten = 2,
// ColorDodge = 6, PremultipliedAlpha = 3,
// ColorBurn = 7, // 5
// HardLight = 8, // ColorDodge = 6,
// SoftLight = 9, // ColorBurn = 7,
// Difference = 10, // HardLight = 8,
// Exclusion = 11, // SoftLight = 9,
// Difference = 10,
// Exclusion = 11,
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Web; using Microsoft.AspNetCore.Components;
using System.Web;
namespace Sandbox.UI; namespace Sandbox.UI;
@@ -12,7 +13,7 @@ public class WebPanel : Panel
/// </summary> /// </summary>
public WebSurface Surface { get; private set; } public WebSurface Surface { get; private set; }
[Parameter]
public string Url public string Url
{ {
get => Surface.Url; get => Surface.Url;
@@ -52,6 +53,8 @@ public class WebPanel : Panel
.WithName( "WebPanel" ) .WithName( "WebPanel" )
.Finish(); .Finish();
sufaceTexture.Flags |= TextureFlags.PremultipliedAlpha;
Style.SetBackgroundImage( sufaceTexture ); Style.SetBackgroundImage( sufaceTexture );
} }

View File

@@ -157,12 +157,17 @@ internal sealed class TextBlock : IDisposable
if ( color.a <= 0 ) return; 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( "BoxPosition", textrect.Position );
textAttr.Set( "BoxSize", textrect.Size ); textAttr.Set( "BoxSize", textrect.Size );
textAttr.Set( "TextureIndex", Texture.Index ); textAttr.Set( "TextureIndex", Texture.Index );
textAttr.Set( "SamplerIndex", SamplerState.GetBindlessIndex( new SamplerState() { Filter = TextFilter } ) ); 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 ); 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 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 ) ) using ( var canvas = new SKCanvas( bitmap ) )
{ {
var o = new Topten.RichTextKit.TextPaintOptions var o = new Topten.RichTextKit.TextPaintOptions
@@ -599,6 +604,8 @@ internal sealed class TextBlock : IDisposable
.WithData( bitmap.GetPixels(), width * height * bitmap.BytesPerPixel ) .WithData( bitmap.GetPixels(), width * height * bitmap.BytesPerPixel )
.WithDynamicUsage() .WithDynamicUsage()
.Finish(); .Finish();
Texture.Flags |= TextureFlags.PremultipliedAlpha;
} }
} }

View File

@@ -1,5 +1,4 @@
using Sandbox.Rendering; using Sandbox.Rendering;
using System.Runtime.InteropServices;
namespace Sandbox.UI; namespace Sandbox.UI;
@@ -70,7 +69,12 @@ public partial class Panel
if ( bgBlendMode == BlendMode.Normal || ComputedStyle.BackgroundImage == null ) 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 ); bgAttribs.Set( "Texture", ComputedStyle.BackgroundImage );
Graphics.DrawQuad( rect, Material.UI.Box, color, bgAttribs ); Graphics.DrawQuad( rect, Material.UI.Box, color, bgAttribs );
} }

View File

@@ -4,9 +4,9 @@
// Blend Modes (https://web.dev/learn/css/blend-modes/) // Blend Modes (https://web.dev/learn/css/blend-modes/)
// I only filled in what I needed. A job for someone else - garry // 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 #if D_BLENDMODE == 0
RenderState( BlendEnable, true ); RenderState( BlendEnable, true );
RenderState( SrcBlend, SRC_ALPHA ); RenderState( SrcBlend, SRC_ALPHA );
@@ -15,18 +15,34 @@ DynamicCombo( D_BLENDMODE, 0..2, Sys( ALL ) );
RenderState( SrcBlendAlpha, ONE ); RenderState( SrcBlendAlpha, ONE );
RenderState( DstBlendAlpha, INV_SRC_ALPHA ); RenderState( DstBlendAlpha, INV_SRC_ALPHA );
RenderState( BlendOpAlpha, ADD ); RenderState( BlendOpAlpha, ADD );
// Multiply // Multiply
#elif D_BLENDMODE == 1 #elif D_BLENDMODE == 1
RenderState( BlendEnable, true ); RenderState( BlendEnable, true );
RenderState( SrcBlend, DEST_COLOR ); RenderState( SrcBlend, DEST_COLOR );
RenderState( DstBlend, INV_SRC_ALPHA ); RenderState( DstBlend, ZERO );
RenderState( BlendOp, ADD );
RenderState( SrcBlendAlpha, ONE ); RenderState( SrcBlendAlpha, ONE );
RenderState( DstBlendAlpha, ONE ); RenderState( DstBlendAlpha, ZERO );
// Lighten RenderState( BlendOpAlpha, ADD );
// Lighten / Additive
#elif D_BLENDMODE == 2 #elif D_BLENDMODE == 2
RenderState( BlendEnable, true ); RenderState( BlendEnable, true );
RenderState( SrcBlend, SRC_ALPHA ); RenderState( SrcBlend, SRC_ALPHA );
RenderState( DstBlend, ONE ); RenderState( DstBlend, ONE );
RenderState( BlendOp, ADD );
RenderState( SrcBlendAlpha, ONE ); RenderState( SrcBlendAlpha, ONE );
RenderState( DstBlendAlpha, 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

View File

@@ -329,10 +329,18 @@ PS
} }
vImage.xyz = SrgbGammaToLinear( vImage.xyz ); 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.rgb = lerp( vBox.rgb, vImage.rgb, saturate( vImage.a + ( 1 - vBox.a ) ) );
vBox.a = max( vBox.a, vImage.a ); vBox.a = max( vBox.a, vImage.a );
} }
o.vColor = vBox; o.vColor = vBox;

View File

@@ -96,16 +96,13 @@ PS
Texture2D tex = GetBindlessTexture2D( TextureIndex + 1 ); Texture2D tex = GetBindlessTexture2D( TextureIndex + 1 );
float4 vColor = tex.SampleBias( sampler, vTexCoord, mipBias ); 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 = 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 // Apply fog only on world panels
#if D_WORLDPANEL #if D_WORLDPANEL

View File

@@ -10,6 +10,8 @@ public class ModalSystem : IModalSystem
List<BaseModal> OpenModals = new List<BaseModal>(); List<BaseModal> OpenModals = new List<BaseModal>();
PauseModal _pauseModal;
public ModalSystem() public ModalSystem()
{ {
Instance = this; Instance = this;
@@ -17,6 +19,9 @@ public class ModalSystem : IModalSystem
public bool HasModalsOpen() public bool HasModalsOpen()
{ {
if ( IsPauseMenuOpen )
return true;
return OpenModals.Any( x => x.WantsMouseInput() ); return OpenModals.Any( x => x.WantsMouseInput() );
} }
@@ -167,8 +172,16 @@ public class ModalSystem : IModalSystem
return; return;
} }
var modal = new PauseModal(); _pauseModal = MenuOverlay.Instance.Children.OfType<PauseModal>().FirstOrDefault();
Push( modal );
if ( _pauseModal != null )
{
_pauseModal.ToggleClass( "hidden" );
return;
}
_pauseModal = new PauseModal();
MenuOverlay.Instance.AddChild( _pauseModal );
} }
public void Player( SteamId steamid, string page = "" ) public void Player( SteamId steamid, string page = "" )
@@ -190,6 +203,6 @@ public class ModalSystem : IModalSystem
Push( new WorkshopPublishModal { Options = options } ); Push( new WorkshopPublishModal { Options = options } );
} }
public bool IsModalOpen => OpenModals.Any(); public bool IsModalOpen => HasModalsOpen();
public bool IsPauseMenuOpen => OpenModals.OfType<PauseModal>().Any(); public bool IsPauseMenuOpen => _pauseModal.IsValid() && _pauseModal.IsPauseMenuOpen();
} }

View File

@@ -20,7 +20,14 @@
</div> </div>
<div class="right"> <div class="right">
@{
var ident = game?.FullIdent.Split('#')[0] ?? "unknown/unknown";
var url = $"https://sbox.game/game/pause/{ident}";
//var url = $"https://localhost:44338/game/pause/{ident}";
<WebPanel class="browser" Url="@url"></WebPanel>
}
</div> </div>
</div> </div>
@@ -108,4 +115,36 @@
StateHasChanged(); 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" );
}
} }

View File

@@ -17,6 +17,12 @@ PauseModal.modal
padding: 4rem; padding: 4rem;
gap: 2rem; gap: 2rem;
} }
&.hidden
{
opacity: 0;
pointer-events: none;
}
} }
@@ -151,4 +157,11 @@ PauseModal .tiles-container
flex-grow: 1; flex-grow: 1;
overflow: hidden; overflow: hidden;
} }
}
.browser
{
flex-grow: 1;
width: 100%;
height: 100%;
} }