Plenty of fixes for GTNH (#2202)

This commit is contained in:
Eclipse-Sol authored and GitHub committed 2026-10-03 13:19:15 -07:00
1 parent c7a0f3a208
commit fe82cd320a
26 files changed
+675 -82

No files matched your search

@@ -107,9 +107,11 @@ public final class StateSet {
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
GLStateManager.getActiveTextureUnitStack(), GLStateManager.getProgramStack(),
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
GLStateManager.getTextures().getTextureUnitStates(0)
GLStateManager.getTextures().getTextureUnitStates(0),
GLStateManager.getColorMask(), GLStateManager.getCullState(),
GLStateManager.getPolygonState(), GLStateManager.getPolygonOffsetFillState()
),
R_BLEND | R_DEPTH | R_TEXTURE | R_ACTIVE_UNIT | R_PROGRAM,
R_BLEND | R_DEPTH | R_COLOR_MASK | R_TEXTURE | R_ACTIVE_UNIT | R_PROGRAM | R_POLYGON,
0,
true);
}
@@ -121,9 +123,11 @@ public final class StateSet {
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
GLStateManager.getActiveTextureUnitStack(),
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
GLStateManager.getTextures().getTextureUnitStates(0)
GLStateManager.getTextures().getTextureUnitStates(0),
GLStateManager.getColorMask(), GLStateManager.getCullState(),
GLStateManager.getPolygonState(), GLStateManager.getPolygonOffsetFillState()
),
R_BLEND | R_DEPTH | R_TEXTURE | R_ACTIVE_UNIT,
R_BLEND | R_DEPTH | R_COLOR_MASK | R_TEXTURE | R_ACTIVE_UNIT | R_POLYGON,
0,
true);
}
@@ -54,9 +54,17 @@ public final class GeometryShaderGenerator {
sb.append(" vec4 c0 = gl_in[0].gl_Position;\n");
sb.append(" vec4 c1 = gl_in[1].gl_Position;\n\n");
sb.append(" float d0 = c0.z + c0.w;\n");
sb.append(" float d1 = c1.z + c1.w;\n");
sb.append(" if (d0 < 0.0 && d1 < 0.0) return;\n");
sb.append(" float t0 = d0 < 0.0 ? d0 / (d0 - d1) : 0.0;\n");
sb.append(" float t1 = d1 < 0.0 ? d0 / (d0 - d1) : 1.0;\n");
sb.append(" vec4 p0 = mix(c0, c1, t0);\n");
sb.append(" vec4 p1 = mix(c0, c1, t1);\n\n");
sb.append(" // NDC positions\n");
sb.append(" vec2 n0 = c0.xy / c0.w;\n");
sb.append(" vec2 n1 = c1.xy / c1.w;\n\n");
sb.append(" vec2 n0 = p0.xy / p0.w;\n");
sb.append(" vec2 n1 = p1.xy / p1.w;\n\n");
sb.append(" // Screen-space direction and perpendicular\n");
sb.append(" vec2 dir = normalize((n1 - n0) * u_ViewportSize);\n");
@@ -74,22 +82,22 @@ public final class GeometryShaderGenerator {
}
private static void emitVertex(StringBuilder sb, VertexKey key, int endpointIdx, String offsetExpr) {
final String ci = "c" + endpointIdx;
final String pi = "p" + endpointIdx;
final String ni = "n" + endpointIdx;
final String idx = "[" + endpointIdx + "]";
final String ti = "t" + endpointIdx;
sb.append(" // Endpoint ").append(endpointIdx).append(' ').append(offsetExpr).append('\n');
sb.append(" v_Color = v_Color_gs").append(idx).append(";\n");
sb.append(" v_Color = ").append(lerp("v_Color_gs", ti)).append(";\n");
if (key.separateSpecular()) {
sb.append(" v_SpecularColor = v_SpecularColor_gs").append(idx).append(";\n");
sb.append(" v_SpecularColor = ").append(lerp("v_SpecularColor_gs", ti)).append(";\n");
}
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append(" v_TexCoord0 = v_TexCoord0_gs").append(idx).append(";\n");
if (key.lightmapEnabled()) sb.append(" v_TexCoord1 = v_TexCoord1_gs").append(idx).append(";\n");
if (key.unitTexCoordEnabled(2)) sb.append(" v_TexCoord2 = v_TexCoord2_gs").append(idx).append(";\n");
if (key.unitTexCoordEnabled(3)) sb.append(" v_TexCoord3 = v_TexCoord3_gs").append(idx).append(";\n");
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append(" v_TexCoord0 = ").append(lerp("v_TexCoord0_gs", ti)).append(";\n");
if (key.lightmapEnabled()) sb.append(" v_TexCoord1 = ").append(lerp("v_TexCoord1_gs", ti)).append(";\n");
if (key.unitTexCoordEnabled(2)) sb.append(" v_TexCoord2 = ").append(lerp("v_TexCoord2_gs", ti)).append(";\n");
if (key.unitTexCoordEnabled(3)) sb.append(" v_TexCoord3 = ").append(lerp("v_TexCoord3_gs", ti)).append(";\n");
if (key.fogEnabled()) {
sb.append(" v_FogCoord = v_FogCoord_gs").append(idx).append(";\n");
sb.append(" v_FogCoord = ").append(lerp("v_FogCoord_gs", ti)).append(";\n");
}
if (key.lineStipple()) {
sb.append(" v_LineStart = v_LineStart_gs[0];\n");
@@ -97,11 +105,15 @@ public final class GeometryShaderGenerator {
if (key.clipPlanesEnabled()) {
for (int i = 0; i < 8; i++) {
sb.append(" gl_ClipDistance[").append(i).append("] = gl_in").append(idx).append(".gl_ClipDistance[").append(i).append("];\n");
sb.append(" gl_ClipDistance[").append(i).append("] = mix(gl_in[0].gl_ClipDistance[").append(i).append("], gl_in[1].gl_ClipDistance[").append(i).append("], ").append(ti).append(");\n");
}
}
sb.append(" gl_Position = vec4((").append(ni).append(' ').append(offsetExpr).append(") * ").append(ci).append(".w, ").append(ci).append(".z, ").append(ci).append(".w);\n");
sb.append(" gl_Position = vec4((").append(ni).append(' ').append(offsetExpr).append(") * ").append(pi).append(".w, ").append(pi).append(".z, ").append(pi).append(".w);\n");
sb.append(" EmitVertex();\n\n");
}
private static String lerp(String input, String t) {
return "mix(" + input + "[0], " + input + "[1], " + t + ")";
}
}
@@ -89,33 +89,41 @@ public class GLSM_StateSet_GLTest {
}
@Test
void fontRestoresDepthAxisAndShadeModelButNotCull() {
void fontRestoresDepthAxisShadeModelAndPolygonOffsetButNotStencil() {
try {
GLStateManager.enableDepthTest();
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
GLStateManager.glShadeModel(GL11.GL_SMOOTH);
GLStateManager.enableCull();
GLStateManager.glCullFace(GL11.GL_BACK);
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(0.0f, 0.0f);
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
final int d = GLStateManager.pushState(StateSet.FONT);
GLStateManager.disableDepthTest();
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
GLStateManager.glShadeModel(GL11.GL_FLAT);
GLStateManager.glCullFace(GL11.GL_FRONT);
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(-10.0f, -10.0f);
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
GLStateManager.popStateTo(d);
verifyIsEnabled(GL11.GL_DEPTH_TEST, true, "Depth test enable - restored by FONT pop");
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func");
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func");
assertEquals(GL11.GL_SMOOTH, GLStateManager.getShadeModelState().getValue(), "cache shade model");
assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - not a FONT member, must stay toggled");
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - not a FONT member, must stay toggled");
verifyIsEnabled(GL11.GL_POLYGON_OFFSET_FILL, false, "Polygon offset fill - restored by FONT pop");
assertEquals(0.0f, GLStateManager.getPolygonState().getOffsetFactor(), "cache polygon offset factor");
assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_FACTOR), "driver polygon offset factor");
assertEquals(0.0f, GLStateManager.getPolygonState().getOffsetUnits(), "cache polygon offset units");
assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_UNITS), "driver polygon offset units");
verifyIsEnabled(GL11.GL_STENCIL_TEST, false, "Stencil test - not a FONT member, must stay toggled");
} finally {
GLStateManager.disableDepthTest();
GLStateManager.glDepthFunc(GL11.GL_LESS);
GLStateManager.glShadeModel(GL11.GL_SMOOTH);
GLStateManager.disableCull();
GLStateManager.glCullFace(GL11.GL_BACK);
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(0.0f, 0.0f);
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
}
}
@@ -0,0 +1,114 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.FloatBuffer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@GLCoreTest
class WideLineClipGLTest {
private static final int SIZE = 64;
private static final double NEAR = 0.05;
private static final float LINE_WIDTH = 3.0f;
private static final int BACKGROUND = 0xFF000000;
private static final float[] IN_FRONT = { 0f, -1f, -2f };
private static final float[] BEHIND = { 0f, 0.8f, 2f };
private boolean savedEmulation;
private int vao;
private int vbo;
@BeforeEach
void setUp() {
assumeTrue(GLStateManager.supportsGeometryShaders());
savedEmulation = GLStateManager.wideLineEmulationEnabled;
GLStateManager.wideLineEmulationEnabled = true;
FfpFixture.resetFfpState();
ShaderManager.enable();
ShaderManager.getInstance().activate();
GLStateManager.glViewport(0, 0, SIZE, SIZE);
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
GLStateManager.glLoadIdentity();
GLStateManager.glFrustum(-NEAR, NEAR, -NEAR, NEAR, NEAR, 100.0);
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glLoadIdentity();
GLStateManager.glDisable(GL11.GL_DEPTH_TEST);
GLStateManager.glLineWidth(LINE_WIDTH);
}
@AfterEach
void tearDown() {
GLStateManager.wideLineEmulationEnabled = savedEmulation;
GLStateManager.glLineWidth(1.0f);
if (vbo != 0) { GLStateManager.glDeleteBuffers(vbo); vbo = 0; }
if (vao != 0) { GLStateManager.glDeleteVertexArrays(vao); vao = 0; }
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
GLStateManager.glLoadIdentity();
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
FfpFixture.resetFfpState();
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
@Test
void lineEndingBehindTheCameraKeepsItsWidth() {
assertConstantWidth(IN_FRONT, BEHIND);
}
@Test
void lineStartingBehindTheCameraKeepsItsWidth() {
assertConstantWidth(BEHIND, IN_FRONT);
}
@Test
void lineFullyBehindTheCameraDrawsNothing() {
final int[] pixels = drawLine(new float[] { 0f, -1f, 2f }, BEHIND);
for (int px : pixels) {
assertEquals(BACKGROUND, px);
}
}
private void assertConstantWidth(float[] start, float[] end) {
final int[] pixels = drawLine(start, end);
for (int row = 0; row < SIZE / 4; row++) {
int lit = 0;
for (int x = 0; x < SIZE; x++) {
if (pixels[row * SIZE + x] != BACKGROUND) lit++;
}
final int y = row, count = lit;
assertTrue(count > 0 && count <= LINE_WIDTH + 1, () -> "row " + y + " is " + count + " pixels wide");
}
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
}
private int[] drawLine(float[] start, float[] end) {
vao = GLStateManager.glGenVertexArrays();
GLStateManager.glBindVertexArray(vao);
vbo = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
final FloatBuffer positions = BufferUtils.createFloatBuffer(6);
positions.put(start).put(end).flip();
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, positions, GL15.GL_STATIC_DRAW);
FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, 12, 0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
VAOManager.setCurrentVertexFlags(0);
FfpFixture.clear();
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
GLStateManager.glDrawArrays(GL11.GL_LINES, 0, 2);
GLStateManager.glBindVertexArray(0);
return FfpFixture.readRegion(SIZE);
}
}
@@ -192,6 +192,9 @@ public class BatchingFontRenderer {
private int lightmapTextureId = 0;
private int lightmapPackedLight = -1;
private final TextDrawState drawState = new TextDrawState();
private final TextDrawState liveDrawState = new TextDrawState();
private final Vector3f lightingFactor = new Vector3f(1.0f, 1.0f, 1.0f);
private boolean lightingFactorActive = false;
@@ -243,6 +246,13 @@ public class BatchingFontRenderer {
lightmapPackedLight = packedLight;
}
private void captureDrawState() {
liveDrawState.captureLive();
if (liveDrawState.sameAs(drawState)) return;
sealBatchSegment();
drawState.set(liveDrawState);
}
private static final class TextSegment {
final Matrix4f mvp = new Matrix4f();
final Matrix4f modelView = new Matrix4f();
@@ -256,8 +266,7 @@ public class BatchingFontRenderer {
int packedLight;
float normalX, normalY, normalZ;
int blockEntityId;
boolean depthTest;
boolean depthMask;
final TextDrawState drawState = new TextDrawState();
}
// 16-bit EBO index range
@@ -543,8 +552,7 @@ public class BatchingFontRenderer {
segment.normalY = normal.y;
segment.normalZ = normal.z;
segment.blockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
segment.depthTest = GLStateManager.getDepthTest().isEnabled();
segment.depthMask = GLStateManager.isEffectiveDepthMaskEnabled();
segment.drawState.set(drawState);
batchSegments.add(segment);
batchSealedEnd = end;
}
@@ -576,7 +584,8 @@ public class BatchingFontRenderer {
private static boolean shouldDeferNow() {
if (!GLStateManager.isMainThread()) return false;
return TesrBatchRenderer.INSTANCE.hasPendingGeometry() || ModelPartBatcher.INSTANCE.isActive();
return (TesrBatchRenderer.INSTANCE.hasPendingGeometry() || ModelPartBatcher.INSTANCE.isActive())
&& BatchDrawDefaults.drawsToPassFramebuffer();
}
public void beginDeferredBatch() {
@@ -664,23 +673,13 @@ public class BatchingFontRenderer {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPushMatrix();
try {
boolean curDepthTest = GLStateManager.getDepthTest().isEnabled();
boolean curDepthMask = GLStateManager.isEffectiveDepthMaskEnabled();
for (final TextSegment segment : deferredSegments) {
final BatchingFontRenderer owner = segment.owner;
if (segment.cmdStart == segment.cmdEnd) continue;
GLStateManager.setModelViewMatrix(segment.modelView);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(segment.blockEntityId);
if (segment.depthTest != curDepthTest) {
if (segment.depthTest) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
curDepthTest = segment.depthTest;
}
if (segment.depthMask != curDepthMask) {
GLStateManager.glDepthMask(segment.depthMask);
curDepthMask = segment.depthMask;
}
segment.drawState.apply();
final FontDrawCmd[] cmdsData = owner.batchCommands.elements();
Arrays.sort(cmdsData, segment.cmdStart, segment.cmdEnd, FontDrawCmd.DRAW_ORDER_COMPARATOR);
@@ -708,22 +707,13 @@ public class BatchingFontRenderer {
deferredSegments.get(0).owner.setupFontDrawState();
flushLastTexture = INVALID_TEXTURE;
resetFlushLightmap();
boolean curDepthTest = GLStateManager.getDepthTest().isEnabled();
boolean curDepthMask = GLStateManager.isEffectiveDepthMaskEnabled();
for (final TextSegment segment : deferredSegments) {
FLUSH_MVP.clear();
segment.mvp.get(FLUSH_MVP);
GLStateManager.glUniformMatrix4(segment.owner.mvpMatrixLocation, false, FLUSH_MVP);
uploadLightmap(segment.owner.lightmapLocation, segment.lightmapActive, segment.lightmapU,
segment.lightmapV, segment.lightmapTexture);
if (segment.depthTest != curDepthTest) {
if (segment.depthTest) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
curDepthTest = segment.depthTest;
}
if (segment.depthMask != curDepthMask) {
GLStateManager.glDepthMask(segment.depthMask);
curDepthMask = segment.depthMask;
}
segment.owner.applyDrawState(segment.drawState);
drawCommands(segment.owner.batchCommands.elements(), segment.cmdStart, segment.cmdEnd, segment.owner);
}
} finally {
@@ -734,6 +724,11 @@ public class BatchingFontRenderer {
}
}
private void applyDrawState(TextDrawState state) {
state.apply();
GLStateManager.glUniform1f(alphaTestRefLocation, state.alphaRef);
}
public static void discardDeferredText() {
if (deferredSegments.isEmpty()) return;
for (int i = 0, n = deferredSegments.size(); i < n; i++) {
@@ -841,6 +836,7 @@ public class BatchingFontRenderer {
segment.mvp.get(FLUSH_MVP);
GLStateManager.glUniformMatrix4(mvpMatrixLocation, false, FLUSH_MVP);
uploadLightmap(lightmapLocation, segment.lightmapActive, segment.lightmapU, segment.lightmapV, segment.lightmapTexture);
applyDrawState(segment.drawState);
drawCommands(cmds, segment.cmdStart, segment.cmdEnd, this);
}
if (batchSealedEnd < cmdCount) {
@@ -849,6 +845,7 @@ public class BatchingFontRenderer {
scratchMvp.get(FLUSH_MVP);
GLStateManager.glUniformMatrix4(mvpMatrixLocation, false, FLUSH_MVP);
uploadLightmap(lightmapLocation, lightmapActive, lightmapU, lightmapV, lightmapTextureId);
applyDrawState(drawState);
drawCommands(cmds, batchSealedEnd, cmdCount, this);
}
} finally {
@@ -882,7 +879,6 @@ public class BatchingFontRenderer {
fontBrightnessLast = FontConfig.fontBrightness;
GLStateManager.glUniform1f(fontBrightness, 0.625f * FontConfig.fontBrightness + 0.1875f);
}
GLStateManager.glUniform1f(alphaTestRefLocation, GLStateManager.getAlphaState().getReference());
if (lightmapSamplerProgram != fontShaderId) {
GLStateManager.glUniform1i(lightmapSamplerLocation, LIGHTMAP_TEX_UNIT);
lightmapSamplerProgram = fontShaderId;
@@ -1011,12 +1007,14 @@ public class BatchingFontRenderer {
if (segment.cmdStart == segment.cmdEnd) continue;
GLStateManager.setModelViewMatrix(segment.modelView);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(segment.blockEntityId);
segment.drawState.apply();
emitRangeThroughPipeline(this, cmdsData, segment.cmdStart, segment.cmdEnd,
segment.packedLight, segment.normalX, segment.normalY, segment.normalZ);
}
if (batchSealedEnd < cmdCount) {
GLStateManager.setModelViewMatrix(scratchModelView);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
drawState.apply();
final Vector3f normal = ShaderManager.getCurrentNormal();
emitRangeThroughPipeline(this, cmdsData, batchSealedEnd, cmdCount,
lightmapPackedLight, normal.x, normal.y, normal.z);
@@ -1241,6 +1239,7 @@ public class BatchingFontRenderer {
this.beginBatch();
this.captureLightmapState();
this.captureDrawState();
this.lightingFactorActive = isWorldSpaceText() && FFPVertexLighting.modulatesVertexColor(this.lightingFactor);
float curX = anchorX;
try {
@@ -0,0 +1,95 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
import org.lwjgl.opengl.GL11;
/**
* Drawing text picks up settings from whoever asked for it, such as depth testing, culling, polygon offset and
* alpha cutoff. Text is sometimes drawn later than requested, so these are saved with it to make the late draw
* look the same as an immediate one.
*/
final class TextDrawState {
private final ColorMask colorMaskScratch = new ColorMask();
boolean depthTest;
int depthFunc;
boolean depthMask;
boolean colorRed;
boolean colorGreen;
boolean colorBlue;
boolean colorAlpha;
boolean cull;
int cullFace;
int frontFace;
boolean polygonOffset;
float offsetFactor;
float offsetUnits;
float alphaRef;
void captureLive() {
depthTest = GLStateManager.getDepthTest().isEnabled();
depthFunc = GLStateManager.getDepthState().getFunc();
depthMask = GLStateManager.isEffectiveDepthMaskEnabled();
final ColorMask colorMask = GLStateManager.getEffectiveColorMask(colorMaskScratch);
colorRed = colorMask.red;
colorGreen = colorMask.green;
colorBlue = colorMask.blue;
colorAlpha = colorMask.alpha;
final PolygonState polygon = GLStateManager.getPolygonState();
cull = GLStateManager.getCullState().isEnabled();
cullFace = polygon.getCullFaceMode();
frontFace = polygon.getFrontFace();
polygonOffset = GLStateManager.getPolygonOffsetFillState().isEnabled();
offsetFactor = polygon.getOffsetFactor();
offsetUnits = polygon.getOffsetUnits();
alphaRef = GLStateManager.getAlphaState().getReference();
}
boolean sameAs(TextDrawState other) {
return depthTest == other.depthTest
&& depthFunc == other.depthFunc
&& depthMask == other.depthMask
&& colorRed == other.colorRed
&& colorGreen == other.colorGreen
&& colorBlue == other.colorBlue
&& colorAlpha == other.colorAlpha
&& cull == other.cull
&& cullFace == other.cullFace
&& frontFace == other.frontFace
&& polygonOffset == other.polygonOffset
&& offsetFactor == other.offsetFactor
&& offsetUnits == other.offsetUnits
&& alphaRef == other.alphaRef;
}
void set(TextDrawState other) {
depthTest = other.depthTest;
depthFunc = other.depthFunc;
depthMask = other.depthMask;
colorRed = other.colorRed;
colorGreen = other.colorGreen;
colorBlue = other.colorBlue;
colorAlpha = other.colorAlpha;
cull = other.cull;
cullFace = other.cullFace;
frontFace = other.frontFace;
polygonOffset = other.polygonOffset;
offsetFactor = other.offsetFactor;
offsetUnits = other.offsetUnits;
alphaRef = other.alphaRef;
}
void apply() {
if (depthTest) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
GLStateManager.glDepthFunc(depthFunc);
GLStateManager.glDepthMask(depthMask);
GLStateManager.glColorMask(colorRed, colorGreen, colorBlue, colorAlpha);
if (cull) GLStateManager.enableCull(); else GLStateManager.disableCull();
GLStateManager.glCullFace(cullFace);
GLStateManager.glFrontFace(frontFace);
if (polygonOffset) GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL); else GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(offsetFactor, offsetUnits);
}
}
@@ -12,12 +12,12 @@ import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
import com.gtnewhorizons.angelica.compat.battlegear2.Battlegear2Compat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
import com.gtnewhorizons.angelica.helpers.ItemBlockLight;
import com.gtnewhorizons.angelica.mixins.interfaces.PrimedEntityAccessor;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import mods.battlegear2.api.core.IBattlePlayer;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
@@ -586,10 +586,7 @@ public class DynamicLights {
if (item instanceof IDynamicLightProducer lightProducer) {
return lightProducer.getLuminance(stack);
} else if (item instanceof ItemBlock itemBlock) {
final Block block = itemBlock.field_150939_a;
if (block != null) {
return block.getLightValue();
}
return ItemBlockLight.getLightValue(itemBlock, stack);
}
if (item == Items.lava_bucket) return Blocks.lava.getLightValue();
@@ -0,0 +1,97 @@
package com.gtnewhorizons.angelica.helpers;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.util.ForgeDirection;
/**
* Light value of the block an ItemBlock stack would place.
*/
public final class ItemBlockLight {
private static final SingleBlockAccess ACCESS = new SingleBlockAccess();
private ItemBlockLight() {}
public static int getLightValue(ItemBlock item, ItemStack stack) {
final Block block = item.field_150939_a;
if (block == null) return 0;
ACCESS.set(block, item.getMetadata(stack.getItemDamage()));
try {
return block.getLightValue(ACCESS, 0, 0, 0);
} catch (RuntimeException e) {
return block.getLightValue();
} finally {
ACCESS.set(Blocks.air, 0);
}
}
private static final class SingleBlockAccess implements IBlockAccess {
private Block block = Blocks.air;
private int metadata;
void set(Block block, int metadata) {
this.block = block;
this.metadata = metadata;
}
private static boolean isOrigin(int x, int y, int z) {
return x == 0 && y == 0 && z == 0;
}
@Override
public Block getBlock(int x, int y, int z) {
return isOrigin(x, y, z) ? block : Blocks.air;
}
@Override
public int getBlockMetadata(int x, int y, int z) {
return isOrigin(x, y, z) ? metadata : 0;
}
@Override
public TileEntity getTileEntity(int x, int y, int z) {
return null;
}
@Override
public int getLightBrightnessForSkyBlocks(int x, int y, int z, int min) {
return min << 4;
}
@Override
public int isBlockProvidingPowerTo(int x, int y, int z, int direction) {
return 0;
}
@Override
public boolean isAirBlock(int x, int y, int z) {
return !isOrigin(x, y, z) || block == Blocks.air;
}
@Override
public BiomeGenBase getBiomeGenForCoords(int x, int z) {
return BiomeGenBase.plains;
}
@Override
public int getHeight() {
return 256;
}
@Override
public boolean extendedLevelsInChunkCache() {
return false;
}
@Override
public boolean isSideSolid(int x, int y, int z, ForgeDirection side, boolean _default) {
return false;
}
}
}
@@ -73,9 +73,6 @@ public final class SelectionBoxRenderer {
public static void draw(AxisAlignedBB aabb, int color) {
if (program == null || vao == null) return;
GLStateManager.enableDepthTest();
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
final float minX = (float) aabb.minX, minY = (float) aabb.minY, minZ = (float) aabb.minZ;
final float extX = (float) (aabb.maxX - aabb.minX);
final float extY = (float) (aabb.maxY - aabb.minY);
@@ -119,6 +119,10 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
return this.renderSectionManager;
}
public WorldClient getWorld() {
return this.world;
}
private long asyncEwmaNanos;
private long uploadEwmaNanos;
@@ -43,6 +43,16 @@ public final class BakedTransformCapture extends DirectTessellator {
@Override
protected int interceptDraw(Tessellator tessellator) {
transform = delta.deltaOrNull(deltaMatrix);
try {
return super.interceptDraw(tessellator);
} finally {
transform = null;
}
}
@Override
public int draw() {
final Color4 color = GLStateManager.getColor();
final int packed = ColorABGR.pack(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
if (!sawRun) {
@@ -51,12 +61,7 @@ public final class BakedTransformCapture extends DirectTessellator {
} else if (packed != firstColor) {
colorUniform = false;
}
transform = delta.deltaOrNull(deltaMatrix);
try {
return super.interceptDraw(tessellator);
} finally {
transform = null;
}
return super.draw();
}
// While capturing, Tessellator.instance is this tessellator, so vanilla code (e.g. RenderBlocks.renderBlockAsItem)
@@ -25,6 +25,11 @@ public final class BatchDrawDefaults {
passFramebuffer = GLStateManager.getDrawFramebuffer();
}
public static boolean drawsToPassFramebuffer() {
return passFramebuffer < 0 || TesrBatchRenderer.deferredPipeline() != null
|| GLStateManager.getDrawFramebuffer() == passFramebuffer;
}
public void apply() {
if (passFramebuffer >= 0 && TesrBatchRenderer.deferredPipeline() == null) {
final int framebuffer = GLStateManager.getDrawFramebuffer();
@@ -3,7 +3,6 @@ package jss.notfine.gui;
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiListExtended;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.resources.I18n;
@@ -19,7 +18,7 @@ public class GuiCustomMenu extends GuiScreen {
private final OptionPage[] subPages;
protected String screenTitle;
private GuiListExtended optionsRowList;
private GuiCustomOptionsRowList optionsRowList;
public GuiCustomMenu(GuiScreen parentGuiScreen, OptionPage optionPage, OptionPage... subPages) {
this.parentGuiScreen = parentGuiScreen;
@@ -30,6 +29,7 @@ public class GuiCustomMenu extends GuiScreen {
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
buttonList.clear();
buttonList.add(new GuiButton(200, width / 2 - 110, height - 27, I18n.format("gui.done")));
optionsRowList = new GuiCustomOptionsRowList(mc, width, height, 32, height - 32, 25, optionPage, subPages);
@@ -88,9 +88,15 @@ public class GuiCustomMenu extends GuiScreen {
if(keyCode == Keyboard.KEY_ESCAPE && !(parentGuiScreen instanceof GuiCustomMenu)) {
saveChanges();
}
optionsRowList.keyTyped(keyCode);
super.keyTyped(typedChar, keyCode);
}
@Override
public void onGuiClosed() {
Keyboard.enableRepeatEvents(false);
}
private void saveChanges() {
for(OptionStorage<?> storage : dirtyStorages) {
storage.save();
@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import jss.notfine.gui.options.control.element.NotFineControlElementFactory;
import jss.notfine.gui.options.control.element.SliderControlElement;
import me.jellysquid.mods.sodium.client.gui.options.Option;
import me.jellysquid.mods.sodium.client.gui.options.OptionGroup;
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
@@ -20,6 +21,7 @@ import java.util.List;
public class GuiCustomOptionsRowList extends GuiListExtended {
private static final NotFineControlElementFactory factory = new NotFineControlElementFactory();
private final List<Row> settingsList = Lists.newArrayList();
private GuiButton focusedButton;
public GuiCustomOptionsRowList(Minecraft mc, int width, int height, int top, int bottom, int slotHeight, OptionPage optionPage, OptionPage... subPages) {
super(mc, width, height, top, bottom, slotHeight);
@@ -64,8 +66,14 @@ public class GuiCustomOptionsRowList extends GuiListExtended {
return super.getScrollBarX() + 32;
}
public void keyTyped(int keyCode) {
if(focusedButton instanceof SliderControlElement slider) {
slider.keyTyped(keyCode);
}
}
@SideOnly(Side.CLIENT)
public static class Row implements GuiListExtended.IGuiListEntry {
public class Row implements GuiListExtended.IGuiListEntry {
private final Minecraft mc = Minecraft.getMinecraft();
private final GuiButton buttonOne, buttonTwo;
@@ -93,9 +101,11 @@ public class GuiCustomOptionsRowList extends GuiListExtended {
@Override
public boolean mousePressed(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) {
if(buttonOne.mousePressed(mc, x, y)) {
focusedButton = buttonOne;
return true;
}
if(buttonTwo != null && buttonTwo.mousePressed(mc, x, y)) {
focusedButton = buttonTwo;
return true;
}
return false;
@@ -6,6 +6,7 @@ import me.jellysquid.mods.sodium.client.util.Dim2i;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import net.minecraft.client.Minecraft;
import net.minecraft.util.MathHelper;
import org.lwjgl.input.Keyboard;
public class SliderControlElement extends NotFineControlElement<Integer> {
private final ControlValueFormatter formatter;
@@ -68,6 +69,29 @@ public class SliderControlElement extends NotFineControlElement<Integer> {
}
}
public boolean keyTyped(int keyCode) {
if(!option.isAvailable()) {
return false;
}
final int step;
if(keyCode == Keyboard.KEY_LEFT) {
step = -interval;
} else if(keyCode == Keyboard.KEY_RIGHT) {
step = interval;
} else {
return false;
}
final int stepped = MathHelper.clamp_int(option.getValue() + step, min, max);
if(stepped != option.getValue()) {
option.setValue(stepped);
value = MathHelper.clamp_float((float)(stepped - min) / (max - min), 0f, 1f);
onOptionValueChanged();
}
return true;
}
private void updateSlider(int mouseX) {
value = (float)(mouseX - (xPosition + 4)) / (float)(width - 8);
//Clamp normalized value
@@ -78,6 +78,7 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
this.frame = this.parentFrameBuilder().build();
this.children.add(this.frame);
@@ -287,6 +288,18 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
}
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
final boolean onSearchField = this.searchTextField.isMouseOver(mouseX, mouseY);
super.mouseClicked(mouseX, mouseY, mouseButton);
if (!onSearchField) {
this.searchTextField.setFocused(false);
this.setFocused(this.frame);
}
}
@Override
public boolean shouldCloseOnEsc() {
return !this.hasPendingChanges;
@@ -154,6 +154,11 @@ public class SearchTextFieldComponent extends AbstractWidget {
return this.isFocused();
}
@Override
public boolean isMouseOver(double mouseX, double mouseY) {
return this.dim.containsCursor(mouseX, mouseY);
}
private void drawSelectionHighlight(int x1, int y1, int x2, int y2) {
int i;
if (x1 < x2) {
@@ -54,6 +54,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
protected boolean hasPendingChanges;
protected ControlElement<?> hoveredElement;
private Element focusedElement;
protected OptionPage shaderPacks;
@@ -97,13 +98,20 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
this.rebuildGUI();
}
@Override
public void onGuiClosed() {
Keyboard.enableRepeatEvents(false);
}
protected void rebuildGUI() {
this.controls.clear();
this.children.clear();
this.drawable.clear();
this.focusedElement = null;
if (this.currentPage == null) {
if (this.pages.isEmpty()) {
@@ -298,6 +306,10 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
SodiumGameOptionPages.quality(), SodiumGameOptionPages.advanced(), SodiumGameOptionPages.performance(),
SodiumGameOptionPages.appearance()));
}
if (this.focusedElement != null) {
this.focusedElement.keyTyped(typedChar, keyCode);
}
}
public boolean shouldCloseOnEsc() {
@@ -314,7 +326,11 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
super.mouseClicked(mouseX, mouseY, mouseButton);
this.children.forEach(element -> element.mouseClicked(mouseX, mouseY, mouseButton));
this.children.forEach(element -> {
if (element.mouseClicked(mouseX, mouseY, mouseButton)) {
this.focusedElement = element;
}
});
}
@Override
@@ -7,6 +7,7 @@ import me.jellysquid.mods.sodium.client.gui.options.control.ControlValueFormatte
import me.jellysquid.mods.sodium.client.gui.utils.Rect2i;
import me.jellysquid.mods.sodium.client.util.Dim2i;
import net.minecraft.util.MathHelper;
import org.lwjgl.input.Keyboard;
public class SodiumControlElementFactory implements ControlElementFactory {
@Override
@@ -194,6 +195,30 @@ public class SodiumControlElementFactory implements ControlElementFactory {
return false;
}
@Override
public boolean keyTyped(char typedChar, int keyCode) {
if (!this.option.isAvailable()) {
return false;
}
final int step;
if (keyCode == Keyboard.KEY_LEFT) {
step = -this.interval;
} else if (keyCode == Keyboard.KEY_RIGHT) {
step = this.interval;
} else {
return false;
}
final int value = MathHelper.clamp_int(this.option.getValue() + step, this.min, this.min + this.range);
if (this.option.getValue() != value) {
this.option.setValue(value);
}
return true;
}
}
private static class TickBoxControlElement extends SodiumControlElement<Boolean> {
@@ -320,8 +320,6 @@ public class Iris {
// See: https://github.com/IrisShaders/Iris/issues/323
lastDimensionName = "Overworld";
Iris.getPipelineManager().preparePipeline("Overworld");
BlockRenderingSettings.INSTANCE.reloadRendererIfRequired();
}
public static void toggleShaders(Minecraft minecraft, boolean enabled) throws IOException {
@@ -684,8 +682,6 @@ public class Iris {
// https://github.com/IrisShaders/Iris/issues/1330
if (Minecraft.getMinecraft().theWorld != null) {
Iris.getPipelineManager().preparePipeline(Iris.getCurrentDimensionName());
BlockRenderingSettings.INSTANCE.reloadRendererIfRequired();
}
if (loadedIncompatiblePack() && Minecraft.getMinecraft().thePlayer != null) {
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.uniforms.SystemTimeUniforms;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL11;
@@ -43,6 +44,7 @@ public class PipelineManager {
Iris.logger.info("Creating pipeline for dimension '{}'", currentDimension);
pipeline = pipelineFactory.apply(currentDimension);
pipelinesPerDimension.put(currentDimension, pipeline);
BlockRenderingSettings.INSTANCE.reloadRendererIfRequired();
if (Tracy.ENABLED) {
shaderSection = Tracy.sectionEnter(Tracy.SECTION_SHADERS, "shaders " + Iris.getCurrentPackName() + " / " + currentDimension);
}
@@ -1,5 +1,6 @@
package net.irisshaders.iris.api.v0.item;
import com.gtnewhorizons.angelica.helpers.ItemBlockLight;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
@@ -11,8 +12,7 @@ public interface IrisItemLightProvider {
default int getLightEmission(EntityPlayer player, ItemStack stack) {
if (stack.getItem() instanceof ItemBlock item) {
return item.field_150939_a.getLightValue();
return ItemBlockLight.getLightValue(item, stack);
}
return 0;
@@ -10,19 +10,25 @@ void main() {
vec4 c0 = gl_in[0].gl_Position;
vec4 c1 = gl_in[1].gl_Position;
vec2 n0 = c0.xy / c0.w;
vec2 n1 = c1.xy / c1.w;
float d0 = c0.z + c0.w;
float d1 = c1.z + c1.w;
if (d0 < 0.0 && d1 < 0.0) return;
vec4 p0 = d0 < 0.0 ? mix(c0, c1, d0 / (d0 - d1)) : c0;
vec4 p1 = d1 < 0.0 ? mix(c0, c1, d0 / (d0 - d1)) : c1;
vec2 n0 = p0.xy / p0.w;
vec2 n1 = p1.xy / p1.w;
vec2 dir = normalize((n1 - n0) * u_ViewportSize);
vec2 offset = vec2(-dir.y, dir.x) * u_LineWidth / u_ViewportSize;
gl_Position = vec4((n0 + offset) * c0.w, c0.z, c0.w);
gl_Position = vec4((n0 + offset) * p0.w, p0.z, p0.w);
EmitVertex();
gl_Position = vec4((n0 - offset) * c0.w, c0.z, c0.w);
gl_Position = vec4((n0 - offset) * p0.w, p0.z, p0.w);
EmitVertex();
gl_Position = vec4((n1 + offset) * c1.w, c1.z, c1.w);
gl_Position = vec4((n1 + offset) * p1.w, p1.z, p1.w);
EmitVertex();
gl_Position = vec4((n1 - offset) * c1.w, c1.z, c1.w);
gl_Position = vec4((n1 - offset) * p1.w, p1.z, p1.w);
EmitVertex();
EndPrimitive();
}
@@ -66,6 +66,7 @@ public class MixinRenderGlobal implements IRenderGlobalExt {
@Unique private static final Tracy.ZoneId Z_MT_QUEUE = Tracy.zoneId("mtQueue", Tracy.COLOR_TERRAIN);
@Shadow public Minecraft mc;
@Shadow public WorldClient theWorld;
@Shadow @Final private TextureManager renderEngine;
@Unique private static final String SECTION_SOLID = "draw_chunk_layer_solid";
@@ -264,6 +265,7 @@ public class MixinRenderGlobal implements IRenderGlobalExt {
@Inject(method = "loadRenderers", at = @At("RETURN"))
private void onReload(CallbackInfo ci) {
if (this.celeritas$renderer.getWorld() != this.theWorld) return;
angelica$reload();
}
@@ -7,6 +7,7 @@ import net.coderbot.iris.Iris;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.player.EntityPlayer;
@@ -20,6 +21,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(RenderGlobal.class)
public class MixinRenderGlobal {
@Inject(method = "setWorldAndLoadRenderers", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;loadRenderers()V"))
private void iris$preparePipelineForNewWorld(WorldClient world, CallbackInfo ci) {
Iris.getPipelineManager().preparePipeline(Iris.getCurrentDimensionName());
}
@Inject(method = "renderSky", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/Tessellator;instance:Lnet/minecraft/client/renderer/Tessellator;"))
private void iris$renderSky$beginNormalSky(float partialTicks, CallbackInfo ci) {
// None of the vanilla sky is rendered until after this call, so if anything is rendered before, it's CUSTOM_SKY.
@@ -0,0 +1,145 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.StateSet;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@GLCoreTest
class TextDrawStateGLTest {
@AfterEach
void resetState() {
setDefaults();
}
private static void setDefaults() {
GLStateManager.disableDepthTest();
GLStateManager.glDepthFunc(GL11.GL_LESS);
GLStateManager.glDepthMask(true);
GLStateManager.glColorMask(true, true, true, true);
GLStateManager.disableCull();
GLStateManager.glCullFace(GL11.GL_BACK);
GLStateManager.glFrontFace(GL11.GL_CCW);
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(0.0f, 0.0f);
GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f);
}
private static void setNonDefaults() {
GLStateManager.enableDepthTest();
GLStateManager.glDepthFunc(GL11.GL_GEQUAL);
GLStateManager.glDepthMask(false);
GLStateManager.glColorMask(false, false, false, false);
GLStateManager.enableCull();
GLStateManager.glCullFace(GL11.GL_FRONT);
GLStateManager.glFrontFace(GL11.GL_CW);
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(-10.0f, -10.0f);
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f);
}
private static TextDrawState capture() {
final TextDrawState state = new TextDrawState();
state.captureLive();
return state;
}
private static List<Field> stateFields() {
final List<Field> fields = new ArrayList<>();
for (Field field : TextDrawState.class.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers()) || !field.getType().isPrimitive()) continue;
field.setAccessible(true);
fields.add(field);
}
return fields;
}
private static boolean isGlState(Field field) {
return !field.getName().equals("alphaRef");
}
@Test
void everyFieldIsMovedByTheTestState() throws IllegalAccessException {
setDefaults();
final TextDrawState defaults = capture();
setNonDefaults();
final TextDrawState moved = capture();
for (Field field : stateFields()) {
assertNotEquals(field.get(defaults), field.get(moved), field.getName() + " - add it to setNonDefaults()");
}
}
@Test
void sameAsAndSetCoverEveryField() throws IllegalAccessException {
setNonDefaults();
final TextDrawState moved = capture();
setDefaults();
final TextDrawState defaults = capture();
for (Field field : stateFields()) {
final TextDrawState copy = new TextDrawState();
copy.set(moved);
assertEquals(field.get(moved), field.get(copy), field.getName() + " - missing from set()");
assertTrue(copy.sameAs(moved));
field.set(copy, field.get(defaults));
assertFalse(copy.sameAs(moved), field.getName() + " - missing from sameAs()");
}
}
@Test
void applyReplaysCapturedState() throws IllegalAccessException {
setNonDefaults();
final TextDrawState captured = capture();
setDefaults();
captured.apply();
final TextDrawState replayed = capture();
for (Field field : stateFields()) {
if (!isGlState(field)) continue;
assertEquals(field.get(captured), field.get(replayed), field.getName());
}
assertEquals(GL11.GL_GEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func");
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face");
assertEquals(GL11.GL_CW, GL11.glGetInteger(GL11.GL_FRONT_FACE), "driver front face");
assertEquals(-10.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_FACTOR), "driver polygon offset factor");
assertTrue(GL11.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL), "driver polygon offset fill");
}
@Test
void fontStateSetRestoresEverythingApplyTouches() throws IllegalAccessException {
assertApplyDoesNotLeak(StateSet.FONT);
}
@Test
void fontPipelineStateSetRestoresEverythingApplyTouches() throws IllegalAccessException {
assertApplyDoesNotLeak(StateSet.FONT_PIPELINE);
}
private static void assertApplyDoesNotLeak(StateSet set) throws IllegalAccessException {
setNonDefaults();
final TextDrawState moved = capture();
setDefaults();
final TextDrawState before = capture();
final int depth = GLStateManager.pushState(set);
moved.apply();
GLStateManager.popStateTo(depth);
final TextDrawState after = capture();
for (Field field : stateFields()) {
if (!isGlState(field)) continue;
assertEquals(field.get(before), field.get(after), field.getName() + " - not restored by the font state set");
}
}
}