diff --git a/common b/common index ef0ead6..7d0bfd2 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit ef0ead6bf895f723a78ddf5426f3881ef13f426a +Subproject commit 7d0bfd26c9f19294b082887beacb6efc6d4c0778 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..37da9f3 --- /dev/null +++ b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java @@ -0,0 +1,377 @@ +package org.saturnclient.impl.ui; + +// Glass: a copy of the game behind the screen, blurred, drawn through each panel's rounded rectangle +// texture as a mask. There's a copy for each blur strength in use (menus and widgets have their own), +// blurred with vanilla's blur at that radius, so it holds the world or panorama without the screen's +// own animated background blur. +// +// The GUI rewrites split this into three implementations: +// - 1.21.6+: the GUI is drawn after the screen builds it, so the copy is taken from the main +// framebuffer while the screen builds its GUI, and glass is a GUI element with its own pipeline +// (shaders/core/glass.fsh). The blur is vanilla's effect rebuilt with the radius set. +// - 1.21.5: the screen draws straight into the framebuffer, blurring it first, so the game is saved +// before the blur (saveBackground). Glass is a render layer with its own pipeline +// (shaders/core/glass_pre_1_21_6.fsh), and the blur takes the radius as it runs. +// - 1.21.4: as 1.21.5, with GL framebuffers copied by blitting, and a shader program +// (shaders/core/glass.json) instead of a pipeline. +//? if >=1.21.6 { +import java.io.Reader; +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +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 com.mojang.serialization.JsonOps; + +import org.saturnclient.config.Config; +import org.saturnclient.saturnclient.SaturnClient; + +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.PostEffectPipeline; +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.render.ProjectionMatrix2; +import net.minecraft.client.texture.AbstractTexture; +import net.minecraft.client.texture.TextureSetup; +import net.minecraft.client.util.Pool; +import net.minecraft.util.Identifier; + +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("post_effect/blur.json"); + private static final ProjectionMatrix2 PROJECTION = new ProjectionMatrix2("saturn glass", 0.1f, 1000f, false); + // For the blurs' intermediate targets, freed once unused for a few frames + private static final Pool POOL = new Pool(3); + + // By blur strength. A blur that failed to load is kept as null, so it isn't retried every frame. + private static final Map BLURS = new HashMap<>(); + private static final Map BACKDROPS = new HashMap<>(); + + private GlassRenderer() { + } + + // Copies the main framebuffer and blurs the copy by blur. Call once a frame for each blur, while + // the screen builds its GUI, before drawing glass with it. + public static void captureBackdrop(int blur) { + Framebuffer main = SaturnClient.client.getFramebuffer(); + int width = main.textureWidth; + int height = main.textureHeight; + + SimpleFramebuffer backdrop = BACKDROPS.get(blur); + if (backdrop == null) { + backdrop = new SimpleFramebuffer("Saturn glass " + blur, width, height, false); + BACKDROPS.put(blur, backdrop); + } else if (backdrop.textureWidth != width || backdrop.textureHeight != height) { + backdrop.resize(width, height); + } + //? if <1.21.11 + //backdrop.getColorAttachment().setTextureFilter(FilterMode.LINEAR, false); + + RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( + main.getColorAttachment(), backdrop.getColorAttachment(), 0, 0, 0, 0, 0, width, height); + + PostEffectProcessor processor = getBlur(blur); + if (processor != null) { + processor.render(backdrop, POOL); + } + POOL.decrementLifespan(); + release(blur); + } + + // The mask, then the copy blurred by blur, as the glass pipeline samples them + public static TextureSetup textures(AbstractTexture mask, int blur) { + GpuTextureView backdrop = BACKDROPS.get(blur).getColorAttachmentView(); + //? if >=1.21.11 + return TextureSetup.of(mask.getGlTextureView(), mask.getSampler(), backdrop, RenderSystem.getSamplerCache().get(FilterMode.LINEAR)); + //? if <1.21.11 + //return TextureSetup.of(mask.getGlTextureView(), backdrop); + } + + // Frees the copies and blurs of strengths that are no longer set, besides kept + private static void release(int kept) { + BACKDROPS.entrySet().removeIf(entry -> { + boolean unused = entry.getKey() != kept && !isSet(entry.getKey()); + if (unused) { + entry.getValue().delete(); + } + return unused; + }); + BLURS.entrySet().removeIf(entry -> { + boolean unused = entry.getKey() != kept && !isSet(entry.getKey()); + if (unused && entry.getValue() != null) { + entry.getValue().close(); + } + return unused; + }); + } + + private static boolean isSet(int blur) { + return blur == Config.menuBlur.value || blur == Config.widgetBlur.value; + } + + private static PostEffectProcessor getBlur(int blur) { + if (!BLURS.containsKey(blur)) { + BLURS.put(blur, loadBlur(blur)); + } + return BLURS.get(blur); + } + + // Vanilla's blur, with its radius set to blur. Vanilla's own leaves it at 0, which makes the shader + // read the menu blur setting (Saturn's animated background blur while a Saturn screen is open). + private static PostEffectProcessor loadBlur(int blur) { + try (Reader reader = SaturnClient.client.getResourceManager().getResourceOrThrow(BLUR).getReader()) { + JsonElement json = JsonParser.parseReader(reader); + setRadius(json, blur); + + PostEffectPipeline pipeline = PostEffectPipeline.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow(); + return PostEffectProcessor.parseEffect(pipeline, SaturnClient.client.getTextureManager(), + DefaultFramebufferSet.MAIN_ONLY, Identifier.of("saturnclient", "glass_blur_" + blur), PROJECTION); + } catch (Exception e) { + SaturnClient.LOGGER.error("Couldn't load the glass blur", e); + return null; + } + } + + // Sets every "Radius" uniform in the effect + private static void setRadius(JsonElement json, int radius) { + if (json.isJsonArray()) { + json.getAsJsonArray().forEach(element -> setRadius(element, radius)); + } else if (json.isJsonObject()) { + JsonObject object = json.getAsJsonObject(); + if (object.has("name") && object.get("name").getAsString().equals("Radius")) { + object.addProperty("value", (float) radius); + } + object.entrySet().forEach(entry -> setRadius(entry.getValue(), radius)); + } + } +} +//?} else if >=1.21.5 { +/*import java.util.HashMap; +import java.util.Map; + +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.FilterMode; + +import org.saturnclient.config.Config; +import org.saturnclient.saturnclient.SaturnClient; + +import net.minecraft.client.gl.Framebuffer; +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.render.RenderLayer; +import net.minecraft.client.render.RenderPhase; +import net.minecraft.client.util.Pool; +import net.minecraft.util.Identifier; + +public final class GlassRenderer { + private static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET) + .withLocation(Identifier.of("saturnclient", "pipeline/glass")) + .withFragmentShader(Identifier.of("saturnclient", "core/glass_pre_1_21_6")) + .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 final Map BACKDROPS = new HashMap<>(); + // By mask and blur + private static final Map LAYERS = new HashMap<>(); + // The game behind the screen before the screen blurs it, which glass is blurred from + private static SimpleFramebuffer background; + + private GlassRenderer() { + } + + // Saves the main framebuffer for glass. Call each frame before the screen blurs its background. + public static void saveBackground() { + background = copy(SaturnClient.client.getFramebuffer(), background, "Saturn glass background"); + } + + // Copies the saved background and blurs the copy by blur. Call once a frame for each blur, before + // drawing glass with it. + public static void captureBackdrop(int blur) { + Framebuffer from = background != null ? background : SaturnClient.client.getFramebuffer(); + SimpleFramebuffer backdrop = copy(from, BACKDROPS.get(blur), "Saturn glass " + blur); + BACKDROPS.put(blur, backdrop); + + PostEffectProcessor processor = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR, + DefaultFramebufferSet.MAIN_ONLY); + if (processor != null) { + processor.render(backdrop, POOL, pass -> pass.setUniform("Radius", (float) blur)); + } + POOL.decrementLifespan(); + release(blur); + } + + // Draws glass through mask with the copy blurred by blur + public static RenderLayer getLayer(Identifier mask, int blur) { + return LAYERS.computeIfAbsent(mask + " " + blur, key -> RenderLayer.of("saturn_glass", 786432, PIPELINE, + RenderLayer.MultiPhaseParameters.builder() + .texture(new RenderPhase.TextureBase(() -> { + RenderSystem.setShaderTexture(0, + SaturnClient.client.getTextureManager().getTexture(mask).getGlTexture()); + RenderSystem.setShaderTexture(1, BACKDROPS.get(blur).getColorAttachment()); + }, () -> { + })) + .build(false))); + } + + // Frees the copies of strengths that are no longer set, besides kept + private static void release(int kept) { + BACKDROPS.entrySet().removeIf(entry -> { + boolean unused = entry.getKey() != kept && entry.getKey() != Config.menuBlur.value + && entry.getKey() != Config.widgetBlur.value; + if (unused) { + entry.getValue().delete(); + } + return unused; + }); + } + + // Copies from into to, made or resized to match + private static SimpleFramebuffer copy(Framebuffer from, SimpleFramebuffer to, String name) { + int width = from.textureWidth; + int height = from.textureHeight; + + if (to == null) { + to = new SimpleFramebuffer(name, width, height, false); + } else if (to.textureWidth != width || to.textureHeight != height) { + to.resize(width, height); + } + to.getColorAttachment().setTextureFilter(FilterMode.LINEAR, false); + + RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( + from.getColorAttachment(), to.getColorAttachment(), 0, 0, 0, 0, 0, width, height); + return to; + } +} +*///?} else { +/*import java.util.HashMap; +import java.util.Map; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; + +import org.saturnclient.config.Config; +import org.saturnclient.saturnclient.SaturnClient; + +import net.minecraft.client.gl.Defines; +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.PostEffectProcessor; +import net.minecraft.client.gl.ShaderProgramKey; +import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.render.DefaultFramebufferSet; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.RenderPhase; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.util.Pool; +import net.minecraft.util.Identifier; + +public final class GlassRenderer { + private static final ShaderProgramKey PROGRAM = new ShaderProgramKey(Identifier.of("saturnclient", "core/glass"), + VertexFormats.POSITION_TEXTURE_COLOR, Defines.EMPTY); + + 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 final Map BACKDROPS = new HashMap<>(); + // By mask and blur + private static final Map LAYERS = new HashMap<>(); + // The game behind the screen before the screen blurs it, which glass is blurred from + private static SimpleFramebuffer background; + + private GlassRenderer() { + } + + // Saves the main framebuffer for glass. Call each frame before the screen blurs its background. + public static void saveBackground() { + background = copy(SaturnClient.client.getFramebuffer(), background); + } + + // Copies the saved background and blurs the copy by blur. Call once a frame for each blur, before + // drawing glass with it. + public static void captureBackdrop(int blur) { + Framebuffer from = background != null ? background : SaturnClient.client.getFramebuffer(); + SimpleFramebuffer backdrop = copy(from, BACKDROPS.get(blur)); + BACKDROPS.put(blur, backdrop); + + PostEffectProcessor processor = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR, + DefaultFramebufferSet.MAIN_ONLY); + if (processor != null) { + processor.setUniforms("Radius", blur); + processor.render(backdrop, POOL); + } + POOL.decrementLifespan(); + release(blur); + SaturnClient.client.getFramebuffer().beginWrite(false); + } + + // Draws glass through mask with the copy blurred by blur + public static RenderLayer getLayer(Identifier mask, int blur) { + return LAYERS.computeIfAbsent(mask + " " + blur, key -> RenderLayer.of("saturn_glass", + VertexFormats.POSITION_TEXTURE_COLOR, VertexFormat.DrawMode.QUADS, 786432, + RenderLayer.MultiPhaseParameters.builder() + .texture(new RenderPhase.TextureBase(() -> { + RenderSystem.setShaderTexture(0, mask); + RenderSystem.setShaderTexture(1, BACKDROPS.get(blur).getColorAttachment()); + }, () -> { + })) + .program(new RenderPhase.ShaderProgram(PROGRAM)) + .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) + .build(false))); + } + + // Frees the copies of strengths that are no longer set, besides kept + private static void release(int kept) { + BACKDROPS.entrySet().removeIf(entry -> { + boolean unused = entry.getKey() != kept && entry.getKey() != Config.menuBlur.value + && entry.getKey() != Config.widgetBlur.value; + if (unused) { + entry.getValue().delete(); + } + return unused; + }); + } + + // Copies from into to, made or resized to match + private static SimpleFramebuffer copy(Framebuffer from, SimpleFramebuffer to) { + int width = from.textureWidth; + int height = from.textureHeight; + + if (to == null) { + to = new SimpleFramebuffer(width, height, false); + } else if (to.textureWidth != width || to.textureHeight != height) { + to.resize(width, height); + } + to.setTexFilter(GL11.GL_LINEAR); + + GlStateManager._glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, from.fbo); + GlStateManager._glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, to.fbo); + GlStateManager._glBlitFrameBuffer(0, 0, width, height, 0, 0, width, height, GL11.GL_COLOR_BUFFER_BIT, + GL11.GL_NEAREST); + SaturnClient.client.getFramebuffer().beginWrite(false); + return to; + } +} +*///?} diff --git a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java index 3cdb564..3398516 100644 --- a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java +++ b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java @@ -5,7 +5,9 @@ package org.saturnclient.impl.ui; //? if >=1.21.6 { import java.util.ArrayDeque; import java.util.Deque; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import org.jetbrains.annotations.Nullable; import org.joml.Matrix3x2f; @@ -40,7 +42,6 @@ import net.minecraft.client.render.entity.state.EntityRenderState; import net.minecraft.client.render.item.KeyedItemRenderState; //?} else //import net.minecraft.client.render.item.ItemRenderState; -//? if >=1.21.11 import net.minecraft.client.texture.AbstractTexture; import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.TextureSetup; @@ -61,6 +62,8 @@ public class RenderScopeImpl implements RenderScope { private ScissorStack scissorStack; private int opacity = 0xFF << 24; + // The glass blurs made for this scope's frame + private final Set capturedBlurs = new HashSet<>(); public RenderScopeImpl(DrawContext context) { this(context.getMatrices(), ((DrawContextAccessor) (Object) context).getState()); @@ -230,6 +233,25 @@ public class RenderScopeImpl implements RenderScope { this.scissorStack.peekLast())); } + @Override + public void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) { + int color = getColor(0xFFFFFFFF); + if (mask == null || color >>> 24 == 0) + return; + + if (capturedBlurs.add(blur)) { + GlassRenderer.captureBackdrop(blur); + } + + AbstractTexture maskTexture = SaturnClient.client.getTextureManager().getTexture((Identifier) (Object) mask); + this.state.addSimpleElement(new TexturedQuadGuiElementRenderState( + GlassRenderer.PIPELINE, + GlassRenderer.textures(maskTexture, blur), + 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) { @@ -368,7 +390,9 @@ public class RenderScopeImpl implements RenderScope { //?} else { /*import java.util.ArrayDeque; import java.util.Deque; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import java.util.function.Consumer; import org.jetbrains.annotations.Nullable; @@ -421,6 +445,8 @@ public class RenderScopeImpl implements RenderScope { public VertexConsumerProvider.Immediate vertexConsumers; private ScissorStack scissorStack = new ScissorStack(); private int opacity = 255 << 24; + // The glass blurs made for this scope's frame + private final Set capturedBlurs = new HashSet<>(); private final ItemRenderState itemRenderState; // ---------------------------- @@ -660,6 +686,33 @@ public class RenderScopeImpl implements RenderScope { matrices.pop(); } + @Override + public void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) { + int color = getColor(0xFFFFFFFF); + if (mask == null || color >>> 24 == 0) + return; + + if (capturedBlurs.add(blur)) { + GlassRenderer.captureBackdrop(blur); + } + + // In quarter units, as drawTexturedQuad draws + int x1 = x * 4; + int x2 = (x + width) * 4; + int y1 = y * 4; + int y2 = (y + height) * 4; + + matrices.push(); + matrices.scale(0.25f, 0.25f, 1.0f); + Matrix4f matrix4f = matrices.peek().getPositionMatrix(); + VertexConsumer vertexConsumer = vertexConsumers.getBuffer(GlassRenderer.getLayer((Identifier) (Object) mask, blur)); + vertexConsumer.vertex(matrix4f, x1, y1, 0).texture(0, 0).color(color); + vertexConsumer.vertex(matrix4f, x1, y2, 0).texture(0, 1).color(color); + vertexConsumer.vertex(matrix4f, x2, y2, 0).texture(1, 1).color(color); + vertexConsumer.vertex(matrix4f, x2, y1, 0).texture(1, 0).color(color); + matrices.pop(); + } + // ---------------------------- // Items // ---------------------------- diff --git a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java index 827db8a..908ad91 100644 --- a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java +++ b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java @@ -3,6 +3,7 @@ package org.saturnclient.impl.ui; import java.time.Duration; import java.time.Instant; +import org.saturnclient.config.Config; import org.saturnclient.ui.RenderScope; import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.SaturnScreen.ScreenProvider; @@ -86,8 +87,10 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { } long elapsed = Duration.between(screen.start, Instant.now()).toMillis(); - mouseX *= 2; - mouseY *= 2; + screen.updateScale(); + + mouseX = (int) (mouseX / screen.scale); + mouseY = (int) (mouseY / screen.scale); // From 1.21.6 the background, blur included, is drawn in renderBackground. //? if <1.21.6 { @@ -95,12 +98,18 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, screen.backgroundOpacity, delta); } - PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect( + // Glass is blurred from the game as it is before this screen blurs it. + if (Config.usesGlass()) { + context.draw(); + GlassRenderer.saveBackground(); + } + + float radius = screen.getBackgroundBlur(); + PostEffectProcessor postEffectProcessor = radius < 1 ? null : this.client.getShaderLoader().loadPostEffect( Identifier.ofVanilla("blur"), DefaultFramebufferSet.MAIN_ONLY); if (postEffectProcessor != null) { - float radius = screen.getBackgroundBlur(); //? if >=1.21.5 postEffectProcessor.render(this.client.getFramebuffer(), this.pool, pass -> pass.setUniform("Radius", radius)); //? if <1.21.5 @@ -245,11 +254,11 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { @Override public int getWidth() { - return width * 2; + return width; } @Override public int getHeight() { - return height * 2; + return height; } } \ No newline at end of file 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..7d5ee7e --- /dev/null +++ b/src/main/resources/assets/saturnclient/shaders/core/glass.fsh @@ -0,0 +1,28 @@ +#version 330 + +// Saturn's glass: the blurred game behind the screen, cut to a panel's shape. For 1.21.6 and +// later; glass_pre_1_21_6.fsh (with glass.json, the program 1.21.4 loads) is for older versions. + +// Vanilla's own, since its layout changes between versions (1.21.11 dropped LineWidth) +#moj_import + +// 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; +} diff --git a/src/main/resources/assets/saturnclient/shaders/core/glass.json b/src/main/resources/assets/saturnclient/shaders/core/glass.json new file mode 100644 index 0000000..6aaac85 --- /dev/null +++ b/src/main/resources/assets/saturnclient/shaders/core/glass.json @@ -0,0 +1,13 @@ +{ + "vertex": "minecraft:core/position_tex_color", + "fragment": "saturnclient:core/glass_pre_1_21_6", + "samplers": [ + { "name": "Sampler0" }, + { "name": "Sampler1" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] } + ] +} diff --git a/src/main/resources/assets/saturnclient/shaders/core/glass_pre_1_21_6.fsh b/src/main/resources/assets/saturnclient/shaders/core/glass_pre_1_21_6.fsh new file mode 100644 index 0000000..e529808 --- /dev/null +++ b/src/main/resources/assets/saturnclient/shaders/core/glass_pre_1_21_6.fsh @@ -0,0 +1,27 @@ +#version 150 + +// Saturn's glass (see glass.fsh), for 1.21.4 and 1.21.5, which set uniforms one by one instead of in +// blocks. + +uniform vec4 ColorModulator; + +// 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; +}