Blur each glass strength separately on 1.21.11
Every blur strength in use gets its own copy of the game, blurred with vanilla's blur at that radius instead of the animated menu blur. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+1
-1
Submodule common updated: 4d9c629eaf...7bc308155e
@@ -49,13 +49,12 @@ public class GameRendererMixin {
|
|||||||
|
|
||||||
//? if >=1.21.6 {
|
//? if >=1.21.6 {
|
||||||
// From 1.21.6 the menu blur's strength is the vanilla setting, read here every frame. While a
|
// 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. Glass
|
// Saturn screen is open, it's Saturn's background blur instead, faded in with the screen.
|
||||||
// uses the same blur, so this holds even when the screen doesn't blur its whole background.
|
|
||||||
@Redirect(method = "render", at = @At(value = "INVOKE",
|
@Redirect(method = "render", at = @At(value = "INVOKE",
|
||||||
target = "Lnet/minecraft/client/option/GameOptions;getMenuBackgroundBlurrinessValue()I"))
|
target = "Lnet/minecraft/client/option/GameOptions;getMenuBackgroundBlurrinessValue()I"))
|
||||||
private int useSaturnBlur(GameOptions options) {
|
private int useSaturnBlur(GameOptions options) {
|
||||||
if (MinecraftClient.getInstance().currentScreen instanceof SaturnScreenFabric saturnScreen) {
|
if (MinecraftClient.getInstance().currentScreen instanceof SaturnScreenFabric saturnScreen) {
|
||||||
return saturnScreen.screen.getBlurRadius();
|
return saturnScreen.screen.getBackgroundBlur();
|
||||||
}
|
}
|
||||||
|
|
||||||
return options.getMenuBackgroundBlurrinessValue();
|
return options.getMenuBackgroundBlurrinessValue();
|
||||||
|
|||||||
@@ -76,14 +76,6 @@ public class SaturnProviderImpl implements SaturnProvider {
|
|||||||
return (IdentifierRef) (Object) playerSkin.get();
|
return (IdentifierRef) (Object) playerSkin.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsGlass() {
|
|
||||||
//? if >=1.21.11 {
|
|
||||||
return true;
|
|
||||||
//?} else
|
|
||||||
//return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getModVersion() {
|
public String getModVersion() {
|
||||||
return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID)
|
return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID)
|
||||||
|
|||||||
@@ -1,28 +1,38 @@
|
|||||||
package org.saturnclient.impl.ui;
|
package org.saturnclient.impl.ui;
|
||||||
|
|
||||||
//? if >=1.21.11 {
|
//? if >=1.21.11 {
|
||||||
|
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.pipeline.RenderPipeline;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import com.mojang.blaze3d.textures.FilterMode;
|
import com.mojang.blaze3d.textures.FilterMode;
|
||||||
import com.mojang.blaze3d.textures.GpuTextureView;
|
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||||
|
import com.mojang.serialization.JsonOps;
|
||||||
|
|
||||||
import org.saturnclient.saturnclient.SaturnClient;
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
|
|
||||||
import net.minecraft.client.gl.Framebuffer;
|
import net.minecraft.client.gl.Framebuffer;
|
||||||
import net.minecraft.client.gl.GpuSampler;
|
import net.minecraft.client.gl.GpuSampler;
|
||||||
|
import net.minecraft.client.gl.PostEffectPipeline;
|
||||||
import net.minecraft.client.gl.PostEffectProcessor;
|
import net.minecraft.client.gl.PostEffectProcessor;
|
||||||
import net.minecraft.client.gl.RenderPipelines;
|
import net.minecraft.client.gl.RenderPipelines;
|
||||||
import net.minecraft.client.gl.SimpleFramebuffer;
|
import net.minecraft.client.gl.SimpleFramebuffer;
|
||||||
import net.minecraft.client.render.DefaultFramebufferSet;
|
import net.minecraft.client.render.DefaultFramebufferSet;
|
||||||
|
import net.minecraft.client.render.ProjectionMatrix2;
|
||||||
import net.minecraft.client.texture.TextureSetup;
|
import net.minecraft.client.texture.TextureSetup;
|
||||||
import net.minecraft.client.util.Pool;
|
import net.minecraft.client.util.Pool;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
// Glass: a copy of the game behind the screen, blurred with vanilla's blur (as strong as Saturn's
|
// Glass: a copy of the game behind the screen, blurred, drawn through each panel's rounded rectangle
|
||||||
// background blur), drawn through each panel's rounded rectangle texture (shaders/core/glass.fsh).
|
// texture (shaders/core/glass.fsh). There's a copy for each blur strength in use (menus and widgets
|
||||||
// The copy is made while the screen builds its GUI, before the GUI is drawn, so it holds the world
|
// have their own), made while the screen builds its GUI, before the GUI is drawn, so it holds the
|
||||||
// or panorama alone.
|
// world or panorama alone, without the screen's own animated background blur.
|
||||||
// TODO: glass before 1.21.11 (#23). Those versions blur the whole background instead.
|
// TODO: glass before 1.21.11 (#23). Those versions draw translucent panels without it.
|
||||||
public final class GlassRenderer {
|
public final class GlassRenderer {
|
||||||
public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET)
|
public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET)
|
||||||
.withLocation(Identifier.of("saturnclient", "pipeline/glass"))
|
.withLocation(Identifier.of("saturnclient", "pipeline/glass"))
|
||||||
@@ -30,23 +40,29 @@ public final class GlassRenderer {
|
|||||||
.withSampler("Sampler1")
|
.withSampler("Sampler1")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static final Identifier BLUR = Identifier.ofVanilla("blur");
|
private static final Identifier BLUR = Identifier.ofVanilla("post_effect/blur.json");
|
||||||
// For the blur's intermediate target, freed once unused for a few frames
|
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);
|
private static final Pool POOL = new Pool(3);
|
||||||
|
|
||||||
private static SimpleFramebuffer backdrop;
|
// By blur strength. A blur that failed to load is kept as null, so it isn't retried every frame.
|
||||||
|
private static final Map<Integer, PostEffectProcessor> BLURS = new HashMap<>();
|
||||||
|
private static final Map<Integer, SimpleFramebuffer> BACKDROPS = new HashMap<>();
|
||||||
|
|
||||||
private GlassRenderer() {
|
private GlassRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copies the main framebuffer and blurs the copy. Call once a frame, before drawing glass.
|
// Copies the main framebuffer and blurs the copy by blur. Call once a frame for each blur, before
|
||||||
public static void captureBackdrop() {
|
// drawing glass with it.
|
||||||
|
public static void captureBackdrop(int blur) {
|
||||||
Framebuffer main = SaturnClient.client.getFramebuffer();
|
Framebuffer main = SaturnClient.client.getFramebuffer();
|
||||||
int width = main.textureWidth;
|
int width = main.textureWidth;
|
||||||
int height = main.textureHeight;
|
int height = main.textureHeight;
|
||||||
|
|
||||||
|
SimpleFramebuffer backdrop = BACKDROPS.get(blur);
|
||||||
if (backdrop == null) {
|
if (backdrop == null) {
|
||||||
backdrop = new SimpleFramebuffer("Saturn glass", width, height, false);
|
backdrop = new SimpleFramebuffer("Saturn glass " + blur, width, height, false);
|
||||||
|
BACKDROPS.put(blur, backdrop);
|
||||||
} else if (backdrop.textureWidth != width || backdrop.textureHeight != height) {
|
} else if (backdrop.textureWidth != width || backdrop.textureHeight != height) {
|
||||||
backdrop.resize(width, height);
|
backdrop.resize(width, height);
|
||||||
}
|
}
|
||||||
@@ -54,18 +70,53 @@ public final class GlassRenderer {
|
|||||||
RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture(
|
RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture(
|
||||||
main.getColorAttachment(), backdrop.getColorAttachment(), 0, 0, 0, 0, 0, width, height);
|
main.getColorAttachment(), backdrop.getColorAttachment(), 0, 0, 0, 0, 0, width, height);
|
||||||
|
|
||||||
PostEffectProcessor blur = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR,
|
PostEffectProcessor processor = getBlur(blur);
|
||||||
DefaultFramebufferSet.MAIN_ONLY);
|
if (processor != null) {
|
||||||
if (blur != null) {
|
processor.render(backdrop, POOL);
|
||||||
blur.render(backdrop, POOL);
|
|
||||||
}
|
}
|
||||||
POOL.decrementLifespan();
|
POOL.decrementLifespan();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The mask, then the blurred copy, as the glass pipeline samples them
|
// The mask, then the copy blurred by blur, as the glass pipeline samples them
|
||||||
public static TextureSetup textures(GpuTextureView mask, GpuSampler maskSampler) {
|
public static TextureSetup textures(GpuTextureView mask, GpuSampler maskSampler, int blur) {
|
||||||
return TextureSetup.of(mask, maskSampler, backdrop.getColorAttachmentView(),
|
return TextureSetup.of(mask, maskSampler, BACKDROPS.get(blur).getColorAttachmentView(),
|
||||||
RenderSystem.getSamplerCache().get(FilterMode.LINEAR));
|
RenderSystem.getSamplerCache().get(FilterMode.LINEAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//?}
|
//?}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ package org.saturnclient.impl.ui;
|
|||||||
//? if >=1.21.6 {
|
//? if >=1.21.6 {
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.Matrix3x2f;
|
import org.joml.Matrix3x2f;
|
||||||
@@ -62,8 +64,8 @@ public class RenderScopeImpl implements RenderScope {
|
|||||||
private ScissorStack scissorStack;
|
private ScissorStack scissorStack;
|
||||||
private int opacity = 0xFF << 24;
|
private int opacity = 0xFF << 24;
|
||||||
//? if >=1.21.11 {
|
//? if >=1.21.11 {
|
||||||
// Whether the glass backdrop was made for this scope's frame
|
// The glass blurs made for this scope's frame
|
||||||
private boolean backdropCaptured = false;
|
private final Set<Integer> capturedBlurs = new HashSet<>();
|
||||||
//?}
|
//?}
|
||||||
|
|
||||||
public RenderScopeImpl(DrawContext context) {
|
public RenderScopeImpl(DrawContext context) {
|
||||||
@@ -236,20 +238,19 @@ public class RenderScopeImpl implements RenderScope {
|
|||||||
|
|
||||||
//? if >=1.21.11 {
|
//? if >=1.21.11 {
|
||||||
@Override
|
@Override
|
||||||
public void drawGlass(IdentifierRef mask, int x, int y, int width, int height) {
|
public void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) {
|
||||||
int color = getColor(0xFFFFFFFF);
|
int color = getColor(0xFFFFFFFF);
|
||||||
if (mask == null || color >>> 24 == 0)
|
if (mask == null || color >>> 24 == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!backdropCaptured) {
|
if (capturedBlurs.add(blur)) {
|
||||||
GlassRenderer.captureBackdrop();
|
GlassRenderer.captureBackdrop(blur);
|
||||||
backdropCaptured = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractTexture maskTexture = SaturnClient.client.getTextureManager().getTexture((Identifier) (Object) mask);
|
AbstractTexture maskTexture = SaturnClient.client.getTextureManager().getTexture((Identifier) (Object) mask);
|
||||||
this.state.addSimpleElement(new TexturedQuadGuiElementRenderState(
|
this.state.addSimpleElement(new TexturedQuadGuiElementRenderState(
|
||||||
GlassRenderer.PIPELINE,
|
GlassRenderer.PIPELINE,
|
||||||
GlassRenderer.textures(maskTexture.getGlTextureView(), maskTexture.getSampler()),
|
GlassRenderer.textures(maskTexture.getGlTextureView(), maskTexture.getSampler(), blur),
|
||||||
new Matrix3x2f(this.matrices.stack),
|
new Matrix3x2f(this.matrices.stack),
|
||||||
x, y, x + width, y + height, 0, 1, 0, 1, color,
|
x, y, x + width, y + height, 0, 1, 0, 1, color,
|
||||||
this.scissorStack.peekLast()));
|
this.scissorStack.peekLast()));
|
||||||
|
|||||||
Reference in New Issue
Block a user