Compare commits

...
10 Commits
Author SHA1 Message Date
Alexdoru a66db1517a fix world client leaks in bop fog handlers (#2056) 2026-09-02 11:15:25 -07:00
Jason Mitchell 269b7245d3 Make dynamic lights config less heavy (#2051) 2026-08-30 13:02:07 -07:00
Wokafishh 67f2484976 Blockrotations/Natural Textures (#2020) 2026-08-30 11:16:21 -07:00
KAMKEELandJason Mitchell 1c8687e6fd Add Secondary Color Support to Angelica (#2046)
Co-authored-by: Jason Mitchell <[email protected]>
2026-08-30 10:26:08 -07:00
Jason Mitchell e6bbd155da Stop hardcoding some translations (#2049) 2026-08-30 09:40:27 -07:00
Ranzu 2f64fef6a6 Fix color leak getting into guis (Broken ColorUtils) (#2045) 2026-08-27 14:19:55 -07:00
boubou19 b0fd1ea46e Fix some CTM Allocations (#2044) 2026-08-27 11:59:02 -07:00
ah-OOG-ah 6d8cbf9c2f Add a flag to StateAwareTesselator for per-quad directional shading (#1989)
* Add a flag for no directional shading

* Mark StateAwareTessellator as an API

* Reset internal state better
2026-08-19 18:16:04 +00:00
boubou19 096dd01446 Avoid rescanning atlas for each CTM tile (#2029) 2026-08-19 09:32:42 -07:00
Eclipse-SolandMartin Robertz 22c9479946 Many bug fixes (#2017)
Co-authored-by: Martin Robertz <[email protected]>
2026-08-19 08:55:43 -07:00
127 changed files with 3721 additions and 980 deletions

No files matched your search

+2
View File
@@ -161,6 +161,8 @@ DependencyHandlerScope.of(dependencies).apply {
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
compileOnly("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
compileOnly("com.github.GTNewHorizons:OpenBlocks:1.12.19-GTNH:dev")
compileOnly("com.github.GTNewHorizons:Chisel:2.17.31-GTNH:dev")
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.6-GTNH:dev")
@@ -307,6 +307,11 @@ public class DisplayListManager {
}
}
public static void recordSecondaryColor(float r, float g, float b) {
drawBarrier();
currentRecorder.writeSecondaryColor(r, g, b);
}
public static void recordColorMask(boolean r, boolean g, boolean b, boolean a) {
drawBarrier();
currentRecorder.writeColorMask(r, g, b, a);
@@ -99,6 +99,7 @@ public class Feature {
));
attribToFeatures.put(GL11.GL_CURRENT_BIT, ImmutableSet.of(
GLStateManager.getColor()
, GLStateManager.getSecondaryColor() // Current secondary color
, ShaderManager.getNormalStack() // Current normal vector
, ShaderManager.getTexCoordStack() // Current texture coordinates
// Current color index
@@ -122,6 +123,7 @@ public class Feature {
, GLStateManager.getBlendMode()
, GLStateManager.getColorLogicOpState()
, GLStateManager.getColorMaterial()
, GLStateManager.getColorSumState()
, GLStateManager.getCullState()
, GLStateManager.getDepthTest()
, GLStateManager.getDitherState()
@@ -197,6 +199,7 @@ public class Feature {
));
attribToFeatures.put(GL11.GL_FOG_BIT, ImmutableSet.of(
GLStateManager.getFogMode()
, GLStateManager.getColorSumState()
, GLStateManager.getFogState()
// ^^ Fog density
// ^^ Linear fog start
@@ -18,6 +18,7 @@ import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
@@ -30,6 +31,7 @@ import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
@@ -103,6 +105,9 @@ class GLContextState {
public final FogStateStack fogState = new FogStateStack();
public final BooleanStateStack fogMode = track(new BooleanStateStack(GL11.GL_FOG, false, true));
public final Color4Stack color = new Color4Stack();
// GL_CURRENT_SECONDARY_COLOR - alpha is always 1 and unused, the color sum only adds RGB
public final Color4Stack secondaryColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 1.0F));
public final BooleanStateStack colorSumState = track(new BooleanStateStack(GL14.GL_COLOR_SUM, false, true));
public final Color4Stack clearColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 0.0F));
public final ColorMaskStack colorMask = new ColorMaskStack();
public final IntegerStateStack drawBuffer = new IntegerStateStack(GLStateManager.DEFAULT_DRAW_BUFFER);
@@ -180,7 +185,10 @@ class GLContextState {
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
public int clientAttribStackPointer = 0;
public final boolean[] restoreUnitChanged = new boolean[GLStateManager.MAX_TEXTURE_UNITS];
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged;
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged, restoreSecondaryColorChanged;
public final BlendState vanillaBlendBefore = new BlendState();
public final BlendState vanillaBlendAfter = new BlendState();
public boolean vanillaBlendEnabledBefore;
public int drawFramebuffer = 0;
public int readFramebuffer = 0;
public int texGenGeneration;
@@ -45,6 +45,8 @@ import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
@@ -502,6 +504,7 @@ public class GLStateManager {
.addFeature(GL11.GL_COLOR_WRITEMASK)
.addFeature(GL11.GL_CURRENT_COLOR)
.addFeature(GL11.GL_CURRENT_NORMAL)
.addFeature(GL14.GL_CURRENT_SECONDARY_COLOR)
.addFeature(GL11.GL_CURRENT_RASTER_COLOR)
.addFeature(GL11.GL_CURRENT_RASTER_POSITION)
.addFeature(GL11.GL_CURRENT_RASTER_TEXTURE_COORDS)
@@ -657,6 +660,7 @@ public class GLStateManager {
case GL11.GL_BLEND -> enableBlend();
case GL11.GL_COLOR_MATERIAL -> enableColorMaterial();
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.enable();
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.enable();
case GL11.GL_CULL_FACE -> enableCull();
case GL11.GL_DEPTH_TEST -> enableDepthTest();
case GL11.GL_DITHER -> glCtx.ditherState.enable();
@@ -738,6 +742,7 @@ public class GLStateManager {
case GL11.GL_BLEND -> disableBlend();
case GL11.GL_COLOR_MATERIAL -> disableColorMaterial();
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.disable();
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.disable();
case GL11.GL_CULL_FACE -> disableCull();
case GL11.GL_DEPTH_TEST -> disableDepthTest();
case GL11.GL_DITHER -> glCtx.ditherState.disable();
@@ -813,6 +818,7 @@ public class GLStateManager {
case GL11.GL_BLEND -> glCtx.blendMode.isEnabled();
case GL11.GL_COLOR_MATERIAL -> glCtx.colorMaterial.isEnabled();
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.isEnabled();
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.isEnabled();
case GL11.GL_CULL_FACE -> glCtx.cullState.isEnabled();
case GL11.GL_DEPTH_TEST -> glCtx.depthTest.isEnabled();
case GL11.GL_DITHER -> glCtx.ditherState.isEnabled();
@@ -888,6 +894,7 @@ public class GLStateManager {
case GL11.GL_BLEND -> glCtx.blendMode.isEnabled();
case GL11.GL_COLOR_MATERIAL -> glCtx.colorMaterial.isEnabled();
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.isEnabled();
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.isEnabled();
case GL11.GL_CULL_FACE -> glCtx.cullState.isEnabled();
case GL11.GL_DEPTH_TEST -> glCtx.depthTest.isEnabled();
case GL11.GL_DEPTH_WRITEMASK -> glCtx.depthState.isEnabled();
@@ -1131,6 +1138,7 @@ public class GLStateManager {
case GL11.GL_TEXTURE_MATRIX -> glCtx.textures.getTextureUnitMatrix(getActiveTextureUnit()).get(0, params);
case GL11.GL_COLOR_CLEAR_VALUE -> glCtx.clearColor.get(params);
case GL11.GL_CURRENT_COLOR -> glCtx.color.get(params);
case GL14.GL_CURRENT_SECONDARY_COLOR -> glCtx.secondaryColor.get(params);
case GL11.GL_DEPTH_RANGE -> {
final int pos = params.position();
params.put(pos, (float) glCtx.viewportState.depthRangeNear);
@@ -1229,7 +1237,7 @@ public class GLStateManager {
}
case GL11.GL_MODELVIEW_MATRIX, GL11.GL_PROJECTION_MATRIX, GL11.GL_TEXTURE_MATRIX -> widenFromFloat(pname, params, 16);
case GL11.GL_COLOR_CLEAR_VALUE, GL11.GL_CURRENT_COLOR, GL11.GL_CURRENT_TEXTURE_COORDS, GL11.GL_FOG_COLOR,
GL11.GL_LIGHT_MODEL_AMBIENT, GL14.GL_BLEND_COLOR -> widenFromFloat(pname, params, 4);
GL11.GL_LIGHT_MODEL_AMBIENT, GL14.GL_BLEND_COLOR, GL14.GL_CURRENT_SECONDARY_COLOR -> widenFromFloat(pname, params, 4);
case GL11.GL_CURRENT_NORMAL -> widenFromFloat(pname, params, 3);
default -> {
if (!HAS_MULTIPLE_SET.contains(pname)) {
@@ -1302,12 +1310,16 @@ public class GLStateManager {
return;
}
}
final BooleanStateStack blend = ctx().blendMode;
final boolean wasEnabled = blend.isEffectivelyEnabled();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
blend.beforeModify();
bh.deferBlendModeToggle(true);
return;
} else {
blend.enable();
}
ctx().blendMode.enable();
if (!wasEnabled && blend.isEffectivelyEnabled()) postVanillaBlendChange();
}
public static void disableBlend() {
@@ -1318,12 +1330,80 @@ public class GLStateManager {
return;
}
}
final BooleanStateStack blend = ctx().blendMode;
final boolean wasEnabled = blend.isEffectivelyEnabled();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
blend.beforeModify();
bh.deferBlendModeToggle(false);
return;
} else {
blend.disable();
}
ctx().blendMode.disable();
if (wasEnabled && !blend.isEffectivelyEnabled()) postVanillaBlendChange();
}
private static void postVanillaBlendChange() {
if (GLSMHooks.VANILLA_BLEND_CHANGE.hasListeners()) {
GLSMHooks.VANILLA_BLEND_CHANGE.post(GLSMHooks.vanillaBlendChangeEvent);
}
}
private static boolean snapshotVanillaBlendFunc() {
if (!GLSMHooks.VANILLA_BLEND_CHANGE.hasListeners()) return false;
final GLContextState glCtx = ctx();
glCtx.blendState.readEffective(glCtx.vanillaBlendBefore);
glCtx.vanillaBlendEnabledBefore = glCtx.blendMode.isEffectivelyEnabled();
return true;
}
private static void postVanillaBlendChangeIfMoved(boolean snapshotted) {
if (!snapshotted) return;
final GLContextState glCtx = ctx();
final BlendState before = glCtx.vanillaBlendBefore;
final BlendState after = glCtx.blendState.readEffective(glCtx.vanillaBlendAfter);
if (glCtx.blendMode.isEffectivelyEnabled() != glCtx.vanillaBlendEnabledBefore
|| after.getSrcRgb() != before.getSrcRgb()
|| after.getDstRgb() != before.getDstRgb()
|| after.getSrcAlpha() != before.getSrcAlpha()
|| after.getDstAlpha() != before.getDstAlpha()) {
postVanillaBlendChange();
}
}
public static boolean isEffectiveBlendEnabled() {
return ctx().blendMode.isEffectivelyEnabled();
}
public static BlendState getEffectiveBlendState(BlendState out) {
return ctx().blendState.readEffective(out);
}
public static boolean isEffectiveDepthMaskEnabled() {
return ctx().depthState.isEffectiveMaskEnabled();
}
public static boolean isEffectiveAlphaTestEnabled() {
return ctx().alphaTest.isEffectivelyEnabled();
}
public static AlphaState getEffectiveAlphaState(AlphaState out) {
return ctx().alphaState.readEffective(out);
}
private static int foreignDrawDepth;
public static void beginForeignDraw() {
foreignDrawDepth++;
}
public static void endForeignDraw() {
if (foreignDrawDepth > 0 && --foreignDrawDepth == 0 && GLSMHooks.FOREIGN_DRAW_END.hasListeners()) {
GLSMHooks.FOREIGN_DRAW_END.post(GLSMHooks.foreignDrawEndEvent);
}
}
public static boolean isForeignDraw() {
return foreignDrawDepth > 0;
}
public static void enableScissorTest() {
@@ -1357,9 +1437,11 @@ public class GLStateManager {
return;
}
}
final boolean snapshotted = snapshotVanillaBlendFunc();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
bh.deferBlendFunc(srcFactor, dstFactor, srcFactor, dstFactor);
postVanillaBlendChangeIfMoved(snapshotted);
return;
}
final boolean caching = isCachingEnabled();
@@ -1373,6 +1455,7 @@ public class GLStateManager {
GLSMHooks.blendFuncChangeEvent.dstAlpha = GL11.GL_ONE_MINUS_SRC_ALPHA;
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
postVanillaBlendChangeIfMoved(snapshotted);
return;
}
final boolean bypass = !caching;
@@ -1391,6 +1474,7 @@ public class GLStateManager {
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
}
postVanillaBlendChangeIfMoved(snapshotted);
}
public static void glBlendEquation(int mode) {
@@ -1430,9 +1514,11 @@ public class GLStateManager {
return;
}
}
final boolean snapshotted = snapshotVanillaBlendFunc();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
bh.deferBlendFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
postVanillaBlendChangeIfMoved(snapshotted);
return;
}
if (GLSMConfig.hudCacheOverride && dstAlpha != GL11.GL_ONE_MINUS_SRC_ALPHA) {
@@ -1452,6 +1538,7 @@ public class GLStateManager {
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
}
postVanillaBlendChangeIfMoved(snapshotted);
}
public static void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
@@ -1673,6 +1760,40 @@ public class GLStateManager {
return false;
}
public static void glSecondaryColor3f(float red, float green, float blue) {
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordSecondaryColor(red, green, blue);
if (mode == RecordMode.COMPILE) {
return;
}
}
changeSecondaryColor(red, green, blue);
}
public static void glSecondaryColor3d(double red, double green, double blue) {
glSecondaryColor3f((float) red, (float) green, (float) blue);
}
public static void glSecondaryColor3b(byte red, byte green, byte blue) {
glSecondaryColor3f(b2f(red), b2f(green), b2f(blue));
}
public static void glSecondaryColor3ub(byte red, byte green, byte blue) {
glSecondaryColor3f(ub2f(red), ub2f(green), ub2f(blue));
}
/** Helper for glSecondaryColor* - the color sum is emulated by the FFP shaders, nothing is sent to the driver. */
private static void changeSecondaryColor(float red, float green, float blue) {
final GLContextState glCtx = ctx();
if (!isCachingEnabled() || red != glCtx.secondaryColor.getRed() || green != glCtx.secondaryColor.getGreen() || blue != glCtx.secondaryColor.getBlue()) {
glCtx.secondaryColor.setRed(red);
glCtx.secondaryColor.setGreen(green);
glCtx.secondaryColor.setBlue(blue);
glCtx.fragmentGeneration++;
}
}
private static final Color4 DirtyColor = new Color4(-1.0F, -1.0F, -1.0F, -1.0F);
public static void clearCurrentColor() {
@@ -1746,6 +1867,7 @@ public class GLStateManager {
}
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
if (ah != null && ah.isAlphaTestLocked()) {
glCtx.alphaTest.beforeModify();
ah.deferAlphaTestToggle(true);
return;
}
@@ -1767,6 +1889,7 @@ public class GLStateManager {
}
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
if (ah != null && ah.isAlphaTestLocked()) {
glCtx.alphaTest.beforeModify();
ah.deferAlphaTestToggle(false);
return;
}
@@ -3532,6 +3655,9 @@ public class GLStateManager {
final int mask = glCtx.attribs.popInt();
glCtx.attribDepth--;
// Snapshot before the stacks pop, so applyRestoredState can tell whether vanilla blend actually moved.
final boolean blendSnapshotted = (mask & (GL11.GL_COLOR_BUFFER_BIT | GL11.GL_ENABLE_BIT)) != 0 && snapshotVanillaBlendFunc();
// First: restore BooleanStateStack states that were actually modified (fast path)
// These use lazy copy-on-write with global depth tracking
final List<IStateStack<?>> modified = glCtx.modifiedAtDepth[glCtx.attribDepth];
@@ -3550,7 +3676,12 @@ public class GLStateManager {
// Third: apply restored state to the GL driver. BooleanStateStacks already issue GL calls via setEnabled(); non-boolean stacks are pure data
// containers, so we must explicitly drive GL here.
applyRestoredState(mask);
applyRestoredState(mask, blendSnapshotted);
}
private static boolean isDepthColorOverridden() {
final DeferredDepthColorHandler dch = GLSMHooks.depthColorHandler;
return dch != null && dch.isOverrideHeld();
}
private static void captureRestoreChanges(int mask) {
@@ -3565,6 +3696,9 @@ public class GLStateManager {
glCtx.restoreDrawBufferChanged = glCtx.drawBuffer.topChanged();
glCtx.restoreLogicOpChanged = glCtx.logicOpMode.topChanged();
}
if ((mask & GL11.GL_CURRENT_BIT) != 0) {
glCtx.restoreSecondaryColorChanged = glCtx.secondaryColor.topChanged();
}
if ((mask & GL11.GL_STENCIL_BUFFER_BIT) != 0) {
glCtx.restoreStencilChanged = glCtx.stencilState.topChanged();
}
@@ -3639,20 +3773,25 @@ public class GLStateManager {
/**
* After popping GLSM stacks, apply restored state to the GL driver.
*/
private static void applyRestoredState(int mask) {
private static void applyRestoredState(int mask, boolean blendSnapshotted) {
final GLContextState glCtx = ctx();
if ((mask & GL11.GL_DEPTH_BUFFER_BIT) != 0 && glCtx.restoreDepthChanged) {
RENDER_BACKEND.depthFunc(glCtx.depthState.getFunc());
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
if (!isDepthColorOverridden()) {
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
}
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
}
if ((mask & GL11.GL_COLOR_BUFFER_BIT) != 0) {
if (glCtx.restoreBlendChanged) {
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
final DeferredBlendHandler restoreBlendHandler = GLSMHooks.blendHandler;
if (restoreBlendHandler == null || !restoreBlendHandler.isOverrideHeld()) {
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
}
RENDER_BACKEND.blendEquationSeparate(glCtx.blendState.getEquationRgb(), glCtx.blendState.getEquationAlpha());
RENDER_BACKEND.blendColor(glCtx.blendState.getBlendColorR(), glCtx.blendState.getBlendColorG(), glCtx.blendState.getBlendColorB(), glCtx.blendState.getBlendColorA());
}
if (glCtx.restoreColorMaskChanged) {
if (glCtx.restoreColorMaskChanged && !isDepthColorOverridden()) {
RENDER_BACKEND.colorMask(glCtx.colorMask.red, glCtx.colorMask.green, glCtx.colorMask.blue, glCtx.colorMask.alpha);
}
if (glCtx.restoreClearColorChanged) {
@@ -3736,7 +3875,11 @@ public class GLStateManager {
ShaderManager.bumpTexCoordGeneration();
glCtx.dirtyTexCoordAttrib = true;
}
// The secondary color is a fragment stage uniform, not a vertex attribute
if (glCtx.restoreSecondaryColorChanged) glCtx.fragmentGeneration++;
}
postVanillaBlendChangeIfMoved(blendSnapshotted);
}
public static void glClear(int mask) {
@@ -4925,16 +5068,22 @@ public class GLStateManager {
final ShaderManager ffp = ShaderManager.getInstance();
if (program == 0 && ffp.isEnabled()) {
final int prev = glCtx.activeProgram;
final boolean caching = isCachingEnabled();
if (prev != 0) glCtx.programGeneration++;
final boolean changed = prev != 0;
if (changed) glCtx.programGeneration++;
glCtx.activeProgram = 0; // Track that FFP was requested
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
if (changed && GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
GLSMHooks.programChangeEvent.previousProgram = prev;
GLSMHooks.programChangeEvent.newProgram = 0;
GLSMHooks.programChangeEvent.postBind = false;
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
}
ffp.activate();
if (changed && GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
GLSMHooks.programChangeEvent.previousProgram = prev;
GLSMHooks.programChangeEvent.newProgram = 0;
GLSMHooks.programChangeEvent.postBind = true;
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
}
return;
}
@@ -7307,6 +7456,14 @@ public class GLStateManager {
return ctx().color;
}
public static Color4Stack getSecondaryColor() {
return ctx().secondaryColor;
}
public static BooleanStateStack getColorSumState() {
return ctx().colorSumState;
}
public static Color4Stack getClearColor() {
return ctx().clearColor;
}
@@ -103,6 +103,7 @@ public final class FFPUniformBlock {
public static final int TEX_ENV_COLOR_2 = vec4("u_TexEnvColor2");
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 FOG_PARAMS = vec4("u_FogParams");
public static final int FOG_COLOR = vec4("u_FogColor");
@@ -12,7 +12,7 @@ import org.lwjgl.opengl.GL13;
* Packed-long fragment shader permutation key for FFP emulation.
*
* Layout:
* long[0]: global bits (12) + unit 0 (47 bits at offset 12)
* long[0]: global bits (13) + unit 0 (47 bits at offset 13)
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
* Only max(1, nrEnabledUnits) longs are significant.
*
@@ -63,7 +63,7 @@ public final class FragmentKey {
public static final int FOG_EXP = 2;
public static final int FOG_EXP2 = 3;
private static final int GLOBAL_BITS = 12;
private static final int GLOBAL_BITS = 13;
private static final int BIT_FOG_MODE = 0; // 2 bits
private static final int BIT_ALPHA_TEST = 2; // 1 bit
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
@@ -71,6 +71,7 @@ public final class FragmentKey {
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
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
private static final int U_ENABLED = 0;
private static final int U_MODE = 1; // 3 bits
@@ -130,10 +131,12 @@ public final class FragmentKey {
global |= (1L << BIT_LINE_STIPPLE);
}
// Separate specular
// Separate specular / color sum
if (GLStateManager.getLightingState().isEnabled()
&& GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
global |= (1L << BIT_SEPARATE_SPECULAR);
} else if (GLStateManager.getColorSumState().isEnabled()) {
global |= (1L << BIT_COLOR_SUM);
}
// Per-unit state
@@ -238,6 +241,7 @@ public final class FragmentKey {
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 boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
@@ -374,10 +378,10 @@ public final class FragmentKey {
default -> "?";
};
final StringBuilder sb = new StringBuilder();
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b overlay=%b units=%d",
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(), overlayEnabled(), nrEnabledUnits()));
separateSpecular(), colorSum(), overlayEnabled(), nrEnabledUnits()));
for (int i = 0; i < nrEnabledUnits(); i++) {
if (!unitEnabled(i)) {
sb.append(String.format(" u%d=OFF", i));
@@ -276,6 +276,10 @@ public final class FragmentShaderGenerator {
sb.append(" // Add separate specular\n");
sb.append(" color.rgb += v_SpecularColor;\n");
}
if (key.colorSum()) {
sb.append(" // Color sum - GL_COLOR_SUM with the current secondary color\n");
sb.append(" color.rgb += u_SecondaryColor;\n");
}
}
private static void emitAlphaTest(StringBuilder sb, FragmentKey key) {
@@ -331,6 +331,9 @@ public class Uniforms {
GLStateManager.getOverlayR(), GLStateManager.getOverlayG(),
GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
final var secondary = GLStateManager.getSecondaryColor();
putVec3(FFPUniformBlock.SECONDARY_COLOR, secondary.getRed(), secondary.getGreen(), secondary.getBlue());
// Mesa STATE_FOG_PARAMS_OPTIMIZED
final FogState fog = GLStateManager.getFogState();
final float start = fog.getStart();
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.hooks;
public interface DeferredBlendHandler {
boolean isBlendLocked();
boolean isOverrideHeld();
void deferBlendModeToggle(boolean enabled);
void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha);
void flushDeferredBlend();
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.hooks;
public interface DeferredDepthColorHandler {
boolean isDepthColorLocked();
boolean isOverrideHeld();
void deferDepthEnable(boolean enabled);
void deferColorMask(boolean r, boolean g, boolean b, boolean a);
}
@@ -6,6 +6,14 @@ public final class GLSMConfig {
public static float lastBrightnessX;
public static float lastBrightnessY;
public static int packBrightness(float x, float y) {
return ((int) y << 16) | ((int) x & 0xFFFF);
}
public static int packedLastBrightness() {
return packBrightness(lastBrightnessX, lastBrightnessY);
}
public static boolean hudCacheOverride;
public static volatile boolean expandVertexFormats;
@@ -25,6 +25,8 @@ public final class GLSMHooks {
public static final EventBus<LightmapCoordsEvent> LIGHTMAP_COORDS = EventBus.create(LightmapCoordsEvent.class);
public static final EventBus<AlphaStateChangeEvent> ALPHA_STATE_CHANGE = EventBus.create(AlphaStateChangeEvent.class);
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
public static final EventBus<ForeignDrawEndEvent> FOREIGN_DRAW_END = EventBus.create(ForeignDrawEndEvent.class);
public static final EventBus<VanillaBlendChangeEvent> VANILLA_BLEND_CHANGE = EventBus.create(VanillaBlendChangeEvent.class);
public static final EventBus<LoadingCheckpointEvent> LOADING_CHECKPOINT = EventBus.create(LoadingCheckpointEvent.class);
// Reusable event instances
@@ -38,6 +40,8 @@ public final class GLSMHooks {
public static final LightmapCoordsEvent lightmapCoordsEvent = new LightmapCoordsEvent();
public static final AlphaStateChangeEvent alphaStateChangeEvent = new AlphaStateChangeEvent();
public static final ShaderColorChangeEvent shaderColorChangeEvent = new ShaderColorChangeEvent();
public static final ForeignDrawEndEvent foreignDrawEndEvent = new ForeignDrawEndEvent();
public static final VanillaBlendChangeEvent vanillaBlendChangeEvent = new VanillaBlendChangeEvent();
public static final LoadingCheckpointEvent loadingCheckpointEvent = new LoadingCheckpointEvent();
private GLSMHooks() {}
@@ -0,0 +1,8 @@
package com.gtnewhorizons.angelica.glsm.hooks;
public interface VanillaBooleanLayer {
boolean isOverrideHeld();
boolean getVanilla();
void setVanilla(boolean enabled);
}
@@ -0,0 +1,22 @@
package com.gtnewhorizons.angelica.glsm.hooks;
public interface VanillaStateLayer<T> {
boolean isOverrideHeld();
void readVanilla(T into);
void writeVanilla(T from);
static boolean isHeld(VanillaStateLayer<?> layer) {
return layer != null && layer.isOverrideHeld();
}
static <T> void capture(VanillaStateLayer<T> layer, T slot) {
if (isHeld(layer)) layer.readVanilla(slot);
}
static <T> boolean restore(VanillaStateLayer<T> layer, T saved) {
if (!isHeld(layer)) return false;
layer.writeVanilla(saved);
return true;
}
}
@@ -0,0 +1,6 @@
package com.gtnewhorizons.angelica.glsm.hooks.events;
import net.minecraftforge.eventbus.api.event.MutableEvent;
public final class ForeignDrawEndEvent extends MutableEvent {
}
@@ -0,0 +1,6 @@
package com.gtnewhorizons.angelica.glsm.hooks.events;
import net.minecraftforge.eventbus.api.event.MutableEvent;
public final class VanillaBlendChangeEvent extends MutableEvent {
}
@@ -274,6 +274,13 @@ public final class CommandBufferExecutor {
ptr += 16;
GLStateManager.glColor4f(r, g, b, a);
}
case GLCommand.SECONDARY_COLOR -> {
final float r = memGetFloat(ptr);
final float g = memGetFloat(ptr + 4);
final float b = memGetFloat(ptr + 8);
ptr += 12;
GLStateManager.glSecondaryColor3f(r, g, b);
}
case GLCommand.CLEAR_COLOR -> {
final float r = memGetFloat(ptr);
final float g = memGetFloat(ptr + 4);
@@ -356,6 +356,14 @@ public final class CommandRecorder {
writeFloat(a);
}
public void writeSecondaryColor(float r, float g, float b) {
ensureCapacity(16);
writeInt(GLCommand.SECONDARY_COLOR);
writeFloat(r);
writeFloat(g);
writeFloat(b);
}
public void writeClearColor(float r, float g, float b, float a) {
ensureCapacity(20);
writeInt(GLCommand.CLEAR_COLOR);
@@ -61,6 +61,7 @@ public final class GLCommand {
public static final int COLOR = 54; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
public static final int CLEAR_COLOR = 55; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
public static final int BLEND_COLOR = 56; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
public static final int SECONDARY_COLOR = 57; // [cmd:4][r:4f][g:4f][b:4f]
// === Mixed int+float commands ===
public static final int ALPHA_FUNC = 60; // [cmd:4][func:4][ref:4f]
@@ -155,6 +156,7 @@ public final class GLCommand {
case COLOR -> "COLOR";
case CLEAR_COLOR -> "CLEAR_COLOR";
case BLEND_COLOR -> "BLEND_COLOR";
case SECONDARY_COLOR -> "SECONDARY_COLOR";
case ALPHA_FUNC -> "ALPHA_FUNC";
case FOGF -> "FOGF";
case LIGHTF -> "LIGHTF";
@@ -211,7 +213,7 @@ public final class GLCommand {
// Three int commands (16 bytes)
case GLCommand.STENCIL_FUNC, GLCommand.STENCIL_OP, GLCommand.TEX_PARAMETERI,
GLCommand.LIGHTF, GLCommand.LIGHTI,
GLCommand.MATERIALF, GLCommand.TEX_PARAMETERF -> 16;
GLCommand.MATERIALF, GLCommand.TEX_PARAMETERF, GLCommand.SECONDARY_COLOR -> 16;
// Four int commands (20 bytes)
case GLCommand.VIEWPORT, GLCommand.BLEND_FUNC, GLCommand.COLOR_MASK,
@@ -142,6 +142,7 @@ public class GLSMRedirector {
private static final String GLU = "org/lwjgl/util/glu/GLU";
private static final String OpenGlHelper = "net/minecraft/client/renderer/OpenGlHelper";
private static final String EXTBlendFunc = "org/lwjgl/opengl/EXTBlendFuncSeparate";
private static final String EXTSecondaryColor = "org/lwjgl/opengl/EXTSecondaryColor";
private static final String ARBMultiTexture = "org/lwjgl/opengl/ARBMultitexture";
private static final String ARBShaderObjects = "org/lwjgl/opengl/ARBShaderObjects";
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
@@ -403,7 +404,12 @@ public class GLSMRedirector {
.add("glBlendEquation")
.add("glMultiDrawArrays")
.add("glPointParameterf")
.add("glPointParameteri");
.add("glPointParameteri")
// Removed in core profile - emulated by the FFP shaders, calling through would abort the JVM
.add("glSecondaryColor3f")
.add("glSecondaryColor3d")
.add("glSecondaryColor3b")
.add("glSecondaryColor3ub");
final var gl15 = RedirectMap.newMap()
.add("glGenBuffers")
.add("glBindBuffer")
@@ -589,6 +595,11 @@ public class GLSMRedirector {
.add("isFramebufferEnabled")
);
methodRedirects.put(EXTBlendFunc, RedirectMap.newMap().add("glBlendFuncSeparateEXT", "tryBlendFuncSeparate"));
methodRedirects.put(EXTSecondaryColor, RedirectMap.newMap()
.add("glSecondaryColor3bEXT", "glSecondaryColor3b")
.add("glSecondaryColor3fEXT", "glSecondaryColor3f")
.add("glSecondaryColor3dEXT", "glSecondaryColor3d")
.add("glSecondaryColor3ubEXT", "glSecondaryColor3ub"));
// ARB
methodRedirects.put(ARBMultiTexture, RedirectMap.newMap()
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
import lombok.Setter;
public class AlphaStateStack extends AlphaState implements IStateStack<AlphaState> {
@@ -10,6 +12,8 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
protected int pointer;
@Setter private VanillaStateLayer<AlphaState> vanillaLayer;
public AlphaStateStack() {
stack = new AlphaState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
@@ -22,7 +26,7 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
}
stack[pointer++].set(this);
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
return this;
}
@@ -31,10 +35,19 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
throw new IllegalStateException("Stack underflow");
}
set(stack[--pointer]);
final AlphaState saved = stack[--pointer];
if (!VanillaStateLayer.restore(vanillaLayer, saved)) {
set(saved);
}
return this;
}
public AlphaState readEffective(AlphaState out) {
out.set(this);
VanillaStateLayer.capture(vanillaLayer, out);
return out;
}
public boolean isEmpty() {
return pointer == 0;
}
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import lombok.Setter;
public class BlendStateStack extends BlendState implements IStateStack<BlendStateStack> {
@@ -10,6 +12,8 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
protected int pointer;
@Setter private VanillaStateLayer<BlendState> vanillaLayer;
public BlendStateStack() {
stack = new BlendState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
@@ -22,7 +26,7 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
}
stack[pointer++].set(this);
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
return this;
}
@@ -31,10 +35,21 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
throw new IllegalStateException("Stack underflow");
}
set(stack[--pointer]);
final BlendState saved = stack[--pointer];
if (VanillaStateLayer.restore(vanillaLayer, saved)) {
setExceptFunc(saved);
} else {
set(saved);
}
return this;
}
public BlendState readEffective(BlendState out) {
out.set(this);
VanillaStateLayer.capture(vanillaLayer, out);
return out;
}
public boolean isEmpty() {
return pointer == 0;
}
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
import com.gtnewhorizons.angelica.glsm.states.BooleanState;
import lombok.Setter;
/**
* A stack for boolean GL state with lazy copy-on-write optimization.
@@ -14,6 +16,7 @@ import com.gtnewhorizons.angelica.glsm.states.BooleanState;
public class BooleanStateStack extends BooleanState implements IStateStack<BooleanStateStack> {
protected final boolean[] stack;
@Setter private VanillaBooleanLayer vanillaLayer;
/**
* The depth at which state has been saved. Compared against GLStateManager.getAttribDepth()
@@ -56,7 +59,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
if (savedDepth >= stack.length) {
throw new IllegalStateException("Stack overflow size " + (savedDepth + 1) + " reached");
}
stack[savedDepth++] = enabled;
stack[savedDepth++] = vanillaValue();
return this;
}
@@ -65,12 +68,26 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
if (savedDepth == 0) {
throw new IllegalStateException("Stack underflow");
}
final boolean oldValue = stack[--savedDepth];
// Call setEnabledDirect to avoid triggering beforeModify during restore
setEnabledDirect(oldValue);
restore(stack[--savedDepth]);
return this;
}
public boolean isEffectivelyEnabled() {
return (vanillaLayer != null && vanillaLayer.isOverrideHeld()) ? vanillaLayer.getVanilla() : enabled;
}
private boolean vanillaValue() {
return isEffectivelyEnabled();
}
private void restore(boolean value) {
if (vanillaLayer != null && vanillaLayer.isOverrideHeld()) {
vanillaLayer.setVanilla(value);
} else {
setEnabledDirect(value);
}
}
@Override
public boolean isEmpty() {
return savedDepth == 0;
@@ -92,8 +109,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
public BooleanStateStack popDepth() {
// We're only called if we were modified, so savedDepth should match
if (savedDepth > 0) {
final boolean oldValue = stack[--savedDepth];
setEnabledDirect(oldValue);
restore(stack[--savedDepth]);
}
return this;
}
@@ -106,7 +122,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
public void beforeModify() {
final int globalDepth = GLStateManager.getAttribDepth();
if (savedDepth < globalDepth) {
stack[savedDepth++] = enabled;
stack[savedDepth++] = vanillaValue();
GLStateManager.registerModifiedState(this);
}
}
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
import lombok.Setter;
public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskStack> {
@@ -10,6 +12,8 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
protected int pointer;
@Setter private VanillaStateLayer<ColorMask> vanillaLayer;
public ColorMaskStack() {
stack = new ColorMask[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
@@ -22,7 +26,7 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
}
stack[pointer++].set(this);
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
return this;
}
@@ -31,7 +35,10 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
throw new IllegalStateException("Stack underflow");
}
set(stack[--pointer]);
final ColorMask saved = stack[--pointer];
if (!VanillaStateLayer.restore(vanillaLayer, saved)) {
set(saved);
}
return this;
}
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.DepthState;
import lombok.Setter;
public class DepthStateStack extends DepthState implements IStateStack<DepthStateStack> {
@@ -10,6 +12,10 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
protected int pointer;
@Setter private VanillaStateLayer<DepthState> vanillaLayer;
private final DepthState effective = new DepthState();
public DepthStateStack() {
stack = new DepthState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
@@ -22,7 +28,7 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
}
stack[pointer++].set(this);
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
return this;
}
@@ -31,10 +37,21 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
throw new IllegalStateException("Stack underflow");
}
set(stack[--pointer]);
final DepthState saved = stack[--pointer];
if (VanillaStateLayer.restore(vanillaLayer, saved)) {
setExceptMask(saved);
} else {
set(saved);
}
return this;
}
public boolean isEffectiveMaskEnabled() {
if (!VanillaStateLayer.isHeld(vanillaLayer)) return isEnabled();
vanillaLayer.readVanilla(effective);
return effective.isEnabled();
}
public boolean isEmpty() {
return pointer == 0;
}
@@ -55,6 +55,15 @@ public class BlendState implements ISettableState<BlendState> {
return this;
}
public void setExceptFunc(BlendState state) {
final int sRgb = srcRgb, dRgb = dstRgb, sAlpha = srcAlpha, dAlpha = dstAlpha;
set(state);
this.srcRgb = sRgb;
this.dstRgb = dRgb;
this.srcAlpha = sAlpha;
this.dstAlpha = dAlpha;
}
@Override
public boolean sameAs(Object state) {
if (this == state) return true;
@@ -18,6 +18,12 @@ public class DepthState implements ISettableState<DepthState> {
return this;
}
public void setExceptMask(DepthState state) {
final boolean mask = enabled;
set(state);
this.enabled = mask;
}
@Override
public boolean sameAs(Object state) {
if (this == state) return true;
@@ -108,7 +108,7 @@ public class TessellatorStreamingDrawer {
final int defaultBrightness;
if (!tess.hasBrightness) {
defaultBrightness = ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
defaultBrightness = GLSMConfig.packedLastBrightness();
} else {
defaultBrightness = 0;
}
@@ -4,8 +4,11 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import java.nio.FloatBuffer;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -22,6 +25,7 @@ public class GLSM_CoreRemovedCaps_GLTest {
cap("GL_POLYGON_STIPPLE", GL11.GL_POLYGON_STIPPLE),
cap("GL_INDEX_LOGIC_OP", GL11.GL_INDEX_LOGIC_OP),
cap("GL_AUTO_NORMAL", GL11.GL_AUTO_NORMAL),
cap("GL_COLOR_SUM", GL14.GL_COLOR_SUM),
cap("GL_MAP1_COLOR_4", GL11.GL_MAP1_COLOR_4),
cap("GL_MAP1_INDEX", GL11.GL_MAP1_INDEX),
cap("GL_MAP1_NORMAL", GL11.GL_MAP1_NORMAL),
@@ -46,6 +50,14 @@ public class GLSM_CoreRemovedCaps_GLTest {
return Arguments.of(name, value);
}
private static void assertSecondaryColor(float red, float green, float blue) {
final FloatBuffer params = BufferUtils.createFloatBuffer(4);
GLStateManager.glGetFloat(GL14.GL_CURRENT_SECONDARY_COLOR, params);
assertEquals(red, params.get(0), "red must round-trip through the cache");
assertEquals(green, params.get(1), "green must round-trip through the cache");
assertEquals(blue, params.get(2), "blue must round-trip through the cache");
}
private static void assertNoGlError(String what) {
final int error = GL11.glGetError();
assertEquals(GL11.GL_NO_ERROR, error, () -> what + " raised 0x" + Integer.toHexString(error));
@@ -83,6 +95,71 @@ public class GLSM_CoreRemovedCaps_GLTest {
}
}
@Test
void secondaryColorIsCacheOnly() {
try {
// Calling through to the driver here aborts the JVM in a core profile - the color sum is emulated by the FFP shaders
GLStateManager.glSecondaryColor3f(0.25F, 0.5F, 0.75F);
assertNoGlError("glSecondaryColor3f");
assertSecondaryColor(0.25F, 0.5F, 0.75F);
} finally {
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@Test
void secondaryColorIsRestoredByPopAttrib() {
try {
GLStateManager.glSecondaryColor3f(0.125F, 0.25F, 0.375F);
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
GLStateManager.glSecondaryColor3f(1.0F, 1.0F, 1.0F);
GLStateManager.glPopAttrib();
assertNoGlError("glPushAttrib/glSecondaryColor3f/glPopAttrib");
assertSecondaryColor(0.125F, 0.25F, 0.375F);
} finally {
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@Test
void colorSumIsRestoredByFogBitPopAttrib() {
try {
GLStateManager.glPushAttrib(GL11.GL_FOG_BIT);
GLStateManager.glEnable(GL14.GL_COLOR_SUM);
GLStateManager.glPopAttrib();
assertNoGlError("glPushAttrib(GL_FOG_BIT)/glEnable(GL_COLOR_SUM)/glPopAttrib");
assertFalse(GLStateManager.glIsEnabled(GL14.GL_COLOR_SUM));
} finally {
GLStateManager.glDisable(GL14.GL_COLOR_SUM);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@Test
void secondaryColorReplaysFromDisplayList() {
final int list = GLStateManager.glGenLists(1);
try {
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glSecondaryColor3f(0.5F, 0.625F, 0.75F);
GLStateManager.glEndList();
assertNoGlError("glSecondaryColor3f display list compile");
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
GLStateManager.glCallList(list);
assertNoGlError("glSecondaryColor3f display list replay");
assertSecondaryColor(0.5F, 0.625F, 0.75F);
} finally {
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@Test
void displayListReplayIsCacheOnly() {
final int list = GLStateManager.glGenLists(1);
@@ -8,7 +8,9 @@ import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.ffp.FFPVertexLighting;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer;
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
@@ -17,9 +19,15 @@ import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Setter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gl.program.Program;
import net.coderbot.iris.gl.program.ProgramBuilder;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.pipeline.PipelineManager;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
@@ -41,6 +49,8 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack.stackPush;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetFloat;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutByte;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutShort;
@@ -87,6 +97,7 @@ public class BatchingFontRenderer {
private static class FontAAShader {
private static Program fontShader = null;
public static Program getProgram() {
if (fontShader == null) {
final String vsh = ShaderLoader.getShaderSource("angelica:fontFilter.vsh");
@@ -172,6 +183,9 @@ public class BatchingFontRenderer {
private int blendSrcRGB = GL11.GL_SRC_ALPHA;
private int blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
private final BlendState blendStateBefore = new BlendState();
private static final BlendState deferredBlendStateBefore = new BlendState();
private float lightmapU = 0.0f;
private float lightmapV = 0.0f;
private boolean lightmapActive = false;
@@ -190,10 +204,19 @@ public class BatchingFontRenderer {
return (argb & 0xFF000000) | (red << 16) | (green << 8) | blue;
}
/**
* True only for a perspective projection (world-space text: signs, nameplates, etc).
* GUI/HUD text uses an orthographic projection and must never be shaded by world lighting -
* mods rely on getting back the exact color they requested (e.g. GTNHLib's ColorResource).
*/
private static boolean isWorldSpaceText() {
return GLStateManager.getProjectionMatrix().m33() == 0.0f;
}
private void captureLightmapState() {
final int unitBinding = GLStateManager.getTextures().getTextureUnitBindings(LIGHTMAP_TEX_UNIT).getBinding();
final boolean active = GLStateManager.getProjectionMatrix().m33() == 0.0f && unitBinding != 0;
final boolean active = isWorldSpaceText() && unitBinding != 0;
float u = 0.0f;
float v = 0.0f;
@@ -217,6 +240,7 @@ public class BatchingFontRenderer {
private static final class TextSegment {
final Matrix4f mvp = new Matrix4f();
final Matrix4f modelView = new Matrix4f();
BatchingFontRenderer owner;
int cmdStart;
int cmdEnd;
@@ -224,6 +248,9 @@ public class BatchingFontRenderer {
float lightmapV;
boolean lightmapActive;
int lightmapTexture;
int packedLight;
float normalX, normalY, normalZ;
int blockEntityId;
}
// 16-bit EBO index range
@@ -235,7 +262,6 @@ public class BatchingFontRenderer {
private int deferredCmdWatermark;
private static final ObjectArrayList<TextSegment> deferredSegments = ObjectArrayList.wrap(new TextSegment[16], 0);
private static final ObjectArrayList<TextSegment> deferredSegmentPool = ObjectArrayList.wrap(new TextSegment[16], 0);
private boolean deferCurrentBatch;
private final Matrix4f batchProj = new Matrix4f();
private final Matrix4f batchModelView = new Matrix4f();
@@ -467,6 +493,10 @@ public class BatchingFontRenderer {
}
}
private void resolveModelView(Matrix4f dest) {
dest.set(batchMatrixValid ? batchModelView : GLStateManager.getModelViewMatrix());
}
private void sealBatchSegment() {
final int end = batchCommands.size();
if (end == batchSealedEnd) return;
@@ -475,10 +505,17 @@ public class BatchingFontRenderer {
segment.cmdStart = batchSealedEnd;
segment.cmdEnd = end;
resolveMvp(segment.mvp);
resolveModelView(segment.modelView);
segment.lightmapActive = lightmapActive;
segment.lightmapU = lightmapU;
segment.lightmapV = lightmapV;
segment.lightmapTexture = lightmapTextureId;
segment.packedLight = GLSMConfig.packedLastBrightness();
final Vector3f normal = ShaderManager.getCurrentNormal();
segment.normalX = normal.x;
segment.normalY = normal.y;
segment.normalZ = normal.z;
segment.blockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
batchSegments.add(segment);
batchSealedEnd = end;
}
@@ -499,27 +536,29 @@ public class BatchingFontRenderer {
}
batchDepth--;
if (batchDepth == 0) {
// We finished any nested batches
flushBatch();
// We finished any nested batches.
if (shouldDeferNow()) {
deferBatch();
} else {
flushBatch();
}
}
}
private static boolean shouldDeferNow() {
if (!GLStateManager.isMainThread()) return false;
return TesrBatchRenderer.INSTANCE.hasPendingGeometry() || ModelPartBatcher.INSTANCE.isActive();
}
public void beginDeferredBatch() {
deferCurrentBatch = batchDepth == 0 && TesrBatchRenderer.INSTANCE.hasActivePass() || ModelPartBatcher.INSTANCE.isActive();
if (deferCurrentBatch && deferredIdxPos / 6 > QUAD_BUDGET / 2) {
if (batchDepth == 0 && shouldDeferNow() && deferredIdxPos / 6 > QUAD_BUDGET / 2) {
flushDeferredText();
}
beginBatch();
}
public void endDeferredBatch() {
if (deferCurrentBatch && batchDepth == 1) {
deferCurrentBatch = false;
batchDepth = 0;
deferBatch();
} else {
endBatch();
}
endBatch();
}
private void deferBatch() {
@@ -544,38 +583,123 @@ public class BatchingFontRenderer {
public static void flushDeferredText() {
if (deferredSegments.isEmpty()) return;
if (deferredSegments.getFirst().owner.shouldDrawThroughPipeline()) {
flushDeferredThroughPipeline();
return;
}
GLStateManager.beginForeignDraw();
try {
flushDeferredTextInner();
} finally {
GLStateManager.endForeignDraw();
}
}
private static void flushDeferredThroughPipeline() {
final BatchingFontRenderer first = deferredSegments.getFirst().owner;
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
GLStateManager.getEffectiveBlendState(deferredBlendStateBefore);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
final int prevBlockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
boolean textureChanged = false;
GLStateManager.enableTexture();
GLStateManager.enableAlphaTest();
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(first.blendSrcRGB, first.blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.glDepthMask(false);
final Boolean prevTranslucency = GbufferPrograms.beginTranslucencyDeclaration(Boolean.TRUE);
GbufferPrograms.setBlockEntityDefaults();
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPushMatrix();
try {
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);
final FontDrawCmd[] cmdsData = owner.batchCommands.elements();
Arrays.sort(cmdsData, segment.cmdStart, segment.cmdEnd, FontDrawCmd.DRAW_ORDER_COMPARATOR);
textureChanged |= emitRangeThroughPipeline(owner, cmdsData, segment.cmdStart, segment.cmdEnd,
segment.packedLight, segment.normalX, segment.normalY, segment.normalZ);
}
} finally {
GLStateManager.glPopMatrix();
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
GbufferPrograms.endTranslucencyDeclaration(prevTranslucency);
GLStateManager.glDepthMask(depthMaskBefore);
if (isTextureEnabledBefore) {
GLStateManager.enableTexture();
} else {
GLStateManager.disableTexture();
}
if (isBlendEnabledBefore) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(deferredBlendStateBefore.getSrcRgb(), deferredBlendStateBefore.getDstRgb(),
deferredBlendStateBefore.getSrcAlpha(), deferredBlendStateBefore.getDstAlpha());
if (isAlphaTestEnabledBefore) {
GLStateManager.enableAlphaTest();
} else {
GLStateManager.disableAlphaTest();
}
if (textureChanged) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
}
discardDeferredText();
}
}
private static void flushDeferredTextInner() {
uploadVertexData(deferredVertexPos);
final int prevProgram = GLStateManager.glGetInteger(GL20.GL_CURRENT_PROGRAM);
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_BLEND);
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
GLStateManager.getEffectiveBlendState(deferredBlendStateBefore);
final int shadeModelBefore = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final int lightmapBindingBefore = boundLightmapTexture();
final boolean depthTestBefore = GLStateManager.getDepthTest().isEnabled();
final boolean depthMaskBefore = GLStateManager.getDepthState().isEnabled();
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
deferredSegments.get(0).owner.setupFontDrawState();
GLStateManager.glDepthMask(false);
flushLastTexture = DUMMY_RESOURCE_LOCATION;
flushTextureChanged = false;
resetFlushLightmap();
try (MemoryStack stack = stackPush()) {
final FloatBuffer mvpBuf = stack.mallocFloat(16);
for (int i = 0, n = deferredSegments.size(); i < n; i++) {
final TextSegment segment = deferredSegments.get(i);
mvpBuf.clear();
segment.mvp.get(mvpBuf);
GLStateManager.glUniformMatrix4(segment.owner.mvpMatrixLocation, false, mvpBuf);
uploadLightmap(segment.owner.lightmapLocation, segment.lightmapActive, segment.lightmapU,
segment.lightmapV, segment.lightmapTexture);
drawCommands(segment.owner.batchCommands.elements(), segment.cmdStart, segment.cmdEnd, segment.owner);
try {
try (MemoryStack stack = stackPush()) {
final FloatBuffer mvpBuf = stack.mallocFloat(16);
for (final TextSegment segment : deferredSegments) {
mvpBuf.clear();
segment.mvp.get(mvpBuf);
GLStateManager.glUniformMatrix4(segment.owner.mvpMatrixLocation, false, mvpBuf);
uploadLightmap(segment.owner.lightmapLocation, segment.lightmapActive, segment.lightmapU,
segment.lightmapV, segment.lightmapTexture);
drawCommands(segment.owner.batchCommands.elements(), segment.cmdStart, segment.cmdEnd, segment.owner);
}
}
} finally {
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore,
lightmapBindingBefore, isAlphaTestEnabledBefore, deferredBlendStateBefore, shadeModelBefore);
restoreDepth(depthTestBefore, depthMaskBefore);
discardDeferredText();
}
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore, lightmapBindingBefore);
restoreDepth(depthTestBefore, depthMaskBefore);
discardDeferredText();
}
public static void discardDeferredText() {
@@ -614,18 +738,47 @@ public class BatchingFontRenderer {
}
private static final Matrix4f scratchMvp = new Matrix4f();
private static final Matrix4f scratchModelView = new Matrix4f();
private int fontAAModeLast = -1;
private int fontAAStrengthLast = -1;
private void flushBatch() {
final boolean locked = GLStateManager.acquireDrawLock();
try {
flushBatchInner();
if (shouldDrawThroughPipeline()) {
flushBatchThroughPipeline();
} else {
GLStateManager.beginForeignDraw();
try {
flushBatchInner();
} finally {
GLStateManager.endForeignDraw();
}
}
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
}
private boolean shouldDrawThroughPipeline() {
if (FontConfig.fontAAMode != 0) {
return false;
}
if (!GLStateManager.isMainThread()) {
return false;
}
final PipelineManager pipelines = Iris.getPipelineManagerNullable();
if (pipelines == null) {
return false;
}
return !Tessellator.instance.isDrawing
&& pipelines.getPipelineNullable() instanceof DeferredWorldRenderingPipeline drp
&& drp.shouldOverrideShaders();
}
private void flushBatchInner() {
final int cmdCount = batchCommands.size();
final int segmentCount = batchSegments.size();
@@ -646,11 +799,14 @@ public class BatchingFontRenderer {
Arrays.sort(cmds, batchSealedEnd, cmdCount, FontDrawCmd.DRAW_ORDER_COMPARATOR);
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_BLEND);
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
GLStateManager.getEffectiveBlendState(blendStateBefore);
final int shadeModelBefore = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final int lightmapBindingBefore = boundLightmapTexture();
final boolean depthTestBefore = GLStateManager.getDepthTest().isEnabled();
final boolean depthMaskBefore = GLStateManager.getDepthState().isEnabled();
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
setupFontDrawState();
flushLastTexture = DUMMY_RESOURCE_LOCATION;
@@ -675,7 +831,8 @@ public class BatchingFontRenderer {
drawCommands(cmds, batchSealedEnd, cmdCount, this);
}
}
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore, lightmapBindingBefore);
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore,
lightmapBindingBefore, isAlphaTestEnabledBefore, blendStateBefore, shadeModelBefore);
restoreDepth(depthTestBefore, depthMaskBefore);
truncateBatchToWatermark();
@@ -783,7 +940,9 @@ public class BatchingFontRenderer {
}
}
private static void restoreFontDrawState(int prevProgram, boolean textureEnabledBefore, boolean blendEnabledBefore, int boundTextureBefore, int lightmapBindingBefore) {
private static void restoreFontDrawState(int prevProgram, boolean textureEnabledBefore, boolean blendEnabledBefore,
int boundTextureBefore, int lightmapBindingBefore, boolean alphaTestEnabledBefore, BlendState blendBefore,
int shadeModelBefore) {
GLStateManager.glUseProgram(prevProgram);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
@@ -792,9 +951,19 @@ public class BatchingFontRenderer {
if (textureEnabledBefore) {
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
}
if (!blendEnabledBefore) {
if (blendEnabledBefore) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(blendBefore.getSrcRgb(), blendBefore.getDstRgb(),
blendBefore.getSrcAlpha(), blendBefore.getDstAlpha());
if (alphaTestEnabledBefore) {
GLStateManager.enableAlphaTest();
} else {
GLStateManager.disableAlphaTest();
}
GLStateManager.glShadeModel(shadeModelBefore);
if (flushTextureChanged) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
}
@@ -803,11 +972,156 @@ public class BatchingFontRenderer {
}
}
private void flushBatchThroughPipeline() {
final int cmdCount = batchCommands.size();
final int segmentCount = batchSegments.size();
if (segmentCount == 0 && (vertexDataPos == deferredVertexPos || cmdCount == deferredCmdWatermark)) {
truncateBatchToWatermark();
return;
}
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
GLStateManager.getEffectiveBlendState(blendStateBefore);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final int prevBlockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
boolean textureChanged = false;
GLStateManager.enableTexture();
GLStateManager.enableAlphaTest();
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(blendSrcRGB, blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
final Boolean prevTranslucency = GbufferPrograms.beginTranslucencyDeclaration(Boolean.TRUE);
GbufferPrograms.setBlockEntityDefaults();
final FontDrawCmd[] cmdsData = batchCommands.elements();
for (int i = 0; i < segmentCount; i++) {
final TextSegment segment = batchSegments.get(i);
Arrays.sort(cmdsData, segment.cmdStart, segment.cmdEnd, FontDrawCmd.DRAW_ORDER_COMPARATOR);
}
Arrays.sort(cmdsData, batchSealedEnd, cmdCount, FontDrawCmd.DRAW_ORDER_COMPARATOR);
// The unsealed tail still belongs to the modelview live at flush time; capture it before the segments move it.
resolveModelView(scratchModelView);
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPushMatrix();
try {
for (int i = 0; i < segmentCount; i++) {
final TextSegment segment = batchSegments.get(i);
if (segment.cmdStart == segment.cmdEnd) continue;
GLStateManager.setModelViewMatrix(segment.modelView);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(segment.blockEntityId);
textureChanged |= 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);
final Vector3f normal = ShaderManager.getCurrentNormal();
textureChanged |= emitRangeThroughPipeline(this, cmdsData, batchSealedEnd, cmdCount,
GLSMConfig.packedLastBrightness(), normal.x, normal.y, normal.z);
}
} finally {
GLStateManager.glPopMatrix();
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
GbufferPrograms.endTranslucencyDeclaration(prevTranslucency);
if (isTextureEnabledBefore) {
GLStateManager.enableTexture();
} else {
GLStateManager.disableTexture();
}
if (isBlendEnabledBefore) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(blendStateBefore.getSrcRgb(), blendStateBefore.getDstRgb(),
blendStateBefore.getSrcAlpha(), blendStateBefore.getDstAlpha());
if (isAlphaTestEnabledBefore) {
GLStateManager.enableAlphaTest();
} else {
GLStateManager.disableAlphaTest();
}
if (textureChanged) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
}
truncateBatchToWatermark();
}
}
/** Replays one sealed command range through the Tessellator so Iris sees it as ordinary geometry. */
private static boolean emitRangeThroughPipeline(BatchingFontRenderer owner, FontDrawCmd[] cmdsData, int from, int to,
int packedLight, float normalX, float normalY, float normalZ) {
final Tessellator tessellator = Tessellator.instance;
ResourceLocation lastTexture = DUMMY_RESOURCE_LOCATION;
boolean textureChanged = false;
boolean drawing = false;
try {
for (int i = from; i < to; i++) {
final FontDrawCmd cmd = cmdsData[i];
if (!Objects.equals(lastTexture, cmd.texture)) {
if (drawing) {
tessellator.draw();
drawing = false;
}
if (cmd.texture == null) {
GLStateManager.disableTexture();
} else {
GLStateManager.enableTexture();
((FontRendererAccessor) owner.underlying).angelica$bindTexture(cmd.texture);
textureChanged = true;
}
lastTexture = cmd.texture;
}
if (!drawing) {
tessellator.startDrawingQuads();
tessellator.setBrightness(packedLight);
tessellator.setNormal(normalX, normalY, normalZ);
drawing = true;
}
owner.emitCommand(tessellator, cmd);
}
} finally {
if (drawing) {
tessellator.draw();
}
}
return textureChanged;
}
private void emitCommand(Tessellator tessellator, FontDrawCmd cmd) {
final int firstVertex = cmd.startVtx / 6 * 4;
final int quadCount = cmd.idxCount / 6;
for (int q = 0; q < quadCount; q++) {
final long base = vertexDataAddress + (long) (firstVertex + q * 4) * VERTEX_SIZE;
emitVertex(tessellator, base);
emitVertex(tessellator, base + VERTEX_SIZE);
emitVertex(tessellator, base + 3L * VERTEX_SIZE);
emitVertex(tessellator, base + 2L * VERTEX_SIZE);
}
}
private void emitVertex(Tessellator tessellator, long ptr) {
tessellator.setColorRGBA(
memGetByte(ptr + 16) & 0xFF,
memGetByte(ptr + 17) & 0xFF,
memGetByte(ptr + 18) & 0xFF,
memGetByte(ptr + 19) & 0xFF);
tessellator.addVertexWithUV(memGetFloat(ptr), memGetFloat(ptr + 4), 0.0D,
memGetFloat(ptr + 8), memGetFloat(ptr + 12));
}
private static void restoreDepth(boolean testBefore, boolean maskBefore) {
if (testBefore != GLStateManager.getDepthTest().isEnabled()) {
if (testBefore) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
}
if (maskBefore != GLStateManager.getDepthState().isEnabled()) {
if (maskBefore != GLStateManager.isEffectiveDepthMaskEnabled()) {
GLStateManager.glDepthMask(maskBefore);
}
}
@@ -865,7 +1179,7 @@ public class BatchingFontRenderer {
}
private static int hsvToRgb(float hue, float sat, float val) {
int h = (int)(hue / 60f) % 6;
int h = (int) (hue / 60f) % 6;
float f = hue / 60f - h;
float p = val * (1 - sat);
float q = val * (1 - f * sat);
@@ -879,11 +1193,12 @@ public class BatchingFontRenderer {
case 4: r=t; g=p; b=val; break;
default: r=val; g=p; b=q; break;
}
return ((int)(r*255) << 16) | ((int)(g*255) << 8) | (int)(b*255);
return ((int) (r * 255) << 16) | ((int) (g * 255) << 8) | (int) (b * 255);
}
private static final int RAINBOW_LUT_SIZE = 24;
private static final int[] RAINBOW_LUT = new int[RAINBOW_LUT_SIZE];
static {
for (int i = 0; i < RAINBOW_LUT_SIZE; i++) {
RAINBOW_LUT[i] = hsvToRgb(i * 15f, 1f, 1f);
@@ -918,7 +1233,7 @@ public class BatchingFontRenderer {
private static final float WAVE_FREQUENCY = 0.5f;
public float drawString(final float anchorX, final float anchorY, final int color, final boolean enableShadow,
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
// noinspection SizeReplaceableByIsEmpty
if (string == null || string.length() == 0) {
return anchorX + (enableShadow ? 1.0f : 0.0f);
@@ -930,7 +1245,7 @@ public class BatchingFontRenderer {
this.beginBatch();
this.captureLightmapState();
this.lightingFactorActive = FFPVertexLighting.modulatesVertexColor(this.lightingFactor);
this.lightingFactorActive = isWorldSpaceText() && FFPVertexLighting.modulatesVertexColor(this.lightingFactor);
float curX = anchorX;
try {
final int totalStringLength = string.length();
@@ -1007,86 +1322,86 @@ public class BatchingFontRenderer {
charIdx += SECTION_X_PAYLOAD;
}
} else {
final boolean is09 = charInRange(fmtCode, '0', '9');
final boolean isAF = charInRange(fmtCode, 'a', 'f');
if (is09 || isAF) {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curGradient = false;
// wave/dinnerbone NOT reset — they're positional effects, independent of color
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
final int rgb = this.colorCode[colorIdx];
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
final int shadowRgb = this.colorCode[colorIdx + 16];
curShadowColor = (curShadowColor & 0xFF000000) | (shadowRgb & 0x00FFFFFF);
} else if (fmtCode == 'k') {
curRandom = true;
} else if (fmtCode == 'l') {
curBold = true;
} else if (fmtCode == 'm') {
curStrikethrough = true;
strikethroughStartX = curX - 1.0f;
strikethroughEndX = strikethroughStartX;
} else if (fmtCode == 'n') {
curUnderline = true;
underlineStartX = curX - 1.0f;
underlineEndX = underlineStartX;
} else if (fmtCode == 'o') {
curItalic = true;
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
curRainbow = true;
curGradient = false;
rainbowCharIndex = 0;
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
curWave = !curWave;
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
curDinnerbone = !curDinnerbone;
} else if (fmtCode == 'u' && AngelicaConfig.enableDropShadow) {
int customRgb = (charIdx + SECTION_X_LENGTH < stringEnd)
? ColorCodeUtils.parseSectionXAt(string, charIdx + 1)
: -1;
if (customRgb != -1) {
curShadow = true;
curShadowCustomColor = true;
curShadowColorOverride = customRgb;
charIdx += SECTION_X_LENGTH;
} else {
curShadow = !curShadow;
if (!curShadow) curShadowCustomColor = false;
}
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
if (color1 != -1 && color2 != -1) {
curGradient = true;
final boolean is09 = charInRange(fmtCode, '0', '9');
final boolean isAF = charInRange(fmtCode, 'a', 'f');
if (is09 || isAF) {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
gradientStartRgb = color1;
gradientEndRgb = color2;
gradientCharIndex = 0;
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
charIdx += GRADIENT_PAYLOAD;
curGradient = false;
// wave/dinnerbone NOT reset — they're positional effects, independent of color
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
final int rgb = this.colorCode[colorIdx];
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
final int shadowRgb = this.colorCode[colorIdx + 16];
curShadowColor = (curShadowColor & 0xFF000000) | (shadowRgb & 0x00FFFFFF);
} else if (fmtCode == 'k') {
curRandom = true;
} else if (fmtCode == 'l') {
curBold = true;
} else if (fmtCode == 'm') {
curStrikethrough = true;
strikethroughStartX = curX - 1.0f;
strikethroughEndX = strikethroughStartX;
} else if (fmtCode == 'n') {
curUnderline = true;
underlineStartX = curX - 1.0f;
underlineEndX = underlineStartX;
} else if (fmtCode == 'o') {
curItalic = true;
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
curRainbow = true;
curGradient = false;
rainbowCharIndex = 0;
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
curWave = !curWave;
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
curDinnerbone = !curDinnerbone;
} else if (fmtCode == 'u' && AngelicaConfig.enableDropShadow) {
int customRgb = (charIdx + SECTION_X_LENGTH < stringEnd)
? ColorCodeUtils.parseSectionXAt(string, charIdx + 1)
: -1;
if (customRgb != -1) {
curShadow = true;
curShadowCustomColor = true;
curShadowColorOverride = customRgb;
charIdx += SECTION_X_LENGTH;
} else {
curShadow = !curShadow;
if (!curShadow) curShadowCustomColor = false;
}
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
if (color1 != -1 && color2 != -1) {
curGradient = true;
curRainbow = false;
gradientStartRgb = color1;
gradientEndRgb = color2;
gradientCharIndex = 0;
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
charIdx += GRADIENT_PAYLOAD;
}
} else if (fmtCode == 'r') {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curWave = false;
curDinnerbone = false;
curGradient = false;
curShadow = false;
curShadowCustomColor = false;
curColor = color;
curShadowColor = shadowColor;
}
} else if (fmtCode == 'r') {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curWave = false;
curDinnerbone = false;
curGradient = false;
curShadow = false;
curShadowCustomColor = false;
curColor = color;
curShadowColor = shadowColor;
}
} // close else block for non-§x codes
continue;
@@ -1130,9 +1445,9 @@ public class BatchingFontRenderer {
}
if (curGradient && gradientTotalChars > 0) {
float t = Math.min(gradientCharIndex * gradientStep, 1f);
int gr = (int)((gradientStartRgb >> 16 & 0xFF) * (1-t) + (gradientEndRgb >> 16 & 0xFF) * t);
int gg = (int)((gradientStartRgb >> 8 & 0xFF) * (1-t) + (gradientEndRgb >> 8 & 0xFF) * t);
int gb = (int)((gradientStartRgb & 0xFF) * (1-t) + (gradientEndRgb & 0xFF) * t);
int gr = (int) ((gradientStartRgb >> 16 & 0xFF) * (1 - t) + (gradientEndRgb >> 16 & 0xFF) * t);
int gg = (int) ((gradientStartRgb >> 8 & 0xFF) * (1 - t) + (gradientEndRgb >> 8 & 0xFF) * t);
int gb = (int) ((gradientStartRgb & 0xFF) * (1 - t) + (gradientEndRgb & 0xFF) * t);
int rgbEffect = (gr << 16) | (gg << 8) | gb;
curColor = (curColor & 0xFF000000) | rgbEffect;
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
@@ -1155,7 +1470,7 @@ public class BatchingFontRenderer {
// Wave: Y offset via sine wave
float renderY = heightNorth;
if (curWave) {
float time = HUDCaching.renderingCacheOverride ? 0f : (float)((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
float time = HUDCaching.renderingCacheOverride ? 0f : (float) ((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
renderY += (float) Math.sin(visibleCharIndex * WAVE_FREQUENCY + time) * AngelicaConfig.waveAmplitude;
}
@@ -0,0 +1,96 @@
package com.gtnewhorizons.angelica.client.gui;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
public record DynamicLightsEntityFilter(List<String> modPrefixes, List<String> nameTerms, DynamicLightsEntityFilter.State state) {
public enum State {
ANY,
ENABLED,
DISABLED
}
private static final Pattern WHITESPACE = Pattern.compile("\\s+");
private static final DynamicLightsEntityFilter MATCH_ALL = new DynamicLightsEntityFilter(List.of(), List.of(), State.ANY);
public DynamicLightsEntityFilter {
modPrefixes = List.copyOf(modPrefixes);
nameTerms = List.copyOf(nameTerms);
}
public static DynamicLightsEntityFilter parse(String query) {
if (query == null) {
return MATCH_ALL;
}
final var normalized = query.strip().toLowerCase(Locale.ROOT);
if (normalized.isEmpty()) {
return MATCH_ALL;
}
final var modPrefixes = new ArrayList<String>();
final var nameTerms = new ArrayList<String>();
var wantEnabled = false;
var wantDisabled = false;
for (final var token : WHITESPACE.split(normalized)) {
switch (token) {
case "$on" -> wantEnabled = true;
case "$off" -> wantDisabled = true;
default -> {
if (token.length() > 1 && token.charAt(0) == '@') {
modPrefixes.add(token.substring(1));
} else {
nameTerms.add(token);
}
}
}
}
final var state = wantEnabled == wantDisabled ? State.ANY : wantEnabled ? State.ENABLED : State.DISABLED;
if (modPrefixes.isEmpty() && nameTerms.isEmpty() && state == State.ANY) {
return MATCH_ALL;
}
return new DynamicLightsEntityFilter(modPrefixes, nameTerms, state);
}
public boolean isMatchAll() {
return modPrefixes.isEmpty() && nameTerms.isEmpty() && state == State.ANY;
}
public boolean matches(String lowerName, String lowerModId, boolean enabled) {
return matchesState(enabled) && matchesAnyMod(lowerModId) && containsAllTerms(lowerName);
}
private boolean matchesState(boolean enabled) {
return switch (state) {
case ANY -> true;
case ENABLED -> enabled;
case DISABLED -> !enabled;
};
}
private boolean matchesAnyMod(String lowerModId) {
if (modPrefixes.isEmpty()) {
return true;
}
for (final var prefix : modPrefixes) {
if (lowerModId.startsWith(prefix)) {
return true;
}
}
return false;
}
private boolean containsAllTerms(String lowerName) {
for (final var term : nameTerms) {
if (!lowerName.contains(term)) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,242 @@
package com.gtnewhorizons.angelica.client.gui;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
import net.coderbot.iris.gui.element.IrisGuiSlot;
import net.coderbot.iris.gui.element.widget.IrisButton;
public class DynamicLightsEntityScreen extends GuiScreen {
private static final String ENABLED_PREFIX = EnumChatFormatting.GREEN + "[x] " + EnumChatFormatting.WHITE;
private static final String DISABLED_PREFIX = EnumChatFormatting.DARK_GRAY + "[ ] " + EnumChatFormatting.GRAY;
private static List<EntityTypeEntry> entityTypes;
private final GuiScreen parent;
private final String title = I18n.format("options.dynamiclights.entities.title");
private final String info = I18n.format("options.dynamiclights.entities.info");
private List<EntityTypeEntry> displayed;
private String countLabel;
private String search = "";
private boolean dirty;
private GuiTextField searchBox;
private EntityRowList rowList;
private IrisButton toggleButton;
public DynamicLightsEntityScreen(GuiScreen parent) {
this.parent = parent;
this.refilter();
}
private static List<EntityTypeEntry> entityTypes() {
if (entityTypes == null) {
entityTypes = EntityLightConfig.getAllEntityTypes();
}
return entityTypes;
}
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
this.searchBox = new GuiTextField(this.fontRendererObj, this.width / 2 - 150, 30, 300, 20);
this.searchBox.setMaxStringLength(64);
this.searchBox.setText(this.search);
this.rowList = new EntityRowList();
this.toggleButton = new IrisButton(this.width / 2 - 154, this.height - 27, 150, 20, "", button -> this.toggleVisible());
this.updateToggleButton();
this.buttonList.add(this.toggleButton);
this.buttonList.add(new IrisButton(this.width / 2 + 4, this.height - 27, 150, 20, I18n.format("gui.done"), button -> this.onClose()));
}
@Override
public void onGuiClosed() {
Keyboard.enableRepeatEvents(false);
}
private void onClose() {
if (this.dirty) {
EntityLightConfig.save();
this.dirty = false;
}
this.mc.displayGuiScreen(this.parent);
}
private void refilter() {
final var filter = DynamicLightsEntityFilter.parse(this.search);
if (filter.isMatchAll()) {
this.displayed = entityTypes();
} else {
final var matches = new ArrayList<EntityTypeEntry>();
for (final var entry : entityTypes()) {
if (filter.matches(entry.getSearchName(), entry.getSearchModId(), EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass()))) {
matches.add(entry);
}
}
this.displayed = matches;
}
this.countLabel = this.displayed.size() + " / " + entityTypes().size();
this.updateToggleButton();
}
private boolean anyDisplayedEnabled() {
for (final var entry : this.displayed) {
if (EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass())) {
return true;
}
}
return false;
}
private void updateToggleButton() {
if (this.toggleButton == null) {
return;
}
final var subset = this.displayed.size() != entityTypes().size();
final var key = this.anyDisplayedEnabled()
? (subset ? "options.dynamiclights.entities.disableshown" : "options.dynamiclights.entities.disableall")
: (subset ? "options.dynamiclights.entities.enableshown" : "options.dynamiclights.entities.enableall");
this.toggleButton.displayString = I18n.format(key);
this.toggleButton.enabled = !this.displayed.isEmpty();
}
private void toggleVisible() {
final var enable = !this.anyDisplayedEnabled();
for (final var entry : this.displayed) {
EntityLightConfig.setEntityTypeEnabled(entry.getEntityClass(), enable);
}
this.dirty = true;
this.refilter();
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
this.drawDefaultBackground();
this.rowList.drawScreen(mouseX, mouseY, partialTicks);
super.drawScreen(mouseX, mouseY, partialTicks);
drawCenteredString(this.fontRendererObj, this.title, this.width / 2, 8, 0xFFFFFF);
drawCenteredString(this.fontRendererObj, this.info, this.width / 2, 20, 0xA0A0A0);
this.searchBox.drawTextBox();
if (!this.searchBox.isFocused() && this.search.isEmpty()) {
this.drawString(this.fontRendererObj, I18n.format("options.dynamiclights.entities.search"), this.width / 2 - 144, 36, 0x808080);
}
drawCenteredString(this.fontRendererObj, this.countLabel, this.width / 2, this.height - 39, 0x808080);
}
@Override
public void updateScreen() {
this.searchBox.updateCursorCounter();
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
if (!this.rowList.mouseClicked(mouseX, mouseY, mouseButton)) {
super.mouseClicked(mouseX, mouseY, mouseButton);
}
this.searchBox.mouseClicked(mouseX, mouseY, mouseButton);
}
@Override
protected void mouseMovedOrUp(int mouseX, int mouseY, int state) {
if (state == -1 || !this.rowList.mouseReleased(mouseX, mouseY, Mouse.getEventButton())) {
super.mouseMovedOrUp(mouseX, mouseY, state);
}
}
@Override
protected void actionPerformed(GuiButton button) {
if (button.enabled && button instanceof IrisButton irisButton) {
irisButton.onPress();
}
}
@Override
protected void keyTyped(char typedChar, int keyCode) {
if (this.searchBox.isFocused()) {
if (keyCode == Keyboard.KEY_ESCAPE) {
this.searchBox.setFocused(false);
return;
}
this.searchBox.textboxKeyTyped(typedChar, keyCode);
if (!this.searchBox.getText().equals(this.search)) {
this.search = this.searchBox.getText();
this.refilter();
}
return;
}
if (keyCode == Keyboard.KEY_ESCAPE) {
this.onClose();
return;
}
super.keyTyped(typedChar, keyCode);
}
private class EntityRowList extends IrisGuiSlot {
EntityRowList() {
super(DynamicLightsEntityScreen.this.mc, DynamicLightsEntityScreen.this.width, DynamicLightsEntityScreen.this.height, 56, DynamicLightsEntityScreen.this.height - 44, 18);
this.setRenderBackground(false);
}
@Override
protected int getSize() {
return displayed.size();
}
@Override
protected boolean isSelected(int index) {
return false;
}
@Override
protected void drawBackground() {}
@Override
public int getListWidth() {
return Math.min(400, DynamicLightsEntityScreen.this.width - 40);
}
@Override
protected boolean elementClicked(int index, boolean doubleClick, int mouseX, int mouseY, int button) {
if (button != 0 || index >= displayed.size()) {
return false;
}
final var entry = displayed.get(index);
EntityLightConfig.setEntityTypeEnabled(entry.getEntityClass(), !EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass()));
dirty = true;
updateToggleButton();
return true;
}
@Override
protected void drawSlot(int index, int x, int y, int slotHeight, Tessellator tessellator, int mouseX, int mouseY) {
final var entry = displayed.get(index);
final var enabled = EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass());
final var label = (enabled ? ENABLED_PREFIX : DISABLED_PREFIX) + entry.getDisplayName() + EnumChatFormatting.DARK_GRAY + " (" + entry.getModId() + ")";
drawString(fontRendererObj, fontRendererObj.trimStringToWidth(label, this.getListWidth() - 8), x, y + 5, 0xFFFFFF);
}
}
}
@@ -1,105 +1,32 @@
package com.gtnewhorizons.angelica.client.gui;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfigStorage;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
import jss.notfine.core.Settings;
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.OptionImpl;
import me.jellysquid.mods.sodium.client.gui.options.OptionImpact;
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
import me.jellysquid.mods.sodium.client.gui.options.SubScreenOption;
import net.minecraft.client.resources.I18n;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class DynamicLightsOptionPages {
public static OptionPage dynamicLights() {
if (!DynamicLights.configEnabled) {
return null;
}
List<OptionGroup> groups = new ArrayList<>();
groups.add(OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS.option)
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
.build());
groups.add(OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
.build());
groups.add(OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
.build());
// Entity type toggles - grouped by mod ID
List<EntityTypeEntry> allEntities = EntityLightConfig.getAllEntityTypes();
Map<String, List<EntityTypeEntry>> byMod = new LinkedHashMap<>();
for (EntityTypeEntry entry : allEntities) {
byMod.computeIfAbsent(entry.getModId(), _ -> new ArrayList<>()).add(entry);
}
// Create a group for each mod's entities
for (Map.Entry<String, List<EntityTypeEntry>> modEntry : byMod.entrySet()) {
String modId = modEntry.getKey();
List<EntityTypeEntry> entities = modEntry.getValue();
OptionGroup.Builder groupBuilder = OptionGroup.createBuilder();
for (EntityTypeEntry entity : entities) {
Option<Boolean> option = createEntityOption(entity);
if (option != null) {
groupBuilder.add(option);
}
}
// Only add group if it has options
OptionGroup group = groupBuilder.build();
if (!group.getOptions().isEmpty()) {
groups.add(group);
}
}
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.copyOf(groups));
}
private static Option<Boolean> createEntityOption(EntityTypeEntry entry) {
final String className = entry.getClassName();
// Resolve class once during option creation, not on every get/set
Class<?> resolvedClass;
try {
resolvedClass = Class.forName(className);
} catch (ClassNotFoundException e) {
// Class not loaded, skip this option
return null;
}
final Class<?> entityClass = resolvedClass;
return OptionImpl.createBuilder(Boolean.class, EntityLightConfigStorage.INSTANCE)
.setName(entry.getDisplayName() + " (" + entry.getModId() + ")")
.setTooltip(I18n.format("options.dynamiclights.entity.tooltip", entry.getDisplayName()))
.setControl(TickBoxControl::new)
.setBinding(
(_, value) -> EntityLightConfig.setEntityTypeEnabled(entityClass, value),
storage -> EntityLightConfig.isEntityTypeEnabled(entityClass)
)
.setImpact(OptionImpact.LOW)
.build();
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.of(
OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS.option)
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
.build(),
OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
.build(),
OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
.build(),
OptionGroup.createBuilder()
.add(new SubScreenOption(I18n.format("options.dynamiclights.entities"), I18n.format("options.dynamiclights.entities.tooltip"), DynamicLightsEntityScreen::new))
.build()));
}
}
@@ -17,6 +17,10 @@ public final class BopFogColorSource implements BopFogBlend.ColorSource {
return this;
}
public void freeWorld() {
this.world = null;
}
@Override
public int fogColour(int x, int y, int z) {
final BiomeGenBase biome = FogBiomeCache.get(world, x, z);
@@ -9,7 +9,7 @@ public final class FogBiomeCache {
private static final int MAX_ENTRIES = 16384;
private static final Long2ObjectOpenHashMap<BiomeGenBase> CACHE = new Long2ObjectOpenHashMap<>();
private static World lastWorld;
private static World cachedWorld;
private static int generation;
private FogBiomeCache() {}
@@ -18,10 +18,14 @@ public final class FogBiomeCache {
return generation;
}
public static void invalidate() {
cachedWorld = null;
}
public static BiomeGenBase get(World world, int x, int z) {
if (world != lastWorld) {
if (world != cachedWorld) {
CACHE.clear();
lastWorld = world;
cachedWorld = world;
generation++;
}
final long key = ((long) x << 32) | (z & 0xFFFFFFFFL);
@@ -15,6 +15,7 @@ import net.coderbot.batchedentityrendering.impl.BlendingStateHolder;
import net.coderbot.batchedentityrendering.impl.TransparencyType;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.layer.PassOverride;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
@@ -68,7 +69,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
return TRANSLUCENT;
}
private static MultiPhaseParameters.Builder tesrMaterialPhases(ResourceLocation texture, TesrMaterial material) {
private static MultiPhaseParameters.Builder tesrMaterialPhases(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
final MultiPhaseParameters.Builder b = MultiPhaseParameters.builder();
b.texture(texture != null ? new RenderPhase.Texture(texture, false, false) : NO_TEXTURE);
if (material.isNoCull()) {
@@ -92,6 +93,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
case GLINT -> b.transparency(GLINT_TRANSPARENCY);
case OPAQUE -> b.transparency(NO_TRANSPARENCY);
}
boolean shaderPhaseTaken = true;
switch (material.special()) {
case GLINT -> b.shader(SPECIAL_GLINT);
case BEACON_BEAM -> b.shader(SPECIAL_BEACON_BEAM);
@@ -99,9 +101,15 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
final TesrShader shader = material.shader();
if (shader != null) {
b.shader(new RenderPhase.Shader("angelica_tesr_shader_" + shader.name(), shader.bind(), shader.release()));
} else {
shaderPhaseTaken = false;
}
}
}
if (!shaderPhaseTaken && pass != PassOverride.NONE) {
b.shader(new RenderPhase.Shader("angelica_tesr_pass" + pass.nameSuffix(), pass::apply, pass::clear));
}
b.layering(polygonOffset(offsetFactor, offsetUnits));
return b;
}
@@ -113,12 +121,23 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
GbufferPrograms::teardownSpecialRenderCondition);
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material) {
final MultiPhaseParameters.Builder b = tesrMaterialPhases(texture, material).shadeModel(SMOOTH_SHADE_MODEL);
return tesr(texture, material, PassOverride.NONE, 0.0f, 0.0f);
}
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
final MultiPhaseParameters.Builder b = tesrMaterialPhases(texture, material, pass, offsetFactor, offsetUnits).shadeModel(SMOOTH_SHADE_MODEL);
final boolean sortsTranslucent = material.transparency() != TesrMaterial.Transparency.OPAQUE;
return of("angelica_tesr_" + material.transparency().name().toLowerCase(Locale.ROOT),
return of("angelica_tesr_" + material.transparency().name().toLowerCase(Locale.ROOT) + pass.nameSuffix()
+ (offsetFactor == 0.0f && offsetUnits == 0.0f ? "" : "_offset" + offsetFactor + "_" + offsetUnits),
BatchVertexFormats.POSITION_COLOR_TEXTURE_LIGHTF_NORMAL, GL11.GL_QUADS, 65536, true, sortsTranslucent, b.build(false));
}
public static RenderLayer tesrNoPass(ResourceLocation texture, TesrMaterial material) {
return of("angelica_tesr_nopass_" + material.transparency().name().toLowerCase(Locale.ROOT),
BatchVertexFormats.POSITION_COLOR_TEXTURE_LIGHTF_NORMAL, GL11.GL_QUADS, 65536, true, false,
tesrMaterialPhases(texture, material, PassOverride.NONE, 0.0f, 0.0f).build(false));
}
public static RenderLayer getOutline(ResourceLocation texture, RenderPhase.Cull cull) {
return of("outline", DefaultVertexFormat.POSITION_COLOR_TEXTURE, 7, 256, RenderLayer.MultiPhaseParameters.builder().texture(new RenderPhase.Texture(texture, false, false)).cull(cull).depthTest(ALWAYS_DEPTH_TEST).alpha(ONE_TENTH_ALPHA).fog(NO_FOG).target(OUTLINE_TARGET).build(RenderLayer.OutlineMode.IS_OUTLINE));
}
@@ -313,6 +332,10 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
return this;
}
public void layering(Layering layering) {
this.layering = layering;
}
public Builder shadeModel(RenderPhase.ShadeModel shadeModel) {
this.shadeModel = shadeModel;
return this;
@@ -9,7 +9,9 @@ import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Optional;
@@ -77,6 +79,7 @@ public abstract class RenderPhase {
protected static final WriteMaskState COLOR_MASK;
protected static final WriteMaskState DEPTH_MASK;
protected static final Layering NO_LAYERING;
protected static final Fog NO_FOG;
protected static final Fog FOG;
protected static final Fog BLACK_FOG;
@@ -85,6 +88,22 @@ public abstract class RenderPhase {
protected static final Target TRANSLUCENT_TARGET;
protected static final Shader NO_SHADER = new Shader("no_shader", () -> {}, () -> {});
private static final Map<Long, Layering> POLYGON_OFFSET_LAYERINGS = new ConcurrentHashMap<>();
public static Layering polygonOffset(float factor, float units) {
if (factor == 0.0f && units == 0.0f) {
return NO_LAYERING;
}
final long key = ((long) Float.floatToIntBits(factor) << 32) | (Float.floatToIntBits(units) & 0xFFFFFFFFL);
return POLYGON_OFFSET_LAYERINGS.computeIfAbsent(key, k -> new Layering("polygon_offset_" + factor + "_" + units, () -> {
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(factor, units);
}, () -> {
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(0.0f, 0.0f);
}));
}
public RenderPhase(String name, Runnable beginAction, Runnable endAction) {
this.name = name;
this.beginAction = beginAction;
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.config;
import com.gtnewhorizon.gtnhlib.config.Config;
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
import net.coderbot.iris.shadows.ShadowGraphGate;
@Config(modid = "angelica", filename = "angelica-modules")
public class AngelicaConfig {
@@ -347,6 +348,20 @@ public class AngelicaConfig {
@Config.DefaultBoolean(true)
public static boolean shadowSkipInMeshTileEntities;
@Config.Comment({
"How much a celestial body can move before the shadow map can update.",
"",
"Note this option is disabled when a shader pack utilizes voxelization features."
})
@Config.DefaultFloat(ShadowGraphGate.DEFAULT_ANGLE_DELTA_DEGREES)
@Config.RangeFloat(min = 0f, max = 0.1f)
public static float shadowGraphAngleDelta;
@Config.Comment("Adjusts the rate the shadow map rebuilds when when a celestial body is near the horizon. Rate is sinusoidal")
@Config.DefaultFloat(ShadowGraphGate.DEFAULT_HORIZON_SCALE)
@Config.RangeFloat(min = 0f, max = 1.0f)
public static float shadowGraphHorizonScale;
@Config.Comment("ASM transformer exclusion narrowing for mod compatibility. Disable per-mod if narrowing causes class loading issues.")
public static TransformerCompat transformerCompat = new TransformerCompat();
@@ -437,6 +452,18 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static GLProfile glProfile;
@Config.Comment("Enable random top-face texture orientation for configured blocks")
@Config.DefaultBoolean(true)
public static boolean enableNaturalTextures = true;
@Config.Comment("List of block registry names to apply random top-face texture rotation to")
@Config.DefaultStringList({
"minecraft:andesite", "minecraft:dirt", "minecraft:granite", "minecraft:grass", "minecraft:mycelium", "minecraft:sand", "minecraft:soul_sand", "etfuturum:calcite", "etfuturum:coarse_dirt", "etfuturum:cobbled_deepslate", "etfuturum:concrete_powder", "etfuturum:deepslate", "etfuturum:polished_deepslate", "etfuturum:soul_soil", "etfuturum:tuff", "BiomesOPlenty:ash", "BiomesOPlenty:cragRock", "BiomesOPlenty:driedDirt", "BiomesOPlenty:hardDirt", "BiomesOPlenty:hardSand", "BiomesOPlenty:mud", "BiomesOPlenty:newBopDirt", "Botania:dirtPath", "Botania:enchantedSoil", "Botania:livingrock", "Botania:prismarine", "Botania:shimmerrock", "Botany:loam", "Botany:loamNoWeed", "Botany:soil", "Botany:soilNoWeed", "chisel:moss", "chisel:moss_carpet", "DraconicEvolution:earth", "dreamcraft:SandClayMix", "ExtraUtilities:color_hellsand", "ExtraUtilities:cursedearthside", "GalaxySpace:acentauribbgrunt", "GalaxySpace:acentauribbsubgrunt", "GalaxySpace:barnardaCdirt", "GalaxySpace:barnardaEgrunt", "GalaxySpace:barnardaEsubgrunt", "GalaxySpace:barnardaFgrunt", "GalaxySpace:barnardaFsubgrunt", "GalaxySpace:callistoblocks", "GalaxySpace:ceresblocks", "GalaxySpace:deimosblocks", "GalaxySpace:europagrunt", "GalaxySpace:ganymedeblocks", "GalaxySpace:haumeablocks", "GalaxySpace:ioblocks", "GalaxySpace:makemakegrunt", "GalaxySpace:mercuryblocks", "GalaxySpace:mirandablocks", "GalaxySpace:oberonblocks", "GalaxySpace:phobosblocks", "GalaxySpace:proteusblocks", "GalaxySpace:tcetieblocks", "GalaxySpace:titanblocks", "GalaxySpace:tritonblocks", "GalaxySpace:vegabgrunt", "GalaxySpace:vegabsubgrunt", "GalaxySpace:venusblocks", "gregtech:gt.blockgranites", "IC2:blockBasalt", "MagicBees:magicbees.enchantedEarth", "Natura:heatsand", "RandomThings:fertilizedDirt", "TConstruct:CraftedSoil", "TConstruct:SpeedBlock", "thaumicbases:oldCobble", "thaumicbases:oldCobbleMossy", "ToxicEverglades:blockDarkWorldGround2", "VillageNames:concretePowder", "witchery:pitdirt"
})
public static String[] naturalTextureBlocks = new String[] {
"minecraft:andesite", "minecraft:dirt", "minecraft:granite", "minecraft:grass", "minecraft:mycelium", "minecraft:sand", "minecraft:soul_sand", "etfuturum:calcite", "etfuturum:coarse_dirt", "etfuturum:cobbled_deepslate", "etfuturum:concrete_powder", "etfuturum:deepslate", "etfuturum:polished_deepslate", "etfuturum:soul_soil", "etfuturum:tuff", "BiomesOPlenty:ash", "BiomesOPlenty:cragRock", "BiomesOPlenty:driedDirt", "BiomesOPlenty:hardDirt", "BiomesOPlenty:hardSand", "BiomesOPlenty:mud", "BiomesOPlenty:newBopDirt", "Botania:dirtPath", "Botania:enchantedSoil", "Botania:livingrock", "Botania:prismarine", "Botania:shimmerrock", "Botany:loam", "Botany:loamNoWeed", "Botany:soil", "Botany:soilNoWeed", "chisel:moss", "chisel:moss_carpet", "DraconicEvolution:earth", "dreamcraft:SandClayMix", "ExtraUtilities:color_hellsand", "ExtraUtilities:cursedearthside", "GalaxySpace:acentauribbgrunt", "GalaxySpace:acentauribbsubgrunt", "GalaxySpace:barnardaCdirt", "GalaxySpace:barnardaEgrunt", "GalaxySpace:barnardaEsubgrunt", "GalaxySpace:barnardaFgrunt", "GalaxySpace:barnardaFsubgrunt", "GalaxySpace:callistoblocks", "GalaxySpace:ceresblocks", "GalaxySpace:deimosblocks", "GalaxySpace:europagrunt", "GalaxySpace:ganymedeblocks", "GalaxySpace:haumeablocks", "GalaxySpace:ioblocks", "GalaxySpace:makemakegrunt", "GalaxySpace:mercuryblocks", "GalaxySpace:mirandablocks", "GalaxySpace:oberonblocks", "GalaxySpace:phobosblocks", "GalaxySpace:proteusblocks", "GalaxySpace:tcetieblocks", "GalaxySpace:titanblocks", "GalaxySpace:tritonblocks", "GalaxySpace:vegabgrunt", "GalaxySpace:vegabsubgrunt", "GalaxySpace:venusblocks", "gregtech:gt.blockgranites", "IC2:blockBasalt", "MagicBees:magicbees.enchantedEarth", "Natura:heatsand", "RandomThings:fertilizedDirt", "TConstruct:CraftedSoil", "TConstruct:SpeedBlock", "thaumicbases:oldCobble", "thaumicbases:oldCobbleMossy", "ToxicEverglades:blockDarkWorldGround2", "VillageNames:concretePowder", "witchery:pitdirt"
};
public static void applyGpuCullingMode() {
GpuCulling.setMode(gpuCullingMode == null ? GpuCullingMode.CPU_ONLY : gpuCullingMode);
}
@@ -56,7 +56,9 @@ public class FontConfig {
@Config.RangeFloat(min = -2F, max = 2F)
public static float glyphSpacing;
@Config.Comment("Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.")
@Config.Comment({
"Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.",
"Turning off AA lets shaders pick up the text."})
@Config.DefaultInt(2)
@Config.RangeInt(min = 0, max = 2)
public static int fontAAMode;
@@ -71,7 +71,6 @@ public final class EntityLightConfig {
}
}
@SuppressWarnings("unchecked")
public static List<EntityTypeEntry> getAllEntityTypes() {
List<EntityTypeEntry> entries = new ArrayList<>();
Set<String> seenClasses = new HashSet<>();
@@ -88,9 +87,8 @@ public final class EntityLightConfig {
String modId = getModIdForEntity(entityClass, entityId);
String displayName = getDisplayName(entityId, entityClass);
boolean enabled = !disabledClassNames.contains(className);
entries.add(new EntityTypeEntry(displayName, className, modId, enabled));
entries.add(new EntityTypeEntry(displayName, entityClass, modId));
}
// Sort by mod ID, then by display name
@@ -1,20 +0,0 @@
package com.gtnewhorizons.angelica.dynamiclights.config;
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
public class EntityLightConfigStorage implements OptionStorage<EntityLightConfigStorage> {
public static final EntityLightConfigStorage INSTANCE = new EntityLightConfigStorage();
private EntityLightConfigStorage() {}
@Override
public EntityLightConfigStorage getData() {
return this;
}
@Override
public void save() {
EntityLightConfig.save();
}
}
@@ -1,42 +1,47 @@
package com.gtnewhorizons.angelica.dynamiclights.config;
import java.util.Locale;
import org.jetbrains.annotations.NotNull;
public class EntityTypeEntry {
private final String displayName;
private final String className;
private final String modId;
private boolean enabled;
public final class EntityTypeEntry {
public EntityTypeEntry(@NotNull String displayName, @NotNull String className, @NotNull String modId, boolean enabled) {
private final String displayName;
private final Class<?> entityClass;
private final String modId;
private final String searchName;
private final String searchModId;
public EntityTypeEntry(@NotNull String displayName, @NotNull Class<?> entityClass, @NotNull String modId) {
this.displayName = displayName;
this.className = className;
this.entityClass = entityClass;
this.modId = modId;
this.enabled = enabled;
this.searchName = displayName.toLowerCase(Locale.ROOT);
this.searchModId = modId.toLowerCase(Locale.ROOT);
}
public String getDisplayName() {
return displayName;
}
public String getClassName() {
return className;
public Class<?> getEntityClass() {
return entityClass;
}
public String getModId() {
return modId;
}
public boolean isEnabled() {
return enabled;
public String getSearchName() {
return searchName;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
public String getSearchModId() {
return searchModId;
}
@Override
public String toString() {
return displayName + " (" + modId + ") [" + (enabled ? "ON" : "OFF") + "]";
return displayName + " (" + modId + ")";
}
}
@@ -10,6 +10,9 @@ import com.gtnewhorizons.angelica.glsm.hooks.ShaderTransformPostProcessor;
import com.gtnewhorizons.angelica.glsm.shader.ShaderType;
import org.taumc.glsl.grammar.GLSLParser;
import com.gtnewhorizons.angelica.glsm.hooks.ShaderWorkSubmitter;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
import net.coderbot.iris.Iris;
import java.util.concurrent.CompletableFuture;
@@ -38,7 +41,20 @@ public class IrisGLSMBridge {
private static Runnable fogDensityListener = null;
private static Runnable colorModulatorListener = null;
private static boolean inputsDeferred = false;
private static boolean blendDeferred = false;
private static boolean programRestoreDeferred = false;
private static final Int2IntOpenHashMap programLastUpdatedFrame = new Int2IntOpenHashMap();
private static void refreshBlendCondition() {
if (Iris.getPipelineManager().getPipelineNullable() instanceof DeferredWorldRenderingPipeline drp) {
drp.onVanillaBlendChanged();
}
}
static {
programLastUpdatedFrame.defaultReturnValue(-1);
StateUpdateNotifiers.alphaFuncNotifier = listener -> alphaFuncListener = listener;
@@ -72,6 +88,44 @@ public class IrisGLSMBridge {
};
}
private static VanillaBooleanLayer gated(VanillaBooleanLayer layer) {
return new VanillaBooleanLayer() {
@Override
public boolean isOverrideHeld() {
return Iris.enabled && layer.isOverrideHeld();
}
@Override
public boolean getVanilla() {
return layer.getVanilla();
}
@Override
public void setVanilla(boolean enabled) {
layer.setVanilla(enabled);
}
};
}
private static <T> VanillaStateLayer<T> gated(VanillaStateLayer<T> layer) {
return new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return Iris.enabled && layer.isOverrideHeld();
}
@Override
public void readVanilla(T into) {
layer.readVanilla(into);
}
@Override
public void writeVanilla(T from) {
layer.writeVanilla(from);
}
};
}
public static void register() {
GLSMConfig.expandVertexFormats = Iris.enabled;
IrisSamplers.initRenderer();
@@ -88,6 +142,11 @@ public class IrisGLSMBridge {
return Iris.enabled && BlendModeStorage.isBlendLocked();
}
@Override
public boolean isOverrideHeld() {
return Iris.enabled && BlendModeStorage.isOverrideHeld();
}
@Override
public void deferBlendModeToggle(boolean enabled) {
BlendModeStorage.deferBlendModeToggle(enabled);
@@ -104,6 +163,13 @@ public class IrisGLSMBridge {
}
};
GLStateManager.getBlendMode().setVanillaLayer(gated(BlendModeStorage.ENABLE_LAYER));
GLStateManager.getBlendState().setVanillaLayer(gated(BlendModeStorage.FUNC_LAYER));
GLStateManager.getAlphaTest().setVanillaLayer(gated(AlphaTestStorage.ENABLE_LAYER));
GLStateManager.getAlphaState().setVanillaLayer(gated(AlphaTestStorage.FUNC_LAYER));
GLStateManager.getDepthState().setVanillaLayer(gated(DepthColorStorage.DEPTH_LAYER));
GLStateManager.getColorMask().setVanillaLayer(gated(DepthColorStorage.COLOR_LAYER));
GLSMHooks.alphaHandler = new DeferredAlphaHandler() {
@Override
public boolean isAlphaTestLocked() {
@@ -127,6 +193,11 @@ public class IrisGLSMBridge {
return Iris.enabled && DepthColorStorage.isDepthColorLocked();
}
@Override
public boolean isOverrideHeld() {
return Iris.enabled && DepthColorStorage.isOverrideHeld();
}
@Override
public void deferDepthEnable(boolean enabled) {
DepthColorStorage.deferDepthEnable(enabled);
@@ -190,9 +261,41 @@ public class IrisGLSMBridge {
StateTracker.INSTANCE.lightmapSampler = event.enabled;
updatePipeline = true;
}
if (updatePipeline) {
if (!updatePipeline) return;
if (GLStateManager.isForeignDraw()) {
inputsDeferred = true;
return;
}
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setInputs(StateTracker.INSTANCE.getInputs()));
});
GLSMHooks.VANILLA_BLEND_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (GLStateManager.isForeignDraw()) {
blendDeferred = true;
return;
}
refreshBlendCondition();
});
GLSMHooks.FOREIGN_DRAW_END.addListener(event -> {
if (!Iris.enabled) return;
if (inputsDeferred) {
inputsDeferred = false;
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setInputs(StateTracker.INSTANCE.getInputs()));
}
if (blendDeferred) {
blendDeferred = false;
refreshBlendCondition();
}
if (programRestoreDeferred) {
programRestoreDeferred = false;
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (pipeline instanceof DeferredWorldRenderingPipeline drp && drp.shouldOverrideShaders()) {
drp.restorePassAfterModProgram(GLStateManager.getActiveProgram());
}
}
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
@@ -208,15 +311,29 @@ public class IrisGLSMBridge {
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
if (!drp.shouldOverrideShaders()) return;
if (drp.getActivePassProgramId() == -1) return;
DepthColorStorage.unlockDepthColor();
if (DepthColorStorage.isOwnedProgram(event.newProgram)) {
DepthColorStorage.unlockDepthColor();
} else {
if (event.newProgram != 0 && !DepthColorStorage.isOwnedProgram(event.newProgram)) {
drp.onModProgramOverride();
}
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (!event.postBind) return;
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
if (!drp.shouldOverrideShaders()) return;
if (GLStateManager.isForeignDraw()) {
programRestoreDeferred = true;
return;
}
drp.restorePassAfterModProgram(event.newProgram);
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (!event.postBind) return;
@@ -417,11 +417,13 @@ public enum Mixins implements IMixins {
.addClientMixins(
"shaders.MixinDroppedItemGlintEdges"
, "shaders.MixinHeldItemGlintEdges"
, "shaders.MixinEffectRenderer_Particles"
, "shaders.MixinEntityPickupFX"
, "shaders.MixinEntityRenderer"
, "shaders.MixinGuiIngameForge"
, "shaders.MixinFramebuffer"
, "shaders.MixinItem"
, "shaders.MixinItemRenderer_ItemId"
, "shaders.MixinLocale"
, "shaders.MixinRender"
, "shaders.MixinRenderBiped"
@@ -744,6 +746,24 @@ public enum Mixins implements IMixins {
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins("client.etfuturum.MixinLayerBetterElytra")
),
ET_FUTURUM_BEACON_BEAM(new MixinBuilder("Iris Et Futurum Beacon Beam")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.ET_FUTURUM_REQUIEM)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins("client.etfuturum.MixinTileEntityNewBeaconRenderer")
),
OPENBLOCKS_TROPHY_ENTITY_GBUFFER(new MixinBuilder("Render OpenBlocks trophies with the entity gbuffer programs")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.OPENBLOCKS)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins("client.openblocks.MixinTileEntityTrophyRenderer")
),
DRAGONAPI_PARTICLE_SPLIT(new MixinBuilder("Split DragonAPI's replacement particle renderer between the particle gbuffer programs")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.DRAGON_API)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins("client.dragonapi.MixinThrottleableEffectRenderer")
),
MCPATCHER_FORGE(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures)
@@ -23,6 +23,7 @@ public enum TargetedMod implements ITargetMod {
NTM_SPACE("com.hbm.dim.SolarSystem"),
IC2("ic2.core.coremod.IC2core", "IC2"),
NATURA(null, "Natura"),
OPENBLOCKS(null, "OpenBlocks"),
SECURITYCRAFT(null, "securitycraft"),
STARMINER("jp.mc.ancientred.starminer.core.SMPlugin", "modJ_StarMiner"),
SUPERNOVA("com.mitchej123.supernova.core.SupernovaCore", "supernova"),
@@ -0,0 +1,8 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import net.minecraft.item.ItemStack;
public interface ItemRendererAccessor {
ItemStack angelica$getItemToRender();
}
@@ -73,6 +73,9 @@ 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);
@@ -123,11 +123,11 @@ public class DeferredEntityOverlay {
GbufferPrograms.beginEntities();
try {
for (DeferredEntry entry : deferred) {
CapturedRenderingState.INSTANCE.setCurrentEntity(EntityIdHelper.getEntityId(entry.entity));
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(EntityIdHelper.getEntityId(entry.entity), 0);
renderOverlay(entry);
}
} finally {
CapturedRenderingState.INSTANCE.setCurrentEntity(-1);
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(-1, 0);
GbufferPrograms.endEntities();
}
recycle();
@@ -0,0 +1,63 @@
package com.gtnewhorizons.angelica.rendering;
import com.gtnewhorizons.angelica.client.rendering.DeferredDrawBatcher;
import com.gtnewhorizons.angelica.rendering.celeritas.SpriteExtension;
import net.coderbot.iris.layer.GbufferPrograms;
import net.minecraft.client.particle.EntityCloudFX;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.particle.EntityFireworkOverlayFX;
import net.minecraft.client.particle.EntityFireworkSparkFX;
import net.minecraft.client.particle.EntityFireworkStarterFX;
import net.minecraft.client.particle.EntitySpellParticleFX;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevel;
/**
* Splits a particle run between {@code gbuffers_particles} and {@code gbuffers_particles_translucent}.
*/
public final class ParticleRunSplitter {
private static boolean runTranslucent;
private ParticleRunSplitter() {}
public static void beginRun() {
runTranslucent = false;
GbufferPrograms.setTranslucencyDeclaration(Boolean.FALSE);
}
public static void splitIfNeeded(EntityFX particle, Tessellator tessellator, float partialTicks) {
final boolean translucent = isTranslucent(particle);
if (translucent == runTranslucent) {
return;
}
final boolean batcherWasActive = DeferredDrawBatcher.isActive();
if (batcherWasActive) {
DeferredDrawBatcher.exitAndFlush();
}
tessellator.draw();
runTranslucent = translucent;
GbufferPrograms.setTranslucencyDeclaration(translucent);
tessellator.startDrawingQuads();
tessellator.setBrightness(particle.getBrightnessForRender(partialTicks));
if (batcherWasActive) {
DeferredDrawBatcher.enter();
}
}
public static boolean isTranslucent(EntityFX particle) {
if (particle.particleIcon instanceof TextureAtlasSprite sprite) {
return ((SpriteExtension) sprite).celeritas$getTransparencyLevel() == SpriteTransparencyLevel.TRANSLUCENT;
}
return particle instanceof EntityFireworkSparkFX
|| particle instanceof EntityFireworkOverlayFX
|| particle instanceof EntityFireworkStarterFX
|| particle instanceof EntitySpellParticleFX
|| particle instanceof EntityCloudFX;
}
}
@@ -71,6 +71,7 @@ public final class PlayerReflectionCapture {
private static final Matrix4f pendingModelView = new Matrix4f();
private static int pendingSkin;
private static int pendingEntityId;
private static int pendingItemId;
private static int vbo;
private static int vao;
@@ -134,6 +135,7 @@ public final class PlayerReflectionCapture {
pendingModelView.set(GLStateManager.getModelViewMatrix());
pendingSkin = GLStateManager.getBoundTextureForServerState(0);
pendingEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedEntity();
pendingItemId = CapturedRenderingState.INSTANCE.getCurrentRenderedItem();
pending = true;
}
@@ -328,9 +330,10 @@ public final class PlayerReflectionCapture {
final boolean prevDepthWrite = GLStateManager.getDepthState().isEnabled();
final int prevEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedEntity();
final int prevItemId = CapturedRenderingState.INSTANCE.getCurrentRenderedItem();
savedModelView.set(GLStateManager.getModelViewMatrix());
CapturedRenderingState.INSTANCE.setCurrentEntity(pendingEntityId);
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(pendingEntityId, pendingItemId);
GLStateManager.setModelViewMatrix(pendingModelView);
if (maskable) {
GLStateManager.glColorMask(false, false, false, false);
@@ -344,7 +347,7 @@ public final class PlayerReflectionCapture {
GLStateManager.glColorMask(pr, pg, pb, pa);
}
GLStateManager.setModelViewMatrix(savedModelView);
CapturedRenderingState.INSTANCE.setCurrentEntity(prevEntityId);
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(prevEntityId, prevItemId);
GLStateManager.glBindVertexArray(0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, prevTexture);
@@ -1,18 +1,20 @@
package com.gtnewhorizons.angelica.rendering;
/// Despite not in the `api` package, this interface is used by GT5uNH and GTNHLib, possibly others. Be careful what you
/// change!
@SuppressWarnings("unused")
public interface StateAwareTessellator {
/**
* True if the vertex originated from a RenderBlocks method call with the enableAO flag set.
*/
/// True if the vertex originated from a RenderBlocks method call with the enableAO flag set.
int RENDERED_WITH_VANILLA_AO = 0x1;
int NO_DIRECTIONAL_SHADING = 0x2;
void angelica$setAppliedAo(boolean flag);
void angelica$setNoDirectionalShading(boolean flag);
/**
* Sets whether we're doing terrain meshing as part of celeritas -- collects additional information.
*
* Enables per-vertex AO state collection into {@link #angelica$getVertexStates()}.
*/
/// Sets whether we're doing terrain meshing as part of celeritas -- collects additional information.
///
/// Enables per-vertex AO state collection into [#angelica$getVertexStates()].
void angelica$setCeleritasMeshing(boolean active);
int[] angelica$getVertexStates();
@@ -1,5 +1,7 @@
package com.gtnewhorizons.angelica.rendering.celeritas;
import static com.gtnewhorizons.angelica.rendering.StateAwareTessellator.NO_DIRECTIONAL_SHADING;
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
import com.gtnewhorizons.angelica.api.TintComputer;
import com.gtnewhorizons.angelica.api.TintRegistry;
@@ -28,6 +30,7 @@ import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
import org.embeddedt.embeddium.impl.model.light.flat.FlatLightPipeline;
import org.embeddedt.embeddium.impl.model.light.smooth.SmoothLightPipeline;
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
import org.embeddedt.embeddium.impl.render.chunk.ChunkColorWriter;
import org.embeddedt.embeddium.impl.render.chunk.RenderPassConfiguration;
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildBuffers;
@@ -43,6 +46,9 @@ import java.util.List;
public class AngelicaChunkBuildContext extends ChunkBuildContext {
public static final int NUM_PASSES = 2;
// Yoinked from Celeritas
private static final int[] REVERSED_QUAD_ORDER = { 0, 3, 2, 1 };
private final TextureMapExtension textureAtlas;
private final ChunkVertexEncoder.Vertex[] vertices = ChunkVertexEncoder.Vertex.uninitializedQuad();
@Getter
@@ -62,6 +68,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
private boolean lightPipelineReady = false;
private int originX, originY, originZ;
private float cachedSkylightSubtracted;
private final float[] fluidQuadPos = new float[12];
private ModelQuadFacing fluidQuadLightFace;
public AngelicaChunkBuildContext(RenderPassConfiguration<?> renderPassConfiguration, WorldClient world) {
super(renderPassConfiguration);
@@ -160,6 +168,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
boolean hasCachedMaterial = false;
TextureAtlasSprite lastSprite = null;
Material lastMaterial = null;
this.fluidQuadLightFace = null;
for (int quadIdx = 0; quadIdx < numQuads; quadIdx++) {
float uSum = 0, vSum = 0;
@@ -201,11 +210,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
// Otherwise, we need to use the block's lightmap values as they are as the ISBRH may have
// specified its own lightmaps for visual purposes (e.g. fullbright)
if (useAoCalculation && (quadState & StateAwareTessellator.RENDERED_WITH_VANILLA_AO) != 0) {
quadView.setup(trueNormal, blockX, blockY, blockZ);
final ModelQuadFacing lightFace = quadView.getLightFace();
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
final ModelQuadFacing cullFace = quadView.getCullFace();
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, cullFace, lightFace, shouldApplyDiffuse, true);
calculateQuadLight(trueNormal, blockX, blockY, blockZ, worldX, worldY, worldZ, isFluid,
blockAllowsSmoothLighting, shouldApplyDiffuse && (quadState & NO_DIRECTIONAL_SHADING) == 0);
for (int vIdx = 0; vIdx < 4; vIdx++) {
final var vertex = vertices[vIdx];
@@ -227,11 +233,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
}
if (useAoCalculation && isFluid) {
quadView.setup(trueNormal, blockX, blockY, blockZ);
final ModelQuadFacing lightFace = quadView.getLightFace();
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
final ModelQuadFacing cullFace = quadView.getCullFace();
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, cullFace, lightFace, shouldApplyDiffuse, true);
calculateQuadLight(trueNormal, blockX, blockY, blockZ, worldX, worldY, worldZ, true,
blockAllowsSmoothLighting, shouldApplyDiffuse);
for (int vIdx = 0; vIdx < 4; vIdx++) {
vertices[vIdx].light = quadLightData.lm[vIdx];
}
@@ -274,6 +277,42 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
}
}
private void calculateQuadLight(int trueNormal, int blockX, int blockY, int blockZ, int worldX, int worldY,
int worldZ, boolean isFluid, boolean blockAllowsSmoothLighting,
boolean shouldApplyDiffuse) {
final boolean isFlippedFluidCopy = isFluid && fluidQuadLightFace != null && isReversedFluidQuad();
quadView.setup(trueNormal, blockX, blockY, blockZ, isFluid ? ModelQuadFlags.IS_VANILLA_SHADED : 0,
isFlippedFluidCopy ? fluidQuadLightFace : null);
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, quadView.getCullFace(),
quadView.getLightFace(), shouldApplyDiffuse, true);
if (isFluid && !isFlippedFluidCopy) {
fluidQuadLightFace = quadView.getLightFace();
for (int vIdx = 0; vIdx < 4; vIdx++) {
final var vertex = vertices[vIdx];
fluidQuadPos[vIdx * 3] = vertex.x;
fluidQuadPos[vIdx * 3 + 1] = vertex.y;
fluidQuadPos[vIdx * 3 + 2] = vertex.z;
}
} else if (!isFluid) {
fluidQuadLightFace = null;
}
}
private boolean isReversedFluidQuad() {
for (int vIdx = 0; vIdx < 4; vIdx++) {
final int src = REVERSED_QUAD_ORDER[vIdx] * 3;
final var vertex = vertices[vIdx];
if (vertex.x != fluidQuadPos[src] || vertex.y != fluidQuadPos[src + 1] || vertex.z != fluidQuadPos[src + 2]) {
return false;
}
}
return true;
}
private void applyBlockLightTint(int x, int y, int z, ChunkVertexEncoder.Vertex[] vertices) {
if (!hasColoredLight) return;
final ExtQuadLightData ext = (ExtQuadLightData) quadLightData;
@@ -11,6 +11,8 @@ import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHold
import com.gtnewhorizons.angelica.rendering.celeritas.iris.BlockRenderContext;
import com.gtnewhorizons.angelica.rendering.celeritas.iris.ContextAwareChunkVertexEncoder;
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.utils.NaturalTextureUtils;
import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
import it.unimi.dsi.fastutil.floats.FloatArrayList;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
@@ -361,8 +363,16 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
setRenderPass(pass);
// Trigger side effects from canRenderInPass (some ISBRHs like BuildCraft set global state in this method that gets read elsewhere renderWorldBlock)
block.canRenderInPass(pass);
// Some blocks get randomly rotated top faces
final boolean applyNaturalTex = AngelicaConfig.enableNaturalTextures && NaturalTextureUtils.isNaturalBlock(block);
if (applyNaturalTex) { renderBlocks.uvRotateTop = NaturalTextureUtils.getTopRotation(x, y, z); }
tessellator.startDrawingQuads();
renderBlocks.renderBlockByRenderType(block, x, y, z);
if (applyNaturalTex) { renderBlocks.uvRotateTop = 0; } // Reset
boolean blockAllowsSmoothLighting = Minecraft.isAmbientOcclusionEnabled() // smooth lighting on
&& block.getLightValue() == 0; // does not emit real block light
buildContext.copyRawBuffer(tessellator.rawBuffer, tessellator.vertexCount,
@@ -6,6 +6,8 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import com.gtnewhorizons.angelica.compat.bop.BopFogBlend;
import com.gtnewhorizons.angelica.compat.bop.FogBiomeCache;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
@@ -166,6 +168,8 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
TesrBatchRenderer.INSTANCE.clearRetained();
ModelPartBatcher.INSTANCE.clear();
GpuCulling.onWorldUnload();
BopFogBlend.invalidate();
FogBiomeCache.invalidate();
super.unloadWorld();
}
@@ -13,18 +13,24 @@ public class VertexArrayQuadView implements ModelQuadView {
private int blockX, blockY, blockZ;
private ModelQuadFacing lightFace;
private int flags;
private int extraFlags;
public VertexArrayQuadView(ChunkVertexEncoder.Vertex[] vertices) {
this.vertices = vertices;
}
public void setup(int trueNormal, int blockX, int blockY, int blockZ) {
setup(trueNormal, blockX, blockY, blockZ, 0, null);
}
public void setup(int trueNormal, int blockX, int blockY, int blockZ, int extraFlags, ModelQuadFacing forcedLightFace) {
this.trueNormal = trueNormal;
this.blockX = blockX;
this.blockY = blockY;
this.blockZ = blockZ;
this.lightFace = null; // Reset cached lightFace
this.lightFace = forcedLightFace;
this.flags = 0;
this.extraFlags = extraFlags;
}
@Override
@@ -65,7 +71,7 @@ public class VertexArrayQuadView implements ModelQuadView {
@Override
public int getFlags() {
if (flags == 0) {
flags = ModelQuadFlags.getQuadFlags(this, getLightFace());
flags = ModelQuadFlags.getQuadFlags(this, getLightFace(), extraFlags);
}
return flags;
}
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.rendering.celeritas.light;
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
import lombok.Setter;
import net.minecraft.block.Block;
import net.minecraft.world.IBlockAccess;
@@ -9,6 +10,16 @@ import org.embeddedt.embeddium.impl.model.light.data.LightDataAccess;
public class LightDataCache extends LightDataAccess {
private IBlockAccess world;
/**
* Doing this to force packs like Steadfast's water to work properly.
*/
private static int skyLight(IBlockAccess world, Block block, int x, int y, int z, int neighborMaxed) {
if (world instanceof WorldSlice slice && block.getMaterial().isLiquid()) {
return slice.getRawSkyLight(x, y, z);
}
return neighborMaxed;
}
@Override
protected int compute(int x, int y, int z) {
final IBlockAccess world = this.world;
@@ -26,7 +37,7 @@ public class LightDataCache extends LightDataAccess {
} else {
final int packed = world.getLightBrightnessForSkyBlocks(x, y, z, 0);
bl = (packed >> 4) & 0xF;
sl = (packed >> 20) & 0xF;
sl = skyLight(world, block, x, y, z, (packed >> 20) & 0xF);
}
// AO value - blocks that emit light don't contribute to AO
@@ -276,6 +276,17 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
return skyBrightness << 20 | blockBrightness << 4;
}
public int getRawSkyLight(int x, int y, int z) {
final int minHeight = ModStatus.isCubicChunksLoaded ? CubicChunksAPI.getMinHeight(this.world) : 0;
final int maxHeight = ModStatus.isCubicChunksLoaded ? CubicChunksAPI.getMaxHeight(this.world) : 256;
if (y < minHeight || y >= maxHeight || x < -30_000_000 || z < -30_000_000 || x >= 30_000_000 || z >= 30_000_000) {
return 15;
}
return this.getLightLevel(EnumSkyBlock.Sky, x, y, z);
}
private int getSkyBlockTypeBrightness(EnumSkyBlock skyBlock, int x, int y, int z) {
if (this.getBlock(x, y, z).getUseNeighborBrightness()) {
int yp = this.getLightLevel(skyBlock, x, y + 1, z);
@@ -15,6 +15,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
import com.gtnewhorizons.angelica.profiling.BailClassCounts;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
@@ -24,6 +25,7 @@ import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
import net.coderbot.batchedentityrendering.impl.TransparencyType;
import net.coderbot.iris.Iris;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.layer.PassOverride;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import net.minecraft.client.model.ModelBox;
@@ -105,22 +107,34 @@ public final class ModelPartBatcher {
private static final class LayerKey {
ResourceLocation texture;
TesrMaterial material;
PassOverride pass;
float offsetFactor;
float offsetUnits;
LayerKey set(ResourceLocation texture, TesrMaterial material) {
LayerKey set(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
this.texture = texture;
this.material = material;
this.pass = pass;
this.offsetFactor = offsetFactor;
this.offsetUnits = offsetUnits;
return this;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof LayerKey other)) return false;
return Objects.equals(texture, other.texture) && Objects.equals(material, other.material);
return Objects.equals(texture, other.texture) && Objects.equals(material, other.material)
&& Objects.equals(pass, other.pass)
&& Float.floatToIntBits(offsetFactor) == Float.floatToIntBits(other.offsetFactor)
&& Float.floatToIntBits(offsetUnits) == Float.floatToIntBits(other.offsetUnits);
}
@Override
public int hashCode() {
return Objects.hashCode(texture) * 31 + Objects.hashCode(material);
int h = (Objects.hashCode(texture) * 31 + Objects.hashCode(material)) * 31 + Objects.hashCode(pass);
h = h * 31 + Float.floatToIntBits(offsetFactor);
h = h * 31 + Float.floatToIntBits(offsetUnits);
return h;
}
}
@@ -128,6 +142,9 @@ public final class ModelPartBatcher {
private final LayerKey scratchKey = new LayerKey();
private ResourceLocation lastLayerTexture;
private TesrMaterial lastLayerMaterial;
private PassOverride lastLayerPass;
private float lastLayerOffsetFactor;
private float lastLayerOffsetUnits;
private RenderLayer lastLayer;
private static ResourceLocation lastBoundLocation;
@@ -177,7 +194,9 @@ public final class ModelPartBatcher {
if (instancedThisCycle) {
TesrBatchRenderer.INSTANCE.instancedRenderer.endFrame();
}
BatchingFontRenderer.flushDeferredText();
if (!TesrBatchRenderer.INSTANCE.hasPendingGeometry()) {
BatchingFontRenderer.flushDeferredText();
}
return;
}
final boolean inEntityLoop = mode == Mode.ENTITIES && Iris.enabled && GbufferPrograms.isEntityLoopActive();
@@ -193,6 +212,9 @@ public final class ModelPartBatcher {
if (instancedThisCycle) {
TesrBatchRenderer.INSTANCE.instancedRenderer.endFrame();
}
if (!TesrBatchRenderer.INSTANCE.hasPendingGeometry()) {
BatchingFontRenderer.flushDeferredText();
}
}
public boolean isActive() {
@@ -278,11 +300,19 @@ public final class ModelPartBatcher {
}
}
}
final RenderLayer layer = layerFor(texture, material);
final int entityId = mode == Mode.ENTITIES ? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity()) : CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
final PassOverride pass = shaderPipeline != null ? PassOverride.capture() : PassOverride.NONE;
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
final PolygonState polygon = GLStateManager.getPolygonState();
final float offsetFactor = offset ? polygon.getOffsetFactor() : 0.0f;
final float offsetUnits = offset ? polygon.getOffsetUnits() : 0.0f;
final RenderLayer layer = layerFor(texture, material, pass, offsetFactor, offsetUnits);
// Entities nested inside a TESR (mob spawner, OpenBlocks trophy) run in the block entity pass but carry an entity id
final int entityId = mode == Mode.ENTITIES || pass.isEntityPhase()
? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity())
: CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
final Color4 color = GLStateManager.getColor();
final int colorABGR = ColorABGR.pack(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
final int packedLight = ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
final int packedLight = GLSMConfig.packedLastBrightness();
final int mvGen = GLStateManager.getMvGeneration();
if (!mvCaptured || mvGen != lastMvGeneration) {
modelView.set(GLStateManager.getModelViewMatrix());
@@ -349,17 +379,21 @@ public final class ModelPartBatcher {
return entry;
}
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material) {
if (texture == lastLayerTexture && material == lastLayerMaterial) {
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
if (texture == lastLayerTexture && material == lastLayerMaterial && pass.equals(lastLayerPass)
&& offsetFactor == lastLayerOffsetFactor && offsetUnits == lastLayerOffsetUnits) {
return lastLayer;
}
RenderLayer layer = layers.get(scratchKey.set(texture, material));
RenderLayer layer = layers.get(scratchKey.set(texture, material, pass, offsetFactor, offsetUnits));
if (layer == null) {
layer = RenderLayer.tesr(texture, material);
layers.put(new LayerKey().set(texture, material), layer);
layer = RenderLayer.tesr(texture, material, pass, offsetFactor, offsetUnits);
layers.put(new LayerKey().set(texture, material, pass, offsetFactor, offsetUnits), layer);
}
lastLayerTexture = texture;
lastLayerMaterial = material;
lastLayerPass = pass;
lastLayerOffsetFactor = offsetFactor;
lastLayerOffsetUnits = offsetUnits;
lastLayer = layer;
return layer;
}
@@ -12,6 +12,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
@@ -19,6 +20,7 @@ import net.coderbot.batchedentityrendering.impl.SegmentedBufferBuilder;
import net.coderbot.batchedentityrendering.impl.TransparencyType;
import net.coderbot.iris.Iris;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.layer.PassOverride;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
@@ -129,13 +131,23 @@ public final class TesrBatchRenderer {
return activePass >= 0;
}
public boolean hasPendingGeometry() {
return activePass >= 0 || deferredFlushPending;
}
public void queue(TemplateBuffer template, ResourceLocation texture, TesrMaterial material) {
modelView.set(GLStateManager.getModelViewMatrix());
final int packedLight = currentPackedLight(material);
final int colorABGR = resolveColor(material);
if (activePass >= 0) {
final RenderLayer layer = layerFor(texture, material);
final int blockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
final PolygonState polygon = GLStateManager.getPolygonState();
final PassOverride pass = PassOverride.capture();
final RenderLayer layer = layerFor(texture, material, pass,
offset ? polygon.getOffsetFactor() : 0.0f, offset ? polygon.getOffsetUnits() : 0.0f);
final int blockEntityId = pass.isEntityPhase()
? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity())
: CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
retained[activePass].queue(template, layer, material, modelView, packedLight, colorABGR, blockEntityId, captureTextureMatrix(), false);
} else {
drawImmediate(template, texture, material, packedLight, colorABGR);
@@ -158,8 +170,12 @@ public final class TesrBatchRenderer {
boolean depthEqual;
TesrMaterial.SpecialRender special;
TesrShader shader;
boolean noPass;
PassOverride pass;
float offsetFactor;
float offsetUnits;
LayerKey set(ResourceLocation texture, TesrMaterial.Transparency transparency, boolean noCull, boolean unlit, boolean noDepthWrite, boolean depthOnly, float cutoutAlpha, boolean depthEqual, TesrMaterial.SpecialRender special, TesrShader shader) {
LayerKey set(ResourceLocation texture, TesrMaterial.Transparency transparency, boolean noCull, boolean unlit, boolean noDepthWrite, boolean depthOnly, float cutoutAlpha, boolean depthEqual, TesrMaterial.SpecialRender special, TesrShader shader, boolean noPass, PassOverride pass, float offsetFactor, float offsetUnits) {
this.texture = texture;
this.transparency = transparency;
this.noCull = noCull;
@@ -170,11 +186,15 @@ public final class TesrBatchRenderer {
this.depthEqual = depthEqual;
this.special = special;
this.shader = shader;
this.noPass = noPass;
this.pass = pass;
this.offsetFactor = offsetFactor;
this.offsetUnits = offsetUnits;
return this;
}
LayerKey copy() {
return new LayerKey().set(texture, transparency, noCull, unlit, noDepthWrite, depthOnly, cutoutAlpha, depthEqual, special, shader);
return new LayerKey().set(texture, transparency, noCull, unlit, noDepthWrite, depthOnly, cutoutAlpha, depthEqual, special, shader, noPass, pass, offsetFactor, offsetUnits);
}
@Override
@@ -184,7 +204,10 @@ public final class TesrBatchRenderer {
&& noCull == other.noCull && unlit == other.unlit && noDepthWrite == other.noDepthWrite
&& depthOnly == other.depthOnly
&& Float.floatToIntBits(cutoutAlpha) == Float.floatToIntBits(other.cutoutAlpha)
&& depthEqual == other.depthEqual && special == other.special && shader == other.shader;
&& depthEqual == other.depthEqual && special == other.special && shader == other.shader && noPass == other.noPass
&& Objects.equals(pass, other.pass)
&& Float.floatToIntBits(offsetFactor) == Float.floatToIntBits(other.offsetFactor)
&& Float.floatToIntBits(offsetUnits) == Float.floatToIntBits(other.offsetUnits);
}
@Override
@@ -199,6 +222,10 @@ public final class TesrBatchRenderer {
h = h * 31 + (depthEqual ? 1 : 0);
h = h * 31 + Objects.hashCode(special);
h = h * 31 + System.identityHashCode(shader);
h = h * 31 + (noPass ? 1 : 0);
h = h * 31 + Objects.hashCode(pass);
h = h * 31 + Float.floatToIntBits(offsetFactor);
h = h * 31 + Float.floatToIntBits(offsetUnits);
return h;
}
}
@@ -207,24 +234,45 @@ public final class TesrBatchRenderer {
private final LayerKey scratchKey = new LayerKey();
private ResourceLocation lastLayerTexture;
private TesrMaterial lastLayerMaterial;
private PassOverride lastLayerPass;
private float lastLayerOffsetFactor;
private float lastLayerOffsetUnits;
private RenderLayer lastLayer;
private ResourceLocation lastImmediateTexture;
private TesrMaterial lastImmediateMaterial;
private RenderLayer lastImmediateLayer;
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material) {
if (texture == lastLayerTexture && material == lastLayerMaterial) {
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
if (texture == lastLayerTexture && material == lastLayerMaterial && pass.equals(lastLayerPass)
&& offsetFactor == lastLayerOffsetFactor && offsetUnits == lastLayerOffsetUnits) {
return lastLayer;
}
final RenderLayer layer = layerLookup(texture, material);
final RenderLayer layer = layerLookup(texture, material, false, pass, offsetFactor, offsetUnits);
lastLayerTexture = texture;
lastLayerMaterial = material;
lastLayerPass = pass;
lastLayerOffsetFactor = offsetFactor;
lastLayerOffsetUnits = offsetUnits;
lastLayer = layer;
return layer;
}
private RenderLayer layerLookup(ResourceLocation texture, TesrMaterial material) {
final LayerKey key = scratchKey.set(texture, material.transparency(), material.isNoCull(), material.isUnlit(), material.isNoDepthWrite(), material.isDepthOnly(), material.cutoutAlpha(), material.isDepthEqual(), material.special(), material.shader());
private RenderLayer noPassLayerFor(ResourceLocation texture, TesrMaterial material) {
if (texture == lastImmediateTexture && material == lastImmediateMaterial) {
return lastImmediateLayer;
}
final RenderLayer layer = layerLookup(texture, material, true, PassOverride.NONE, 0.0f, 0.0f);
lastImmediateTexture = texture;
lastImmediateMaterial = material;
lastImmediateLayer = layer;
return layer;
}
private RenderLayer layerLookup(ResourceLocation texture, TesrMaterial material, boolean noPass, PassOverride pass, float offsetFactor, float offsetUnits) {
final LayerKey key = scratchKey.set(texture, material.transparency(), material.isNoCull(), material.isUnlit(), material.isNoDepthWrite(), material.isDepthOnly(), material.cutoutAlpha(), material.isDepthEqual(), material.special(), material.shader(), noPass, pass, offsetFactor, offsetUnits);
RenderLayer layer = layers.get(key);
if (layer == null) {
layer = RenderLayer.tesr(texture, material);
layer = noPass ? RenderLayer.tesrNoPass(texture, material) : RenderLayer.tesr(texture, material, pass, offsetFactor, offsetUnits);
layers.put(key.copy(), layer);
}
return layer;
@@ -232,9 +280,9 @@ public final class TesrBatchRenderer {
private static int currentPackedLight(TesrMaterial material) {
if (material.hasLightmap()) {
return ((int) material.lightmapY() << 16) | ((int) material.lightmapX() & 0xFFFF);
return GLSMConfig.packBrightness(material.lightmapX(), material.lightmapY());
}
return ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
return GLSMConfig.packedLastBrightness();
}
private static int resolveColor(TesrMaterial material) {
@@ -290,6 +338,7 @@ public final class TesrBatchRenderer {
try {
final EntityRenderer entityRenderer = Minecraft.getMinecraft().entityRenderer;
final boolean savedDepthMask = GLStateManager.getDepthState().isEnabled();
final boolean savedDepthTest = GLStateManager.getDepthTest().isEnabled();
final boolean savedBlend = GLStateManager.getBlendMode().isEnabled();
final int savedSrcRgb = GLStateManager.getBlendState().getSrcRgb();
final int savedDstRgb = GLStateManager.getBlendState().getDstRgb();
@@ -312,11 +361,12 @@ public final class TesrBatchRenderer {
bufferSource.endBatch(hook);
instancedRenderer.endFrame();
AngelicaBufferSource.rebindPass();
BatchingFontRenderer.flushDeferredText();
BatchingFontRenderer.flushDeferredText();
} finally {
if (wrap) GbufferPrograms.endBlockEntities();
entityRenderer.disableLightmap(0);
GLStateManager.glDepthMask(savedDepthMask);
if (savedDepthTest) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
if (savedBlend) GLStateManager.enableBlend(); else GLStateManager.disableBlend();
GLStateManager.tryBlendFuncSeparate(savedSrcRgb, savedDstRgb, savedSrcAlpha, savedDstAlpha);
if (savedAlphaTest) GLStateManager.enableAlphaTest(); else GLStateManager.disableAlphaTest();
@@ -452,7 +502,7 @@ public final class TesrBatchRenderer {
final long end = VertexTransform.writeInstance(addr, format, template, identity, scratchVec, colorABGR, packedLight, null);
immediateScratch.position((int) (end - addr));
immediateScratch.flip();
final RenderLayer layer = layerFor(texture, material);
final RenderLayer layer = noPassLayerFor(texture, material);
GLStateManager.glPushAttrib(AngelicaBufferSource.SAVED_STATE_BITS);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
layer.startDrawing();
@@ -0,0 +1,48 @@
package com.gtnewhorizons.angelica.utils;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import java.util.Set;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
public class NaturalTextureUtils {
// Yes this is supposed to be volatile
private volatile static Set<Block> cachedWhitelistedBlocks = null;
public static int getTopRotation(int x, int y, int z) {
long hash = (x * 3129871L) ^ (z * 116586811L) ^ y;
hash = hash * hash * 42317861L + hash * 11L;
return (int) (hash & 3);
}
// This method gets called from worker threads
public static boolean isNaturalBlock(Block block) {
if (cachedWhitelistedBlocks == null) {
// If list is empty add back all the stuff from config
initCache();
}
return cachedWhitelistedBlocks.contains(block);
}
// On reload clear the list
public static void reloadCache() {
cachedWhitelistedBlocks = null;
}
private static void initCache() {
Set<Block> set = new ReferenceOpenHashSet<>();
if (AngelicaConfig.naturalTextureBlocks != null) {
for (String name : AngelicaConfig.naturalTextureBlocks) {
Block b = (Block) Block.blockRegistry.getObject(name);
if (b != null && b != Blocks.air) {
set.add(b);
}
}
}
cachedWhitelistedBlocks = set;
}
}
@@ -29,7 +29,7 @@ public class Zoom {
private static long zoomLerpLastNano = 0L;
@Getter
private static final KeyBinding zoomKey = new KeyBinding("Zoom", 0, "key.categories.misc");
private static final KeyBinding zoomKey = new KeyBinding("angelica.keybind.zoom", 0, "key.categories.misc");
private static boolean zoomEnabled = false;
@@ -71,7 +71,7 @@ public class CTMUtils {
}
public static void clearCurrentCompact() {
CURRENT_COMPACT.remove();
CURRENT_COMPACT.set(null);
}
private static final StampedLock lock = new StampedLock();
@@ -98,6 +98,7 @@ public class TileLoader {
logger.severe("beforeChange was not called, invoking directly");
changeHandler.beforeChange();
}
long currentSize = Long.MIN_VALUE;
for (TileLoader loader : loaders) {
if (loader.isForThisMap(mapName)) {
if (loader.baseTextureMap == null) {
@@ -105,10 +106,13 @@ public class TileLoader {
loader.baseTexturesByName.putAll(map);
}
if (!loader.tilesToRegister.isEmpty()) {
if (currentSize == Long.MIN_VALUE) currentSize = getTextureSize(map.values());
loader.subLogger
.fine("adding icons to %s (%d remaining)", mapName, loader.tilesToRegister.size(), mapName);
while (!loader.tilesToRegister.isEmpty() && loader.registerOneIcon(textureMap, mapName, map)) {
// nothing
while (!loader.tilesToRegister.isEmpty()) {
long updatedSize = loader.registerOneIcon(textureMap, mapName, map, currentSize);
if (updatedSize < 0) break;
currentSize = updatedSize;
}
loader.subLogger.fine(
"done adding icons to %s (%d remaining)",
@@ -275,42 +279,44 @@ public class TileLoader {
return false;
}
protected boolean registerOneIcon(TextureMap textureMap, String mapName, Map<String, TextureAtlasSprite> map) {
protected long registerOneIcon(TextureMap textureMap, String mapName, Map<String, TextureAtlasSprite> map,
long currentSize) {
ResourceLocation resource = tilesToRegister.iterator()
.next();
String name = resource.toString();
if (registerDefaultIcon(name)) {
tilesToRegister.remove(resource);
return true;
return currentSize;
}
BufferedImage image = tileImages.get(resource);
if (image == null) {
subLogger.error("tile for %s unexpectedly missing", resource);
tilesToRegister.remove(resource);
return true;
return currentSize;
}
int width = image.getWidth();
int height = image.getHeight();
long currentSize = getTextureSize(map.values());
long newSize = 4 * width * width;
if (newSize + currentSize > MAX_TILESHEET_SIZE) {
float sizeMB = (float) currentSize / 1048576.0f;
if (currentSize <= 0) {
subLogger.error("%s too big for any tilesheet (%.1fMB), dropping", name, sizeMB);
tilesToRegister.remove(resource);
return true;
return currentSize;
} else {
subLogger.warning("%s nearly full (%.1fMB), will start a new tilesheet", mapName, sizeMB);
return false;
return -1;
}
}
TextureAtlasSprite previous = map.get(name);
TextureAtlasSprite icon = registerIconToMap(textureMap, resource, name);
map.put(name, icon);
currentSize += getTextureSize(icon) - getTextureSize(previous);
iconMap.put(name, icon);
String extra = (width == height ? "" : ", " + (height / width) + " frames");
subLogger.finer("%s -> %s icon %dx%d%s", name, mapName, width, width, extra);
tilesToRegister.remove(resource);
return true;
return currentSize;
}
protected TextureAtlasSprite registerIconToMap(TextureMap textureMap, ResourceLocation resource, String name) {
@@ -12,9 +12,7 @@ public class NotFineConfig {
private static final String CATEGORY_TOGGLE = "toggle";
private static final String CATEGORY_UNFINISHED = "unfinished";
public static boolean allowAdvancedOpenGL;
public static boolean allowToggle3DAnaglyph;
public static boolean allowToggleFBO;
public static boolean betterBlockFaceCulling;
public static boolean renderPass;
@@ -23,12 +21,7 @@ public class NotFineConfig {
File configFile = new File(Launch.minecraftHome + File.separator + "config" + File.separator + NotFine.MODID + File.separator + "notfine.cfg");
Configuration config = new Configuration(configFile);
allowAdvancedOpenGL = config.getBoolean("allowAdvancedOpenGL", CATEGORY_GENERAL, false,
"Allow Advanced OpenGL to be enabled when it might be supported.");
allowToggle3DAnaglyph = config.getBoolean("allowToggle3DAnaglyph", CATEGORY_GENERAL, true,
"Allow 3D Anaglyph to be enabled.");
allowToggleFBO = config.getBoolean("allowToggleFBO", CATEGORY_GENERAL, false,
"Allow FBOs to be disabled.");
allowToggle3DAnaglyph = config.getBoolean("allowToggle3DAnaglyph", CATEGORY_GENERAL, true, "Allow 3D Anaglyph to be enabled.");
config.setCategoryComment(CATEGORY_TOGGLE, "Toggle mod features.");
betterBlockFaceCulling = config.getBoolean("betterBlockFaceCulling", CATEGORY_TOGGLE, true,
@@ -43,10 +36,7 @@ public class NotFineConfig {
config.save();
}
//These will probably break hard with Angelica.
//AdvancedOpenGL even breaks hard on its own.
allowAdvancedOpenGL = false;
allowToggleFBO = false;
//This will probably break hard with Angelica.
renderPass = false;
}
@@ -243,21 +243,6 @@ public class NotFineGameOptionPages {
List<OptionGroup> groups = new ArrayList<>();
groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
.setName(I18n.format("options.advancedOpengl"))
.setTooltip(I18n.format("sodium.options.advanced_opengl.tooltip"))
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> opts.advancedOpengl = value, opts -> opts.advancedOpengl)
.setImpact(OptionImpact.VARIES)
.setEnabled(NotFineConfig.allowAdvancedOpenGL)
.build())
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
.setName(I18n.format("options.fboEnable"))
.setTooltip(I18n.format("sodium.options.fbo.tooltip"))
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> opts.fboEnable = value, opts -> opts.fboEnable)
.setEnabled(NotFineConfig.allowToggleFBO)
.build())
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
.setName(I18n.format("options.anaglyph"))
.setTooltip(I18n.format("sodium.options.anaglyph.tooltip"))
@@ -18,15 +18,9 @@ public class ClientProxy extends CommonProxy {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
if(!NotFineConfig.allowAdvancedOpenGL) {
Minecraft.getMinecraft().gameSettings.advancedOpengl = false;
}
if(!NotFineConfig.allowToggle3DAnaglyph) {
Minecraft.getMinecraft().gameSettings.anaglyph = false;
}
if(!NotFineConfig.allowToggleFBO) {
Minecraft.getMinecraft().gameSettings.fboEnable = true;
}
for(Settings setting : Settings.values()) {
setting.ready();
@@ -105,9 +105,9 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
final Dim2i applyButtonDim = new Dim2i(tabFrameDim.getLimitX() - 134, tabFrameDim.getLimitY() + 5, 65, 20);
final Dim2i closeButtonDim = new Dim2i(tabFrameDim.getLimitX() - 65, tabFrameDim.getLimitY() + 5, 65, 20);
this.undoButton = new FlatButtonWidget(undoButtonDim, "Undo", this::undoChanges);
this.applyButton = new FlatButtonWidget(applyButtonDim, "Apply", this::applyChanges);
this.closeButton = new FlatButtonWidget(closeButtonDim, "Close", this::onClose);
this.undoButton = new FlatButtonWidget(undoButtonDim, I18n.format("sodium.options.buttons.undo"), this::undoChanges);
this.applyButton = new FlatButtonWidget(applyButtonDim, I18n.format("sodium.options.buttons.apply"), this::applyChanges);
this.closeButton = new FlatButtonWidget(closeButtonDim, I18n.format("gui.done"), this::onClose);
// Pre-compute button text and widths to avoid duplicate calculations
final String irisText = Iris.enabled ? I18n.format(IrisApi.getInstance().getMainScreenLanguageKey()) : null;
@@ -202,16 +202,7 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
}
private void updateControls() {
boolean hasChanges = this.getAllOptions()
.anyMatch(Option::hasChanged);
for (OptionPage page : this.pages) {
for (Option<?> option : page.getOptions()) {
if (option.hasChanged()) {
hasChanges = true;
}
}
}
final boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
this.applyButton.setEnabled(hasChanges);
this.undoButton.setVisible(hasChanges);
@@ -93,11 +93,7 @@ public class SearchTextFieldComponent extends AbstractWidget {
return;
}
if (!this.isFocused() && this.text.isEmpty()) {
final String key = "rso.search_bar_empty";
String text = I18n.format(key);
if (text.equals(key))
text = "Search options...";
this.textRenderer.drawString(text, this.dim.getOriginX() + 6, this.dim.getOriginY() + 6, 0xFFAAAAAA);
this.textRenderer.drawString(I18n.format("rso.search_bar_empty"), this.dim.getOriginX() + 6, this.dim.getOriginY() + 6, 0xFFAAAAAA);
}
drawRect(this.dim.getOriginX(), this.dim.getOriginY(), this.dim.getLimitX(), this.dim.getLimitY(), this.isFocused() ? 0xE0000000 : 0x90000000);
@@ -11,8 +11,6 @@ import net.minecraft.client.resources.I18n;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.util.MathHelper;
import org.lwjgl.opengl.Display;
import com.cardinalstar.cubicchunks.api.compat.CubicChunksVideoSettings;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.compat.ModStatus;
@@ -21,7 +19,6 @@ import com.gtnewhorizons.angelica.rendering.celeritas.MultiDrawModeResolver;
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
import com.gtnewhorizons.angelica.config.GpuCullingMode;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import cpw.mods.fml.common.Optional.Method;
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
import jss.notfine.core.Settings;
@@ -48,6 +45,7 @@ import me.jellysquid.mods.sodium.client.gui.options.storage.MinecraftOptionsStor
import me.jellysquid.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gui.option.IrisVideoSettings;
import net.coderbot.iris.pipeline.ShadowRenderer;
import org.embeddedt.embeddium.impl.render.chunk.occlusion.AsyncOcclusionMode;
public class SodiumGameOptionPages {
@@ -305,9 +303,10 @@ public class SodiumGameOptionPages {
.add(OptionImpl.createBuilder(StreamingUploader.UploadStrategy.class, sodiumOpts)
.setName(I18n.format("sodium.options.upload_method.name"))
.setTooltip(I18n.format("sodium.options.upload_method.tooltip"))
.setControl(o ->
new CyclingControl<>(o, StreamingUploader.UploadStrategy.class, StreamingUploader.UploadStrategy.values())
)
.setControl(o -> new CyclingControl<>(o, StreamingUploader.UploadStrategy.class, new String[]{
I18n.format("sodium.options.upload_method.buffer_data"),
I18n.format("sodium.options.upload_method.buffer_subdata"),
I18n.format("sodium.options.upload_method.map_buffer") }))
.setBinding((opts, value) -> opts.advanced.streamingUploadStrategy = value, opts -> opts.advanced.streamingUploadStrategy)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.setImpact(OptionImpact.VARIES)
@@ -411,7 +410,10 @@ public class SodiumGameOptionPages {
.add(OptionImpl.createBuilder(AsyncOcclusionMode.class, sodiumOpts)
.setName(I18n.format("sodium.options.async_occlusion_mode.name"))
.setTooltip(I18n.format("sodium.options.async_occlusion_mode.tooltip"))
.setControl(o -> new CyclingControl<>(o, AsyncOcclusionMode.class, new String[]{"None", "Only Shadow", "Everything"}))
.setControl(o -> new CyclingControl<>(o, AsyncOcclusionMode.class, new String[]{
I18n.format("sodium.options.async_occlusion_mode.none"),
I18n.format("sodium.options.async_occlusion_mode.only_shadow"),
I18n.format("sodium.options.async_occlusion_mode.everything") }))
.setImpact(OptionImpact.MEDIUM)
.setBinding((opts, value) -> opts.performance.asyncOcclusionMode = value, opts -> opts.performance.asyncOcclusionMode)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
@@ -519,6 +521,36 @@ public class SodiumGameOptionPages {
.build());
if (Iris.enabled) {
final OptionImpl<AngelicaConfig, Integer> shadowGraphAngleDelta =
OptionImpl.createBuilder(int.class, angelicaOpts)
.setName(I18n.format("options.angelica.shadowGraphAngleDelta"))
.setTooltip(I18n.format("options.angelica.shadowGraphAngleDelta.tooltip"))
.setControl(o -> new SliderControl(o, 0, 100, 1, ControlValueFormatter.shadowGraphAngleDelta()))
.setImpact(OptionImpact.MEDIUM)
.setBinding((opts, value) -> AngelicaConfig.shadowGraphAngleDelta = value * 0.001f,
opts -> Math.round(AngelicaConfig.shadowGraphAngleDelta * 1000.0f))
.build();
shadowGraphAngleDelta.iris$dynamicallyEnable(ShadowRenderer::shadowGraphGateIsNotBypassed);
final OptionImpl<AngelicaConfig, Integer> shadowGraphHorizonScale =
OptionImpl.createBuilder(int.class, angelicaOpts)
.setName(I18n.format("options.angelica.shadowGraphHorizonScale"))
.setTooltip(I18n.format("options.angelica.shadowGraphHorizonScale.tooltip"))
.setControl(o -> new SliderControl(o, 0, 100, 1, ControlValueFormatter.shadowGraphHorizonScale()))
.setImpact(OptionImpact.LOW)
.setBinding((opts, value) -> AngelicaConfig.shadowGraphHorizonScale = value * 0.01f,
opts -> Math.round(AngelicaConfig.shadowGraphHorizonScale * 100.0f))
.build();
shadowGraphHorizonScale.iris$dynamicallyEnable(
() -> ShadowRenderer.shadowGraphGateIsNotBypassed() && shadowGraphAngleDelta.getValue() != 0);
groups.add(OptionGroup.createBuilder()
.add(shadowGraphAngleDelta)
.add(shadowGraphHorizonScale)
.build());
}
groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName(I18n.format("options.angelica.hudCaching"))
@@ -54,7 +54,6 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
protected OptionPage shaderPacks;
public SodiumOptionsGUI(GuiScreen prevScreen) {
this.prevScreen = prevScreen;
@@ -67,10 +66,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
// Add Dynamic Lights page if enabled
if (DynamicLights.configEnabled) {
var dlPage = DynamicLightsOptionPages.dynamicLights();
if (dlPage != null) {
this.pages.add(dlPage);
}
this.pages.add(DynamicLightsOptionPages.dynamicLights());
}
if (Iris.enabled) {
@@ -194,15 +190,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
.findFirst()
.orElse(null);
boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
for (OptionPage page : this.pages) {
for (Option<?> option : page.getOptions()) {
if (option.hasChanged()) {
hasChanges = true;
}
}
}
final boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
this.applyButton.setEnabled(hasChanges);
this.undoButton.setVisible(hasChanges);
@@ -0,0 +1,149 @@
package me.jellysquid.mods.sodium.client.gui.options;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import me.jellysquid.mods.sodium.client.gui.options.control.Control;
import me.jellysquid.mods.sodium.client.gui.options.control.ControlElement;
import me.jellysquid.mods.sodium.client.gui.options.control.element.ControlElementFactory;
import me.jellysquid.mods.sodium.client.gui.options.control.element.SodiumControlElement;
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
import me.jellysquid.mods.sodium.client.util.Dim2i;
/** "Option" that opens a sub screen */
public class SubScreenOption implements Option<Void> {
private static final int ARROW_HOVERED = 0xFF94E4D3;
private static final int ARROW_IDLE = 0xFFAAAAAA;
private static final String ARROW = ">";
private static final OptionStorage<Void> NO_STORAGE = new OptionStorage<Void>() {
@Override
public Void getData() {
return null;
}
@Override
public void save() {}
};
private final String name;
private final String tooltip;
private final Function<GuiScreen, GuiScreen> destination;
private final Control<Void> control = new SubScreenControl(this);
public SubScreenOption(String name, String tooltip, Function<GuiScreen, GuiScreen> destination) {
this.name = name;
this.tooltip = tooltip;
this.destination = destination;
}
private void open() {
final var mc = Minecraft.getMinecraft();
mc.displayGuiScreen(this.destination.apply(mc.currentScreen));
}
@Override
public String getName() {
return this.name;
}
@Override
public String getTooltip() {
return this.tooltip;
}
@Override
public OptionImpact getImpact() {
return null;
}
@Override
public Control<Void> getControl() {
return this.control;
}
@Override
public Void getValue() {
return null;
}
@Override
public void setValue(Void value) {}
@Override
public void reset() {}
@Override
public OptionStorage<?> getStorage() {
return NO_STORAGE;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public boolean hasChanged() {
return false;
}
@Override
public void applyChanges() {}
@Override
public Collection<OptionFlag> getFlags() {
return Collections.emptySet();
}
private record SubScreenControl(SubScreenOption target) implements Control<Void> {
@Override
public Option<Void> getOption() {
return this.target;
}
@Override
public ControlElement<Void> createElement(Dim2i dim, ControlElementFactory factory) {
return new SubScreenElement(this.target, dim);
}
@Override
public int getMaxWidth() {
return 30;
}
}
private static class SubScreenElement extends SodiumControlElement<Void> {
private final SubScreenOption target;
SubScreenElement(SubScreenOption option, Dim2i dim) {
super(option, dim);
this.target = option;
}
@Override
public void render(int mouseX, int mouseY, float delta) {
super.render(mouseX, mouseY, delta);
final var width = this.font.getStringWidth(ARROW);
this.drawString(ARROW, this.dim.getLimitX() - width - 6, this.dim.getCenterY() - 4, this.hovered ? ARROW_HOVERED : ARROW_IDLE);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (button != 0 || !this.dim.containsCursor(mouseX, mouseY)) return false;
this.playClickSound();
this.target.open();
return true;
}
}
}
@@ -2,6 +2,8 @@ package me.jellysquid.mods.sodium.client.gui.options.control;
import net.minecraft.client.resources.I18n;
import java.util.Locale;
public interface ControlValueFormatter {
static ControlValueFormatter guiScale() {
return (v) -> (v == 0) ? I18n.format("options.guiScale.auto") : I18n.format(v + "x");
@@ -47,4 +49,14 @@ public interface ControlValueFormatter {
static ControlValueFormatter number() {
return String::valueOf;
}
static ControlValueFormatter shadowGraphAngleDelta() {
return (v) -> v == 0
? I18n.format("options.angelica.shadowGraphAngleDelta.always")
: String.format(Locale.ROOT, "%.3f°", v * 0.001f);
}
static ControlValueFormatter shadowGraphHorizonScale() {
return (v) -> v == 100 ? I18n.format("options.angelica.shadowGraphHorizonScale.off") : v + "%";
}
}
@@ -6,6 +6,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.coderbot.batchedentityrendering.impl.ordering.SimpleRenderOrderManager;
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.coderbot.iris.uniforms.CapturedRenderingState;
import org.lwjgl.opengl.GL11;
@@ -159,15 +161,17 @@ public class AngelicaBufferSource implements Groupable {
private void clearCurrentId() {
if (!anyIdSet) return;
if (idKind == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntity(-1);
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0f, 0f, 0f, 0f);
} else {
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
}
// A pass can set either kind: nested entities inside a TESR write the entity id during a block entity pass
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(-1, 0);
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0f, 0f, 0f, 0f);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
anyIdSet = false;
}
private GroupIdKind effectiveIdKind() {
return GbufferPrograms.getCurrentPhase() == WorldRenderingPhase.ENTITIES ? GroupIdKind.ENTITY : idKind;
}
private void drawLayer(RenderLayer layer, LayerDrawHook hook) {
final List<BufferSegment> segments = typeToSegment.get(layer);
final boolean hasDynamic = segments != null && !segments.isEmpty();
@@ -184,7 +188,7 @@ public class AngelicaBufferSource implements Groupable {
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
SegmentedBufferBuilder.LayerBuffer bound = null;
final boolean entityKind = idKind == GroupIdKind.ENTITY;
final boolean entityKind = effectiveIdKind() == GroupIdKind.ENTITY;
int currentId = Integer.MIN_VALUE;
int currentColor = 0;
boolean colorSet = false;
@@ -271,8 +275,8 @@ public class AngelicaBufferSource implements Groupable {
public void applyIdAndRebind(int id) {
anyIdSet = true;
if (idKind == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntity(id);
if (effectiveIdKind() == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(id, 0);
rebindPass();
} else {
setBlockEntityAndRebind(id);
@@ -281,8 +285,8 @@ public class AngelicaBufferSource implements Groupable {
public void applyIdNoRebind(int id) {
anyIdSet = true;
if (idKind == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntity(id);
if (effectiveIdKind() == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(id, 0);
} else {
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(id);
}
+12 -6
View File
@@ -54,6 +54,7 @@ import net.minecraft.launchwrapper.Launch;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.input.Keyboard;
import java.io.IOException;
@@ -342,11 +343,11 @@ public class Iris {
final Minecraft mc = Minecraft.getMinecraft();
try {
reload();
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText("Shaders Reloaded!"));
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.reloaded")));
} catch (Exception e) {
logger.error("Error while reloading Shaders for Iris!", e);
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo reload shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.reloaded.failure", Throwables.getRootCause(e).getMessage())));
}
} else if (toggleShadersKeybind.isPressed()) {
final Minecraft mc = Minecraft.getMinecraft();
@@ -355,7 +356,7 @@ public class Iris {
} catch (Exception e) {
logger.error("Error while toggling shaders!", e);
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo toggle shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.toggled.failure", Throwables.getRootCause(e).getMessage())));
setShadersDisabled();
fallback = true;
}
@@ -919,6 +920,11 @@ public class Iris {
return pipelineManager;
}
@Nullable
public static PipelineManager getPipelineManagerNullable() {
return pipelineManager;
}
@NotNull
public static Optional<ShaderPack> getCurrentPack() {
return Optional.ofNullable(currentPack);
@@ -968,9 +974,9 @@ public class Iris {
public void fmlInitEvent() {
IRIS_VERSION = Tags.VERSION;
reloadKeybind = new KeyBinding("Reload Shaders", 0, "Iris Keybinds");
toggleShadersKeybind = new KeyBinding("Toggle Shaders", 0, "Iris Keybinds");
shaderpackScreenKeybind = new KeyBinding("Shaderpack Selection Screen", 0, "Iris Keybinds");
reloadKeybind = new KeyBinding("iris.keybind.reload", 0, "key.category.iris.keybinds");
toggleShadersKeybind = new KeyBinding("iris.keybind.toggleShaders", 0, "key.category.iris.keybinds");
shaderpackScreenKeybind = new KeyBinding("iris.keybind.shaderPackSelection", 0, "key.category.iris.keybinds");
ClientRegistry.registerKeyBinding(reloadKeybind);
ClientRegistry.registerKeyBinding(toggleShadersKeybind);
@@ -69,13 +69,17 @@ public class CeleritasTerrainPipeline {
IntFunction<ProgramImages> createTerrainImages,
IntFunction<ProgramImages> createShadowImages,
CustomUniforms customUniforms,
Optional<ProgramSource> terrainSource,
Optional<ProgramSource> terrainSolidSource,
Optional<ProgramSource> terrainCutoutSource,
Optional<ProgramSource> translucentSource,
Optional<ProgramSource> shadowSource,
Optional<ProgramSource> shadowSolidSource,
Optional<ProgramSource> shadowCutoutSource,
Optional<ProgramSource> shadowTranslucentSource,
CompletableFuture<Map<PatchShaderType, String>> terrainFuture,
CompletableFuture<Map<PatchShaderType, String>> terrainSolidFuture,
CompletableFuture<Map<PatchShaderType, String>> terrainCutoutFuture,
CompletableFuture<Map<PatchShaderType, String>> translucentFuture,
CompletableFuture<Map<PatchShaderType, String>> shadowFuture,
CompletableFuture<Map<PatchShaderType, String>> shadowSolidFuture,
CompletableFuture<Map<PatchShaderType, String>> shadowCutoutFuture,
CompletableFuture<Map<PatchShaderType, String>> shadowTranslucentFuture,
RenderTargets renderTargets,
ImmutableSet<Integer> flippedAfterPrepare,
@@ -90,11 +94,12 @@ public class CeleritasTerrainPipeline {
// Build program source map (local - only needed during construction)
final EnumMap<IrisTerrainPass, Optional<ProgramSource>> gbufferProgramSource = new EnumMap<>(IrisTerrainPass.class);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_SOLID, terrainSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_CUTOUT, terrainSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_TRANSLUCENT, translucentSource.isPresent() ? translucentSource : terrainSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW, shadowSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW_TRANSLUCENT, shadowTranslucentSource.isPresent() ? shadowTranslucentSource : shadowSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_SOLID, terrainSolidSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_CUTOUT, terrainCutoutSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_TRANSLUCENT, translucentSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW, shadowSolidSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW_CUTOUT, shadowCutoutSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW_TRANSLUCENT, shadowTranslucentSource);
// Initialize PassInfo, framebuffers, blend modes, and alpha in single pass
for (IrisTerrainPass pass : IrisTerrainPass.VALUES) {
@@ -103,10 +108,12 @@ public class CeleritasTerrainPipeline {
// Set up framebuffer, blend mode, and buffer blend overrides
final ProgramId programId = switch (pass) {
case GBUFFER_SOLID -> ProgramId.TerrainSolid;
case GBUFFER_CUTOUT -> ProgramId.TerrainCutout;
case GBUFFER_TRANSLUCENT -> ProgramId.Water;
case SHADOW_TRANSLUCENT -> ProgramId.ShadowWater;
case SHADOW, SHADOW_CUTOUT -> ProgramId.Shadow;
default -> ProgramId.Terrain;
case SHADOW -> ProgramId.ShadowSolid;
case SHADOW_CUTOUT -> ProgramId.ShadowCutout;
};
if (pass.isShadow()) {
@@ -146,10 +153,12 @@ public class CeleritasTerrainPipeline {
}
// Process and apply shader sources
processShaderFuture(terrainFuture, terrainSource, passInfoMap.get(IrisTerrainPass.GBUFFER_SOLID), passInfoMap.get(IrisTerrainPass.GBUFFER_CUTOUT));
processShaderFuture(terrainSolidFuture, terrainSolidSource, passInfoMap.get(IrisTerrainPass.GBUFFER_SOLID));
processShaderFuture(terrainCutoutFuture, terrainCutoutSource, passInfoMap.get(IrisTerrainPass.GBUFFER_CUTOUT));
processShaderFuture(translucentFuture, translucentSource, passInfoMap.get(IrisTerrainPass.GBUFFER_TRANSLUCENT));
processShaderFuture(shadowFuture, shadowSource, passInfoMap.get(IrisTerrainPass.SHADOW), passInfoMap.get(IrisTerrainPass.SHADOW_CUTOUT));
processShaderFuture(shadowTranslucentFuture, shadowTranslucentSource.isPresent() ? shadowTranslucentSource : shadowSource, passInfoMap.get(IrisTerrainPass.SHADOW_TRANSLUCENT));
processShaderFuture(shadowSolidFuture, shadowSolidSource, passInfoMap.get(IrisTerrainPass.SHADOW));
processShaderFuture(shadowCutoutFuture, shadowCutoutSource, passInfoMap.get(IrisTerrainPass.SHADOW_CUTOUT));
processShaderFuture(shadowTranslucentFuture, shadowTranslucentSource, passInfoMap.get(IrisTerrainPass.SHADOW_TRANSLUCENT));
}
private void processShaderFuture(@Nullable CompletableFuture<Map<PatchShaderType, String>> future, Optional<ProgramSource> source, PassInfo... targets) {
@@ -1,22 +0,0 @@
package net.coderbot.iris.fantastic;
// TODO: RenderType
//public class IrisParticleRenderTypes {
// public static final ParticleRenderType OPAQUE_TERRAIN = new ParticleRenderType() {
// public void begin(BufferBuilder bufferBuilder, TextureManager textureManager) {
// GL11.glDisable(GL11.GL_BLEND);
// GL11.glDepthMask(true);
// GL11.glAlphaFunc(GL11.GL_GREATER, 0.1F);
// textureManager.bind(TextureAtlas.LOCATION_BLOCKS);
// bufferBuilder.begin(7, DefaultVertexFormat.PARTICLE);
// }
//
// public void end(Tesselator tesselator) {
// tesselator.end();
// }
//
// public String toString() {
// return "OPAQUE_TERRAIN_SHEET";
// }
// };
//}
@@ -12,6 +12,8 @@ public enum RenderCondition {
BEACON_BEAM,
ENTITIES,
ENTITIES_TRANSLUCENT,
PARTICLES,
PARTICLES_TRANSLUCENT,
GLINT,
ENTITY_EYES,
HAND_OPAQUE,
@@ -0,0 +1,24 @@
package net.coderbot.iris.gbuffer_overrides.matching;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import org.lwjgl.opengl.GL11;
/**
* Decides whether a draw is blending.
*/
public final class TranslucentBlendMatcher {
/** Render thread only. */
private static final BlendState scratch = new BlendState();
private TranslucentBlendMatcher() {}
public static boolean matchesCurrentState() {
if (!GLStateManager.isEffectiveBlendEnabled()) {
return false;
}
GLStateManager.getEffectiveBlendState(scratch);
return scratch.getSrcRgb() == GL11.GL_SRC_ALPHA && scratch.getDstRgb() == GL11.GL_ONE_MINUS_SRC_ALPHA;
}
}
@@ -3,57 +3,114 @@ package net.coderbot.iris.gl.blending;
import com.gtnewhorizons.angelica.glsm.states.AlphaTestFunction;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
import lombok.Getter;
import org.lwjgl.opengl.GL11;
public class AlphaTestStorage {
private static boolean originalAlphaTestEnable;
private static AlphaTest originalAlphaTest;
@Getter
private static boolean vanillaEnabled;
private static int vanillaFunction = GL11.GL_ALWAYS;
private static float vanillaReference;
@Getter
private static boolean alphaTestLocked;
private static boolean overrideHeld;
public static void overrideAlphaTest(AlphaTest override) {
if (!alphaTestLocked) {
if (!overrideHeld) {
final AlphaState alphaState = GLStateManager.getAlphaState();
// Only save the previous state if the alpha test wasn't already locked
originalAlphaTestEnable = GLStateManager.getAlphaTest().isEnabled();
originalAlphaTest = new AlphaTest(AlphaTestFunction.fromGlId(alphaState.getFunction()).orElse(AlphaTestFunction.ALWAYS), alphaState.getReference());
}
// Only save the previous state if the alpha test wasn't already overridden
vanillaEnabled = GLStateManager.getAlphaTest().isEnabled();
vanillaFunction = alphaState.getFunction();
vanillaReference = alphaState.getReference();
}
alphaTestLocked = false;
overrideHeld = true;
if (override == null) {
GLStateManager.disableAlphaTest();
} else {
if (override == null) {
apply(false, GL11.GL_ALWAYS, 0.0f);
} else {
apply(true, override.getFunction().getGlId(), override.getReference());
}
}
private static void apply(boolean enabled, int function, float reference) {
alphaTestLocked = false;
try {
if (enabled) {
GLStateManager.enableAlphaTest();
GLStateManager.glAlphaFunc(function, reference);
} else {
GLStateManager.disableAlphaTest();
}
} finally {
alphaTestLocked = true;
}
}
public static void deferAlphaTestToggle(boolean enabled) {
vanillaEnabled = enabled;
}
public static void deferAlphaFunc(int function, float reference) {
vanillaFunction = function;
vanillaReference = reference;
}
public static void restoreAlphaTest() {
if (!overrideHeld) {
return;
}
alphaTestLocked = false;
if (vanillaEnabled) {
GLStateManager.enableAlphaTest();
GLStateManager.glAlphaFunc(override.getFunction().getGlId(), override.getReference());
}
alphaTestLocked = true;
}
public static void deferAlphaTestToggle(boolean enabled) {
originalAlphaTestEnable = enabled;
}
public static void deferAlphaFunc(int function, float reference) {
originalAlphaTest = new AlphaTest(AlphaTestFunction.fromGlId(function).get(), reference);
}
public static void restoreAlphaTest() {
if (!alphaTestLocked) {
return;
}
alphaTestLocked = false;
if (originalAlphaTestEnable) {
GLStateManager.enableAlphaTest();
} else {
} else {
GLStateManager.disableAlphaTest();
}
}
GLStateManager.glAlphaFunc(originalAlphaTest.getFunction().getGlId(), originalAlphaTest.getReference());
}
GLStateManager.glAlphaFunc(vanillaFunction, vanillaReference);
overrideHeld = false;
}
public static final VanillaBooleanLayer ENABLE_LAYER = new VanillaBooleanLayer() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public boolean getVanilla() {
return vanillaEnabled;
}
@Override
public void setVanilla(boolean enabled) {
vanillaEnabled = enabled;
}
};
public static final VanillaStateLayer<AlphaState> FUNC_LAYER = new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public void readVanilla(AlphaState into) {
into.setFunction(vanillaFunction);
into.setReference(vanillaReference);
}
@Override
public void writeVanilla(AlphaState from) {
vanillaFunction = from.getFunction();
vanillaReference = from.getReference();
}
};
}
@@ -2,126 +2,187 @@ package net.coderbot.iris.gl.blending;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import lombok.Getter;
public class BlendModeStorage {
private static final int MAX_DRAW_BUFFERS = 8;
private static final int MAX_DRAW_BUFFERS = 8;
private static boolean originalBlendEnable;
private static final BlendState originalBlend = new BlendState();
@Getter private static boolean blendLocked;
@Getter private static boolean hasDeferredChanges;
private static final int[] bufferOverrideIndices = new int[MAX_DRAW_BUFFERS];
private static final boolean[] bufferOverrideIsDisable = new boolean[MAX_DRAW_BUFFERS];
private static final BlendState[] bufferOverrideStates = new BlendState[MAX_DRAW_BUFFERS];
private static int bufferOverrideCount;
private static boolean vanillaBlendEnable;
private static final BlendState vanillaBlend = new BlendState();
static {
for (int i = 0; i < MAX_DRAW_BUFFERS; i++) {
bufferOverrideStates[i] = new BlendState();
}
}
@Getter
private static boolean blendLocked;
@Getter
private static boolean overrideHeld;
private static boolean hasGlobalOverride;
private static boolean hasDeferredChanges;
private static final int[] bufferOverrideIndices = new int[MAX_DRAW_BUFFERS];
private static final boolean[] bufferOverrideIsDisable = new boolean[MAX_DRAW_BUFFERS];
private static final BlendState[] bufferOverrideStates = new BlendState[MAX_DRAW_BUFFERS];
private static int bufferOverrideCount;
private static boolean bufferOverridesDirty;
static {
for (int i = 0; i < MAX_DRAW_BUFFERS; i++) {
bufferOverrideStates[i] = new BlendState();
}
}
public static void overrideBlend(BlendState override) {
if (!blendLocked) {
// Only save the previous state if the blend mode wasn't already locked
originalBlendEnable = GLStateManager.getBlendMode().isEnabled();
originalBlend.set(GLStateManager.getBlendState());
}
captureVanilla();
bufferOverrideCount = 0;
hasDeferredChanges = false;
blendLocked = false;
bufferOverrideCount = 0;
bufferOverridesDirty = false;
hasDeferredChanges = false;
overrideHeld = true;
hasGlobalOverride = true;
if (override == null) {
GLStateManager.disableBlend();
} else {
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
blendLocked = false;
try {
if (override == null) {
GLStateManager.disableBlend();
} else {
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
} finally {
blendLocked = true;
}
}
blendLocked = true;
}
public static void overrideBufferBlend(int index, BlendState override) {
captureVanilla();
public static void overrideBufferBlend(int index, BlendState override) {
if (!blendLocked) {
// Only save the previous state if the blend mode wasn't already locked
originalBlendEnable = GLStateManager.getBlendMode().isEnabled();
originalBlend.set(GLStateManager.getBlendState());
}
if (override == null) {
RenderSystem.disableBufferBlend(index);
} else {
RenderSystem.enableBufferBlend(index);
RenderSystem.blendFuncSeparatei(index, override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
if (override == null) {
RenderSystem.disableBufferBlend(index);
} else {
RenderSystem.enableBufferBlend(index);
RenderSystem.blendFuncSeparatei(index, override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
if (bufferOverrideCount < MAX_DRAW_BUFFERS) {
final int slot = bufferOverrideCount++;
bufferOverrideIndices[slot] = index;
bufferOverrideIsDisable[slot] = (override == null);
if (override != null) {
bufferOverrideStates[slot].setAll(override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
}
overrideHeld = true;
blendLocked = true;
}
if (bufferOverrideCount < MAX_DRAW_BUFFERS) {
final int slot = bufferOverrideCount++;
bufferOverrideIndices[slot] = index;
bufferOverrideIsDisable[slot] = (override == null);
if (override != null) {
bufferOverrideStates[slot].setAll(override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
}
blendLocked = true;
}
private static void captureVanilla() {
if (!overrideHeld) {
vanillaBlendEnable = GLStateManager.getBlendMode().isEnabled();
vanillaBlend.set(GLStateManager.getBlendState());
}
}
public static void deferBlendModeToggle(boolean enabled) {
originalBlendEnable = enabled;
hasDeferredChanges = true;
}
public static void deferBlendModeToggle(boolean enabled) {
vanillaBlendEnable = enabled;
hasDeferredChanges = true;
}
public static void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
originalBlend.setAll(srcRgb, dstRgb, srcAlpha, dstAlpha);
hasDeferredChanges = true;
}
public static void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
vanillaBlend.setAll(srcRgb, dstRgb, srcAlpha, dstAlpha);
hasDeferredChanges = true;
}
public static void flushDeferredBlend() {
if (!blendLocked || !hasDeferredChanges) return;
hasDeferredChanges = false;
blendLocked = false;
public static void flushDeferredBlend() {
if (!overrideHeld) return;
if (originalBlendEnable) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(
originalBlend.getSrcRgb(), originalBlend.getDstRgb(),
originalBlend.getSrcAlpha(), originalBlend.getDstAlpha());
if (hasDeferredChanges && !hasGlobalOverride) {
hasDeferredChanges = false;
applyVanilla();
bufferOverridesDirty = true;
}
blendLocked = true;
if (bufferOverridesDirty) {
bufferOverridesDirty = false;
reassertBufferOverrides();
}
}
for (int i = 0; i < bufferOverrideCount; i++) {
final int idx = bufferOverrideIndices[i];
if (bufferOverrideIsDisable[i]) {
RenderSystem.disableBufferBlend(idx);
} else {
final BlendState s = bufferOverrideStates[i];
RenderSystem.enableBufferBlend(idx);
RenderSystem.blendFuncSeparatei(idx, s.getSrcRgb(), s.getDstRgb(), s.getSrcAlpha(), s.getDstAlpha());
}
}
}
private static void reassertBufferOverrides() {
for (int i = 0; i < bufferOverrideCount; i++) {
final int idx = bufferOverrideIndices[i];
if (bufferOverrideIsDisable[i]) {
RenderSystem.disableBufferBlend(idx);
} else {
final BlendState s = bufferOverrideStates[i];
RenderSystem.enableBufferBlend(idx);
RenderSystem.blendFuncSeparatei(idx, s.getSrcRgb(), s.getDstRgb(), s.getSrcAlpha(), s.getDstAlpha());
}
}
}
public static void restoreBlend() {
if (!blendLocked) {
return;
}
public static void restoreBlend() {
if (!overrideHeld) {
return;
}
blendLocked = false;
hasDeferredChanges = false;
bufferOverrideCount = 0;
hasGlobalOverride = false;
hasDeferredChanges = false;
bufferOverrideCount = 0;
bufferOverridesDirty = false;
blendLocked = false;
if (originalBlendEnable) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
applyVanilla();
overrideHeld = false;
}
GLStateManager.tryBlendFuncSeparate(originalBlend.getSrcRgb(), originalBlend.getDstRgb(), originalBlend.getSrcAlpha(), originalBlend.getDstAlpha());
}
private static void applyVanilla() {
final boolean wasLocked = blendLocked;
blendLocked = false;
try {
if (vanillaBlendEnable) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(vanillaBlend.getSrcRgb(), vanillaBlend.getDstRgb(), vanillaBlend.getSrcAlpha(), vanillaBlend.getDstAlpha());
} finally {
blendLocked = wasLocked;
}
}
public static final VanillaBooleanLayer ENABLE_LAYER = new VanillaBooleanLayer() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public boolean getVanilla() {
return vanillaBlendEnable;
}
@Override
public void setVanilla(boolean enabled) {
deferBlendModeToggle(enabled);
}
};
public static final VanillaStateLayer<BlendState> FUNC_LAYER = new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public void readVanilla(BlendState into) {
into.setAll(vanillaBlend.getSrcRgb(), vanillaBlend.getDstRgb(), vanillaBlend.getSrcAlpha(), vanillaBlend.getDstAlpha());
}
@Override
public void writeVanilla(BlendState from) {
deferBlendFunc(from.getSrcRgb(), from.getDstRgb(), from.getSrcAlpha(), from.getDstAlpha());
}
};
}
@@ -3,64 +3,105 @@ package net.coderbot.iris.gl.blending;
import com.gtnewhorizons.angelica.glsm.states.DepthState;
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import lombok.Getter;
public class DepthColorStorage {
private static boolean originalDepthEnable;
private static net.coderbot.iris.gl.blending.ColorMask originalColor;
@Getter
private static boolean vanillaDepthEnable;
private static final ColorMask vanillaColor = new ColorMask();
@Getter
private static boolean depthColorLocked;
private static final IntOpenHashSet ownedPrograms = new IntOpenHashSet();
@Getter
private static boolean overrideHeld;
public static void registerOwnedProgram(int programId) {
ownedPrograms.add(programId);
}
private static final IntOpenHashSet ownedPrograms = new IntOpenHashSet();
public static void unregisterOwnedProgram(int programId) {
ownedPrograms.remove(programId);
}
public static void registerOwnedProgram(int programId) {
ownedPrograms.add(programId);
}
public static boolean isOwnedProgram(int programId) {
return ownedPrograms.contains(programId);
}
public static void unregisterOwnedProgram(int programId) {
ownedPrograms.remove(programId);
}
public static boolean isOwnedProgram(int programId) {
return ownedPrograms.contains(programId);
}
public static void disableDepthColor() {
if (!depthColorLocked) {
// Only save the previous state if the depth and color mask wasn't already locked
final ColorMask colorMask = GLStateManager.getColorMask();
final DepthState depthState = GLStateManager.getDepthState();
if (!overrideHeld) {
final ColorMask colorMask = GLStateManager.getColorMask();
final DepthState depthState = GLStateManager.getDepthState();
originalDepthEnable = depthState.isEnabled();
originalColor = new net.coderbot.iris.gl.blending.ColorMask(colorMask.red, colorMask.green, colorMask.blue, colorMask.alpha);
}
vanillaDepthEnable = depthState.isEnabled();
vanillaColor.set(colorMask);
}
depthColorLocked = false;
overrideHeld = true;
GLStateManager.glDepthMask(false);
GLStateManager.glColorMask(false, false, false, false);
depthColorLocked = false;
try {
GLStateManager.glDepthMask(false);
GLStateManager.glColorMask(false, false, false, false);
} finally {
depthColorLocked = true;
}
}
depthColorLocked = true;
}
public static void deferDepthEnable(boolean enabled) {
vanillaDepthEnable = enabled;
}
public static void deferDepthEnable(boolean enabled) {
originalDepthEnable = enabled;
}
public static void deferColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
vanillaColor.setAll(red, green, blue, alpha);
}
public static void deferColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
originalColor = new net.coderbot.iris.gl.blending.ColorMask(red, green, blue, alpha);
}
public static void unlockDepthColor() {
if (!overrideHeld) {
return;
}
public static void unlockDepthColor() {
if (!depthColorLocked) {
return;
}
depthColorLocked = false;
depthColorLocked = false;
GLStateManager.glDepthMask(vanillaDepthEnable);
GLStateManager.glColorMask(vanillaColor.red, vanillaColor.green, vanillaColor.blue, vanillaColor.alpha);
overrideHeld = false;
}
GLStateManager.glDepthMask(originalDepthEnable);
public static final VanillaStateLayer<DepthState> DEPTH_LAYER = new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
GLStateManager.glColorMask(originalColor.isRedMasked(), originalColor.isGreenMasked(), originalColor.isBlueMasked(), originalColor.isAlphaMasked());
}
@Override
public void readVanilla(DepthState into) {
into.setEnabled(vanillaDepthEnable);
}
@Override
public void writeVanilla(DepthState from) {
vanillaDepthEnable = from.isEnabled();
}
};
public static final VanillaStateLayer<ColorMask> COLOR_LAYER = new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public void readVanilla(ColorMask into) {
into.set(vanillaColor);
}
@Override
public void writeVanilla(ColorMask from) {
vanillaColor.set(from);
}
};
}
@@ -36,7 +36,6 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
@Getter
private ShaderPackEntry applied = null;
@Setter
@Getter
private ShaderPackEntry selected = null;
@@ -203,6 +202,11 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
}
}
public void setSelected(ShaderPackEntry selected) {
this.selected = selected;
this.screen.refreshScreenSwitchButton();
}
public void select(String name) {
for (BaseEntry entry : this.entries) {
if (entry instanceof ShaderPackEntry shaderPackEntry && name.equals(shaderPackEntry.getPackName())) {
@@ -39,8 +39,10 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
@@ -85,6 +87,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
private boolean guiHidden = false;
private boolean dirty = false;
private final Map<String, Boolean> packOptionAvailability = new HashMap<>();
public ShaderPackScreen(GuiScreen parent) {
this.title = I18n.format("options.iris.shaderPackSelection.title");
@@ -250,19 +254,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
I18n.format("options.iris.openShaderPackFolder"), button -> this.openShaderPackFolder()));
this.screenSwitchButton = new IrisButton(topCenter + 78, this.height - 51, 152, 20,
I18n.format("options.iris.shaderPackList"), button -> {
this.optionMenuOpen = !this.optionMenuOpen;
// UX: Apply changes before switching screens to avoid unintuitive behavior
//
// Not doing this leads to unintuitive behavior, since selecting a pack in the
// list (but not applying) would open the settings for the previous pack, rather
// than opening the settings for the selected (but not applied) pack.
this.applyChanges();
initGui();
}
);
I18n.format("options.iris.shaderPackList"), button -> this.toggleOptionMenu());
this.buttonList.add(this.screenSwitchButton);
refreshScreenSwitchButton();
@@ -299,6 +291,17 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
}
@Override
public void updateScreen() {
super.updateScreen();
if (this.notificationDialogTimer > 0) {
this.notificationDialogTimer--;
} else if (this.notificationDialog != null) {
this.notificationDialog = null;
}
}
/**
* Called when the mouse is clicked.
*/
@@ -346,10 +349,52 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
}
}
/**
* Switches between the pack list and the pack settings, applying the pending pack first.
* <p>
* UX: Apply changes before switching screens to avoid unintuitive behavior. Not doing this leads to
* unintuitive behavior, since selecting a pack in the list (but not applying) would open the settings
* for the previous pack, rather than opening the settings for the selected (but not applied) pack.
*/
private void toggleOptionMenu() {
final boolean opening = !this.optionMenuOpen;
this.optionMenuOpen = opening;
this.applyChanges();
// The pack is only parsed by applyChanges, so this is the first point where an option-less
// pack can be recognized. Stay on the list instead of opening an empty settings screen.
if (opening && !currentPackHasOptions()) {
this.optionMenuOpen = false;
displayNotification(I18n.format("options.iris.shaderPackOptions.noOptions", Iris.getCurrentPackName()));
}
initGui();
}
private boolean canOpenOptionMenu() {
if (!this.shaderPackList.getTopButtonRow().shadersEnabled) {
return false;
}
final ShaderPackEntry highlighted = this.shaderPackList.getSelected();
final String name = highlighted != null ? highlighted.getPackName() : Iris.getCurrentPackName();
return name == null || this.packOptionAvailability.getOrDefault(name, Boolean.TRUE);
}
private boolean currentPackHasOptions() {
return Iris.getCurrentPack().map(p -> p.getMenuContainer().hasMenuElements()).orElse(false);
}
public void refreshForChangedPack() {
if (Iris.getCurrentPack().isPresent()) {
final ShaderPack currentPack = Iris.getCurrentPack().get();
if (Iris.getCurrentPackName() != null) {
this.packOptionAvailability.put(Iris.getCurrentPackName(), currentPack.getMenuContainer().hasMenuElements());
}
this.navigation = new NavigationController();
if (this.shaderOptionList != null) {
@@ -364,11 +409,10 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
}
public void refreshScreenSwitchButton() {
if (this.screenSwitchButton != null) {
if (this.screenSwitchButton != null && this.shaderPackList != null) {
this.screenSwitchButton.displayString = optionMenuOpen ? I18n.format("options.iris.shaderPackList") : I18n.format("options.iris.shaderPackSettings");
// Disable the settings switch when shaders are off or the selected pack exposes no options
this.screenSwitchButton.enabled = optionMenuOpen || (shaderPackList.getTopButtonRow().shadersEnabled
&& Iris.getCurrentPack().map(p -> !p.getMenuContainer().mainScreen.elements.isEmpty()).orElse(true));
// Disable the settings switch when shaders are off or the highlighted pack exposes no options
this.screenSwitchButton.enabled = optionMenuOpen || canOpenOptionMenu();
}
}
@@ -435,12 +479,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
return;
}
if (keyCode == Keyboard.KEY_TAB) {
final boolean canOpenOptions = shaderPackList.getTopButtonRow().shadersEnabled
&& Iris.getCurrentPack().map(p -> !p.getMenuContainer().mainScreen.elements.isEmpty()).orElse(false);
if (this.optionMenuOpen || canOpenOptions) {
this.optionMenuOpen = !this.optionMenuOpen;
this.applyChanges();
this.initGui();
if (this.optionMenuOpen || canOpenOptionMenu()) {
toggleOptionMenu();
}
return;
}
@@ -1,24 +1,32 @@
package net.coderbot.iris.layer;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import net.coderbot.iris.gl.shader.ProgramCreator;
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import java.util.Arrays;
public class GbufferPrograms {
private static boolean entities;
private static boolean blockEntities;
private static boolean particles;
private static boolean outline;
private static boolean entityLoop;
private static Runnable phaseChangeListener;
static {
StateUpdateNotifiers.phaseChangeNotifier = listener -> phaseChangeListener = listener;
}
private static void checkReentrancy() {
if (entities || blockEntities || outline) {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call begin function when entities = "
@@ -61,8 +69,18 @@ public class GbufferPrograms {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call endEntities when entities = false");
}
setPhase(WorldRenderingPhase.NONE);
entities = false;
endPhase();
}
private static void endPhase() {
if (particles) {
setPhase(WorldRenderingPhase.PARTICLES);
} else if (entityLoop) {
setPhase(WorldRenderingPhase.ENTITIES);
} else {
setPhase(WorldRenderingPhase.NONE);
}
}
public static void beginOutline() {
@@ -76,10 +94,41 @@ public class GbufferPrograms {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call endOutline when outline = false");
}
setPhase(WorldRenderingPhase.NONE);
outline = false;
endPhase();
}
public static int beginParticles() {
if (particleDepth == particlePhaseSaves.length) {
particlePhaseSaves = Arrays.copyOf(particlePhaseSaves, particleDepth * 2);
particleTranslucencySaves = Arrays.copyOf(particleTranslucencySaves, particleDepth * 2);
}
particlePhaseSaves[particleDepth] = getCurrentPhase();
particleTranslucencySaves[particleDepth] = beginTranslucencyDeclaration(Boolean.FALSE);
setPhase(WorldRenderingPhase.PARTICLES);
particles = true;
return particleDepth++;
}
public static void endParticles(int depth) {
particleDepth = depth;
endTranslucencyDeclaration(particleTranslucencySaves[depth]);
particleTranslucencySaves[depth] = null;
setPhase(particlePhaseSaves[depth]);
particlePhaseSaves[depth] = null;
particles = particleDepth > 0;
}
private static WorldRenderingPhase[] particlePhaseSaves = new WorldRenderingPhase[4];
private static Boolean[] particleTranslucencySaves = new Boolean[4];
private static int particleDepth;
public static void beginBlockEntities() {
checkReentrancy();
setPhase(WorldRenderingPhase.BLOCK_ENTITIES);
@@ -92,8 +141,101 @@ public class GbufferPrograms {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call endBlockEntities when blockEntities = false");
}
setPhase(WorldRenderingPhase.NONE);
blockEntities = false;
endPhase();
}
public static void setCutoutDefaults() {
GLStateManager.enableAlphaTest();
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
final int previousUnit = GLStateManager.getActiveTextureUnitForServerState();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.enableTexture();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + previousUnit);
}
public static long pushCutoutDefaults() {
final long saved = packCutoutState();
setCutoutDefaults();
return saved;
}
public static void popCutoutDefaults(long saved) {
if ((saved & ALPHA_ENABLED_BIT) != 0) {
GLStateManager.enableAlphaTest();
} else {
GLStateManager.disableAlphaTest();
}
GLStateManager.glAlphaFunc((int) ((saved >>> 32) & 0xFFFF), Float.intBitsToFloat((int) saved));
if ((saved & LIGHTMAP_ENABLED_BIT) == 0) {
final int previousUnit = GLStateManager.getActiveTextureUnitForServerState();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.disableTexture();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + previousUnit);
}
}
public static int pushBlendState() {
if (blendDepth == blendSaves.length) {
blendSaves = Arrays.copyOf(blendSaves, blendDepth * 2);
blendEnabledSaves = Arrays.copyOf(blendEnabledSaves, blendDepth * 2);
}
BlendState saved = blendSaves[blendDepth];
if (saved == null) {
saved = new BlendState();
blendSaves[blendDepth] = saved;
}
GLStateManager.getEffectiveBlendState(saved);
blendEnabledSaves[blendDepth] = GLStateManager.isEffectiveBlendEnabled();
return blendDepth++;
}
public static void popBlendState(int depth) {
blendDepth = depth;
final boolean enabled = blendEnabledSaves[depth];
if (enabled != GLStateManager.isEffectiveBlendEnabled()) {
if (enabled) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
}
final BlendState saved = blendSaves[depth];
GLStateManager.getEffectiveBlendState(blendScratch);
if (blendScratch.getSrcRgb() != saved.getSrcRgb() || blendScratch.getDstRgb() != saved.getDstRgb()
|| blendScratch.getSrcAlpha() != saved.getSrcAlpha() || blendScratch.getDstAlpha() != saved.getDstAlpha()) {
GLStateManager.tryBlendFuncSeparate(saved.getSrcRgb(), saved.getDstRgb(), saved.getSrcAlpha(), saved.getDstAlpha());
}
}
public static void popBlendStateTop() {
if (blendDepth > 0) {
popBlendState(blendDepth - 1);
}
}
private static BlendState[] blendSaves = new BlendState[4];
private static boolean[] blendEnabledSaves = new boolean[4];
private static int blendDepth;
private static final BlendState blendScratch = new BlendState();
private static final long ALPHA_ENABLED_BIT = 1L << 48;
private static final long LIGHTMAP_ENABLED_BIT = 1L << 49;
private static final AlphaState alphaScratch = new AlphaState();
private static long packCutoutState() {
GLStateManager.getEffectiveAlphaState(alphaScratch);
return (Float.floatToRawIntBits(alphaScratch.getReference()) & 0xFFFFFFFFL)
| ((long) (alphaScratch.getFunction() & 0xFFFF) << 32)
| (GLStateManager.isEffectiveAlphaTestEnabled() ? ALPHA_ENABLED_BIT : 0L)
| (GLStateManager.getTextures().getTextureUnitStates(1).isEnabled() ? LIGHTMAP_ENABLED_BIT : 0L);
}
public static void setBlockEntityDefaults() {
@@ -121,7 +263,23 @@ public class GbufferPrograms {
}
}
public static boolean beginNestedEntityPhase() {
if (getCurrentPhase() != WorldRenderingPhase.BLOCK_ENTITIES) {
return false;
}
setOverridePhase(WorldRenderingPhase.ENTITIES);
return true;
}
public static void endNestedEntityPhase(boolean pushed) {
if (pushed) {
setOverridePhase(null);
}
}
public static void setOverridePhase(WorldRenderingPhase phase) {
overridePhase = phase;
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (pipeline != null) {
@@ -129,20 +287,62 @@ public class GbufferPrograms {
}
}
@Getter
private static WorldRenderingPhase overridePhase;
public static Boolean beginTranslucencyDeclaration(Boolean translucent) {
final Boolean previous = declaredTranslucent;
declaredTranslucent = translucent;
applyTranslucencyDeclaration();
return previous;
}
public static void endTranslucencyDeclaration(Boolean previous) {
declaredTranslucent = previous;
applyTranslucencyDeclaration();
}
public static void setTranslucencyDeclaration(Boolean translucent) {
declaredTranslucent = translucent;
applyTranslucencyDeclaration();
}
private static Boolean declaredTranslucent;
private static void applyTranslucencyDeclaration() {
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (pipeline != null) {
pipeline.setDeclaredTranslucency(declaredTranslucent);
}
}
private static SpecialCondition currentSpecial;
public static SpecialCondition getSpecialCondition() {
return currentSpecial;
}
public static Boolean getDeclaredTranslucency() {
return declaredTranslucent;
}
public static void setupSpecialRenderCondition(SpecialCondition override) {
currentSpecial = override;
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setSpecialCondition(override));
}
public static void teardownSpecialRenderCondition() {
currentSpecial = null;
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setSpecialCondition(null));
}
public static void runPhaseChangeNotifier() {
if (phaseChangeListener != null) {
phaseChangeListener.run();
}
}
public static void init() {
// Empty initializer to run static
}
@@ -0,0 +1,48 @@
package net.coderbot.iris.layer;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
import java.util.Locale;
public record PassOverride(SpecialCondition special, Boolean translucent, WorldRenderingPhase phase) {
public static final PassOverride NONE = new PassOverride(null, null, null);
public PassOverride(SpecialCondition special, Boolean translucent) {
this(special, translucent, null);
}
public static PassOverride capture() {
final SpecialCondition special = GbufferPrograms.getSpecialCondition();
final Boolean translucent = GbufferPrograms.getDeclaredTranslucency();
final WorldRenderingPhase phase = GbufferPrograms.getOverridePhase();
return special == null && translucent == null && phase == null
? NONE : new PassOverride(special, translucent, phase);
}
public void apply() {
GbufferPrograms.setupSpecialRenderCondition(special);
GbufferPrograms.setTranslucencyDeclaration(translucent);
GbufferPrograms.setOverridePhase(phase);
}
public void clear() {
GbufferPrograms.setupSpecialRenderCondition(null);
GbufferPrograms.setTranslucencyDeclaration(null);
GbufferPrograms.setOverridePhase(null);
}
public boolean isEntityPhase() {
return phase == WorldRenderingPhase.ENTITIES;
}
public String nameSuffix() {
if (special == null && translucent == null && phase == null) {
return "";
}
return "_" + (special == null ? "plain" : special.name().toLowerCase(Locale.ROOT))
+ (translucent == null ? "" : (translucent ? "_translucent" : "_opaque"))
+ (phase == null ? "" : "_" + phase.name().toLowerCase(Locale.ROOT));
}
}
@@ -26,6 +26,7 @@ import net.coderbot.iris.features.FeatureFlags;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
import net.coderbot.iris.gbuffer_overrides.matching.ProgramTable;
import net.coderbot.iris.gbuffer_overrides.matching.RenderCondition;
import net.coderbot.iris.gbuffer_overrides.matching.TranslucentBlendMatcher;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.gbuffer_overrides.state.RenderTargetStateListener;
import net.coderbot.iris.gl.blending.AlphaTestOverride;
@@ -127,9 +128,6 @@ import java.util.function.Supplier;
* Encapsulates the compiled shader program objects for the currently loaded shaderpack.
*/
public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, RenderTargetStateListener {
private final static int SRC_ALPHA = 770;
private final static int ONE_MINUS_SRC_ALPHA = 771;
private final static int ONE = 1;
private final RenderTargets renderTargets;
@Nullable
@@ -260,14 +258,31 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
final Map<Pair<String, InputAvailability>, CompletableFuture<Map<PatchShaderType, String>>> instancedTransformFutures = submitAttributeTransforms(resolver, INSTANCED_PROGRAM_IDS, true);
final Optional<ProgramSource> terrainSource = first(programs.getGbuffersTerrain(), programs.getGbuffersTexturedLit(), programs.getGbuffersTextured(), programs.getGbuffersBasic());
final Optional<ProgramSource> terrainSolidOverride = programs.getGbuffersTerrainSolid();
final Optional<ProgramSource> terrainCutoutOverride = programs.getGbuffersTerrainCutout();
final Optional<ProgramSource> terrainSolidSource = first(terrainSolidOverride, terrainSource);
final Optional<ProgramSource> terrainCutoutSource = first(terrainCutoutOverride, terrainSource);
final Optional<ProgramSource> translucentSource = first(programs.getGbuffersWater(), terrainSource);
final Optional<ProgramSource> shadowSource = programs.getShadow();
final Optional<ProgramSource> shadowSolidOverride = programs.getShadowSolid();
final Optional<ProgramSource> shadowCutoutOverride = programs.getShadowCutout();
final Optional<ProgramSource> shadowSolidSource = first(shadowSolidOverride, shadowSource);
final Optional<ProgramSource> shadowCutoutSource = first(shadowCutoutOverride, shadowSource);
final Optional<ProgramSource> shadowTranslucentSource = first(programs.getShadowWater(), shadowSource);
// Celeritas terrain transform futures
final CompletableFuture<Map<PatchShaderType, String>> celeritasTerrainFuture = terrainSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
final CompletableFuture<Map<PatchShaderType, String>> celeritasTerrainSolidFuture = terrainSolidOverride
.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(celeritasTerrainFuture);
final CompletableFuture<Map<PatchShaderType, String>> celeritasTerrainCutoutFuture = terrainCutoutOverride
.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(celeritasTerrainFuture);
final CompletableFuture<Map<PatchShaderType, String>> celeritasTranslucentFuture = translucentSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowFuture = shadowSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowSolidFuture = shadowSolidOverride
.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(celeritasShadowFuture);
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowCutoutFuture = shadowCutoutOverride
.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(celeritasShadowFuture);
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowTranslucentFuture = shadowTranslucentSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
final Int2ObjectArrayMap<CompletableFuture<String>> setupComputeFutures = submitSetupComputeTransforms(programs.getSetup(), programs.getPackDirectives().getTextureMap());
@@ -461,6 +476,8 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
ProgramId.BeaconBeam, ProgramId.BeaconBeam, ProgramId.BeaconBeam,
ProgramId.Entities, ProgramId.Entities, ProgramId.Entities,
ProgramId.EntitiesTrans, ProgramId.EntitiesTrans, ProgramId.EntitiesTrans,
ProgramId.Particles, ProgramId.Particles, ProgramId.Particles,
ProgramId.ParticlesTrans, ProgramId.ParticlesTrans, ProgramId.ParticlesTrans,
null, ProgramId.ArmorGlint, ProgramId.ArmorGlint,
null, ProgramId.SpiderEyes, ProgramId.SpiderEyes,
ProgramId.Hand, ProgramId.Hand, ProgramId.Hand,
@@ -697,11 +714,14 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
this.celeritasTerrainPipeline = new CeleritasTerrainPipeline(createTerrainSamplers,
shadowRenderer == null ? null : createShadowTerrainSamplers, createTerrainImages,
shadowRenderer == null ? null : createShadowTerrainImages, this.customUniforms,
terrainSource,
terrainSolidSource,
terrainCutoutSource,
translucentSource,
shadowSource,
shadowSolidSource,
shadowCutoutSource,
shadowTranslucentSource,
celeritasTerrainFuture, celeritasTranslucentFuture, celeritasShadowFuture, celeritasShadowTranslucentFuture,
celeritasTerrainSolidFuture, celeritasTerrainCutoutFuture, celeritasTranslucentFuture,
celeritasShadowSolidFuture, celeritasShadowCutoutFuture, celeritasShadowTranslucentFuture,
renderTargets, flippedAfterPrepare, flippedAfterTranslucent,
celeritasShadowFb);
@@ -843,28 +863,18 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
}
switch (phase) {
case NONE, OUTLINE, DEBUG, PARTICLES:
case NONE, OUTLINE, DEBUG:
return RenderCondition.DEFAULT;
case PARTICLES:
return isTranslucentDraw() ? RenderCondition.PARTICLES_TRANSLUCENT : RenderCondition.PARTICLES;
case SKY, SUNSET, CUSTOM_SKY, SUN, MOON, STARS, VOID:
return RenderCondition.SKY;
case TERRAIN_SOLID, TERRAIN_CUTOUT, TERRAIN_CUTOUT_MIPPED:
return RenderCondition.TERRAIN_OPAQUE;
case ENTITIES:
if (GLStateManager.getBlendState().getSrcRgb() == SRC_ALPHA &&
GLStateManager.getBlendState().getSrcAlpha() == ONE_MINUS_SRC_ALPHA &&
GLStateManager.getBlendState().getDstRgb() == ONE &&
GLStateManager.getBlendState().getDstAlpha() == ONE_MINUS_SRC_ALPHA)
{
return RenderCondition.ENTITIES_TRANSLUCENT;
} else {
return RenderCondition.ENTITIES;
}
return isTranslucentDraw() ? RenderCondition.ENTITIES_TRANSLUCENT : RenderCondition.ENTITIES;
case BLOCK_ENTITIES:
if (GLStateManager.getBlendState().getSrcRgb() == SRC_ALPHA &&
GLStateManager.getBlendState().getDstRgb() == ONE_MINUS_SRC_ALPHA) {
return RenderCondition.BLOCK_ENTITIES_TRANSLUCENT;
}
return RenderCondition.BLOCK_ENTITIES;
return isTranslucentDraw() ? RenderCondition.BLOCK_ENTITIES_TRANSLUCENT : RenderCondition.BLOCK_ENTITIES;
case DESTROY:
return RenderCondition.DESTROY;
case HAND_SOLID:
@@ -884,6 +894,10 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
}
}
private boolean isTranslucentDraw() {
return declaredTranslucent != null ? declaredTranslucent : TranslucentBlendMatcher.matchesCurrentState();
}
public boolean shouldOverrideShaders() {
return isRenderingLevelGeometry() && isMainBound;
}
@@ -906,10 +920,60 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
* Called when a mod overrides the GL program away from the active Iris pass
*/
public void onModProgramOverride() {
if (current != null) {
modProgramOverrode = true;
programBeforeModOverride = getActivePassProgramId();
}
current = null;
currentCondition = null;
}
public void restorePassAfterModProgram(int newProgram) {
if (refreshingPass || drivingProgram || !modProgramOverrode) {
return;
}
if (newProgram != 0 && newProgram != programBeforeModOverride) {
return;
}
refreshingPass = true;
try {
current = null;
matchPass();
} finally {
modProgramOverrode = false;
programBeforeModOverride = -1;
refreshingPass = false;
}
}
private boolean refreshingPass;
private boolean modProgramOverrode;
private int programBeforeModOverride = -1;
public void onVanillaBlendChanged() {
if (matchingBlend || drivingProgram || refreshingPass) {
return;
}
if (declaredTranslucent != null) {
return;
}
final WorldRenderingPhase phase = getPhase();
if (phase != WorldRenderingPhase.ENTITIES && phase != WorldRenderingPhase.BLOCK_ENTITIES) {
return;
}
matchingBlend = true;
try {
matchPass();
} finally {
matchingBlend = false;
}
}
private boolean matchingBlend;
private void matchPass() {
if (!shouldOverrideShaders()) {
return;
@@ -946,13 +1010,20 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
current = pass;
if (pass != null) {
pass.use();
} else {
Program.unbind();
drivingProgram = true;
try {
if (pass != null) {
pass.use();
} else {
Program.unbind();
}
} finally {
drivingProgram = false;
}
}
private boolean drivingProgram;
@Override
public void rebindCurrentPass() {
final Pass pass = this.current;
@@ -1631,6 +1702,9 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
@Nullable
public ComputeProgram getShadowVoxelizationCompute() { return shadowVoxelizationCompute; }
@Nullable
public ShadowRenderer getShadowRenderer() { return shadowRenderer; }
private GlUniformMatrix4f voxelizationModelView;
private GlUniformMatrix3f voxelizationNormalMatrix;
private final Matrix3f voxelizationNormalScratch = new Matrix3f();
@@ -1772,6 +1846,14 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
@Override
public void beginHand() {
isRenderingFullScreenPass = true;
beginPass(null);
currentCondition = null;
profiler.startSection("iris_center_depth");
centerDepthSampler.sampleCenterDepth();
profiler.endSection();
isRenderingFullScreenPass = false;
if (!usesSampler(IrisSamplers.USAGE_DEPTHTEX2)) {
return;
}
@@ -2005,10 +2087,6 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
isRenderingFullScreenPass = true;
profiler.startSection("iris_center_depth");
centerDepthSampler.sampleCenterDepth();
profiler.endSection();
profiler.startSection("iris_composites");
GLDebug.pushGroup("composite");
try {
@@ -2110,6 +2188,15 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
matchPass();
}
@Override
public void setDeclaredTranslucency(@Nullable Boolean translucent) {
this.declaredTranslucent = translucent;
matchPass();
}
@Nullable
private Boolean declaredTranslucent;
@Override
public RenderTargetStateListener getRenderTargetStateListener() {
return this;
@@ -19,6 +19,7 @@ import net.coderbot.iris.uniforms.FrameUpdateNotifier;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.shader.Framebuffer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.OptionalInt;
@@ -83,6 +84,11 @@ public class FixedFunctionWorldRenderingPipeline implements WorldRenderingPipeli
}
@Override
public void setDeclaredTranslucency(@Nullable Boolean translucent) {
}
@Override
public RenderTargetStateListener getRenderTargetStateListener() {
return RenderTargetStateListener.NOP;
@@ -1,10 +1,12 @@
package net.coderbot.iris.pipeline;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.compat.mojang.Camera;
import com.gtnewhorizons.angelica.compat.mojang.GameModeUtil;
import com.gtnewhorizons.angelica.compat.mojang.InteractionHand;
import com.gtnewhorizons.angelica.event.RenderHandEvent;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.ItemRendererAccessor;
import com.gtnewhorizons.angelica.rendering.RenderingState;
import com.gtnewhorizons.angelica.rendering.celeritas.BlockRenderLayer;
import lombok.Getter;
@@ -83,96 +85,133 @@ public class HandRenderer {
!RenderHandEvent.post();
}
public boolean isHandTranslucent(InteractionHand hand) {
ItemStack heldItem = hand.getItemInHand(Minecraft.getMinecraft().thePlayer);
public boolean isItemTranslucent(ItemStack heldItem) {
if (heldItem == null) return false;
final Item item = heldItem.getItem();
if (item instanceof ItemBlock itemBlock) {
final Block block = itemBlock.field_150939_a;
if (block == null) return false;
final Map<Block, BlockRenderLayer> blockTypeIds = BlockRenderingSettings.INSTANCE.getBlockTypeIds();
return blockTypeIds != null && blockTypeIds.get(itemBlock.field_150939_a) == BlockRenderLayer.TRANSLUCENT;
final BlockRenderLayer override = blockTypeIds != null ? blockTypeIds.get(block) : null;
final BlockRenderLayer layer = override != null ? override : BlockRenderLayer.fromVanillaPass(block.getRenderBlockPass());
return layer == BlockRenderLayer.TRANSLUCENT;
}
return false;
}
public boolean isHandTranslucent(InteractionHand hand) {
return isItemTranslucent(hand.getItemInHand(Minecraft.getMinecraft().thePlayer));
}
private ItemStack mainHandItem() {
return ((ItemRendererAccessor) Minecraft.getMinecraft().entityRenderer.itemRenderer).angelica$getItemToRender();
}
public boolean isAnyHandTranslucent() {
return isHandTranslucent(InteractionHand.MAIN_HAND) || isHandTranslucent(InteractionHand.OFF_HAND);
return isItemTranslucent(mainHandItem())
|| (ModStatus.isBackhandLoaded && isHandTranslucent(InteractionHand.OFF_HAND));
}
public boolean isAnyHandSolid() {
return !(isItemTranslucent(mainHandItem())
&& (!ModStatus.isBackhandLoaded || isHandTranslucent(InteractionHand.OFF_HAND)));
}
public void renderSolid(float tickDelta, Camera camera, RenderGlobal gameRenderer, WorldRenderingPipeline pipeline) {
if (!canRender(camera, gameRenderer) || !IrisApi.getInstance().isShaderPackInUse()) {
if (!IrisApi.getInstance().isShaderPackInUse() || !canRender(camera, gameRenderer) || !isAnyHandSolid()) {
return;
}
Minecraft mc = Minecraft.getMinecraft();
ACTIVE = true;
final int blendSave = GbufferPrograms.pushBlendState();
final long cutoutSave = GbufferPrograms.pushCutoutDefaults();
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
GLStateManager.disableBlend();
GLStateManager.defaultBlendFunc();
pipeline.setPhase(WorldRenderingPhase.HAND_SOLID);
GLStateManager.glPushMatrix();
GLStateManager.glDepthMask(true); // actually write to the depth buffer, it's normally disabled at this point
mc.mcProfiler.startSection("iris_hand");
try {
setupGlState(gameRenderer, camera, tickDelta);
setupGlState(gameRenderer, camera, tickDelta);
GbufferPrograms.setBlockEntityDefaults();
GbufferPrograms.setBlockEntityDefaults();
renderingSolid = true;
renderingSolid = true;
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
} finally {
renderingSolid = false;
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
mc.mcProfiler.endSection();
GLStateManager.defaultBlendFunc();
GLStateManager.glDepthMask(false);
GLStateManager.glPopMatrix();
GLStateManager.glPopMatrix();
resetProjectionMatrix();
mc.mcProfiler.endSection();
GLStateManager.glDepthMask(depthMaskBefore);
GbufferPrograms.popCutoutDefaults(cutoutSave);
GbufferPrograms.popBlendState(blendSave);
resetProjectionMatrix();
pipeline.setPhase(WorldRenderingPhase.NONE);
renderingSolid = false;
pipeline.setPhase(WorldRenderingPhase.NONE);
ACTIVE = false;
ACTIVE = false;
}
}
// TODO: RenderType
public void renderTranslucent(float tickDelta, Camera camera, RenderGlobal gameRenderer, WorldRenderingPipeline pipeline) {
if (!canRender(camera, gameRenderer) || !isAnyHandTranslucent() || !IrisApi.getInstance().isShaderPackInUse()) {
if (!IrisApi.getInstance().isShaderPackInUse() || !canRender(camera, gameRenderer) || !isAnyHandTranslucent()) {
return;
}
Minecraft mc = Minecraft.getMinecraft();
ACTIVE = true;
final int blendSave = GbufferPrograms.pushBlendState();
final long cutoutSave = GbufferPrograms.pushCutoutDefaults();
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
GLStateManager.enableBlend();
GLStateManager.defaultBlendFunc();
pipeline.setPhase(WorldRenderingPhase.HAND_TRANSLUCENT);
GLStateManager.glPushMatrix();
GLStateManager.glDepthMask(false);
mc.mcProfiler.startSection("iris_hand_translucent");
try {
setupGlState(gameRenderer, camera, tickDelta);
setupGlState(gameRenderer, camera, tickDelta);
GbufferPrograms.setBlockEntityDefaults();
GbufferPrograms.setBlockEntityDefaults();
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
} finally {
mc.mcProfiler.endSection();
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
GLStateManager.glPopMatrix();
resetProjectionMatrix();
GLStateManager.glPopMatrix();
GLStateManager.glDepthMask(depthMaskBefore);
GbufferPrograms.popCutoutDefaults(cutoutSave);
GbufferPrograms.popBlendState(blendSave);
resetProjectionMatrix();
pipeline.setPhase(WorldRenderingPhase.NONE);
Minecraft.getMinecraft().mcProfiler.endSection();
pipeline.setPhase(WorldRenderingPhase.NONE);
ACTIVE = false;
ACTIVE = false;
}
}
private void resetProjectionMatrix() {
@@ -17,6 +17,7 @@ import com.gtnewhorizons.angelica.rendering.RenderingState;
import com.gtnewhorizons.angelica.rendering.celeritas.AngelicaRenderSectionManager;
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Setter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.gui.option.IrisVideoSettings;
@@ -106,7 +107,8 @@ public class ShadowRenderer {
private final ShadowCullState packCullingState;
private final ShadowCompositeRenderer compositeRenderer;
private boolean packHasVoxelization;
private boolean playerReflectionCaptureEnabled = true;
@Setter
private boolean playerReflectionCaptureEnabled = true;
private final boolean shouldRenderTerrain;
private final boolean shouldRenderTranslucent;
private final boolean shouldRenderEntities;
@@ -148,10 +150,19 @@ public class ShadowRenderer {
private boolean preSubmitActive;
private float preSubmittedShadowAngle = Float.NaN;
private float shadowAngleDelta() {
final Vector4f light = celestialUniforms.getShadowLightPositionInWorldSpace();
final float x = light.x(), y = light.y(), z = light.z();
final float length = (float) Math.sqrt(x * x + y * y + z * z);
return ShadowGraphGate.deltaForElevation(length > 0.0f ? y / length : 1.0f,
ShadowGraphGate.degreesToRotation(AngelicaConfig.shadowGraphAngleDelta),
AngelicaConfig.shadowGraphHorizonScale);
}
public void preSubmitGraphUpdate(int frame, boolean spectator) {
final AngelicaRenderSectionManager rsm = CeleritasWorldRenderer.getInstance().getRenderSectionManager();
final float currentShadowAngle = getShadowAngle();
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle)) {
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle, shadowAngleDelta())) {
rsm.markShadowGraphDirty();
lastGraphShadowAngle = currentShadowAngle;
}
@@ -235,13 +246,21 @@ public class ShadowRenderer {
public void setUsesImages(boolean usesImages) {
this.packHasVoxelization = packHasVoxelization || usesImages;
this.shadowWritesImages = usesImages;
}
public void setPlayerReflectionCaptureEnabled(boolean enabled) {
this.playerReflectionCaptureEnabled = enabled;
private boolean shadowWritesImages;
public boolean shadowTerrainAlwaysRelays() {
return !shouldRenderTerrain || voxelizationActive() || shadowWritesImages;
}
public static MatrixStack createShadowModelView(float sunPathRotation, float intervalSize) {
public static boolean shadowGraphGateIsNotBypassed() {
return !(Iris.getPipelineManager().getPipelineNullable() instanceof DeferredWorldRenderingPipeline deferred)
|| deferred.getShadowRenderer() == null || !deferred.getShadowRenderer().shadowTerrainAlwaysRelays();
}
public static MatrixStack createShadowModelView(float sunPathRotation, float intervalSize) {
// Use entity position for shadow matrix
final Vector3d entityPos = Camera.INSTANCE.getEntityPos();
@@ -833,7 +852,7 @@ public class ShadowRenderer {
globalTileEntities.clear();
final float currentShadowAngle = getShadowAngle();
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle)) {
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle, shadowAngleDelta())) {
CeleritasWorldRenderer.getInstance().getRenderSectionManager().markShadowGraphDirty();
lastGraphShadowAngle = currentShadowAngle;
}
@@ -843,8 +862,7 @@ public class ShadowRenderer {
final boolean consumePreSubmit = preSubmitActive;
preSubmitActive = false;
final boolean relayTerrain = !deferActive
|| voxelizationActive()
final boolean relayTerrain = shadowTerrainAlwaysRelays()
|| !targets.isTerrainSnapshotValid()
|| consumePreSubmit
|| CeleritasWorldRenderer.getInstance().getRenderSectionManager().isShadowGraphDirty()
@@ -14,6 +14,7 @@ import net.coderbot.iris.shaderpack.CloudSetting;
import net.coderbot.iris.shaderpack.texture.TextureStage;
import net.coderbot.iris.uniforms.FrameUpdateNotifier;
import net.minecraft.client.renderer.EntityRenderer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.OptionalInt;
@@ -31,6 +32,7 @@ public interface WorldRenderingPipeline {
void setPhase(WorldRenderingPhase phase);
void setInputs(InputAvailability availability);
void setSpecialCondition(SpecialCondition special);
void setDeclaredTranslucency(@Nullable Boolean translucent);
RenderTargetStateListener getRenderTargetStateListener();
int getCurrentNormalTexture();
@@ -17,6 +17,8 @@ public class ProgramSet {
private final PackDirectives packDirectives;
private final ProgramSource shadow;
private final ProgramSource shadowSolid;
private final ProgramSource shadowCutout;
private final ProgramSource shadowWater;
private final ComputeSource[] shadowCompute;
@@ -34,6 +36,8 @@ public class ProgramSet {
private final ProgramSource gbuffersTextured;
private final ProgramSource gbuffersTexturedLit;
private final ProgramSource gbuffersTerrain;
private final ProgramSource gbuffersTerrainSolid;
private final ProgramSource gbuffersTerrainCutout;
private ProgramSource gbuffersDamagedBlock;
private final ProgramSource gbuffersSkyBasic;
private final ProgramSource gbuffersSkyTextured;
@@ -43,6 +47,8 @@ public class ProgramSet {
private final ProgramSource gbuffersEntitiesTrans;
private final ProgramSource gbuffersEntitiesGlowing;
private final ProgramSource gbuffersLightning;
private final ProgramSource gbuffersParticles;
private final ProgramSource gbuffersParticlesTrans;
private final ProgramSource gbuffersGlint;
private final ProgramSource gbuffersEntityEyes;
private final ProgramSource gbuffersBlock;
@@ -83,6 +89,10 @@ public class ProgramSet {
// - https://github.com/IrisShaders/Iris/issues/987
this.shadow = readProgramSource(directory, sourceProvider, "shadow", this, shaderProperties,
BlendModeOverride.OFF);
this.shadowSolid = readProgramSource(directory, sourceProvider, "shadow_solid", this, shaderProperties,
BlendModeOverride.OFF);
this.shadowCutout = readProgramSource(directory, sourceProvider, "shadow_cutout", this, shaderProperties,
BlendModeOverride.OFF);
this.shadowWater = readProgramSource(directory, sourceProvider, "shadow_water", this, shaderProperties,
BlendModeOverride.OFF);
this.shadowCompute = readComputeArray(directory, sourceProvider, "shadow");
@@ -114,6 +124,8 @@ public class ProgramSet {
this.gbuffersTextured = readProgramSource(directory, sourceProvider, "gbuffers_textured", this, shaderProperties);
this.gbuffersTexturedLit = readProgramSource(directory, sourceProvider, "gbuffers_textured_lit", this, shaderProperties);
this.gbuffersTerrain = readProgramSource(directory, sourceProvider, "gbuffers_terrain", this, shaderProperties);
this.gbuffersTerrainSolid = readProgramSource(directory, sourceProvider, "gbuffers_terrain_solid", this, shaderProperties);
this.gbuffersTerrainCutout = readProgramSource(directory, sourceProvider, "gbuffers_terrain_cutout", this, shaderProperties);
this.gbuffersDamagedBlock = readProgramSource(directory, sourceProvider, "gbuffers_damagedblock", this, shaderProperties);
this.gbuffersSkyBasic = readProgramSource(directory, sourceProvider, "gbuffers_skybasic", this, shaderProperties);
this.gbuffersSkyTextured = readProgramSource(directory, sourceProvider, "gbuffers_skytextured", this, shaderProperties);
@@ -123,6 +135,8 @@ public class ProgramSet {
this.gbuffersEntitiesTrans = readProgramSource(directory, sourceProvider, "gbuffers_entities_translucent", this, shaderProperties);
this.gbuffersEntitiesGlowing = readProgramSource(directory, sourceProvider, "gbuffers_entities_glowing", this, shaderProperties);
this.gbuffersLightning = readProgramSource(directory, sourceProvider, "gbuffers_lightning", this, shaderProperties);
this.gbuffersParticles = readProgramSource(directory, sourceProvider, "gbuffers_particles", this, shaderProperties);
this.gbuffersParticlesTrans = readProgramSource(directory, sourceProvider, "gbuffers_particles_translucent", this, shaderProperties);
this.gbuffersGlint = readProgramSource(directory, sourceProvider, "gbuffers_armor_glint", this, shaderProperties);
this.gbuffersEntityEyes = readProgramSource(directory, sourceProvider, "gbuffers_spidereyes", this, shaderProperties);
this.gbuffersBlock = readProgramSource(directory, sourceProvider, "gbuffers_block", this, shaderProperties);
@@ -224,6 +238,8 @@ public class ProgramSet {
List<ComputeSource> computes = new ArrayList<>();
programs.add(shadow);
programs.add(shadowSolid);
programs.add(shadowCutout);
programs.add(shadowWater);
programs.addAll(Arrays.asList(shadowcomp));
programs.addAll(Arrays.asList(begin));
@@ -231,8 +247,9 @@ public class ProgramSet {
programs.addAll (Arrays.asList(
gbuffersBasic, gbuffersBeaconBeam, gbuffersTextured, gbuffersTexturedLit, gbuffersTerrain,
gbuffersTerrainSolid, gbuffersTerrainCutout,
gbuffersDamagedBlock, gbuffersSkyBasic, gbuffersSkyTextured, gbuffersClouds, gbuffersWeather,
gbuffersEntities, gbuffersEntitiesTrans, gbuffersEntitiesGlowing, gbuffersLightning, gbuffersGlint, gbuffersEntityEyes, gbuffersBlock, gbuffersBlockTrans,
gbuffersEntities, gbuffersEntitiesTrans, gbuffersEntitiesGlowing, gbuffersLightning, gbuffersParticles, gbuffersParticlesTrans, gbuffersGlint, gbuffersEntityEyes, gbuffersBlock, gbuffersBlockTrans,
gbuffersHand, dhShadow, dhTerrain, dhGeneric, dhWater
));
@@ -309,6 +326,14 @@ public class ProgramSet {
return shadow.requireValid();
}
public Optional<ProgramSource> getShadowSolid() {
return shadowSolid.requireValid();
}
public Optional<ProgramSource> getShadowCutout() {
return shadowCutout.requireValid();
}
public Optional<ProgramSource> getShadowWater() {
return shadowWater.requireValid();
}
@@ -353,6 +378,14 @@ public class ProgramSet {
return gbuffersTerrain.requireValid();
}
public Optional<ProgramSource> getGbuffersTerrainSolid() {
return gbuffersTerrainSolid.requireValid();
}
public Optional<ProgramSource> getGbuffersTerrainCutout() {
return gbuffersTerrainCutout.requireValid();
}
public Optional<ProgramSource> getGbuffersDamagedBlock() {
return gbuffersDamagedBlock.requireValid();
}
@@ -389,6 +422,14 @@ public class ProgramSet {
return gbuffersLightning.requireValid();
}
public Optional<ProgramSource> getGbuffersParticles() {
return gbuffersParticles.requireValid();
}
public Optional<ProgramSource> getGbuffersParticlesTrans() {
return gbuffersParticlesTrans.requireValid();
}
public Optional<ProgramSource> getGbuffersGlint() {
return gbuffersGlint.requireValid();
}
@@ -428,6 +469,8 @@ public class ProgramSet {
public Optional<ProgramSource> get(ProgramId programId) {
return switch (programId) {
case Shadow -> getShadow();
case ShadowSolid -> getShadowSolid();
case ShadowCutout -> getShadowCutout();
case ShadowWater -> first(getShadowWater(), getShadow());
case Basic -> getGbuffersBasic();
case Line -> gbuffersLine.requireValid();
@@ -437,6 +480,8 @@ public class ProgramSet {
case SkyTextured -> getGbuffersSkyTextured();
case Clouds -> getGbuffersClouds();
case Terrain -> getGbuffersTerrain();
case TerrainSolid -> getGbuffersTerrainSolid();
case TerrainCutout -> getGbuffersTerrainCutout();
case DamagedBlock -> getGbuffersDamagedBlock();
case Block -> getGbuffersBlock();
case BlockTrans -> getGbuffersBlockTrans();
@@ -445,6 +490,8 @@ public class ProgramSet {
case EntitiesTrans -> getGbuffersEntitiesTrans();
case EntitiesGlowing -> getGbuffersEntitiesGlowing();
case Lightning -> getGbuffersLightning();
case Particles -> getGbuffersParticles();
case ParticlesTrans -> getGbuffersParticlesTrans();
case ArmorGlint -> getGbuffersGlint();
case SpiderEyes -> getGbuffersEntityEyes();
case Hand -> getGbuffersHand();
@@ -27,7 +27,9 @@ public enum ProgramId {
Terrain(ProgramGroup.Gbuffers, "terrain", TexturedLit),
TerrainSolid(ProgramGroup.Gbuffers, "terrain_solid", Terrain),
TerrainCutoutMip(ProgramGroup.Gbuffers, "terrain_cutout_mip", Terrain),
// terrain_cutout_mip program was removed in modern versions of Minecraft. As the goal is to be a faithful backport of Iris
// we likely should just group cutout and cutout mipped into the same program.
// CUTOUT_MIPPED essentially became CUTOUT and now everything uses mipmaps in modern. We don't as of writing.
TerrainCutout(ProgramGroup.Gbuffers, "terrain_cutout", Terrain),
DamagedBlock(ProgramGroup.Gbuffers, "damagedblock", Terrain),
@@ -40,6 +42,8 @@ public enum ProgramId {
EntitiesTrans(ProgramGroup.Gbuffers, "entities_translucent", Entities),
EntitiesGlowing(ProgramGroup.Gbuffers, "entities_glowing", Entities),
Lightning(ProgramGroup.Gbuffers, "lightning", Entities),
Particles(ProgramGroup.Gbuffers, "particles", TexturedLit),
ParticlesTrans(ProgramGroup.Gbuffers, "particles_translucent", Particles),
ArmorGlint(ProgramGroup.Gbuffers, "armor_glint", Textured),
SpiderEyes(ProgramGroup.Gbuffers, "spidereyes", Textured,
new BlendModeOverride(new BlendState(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE)),
@@ -82,6 +82,10 @@ public class OptionMenuContainer {
// Screens will call this when they contain a "*" element, so that the list of
// unused options can be added after all other screens have been resolved
public boolean hasMenuElements() {
return !this.originalMainElements.isEmpty();
}
public void queueForUnusedOptionDump(int index, List<OptionMenuElement> elementList) {
this.unusedOptionDumpQueue.put(elementList, index);
}
@@ -2,14 +2,24 @@ package net.coderbot.iris.shadows;
public final class ShadowGraphGate {
public static final float SHADOW_ANGLE_GRAPH_DELTA = 0.0005f;
public static final float DEFAULT_ANGLE_DELTA_DEGREES = 0.03f;
public static final float DEFAULT_HORIZON_SCALE = 0.5f;
private ShadowGraphGate() {}
public static boolean shouldMarkDirty(float last, float current) {
float delta = current - last;
if (delta > 0.5f) delta -= 1.0f;
if (delta < -0.5f) delta += 1.0f;
return Float.isNaN(last) || Math.abs(delta) > SHADOW_ANGLE_GRAPH_DELTA;
public static float degreesToRotation(float degrees) {
return degrees / 360.0f;
}
public static boolean shouldMarkDirty(float last, float current, float delta) {
float d = current - last;
if (d > 0.5f) d -= 1.0f;
if (d < -0.5f) d += 1.0f;
return Float.isNaN(last) || Math.abs(d) > delta;
}
public static float deltaForElevation(float sinElevation, float baseDelta, float minElevationScale) {
return baseDelta * Math.max(minElevationScale, sinElevation * sinElevation);
}
}
@@ -54,13 +54,20 @@ public class CapturedRenderingState {
}
}
public void setCurrentEntity(int entity) {
private void setCurrentEntity(int entity) {
this.currentRenderedEntity = entity;
if (this.entityIdListener != null) {
this.entityIdListener.run();
}
setCurrentRenderedItem(0);
}
/**
* I should have done this a while ago, better solution to not forget these exist in tandem.
*/
public void setCurrentEntityAndItem(int entity, int item) {
setCurrentEntity(entity);
setCurrentRenderedItem(item);
}
public void setCurrentEntityColor(float r, float g, float b, float a) {
@@ -16,6 +16,7 @@ import net.minecraft.util.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import static net.coderbot.iris.gl.uniform.UniformUpdateFrequency.PER_FRAME;
import static org.joml.Math.clamp;
public final class IdMapUniforms {
@@ -71,6 +72,13 @@ public final class IdMapUniforms {
lightValue = 0;
}
/**
* Damn you chisel beacon
*/
private static int clampEmission(int emission) {
return clamp(0, 15, emission);
}
public void update() {
EntityPlayer player = Minecraft.getMinecraft().thePlayer;
@@ -98,7 +106,7 @@ public final class IdMapUniforms {
intID = itemIdMap.applyAsInt(new NamespacedId(heldItemId.getResourceDomain(), heldItemId.getResourcePath()));
IrisItemLightProvider lightProvider = (IrisItemLightProvider) heldItem;
lightValue = lightProvider.getLightEmission(Minecraft.getMinecraft().thePlayer, heldStack);
lightValue = clampEmission(lightProvider.getLightEmission(Minecraft.getMinecraft().thePlayer, heldStack));
if (applyOldHandLight) {
lightProvider = applyOldHandLighting(player, lightProvider);
@@ -119,7 +127,7 @@ public final class IdMapUniforms {
}
IrisItemLightProvider lightProvider = (IrisItemLightProvider) offHandItem;
int newEmission = lightProvider.getLightEmission(Minecraft.getMinecraft().thePlayer, offHandStack);
int newEmission = clampEmission(lightProvider.getLightEmission(Minecraft.getMinecraft().thePlayer, offHandStack));
if (lightValue < newEmission) {
lightValue = newEmission;
@@ -31,7 +31,7 @@ public class ItemIdManager {
* Set the item ID for a Block rendered outside of terrain.
*/
public static void setBlockId(Block block, int metadata) {
CapturedRenderingState.INSTANCE.setCurrentRenderedItem(Math.max(0, ItemMaterialHelper.getMaterialId(block, metadata)));
CapturedRenderingState.INSTANCE.setCurrentRenderedItem(ItemMaterialHelper.getMaterialId(block, metadata));
}
/**
@@ -27,15 +27,13 @@ public class ItemMaterialHelper {
/**
* Get the material ID for an ItemStack.
* For ItemBlock: checks block.properties first, then item.properties.
* For other items: checks item.properties only.
* 0 if unmapped.
*
* @param itemStack The item stack to get material ID for
* @return The material ID, or -1 if not found
*/
public static int getMaterialId(ItemStack itemStack) {
if (itemStack == null || itemStack.getItem() == null) {
return -1;
return 0;
}
// Check item NBT conditions first
@@ -56,7 +54,7 @@ public class ItemMaterialHelper {
public static int getMaterialId(Item item, int metadata) {
if (item == null) {
return -1;
return 0;
}
// Check cache first
@@ -84,11 +82,11 @@ public class ItemMaterialHelper {
/**
* Get the material ID for a Block rendered outside of terrain.
*
* @return The material ID, or -1 if not found
* @return The material ID, or 0 if not found
*/
public static int getMaterialId(Block block, int metadata) {
if (block == null) {
return -1;
return 0;
}
Int2IntMap metadataCache = BLOCK_MATERIAL_CACHE.get(block);
@@ -112,60 +110,44 @@ public class ItemMaterialHelper {
}
private static int lookupBlockMaterialId(Block block, int metadata) {
Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
if (blockMetaMatches != null) {
Int2IntMap metaMap = blockMetaMatches.get(block);
if (metaMap != null) {
int id = BlockMaterialMapping.resolveId(metaMap, metadata);
if (id != -1) {
return id;
}
}
}
final Item item = Item.getItemFromBlock(block);
return item != null ? lookupItemPropertiesId(item) : -1;
return lookupBlockPropertiesId(block, metadata);
}
/**
* Perform the actual material ID lookup without caching.
*
* For block items (ItemBlock): use block.properties first, then fall back to item.properties.
* For non-block items: use item.properties only.
*
* This matches modern Iris behavior where block items use block IDs and non-block items use item IDs.
*/
private static int lookupMaterialId(Item item, int metadata) {
// For ItemBlock: check block.properties first
if (item instanceof ItemBlock itemBlock) {
final Block block = itemBlock.field_150939_a; // The block this item places
if (block != null) {
Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
if (blockMetaMatches != null) {
Int2IntMap metaMap = blockMetaMatches.get(block);
if (metaMap != null) {
int id = BlockMaterialMapping.resolveId(metaMap, itemBlock.getMetadata(metadata));
if (id != -1) {
return id;
}
}
}
}
return block == null ? 0 : lookupBlockPropertiesId(block, itemBlock.getMetadata(metadata));
}
// Fall back to item.properties
return lookupItemPropertiesId(item);
}
private static int lookupBlockPropertiesId(Block block, int metadata) {
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
if (blockMetaMatches == null) {
return 0;
}
final Int2IntMap metaMap = blockMetaMatches.get(block);
if (metaMap == null) {
return 0;
}
final int id = BlockMaterialMapping.resolveId(metaMap, metadata);
return id == -1 ? 0 : id;
}
private static int lookupItemPropertiesId(Item item) {
final Object2IntFunction<NamespacedId> itemIds = BlockRenderingSettings.INSTANCE.getItemIds();
if (itemIds == null) {
return -1;
return 0;
}
final NamespacedId name = getCachedItemName(item);
return name == null ? -1 : itemIds.applyAsInt(name);
return name == null ? 0 : itemIds.applyAsInt(name);
}
/**
@@ -101,13 +101,18 @@ sodium.options.use_no_error_context.name=No Error Context
sodium.options.use_no_error_context.tooltip=If enabled, the OpenGL context will be created with error checking disabled. This may slightly improve performance, but it also increases the risk that the game will crash instead of gracefully handling OpenGL errors. You should disable this option if you are experiencing sudden unexplained crashes.
sodium.options.async_occlusion_mode.name=Async Occlusion
sodium.options.async_occlusion_mode.tooltip=Controls which render passes use asynchronous occlusion culling. ONLY_SHADOW (default) runs async occlusion only for the shadow pass, keeping the main render list synchronous. EVERYTHING runs all passes async, which may cause 1-frame visibility lag during fast camera movement. NONE disables async occlusion entirely.
sodium.options.async_occlusion_mode.none=None
sodium.options.async_occlusion_mode.only_shadow=Only Shadow
sodium.options.async_occlusion_mode.everything=Everything
sodium.options.use_occlusion_culling.name=Occlusion Culling
sodium.options.use_occlusion_culling.tooltip=If enabled, chunks that are hidden behind other chunks will not be rendered. Disabling this will significantly reduce performance but can be useful for debugging.
sodium.options.threads.value=%s thread(s)
sodium.options.default=Default
sodium.options.buttons.undo=Undo
sodium.options.buttons.apply=Apply
sodium.options.buttons.donate=Buy coffee for JellySquid!
sodium.options.anaglyph.tooltip=If enabled, the world will be rendered twice with red/cyan color masks.
sodium.options.show_cape.tooltip=If enabled, your cape will be shown.
rso.search_bar_empty=Search options...
options.dynamic_fov=Dynamic FOV
options.dynamic_fov.tooltip=If enabled, the game's field of view will change based on the player's speed.
@@ -115,6 +120,15 @@ options.framerate=%s FPS
iris.shaders.toggled=Toggled shaders to %s!
iris.shaders.disabled=Shaders disabled!
iris.shaders.reloaded=Shaders Reloaded!
iris.shaders.reloaded.failure=Failed to reload shaders! Reason: %s
iris.shaders.toggled.failure=Failed to toggle shaders! Reason: %s
key.category.iris.keybinds=Iris Keybinds
iris.keybind.reload=Reload Shaders
iris.keybind.toggleShaders=Toggle Shaders
iris.keybind.shaderPackSelection=Shaderpack Selection Screen
angelica.keybind.zoom=Zoom
options.iris.apply=Apply
options.iris.refresh=Refresh
@@ -130,6 +144,7 @@ options.iris.shaderPackSelection.failedAdd=Files were not valid Shader Packs
options.iris.shaderPackSelection.failedAddSingle="%s" is not a valid Shader Pack
options.iris.shaderPackSelection.copyError=Could not add Shader Pack "%s"
options.iris.shaderPackSelection.copyErrorAlreadyExists="%s" is already in your Shader Packs folder!
options.iris.shaderPackOptions.noOptions="%s" has no configurable settings
options.iris.shaderPackOptions.tooManyFiles=Cannot import multiple Shader Settings files!
options.iris.shaderPackOptions.failedImport=Failed to import Shader Settings file "%s"
options.iris.shaderPackOptions.importedSettings=Imported Shader Settings from "%s"
@@ -175,10 +190,14 @@ options.dynamic_lights_cull_timeout=Culling Timeout (ticks)
options.dynamic_lights_cull_timeout.tooltip=Maximum ticks a hidden chunk can wait before being forced to rebuild. Lower = more responsive but more GPU work.
options.dynamiclights.page=Dynamic Lights
options.dynamiclights.entities=Configure Entities...
options.dynamiclights.entities.tooltip=Choose which entity types emit dynamic light.
options.dynamiclights.entities.title=Dynamic Lights - Entities
options.dynamiclights.entities.info=Toggle which entity types emit dynamic light. Some entries are for mod compatibility.
options.dynamiclights.entities.search=Search (@mod, $on, $off)
options.dynamiclights.entity.tooltip=Toggle dynamic lighting for %s entities. Disable to improve performance if this entity type emits unwanted lights.
options.dynamiclights.entities.enableall=Enable All
options.dynamiclights.entities.disableall=Disable All
options.dynamiclights.entities.enableshown=Enable Shown
options.dynamiclights.entities.disableshown=Disable Shown
options.angelica.search.no_results=No matching options
options.angelica.fontconfig=Font Configuration...
@@ -224,6 +243,12 @@ options.angelica.droppedItemLimit=Dropped Item Render Limit
options.angelica.droppedItemLimit.tooltip=The maximum number of dropped items that will be rendered. Lower values can improve performance.
options.angelica.mobSpawnerRenderDistance=Mob spawner render distance
options.angelica.mobSpawnerRenderDistance.tooltip=Render distance for the spinning mob inside mod spawners
options.angelica.shadowGraphAngleDelta=Shadow Graph Rebuild Threshold
options.angelica.shadowGraphAngleDelta.tooltip=How much a celestial body can move before the shadow map can update. Note this option is disabled when a shader pack utilizes voxelization features.
options.angelica.shadowGraphAngleDelta.always=Always
options.angelica.shadowGraphHorizonScale=Shadow Graph Horizon Tightening
options.angelica.shadowGraphHorizonScale.tooltip=Adjusts the rate the shadow map rebuilds when when a celestial body is near the horizon. Rate is sinusoidal.
options.angelica.shadowGraphHorizonScale.off=OFF
options.angelica.itemdisplaylistcount=Item Renderer Cache Limit
options.angelica.itemdisplaylistcount.tooltip=The upper limit of VBO's to cache in the optimized item renderer. Higher values will significantly increase performance, but may result in increased VRAM usage.
options.angelica.threadedChunkBuilding=Threaded Chunk Building
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff. Show more