From 362bc996e0abb149eb63598758a2045f3af3db4a Mon Sep 17 00:00:00 2001 From: RedxAx Date: Thu, 23 Oct 2025 17:03:08 +0300 Subject: [PATCH 1/5] Fixed Off-Hand Items Don't Render. --- .../reglass/mixin/client/InGameHudMixin.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main/java/restudio/reglass/mixin/client/InGameHudMixin.java b/src/main/java/restudio/reglass/mixin/client/InGameHudMixin.java index 0035e35..e0e637c 100644 --- a/src/main/java/restudio/reglass/mixin/client/InGameHudMixin.java +++ b/src/main/java/restudio/reglass/mixin/client/InGameHudMixin.java @@ -6,6 +6,7 @@ import net.minecraft.client.render.RenderTickCounter; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.util.Arm; import net.minecraft.util.profiler.Profilers; import net.minecraft.world.GameMode; import org.spongepowered.asm.mixin.Final; @@ -67,10 +68,10 @@ protected abstract void renderHotbarItem( int hotbarWidth = 182; int hotbarHeight = 22; int x = context.getScaledWindowWidth() / 2 - hotbarWidth / 2; - int y = context.getScaledWindowHeight() - hotbarHeight; + int offhandY = context.getScaledWindowHeight() - hotbarHeight; ReGlassApi.create(context) - .dimensions(x, y, hotbarWidth, hotbarHeight) + .dimensions(x, offhandY, hotbarWidth, hotbarHeight) .cornerRadius(11) .style(new WidgetStyle().tint(0x000000, 0.3f)) .render(); @@ -102,14 +103,14 @@ protected abstract void renderHotbarItem( WidgetStyle selectorStyle = new WidgetStyle().smoothing(-0.005f); ReGlassApi.create(context) - .dimensions(circleX, y, hotbarHeight, hotbarHeight) + .dimensions(circleX, offhandY, hotbarHeight, hotbarHeight) .cornerRadius(0.5f * hotbarHeight) .style(selectorStyle) .render(); for (int i = 0; i < 9; ++i) { int itemX = x + 3 + i * 20; - int itemY = y + 3; + int itemY = offhandY + 3; this.renderHotbarItem( context, itemX, @@ -121,6 +122,20 @@ protected abstract void renderHotbarItem( ); } + ItemStack offHandStack = player.getOffHandStack(); + if (!offHandStack.isEmpty()) { + Arm arm = player.getMainArm().getOpposite(); + int offhandX = (arm == Arm.LEFT ? x - hotbarHeight - 4 : x + hotbarWidth + 4); + + ReGlassApi.create(context) + .dimensions(offhandX, offhandY, hotbarHeight, hotbarHeight) + .cornerRadius(hotbarHeight * 0.5f) + .style(new WidgetStyle().tint(0x000000, 0.3f)) + .render(); + + this.renderHotbarItem(context, offhandX + 3, offhandY + 3, tickCounter, player, offHandStack, 0); + } + LiquidGlassUniforms.get().tryApplyBlur(context); Profilers.get().pop(); From 8e33494c18a43e41ef5b5ba82f0dec56c3fb8b47 Mon Sep 17 00:00:00 2001 From: RedxAx Date: Thu, 23 Oct 2025 17:04:39 +0300 Subject: [PATCH 2/5] Fixed Off-Hand Items Don't Render. --- gradle.properties | 2 +- src/main/resources/fabric.mod.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index c073e24..f2df517 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ loader_version=0.16.14 loom_version=1.11-SNAPSHOT # Mod Properties -mod_version=1.0.1 +mod_version=1.0.2 maven_group=restudio.reglass archives_base_name=reglass diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 163451a..219f6f7 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "reglass", - "version": "1.0.1", + "version": "1.0.2", "name": "ReGlass", "description": "Liquid Glass Implemented On Your Favorite Pixelated And Cubic Game.", "authors": [ From d25e2174a2526694afe541cdf9a72d9fb516732f Mon Sep 17 00:00:00 2001 From: RedxAx Date: Thu, 30 Oct 2025 11:56:57 +0300 Subject: [PATCH 3/5] Removed Debug Effect Toggle Key. --- .../reglass/mixin/client/ScreenMixin.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main/java/restudio/reglass/mixin/client/ScreenMixin.java b/src/main/java/restudio/reglass/mixin/client/ScreenMixin.java index 9b69352..2272961 100644 --- a/src/main/java/restudio/reglass/mixin/client/ScreenMixin.java +++ b/src/main/java/restudio/reglass/mixin/client/ScreenMixin.java @@ -3,13 +3,10 @@ import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.render.state.GuiRenderState; import net.minecraft.client.gui.screen.Screen; -import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import restudio.reglass.client.Config; import restudio.reglass.client.LiquidGlassUniforms; import restudio.reglass.client.api.ReGlassConfig; import restudio.reglass.mixin.accessor.GuiRenderStateAccessor; @@ -31,21 +28,6 @@ public class ScreenMixin { } } - @Inject( - method = "keyPressed", - at = @At("HEAD"), - cancellable = true - ) - private void onKeyPressed( - int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir - ) { - if (keyCode == GLFW.GLFW_KEY_V) { - Config.redesginMinecraft = !Config.redesginMinecraft; - ReGlassConfig.INSTANCE.features.enableRedesign = Config.redesginMinecraft; - cir.setReturnValue(true); - } - } - @Inject( method = "renderDarkening(Lnet/minecraft/client/gui/DrawContext;)V", at = @At("HEAD"), From 73822f89c31eb48b8a965d13c45214772b800eb7 Mon Sep 17 00:00:00 2001 From: RedxAx Date: Fri, 31 Oct 2025 19:51:31 +0300 Subject: [PATCH 4/5] ReGlass 1.1.0 --- gradle.properties | 2 +- .../reglass/client/LiquidGlassUniforms.java | 47 +++- .../reglass/client/api/ReGlassApi.java | 24 +- .../reglass/client/api/ReGlassConfig.java | 6 + .../client/config/ReGlassSettingsIO.java | 18 ++ .../gui/LiquidGlassGuiElementRenderState.java | 2 +- .../reglass/client/runtime/ReGlassAnim.java | 22 ++ .../screen/config/ReGlassConfigScreen.java | 16 +- .../widget/world/WorldListEntryWidget.java | 7 +- .../reglass/mixin/client/InGameHudMixin.java | 5 +- .../mixin/logical/GameRendererMixin.java | 2 +- .../mixin/widgets/DrawContextMixin.java | 10 +- .../mixin/widgets/SliderWidgetMixin.java | 7 +- .../shaders/program/liquid_glass_gui.fsh | 230 +++++++++++------- src/main/resources/fabric.mod.json | 4 +- 15 files changed, 295 insertions(+), 107 deletions(-) diff --git a/gradle.properties b/gradle.properties index f2df517..c252235 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ loader_version=0.16.14 loom_version=1.11-SNAPSHOT # Mod Properties -mod_version=1.0.2 +mod_version=1.1.0 maven_group=restudio.reglass archives_base_name=reglass diff --git a/src/main/java/restudio/reglass/client/LiquidGlassUniforms.java b/src/main/java/restudio/reglass/client/LiquidGlassUniforms.java index 176e981..916e8a5 100644 --- a/src/main/java/restudio/reglass/client/LiquidGlassUniforms.java +++ b/src/main/java/restudio/reglass/client/LiquidGlassUniforms.java @@ -41,6 +41,14 @@ public final class LiquidGlassUniforms { private List usedBlurRadiiOrdered = new ArrayList<>(); private final HashMap blurRadiusToIndex = new HashMap<>(); + private static final class FadeState { + float hover; + float focus; + } + + private final HashMap fades = new HashMap<>(); + private double dtSeconds = 0.0; + private LiquidGlassUniforms() { samplerInfo = RenderSystem.getDevice().createBuffer(() -> "reglass SamplerInfo", 130, 16); @@ -57,6 +65,11 @@ private LiquidGlassUniforms() { calc.putFloat(); calc.putFloat(); calc.putFloat(); + calc.putFloat(); + calc.putFloat(); + calc.putFloat(); + calc.putFloat(); + calc.putFloat(); int customUniformsSize = calc.get(); customUniforms = RenderSystem.getDevice().createBuffer(() -> "reglass CustomUniforms", 130, customUniformsSize); @@ -71,11 +84,12 @@ private LiquidGlassUniforms() { bgConfig = RenderSystem.getDevice().createBuffer(() -> "reglass BgConfig", 130, bgConfigSize); } - public void beginFrame() { + public void beginFrame(double dtSeconds) { widgets.clear(); screenWantsBlur = false; usedBlurRadiiOrdered.clear(); blurRadiusToIndex.clear(); + this.dtSeconds = Math.max(0.0, dtSeconds); } public void setScreenWantsBlur(boolean wantsBlur) { this.screenWantsBlur = wantsBlur; } @@ -118,6 +132,11 @@ public void uploadSharedUniforms() { b.putFloat(ReGlassAnim.INSTANCE.debugStep()); b.putFloat(config.features.pixelatedGrid ? 1.0f : 0.0f); b.putFloat(ReGlassAnim.INSTANCE.pixelatedGridSize()); + b.putFloat(ReGlassAnim.INSTANCE.hoverScalePx()); + b.putFloat(ReGlassAnim.INSTANCE.focusScalePx()); + b.putFloat(ReGlassAnim.INSTANCE.focusBorderWidthPx()); + b.putFloat(ReGlassAnim.INSTANCE.focusBorderIntensity()); + b.putFloat(ReGlassAnim.INSTANCE.focusBorderSpeed()); } try (var map = RenderSystem.getDevice().createCommandEncoder().mapBuffer(bgConfig, false, true)) { @@ -140,6 +159,23 @@ public void addWidget(LiquidGlassGuiElementRenderState element) { widgets.add(element); } + private static long rectKey(int x1, int y1, int x2, int y2) { + long a = (((long) x1) & 0xFFFFFFFFL) | ((((long) y1) & 0xFFFFFFFFL) << 32); + long b = (((long) x2) & 0xFFFFFFFFL) | ((((long) y2) & 0xFFFFFFFFL) << 32); + long h = 1469598103934665603L; + h ^= a; h *= 1099511628211L; + h ^= b; h *= 1099511628211L; + return h; + } + + private float smoothToward(float current, float target, double dt, float tau) { + if (tau <= 1e-5f) return target; + float a = (float) (1.0 - Math.exp(-Math.max(0.0, dt) / Math.max(1e-4, tau))); + float v = current + (target - current) * a; + if (Math.abs(v - target) < 1e-4f) return target; + return v; + } + public void uploadWidgetInfo() { MinecraftClient mc = MinecraftClient.getInstance(); int fbH = mc.getFramebuffer().textureHeight; @@ -259,7 +295,14 @@ public void uploadWidgetInfo() { int radius = Math.max(0, s.getBlurRadius()); Integer idx = blurRadiusToIndex.get(radius); if (idx == null) idx = 0; - b.putVec4((float) idx, 0f, 0f, 0f); + var w = widgets.get(i); + long key = rectKey(w.x1(), w.y1(), w.x2(), w.y2()); + FadeState fs = fades.computeIfAbsent(key, k -> new FadeState()); + fs.hover = smoothToward(fs.hover, Math.max(0f, Math.min(1f, w.hover())), dtSeconds, 0.12f); + fs.focus = smoothToward(fs.focus, Math.max(0f, Math.min(1f, w.focus())), dtSeconds, 0.18f); + double h = Math.sin(w.x1() * 12.9898 + w.y1() * 78.233 + i * 37.719); + float seed = (float) (h - Math.floor(h)); + b.putVec4((float) idx, fs.hover, fs.focus, seed); } else b.putVec4(0f, 0f, 0f, 0f); } } diff --git a/src/main/java/restudio/reglass/client/api/ReGlassApi.java b/src/main/java/restudio/reglass/client/api/ReGlassApi.java index e385326..1060bc8 100644 --- a/src/main/java/restudio/reglass/client/api/ReGlassApi.java +++ b/src/main/java/restudio/reglass/client/api/ReGlassApi.java @@ -27,6 +27,8 @@ public static class Builder { private float cornerRadius = -1f; @Nullable private Text text = null; private WidgetStyle style = new WidgetStyle(); + private float hoverAmount = 0f; + private float focusAmount = 0f; private Builder(DrawContext context) { this.context = context; @@ -70,11 +72,31 @@ public Builder style(WidgetStyle style) { return this; } + public Builder hover(float amount) { + if (Float.isNaN(amount)) amount = 0f; + this.hoverAmount = Math.max(0f, Math.min(1f, amount)); + return this; + } + + public Builder focus(float amount) { + if (Float.isNaN(amount)) amount = 0f; + this.focusAmount = Math.max(0f, Math.min(1f, amount)); + return this; + } + + public Builder selected(float amount) { + return this.focus(amount); + } + public void render() { float finalCornerRadius = this.cornerRadius < 0 ? 0.5f * Math.min(this.width, this.height) : this.cornerRadius; Matrix3x2f pose = new Matrix3x2f(context.getMatrices()); ScreenRect scissorRect = ((DrawContextAccessor) context).getScissorStack().peekLast(); - context.state.addSpecialElement(new LiquidGlassGuiElementRenderState(this.x, this.y, this.x + this.width, this.y + this.height, finalCornerRadius, this.text, this.style, pose, scissorRect)); + context.state.addSpecialElement(new LiquidGlassGuiElementRenderState( + this.x, this.y, this.x + this.width, this.y + this.height, + finalCornerRadius, this.text, this.style, pose, scissorRect, + this.hoverAmount, this.focusAmount + )); } } } \ No newline at end of file diff --git a/src/main/java/restudio/reglass/client/api/ReGlassConfig.java b/src/main/java/restudio/reglass/client/api/ReGlassConfig.java index d5e3bf0..991f50c 100644 --- a/src/main/java/restudio/reglass/client/api/ReGlassConfig.java +++ b/src/main/java/restudio/reglass/client/api/ReGlassConfig.java @@ -45,6 +45,12 @@ public class ReGlassConfig { public float debugStep = 9.0f; public float pixelatedGridSize = 8.0f; + public float hoverScalePx = 1.5f; + public float focusScalePx = 2.5f; + public float focusBorderWidthPx = 2.0f; + public float focusBorderIntensity = 0.75f; + public float focusBorderSpeed = 1.6f; + private ReGlassConfig() {} public static class Features { diff --git a/src/main/java/restudio/reglass/client/config/ReGlassSettingsIO.java b/src/main/java/restudio/reglass/client/config/ReGlassSettingsIO.java index 2e92672..9c976f5 100644 --- a/src/main/java/restudio/reglass/client/config/ReGlassSettingsIO.java +++ b/src/main/java/restudio/reglass/client/config/ReGlassSettingsIO.java @@ -81,6 +81,12 @@ private static Data snapshot() { d.pixelatedGridSize = c.pixelatedGridSize; + d.hoverScalePx = c.hoverScalePx; + d.focusScalePx = c.focusScalePx; + d.focusBorderWidthPx = c.focusBorderWidthPx; + d.focusBorderIntensity = c.focusBorderIntensity; + d.focusBorderSpeed = c.focusBorderSpeed; + return d; } @@ -121,6 +127,12 @@ public static void apply(Data d) { c.defaultGlareAngleRad = d.glareAngleRad; c.pixelatedGridSize = d.pixelatedGridSize; + + c.hoverScalePx = d.hoverScalePx; + c.focusScalePx = d.focusScalePx; + c.focusBorderWidthPx = d.focusBorderWidthPx; + c.focusBorderIntensity = d.focusBorderIntensity; + c.focusBorderSpeed = d.focusBorderSpeed; } public static class Data { @@ -158,5 +170,11 @@ public static class Data { public float glareAngleRad = (float) (-45.0 * Math.PI / 180.0); public float pixelatedGridSize = 8.0f; + + public float hoverScalePx = 1.5f; + public float focusScalePx = 2.5f; + public float focusBorderWidthPx = 2.0f; + public float focusBorderIntensity = 0.75f; + public float focusBorderSpeed = 1.6f; } } \ No newline at end of file diff --git a/src/main/java/restudio/reglass/client/gui/LiquidGlassGuiElementRenderState.java b/src/main/java/restudio/reglass/client/gui/LiquidGlassGuiElementRenderState.java index 925f2c8..8f09641 100644 --- a/src/main/java/restudio/reglass/client/gui/LiquidGlassGuiElementRenderState.java +++ b/src/main/java/restudio/reglass/client/gui/LiquidGlassGuiElementRenderState.java @@ -7,7 +7,7 @@ import org.joml.Matrix3x2f; import restudio.reglass.client.api.WidgetStyle; -public record LiquidGlassGuiElementRenderState(int x1, int y1, int x2, int y2, float cornerRadius, @Nullable Text text, WidgetStyle style, Matrix3x2f pose, @Nullable ScreenRect scissorArea) implements SpecialGuiElementRenderState { +public record LiquidGlassGuiElementRenderState(int x1, int y1, int x2, int y2, float cornerRadius, @Nullable Text text, WidgetStyle style, Matrix3x2f pose, @Nullable ScreenRect scissorArea, float hover, float focus) implements SpecialGuiElementRenderState { @Override public float scale() { diff --git a/src/main/java/restudio/reglass/client/runtime/ReGlassAnim.java b/src/main/java/restudio/reglass/client/runtime/ReGlassAnim.java index d853773..b8f231c 100644 --- a/src/main/java/restudio/reglass/client/runtime/ReGlassAnim.java +++ b/src/main/java/restudio/reglass/client/runtime/ReGlassAnim.java @@ -32,6 +32,12 @@ public final class ReGlassAnim { private float debugStep; private float pixelatedGridSize; + private float hoverScalePx; + private float focusScalePx; + private float focusBorderWidthPx; + private float focusBorderIntensity; + private float focusBorderSpeed; + private ReGlassAnim() {} public void update(ReGlassConfig cfg, double dtSeconds) { @@ -59,6 +65,11 @@ public void update(ReGlassConfig cfg, double dtSeconds) { glareAngleRad = cfg.defaultGlareAngleRad; debugStep = cfg.debugStep; pixelatedGridSize = cfg.pixelatedGridSize; + hoverScalePx = cfg.hoverScalePx; + focusScalePx = cfg.focusScalePx; + focusBorderWidthPx = cfg.focusBorderWidthPx; + focusBorderIntensity = cfg.focusBorderIntensity; + focusBorderSpeed = cfg.focusBorderSpeed; init = true; return; } @@ -83,6 +94,11 @@ public void update(ReGlassConfig cfg, double dtSeconds) { glareAngleRad = lerp(glareAngleRad, cfg.defaultGlareAngleRad, a); debugStep = lerp(debugStep, cfg.debugStep, a); pixelatedGridSize = lerp(pixelatedGridSize, cfg.pixelatedGridSize, a); + hoverScalePx = lerp(hoverScalePx, cfg.hoverScalePx, a); + focusScalePx = lerp(focusScalePx, cfg.focusScalePx, a); + focusBorderWidthPx = lerp(focusBorderWidthPx, cfg.focusBorderWidthPx, a); + focusBorderIntensity = lerp(focusBorderIntensity, cfg.focusBorderIntensity, a); + focusBorderSpeed = lerp(focusBorderSpeed, cfg.focusBorderSpeed, a); } private static float alpha(double dt, float tau) { @@ -120,4 +136,10 @@ private static float lerp(float a, float b, float t) { public float debugStep() { return debugStep; } public float pixelatedGridSize() { return pixelatedGridSize; } + + public float hoverScalePx() { return hoverScalePx; } + public float focusScalePx() { return focusScalePx; } + public float focusBorderWidthPx() { return focusBorderWidthPx; } + public float focusBorderIntensity() { return focusBorderIntensity; } + public float focusBorderSpeed() { return focusBorderSpeed; } } \ No newline at end of file diff --git a/src/main/java/restudio/reglass/client/screen/config/ReGlassConfigScreen.java b/src/main/java/restudio/reglass/client/screen/config/ReGlassConfigScreen.java index 59999b7..1ec8504 100644 --- a/src/main/java/restudio/reglass/client/screen/config/ReGlassConfigScreen.java +++ b/src/main/java/restudio/reglass/client/screen/config/ReGlassConfigScreen.java @@ -126,6 +126,20 @@ protected void init() { addSlider(MappedSlider.floatSlider(widgetX, y, widgetWidth, widgetHeight, Text.literal("Glare Factor"), 0f, 100f, cfg.defaultGlareFactor, v -> cfg.defaultGlareFactor = v.floatValue()), y).active = cfg.features.enableRedesign; y += widgetHeight + gap * 2; + addTitle("Interactions", widgetX, y, widgetWidth); + y += widgetHeight; + + addSlider(MappedSlider.floatSlider(widgetX, y, widgetWidth, widgetHeight, Text.literal("Hover Scale (px)"), 0f, 6f, cfg.hoverScalePx, v -> cfg.hoverScalePx = v.floatValue()), y).active = cfg.features.enableRedesign; + y += widgetHeight + gap; + addSlider(MappedSlider.floatSlider(widgetX, y, widgetWidth, widgetHeight, Text.literal("Focus Scale (px)"), 0f, 8f, cfg.focusScalePx, v -> cfg.focusScalePx = v.floatValue()), y).active = cfg.features.enableRedesign; + y += widgetHeight + gap; + addSlider(MappedSlider.floatSlider(widgetX, y, widgetWidth, widgetHeight, Text.literal("Focus Border Width (px)"), 0f, 6f, cfg.focusBorderWidthPx, v -> cfg.focusBorderWidthPx = v.floatValue()), y).active = cfg.features.enableRedesign; + y += widgetHeight + gap; + addSlider(MappedSlider.floatSlider(widgetX, y, widgetWidth, widgetHeight, Text.literal("Focus Border Intensity"), 0f, 1f, cfg.focusBorderIntensity, v -> cfg.focusBorderIntensity = v.floatValue()), y).active = cfg.features.enableRedesign; + y += widgetHeight + gap; + addSlider(MappedSlider.floatSlider(widgetX, y, widgetWidth, widgetHeight, Text.literal("Focus Border Speed"), 0f, 4f, cfg.focusBorderSpeed, v -> cfg.focusBorderSpeed = v.floatValue()), y).active = cfg.features.enableRedesign; + y += widgetHeight + gap * 2; + addTitle("Debug", widgetX, y, widgetWidth); y += widgetHeight; @@ -250,7 +264,7 @@ public void renderWidget(DrawContext context, int mouseX, int mouseY, float delt public boolean isMouseOver(double mouseX, double mouseY) { return false; } - + @Override protected void appendClickableNarrations(NarrationMessageBuilder builder) {} } diff --git a/src/main/java/restudio/reglass/client/screen/widget/world/WorldListEntryWidget.java b/src/main/java/restudio/reglass/client/screen/widget/world/WorldListEntryWidget.java index c752fdd..debcd16 100644 --- a/src/main/java/restudio/reglass/client/screen/widget/world/WorldListEntryWidget.java +++ b/src/main/java/restudio/reglass/client/screen/widget/world/WorldListEntryWidget.java @@ -81,9 +81,10 @@ private void loadIcon() { public void render(DrawContext context, int index, int x, int y, int width, int height, int mouseX, int mouseY, boolean hovered, float delta) { super.render(context, index, x, y, width, height, mouseX, mouseY, hovered, delta); + boolean isSelected = this.parent.getList().getSelectedEntries().contains(this); WidgetStyle style = defaultStyle; - if (this.parent.getList().getSelectedEntries().contains(this)) { + if (isSelected) { style = selectedStyle; } else if (hovered) { style = hoveredStyle; @@ -93,6 +94,8 @@ public void render(DrawContext context, int index, int x, int y, int width, int .dimensions(x, y, width, height) .cornerRadius(8) .style(style) + .hover(hovered ? 1f : 0f) + .focus(isSelected ? 1f : 0f) .render(); String displayName = summary.getDisplayName(); @@ -137,4 +140,4 @@ public void close() { this.iconTexture = null; } } -} +} \ No newline at end of file diff --git a/src/main/java/restudio/reglass/mixin/client/InGameHudMixin.java b/src/main/java/restudio/reglass/mixin/client/InGameHudMixin.java index e0e637c..809e384 100644 --- a/src/main/java/restudio/reglass/mixin/client/InGameHudMixin.java +++ b/src/main/java/restudio/reglass/mixin/client/InGameHudMixin.java @@ -67,8 +67,8 @@ protected abstract void renderHotbarItem( int hotbarWidth = 182; int hotbarHeight = 22; - int x = context.getScaledWindowWidth() / 2 - hotbarWidth / 2; - int offhandY = context.getScaledWindowHeight() - hotbarHeight; + int x = this.client.getWindow().getScaledWidth() / 2 - hotbarWidth / 2; + int offhandY = this.client.getWindow().getScaledHeight() - hotbarHeight; ReGlassApi.create(context) .dimensions(x, offhandY, hotbarWidth, hotbarHeight) @@ -106,6 +106,7 @@ protected abstract void renderHotbarItem( .dimensions(circleX, offhandY, hotbarHeight, hotbarHeight) .cornerRadius(0.5f * hotbarHeight) .style(selectorStyle) + .focus(1f) .render(); for (int i = 0; i < 9; ++i) { diff --git a/src/main/java/restudio/reglass/mixin/logical/GameRendererMixin.java b/src/main/java/restudio/reglass/mixin/logical/GameRendererMixin.java index 4b93966..33a2c21 100644 --- a/src/main/java/restudio/reglass/mixin/logical/GameRendererMixin.java +++ b/src/main/java/restudio/reglass/mixin/logical/GameRendererMixin.java @@ -30,7 +30,6 @@ public abstract class GameRendererMixin { @Inject(method = "render", at = @At("HEAD")) private void reglass$beginGuiFrame(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci) { - LiquidGlassUniforms.get().beginFrame(); double deltaTicks; try { deltaTicks = tickCounter.getDynamicDeltaTicks(); @@ -38,6 +37,7 @@ public abstract class GameRendererMixin { deltaTicks = 1.0 / 60.0 * 20.0; } double dt = deltaTicks / 20.0; + LiquidGlassUniforms.get().beginFrame(dt); ReGlassAnim.INSTANCE.update(ReGlassConfig.INSTANCE, dt); } diff --git a/src/main/java/restudio/reglass/mixin/widgets/DrawContextMixin.java b/src/main/java/restudio/reglass/mixin/widgets/DrawContextMixin.java index 9a2fc7b..ddca137 100644 --- a/src/main/java/restudio/reglass/mixin/widgets/DrawContextMixin.java +++ b/src/main/java/restudio/reglass/mixin/widgets/DrawContextMixin.java @@ -10,6 +10,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import restudio.reglass.client.api.ReGlassApi; import restudio.reglass.client.api.ReGlassConfig; +import restudio.reglass.client.api.WidgetStyle; @Mixin(DrawContext.class) public abstract class DrawContextMixin { @@ -29,7 +30,14 @@ private void onDrawTexture(RenderPipeline pipeline, Identifier sprite, int x, in || sprite.getPath().equals(BUTTON_HIGHLIGHTED_TEXTURE.getPath()); if (isButtonTexture && (ReGlassConfig.INSTANCE.features.enableRedesign && ReGlassConfig.INSTANCE.features.buttons)) { - ReGlassApi.create((DrawContext)(Object) this).position(x, y).size(width, height).render(); + boolean isHighlighted = sprite.getPath().equals(BUTTON_HIGHLIGHTED_TEXTURE.getPath()); + boolean isDisabled = sprite.getPath().equals(BUTTON_DISABLED_TEXTURE.getPath()); + ReGlassApi.create((DrawContext)(Object) this) + .position(x, y) + .size(width, height) + .hover(isHighlighted ? 1f : 0f) + .style(WidgetStyle.create().tint(isDisabled ? 0xFF000000 : 0xFFFFFFFF, isDisabled ? 0.4f : 0f)) + .render(); ci.cancel(); } } diff --git a/src/main/java/restudio/reglass/mixin/widgets/SliderWidgetMixin.java b/src/main/java/restudio/reglass/mixin/widgets/SliderWidgetMixin.java index f719b87..b8b7ee6 100644 --- a/src/main/java/restudio/reglass/mixin/widgets/SliderWidgetMixin.java +++ b/src/main/java/restudio/reglass/mixin/widgets/SliderWidgetMixin.java @@ -37,12 +37,17 @@ private void onRenderWidget(DrawContext context, int mouseX, int mouseY, float d ci.cancel(); int knobX = (int) (this.getX() + (((SliderWidgetAccessor) this).getValue() * (this.getWidth() - 4))); + boolean hoveredBg = this.isMouseOver(mouseX, mouseY); + boolean knobHovered = mouseX >= knobX && mouseX < knobX + 4 && mouseY >= getY() && mouseY < getY() + getHeight(); + boolean focus = this.isFocused(); - ReGlassApi.create(context).fromWidget(this).render(); + ReGlassApi.create(context).fromWidget(this).hover(hoveredBg ? 1f : 0f).focus(focus ? 1f : 0f).render(); ReGlassApi.create(context) .size(4, getHeight()) .position(knobX, getY()) .style(knobStyle) + .hover(knobHovered ? 1f : 0f) + .focus(focus ? 1f : 0f) .render(); LiquidGlassUniforms.get().tryApplyBlur(context); diff --git a/src/main/resources/assets/reglass/shaders/program/liquid_glass_gui.fsh b/src/main/resources/assets/reglass/shaders/program/liquid_glass_gui.fsh index f66d538..db90f3a 100644 --- a/src/main/resources/assets/reglass/shaders/program/liquid_glass_gui.fsh +++ b/src/main/resources/assets/reglass/shaders/program/liquid_glass_gui.fsh @@ -5,7 +5,12 @@ uniform sampler2D Sampler2; uniform sampler2D Sampler3; uniform sampler2D Sampler4; uniform sampler2D Sampler5; -layout(std140) uniform SamplerInfo { vec2 OutSize; vec2 InSize; }; + +layout(std140) uniform SamplerInfo { + vec2 OutSize; + vec2 InSize; +}; + layout(std140) uniform CustomUniforms { float Time; vec4 Mouse; @@ -16,7 +21,13 @@ layout(std140) uniform CustomUniforms { float DebugStep; float Pixelated; float PixelGridSize; + float HoverScalePx; + float FocusScalePx; + float FocusBorderWidthPx; + float FocusBorderIntensity; + float FocusBorderSpeed; }; + #define MAX_WIDGETS 64 layout(std140) uniform WidgetInfo { float Count; @@ -32,24 +43,31 @@ layout(std140) uniform WidgetInfo { vec4 ShadowColor[MAX_WIDGETS]; vec4 Extra0[MAX_WIDGETS]; }; -layout(std140) uniform BgConfig { float ShadowExpand; float ShadowFactor; vec2 ShadowOffset; }; + +layout(std140) uniform BgConfig { + float ShadowExpand; + float ShadowFactor; + vec2 ShadowOffset; +}; + out vec4 fragColor; -#define PI 3.141592653589793 struct SDFResult { float dist; vec2 normal; float aspect; int index; }; -vec2 screenToUV(vec2 screen, vec2 res) { return (screen.xy - 0.5 * res.xy) / res.y; } +vec2 screenToUV(vec2 screen, vec2 res) { + return (screen.xy - 0.5 * res.xy) / res.y; +} vec3 sdgBox(in vec2 p, in vec2 b, vec4 ra) { ra.xy = (p.x > 0.0) ? ra.xy : ra.zw; float r = (p.y > 0.0) ? ra.x : ra.y; vec2 w = abs(p) - (b - r); - vec2 s = vec2(p.x < 0.0 ? -1 : 1, p.y < 0.0 ? -1 : 1); + vec2 s = vec2(p.x < 0.0 ? -1.0 : 1.0, p.y < 0.0 ? -1.0 : 1.0); float g = max(w.x, w.y); vec2 q = max(w, 0.0); float l = length(q); float dist = (g > 0.0) ? l - r : g - r; - vec2 n = (g > 0.0) ? (q / max(l, 1e-6)) : ((w.x > w.y) ? vec2(1, 0) : vec2(0, 1)); + vec2 n = (g > 0.0) ? (q / max(l, 1e-6)) : ((w.x > w.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0)); return vec3(dist, s * n); } @@ -82,24 +100,35 @@ vec4 sampleBlur(int idx, vec2 uv) { SDFResult fieldWidgets(vec2 p, vec2 inSize, vec2 fragCoord) { int n = int(Count + 0.5); if (n == 0) return SDFResult(1e6, vec2(0.0), 1.0, -1); + SDFResult pos = SDFResult(1e6, vec2(0.0), 1.0, -1); bool hasPos = false; for (int i = 0; i < MAX_WIDGETS; i++) { if (i >= n) break; if (Smoothings[i].x < 0.0) continue; + vec4 sc = ScissorRects[i]; if (fragCoord.x < sc.x || fragCoord.y < sc.y || fragCoord.x > sc.z || fragCoord.y > sc.w) continue; + vec4 rc = Rects[i]; vec4 rr = Rads[i]; vec2 cPx = vec2(rc.x + 0.5 * rc.z, rc.y + 0.5 * rc.w); vec2 c = screenToUV(cPx, inSize); vec2 b = 0.5 * vec2(rc.z, rc.w) / inSize.y; vec4 rad = rr / inSize.y; + vec3 g = sdgBox(p - c, b, rad); + + vec4 extra = Extra0[i]; + float scaleOff = (HoverScalePx * extra.y + FocusScalePx * extra.z) / inSize.y; + float dist = g.x - scaleOff; + float aspect = min(rc.z, rc.w) / max(rc.z, rc.w); - SDFResult s = SDFResult(g.x, g.yz, aspect, i); - if (!hasPos) { pos = s; hasPos = true; } else { pos = opSmoothUnion(pos, s, Smoothings[i].x); } + SDFResult s = SDFResult(dist, g.yz, aspect, i); + + if (!hasPos) { pos = s; hasPos = true; } + else { pos = opSmoothUnion(pos, s, Smoothings[i].x); } } SDFResult f = pos; @@ -107,17 +136,26 @@ SDFResult fieldWidgets(vec2 p, vec2 inSize, vec2 fragCoord) { for (int i = 0; i < MAX_WIDGETS; i++) { if (i >= n) break; if (Smoothings[i].x >= 0.0) continue; + vec4 sc = ScissorRects[i]; if (fragCoord.x < sc.x || fragCoord.y < sc.y || fragCoord.x > sc.z || fragCoord.y > sc.w) continue; + vec4 rc = Rects[i]; vec4 rr = Rads[i]; vec2 cPx = vec2(rc.x + 0.5 * rc.z, rc.y + 0.5 * rc.w); vec2 c = screenToUV(cPx, inSize); vec2 b = 0.5 * vec2(rc.z, rc.w) / inSize.y; vec4 rad = rr / inSize.y; + vec3 g = sdgBox(p - c, b, rad); + + vec4 extra = Extra0[i]; + float scaleOff = (HoverScalePx * extra.y + FocusScalePx * extra.z) / inSize.y; + float dist = g.x - scaleOff; + float aspect = min(rc.z, rc.w) / max(rc.z, rc.w); - SDFResult s = SDFResult(g.x, g.yz, aspect, i); + SDFResult s = SDFResult(dist, g.yz, aspect, i); + float repulsion = -Smoothings[i].x; SDFResult se = SDFResult(s.dist - repulsion, s.normal, s.aspect, s.index); f = opHardSubtract(f, se); @@ -127,33 +165,31 @@ SDFResult fieldWidgets(vec2 p, vec2 inSize, vec2 fragCoord) { return f; } -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} -float vec2ToAngle(vec2 v) { float a = atan(v.y, v.x); if (a < 0.0) a += 2.0 * PI; return a; } -vec3 vec2ToRgb(vec2 v) { float hue = vec2ToAngle(normalize(v)) / (2.0 * PI); return hsv2rgb(vec3(hue, 1.0, 1.0)); } - void main() { - vec2 inSize = InSize; if (inSize.x <= 0.0 || inSize.y <= 0.0) inSize = vec2(textureSize(Sampler0, 0)); - vec2 original_coord = gl_FragCoord.xy; - vec2 original_UV = original_coord / inSize; - vec3 base = texture(Sampler0, original_UV).rgb; + vec2 inSize = InSize; + if (inSize.x <= 0.0 || inSize.y <= 0.0) inSize = vec2(textureSize(Sampler0, 0)); + + vec2 coord = gl_FragCoord.xy; + vec2 uv = coord / inSize; + vec3 base = texture(Sampler0, uv).rgb; vec3 baseShadowed = base; int n = int(Count + 0.5); for (int i = 0; i < MAX_WIDGETS; i++) { if (i >= n) break; + vec4 sc = ScissorRects[i]; - if (original_coord.x < sc.x || original_coord.y < sc.y || original_coord.x > sc.z || original_coord.y > sc.w) continue; + if (coord.x < sc.x || coord.y < sc.y || coord.x > sc.z || coord.y > sc.w) continue; + vec4 rc = Rects[i]; vec4 rr = Rads[i]; vec2 cPx = vec2(rc.x + 0.5 * rc.z, rc.y + 0.5 * rc.w); - vec2 pShadow = screenToUV(original_coord + Shadow0[i].zw, inSize); + + vec2 pShadow = screenToUV(coord + Shadow0[i].zw, inSize); vec2 c = screenToUV(cPx, inSize); vec2 b = 0.5 * vec2(rc.z, rc.w) / inSize.y; vec4 rad = rr / inSize.y; + vec3 g = sdgBox(pShadow - c, b, rad); float expand = max(Shadow0[i].x, 1e-4); float factor = Shadow0[i].y; @@ -163,47 +199,53 @@ void main() { baseShadowed = mix(baseShadowed, scol, sa); } - vec2 p = screenToUV(original_coord, inSize); - SDFResult f = fieldWidgets(p, inSize, original_coord); + vec2 p = screenToUV(coord, inSize); + SDFResult f = fieldWidgets(p, inSize, coord); float merged = f.dist; int STEP = int(DebugStep + 0.5); - - if (STEP <= 0) { - float px = 2.0 / inSize.y; - vec3 col = (merged > 0.0 ? vec3(1.0) * merged : vec3(1.0) * (-merged) * 2.0) * 3.0; - col = mix(col, vec3(1.0), 1.0 - smoothstep(0.5 / inSize.y - px, 0.5 / inSize.y + px, abs(merged))); - fragColor = vec4(col, 1.0); return; - } if (STEP <= 1) { - float px = 2.0 / inSize.y; vec3 col = (merged > 0.0) ? vec3(0.9, 0.6, 0.3) : vec3(0.65, 0.85, 1.0); col *= 1.0 - exp(-0.03 * abs(merged) * inSize.y); - col *= 0.6 + 0.4 * smoothstep(-0.5, 0.5, cos(0.25 * abs(merged) * inSize.y * 2.0)); - col = mix(col, vec3(1.0), 1.0 - smoothstep(1.5 / inSize.y - px, 1.5 / inSize.y + px, abs(merged))); - fragColor = vec4(col, 1.0); return; + fragColor = vec4(col, 1.0); + return; } - if (merged >= 0.0) { fragColor = vec4(baseShadowed, 1.0); return; } - vec2 coord_pixelated = original_coord; + if (merged >= 0.0) { + fragColor = vec4(baseShadowed, 1.0); + return; + } + + vec2 coordPix = coord; if (Pixelated > 0.5) { - coord_pixelated = floor(original_coord / PixelGridSize) * PixelGridSize + 0.5 * PixelGridSize; + coordPix = floor(coord / PixelGridSize) * PixelGridSize + 0.5 * PixelGridSize; } - vec2 UV_pixelated = coord_pixelated / inSize; + vec2 uvPix = coordPix / inSize; int idx = f.index; vec4 o0 = Optics0[idx]; vec4 o1 = Optics1[idx]; vec4 o2 = Optics2[idx]; vec4 tint = Tints[idx]; + vec4 extra = Extra0[idx]; + + float refThickness = o0.x; + float refFactor = o0.y; + float refDisp = o0.z; + float refFresRange = o0.w; - float refThickness = o0.x, refFactor = o0.y, refDisp = o0.z, refFresRange = o0.w; - float refFresHard = o1.x / 100.0, refFresFac = o1.y / 100.0, glareRange = o1.z, glareHard = o1.w / 100.0; - float glareConv = o2.x / 100.0, glareOpp = o2.y / 100.0, glareFac = o2.z / 100.0, glareAngle = o2.w; + float refFresHard = o1.x / 100.0; + float refFresFac = o1.y / 100.0; + float glareRange = o1.z; + float glareHard = o1.w / 100.0; - vec2 normal = f.normal; float nlen = length(normal); if (nlen > 1e-6) normal /= nlen; + float hoverK = extra.y; + float focusK = extra.z; + int blurIndex = int(extra.x + 0.5); - if (STEP <= 2) { vec3 normalColor = vec2ToRgb(normal); fragColor = vec4(normalColor, nlen); return; } + vec2 normal = f.normal; + float nlen = length(normal); + if (nlen > 1e-6) normal /= nlen; float nmerged = -merged * inSize.y; float xR = 1.0 - nmerged / max(refThickness, 1e-6); @@ -212,62 +254,66 @@ void main() { float edgeFactor = -tan(thetaT - thetaI); if (nmerged >= refThickness) edgeFactor = 0.0; - if (STEP <= 3) { fragColor = vec4(vec3(edgeFactor), 1.0); return; } - - if (STEP <= 4) { - vec3 normalColor = vec2ToRgb(normal); - float ring = max(edgeFactor, 0.0); - vec3 outC = clamp(normalColor * ring * 4.0, 0.0, 1.0); - fragColor = vec4(outC, 1.0); return; - } - - int blurIndex = int(Extra0[idx].x + 0.5); vec2 refrOffset = -normal * edgeFactor * 0.08 * vec2(inSize.y / inSize.x, 1.0); + vec4 blurred = sampleBlur(blurIndex, uvPix + refrOffset); - if (STEP <= 5) { vec4 outColor = sampleBlur(blurIndex, UV_pixelated); fragColor = outColor; return; } - if (STEP <= 6) { vec4 outColor = sampleBlur(blurIndex, UV_pixelated + refrOffset); fragColor = outColor; return; } - - float fresnelFactor = clamp(pow(1.0 + merged * inSize.y / 1500.0 * pow(500.0 / max(refFresRange, 1e-6), 2.0) + refFresHard, 5.0), 0.0, 1.0); - - if (STEP <= 7) { - vec4 blurredPixel = sampleBlur(blurIndex, UV_pixelated + refrOffset); - vec4 outColor = mix(blurredPixel, vec4(1.0), (fresnelFactor * refFresFac * 0.7) * nlen); - fragColor = outColor; return; - } - - if (STEP <= 8) { - vec4 blurredPixel = sampleBlur(blurIndex, UV_pixelated + refrOffset); - float glareGeo = clamp(pow(1.0 + merged * inSize.y / 1500.0 * pow(500.0 / max(glareRange, 1e-6), 2.0) + glareHard, 5.0), 0.0, 1.0); - float ang = (atan(normal.y, normal.x) - PI * 0.25 + glareAngle) * 2.0; - int farSide = 0; if ((ang > PI * (2.0 - 0.5) && ang < PI * (4.0 - 0.5)) || (ang < PI * (0.0 - 0.5))) farSide = 1; - float gAngle = (0.5 + sin(ang) * 0.5) * (farSide == 1 ? 1.2 * glareOpp : 1.2) * glareFac; - gAngle = clamp(pow(gAngle, 0.1 + glareConv * 2.0), 0.0, 1.0); - vec4 outColor = mix(blurredPixel, vec4(1.0), gAngle * glareGeo); - fragColor = outColor; return; - } - - vec4 blurredPixel = sampleBlur(blurIndex, UV_pixelated + refrOffset); vec4 dispPixel; - { const float NR = 0.98, NG = 1.00, NB = 1.02; - dispPixel.r = sampleBlur(blurIndex, UV_pixelated + refrOffset * (1.0 - (NR - 1.0) * refDisp)).r; - dispPixel.g = sampleBlur(blurIndex, UV_pixelated + refrOffset * (1.0 - (NG - 1.0) * refDisp)).g; - dispPixel.b = sampleBlur(blurIndex, UV_pixelated + refrOffset * (1.0 - (NB - 1.0) * refDisp)).b; - dispPixel.a = 1.0; } + { + const float NR = 0.985; + const float NG = 1.000; + const float NB = 1.015; + dispPixel.r = sampleBlur(blurIndex, uvPix + refrOffset * (1.0 - (NR - 1.0) * refDisp)).r; + dispPixel.g = sampleBlur(blurIndex, uvPix + refrOffset * (1.0 - (NG - 1.0) * refDisp)).g; + dispPixel.b = sampleBlur(blurIndex, uvPix + refrOffset * (1.0 - (NB - 1.0) * refDisp)).b; + dispPixel.a = 1.0; + } vec4 outColor = mix(dispPixel, vec4(tint.rgb, 1.0), tint.a * 0.8); - float fres = fresnelFactor; + float fresnelFactor = clamp(pow(1.0 + merged * inSize.y / 1500.0 * pow(500.0 / max(refFresRange, 1e-6), 2.0) + refFresHard, 5.0), 0.0, 1.0); vec3 fresTint = mix(vec3(1.0), tint.rgb, tint.a * 0.5); - outColor = mix(outColor, vec4(fresTint, 1.0), fres * refFresFac * 0.7 * nlen); + outColor = mix(outColor, vec4(fresTint, 1.0), fresnelFactor * refFresFac * 0.7 * nlen); float glareGeo = clamp(pow(1.0 + merged * inSize.y / 1500.0 * pow(500.0 / max(glareRange, 1e-6), 2.0) + glareHard, 5.0), 0.0, 1.0); - float ang = (atan(normal.y, normal.x) - PI * 0.25 + glareAngle) * 2.0; - int farSide = 0; if ((ang > PI * (2.0 - 0.5) && ang < PI * (4.0 - 0.5)) || (ang < PI * (0.0 - 0.5))) farSide = 1; - float gAngle = (0.5 + sin(ang) * 0.5) * (farSide == 1 ? 1.2 * glareOpp : 1.2) * glareFac; - gAngle = clamp(pow(gAngle, 0.1 + glareConv * 2.0), 0.0, 1.0); - vec3 glareMix = mix(blurredPixel.rgb, tint.rgb, tint.a * 0.5); - vec3 glareCol = glareMix; - outColor = mix(outColor, vec4(glareCol, 1.0), gAngle * glareGeo * nlen); + vec3 glareMix = mix(blurred.rgb, tint.rgb, tint.a * 0.5); + outColor.rgb = mix(outColor.rgb, glareMix, 0.25 * glareGeo * nlen); + + float edgeProx = exp(-abs(merged) * inSize.y / 9.0); + + if (hoverK > 0.0001) { + vec2 tdir = normalize(vec2(-normal.y, normal.x)); + float refBoost = 1.0 + 0.35 * hoverK * edgeProx; + vec2 refrHover = -normal * edgeFactor * 0.08 * refBoost * vec2(inSize.y / inSize.x, 1.0); + vec2 chromaT = tdir * 0.0008 * hoverK * edgeProx * vec2(inSize.y / inSize.x, 1.0); + vec3 subtle = vec3( + sampleBlur(blurIndex, uvPix + refrHover + chromaT).r, + sampleBlur(blurIndex, uvPix + refrHover).g, + sampleBlur(blurIndex, uvPix + refrHover - chromaT).b + ); + vec3 add = mix(subtle, vec3(1.0), 0.08) * (0.15 + 0.35 * edgeProx) * hoverK; + outColor.rgb = clamp(outColor.rgb + add, 0.0, 1.0); + } + + if (focusK > 0.0001) { + vec4 rc = Rects[idx]; + vec2 centerPx = vec2(rc.x + 0.5 * rc.z, rc.y + 0.5 * rc.w); + vec2 v = (coord - centerPx) / inSize.y; + + float phi = atan(v.y, v.x); + float phi0 = Time * FocusBorderSpeed; + float c = 0.5 + 0.5 * cos(phi - phi0); + float sweep = smoothstep(0.75, 1.0, c); + + float mergedPx = merged * inSize.y; + float w = max(FocusBorderWidthPx, 1e-3); + float outside = step(0.0, mergedPx); + float band = exp(-pow(max(0.0, mergedPx) / w, 2.0)) * outside; + + float intensity = FocusBorderIntensity * focusK; + vec3 ringTint = mix(vec3(1.0), tint.rgb, 0.2); + vec3 ring = ringTint * (band * sweep) * intensity; + outColor.rgb = clamp(outColor.rgb + ring, 0.0, 1.0); + } vec3 finalCol = mix(outColor.rgb, baseShadowed, smoothstep(-0.001, 0.001, merged)); fragColor = vec4(finalCol, 1.0); diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 219f6f7..4591118 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,13 +1,13 @@ { "schemaVersion": 1, "id": "reglass", - "version": "1.0.2", + "version": "1.1.0", "name": "ReGlass", "description": "Liquid Glass Implemented On Your Favorite Pixelated And Cubic Game.", "authors": [ "RedxAx" ], - "license": "CC0-1.0", + "license": "MIT", "environment": "client", "entrypoints": { "client": [ From 415de56c13924b82fd9bb31fc5eabd46a5d52cd1 Mon Sep 17 00:00:00 2001 From: "DESKTOP-U45FQMV\\34355" <3435578673@qq.com> Date: Thu, 9 Jul 2026 01:13:21 +0800 Subject: [PATCH 5/5] feat(ui): implement iOS-style release trigger and pressed state for ButtonWidget Modify ButtonWidget to fire onClick on mouse release (mouseUp & isMouseOver) rather than on mouse press (mouseDown), aligning interaction behavior with iOS HIG (Human Interface Guidelines). Also introduce a focus state that persists while the button is held down, providing focus feedback prior to the click being finalized. This enhances the overall Liquid Glass UX parity with native mobile interactions. --- .../extensions/PressableWidgetExtension.java | 10 +++ .../mixin/widgets/ClickableWidgetMixin.java | 52 ++++++++++++++ .../mixin/widgets/DrawContextMixin.java | 44 ------------ .../mixin/widgets/PressableWidgetMixin.java | 68 +++++++++++++++++++ src/main/resources/reglass.mixins.json | 3 +- 5 files changed, 132 insertions(+), 45 deletions(-) create mode 100644 src/main/java/restudio/reglass/extensions/PressableWidgetExtension.java create mode 100644 src/main/java/restudio/reglass/mixin/widgets/ClickableWidgetMixin.java delete mode 100644 src/main/java/restudio/reglass/mixin/widgets/DrawContextMixin.java create mode 100644 src/main/java/restudio/reglass/mixin/widgets/PressableWidgetMixin.java diff --git a/src/main/java/restudio/reglass/extensions/PressableWidgetExtension.java b/src/main/java/restudio/reglass/extensions/PressableWidgetExtension.java new file mode 100644 index 0000000..3ae66f1 --- /dev/null +++ b/src/main/java/restudio/reglass/extensions/PressableWidgetExtension.java @@ -0,0 +1,10 @@ +package restudio.reglass.extensions; + +import org.spongepowered.asm.mixin.Unique; + +public interface PressableWidgetExtension { + @Unique + boolean reglass$isDragging(); + @Unique + void reglass$setIsDragging(boolean isDragging); +} diff --git a/src/main/java/restudio/reglass/mixin/widgets/ClickableWidgetMixin.java b/src/main/java/restudio/reglass/mixin/widgets/ClickableWidgetMixin.java new file mode 100644 index 0000000..d9b0b1c --- /dev/null +++ b/src/main/java/restudio/reglass/mixin/widgets/ClickableWidgetMixin.java @@ -0,0 +1,52 @@ +package restudio.reglass.mixin.widgets; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.gui.widget.PressableWidget; +import net.minecraft.client.sound.SoundManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import restudio.reglass.extensions.PressableWidgetExtension; + +@Mixin(ClickableWidget.class) +public abstract class ClickableWidgetMixin { + @Shadow + public abstract void playDownSound(SoundManager soundManager); + + @Shadow + public abstract void onClick(double mouseX, double mouseY); + + @Redirect(method = "mouseClicked", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/ClickableWidget;playDownSound(Lnet/minecraft/client/sound/SoundManager;)V")) + private void redirectPlayDownSound(ClickableWidget instance, SoundManager soundManager) { + if ((Object) this instanceof PressableWidget) { + // Just play nothing. + return; + } + playDownSound(soundManager); + } + + @Redirect(method = "mouseClicked", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/ClickableWidget;onClick(DD)V")) + private void redirectOnClick(ClickableWidget instance, double mouseX, double mouseY) { + if ((Object) this instanceof PressableWidget) { + ((PressableWidgetExtension) this).reglass$setIsDragging(true); + // Just play nothing. + return; + } + onClick(mouseX, mouseY); + } + + @Inject(method = "mouseReleased", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/ClickableWidget;onRelease(DD)V")) + private void modifyOnRelease(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { + if ((Object) this instanceof PressableWidget pressableWidget) { + if (pressableWidget.isMouseOver(mouseX, mouseY)) { + pressableWidget.playDownSound(MinecraftClient.getInstance().getSoundManager()); + pressableWidget.onClick(mouseX, mouseY); + } + ((PressableWidgetExtension) pressableWidget).reglass$setIsDragging(false); + } + } +} diff --git a/src/main/java/restudio/reglass/mixin/widgets/DrawContextMixin.java b/src/main/java/restudio/reglass/mixin/widgets/DrawContextMixin.java deleted file mode 100644 index ddca137..0000000 --- a/src/main/java/restudio/reglass/mixin/widgets/DrawContextMixin.java +++ /dev/null @@ -1,44 +0,0 @@ -package restudio.reglass.mixin.widgets; - -import com.mojang.blaze3d.pipeline.RenderPipeline; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import restudio.reglass.client.api.ReGlassApi; -import restudio.reglass.client.api.ReGlassConfig; -import restudio.reglass.client.api.WidgetStyle; - -@Mixin(DrawContext.class) -public abstract class DrawContextMixin { - - @Unique - private static final Identifier BUTTON_TEXTURE = Identifier.ofVanilla("widget/button"); - @Unique - private static final Identifier BUTTON_DISABLED_TEXTURE = Identifier.ofVanilla("widget/button_disabled"); - @Unique - private static final Identifier BUTTON_HIGHLIGHTED_TEXTURE = Identifier.ofVanilla("widget/button_highlighted"); - - @Inject(method = "drawGuiTexture(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/util/Identifier;IIIII)V", - at = @At("HEAD"), cancellable = true) - private void onDrawTexture(RenderPipeline pipeline, Identifier sprite, int x, int y, int width, int height, int color, CallbackInfo ci) { - boolean isButtonTexture = sprite.getPath().equals(BUTTON_TEXTURE.getPath()) - || sprite.getPath().equals(BUTTON_DISABLED_TEXTURE.getPath()) - || sprite.getPath().equals(BUTTON_HIGHLIGHTED_TEXTURE.getPath()); - - if (isButtonTexture && (ReGlassConfig.INSTANCE.features.enableRedesign && ReGlassConfig.INSTANCE.features.buttons)) { - boolean isHighlighted = sprite.getPath().equals(BUTTON_HIGHLIGHTED_TEXTURE.getPath()); - boolean isDisabled = sprite.getPath().equals(BUTTON_DISABLED_TEXTURE.getPath()); - ReGlassApi.create((DrawContext)(Object) this) - .position(x, y) - .size(width, height) - .hover(isHighlighted ? 1f : 0f) - .style(WidgetStyle.create().tint(isDisabled ? 0xFF000000 : 0xFFFFFFFF, isDisabled ? 0.4f : 0f)) - .render(); - ci.cancel(); - } - } -} \ No newline at end of file diff --git a/src/main/java/restudio/reglass/mixin/widgets/PressableWidgetMixin.java b/src/main/java/restudio/reglass/mixin/widgets/PressableWidgetMixin.java new file mode 100644 index 0000000..f5bc52f --- /dev/null +++ b/src/main/java/restudio/reglass/mixin/widgets/PressableWidgetMixin.java @@ -0,0 +1,68 @@ +package restudio.reglass.mixin.widgets; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.widget.PressableWidget; +import net.minecraft.util.math.ColorHelper; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import restudio.reglass.client.LiquidGlassUniforms; +import restudio.reglass.client.api.ReGlassApi; +import restudio.reglass.client.api.ReGlassConfig; +import restudio.reglass.client.api.WidgetStyle; +import restudio.reglass.extensions.PressableWidgetExtension; + +@Mixin(PressableWidget.class) +public abstract class PressableWidgetMixin implements PressableWidgetExtension { + @Unique + private boolean reglass$isDragging; + @Shadow + public abstract void drawMessage(DrawContext context, TextRenderer textRenderer, int color); + + @Inject(method = "renderWidget", at = @At("HEAD"), cancellable = true) + private void modifyRenderWidget(DrawContext context, int mouseX, int mouseY, float deltaTicks, CallbackInfo ci) { + if ((Object) this instanceof PressableWidget pressableWidget) { + if (!ReGlassConfig.INSTANCE.features.enableRedesign || !ReGlassConfig.INSTANCE.features.buttons) { + return; + } + + boolean isDisabled = !pressableWidget.active; + boolean isHighlighted = pressableWidget.isHovered(); + boolean isFocused = isHighlighted && reglass$isDragging; + + if (ReGlassConfig.INSTANCE.features.enableRedesign && ReGlassConfig.INSTANCE.features.buttons) { + ReGlassApi.create(context) + .position(pressableWidget.getX(), pressableWidget.getY()) + .size(pressableWidget.getWidth(), pressableWidget.getHeight()) + .hover(isHighlighted ? 1f : 0f) + .focus(isFocused ? 1.0f : 0.0f) + .style(WidgetStyle.create().tint(isDisabled ? 0xFF000000 : 0xFFFFFFFF, isDisabled ? 0.4f : 0f)) + .render(); + + LiquidGlassUniforms.get().tryApplyBlur(context); + + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + int i = ColorHelper.withAlpha(1.0f, pressableWidget.active ? -1 : -6250336); + drawMessage(context, textRenderer, i); + // context.drawCenteredTextWithShadow(textRenderer, buttonWidget.getMessage(), buttonWidget.getX() + buttonWidget.getWidth() / 2, buttonWidget.getY() + (buttonWidget.getHeight() - 8) / 2, finalColor); + + ci.cancel(); + } + } + } + + @Override + public void reglass$setIsDragging(boolean isDragging) { + this.reglass$isDragging = isDragging; + } + + @Override + public boolean reglass$isDragging() { + return reglass$isDragging; + } +} diff --git a/src/main/resources/reglass.mixins.json b/src/main/resources/reglass.mixins.json index a90c76e..bc5d299 100644 --- a/src/main/resources/reglass.mixins.json +++ b/src/main/resources/reglass.mixins.json @@ -24,6 +24,7 @@ "client.ScreenMixin", "client.TitleScreenMixin", "logical.GameRendererMixin", - "widgets.DrawContextMixin" + "widgets.ClickableWidgetMixin", + "widgets.PressableWidgetMixin" ] } \ No newline at end of file