From c524451704550dfa616480c25259ba1f5ec53a1c Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 20:43:44 +0200 Subject: [PATCH] Draw glass behind translucent panels on 1.21.11 A copy of the game behind the screen is blurred once a frame and drawn through each panel's rounded rectangle texture by a glass pipeline. Older versions keep blurring the whole background. Co-Authored-By: Claude Opus 5.5 --- common | 2 +- .../mixins/render/GameRendererMixin.java | 5 +- .../impl/provider/SaturnProviderImpl.java | 8 +++ .../saturnclient/impl/ui/GlassRenderer.java | 71 +++++++++++++++++++ .../saturnclient/impl/ui/RenderScopeImpl.java | 26 +++++++ .../saturnclient/shaders/core/glass.fsh | 31 ++++++++ 6 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/saturnclient/impl/ui/GlassRenderer.java create mode 100644 src/main/resources/assets/saturnclient/shaders/core/glass.fsh diff --git a/common b/common index 5c0912f..4d9c629 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 5c0912f0cf6f09e5776f330e559ddd8e38370f47 +Subproject commit 4d9c629eaf4f5d315d772d7e07336ff237369390 diff --git a/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java b/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java index be65e23..324fe01 100644 --- a/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java +++ b/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java @@ -49,12 +49,13 @@ public class GameRendererMixin { //? if >=1.21.6 { // From 1.21.6 the menu blur's strength is the vanilla setting, read here every frame. While a - // Saturn screen is open, it's Saturn's background blur instead, faded in with the screen. + // Saturn screen is open, it's Saturn's background blur instead, faded in with the screen. Glass + // uses the same blur, so this holds even when the screen doesn't blur its whole background. @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/GameOptions;getMenuBackgroundBlurrinessValue()I")) private int useSaturnBlur(GameOptions options) { if (MinecraftClient.getInstance().currentScreen instanceof SaturnScreenFabric saturnScreen) { - return saturnScreen.screen.getBackgroundBlur(); + return saturnScreen.screen.getBlurRadius(); } return options.getMenuBackgroundBlurrinessValue(); diff --git a/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java b/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java index fb9b976..f468ed4 100644 --- a/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java +++ b/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java @@ -76,6 +76,14 @@ public class SaturnProviderImpl implements SaturnProvider { return (IdentifierRef) (Object) playerSkin.get(); } + @Override + public boolean supportsGlass() { + //? if >=1.21.11 { + return true; + //?} else + //return false; + } + @Override public String getModVersion() { return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID) diff --git a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java new file mode 100644 index 0000000..1c2f589 --- /dev/null +++ b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java @@ -0,0 +1,71 @@ +package org.saturnclient.impl.ui; + +//? if >=1.21.11 { +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.FilterMode; +import com.mojang.blaze3d.textures.GpuTextureView; + +import org.saturnclient.saturnclient.SaturnClient; + +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.GpuSampler; +import net.minecraft.client.gl.PostEffectProcessor; +import net.minecraft.client.gl.RenderPipelines; +import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.render.DefaultFramebufferSet; +import net.minecraft.client.texture.TextureSetup; +import net.minecraft.client.util.Pool; +import net.minecraft.util.Identifier; + +// Glass: a copy of the game behind the screen, blurred with vanilla's blur (as strong as Saturn's +// background blur), drawn through each panel's rounded rectangle texture (shaders/core/glass.fsh). +// The copy is made while the screen builds its GUI, before the GUI is drawn, so it holds the world +// or panorama alone. +// TODO: glass before 1.21.11 (#23). Those versions blur the whole background instead. +public final class GlassRenderer { + public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET) + .withLocation(Identifier.of("saturnclient", "pipeline/glass")) + .withFragmentShader(Identifier.of("saturnclient", "core/glass")) + .withSampler("Sampler1") + .build(); + + private static final Identifier BLUR = Identifier.ofVanilla("blur"); + // For the blur's intermediate target, freed once unused for a few frames + private static final Pool POOL = new Pool(3); + + private static SimpleFramebuffer backdrop; + + private GlassRenderer() { + } + + // Copies the main framebuffer and blurs the copy. Call once a frame, before drawing glass. + public static void captureBackdrop() { + Framebuffer main = SaturnClient.client.getFramebuffer(); + int width = main.textureWidth; + int height = main.textureHeight; + + if (backdrop == null) { + backdrop = new SimpleFramebuffer("Saturn glass", width, height, false); + } else if (backdrop.textureWidth != width || backdrop.textureHeight != height) { + backdrop.resize(width, height); + } + + RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( + main.getColorAttachment(), backdrop.getColorAttachment(), 0, 0, 0, 0, 0, width, height); + + PostEffectProcessor blur = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR, + DefaultFramebufferSet.MAIN_ONLY); + if (blur != null) { + blur.render(backdrop, POOL); + } + POOL.decrementLifespan(); + } + + // The mask, then the blurred copy, as the glass pipeline samples them + public static TextureSetup textures(GpuTextureView mask, GpuSampler maskSampler) { + return TextureSetup.of(mask, maskSampler, backdrop.getColorAttachmentView(), + RenderSystem.getSamplerCache().get(FilterMode.LINEAR)); + } +} +//?} diff --git a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java index 3cdb564..5c8b123 100644 --- a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java +++ b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java @@ -61,6 +61,10 @@ public class RenderScopeImpl implements RenderScope { private ScissorStack scissorStack; private int opacity = 0xFF << 24; + //? if >=1.21.11 { + // Whether the glass backdrop was made for this scope's frame + private boolean backdropCaptured = false; + //?} public RenderScopeImpl(DrawContext context) { this(context.getMatrices(), ((DrawContextAccessor) (Object) context).getState()); @@ -230,6 +234,28 @@ public class RenderScopeImpl implements RenderScope { this.scissorStack.peekLast())); } + //? if >=1.21.11 { + @Override + public void drawGlass(IdentifierRef mask, int x, int y, int width, int height) { + int color = getColor(0xFFFFFFFF); + if (mask == null || color >>> 24 == 0) + return; + + if (!backdropCaptured) { + GlassRenderer.captureBackdrop(); + backdropCaptured = true; + } + + AbstractTexture maskTexture = SaturnClient.client.getTextureManager().getTexture((Identifier) (Object) mask); + this.state.addSimpleElement(new TexturedQuadGuiElementRenderState( + GlassRenderer.PIPELINE, + GlassRenderer.textures(maskTexture.getGlTextureView(), maskTexture.getSampler()), + new Matrix3x2f(this.matrices.stack), + x, y, x + width, y + height, 0, 1, 0, 1, color, + this.scissorStack.peekLast())); + } + //?} + // Scissor management @Override public void enableScissor(int x1, int y1, int x2, int y2) { diff --git a/src/main/resources/assets/saturnclient/shaders/core/glass.fsh b/src/main/resources/assets/saturnclient/shaders/core/glass.fsh new file mode 100644 index 0000000..fe6f02b --- /dev/null +++ b/src/main/resources/assets/saturnclient/shaders/core/glass.fsh @@ -0,0 +1,31 @@ +#version 330 + +// Saturn's glass: the blurred game behind the screen, cut to a panel's shape. + +layout(std140) uniform DynamicTransforms { + mat4 ModelViewMat; + vec4 ColorModulator; + vec3 ModelOffset; + mat4 TextureMat; +}; + +// The panel's rounded rectangle, the same texture its background is drawn with +uniform sampler2D Sampler0; +// The game behind the screen, blurred, the size of the framebuffer +uniform sampler2D Sampler1; + +in vec2 texCoord0; +in vec4 vertexColor; + +out vec4 fragColor; + +void main() { + float mask = texture(Sampler0, texCoord0).a * vertexColor.a; + if (mask == 0.0) { + discard; + } + + // The blurred copy lines up with the framebuffer being drawn to. + vec2 screenUv = gl_FragCoord.xy / vec2(textureSize(Sampler1, 0)); + fragColor = vec4(texture(Sampler1, screenUv).rgb, mask) * ColorModulator; +}