mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-10-25 20:22:22 +00:00
Initial commit for gui-item-supersampling (#2299)
This commit is contained in:
1 parent
34ed1d101b
commit
17a2ed7f37
22 files changed
+562
-4
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
+302
@@ -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<Integer> 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);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
+12
@@ -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();
|
||||
|
||||
+21
@@ -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);
|
||||
}
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
}
|
||||
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
}
|
||||
+3
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user