From 17a2ed7f37948d84380391a6f63eab759a367245 Mon Sep 17 00:00:00 2001 From: Eclipse-Sol Date: Sun, 25 Oct 2026 16:10:15 -0400 Subject: [PATCH] Initial commit for gui-item-supersampling (#2299) --- .../angelica/glsm/GLContextState.java | 1 + .../angelica/glsm/GLStateManager.java | 22 ++ .../angelica/glsm/ffp/FFPUniformBlock.java | 1 + .../angelica/glsm/ffp/FragmentKey.java | 12 +- .../glsm/ffp/FragmentShaderGenerator.java | 7 +- .../angelica/glsm/ffp/ShaderManager.java | 4 + .../angelica/glsm/ffp/Uniforms.java | 11 + .../angelica/glsm/texture/TextureInfo.java | 1 + .../shaders/texture_supersampling.glsl | 58 ++++ .../glsm/ffp/TextureSupersamplingGLTest.java | 302 ++++++++++++++++++ .../gtnewhorizons/angelica/mixins/Mixins.java | 4 + .../rendering/GuiBlockSupersampling.java | 18 ++ .../angelica/rendering/RenderingState.java | 6 + .../client/gui/SodiumGameOptionPages.java | 9 + .../sodium/client/gui/SodiumGameOptions.java | 2 + .../resources/assets/angelica/lang/en_US.lang | 2 + .../early/angelica/MixinEntityRenderer.java | 12 + ...ixinForgeHooksClient_GuiSupersampling.java | 21 ++ .../MixinRenderBlocks_GuiSupersampling.java | 19 ++ .../MixinRenderItem_GuiSupersampling.java | 32 ++ ...tyRendererDispatcher_GuiSupersampling.java | 19 ++ .../features/textures/MixinTextureMap.java | 3 + 22 files changed, 562 insertions(+), 4 deletions(-) create mode 100644 glsm/src/main/resources/assets/angelica/shaders/texture_supersampling.glsl create mode 100644 glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/TextureSupersamplingGLTest.java create mode 100644 src/main/java/com/gtnewhorizons/angelica/rendering/GuiBlockSupersampling.java create mode 100644 src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinForgeHooksClient_GuiSupersampling.java create mode 100644 src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinRenderBlocks_GuiSupersampling.java create mode 100644 src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinRenderItem_GuiSupersampling.java create mode 100644 src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinTileEntityRendererDispatcher_GuiSupersampling.java diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java index c52f28833..fed79c546 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java @@ -275,6 +275,7 @@ public class GLContextState { public float savedFilterAniso = -1.0f; public boolean lineQuadsActive = false; public boolean lineStippleActive = false; + public int textureSupersamplingSamples = 1; public Instancing ffpInstancing = Instancing.NONE; public float lastBrightnessX; public float lastBrightnessY; diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java index 053c40ced..e7ccbb223 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java @@ -653,6 +653,28 @@ public class GLStateManager { ctx().ffpInstancing = mode; } + public static int setTextureSupersamplingSamples(int samples) { + if (samples < 1 || samples > 16 || (samples & (samples - 1)) != 0) { + throw new IllegalArgumentException("Texture supersampling requires 1, 2, 4, 8, or 16 samples"); + } + final GLContextState glCtx = ctx(); + final int previous = glCtx.textureSupersamplingSamples; + if (previous != samples) { + beforeUncapturedStateChange(); + glCtx.textureSupersamplingSamples = samples; + } + return previous; + } + + public static void markTextureAsAtlas(int texture, int gutter) { + if (texture <= 0) return; + final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(texture); + if (info.getAtlasGutter() != gutter) { + beforeUncapturedStateChange(); + info.setAtlasGutter(gutter); + } + } + private static final MethodHandle MAT4_STACK_CURR_DEPTH; static { diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FFPUniformBlock.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FFPUniformBlock.java index e7204fbab..96d1e180e 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FFPUniformBlock.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FFPUniformBlock.java @@ -104,6 +104,7 @@ public final class FFPUniformBlock { public static final int TEX_ENV_COLOR_3 = vec4("u_TexEnvColor3"); public static final int OVERLAY_COLOR = vec4("u_OverlayColor"); public static final int SECONDARY_COLOR = vec3("u_SecondaryColor"); + public static final int ATLAS_GUTTER = flt("u_AtlasGutter"); public static final int FOG_PARAMS = vec4("u_FogParams"); public static final int FOG_COLOR = vec4("u_FogColor"); diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FragmentKey.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FragmentKey.java index 2a6163f73..ba6f5ca0d 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FragmentKey.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FragmentKey.java @@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.glsm.GLContextState; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.states.TexEnvState; import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray; +import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; @@ -82,7 +83,7 @@ public final class FragmentKey { private static final int BIT_ALPHA_TEST = 2; // 1 bit private static final int BIT_ALPHA_FUNC = 3; // 3 bits private static final int BIT_SEPARATE_SPECULAR = 6; // 1 bit - private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits + private static final int BIT_TEXTURE_SUPERSAMPLING = 7; // 3 bits, log2(samples) private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit private static final int BIT_LINE_STIPPLE = 11; // 1 bit private static final int BIT_COLOR_SUM = 12; // 1 bit @@ -172,7 +173,10 @@ public final class FragmentKey { } final int nrEnabled = highestEnabled + 1; - global |= ((long) (nrEnabled & 0x7)) << BIT_NR_ENABLED_UNITS; + if (glCtx.textureSupersamplingSamples > 1 && (unit0Bits & 1L) != 0 + && TextureInfoCache.INSTANCE.getInfo(textures.getTextureUnitBindings(0).getBinding()).getAtlasGutter() > 0) { + global |= (long) Integer.numberOfTrailingZeros(glCtx.textureSupersamplingSamples) << BIT_TEXTURE_SUPERSAMPLING; + } global |= (unit0Bits << GLOBAL_BITS); out[0] = global; @@ -256,12 +260,13 @@ public final class FragmentKey { public int fogMode() { return (int) (packed[0] & 0x3); } public boolean combinedGlint() { return (packed[0] & (1L << BIT_COMBINED_GLINT)) != 0; } public boolean glintReplaceAlpha() { return (packed[0] & (1L << BIT_GLINT_REPLACE_ALPHA)) != 0; } + public int textureSupersamplingSamples() { return 1 << (int) ((packed[0] >> BIT_TEXTURE_SUPERSAMPLING) & 0x7); } public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; } public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); } public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; } public boolean colorSum() { return ((packed[0] >> BIT_COLOR_SUM) & 1) != 0; } public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; } - public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); } + public int nrEnabledUnits() { return packed.length == 1 && !unitEnabled(0) ? 0 : packed.length; } public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; } public boolean overlayInstanced() { return ((packed[0] >> BIT_OVERLAY_INSTANCED) & 1) != 0; } @@ -400,6 +405,7 @@ public final class FragmentKey { sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(), alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(), overlayEnabled() || overlayInstanced(), nrEnabledUnits())); + if (textureSupersamplingSamples() > 1) sb.append(" samples=").append(textureSupersamplingSamples()); for (int i = 0; i < nrEnabledUnits(); i++) { if (!unitEnabled(i)) { sb.append(String.format(" u%d=OFF", i)); diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FragmentShaderGenerator.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FragmentShaderGenerator.java index 8b13d8776..6fe4e51b2 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FragmentShaderGenerator.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/FragmentShaderGenerator.java @@ -28,6 +28,10 @@ public final class FragmentShaderGenerator { return sb.toString(); } sb.append("out vec4 fragColor;\n\n"); + if (key.textureSupersamplingSamples() > 1) { + sb.append("#define TEXTURE_SUPERSAMPLING_SAMPLES ").append(key.textureSupersamplingSamples()).append('\n'); + sb.append(ShaderLoader.getShaderSource("angelica:texture_supersampling.glsl")); + } sb.append("void main() {\n"); emitLineStipple(sb, key); @@ -86,7 +90,8 @@ public final class FragmentShaderGenerator { private static void emitTextureSampling(StringBuilder sb, FragmentKey key) { for (int i = 0; i < key.nrEnabledUnits(); i++) { if (!key.unitEnabled(i)) continue; - sb.append(" vec4 tex").append(i).append("Color = textureProj(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n"); + final String sample = i == 0 && key.textureSupersamplingSamples() > 1 ? "sampleSupersampledTexture" : "textureProj"; + sb.append(" vec4 tex").append(i).append("Color = ").append(sample).append("(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n"); } } diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/ShaderManager.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/ShaderManager.java index bc9dbd713..302574f91 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/ShaderManager.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/ShaderManager.java @@ -190,6 +190,10 @@ public final class ShaderManager { } } + boolean isTextureSupersampling() { + return currentProgram != null && currentProgram.getFragmentKey().textureSupersamplingSamples() > 1; + } + public static void endFrame() { final ShaderManager sm = GLStateManager.ctx().ffp; sm.lastFramePreDrawCalls = sm.preDrawCalls; diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/Uniforms.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/Uniforms.java index 64b8d6e3c..6c4040bf0 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/Uniforms.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/Uniforms.java @@ -11,6 +11,7 @@ import com.gtnewhorizons.angelica.glsm.states.TexGenState; import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray; import com.gtnewhorizons.angelica.glsm.states.ViewportState; import com.gtnewhorizons.angelica.glsm.streaming.UniformRingBuffer; +import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache; import org.joml.Math; import org.joml.Matrix3f; import org.joml.Matrix4f; @@ -49,6 +50,7 @@ public class Uniforms { private int viewportX = Integer.MIN_VALUE, viewportY = Integer.MIN_VALUE, viewportWidth = -1, viewportHeight = -1; private int lineStipple = -1; private int weatherGen = -1; + private int atlasGutter = -1; int blockWrites; int blockSkips; @@ -170,6 +172,15 @@ public class Uniforms { stagedFragment++; dirty = true; } + if (glCtx.textureSupersamplingSamples > 1 && ffp.isTextureSupersampling()) { + final int gutter = TextureInfoCache.INSTANCE.getInfo(glCtx.textures.getTextureUnitBindings(0).getBinding()).getAtlasGutter(); + if (gutter != atlasGutter) { + putFloat(FFPUniformBlock.ATLAS_GUTTER, gutter); + atlasGutter = gutter; + stagedMisc++; + dirty = true; + } + } final LineState line = glCtx.lineState; final float lw = line.getWidth(); if (lw != lineWidth) { diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/texture/TextureInfo.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/texture/TextureInfo.java index 0c737ce78..05cb16954 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/texture/TextureInfo.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/texture/TextureInfo.java @@ -21,6 +21,7 @@ public class TextureInfo { protected int resolvedInternalFormat = -1; protected int width = -1; protected int height = -1; + @Getter @Setter private int atlasGutter; protected int minFilter = UNSET_INT; protected int magFilter = UNSET_INT; diff --git a/glsm/src/main/resources/assets/angelica/shaders/texture_supersampling.glsl b/glsm/src/main/resources/assets/angelica/shaders/texture_supersampling.glsl new file mode 100644 index 000000000..f8fb7b086 --- /dev/null +++ b/glsm/src/main/resources/assets/angelica/shaders/texture_supersampling.glsl @@ -0,0 +1,58 @@ +const vec2 SAMPLE_OFFSETS[TEXTURE_SUPERSAMPLING_SAMPLES] = vec2[]( +#if TEXTURE_SUPERSAMPLING_SAMPLES == 2 + vec2(0.25, 0.25), vec2(-0.25, -0.25) +#elif TEXTURE_SUPERSAMPLING_SAMPLES == 4 + vec2(0.125, 0.375), vec2(-0.125, -0.375), + vec2(0.375, -0.125), vec2(-0.375, 0.125) +#elif TEXTURE_SUPERSAMPLING_SAMPLES == 8 + vec2(-0.1875, -0.0625), vec2( 0.3125, 0.0625), + vec2(-0.0625, 0.1875), vec2(-0.3125, -0.1875), + vec2( 0.1875, -0.3125), vec2( 0.4375, -0.4375), + vec2(-0.4375, 0.3125), vec2( 0.0625, 0.4375) +#elif TEXTURE_SUPERSAMPLING_SAMPLES == 16 + vec2( 0.0625, 0.0625), vec2(-0.0625, -0.1875), + vec2(-0.1875, 0.1250), vec2( 0.2500, -0.0625), + vec2(-0.3125, -0.1250), vec2( 0.1250, 0.3125), + vec2( 0.3125, 0.1875), vec2( 0.1875, -0.3125), + vec2(-0.1250, 0.3750), vec2( 0.0000, -0.4375), + vec2(-0.2500, -0.3750), vec2(-0.3750, 0.2500), + vec2(-0.5000, 0.0000), vec2( 0.4375, -0.2500), + vec2( 0.3750, 0.4375), vec2(-0.4375, -0.5000) +#endif +); + +vec4 sampleSupersampledTexture(sampler2D tex, vec4 coord) { + vec2 uv = coord.xy / coord.w; + vec2 dx = dFdx(uv); + vec2 dy = dFdy(uv); + ivec2 size = textureSize(tex, 0); + vec2 offsets[TEXTURE_SUPERSAMPLING_SAMPLES]; + vec2 extent = vec2(0.0); + for (int i = 0; i < TEXTURE_SUPERSAMPLING_SAMPLES; i++) { + offsets[i] = SAMPLE_OFFSETS[i].x * dx + SAMPLE_OFFSETS[i].y * dy; + extent = max(extent, abs(offsets[i])); + } + extent *= vec2(size); + float footprint = max(extent.x, extent.y); + if (footprint >= u_AtlasGutter) return textureGrad(tex, uv, dx, dy); + float supersampling = 1.0 - smoothstep(0.75 * u_AtlasGutter, u_AtlasGutter, footprint); + + // The atlas gutter is 2^maxMipLevel. + float texelsPerPixel = max(length(dx * vec2(size)), length(dy * vec2(size))); + int lod = int(min(ceil(log2(max(texelsPerPixel / sqrt(float(TEXTURE_SUPERSAMPLING_SAMPLES)), 1.0))), + log2(u_AtlasGutter))); + + // Transparent texels must not tint the visible samples. + vec4 color = vec4(0.0); + for (int i = 0; i < TEXTURE_SUPERSAMPLING_SAMPLES; i++) { + ivec2 texel = min(ivec2(fract(uv + offsets[i]) * vec2(size)), size - 1); + vec4 sampleColor = texelFetch(tex, texel >> lod, lod); + color += vec4(sampleColor.rgb * sampleColor.a, sampleColor.a); + } + color /= float(TEXTURE_SUPERSAMPLING_SAMPLES); + if (supersampling < 1.0) { + vec4 filtered = textureGrad(tex, uv, dx, dy); + color = mix(vec4(filtered.rgb * filtered.a, filtered.a), color, supersampling); + } + return vec4(color.rgb / max(color.a, 0.00001), color.a); +} diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/TextureSupersamplingGLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/TextureSupersamplingGLTest.java new file mode 100644 index 000000000..820a1f196 --- /dev/null +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/TextureSupersamplingGLTest.java @@ -0,0 +1,302 @@ +package com.gtnewhorizons.angelica.glsm.ffp; + +import com.gtnewhorizons.angelica.glsm.GLCoreTest; +import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.RenderSystem; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL15; + +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.function.IntBinaryOperator; + +import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.TEXTURE_BIT; +import static org.joml.Math.clamp; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@GLCoreTest +class TextureSupersamplingGLTest { + private static final int SIZE = 200; + private int texture; + private int vao; + private int vbo; + + private static int dotColor(int x, int y) { + final boolean dot = (x == 3 || x == 6 || x == 9 || x == 12) + && (y == 3 || y == 6 || y == 9 || y == 12) + && (x == 3 || x == 12 || y == 3 || y == 12); + return dot ? 0x444444FF : 0xFFFFFFFF; + } + + private static int uploadTexture(int spriteSize, int gutter, IntBinaryOperator color) { + final int atlasSize = Integer.highestOneBit(spriteSize + 2 * gutter) << 1; + final ByteBuffer pixels = BufferUtils.createByteBuffer(atlasSize * atlasSize * 4); + for (int y = 0; y < atlasSize; y++) { + for (int x = 0; x < atlasSize; x++) { + final int rgba = x < spriteSize + 2 * gutter && y < spriteSize + 2 * gutter + ? color.applyAsInt(clamp(0, spriteSize - 1, x - gutter), clamp(0, spriteSize - 1, y - gutter)) + : 0xFF00FFFF; + pixels.put((byte) (rgba >>> 24)).put((byte) (rgba >>> 16)).put((byte) (rgba >>> 8)).put((byte) rgba); + } + } + pixels.flip(); + final int texture = GLStateManager.glGenTextures(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0); + GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, atlasSize, atlasSize, 0, + GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels); + return texture; + } + + private static void vertex(ByteBuffer vertices, float x, float z, float u, float v) { + vertices.putFloat(x).putFloat(0.5f).putFloat(z).putFloat(u).putFloat(v); + } + + private static int countDotRegions(int[] pixels) { + final boolean[] remaining = new boolean[pixels.length]; + for (int i = 0; i < pixels.length; i++) { + final int gray = pixels[i] & 255; + remaining[i] = gray > 0 && gray < 250 + && ((pixels[i] >> 8) & 255) == gray && ((pixels[i] >> 16) & 255) == gray; + } + int regions = 0; + final ArrayDeque pending = new ArrayDeque<>(); + for (int i = 0; i < remaining.length; i++) { + if (!remaining[i]) continue; + regions++; + remaining[i] = false; + pending.add(i); + while (!pending.isEmpty()) { + final int pixel = pending.removeFirst(); + for (int neighbor : new int[]{pixel - 1, pixel + 1, pixel - SIZE, pixel + SIZE}) { + if (neighbor >= 0 && neighbor < remaining.length && remaining[neighbor]) { + remaining[neighbor] = false; + pending.add(neighbor); + } + } + } + } + return regions; + } + + @BeforeEach + void setup() { + FfpFixture.resetFfpState(); + GLStateManager.setTextureSupersamplingSamples(1); + GLStateManager.glDisable(GL11.GL_LIGHTING); + GLStateManager.glDisable(GL11.GL_FOG); + GLStateManager.glColor4f(1, 1, 1, 1); + GLStateManager.glEnable(GL11.GL_TEXTURE_2D); + GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); + GLStateManager.glViewport(0, 0, SIZE, SIZE); + GLStateManager.glMatrixMode(GL11.GL_PROJECTION); + GLStateManager.glLoadIdentity(); + GLStateManager.glOrtho(0, SIZE, SIZE, 0, -100, 100); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + FfpFixture.activateFfp(); + } + + @AfterEach + void cleanup() { + GLStateManager.setTextureSupersamplingSamples(1); + FfpFixture.resetFfpState(); + if (texture != 0) GLStateManager.glDeleteTextures(texture); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + if (vbo != 0) GLStateManager.glDeleteBuffers(vbo); + if (vao != 0) GLStateManager.glDeleteVertexArrays(vao); + GLStateManager.glMatrixMode(GL11.GL_PROJECTION); + GLStateManager.glLoadIdentity(); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + GLStateManager.glLoadIdentity(); + GLStateManager.glViewport(0, 0, 800, 600); + } + + @ParameterizedTest + @ValueSource(ints = {4, 16}) + void recoversTwelveDotsAtScaleThreeAndRestoresNearestAfterward(int samples) { + createDotAtlas(); + + final int[] nearest = drawTop(3); + assertEquals(0, countDotRegions(nearest)); + final int previous = GLStateManager.setTextureSupersamplingSamples(samples); + assertEquals(12, countDotRegions(drawTop(3))); + GLStateManager.setTextureSupersamplingSamples(previous); + assertArrayEquals(nearest, drawTop(3)); + } + + @ParameterizedTest + @ValueSource(ints = {4, 16}) + void highResolutionCheckerboardsStaySmoothWithTheDefaultAtlasGutter(int samples) { + createAtlas(256, 16, (x, y) -> ((x ^ y) & 1) == 0 ? 0xFFFFFFFF : 0x000000FF); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 4); + GLStateManager.glGenerateMipmap(GL11.GL_TEXTURE_2D); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR); + final int[] filtered = drawFlat(); + GLStateManager.setTextureSupersamplingSamples(samples); + final int[] supersampled = drawFlat(); + for (int y = 97; y <= 102; y++) { + for (int x = 97; x <= 102; x++) { + final int index = y * SIZE + x; + assertEquals(128, filtered[index] & 255, 1, "The mip-filtered checkerboard must average to gray"); + assertEquals(filtered[index] & 255, supersampled[index] & 255, 1, + "Supersampling must preserve the mip average instead of aliasing the base-level checkerboard"); + } + } + } + + @ParameterizedTest + @ValueSource(ints = {4, 16}) + void transparentTexelsDoNotTintPartialCoverage(int samples) { + createAtlas(16, (x, y) -> x < 8 ? 0xFF0000FF : 0x0000FF00); + GLStateManager.glEnable(GL11.GL_BLEND); + GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GLStateManager.setTextureSupersamplingSamples(samples); + final int[] pixels = drawTop(3); + int partialPixels = 0; + for (int pixel : pixels) { + final int red = pixel & 255; + if (red > 0 && red < 255) { + partialPixels++; + assertEquals(0, (pixel >> 8) & 255); + assertEquals(0, (pixel >> 16) & 255, "Transparent blue texels must not tint the red edge"); + } + } + assertTrue(partialPixels > 0, "The test must cover a partially transparent pixel"); + } + + @Test + void modelTexturesKeepTheirOwnFilteringWhileSupersamplingIsActive() { + createDotAtlas(); + final int modelTexture = uploadTexture(16, 1, TextureSupersamplingGLTest::dotColor); + try { + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT); + final int[] normal = drawTop(3); + GLStateManager.setTextureSupersamplingSamples(16); + assertArrayEquals(normal, drawTop(3)); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); + assertFalse(Arrays.equals(normal, drawTop(3))); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, modelTexture); + assertArrayEquals(normal, drawTop(3)); + } finally { + GLStateManager.glDeleteTextures(modelTexture); + } + } + + @Test + void atlasFetchesIgnoreSamplerFilteringAndRepeatAcrossNegativeUvs() { + createAtlas(16, 4, TextureSupersamplingGLTest::dotColor); + GLStateManager.setTextureSupersamplingSamples(16); + final int[] nearest = drawTop(3); + final int sampler = RenderSystem.genSampler(); + try { + RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE); + RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE); + RenderSystem.bindSamplerToUnit(0, sampler); + GLStateManager.glMatrixMode(GL11.GL_TEXTURE); + GLStateManager.glTranslatef(-2, 3, 0); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + assertArrayEquals(nearest, drawTop(3)); + } finally { + GLStateManager.glMatrixMode(GL11.GL_TEXTURE); + GLStateManager.glLoadIdentity(); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + RenderSystem.bindSamplerToUnit(0, 0); + RenderSystem.destroySampler(sampler); + } + } + + @Test + void changingTheAtlasGutterSwitchesBetweenMipFilteringAndFullFootprintSampling() { + createAtlas(32, 4, (x, y) -> 0xFF0000FF); + final int[] baseLevel = drawFlat(); + for (int level = 1; level <= 6; level++) { + final int size = 64 >> level; + final ByteBuffer pixels = BufferUtils.createByteBuffer(size * size * 4); + for (int i = 0; i < size * size; i++) pixels.putInt(0xFFFF0000); + pixels.flip(); + GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, size, size, 0, + GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels); + } + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 6); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST_MIPMAP_NEAREST); + final int[] mipFiltered = drawFlat(); + assertFalse(Arrays.equals(baseLevel, mipFiltered)); + GLStateManager.setTextureSupersamplingSamples(16); + assertArrayEquals(baseLevel, drawFlat()); + GLStateManager.markTextureAsAtlas(texture, 1); + assertArrayEquals(mipFiltered, drawFlat()); + GLStateManager.markTextureAsAtlas(texture, 4); + assertArrayEquals(baseLevel, drawFlat()); + } + + private int[] drawFlat() { + FfpFixture.clear(); + GLStateManager.glLoadIdentity(); + GLStateManager.glTranslatef(100.25f, 100.125f, 0); + GLStateManager.glScalef(11, 11, 1); + GLStateManager.glRotatef(90, 1, 0, 0); + GLStateManager.glDrawArrays(GL11.GL_QUADS, 0, 4); + return FfpFixture.readRegion(SIZE); + } + + private void createDotAtlas() { + createAtlas(16, TextureSupersamplingGLTest::dotColor); + } + + private void createAtlas(int spriteSize, IntBinaryOperator color) { + createAtlas(spriteSize, 1, color); + } + + private void createAtlas(int spriteSize, int gutter, IntBinaryOperator color) { + texture = uploadTexture(spriteSize, gutter, color); + GLStateManager.markTextureAsAtlas(texture, gutter); + final int atlasSize = Integer.highestOneBit(spriteSize + 2 * gutter) << 1; + final float minUv = (gutter + 0.01f) / atlasSize; + final float maxUv = (spriteSize + gutter - 0.01f) / atlasSize; + final ByteBuffer vertices = BufferUtils.createByteBuffer(4 * 20); + vertex(vertices, -0.5f, -0.5f, minUv, minUv); + vertex(vertices, 0.5f, -0.5f, maxUv, minUv); + vertex(vertices, 0.5f, 0.5f, maxUv, maxUv); + vertex(vertices, -0.5f, 0.5f, minUv, maxUv); + vertices.flip(); + vao = GLStateManager.glGenVertexArrays(); + GLStateManager.glBindVertexArray(vao); + vbo = GLStateManager.glGenBuffers(); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW); + FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, 20, 0); + FfpFixture.attrib(2, 2, GL11.GL_FLOAT, false, 20, 12); + VAOManager.setCurrentVertexFlags(TEXTURE_BIT); + } + + private int[] drawTop(int scale) { + FfpFixture.clear(); + GLStateManager.glLoadIdentity(); + GLStateManager.glTranslatef(100 + 8 * scale, 100 + 8 * scale, 0); + GLStateManager.glScalef(10 * scale, 10 * scale, -10 * scale); + GLStateManager.glRotatef(210, 1, 0, 0); + GLStateManager.glRotatef(45, 0, 1, 0); + GLStateManager.glDrawArrays(GL11.GL_QUADS, 0, 4); + return FfpFixture.readRegion(SIZE); + } +} diff --git a/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java b/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java index bc08a065a..07cbd6de0 100644 --- a/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java +++ b/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java @@ -68,6 +68,10 @@ public enum Mixins implements IMixins { , "angelica.optimizations.MixinRenderBiped_ArmorResource" , "angelica.optimizations.MixinRendererLivingEntity" , "angelica.rendering.MixinRenderGlobal_SelectionBox" + , "angelica.textures.MixinForgeHooksClient_GuiSupersampling" + , "angelica.textures.MixinRenderBlocks_GuiSupersampling" + , "angelica.textures.MixinRenderItem_GuiSupersampling" + , "angelica.textures.MixinTileEntityRendererDispatcher_GuiSupersampling" , "angelica.gui.MixinGuiIngameForge_ModernF3" ) ), diff --git a/src/main/java/com/gtnewhorizons/angelica/rendering/GuiBlockSupersampling.java b/src/main/java/com/gtnewhorizons/angelica/rendering/GuiBlockSupersampling.java new file mode 100644 index 000000000..fea6e362d --- /dev/null +++ b/src/main/java/com/gtnewhorizons/angelica/rendering/GuiBlockSupersampling.java @@ -0,0 +1,18 @@ +package com.gtnewhorizons.angelica.rendering; + +import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.proxy.ClientProxy; + +public final class GuiBlockSupersampling { + private GuiBlockSupersampling() {} + + public static int begin() { + if (!RenderingState.INSTANCE.isRenderingGuiItem() || RenderingState.INSTANCE.isRenderingWorld()) return 0; + final int samples = ClientProxy.options().quality.guiBlockSupersampling; + return samples > 1 ? GLStateManager.setTextureSupersamplingSamples(samples) : 0; + } + + public static void end(int previous) { + if (previous != 0) GLStateManager.setTextureSupersamplingSamples(previous); + } +} diff --git a/src/main/java/com/gtnewhorizons/angelica/rendering/RenderingState.java b/src/main/java/com/gtnewhorizons/angelica/rendering/RenderingState.java index 23d4c0d09..f8c67266a 100644 --- a/src/main/java/com/gtnewhorizons/angelica/rendering/RenderingState.java +++ b/src/main/java/com/gtnewhorizons/angelica/rendering/RenderingState.java @@ -23,6 +23,12 @@ public class RenderingState { @Getter @Setter private float fov; + @Getter + @Setter + private boolean renderingWorld; + @Getter + @Setter + private boolean renderingGuiItem; public void setCameraPosition(double x, double y, double z) { diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java index 8862ef1f0..a34426d20 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java @@ -286,6 +286,15 @@ public class SodiumGameOptionPages { .add(texelSampling) .add(anisotropicFilteringSlider(vanillaOpts, textureFilterMode::getValue), SodiumGameOptions.anisotropySupported()) + .add(OptionImpl.createBuilder(int.class, sodiumOpts) + .setName(I18n.format("sodium.options.gui_block_supersampling.name")) + .setTooltip(I18n.format("sodium.options.gui_block_supersampling.tooltip")) + .setControl(option -> new SliderControl(option, 0, 4, 1, + value -> value == 0 ? I18n.format("options.off") : (1 << value) + "x")) + .setBinding((opts, value) -> opts.quality.guiBlockSupersampling = 1 << value, + opts -> Integer.numberOfTrailingZeros(opts.quality.guiBlockSupersampling)) + .setImpact(OptionImpact.LOW) + .build()) .add(biomeBlendRadius) .add(biomeBlendMode) // TODO diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptions.java b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptions.java index 26d992447..2fb4bd346 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptions.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptions.java @@ -151,6 +151,7 @@ public class SodiumGameOptions { if (config.quality.texelSampling == null) { config.quality.texelSampling = TexelSampling.LINEAR; } + config.quality.guiBlockSupersampling = Integer.highestOneBit(MathHelper.clamp_int(config.quality.guiBlockSupersampling, 1, 16)); if (GLStateManager.capabilities != null) { if (!GLStateManager.capabilities.OpenGL32) { @@ -250,6 +251,7 @@ public class SodiumGameOptions { public boolean useCeleritasSmoothLighting = true; public TextureFilterMode textureFilterMode = TextureFilterMode.RGSS_ANISOTROPIC; public TexelSampling texelSampling = TexelSampling.LINEAR; + public int guiBlockSupersampling = 1; } public static class ReducerSettings { diff --git a/src/main/resources/assets/angelica/lang/en_US.lang b/src/main/resources/assets/angelica/lang/en_US.lang index 211f62a4b..48e2de526 100644 --- a/src/main/resources/assets/angelica/lang/en_US.lang +++ b/src/main/resources/assets/angelica/lang/en_US.lang @@ -53,6 +53,8 @@ sodium.options.texel_sampling.linear=Linear sodium.options.texel_sampling.nearest=Nearest sodium.options.anisotropic_filtering.name=Anisotropic Filtering sodium.options.anisotropic_filtering.tooltip=Sharpens textures viewed at steep angles. +sodium.options.gui_block_supersampling.name=GUI Block Supersampling +sodium.options.gui_block_supersampling.tooltip=Sets the number of texture samples per pixel for 3D block icons within GUIs. sodium.options.particle_quality.name=Particle Quality sodium.options.particle_quality.tooltip=Controls the maximum number of particles which can be present on screen at any one time. sodium.options.smooth_lighting.tooltip=Controls whether blocks will be smoothly lit and shaded. This slightly increases the amount of time needed to re-build a chunk, but doesn't affect frame rates. diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/MixinEntityRenderer.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/MixinEntityRenderer.java index 402feb4bc..2fb72639c 100644 --- a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/MixinEntityRenderer.java +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/MixinEntityRenderer.java @@ -1,6 +1,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica; import com.gtnewhorizons.angelica.compat.mojang.Camera; +import com.gtnewhorizons.angelica.experimental.surround.Surround; import com.gtnewhorizons.angelica.mixins.interfaces.EntityRendererAccessor; import com.gtnewhorizons.angelica.rendering.RenderingState; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; @@ -33,6 +34,17 @@ public abstract class MixinEntityRenderer implements EntityRendererAccessor { @Accessor("rendererUpdateCount") public abstract int getRendererUpdateCount(); + @Surround(method = "renderWorld(FJ)V") + private void angelica$beginWorldRendering() { + @Surround.Carry final boolean renderingWorld = RenderingState.INSTANCE.isRenderingWorld(); + RenderingState.INSTANCE.setRenderingWorld(true); + } + + @Surround.Finally + private void angelica$endWorldRendering(@Surround.Carry boolean renderingWorld) { + RenderingState.INSTANCE.setRenderingWorld(renderingWorld); + } + @Inject(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ActiveRenderInfo;updateRenderInfo(Lnet/minecraft/entity/player/EntityPlayer;Z)V", shift = At.Shift.AFTER)) private void angelica$captureCameraMatrix(float partialTicks, long finishTimeNano, CallbackInfo ci) { final Minecraft mc = Minecraft.getMinecraft(); diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinForgeHooksClient_GuiSupersampling.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinForgeHooksClient_GuiSupersampling.java new file mode 100644 index 000000000..281f04a09 --- /dev/null +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinForgeHooksClient_GuiSupersampling.java @@ -0,0 +1,21 @@ +package com.gtnewhorizons.angelica.mixins.early.angelica.textures; + +import com.gtnewhorizons.angelica.experimental.surround.Surround; +import com.gtnewhorizons.angelica.rendering.GuiBlockSupersampling; +import net.minecraftforge.client.ForgeHooksClient; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(value = ForgeHooksClient.class, remap = false) +public class MixinForgeHooksClient_GuiSupersampling { + @Surround(method = "renderInventoryItem", at = @At(value = "INVOKE", ordinal = 0, + target = "Lnet/minecraftforge/client/IItemRenderer;renderItem(Lnet/minecraftforge/client/IItemRenderer$ItemRenderType;Lnet/minecraft/item/ItemStack;[Ljava/lang/Object;)V")) + private static void angelica$beginGuiBlock() { + @Surround.Carry final int previous = GuiBlockSupersampling.begin(); + } + + @Surround.Finally + private static void angelica$endGuiBlock(@Surround.Carry int previous) { + GuiBlockSupersampling.end(previous); + } +} diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinRenderBlocks_GuiSupersampling.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinRenderBlocks_GuiSupersampling.java new file mode 100644 index 000000000..7c8dbc135 --- /dev/null +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinRenderBlocks_GuiSupersampling.java @@ -0,0 +1,19 @@ +package com.gtnewhorizons.angelica.mixins.early.angelica.textures; + +import com.gtnewhorizons.angelica.experimental.surround.Surround; +import com.gtnewhorizons.angelica.rendering.GuiBlockSupersampling; +import net.minecraft.client.renderer.RenderBlocks; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(RenderBlocks.class) +public class MixinRenderBlocks_GuiSupersampling { + @Surround(method = "renderBlockAsItem(Lnet/minecraft/block/Block;IF)V") + private void angelica$beginGuiBlock() { + @Surround.Carry final int previous = GuiBlockSupersampling.begin(); + } + + @Surround.Finally + private void angelica$endGuiBlock(@Surround.Carry int previous) { + GuiBlockSupersampling.end(previous); + } +} diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinRenderItem_GuiSupersampling.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinRenderItem_GuiSupersampling.java new file mode 100644 index 000000000..e548bcb13 --- /dev/null +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinRenderItem_GuiSupersampling.java @@ -0,0 +1,32 @@ +package com.gtnewhorizons.angelica.mixins.early.angelica.textures; + +import com.gtnewhorizons.angelica.experimental.surround.Surround; +import com.gtnewhorizons.angelica.rendering.RenderingState; +import net.minecraft.client.renderer.entity.RenderItem; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(RenderItem.class) +public class MixinRenderItem_GuiSupersampling { + + @Surround(id = "withEffects", method = "renderItemAndEffectIntoGUI(Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/item/ItemStack;II)V") + private void angelica$beginGuiEffects() { + @Surround.Carry final boolean previous = RenderingState.INSTANCE.isRenderingGuiItem(); + RenderingState.INSTANCE.setRenderingGuiItem(true); + } + + @Surround.Finally("withEffects") + private void angelica$endGuiEffects(@Surround.Carry boolean previous) { + RenderingState.INSTANCE.setRenderingGuiItem(previous); + } + + @Surround(id = "base", method = "renderItemIntoGUI(Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/item/ItemStack;IIZ)V", remap = false) + private void angelica$beginGuiBase() { + @Surround.Carry final boolean previous = RenderingState.INSTANCE.isRenderingGuiItem(); + RenderingState.INSTANCE.setRenderingGuiItem(true); + } + + @Surround.Finally("base") + private void angelica$endGuiBase(@Surround.Carry boolean previous) { + RenderingState.INSTANCE.setRenderingGuiItem(previous); + } +} diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinTileEntityRendererDispatcher_GuiSupersampling.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinTileEntityRendererDispatcher_GuiSupersampling.java new file mode 100644 index 000000000..454b0648f --- /dev/null +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/textures/MixinTileEntityRendererDispatcher_GuiSupersampling.java @@ -0,0 +1,19 @@ +package com.gtnewhorizons.angelica.mixins.early.angelica.textures; + +import com.gtnewhorizons.angelica.experimental.surround.Surround; +import com.gtnewhorizons.angelica.rendering.GuiBlockSupersampling; +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(TileEntityRendererDispatcher.class) +public class MixinTileEntityRendererDispatcher_GuiSupersampling { + @Surround(method = "renderTileEntityAt(Lnet/minecraft/tileentity/TileEntity;DDDF)V") + private void angelica$beginGuiTileEntity() { + @Surround.Carry final int previous = GuiBlockSupersampling.begin(); + } + + @Surround.Finally + private void angelica$endGuiTileEntity(@Surround.Carry int previous) { + GuiBlockSupersampling.end(previous); + } +} diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/celeritas/features/textures/MixinTextureMap.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/celeritas/features/textures/MixinTextureMap.java index e220e025e..55dd41656 100644 --- a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/celeritas/features/textures/MixinTextureMap.java +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/celeritas/features/textures/MixinTextureMap.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.mixins.early.celeritas.features.textures; +import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.mixins.interfaces.TextureMetadataExtension; import com.gtnewhorizons.angelica.rendering.celeritas.SpriteExtension; import com.gtnewhorizons.angelica.rendering.celeritas.TextureMapExtension; @@ -178,6 +179,8 @@ public class MixinTextureMap implements TextureMapExtension { @Inject(method = "loadTextureAtlas", at = @At("RETURN")) private void celeritas$generateQuadTree(CallbackInfo ci, @Local(ordinal = 0) Stitcher stitcher) { if ((Object) this == Minecraft.getMinecraft().getTextureMapBlocks()) { + GLStateManager.markTextureAsAtlas(((TextureMap) (Object) this).getGlTextureId(), + ((SpriteExtension) this.missingImage).angelica$getGutterWidth()); SodiumGameOptions.recordAtlasMipmapClamp(Minecraft.getMinecraft().gameSettings.mipmapLevels, this.mipmapLevels); SodiumGameOptions.recordAtlasMipmapLevels(this.mipmapLevels);