mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 17:55:08 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a6b1d14e9 | ||
|
|
3c6b7e0fe9 | ||
|
|
6b9cb265f8 | ||
|
|
0d2e82fba5 | ||
|
|
c340a200a8 | ||
|
|
358f495471 | ||
|
|
785cd05a37 | ||
|
|
433d08bc5f | ||
|
|
12840f8953 | ||
|
|
3980599f7e | ||
|
|
299e9d835d | ||
|
|
30f0835a21 | ||
|
|
3717d2d46e | ||
|
|
d69fd20a8b |
No files matched your search
@@ -26,12 +26,20 @@ public final class CaptureGate {
|
||||
public static final boolean TOOL_ATTACHED = detectCaptureTool();
|
||||
public static boolean markersThisFrame = true;
|
||||
public static boolean tracyConnected;
|
||||
private static boolean toolAttached = TOOL_ATTACHED;
|
||||
|
||||
private CaptureGate() {}
|
||||
|
||||
public static void refresh() {
|
||||
tracyConnected = Tracy.isConnected();
|
||||
markersThisFrame = gateValue(FORCED, TOOL_ATTACHED, tracyConnected);
|
||||
markersThisFrame = gateValue(FORCED, toolAttached, tracyConnected);
|
||||
}
|
||||
|
||||
static void probeGlTool() {
|
||||
if (RenderSystem.hasExtension("GL_EXT_debug_tool")) {
|
||||
toolAttached = true;
|
||||
LOGGER.info("Capture tool detected via GL_EXT_debug_tool; instrumentation enabled");
|
||||
}
|
||||
}
|
||||
|
||||
static boolean gateValue(boolean forced, boolean toolAttached, boolean tracyConnected) {
|
||||
@@ -39,7 +47,7 @@ public final class CaptureGate {
|
||||
}
|
||||
|
||||
public static boolean enabledAtStartup() {
|
||||
return FORCED || TOOL_ATTACHED;
|
||||
return FORCED || toolAttached;
|
||||
}
|
||||
|
||||
private static boolean detectCaptureTool() {
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ColorMaskStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/// A draw between [#beginTracking] and [#endTracking] is left to the caller's replay instead of repeated. A replay
|
||||
/// ignores the depth mask, which a previous renderer may leak differently into the pass and the replay, and repeats
|
||||
/// set GL state through the backend, never the GLSM cache.
|
||||
public final class DepthEcho {
|
||||
|
||||
private static int framebuffer;
|
||||
private static int source;
|
||||
private static boolean echoed;
|
||||
private static boolean tracking;
|
||||
private static boolean hit;
|
||||
private static int immediate;
|
||||
private static boolean replaying;
|
||||
private static boolean replayDepthWrite;
|
||||
|
||||
private DepthEcho() {}
|
||||
|
||||
public static void arm(int framebuffer, int source) {
|
||||
DepthEcho.framebuffer = framebuffer;
|
||||
DepthEcho.source = source;
|
||||
}
|
||||
|
||||
public static void disarm() {
|
||||
framebuffer = 0;
|
||||
tracking = false;
|
||||
hit = false;
|
||||
endReplay();
|
||||
}
|
||||
|
||||
public static boolean armed() {
|
||||
return framebuffer != 0;
|
||||
}
|
||||
|
||||
public static void beginTracking() {
|
||||
tracking = framebuffer != 0;
|
||||
hit = false;
|
||||
}
|
||||
|
||||
public static boolean endTracking() {
|
||||
tracking = false;
|
||||
final boolean was = hit;
|
||||
hit = false;
|
||||
return was;
|
||||
}
|
||||
|
||||
public static void beginImmediate() {
|
||||
immediate++;
|
||||
}
|
||||
|
||||
public static void endImmediate() {
|
||||
immediate--;
|
||||
}
|
||||
|
||||
public static void beginReplay() {
|
||||
replaying = true;
|
||||
}
|
||||
|
||||
public static void endReplay() {
|
||||
replaying = false;
|
||||
restoreReplayDepthMask();
|
||||
}
|
||||
|
||||
public static boolean replaying() {
|
||||
return replaying;
|
||||
}
|
||||
|
||||
public static boolean takeEchoed() {
|
||||
final boolean was = echoed;
|
||||
echoed = false;
|
||||
return was;
|
||||
}
|
||||
|
||||
public static boolean skipsCall() {
|
||||
return replaySkips() && !DisplayListManager.isRecording();
|
||||
}
|
||||
|
||||
public static boolean skipsDraw() {
|
||||
if (!replaying) return false;
|
||||
if (replaySkips()) return true;
|
||||
if (!GLSMDepthStencilAlpha.getDepthState().isEnabled()) {
|
||||
RENDER_BACKEND.depthMask(true);
|
||||
replayDepthWrite = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean replaySkips() {
|
||||
return replaying && !(blendsVisibly() && GLSMFramebuffer.getDrawFramebuffer() == framebuffer);
|
||||
}
|
||||
|
||||
public static boolean begin() {
|
||||
if (replayDepthWrite) {
|
||||
restoreReplayDepthMask();
|
||||
return false;
|
||||
}
|
||||
if (framebuffer == 0 || GLSMDepthStencilAlpha.getDepthState().isEnabled() || !blendsVisibly() || GLSMFramebuffer.getDrawFramebuffer() != source) return false;
|
||||
echoed = true;
|
||||
if (tracking && immediate == 0 && !BatchStateGuard.isSuspended()) {
|
||||
hit = true;
|
||||
return false;
|
||||
}
|
||||
RENDER_BACKEND.bindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||
RENDER_BACKEND.depthMask(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void end() {
|
||||
RENDER_BACKEND.depthMask(false);
|
||||
RENDER_BACKEND.bindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, source);
|
||||
}
|
||||
|
||||
private static void restoreReplayDepthMask() {
|
||||
if (!replayDepthWrite) return;
|
||||
replayDepthWrite = false;
|
||||
RENDER_BACKEND.depthMask(GLSMDepthStencilAlpha.getDepthState().isEnabled());
|
||||
}
|
||||
|
||||
private static boolean blendsVisibly() {
|
||||
if (!GLSMDepthStencilAlpha.getDepthTest().isEnabled() || !GLSMBlend.getBlendMode().isEnabled()) return false;
|
||||
if (GLSMDepthStencilAlpha.getDepthState().getFunc() == GL11.GL_EQUAL) return false;
|
||||
final ColorMaskStack mask = GLSMBlend.getColorMask();
|
||||
return mask.red || mask.green || mask.blue || mask.alpha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ColorMaskStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.DepthStateStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/// Per-draw state for depth peeling. Color passes skip the depth test but keep the draw's depth writes, so the target
|
||||
/// ends with the nearest peeled depth and the rest pass's own test keeps only what is in front of it. Depth and rest
|
||||
/// passes discard fragments that would not change the target under the draw's blend, so transparent texels do not take
|
||||
/// a peel. Draws with an application program cannot run [#GLSL], so they skip the peels and draw unsorted afterwards.
|
||||
public final class DepthPeel {
|
||||
|
||||
public static final int TEXTURE_UNIT = 15;
|
||||
public static final int SCENE_TEXTURE_UNIT = 14;
|
||||
|
||||
private static final int PASS_DEPTH = 0;
|
||||
private static final int PASS_COLOR = 1;
|
||||
private static final int PASS_REST = 2;
|
||||
private static final int UNBOUNDED = 4;
|
||||
private static final int COVER_ALPHA = 0;
|
||||
private static final int COVER_RGB = 8;
|
||||
private static final int COVER_RGB_ALPHA = 16;
|
||||
private static final int COVER_ALL = 24;
|
||||
|
||||
public static final String GLSL = "#define PEEL_PASS_COLOR " + PASS_COLOR + "\n"
|
||||
+ "#define PEEL_UNBOUNDED " + UNBOUNDED + "\n"
|
||||
+ "#define PEEL_COVER_MASK " + COVER_ALL + "\n"
|
||||
+ "#define PEEL_COVER_RGB " + COVER_RGB + "\n"
|
||||
+ "#define PEEL_COVER_RGB_ALPHA " + COVER_RGB_ALPHA + "\n"
|
||||
+ "#define PEEL_COVER_ALL " + COVER_ALL + "\n"
|
||||
+ "#define PEEL_SCENE_TOLERANCE 6.0e-8\n"
|
||||
+ "#define PEEL_WINDOW 2.4e-7\n"
|
||||
+ "#define PEEL_MIN_COVERAGE 0.004\n"
|
||||
+ """
|
||||
uniform sampler2D u_PeelDepth;
|
||||
uniform sampler2D u_PeelScene;
|
||||
uniform int u_PeelMode;
|
||||
void peelTest(vec4 c) {
|
||||
ivec2 texel = ivec2(gl_FragCoord.xy);
|
||||
float z = gl_FragCoord.z;
|
||||
if (z > texelFetch(u_PeelScene, texel, 0).r + PEEL_SCENE_TOLERANCE) discard;
|
||||
float bound = texelFetch(u_PeelDepth, texel, 0).r;
|
||||
if ((u_PeelMode & 3) == PEEL_PASS_COLOR) {
|
||||
if (z > bound || z < bound - PEEL_WINDOW) discard;
|
||||
return;
|
||||
}
|
||||
if ((u_PeelMode & PEEL_UNBOUNDED) == 0 && z >= bound - PEEL_WINDOW) discard;
|
||||
int cover = u_PeelMode & PEEL_COVER_MASK;
|
||||
float rgb = max(c.r, max(c.g, c.b));
|
||||
float coverage = cover == PEEL_COVER_ALL ? 1.0 : cover == PEEL_COVER_RGB ? rgb : cover == PEEL_COVER_RGB_ALPHA ? rgb * c.a : c.a;
|
||||
if (coverage < PEEL_MIN_COVERAGE) discard;
|
||||
}
|
||||
""";
|
||||
|
||||
public enum Pass { OFF, DEPTH, COLOR, REST, APPLICATION }
|
||||
|
||||
private static Pass pass = Pass.OFF;
|
||||
private static boolean unbounded;
|
||||
private static boolean overridden;
|
||||
private static boolean skippedApplicationDraws;
|
||||
|
||||
private DepthPeel() {}
|
||||
|
||||
public static void setPass(Pass next, boolean unbounded) {
|
||||
if (pass == Pass.OFF) skippedApplicationDraws = false;
|
||||
pass = next;
|
||||
DepthPeel.unbounded = unbounded;
|
||||
GLSMConfig.depthPeel = next == Pass.DEPTH || next == Pass.COLOR || next == Pass.REST;
|
||||
}
|
||||
|
||||
public static boolean active() {
|
||||
return pass != Pass.OFF;
|
||||
}
|
||||
|
||||
public static boolean skippedApplicationDraws() {
|
||||
return skippedApplicationDraws;
|
||||
}
|
||||
|
||||
public static int shaderMode() {
|
||||
return switch (pass) {
|
||||
case DEPTH -> PASS_DEPTH | coverage() | (unbounded ? UNBOUNDED : 0);
|
||||
case COLOR -> PASS_COLOR;
|
||||
default -> PASS_REST | coverage();
|
||||
};
|
||||
}
|
||||
|
||||
private static int coverage() {
|
||||
if (!GLSMBlend.getBlendMode().isEnabled()) return COVER_ALL;
|
||||
final BlendStateStack blend = GLStateManager.ctx().blendState;
|
||||
if (blend.getDstRgb() != GL11.GL_ONE) return COVER_ALPHA;
|
||||
return blend.getSrcRgb() == GL11.GL_SRC_ALPHA ? COVER_RGB_ALPHA : COVER_RGB;
|
||||
}
|
||||
|
||||
public static boolean begin() {
|
||||
if (pass == Pass.OFF) return true;
|
||||
final boolean fixedFunction = GLSMPrograms.getActiveProgram() == 0;
|
||||
if (pass == Pass.APPLICATION) return !fixedFunction;
|
||||
if (!fixedFunction) {
|
||||
skippedApplicationDraws = true;
|
||||
return false;
|
||||
}
|
||||
if (pass == Pass.DEPTH) {
|
||||
RENDER_BACKEND.colorMask(false, false, false, false);
|
||||
RENDER_BACKEND.depthMask(true);
|
||||
RENDER_BACKEND.depthFunc(GL11.GL_GREATER);
|
||||
overridden = true;
|
||||
} else if (pass == Pass.COLOR) {
|
||||
RENDER_BACKEND.depthFunc(GL11.GL_ALWAYS);
|
||||
overridden = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void end() {
|
||||
if (!overridden) return;
|
||||
overridden = false;
|
||||
final ColorMaskStack mask = GLSMBlend.getColorMask();
|
||||
final DepthStateStack depth = GLSMDepthStencilAlpha.getDepthState();
|
||||
RENDER_BACKEND.colorMask(mask.red, mask.green, mask.blue, mask.alpha);
|
||||
RENDER_BACKEND.depthMask(depth.isEnabled());
|
||||
RENDER_BACKEND.depthFunc(depth.getFunc());
|
||||
}
|
||||
}
|
||||
@@ -149,19 +149,46 @@ public final class GLSMBlend {
|
||||
}
|
||||
|
||||
static void issueBlendFunc(BlendStateStack blend) {
|
||||
if (GLSMConfig.hudCacheOverride) {
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), blend.getSrcAlpha(), blend.getDstAlpha());
|
||||
}
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), emittedSrcAlpha(blend), emittedDstAlpha(blend));
|
||||
}
|
||||
|
||||
private static boolean addsWithoutCoverage(BlendStateStack blend) {
|
||||
return blend.getDstRgb() == GL11.GL_ONE;
|
||||
}
|
||||
|
||||
private static int emittedSrcAlpha(BlendStateStack blend) {
|
||||
if (GLSMConfig.layerBlending) return addsWithoutCoverage(blend) ? GL11.GL_ZERO : GL11.GL_ONE;
|
||||
return GLSMConfig.hudCacheOverride ? GL11.GL_ONE : blend.getSrcAlpha();
|
||||
}
|
||||
|
||||
private static int emittedDstAlpha(BlendStateStack blend) {
|
||||
if (GLSMConfig.layerBlending) return addsWithoutCoverage(blend) ? GL11.GL_ONE : GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
return GLSMConfig.hudCacheOverride ? GL11.GL_ONE_MINUS_SRC_ALPHA : blend.getDstAlpha();
|
||||
}
|
||||
|
||||
public static void setHudCacheOverride(boolean enabled) {
|
||||
if (GLSMConfig.hudCacheOverride == enabled) return;
|
||||
GLSMConfig.hudCacheOverride = enabled;
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isOverrideHeld()) return;
|
||||
reissueBlendFunc();
|
||||
}
|
||||
|
||||
public static void setLayerBlending(boolean enabled) {
|
||||
if (GLSMConfig.layerBlending == enabled) return;
|
||||
GLSMConfig.layerBlending = enabled;
|
||||
reissueBlendFunc();
|
||||
}
|
||||
|
||||
public static boolean isLayerBlending() {
|
||||
return GLSMConfig.layerBlending;
|
||||
}
|
||||
|
||||
private static void reissueBlendFunc() {
|
||||
final BlendStateStack blend = ctx().blendState;
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isOverrideHeld()) {
|
||||
blend.setFuncUnknownState();
|
||||
return;
|
||||
}
|
||||
issueBlendFunc(blend);
|
||||
blend.clearFuncUnknownState();
|
||||
}
|
||||
@@ -226,11 +253,10 @@ public final class GLSMBlend {
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
issueBlendFunc(glCtx.blendState);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
final boolean hudCache = GLSMConfig.hudCacheOverride;
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcRgb;
|
||||
GLSMHooks.blendFuncChangeEvent.dstRgb = dstRgb;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = hudCache ? GL11.GL_ONE : srcAlpha;
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = hudCache ? GL11.GL_ONE_MINUS_SRC_ALPHA : dstAlpha;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = emittedSrcAlpha(glCtx.blendState);
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = emittedDstAlpha(glCtx.blendState);
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
@@ -76,6 +77,7 @@ public final class GLSMDraws {
|
||||
ImmediateModeRecorder.begin(mode);
|
||||
return;
|
||||
}
|
||||
BatchStateGuard.beforeDraw();
|
||||
ImmediateModeRecorder.beginLive(mode);
|
||||
}
|
||||
|
||||
@@ -141,6 +143,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ByteBuffer indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -174,6 +177,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, IntBuffer indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -207,6 +211,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ShortBuffer indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -240,6 +245,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -273,6 +279,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
drawCalls++;
|
||||
@@ -329,12 +336,14 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex);
|
||||
}
|
||||
|
||||
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
final GLContextState glCtx = ctx();
|
||||
@@ -353,6 +362,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
final GLContextState glCtx = ctx();
|
||||
@@ -377,6 +387,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
@@ -393,11 +404,13 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawRangeElementsBaseVertex(int mode, int start, int end, int count, int type, long indices, int baseVertex) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.drawRangeElementsBaseVertex(mode, start, end, count, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
public static void glMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
@@ -423,6 +436,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawArrays(int mode, int first, int count) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -521,6 +535,7 @@ public final class GLSMDraws {
|
||||
|
||||
public static void glCallList(int list) {
|
||||
rejectExitCapture();
|
||||
if (!DisplayListManager.isRecording()) BatchStateGuard.beforeDraw();
|
||||
if (!GLDebug.isActive()) {
|
||||
DisplayListManager.glCallList(list);
|
||||
return;
|
||||
|
||||
@@ -64,6 +64,7 @@ public final class GLSMFramebuffer {
|
||||
private static long firstClearBlockNanos;
|
||||
|
||||
public static void glClear(int mask) {
|
||||
if (DepthEcho.replaying() && !DisplayListManager.isRecording()) return;
|
||||
rejectExitCapture();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
@@ -315,6 +316,7 @@ public final class GLSMFramebuffer {
|
||||
}
|
||||
|
||||
public static void glBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
||||
if (DepthEcho.replaying() && !DisplayListManager.isRecording()) return;
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||
}
|
||||
|
||||
@@ -503,8 +503,8 @@ public final class GLSMQueries {
|
||||
return RENDER_BACKEND.genQueries();
|
||||
}
|
||||
public static void glDeleteQueries(int id) { rejectExitCapture(); RENDER_BACKEND.deleteQueries(id); }
|
||||
public static void glBeginQuery(int target, int id) { rejectExitCapture(); RENDER_BACKEND.beginQuery(target, id); }
|
||||
public static void glEndQuery(int target) { rejectExitCapture(); RENDER_BACKEND.endQuery(target); }
|
||||
public static void glBeginQuery(int target, int id) { rejectExitCapture(); if (!DepthEcho.replaying()) RENDER_BACKEND.beginQuery(target, id); }
|
||||
public static void glEndQuery(int target) { rejectExitCapture(); if (!DepthEcho.replaying()) RENDER_BACKEND.endQuery(target); }
|
||||
public static void glGetQueryObjectui(int id, int pname, IntBuffer params) { rejectExitCapture(); RENDER_BACKEND.getQueryObjectui(id, pname, params); }
|
||||
public static int glGetQueryObjecti(int id, int pname) { rejectExitCapture(); return RENDER_BACKEND.getQueryObjecti(id, pname); }
|
||||
public static void glQueryCounter(int id, int target) { rejectExitCapture(); RENDER_BACKEND.queryCounter(id, target); }
|
||||
|
||||
@@ -458,6 +458,7 @@ public class GLStateManager {
|
||||
static void init(Runnable initCallback) {
|
||||
final GLContextState glCtx = ctx();
|
||||
RenderSystem.initRenderer();
|
||||
CaptureGate.probeGlTool();
|
||||
|
||||
GLSMFramebuffer.queryDefaultDrawBuffer();
|
||||
|
||||
|
||||
@@ -443,7 +443,7 @@ public class RenderSystem {
|
||||
return clipDistanceEmulated;
|
||||
}
|
||||
|
||||
private static boolean hasExtension(String name) {
|
||||
static boolean hasExtension(String name) {
|
||||
final int count = GLSMQueries.glGetInteger(GL30.GL_NUM_EXTENSIONS);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (name.equals(GLSMQueries.glGetStringi(GL11.GL_EXTENSIONS, i))) return true;
|
||||
|
||||
@@ -6,6 +6,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class StateSet {
|
||||
|
||||
public static final int R_DEPTH = 1;
|
||||
@@ -79,28 +81,47 @@ public final class StateSet {
|
||||
public static final StateSet CUTOUT = buildCutout();
|
||||
public static final StateSet MASKS = buildMasks();
|
||||
public static final StateSet CULL = buildCull();
|
||||
public static final StateSet BLOCK_ENTITY_PASS = buildBlockEntityPass();
|
||||
|
||||
private static CowStateStack<?>[] batchMembers() {
|
||||
return new CowStateStack<?>[] {
|
||||
GLSMBlend.getBlendState(), GLSMDepthStencilAlpha.getAlphaState(), GLSMDepthStencilAlpha.getDepthState(),
|
||||
GLSMBlend.getColorMask(), GLSMRasterizer.getPolygonState(),
|
||||
GLSMTextures.getTextures().getTextureUnitBindings(0), GLSMTextures.getTextures().getTextureUnitBindings(1),
|
||||
GLSMTextures.getActiveTextureUnitStack(),
|
||||
GLSMBlend.getBlendMode(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMDepthStencilAlpha.getDepthTest(),
|
||||
GLSMLighting.getLightingState(), GLSMRasterizer.getCullState(), GLSMRasterizer.getPolygonOffsetFillState(),
|
||||
GLSMTextures.getTextures().getTextureUnitStates(0), GLSMTextures.getTextures().getTextureUnitStates(1),
|
||||
GLSMLighting.getLightStates()[0], GLSMLighting.getLightStates()[1],
|
||||
GLSMLighting.getLightDataStates()[0], GLSMLighting.getLightDataStates()[1],
|
||||
GLSMLighting.getColorMaterial(), GLSMLighting.getColorMaterialFace(), GLSMLighting.getColorMaterialParameter(),
|
||||
GLSMLighting.getLightModel(), GLSMLighting.getFrontMaterial(), GLSMLighting.getBackMaterial(),
|
||||
GLSMLighting.getNormalizeState(), GLSMLighting.getRescaleNormalState()
|
||||
};
|
||||
}
|
||||
|
||||
private static StateSet buildBatch() {
|
||||
return new StateSet(0,
|
||||
ids(
|
||||
GLSMBlend.getBlendState(), GLSMDepthStencilAlpha.getAlphaState(), GLSMDepthStencilAlpha.getDepthState(),
|
||||
GLSMBlend.getColorMask(), GLSMRasterizer.getPolygonState(),
|
||||
GLSMTextures.getTextures().getTextureUnitBindings(0), GLSMTextures.getTextures().getTextureUnitBindings(1),
|
||||
GLSMTextures.getActiveTextureUnitStack(),
|
||||
GLSMBlend.getBlendMode(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMDepthStencilAlpha.getDepthTest(),
|
||||
GLSMLighting.getLightingState(), GLSMRasterizer.getCullState(), GLSMRasterizer.getPolygonOffsetFillState(),
|
||||
GLSMTextures.getTextures().getTextureUnitStates(0), GLSMTextures.getTextures().getTextureUnitStates(1),
|
||||
GLSMLighting.getLightStates()[0], GLSMLighting.getLightStates()[1],
|
||||
GLSMLighting.getLightDataStates()[0], GLSMLighting.getLightDataStates()[1],
|
||||
GLSMLighting.getColorMaterial(), GLSMLighting.getColorMaterialFace(), GLSMLighting.getColorMaterialParameter(),
|
||||
GLSMLighting.getLightModel(), GLSMLighting.getFrontMaterial(), GLSMLighting.getBackMaterial(),
|
||||
GLSMLighting.getNormalizeState(), GLSMLighting.getRescaleNormalState()
|
||||
),
|
||||
ids(batchMembers()),
|
||||
R_BLEND | R_DEPTH | R_COLOR_MASK | R_POLYGON | R_TEXTURE | R_ACTIVE_UNIT,
|
||||
B_FRAG_COLORBUF | B_LIGHTING,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildBlockEntityPass() {
|
||||
final CowStateStack<?>[] batch = batchMembers();
|
||||
final CowStateStack<?>[] all = Arrays.copyOf(batch, batch.length + 4);
|
||||
all[batch.length] = GLSMCurrentAttribs.getColor();
|
||||
all[batch.length + 1] = GLSMFog.getFogState();
|
||||
all[batch.length + 2] = GLSMFog.getFogMode();
|
||||
all[batch.length + 3] = GLSMRasterizer.getShadeModelState();
|
||||
return new StateSet(0,
|
||||
ids(all),
|
||||
R_BLEND | R_DEPTH | R_COLOR_MASK | R_POLYGON | R_TEXTURE | R_ACTIVE_UNIT,
|
||||
B_FRAG_COLORBUF | B_LIGHTING | B_CURRENT | B_FRAG_FOG,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildFont() {
|
||||
return new StateSet(0,
|
||||
ids(
|
||||
|
||||
+12
-12
@@ -323,42 +323,42 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArrays(int mode, int first, int count) {
|
||||
protected void issueDrawArrays(int mode, int first, int count) {
|
||||
GL11.glDrawArrays(mode, first, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, int count, int type, long indices) {
|
||||
protected void issueDrawElements(int mode, int count, int type, long indices) {
|
||||
GL11.glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, ByteBuffer indices) {
|
||||
protected void issueDrawElements(int mode, ByteBuffer indices) {
|
||||
GL11.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, IntBuffer indices) {
|
||||
protected void issueDrawElements(int mode, IntBuffer indices) {
|
||||
GL11.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, ShortBuffer indices) {
|
||||
protected void issueDrawElements(int mode, ShortBuffer indices) {
|
||||
GL11.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
protected void issueDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
GL11.glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
protected void issueDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
GL31.glDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
protected void issueDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
GL31.glDrawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
protected void issueMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
GL43.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
|
||||
@@ -378,12 +378,12 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
protected void issueDrawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
GL32.glDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
protected void issueMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
for (int i = 0; i < drawcount; i++) {
|
||||
GL32.glDrawElementsBaseVertex(mode, memGetInt(pCount + 4L * i), type, memGetAddress(pIndices + (long) i * POINTER_SIZE), memGetInt(pBaseVertex + 4L * i));
|
||||
}
|
||||
@@ -1715,7 +1715,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
protected void issueMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
GL14.glMultiDrawArrays(mode, firsts, counts);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import com.gtnewhorizons.angelica.glsm.GLESFormatRemap;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -321,28 +323,221 @@ public abstract class RenderBackend {
|
||||
public abstract void logicOp(int opcode);
|
||||
public abstract void hint(int target, int mode);
|
||||
|
||||
public abstract void drawArrays(int mode, int first, int count);
|
||||
public abstract void drawElements(int mode, int count, int type, long indices);
|
||||
public void drawElements(int mode, ByteBuffer indices) {}
|
||||
public void drawElements(int mode, IntBuffer indices) {}
|
||||
public void drawElements(int mode, ShortBuffer indices) {}
|
||||
public void drawElements(int mode, int count, int type, ByteBuffer indices) {}
|
||||
public abstract void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride);
|
||||
public void multiDrawArraysIndirectCount(int mode, long indirect, long drawcount, int maxdrawcount, int stride) { throw new UnsupportedOperationException(getName() + ": indirect draw-count buffers unsupported"); }
|
||||
public void multiDrawElementsIndirectCount(int mode, int type, long indirect, long drawcount, int maxdrawcount, int stride) { throw new UnsupportedOperationException(getName() + ": indirect draw-count buffers unsupported"); }
|
||||
public abstract void copyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size);
|
||||
public abstract void drawElementsInstanced(int mode, int count, int type, long indices, int primcount);
|
||||
public abstract void drawArraysInstanced(int mode, int first, int count, int primcount);
|
||||
public void provokingVertex(int provokeMode) {}
|
||||
public abstract void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex);
|
||||
public abstract void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex);
|
||||
|
||||
public final void drawArrays(int mode, int first, int count) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawArrays(mode, first, count);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawArrays(mode, first, count);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, int count, int type, long indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, ByteBuffer indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, IntBuffer indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, ShortBuffer indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawArraysInstanced(mode, first, count, primcount);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawArraysInstanced(mode, first, count, primcount);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueMultiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueMultiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueMultiDrawArrays(mode, firsts, counts);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueMultiDrawArrays(mode, firsts, counts);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void issueDrawArrays(int mode, int first, int count);
|
||||
protected abstract void issueDrawElements(int mode, int count, int type, long indices);
|
||||
protected void issueDrawElements(int mode, ByteBuffer indices) {}
|
||||
protected void issueDrawElements(int mode, IntBuffer indices) {}
|
||||
protected void issueDrawElements(int mode, ShortBuffer indices) {}
|
||||
protected void issueDrawElements(int mode, int count, int type, ByteBuffer indices) {}
|
||||
protected abstract void issueMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride);
|
||||
protected abstract void issueDrawElementsInstanced(int mode, int count, int type, long indices, int primcount);
|
||||
protected abstract void issueDrawArraysInstanced(int mode, int first, int count, int primcount);
|
||||
protected abstract void issueDrawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex);
|
||||
protected abstract void issueMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex);
|
||||
protected abstract void issueMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts);
|
||||
public void drawRangeElements(int mode, int start, int end, int count, int type, long indices) {
|
||||
drawElements(mode, count, type, indices);
|
||||
}
|
||||
public void drawRangeElementsBaseVertex(int mode, int start, int end, int count, int type, long indices, int baseVertex) {
|
||||
drawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
}
|
||||
public abstract void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts);
|
||||
public abstract void primitiveRestartIndex(int index);
|
||||
public abstract void pointParameterf(int pname, float param);
|
||||
public abstract void pointParameteri(int pname, int param);
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.Arrays;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
@@ -16,24 +17,24 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (14) + unit 0 (47 bits at offset 14)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* long[0]: global bits (14) + unit 0 (45 bits at offset 14), depth peel (60), combined glint (61), glint alpha (62), clip discard (63)
|
||||
* long[1..3]: unit 1..3 (45 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
* Per-unit packing (47 bits):
|
||||
* Per-unit packing (45 bits):
|
||||
* 0: enabled
|
||||
* 1-3: mode (MODULATE=0..COMBINE=5)
|
||||
* 4: needsEnvColor
|
||||
* 5-8: combineRgb (encoded 0-7)
|
||||
* 9-12: combineAlpha (encoded 0-7)
|
||||
* 13-14: scaleShiftRgb
|
||||
* 15-16: scaleShiftAlpha
|
||||
* 17-21: arg0 RGB (source:3 + operand:2)
|
||||
* 22-26: arg1 RGB
|
||||
* 27-31: arg2 RGB
|
||||
* 32-36: arg0 Alpha
|
||||
* 37-41: arg1 Alpha
|
||||
* 42-46: arg2 Alpha
|
||||
* 5-7: combineRgb (encoded 0-7)
|
||||
* 8-10: combineAlpha (encoded 0-7)
|
||||
* 11-12: scaleShiftRgb
|
||||
* 13-14: scaleShiftAlpha
|
||||
* 15-19: arg0 RGB (source:3 + operand:2)
|
||||
* 20-24: arg1 RGB
|
||||
* 25-29: arg2 RGB
|
||||
* 30-34: arg0 Alpha
|
||||
* 35-39: arg1 Alpha
|
||||
* 40-44: arg2 Alpha
|
||||
*/
|
||||
public final class FragmentKey {
|
||||
public static final int TEX_ENV_MODULATE = 0;
|
||||
@@ -78,6 +79,7 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
private static final int GLOBAL_BITS = 14;
|
||||
private static final int BIT_DEPTH_PEEL = 60;
|
||||
private static final int BIT_COMBINED_GLINT = 61;
|
||||
private static final int BIT_GLINT_REPLACE_ALPHA = 62;
|
||||
private static final int BIT_CLIP_DISCARD = 63;
|
||||
@@ -94,16 +96,16 @@ public final class FragmentKey {
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
private static final int U_NEEDS_ENV_COLOR = 4; // 1 bit
|
||||
private static final int U_COMBINE_RGB = 5; // 4 bits
|
||||
private static final int U_COMBINE_ALPHA = 9; // 4 bits
|
||||
private static final int U_SCALE_RGB = 13; // 2 bits
|
||||
private static final int U_SCALE_ALPHA = 15; // 2 bits
|
||||
private static final int U_ARG0_RGB = 17; // 5 bits (source:3 + operand:2)
|
||||
private static final int U_ARG1_RGB = 22; // 5 bits
|
||||
private static final int U_ARG2_RGB = 27; // 5 bits
|
||||
private static final int U_ARG0_ALPHA = 32; // 5 bits
|
||||
private static final int U_ARG1_ALPHA = 37; // 5 bits
|
||||
private static final int U_ARG2_ALPHA = 42; // 5 bits
|
||||
private static final int U_COMBINE_RGB = 5; // 3 bits
|
||||
private static final int U_COMBINE_ALPHA = 8; // 3 bits
|
||||
private static final int U_SCALE_RGB = 11; // 2 bits
|
||||
private static final int U_SCALE_ALPHA = 13; // 2 bits
|
||||
private static final int U_ARG0_RGB = 15; // 5 bits (source:3 + operand:2)
|
||||
private static final int U_ARG1_RGB = 20; // 5 bits
|
||||
private static final int U_ARG2_RGB = 25; // 5 bits
|
||||
private static final int U_ARG0_ALPHA = 30; // 5 bits
|
||||
private static final int U_ARG1_ALPHA = 35; // 5 bits
|
||||
private static final int U_ARG2_ALPHA = 40; // 5 bits
|
||||
|
||||
static final int MAX_UNITS = 4;
|
||||
|
||||
@@ -127,6 +129,7 @@ public final class FragmentKey {
|
||||
public static int packFromState(long[] out, GLContextState glCtx) {
|
||||
long global = 0;
|
||||
if (CombinedGlint.isActive()) global |= 1L << BIT_COMBINED_GLINT;
|
||||
if (GLSMConfig.depthPeel) global |= 1L << BIT_DEPTH_PEEL;
|
||||
if (CombinedGlint.replacesAlpha()) global |= 1L << BIT_GLINT_REPLACE_ALPHA;
|
||||
if (GLSMRasterizer.clipEmulationActive(glCtx)) global |= 1L << BIT_CLIP_DISCARD;
|
||||
|
||||
@@ -200,8 +203,8 @@ public final class FragmentKey {
|
||||
if (mode == TEX_ENV_COMBINE) {
|
||||
final int combRgb = encodeCombineFunc(envState.combineRgb);
|
||||
final int combAlpha = encodeCombineFunc(envState.combineAlpha);
|
||||
bits |= ((long) (combRgb & 0xF)) << U_COMBINE_RGB;
|
||||
bits |= ((long) (combAlpha & 0xF)) << U_COMBINE_ALPHA;
|
||||
bits |= ((long) (combRgb & 0x7)) << U_COMBINE_RGB;
|
||||
bits |= ((long) (combAlpha & 0x7)) << U_COMBINE_ALPHA;
|
||||
bits |= ((long) (encodeScale(envState.scaleRgb) & 0x3)) << U_SCALE_RGB;
|
||||
bits |= ((long) (encodeScale(envState.scaleAlpha) & 0x3)) << U_SCALE_ALPHA;
|
||||
|
||||
@@ -264,6 +267,7 @@ public final class FragmentKey {
|
||||
public boolean combinedGlint() { return (packed[0] & (1L << BIT_COMBINED_GLINT)) != 0; }
|
||||
public boolean clipDiscard() { return (packed[0] & (1L << BIT_CLIP_DISCARD)) != 0; }
|
||||
public boolean glintReplaceAlpha() { return (packed[0] & (1L << BIT_GLINT_REPLACE_ALPHA)) != 0; }
|
||||
public boolean depthPeel() { return (packed[0] & (1L << BIT_DEPTH_PEEL)) != 0; }
|
||||
public int textureSupersamplingSamples() { return 1 << (int) ((packed[0] >> BIT_TEXTURE_SUPERSAMPLING) & 0x7); }
|
||||
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
@@ -281,8 +285,8 @@ public final class FragmentKey {
|
||||
public boolean unitEnabled(int i) { return (unitBits(i) & 1) != 0; }
|
||||
public int unitMode(int i) { return (int) ((unitBits(i) >> U_MODE) & 0x7); }
|
||||
public boolean unitNeedsEnvColor(int i) { return ((unitBits(i) >> U_NEEDS_ENV_COLOR) & 1) != 0; }
|
||||
public int unitCombineRgb(int i) { return (int) ((unitBits(i) >> U_COMBINE_RGB) & 0xF); }
|
||||
public int unitCombineAlpha(int i) { return (int) ((unitBits(i) >> U_COMBINE_ALPHA) & 0xF); }
|
||||
public int unitCombineRgb(int i) { return (int) ((unitBits(i) >> U_COMBINE_RGB) & 0x7); }
|
||||
public int unitCombineAlpha(int i) { return (int) ((unitBits(i) >> U_COMBINE_ALPHA) & 0x7); }
|
||||
public int unitScaleShiftRgb(int i) { return (int) ((unitBits(i) >> U_SCALE_RGB) & 0x3); }
|
||||
public int unitScaleShiftAlpha(int i) { return (int) ((unitBits(i) >> U_SCALE_ALPHA) & 0x3); }
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
|
||||
@@ -24,6 +25,7 @@ public final class FragmentShaderGenerator {
|
||||
sb.append("#define GLINT_ALPHA_FUNC ").append(key.alphaTestEnabled() ? key.alphaTestFunc() : 7).append('\n');
|
||||
sb.append("#define GLINT_OVERLAY ").append(key.overlayInstanced() ? 2 : key.overlayEnabled() ? 1 : 0).append('\n');
|
||||
sb.append("#define GLINT_LIGHTMAP ").append(key.nrEnabledUnits() > 1 && key.unitEnabled(1) ? 1 : 0).append('\n');
|
||||
if (key.depthPeel()) sb.append("#define DEPTH_PEEL 1\n");
|
||||
sb.append(ShaderLoader.getShaderSource("angelica:combined_glint.frag.glsl"));
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -43,6 +45,7 @@ public final class FragmentShaderGenerator {
|
||||
emitSpecularAdd(sb, key);
|
||||
emitAlphaTest(sb, key);
|
||||
emitFog(sb, key);
|
||||
emitDepthPeel(sb, key);
|
||||
sb.append(" fragColor = color;\n");
|
||||
sb.append("}\n");
|
||||
|
||||
@@ -89,10 +92,15 @@ public final class FragmentShaderGenerator {
|
||||
sb.append("uniform sampler2D u_Sampler").append(i).append(";\n");
|
||||
}
|
||||
}
|
||||
if (key.depthPeel()) sb.append(DepthPeel.GLSL);
|
||||
sb.append(FFPUniformBlock.GLSL_DECL);
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitDepthPeel(StringBuilder sb, FragmentKey key) {
|
||||
if (key.depthPeel()) sb.append(" peelTest(color);\n");
|
||||
}
|
||||
|
||||
private static void emitTextureSampling(StringBuilder sb, FragmentKey key) {
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (!key.unitEnabled(i)) continue;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CompatShaderTransformer;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -35,6 +36,10 @@ public class Program {
|
||||
@Getter private final FragmentKey fragmentKey;
|
||||
|
||||
public final int[] locSampler = { -1, -1, -1, -1 };
|
||||
public final int locPeelDepth;
|
||||
public final int locPeelScene;
|
||||
private final int locPeelMode;
|
||||
private int peelMode = -1;
|
||||
|
||||
Program(int programId, VertexKey vertexKey, FragmentKey fragmentKey) {
|
||||
this.programId = programId;
|
||||
@@ -43,6 +48,19 @@ public class Program {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
locSampler[i] = RENDER_BACKEND.getUniformLocation(programId, "u_Sampler" + i);
|
||||
}
|
||||
locPeelDepth = RENDER_BACKEND.getUniformLocation(programId, "u_PeelDepth");
|
||||
locPeelScene = RENDER_BACKEND.getUniformLocation(programId, "u_PeelScene");
|
||||
locPeelMode = RENDER_BACKEND.getUniformLocation(programId, "u_PeelMode");
|
||||
}
|
||||
|
||||
boolean peels() {
|
||||
return locPeelMode != -1;
|
||||
}
|
||||
|
||||
void syncPeelMode(int mode) {
|
||||
if (peelMode == mode) return;
|
||||
RENDER_BACKEND.uniform1i(locPeelMode, mode);
|
||||
peelMode = mode;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
@@ -99,6 +117,8 @@ public class Program {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (ffpProgram.locSampler[i] != -1) backend.uniform1i(ffpProgram.locSampler[i], i);
|
||||
}
|
||||
if (ffpProgram.locPeelDepth != -1) backend.uniform1i(ffpProgram.locPeelDepth, DepthPeel.TEXTURE_UNIT);
|
||||
if (ffpProgram.locPeelScene != -1) backend.uniform1i(ffpProgram.locPeelScene, DepthPeel.SCENE_TEXTURE_UNIT);
|
||||
if (previousProgram != 0) {
|
||||
backend.useProgram(previousProgram);
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CompatUniformManager;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -188,6 +189,7 @@ public final class ShaderManager {
|
||||
private void uploadUniforms(GLContextState glCtx) {
|
||||
if (currentProgram != null) {
|
||||
uniforms.upload(glCtx);
|
||||
if (currentProgram.peels()) currentProgram.syncPeelMode(DepthPeel.shaderMode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
/** Drains deferred geometry before changing state that is not stored with each batch. */
|
||||
public final class BatchStateGuard {
|
||||
public static Runnable flush;
|
||||
public static Runnable drawFlush;
|
||||
private static int suspended;
|
||||
|
||||
private BatchStateGuard() {}
|
||||
@@ -16,12 +17,20 @@ public final class BatchStateGuard {
|
||||
public static boolean isSuspended() { return suspended != 0; }
|
||||
|
||||
public static void beforeChange() {
|
||||
if (flush == null || suspended != 0 || !GLStateManager.isMainThread()
|
||||
run(flush);
|
||||
}
|
||||
|
||||
public static void beforeDraw() {
|
||||
run(drawFlush);
|
||||
}
|
||||
|
||||
private static void run(Runnable action) {
|
||||
if (action == null || suspended != 0 || !GLStateManager.isMainThread()
|
||||
|| DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.COMPILE) return;
|
||||
final CommandRecorder recorder = DisplayListManager.isRecording() ? DisplayListManager.pauseRecording() : null;
|
||||
suspend();
|
||||
try {
|
||||
flush.run();
|
||||
action.run();
|
||||
} finally {
|
||||
resume();
|
||||
if (recorder != null) DisplayListManager.resumeRecording(recorder);
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextureLifetime;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
@@ -16,11 +17,18 @@ public final class FrameHooks {
|
||||
private static boolean gameLoopStarted;
|
||||
private static long frameCounter;
|
||||
private static boolean frameGroupActive;
|
||||
private static String framePrefix = "frame:";
|
||||
private static String buildLabel = "";
|
||||
|
||||
public static void bootstrapFirstFrame() {
|
||||
DisplayListManager.abortIfLeaked();
|
||||
if (!gameLoopStarted) {
|
||||
gameLoopStarted = true;
|
||||
final GLSMInitConfig config = GLStateManager.getInitConfig();
|
||||
if (config != null && !config.getBuildLabel().isEmpty()) {
|
||||
buildLabel = config.getBuildLabel();
|
||||
framePrefix = buildLabel + " frame:";
|
||||
}
|
||||
RENDER_BACKEND.onFrameBegin();
|
||||
pushFrameGroup();
|
||||
}
|
||||
@@ -48,8 +56,10 @@ public final class FrameHooks {
|
||||
}
|
||||
|
||||
private static void pushFrameGroup() {
|
||||
final boolean wasConnected = CaptureGate.tracyConnected;
|
||||
CaptureGate.refresh();
|
||||
GLDebug.pushGroup("frame:", frameCounter);
|
||||
if (CaptureGate.tracyConnected && !wasConnected && !buildLabel.isEmpty()) Tracy.message(buildLabel);
|
||||
GLDebug.pushGroup(framePrefix, frameCounter);
|
||||
frameGroupActive = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ public final class GLSMConfig {
|
||||
}
|
||||
|
||||
public static boolean hudCacheOverride;
|
||||
public static boolean layerBlending;
|
||||
public static boolean depthPeel;
|
||||
|
||||
public static boolean extendedAttribsExpected;
|
||||
public static volatile boolean expandVertexFormats;
|
||||
|
||||
@@ -17,6 +17,7 @@ public final class GLSMInitConfig {
|
||||
@Getter private final Runnable postInitCallback;
|
||||
private final boolean dsaEnabled;
|
||||
@Getter private final boolean noErrorChecks;
|
||||
@Getter private final String buildLabel;
|
||||
|
||||
private GLSMInitConfig(Builder builder) {
|
||||
this.lwjglDebug = builder.lwjglDebug;
|
||||
@@ -27,6 +28,7 @@ public final class GLSMInitConfig {
|
||||
this.displayHeight = builder.displayHeight;
|
||||
this.postInitCallback = builder.postInitCallback;
|
||||
this.dsaEnabled = builder.dsaEnabled;
|
||||
this.buildLabel = builder.buildLabel;
|
||||
}
|
||||
|
||||
public boolean isDSAEnabled() {
|
||||
@@ -46,6 +48,7 @@ public final class GLSMInitConfig {
|
||||
private Runnable postInitCallback = null;
|
||||
private boolean dsaEnabled = true;
|
||||
private boolean noErrorChecks;
|
||||
private String buildLabel = "";
|
||||
|
||||
private Builder() {}
|
||||
|
||||
@@ -85,6 +88,11 @@ public final class GLSMInitConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder buildLabel(String buildLabel) {
|
||||
this.buildLabel = buildLabel == null ? "" : buildLabel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GLSMInitConfig build() {
|
||||
return new GLSMInitConfig(this);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ final class GLSMOwners {
|
||||
"getColorLogicOpState", "getColorMask", "getEffectiveBlendState", "getEffectiveColorMask",
|
||||
"getIndexLogicOpState", "getLogicOpMode", "glBlendColor", "glBlendEquation", "glBlendEquationSeparate",
|
||||
"glBlendFunc", "glBlendFuncSeparate", "glBlendFuncSeparatei", "glClearColor", "glColorMask", "glLogicOp",
|
||||
"isEffectiveBlendEnabled", "setHudCacheOverride", "tryBlendFuncSeparate"},
|
||||
"isEffectiveBlendEnabled", "isLayerBlending", "setHudCacheOverride", "setLayerBlending",
|
||||
"tryBlendFuncSeparate"},
|
||||
{"com/gtnewhorizons/angelica/glsm/GLSMBuffers",
|
||||
"getBoundCopyReadBuffer", "getBoundCopyWriteBuffer", "getBoundVBO", "getIndexedBufferBinding",
|
||||
"glBindBuffer", "glBindBufferBase", "glBindBufferRange", "glBufferData", "glBufferStorage",
|
||||
|
||||
@@ -53,4 +53,7 @@ void main() {
|
||||
#if GLINT_REPLACE_ALPHA
|
||||
fragColor.a = visible1 ? b.a : a.a;
|
||||
#endif
|
||||
#ifdef DEPTH_PEEL
|
||||
peelTest(fragColor);
|
||||
#endif
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
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.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_BlendAlphaOverride_GLTest {
|
||||
|
||||
private static void assertDriverFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
private static void assertCachedFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GLSMQueries.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GLSMQueries.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GLSMQueries.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GLSMQueries.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void resetOverrides() {
|
||||
GLSMHooks.blendHandler = null;
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
GLSMBlend.setLayerBlending(false);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideChangesTheDriverFuncButNotTheCache() {
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void popUnderOverrideKeepsTheOverriddenAlphaOnTheDriver() {
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
final int d = GLSMAttribStack.pushState(StateSet.BLEND);
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
GLSMBlend.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLSMAttribStack.popStateTo(d);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void separateAlphaIsForcedEvenWhenDstAlphaAlreadyMatches() {
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
static Stream<Arguments> layerBlendRows() {
|
||||
return Stream.of(
|
||||
Arguments.of("translucent over", GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA),
|
||||
Arguments.of("alpha additive", GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("layerBlendRows")
|
||||
void layerBlendingAccumulatesCoverageAndKeepsAdditiveCoverageFree(String name, int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
GLSMBlend.setLayerBlending(true);
|
||||
GLSMBlend.glBlendFunc(srcRgb, dstRgb);
|
||||
assertCachedFunc(srcRgb, dstRgb, srcRgb, dstRgb);
|
||||
assertDriverFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
GLSMBlend.setLayerBlending(false);
|
||||
assertDriverFunc(srcRgb, dstRgb, srcRgb, dstRgb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void togglingWhileABlendOverrideIsHeldReemitsOnTheNextUnchangedFunc() {
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMHooks.blendHandler = new HeldOverride();
|
||||
GLSMBlend.setLayerBlending(true);
|
||||
GLSMHooks.blendHandler = null;
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
private static final class HeldOverride implements DeferredBlendHandler {
|
||||
@Override public boolean isBlendLocked() { return false; }
|
||||
@Override public boolean isOverrideHeld() { return true; }
|
||||
@Override public void deferBlendModeToggle(boolean enabled) {}
|
||||
@Override public void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {}
|
||||
@Override public void flushDeferredBlend() {}
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_HudCacheBlendOverride_GLTest {
|
||||
|
||||
private static void assertDriverFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
private static void assertCachedFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GLSMQueries.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GLSMQueries.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GLSMQueries.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GLSMQueries.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideChangesTheDriverFuncButNotTheCache() {
|
||||
try {
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
} finally {
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popUnderOverrideKeepsTheOverriddenAlphaOnTheDriver() {
|
||||
try {
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
final int d = GLSMAttribStack.pushState(StateSet.BLEND);
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
GLSMBlend.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLSMAttribStack.popStateTo(d);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
} finally {
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void separateAlphaIsForcedEvenWhenDstAlphaAlreadyMatches() {
|
||||
try {
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
} finally {
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,33 @@ public class GLSM_StateSet_GLTest {
|
||||
|| GLSMBlend.getColorMask().blue || GLSMBlend.getColorMask().alpha, "cache color mask - not a HAND member, must stay toggled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void blockEntityPassRestoresColorFogAndShadeModelOnTopOfBatchButNotStencil() {
|
||||
GLSMCurrentAttribs.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
GLSMFog.disableFog();
|
||||
GLSMFog.glFogf(GL11.GL_FOG_END, 64.0f);
|
||||
GLSMRasterizer.glShadeModel(GL11.GL_SMOOTH);
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
GLSMEnable.glDisable(GL11.GL_STENCIL_TEST);
|
||||
|
||||
final int d = GLSMAttribStack.pushState(StateSet.BLOCK_ENTITY_PASS);
|
||||
GLSMCurrentAttribs.glColor4f(0.2f, 0.4f, 0.6f, 0.8f);
|
||||
GLSMFog.enableFog();
|
||||
GLSMFog.glFogf(GL11.GL_FOG_END, 8.0f);
|
||||
GLSMRasterizer.glShadeModel(GL11.GL_FLAT);
|
||||
GLSMDepthStencilAlpha.glDepthMask(false);
|
||||
GLSMEnable.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLSMAttribStack.popStateTo(d);
|
||||
|
||||
assertEquals(1.0f, GLSMCurrentAttribs.getColor().getRed(), "cache color red");
|
||||
assertEquals(1.0f, GLSMCurrentAttribs.getColor().getAlpha(), "cache color alpha");
|
||||
assertFalse(GLSMFog.getFogMode().isEnabled(), "fog enable restored by BLOCK_ENTITY_PASS pop");
|
||||
assertEquals(64.0f, GLSMFog.getFogState().getEnd(), "cache fog end");
|
||||
assertEquals(GL11.GL_SMOOTH, GLSMRasterizer.getShadeModelState().getValue(), "cache shade model");
|
||||
assertTrue(GLSMDepthStencilAlpha.getDepthState().isEnabled(), "depth mask restored, as in BATCH");
|
||||
verifyIsEnabled(GL11.GL_STENCIL_TEST, true, "Stencil test - not a BLOCK_ENTITY_PASS member, must stay toggled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void masksRestoresDepthMaskAndColorMaskButNotDepthTestEnable() {
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
|
||||
@@ -36,6 +36,7 @@ tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configur
|
||||
for (name in forwardedRunProperties) {
|
||||
jvmArgs("-D$name=${System.getProperty(name)}")
|
||||
}
|
||||
jvmArgs("-Dangelica.buildVersion=${project.version}")
|
||||
}
|
||||
|
||||
val isMacOs = org.gradle.internal.os.OperatingSystem.current().isMacOsX
|
||||
|
||||
+12
-12
@@ -493,42 +493,42 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArrays(int mode, int first, int count) {
|
||||
protected void issueDrawArrays(int mode, int first, int count) {
|
||||
GL11C.glDrawArrays(mode, first, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, int count, int type, long indices) {
|
||||
protected void issueDrawElements(int mode, int count, int type, long indices) {
|
||||
GL11C.glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, ByteBuffer indices) {
|
||||
protected void issueDrawElements(int mode, ByteBuffer indices) {
|
||||
GL11C.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, IntBuffer indices) {
|
||||
protected void issueDrawElements(int mode, IntBuffer indices) {
|
||||
GL11C.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, ShortBuffer indices) {
|
||||
protected void issueDrawElements(int mode, ShortBuffer indices) {
|
||||
GL11C.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
protected void issueDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
GL11C.glDrawElements(mode, count, type, MemoryUtil.memAddress(indices));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
protected void issueDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
GL31C.glDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
protected void issueDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
GL31C.glDrawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
|
||||
@@ -539,7 +539,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
protected void issueMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
GL43C.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
|
||||
@@ -549,12 +549,12 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
protected void issueDrawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
GL32C.glDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
protected void issueMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
GL32C.nglMultiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
}
|
||||
|
||||
@@ -1884,7 +1884,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
protected void issueMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
GL14C.glMultiDrawArrays(mode, firsts, counts);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# Translucent TESR ordering scene for a generated superflat (windows-flyby-kit/make_world.py). Needs
|
||||
# -Dangelica.debug.testBlocks=true. $x $y $z is the flight start; ground-standing level is $y-2. Start facing south (yaw 0),
|
||||
# pitch 15. Blocks: test_tesr_provider (0 translucent red, 1 additive purple), test_tesr_model (0 box pane,
|
||||
# 1 Tessellator pane, 2 stacked A/B/A panes), test_tesr_decal, test_tesr_glow; metadata + 8 draws in the solid pass.
|
||||
|
||||
gamerule doMobSpawning false
|
||||
|
||||
# translucent pass: three rows, materials shifted per row so every column stacks three different ones
|
||||
setblock $x-2 $y $z+6 angelica:test_tesr_provider 0 replace
|
||||
setblock $x-1 $y $z+6 angelica:test_tesr_provider 1 replace
|
||||
setblock $x+0 $y $z+6 angelica:test_tesr_model 0 replace
|
||||
setblock $x+1 $y $z+6 angelica:test_tesr_model 1 replace
|
||||
setblock $x-2 $y $z+8 angelica:test_tesr_provider 1 replace
|
||||
setblock $x-1 $y $z+8 angelica:test_tesr_model 0 replace
|
||||
setblock $x+0 $y $z+8 angelica:test_tesr_model 1 replace
|
||||
setblock $x+1 $y $z+8 angelica:test_tesr_provider 0 replace
|
||||
setblock $x-2 $y $z+10 angelica:test_tesr_model 0 replace
|
||||
setblock $x-1 $y $z+10 angelica:test_tesr_model 1 replace
|
||||
setblock $x+0 $y $z+10 angelica:test_tesr_provider 0 replace
|
||||
setblock $x+1 $y $z+10 angelica:test_tesr_provider 1 replace
|
||||
|
||||
# solid pass: the same rows two blocks higher
|
||||
setblock $x-2 $y+2 $z+6 angelica:test_tesr_provider 8 replace
|
||||
setblock $x-1 $y+2 $z+6 angelica:test_tesr_provider 9 replace
|
||||
setblock $x+0 $y+2 $z+6 angelica:test_tesr_model 8 replace
|
||||
setblock $x+1 $y+2 $z+6 angelica:test_tesr_model 9 replace
|
||||
setblock $x-2 $y+2 $z+8 angelica:test_tesr_provider 9 replace
|
||||
setblock $x-1 $y+2 $z+8 angelica:test_tesr_model 8 replace
|
||||
setblock $x+0 $y+2 $z+8 angelica:test_tesr_model 9 replace
|
||||
setblock $x+1 $y+2 $z+8 angelica:test_tesr_provider 8 replace
|
||||
setblock $x-2 $y+2 $z+10 angelica:test_tesr_model 8 replace
|
||||
setblock $x-1 $y+2 $z+10 angelica:test_tesr_model 9 replace
|
||||
setblock $x+0 $y+2 $z+10 angelica:test_tesr_provider 8 replace
|
||||
setblock $x+1 $y+2 $z+10 angelica:test_tesr_provider 9 replace
|
||||
|
||||
# one tile entity submitting translucent A, additive B, translucent A; a depth-equal decal
|
||||
setblock $x-7 $y $z+8 angelica:test_tesr_model 2 replace
|
||||
setblock $x-7 $y+1 $z+8 angelica:test_tesr_decal 0 replace
|
||||
|
||||
# light blue glass wall: glow and provider panes in front, a provider pane behind. The panes behind the wall and
|
||||
# under the water show only when block entities draw before translucent terrain.
|
||||
setblock $x+5 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+5 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+5 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+6 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+6 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+6 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+7 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+7 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+7 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+8 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+8 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+8 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+9 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+9 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+9 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+6 $y $z+13 angelica:test_tesr_glow 0 replace
|
||||
setblock $x+8 $y $z+13 angelica:test_tesr_provider 0 replace
|
||||
setblock $x+7 $y $z+15 angelica:test_tesr_provider 0 replace
|
||||
|
||||
# water pool with a provider pane under the surface
|
||||
setblock $x-10 $y-4 $z+12 water 0 replace
|
||||
setblock $x-10 $y-3 $z+12 water 0 replace
|
||||
setblock $x-10 $y-4 $z+13 water 0 replace
|
||||
setblock $x-10 $y-3 $z+13 water 0 replace
|
||||
setblock $x-10 $y-4 $z+14 water 0 replace
|
||||
setblock $x-10 $y-3 $z+14 water 0 replace
|
||||
setblock $x-9 $y-4 $z+12 water 0 replace
|
||||
setblock $x-9 $y-3 $z+12 water 0 replace
|
||||
setblock $x-9 $y-4 $z+13 water 0 replace
|
||||
setblock $x-9 $y-3 $z+13 water 0 replace
|
||||
setblock $x-9 $y-4 $z+14 water 0 replace
|
||||
setblock $x-9 $y-3 $z+14 water 0 replace
|
||||
setblock $x-8 $y-4 $z+12 water 0 replace
|
||||
setblock $x-8 $y-3 $z+12 water 0 replace
|
||||
setblock $x-8 $y-4 $z+13 water 0 replace
|
||||
setblock $x-8 $y-3 $z+13 water 0 replace
|
||||
setblock $x-8 $y-4 $z+14 water 0 replace
|
||||
setblock $x-8 $y-3 $z+14 water 0 replace
|
||||
setblock $x-9 $y-4 $z+13 angelica:test_tesr_provider 0 replace
|
||||
|
||||
# control: a lone pane with sky behind it
|
||||
setblock $x+5 $y+2 $z+8 angelica:test_tesr_provider 0 replace
|
||||
@@ -0,0 +1,58 @@
|
||||
# Translucency layer scene for a generated superflat (windows-flyby-kit/make_world.py). $x $y $z is the flight start;
|
||||
# ground-standing level is $y-2. Start facing south (yaw 0), pitch 15. Use with -Dangelica.flyby.weather=RAIN. Torch
|
||||
# flame and smoke and rain fall in front of a light blue glass wall and a water pool; one torch row stands behind the glass.
|
||||
|
||||
gamerule doMobSpawning false
|
||||
|
||||
# glass wall
|
||||
setblock $x-1 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x-1 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x-1 $y+0 $z+9 stained_glass 3 replace
|
||||
setblock $x+0 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x+0 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x+0 $y+0 $z+9 stained_glass 3 replace
|
||||
setblock $x+1 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x+1 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x+1 $y+0 $z+9 stained_glass 3 replace
|
||||
setblock $x+2 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x+2 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x+2 $y+0 $z+9 stained_glass 3 replace
|
||||
setblock $x+3 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x+3 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x+3 $y+0 $z+9 stained_glass 3 replace
|
||||
|
||||
# torches in front of and behind the glass
|
||||
setblock $x-1 $y-2 $z+7 torch 5 replace
|
||||
setblock $x-1 $y-2 $z+11 torch 5 replace
|
||||
setblock $x+0 $y-2 $z+7 torch 5 replace
|
||||
setblock $x+0 $y-2 $z+11 torch 5 replace
|
||||
setblock $x+1 $y-2 $z+7 torch 5 replace
|
||||
setblock $x+1 $y-2 $z+11 torch 5 replace
|
||||
setblock $x+2 $y-2 $z+7 torch 5 replace
|
||||
setblock $x+2 $y-2 $z+11 torch 5 replace
|
||||
setblock $x+3 $y-2 $z+7 torch 5 replace
|
||||
setblock $x+3 $y-2 $z+11 torch 5 replace
|
||||
|
||||
# water pool with torches in front of it
|
||||
setblock $x-6 $y-3 $z+7 water 0 replace
|
||||
setblock $x-6 $y-3 $z+8 water 0 replace
|
||||
setblock $x-6 $y-3 $z+9 water 0 replace
|
||||
setblock $x-6 $y-3 $z+10 water 0 replace
|
||||
setblock $x-5 $y-3 $z+7 water 0 replace
|
||||
setblock $x-5 $y-3 $z+8 water 0 replace
|
||||
setblock $x-5 $y-3 $z+9 water 0 replace
|
||||
setblock $x-5 $y-3 $z+10 water 0 replace
|
||||
setblock $x-4 $y-3 $z+7 water 0 replace
|
||||
setblock $x-4 $y-3 $z+8 water 0 replace
|
||||
setblock $x-4 $y-3 $z+9 water 0 replace
|
||||
setblock $x-4 $y-3 $z+10 water 0 replace
|
||||
setblock $x-3 $y-3 $z+7 water 0 replace
|
||||
setblock $x-3 $y-3 $z+8 water 0 replace
|
||||
setblock $x-3 $y-3 $z+9 water 0 replace
|
||||
setblock $x-3 $y-3 $z+10 water 0 replace
|
||||
setblock $x-2 $y-3 $z+7 water 0 replace
|
||||
setblock $x-2 $y-3 $z+8 water 0 replace
|
||||
setblock $x-2 $y-3 $z+9 water 0 replace
|
||||
setblock $x-2 $y-3 $z+10 water 0 replace
|
||||
setblock $x-5 $y-2 $z+6 torch 5 replace
|
||||
setblock $x-3 $y-2 $z+6 torch 5 replace
|
||||
@@ -1228,7 +1228,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void drawArrays(int mode, int first, int count) {
|
||||
@Override protected void issueDrawArrays(int mode, int first, int count) {
|
||||
if (count <= 0) return;
|
||||
final ContextState st = s();
|
||||
if (st.rasterizerDiscard) return;
|
||||
@@ -1250,7 +1250,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
captureVertexWrites(st, first, count, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override public void drawElements(int mode, int count, int type, long indices) {
|
||||
@Override protected void issueDrawElements(int mode, int count, int type, long indices) {
|
||||
final ContextState st = s();
|
||||
final long rp = prepareIndexedDrawBind(st, mode, type, "drawElements");
|
||||
if (rp == 0) return;
|
||||
@@ -1260,7 +1260,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
captureVertexWrites(st, 0, count, 1, type, indices, 0);
|
||||
}
|
||||
|
||||
@Override public void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
@Override protected void issueDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
final ContextState st = s();
|
||||
final long rp = prepareIndexedDrawBind(st, mode, type, "drawElementsInstanced");
|
||||
if (rp == 0) return;
|
||||
@@ -1272,7 +1272,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
|
||||
@Override public void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
@Override protected void issueDrawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
final ContextState st = s();
|
||||
final long rp = prepareIndexedDrawBind(st, mode, type, "drawElementsBaseVertex");
|
||||
if (rp == 0) return;
|
||||
@@ -1297,7 +1297,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
return rp;
|
||||
}
|
||||
|
||||
@Override public void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
@Override protected void issueMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
if (drawcount <= 0) return;
|
||||
final ContextState st = s();
|
||||
final long rp = prepareIndexedDrawBind(st, mode, type, "multiDrawElementsBaseVertex");
|
||||
@@ -1317,7 +1317,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
@Override protected void issueMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
final ContextState cs = s();
|
||||
if (drawcount <= 0) return;
|
||||
Tracy.beginZone(Z_SDL_INDIRECT_DRAW);
|
||||
@@ -4670,7 +4670,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
protected void issueDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
if (count <= 0 || primcount <= 0) return;
|
||||
final ContextState st = s();
|
||||
if (st.rasterizerDiscard) return;
|
||||
@@ -4688,11 +4688,11 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
protected void issueMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
if (firsts == null || counts == null) return;
|
||||
final int n = Math.min(firsts.remaining(), counts.remaining());
|
||||
for (int i = 0; i < n; i++) {
|
||||
drawArrays(mode, firsts.get(firsts.position() + i), counts.get(counts.position() + i));
|
||||
issueDrawArrays(mode, firsts.get(firsts.position() + i), counts.get(counts.position() + i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.api.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.GlowReplayExclusions;
|
||||
|
||||
/// Under Fancy translucency, block entities drawing glows (blended draws without depth writes) in the translucent
|
||||
/// pass are rendered a second time. Renderers that advance state per call opt out here.
|
||||
public final class TesrGlowDepth {
|
||||
|
||||
private TesrGlowDepth() {}
|
||||
|
||||
/// Excludes block entities of `type`, or rendered by a renderer of `type`, including subclasses.
|
||||
public static void excludeFromReplay(Class<?> type) {
|
||||
GlowReplayExclusions.exclude(type);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/** TESR mesh cache/batch API (Render thread only) */
|
||||
@API(owner = "angelica", provides = "angelica|tesr", apiVersion = "3")
|
||||
@API(owner = "angelica", provides = "angelica|tesr", apiVersion = "4")
|
||||
package com.gtnewhorizons.angelica.api.tesr;
|
||||
|
||||
import cpw.mods.fml.common.API;
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.common;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class BlockTesrTranslucentTest extends Block {
|
||||
|
||||
private final Supplier<TileEntity> tile;
|
||||
|
||||
public BlockTesrTranslucentTest(Supplier<TileEntity> tile) {
|
||||
super(Material.glass);
|
||||
this.tile = tile;
|
||||
setHardness(0.3f);
|
||||
setLightOpacity(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaqueCube() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean renderAsNormalBlock() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderType() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(int metadata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createTileEntity(World world, int metadata) {
|
||||
return tile.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.common;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
public abstract class TileTesrTranslucentTest extends TileEntity {
|
||||
|
||||
private static final int SOLID_PASS = 8;
|
||||
|
||||
public int variant() {
|
||||
return getBlockMetadata() & ~SOLID_PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderInPass(int pass) {
|
||||
return pass == ((getBlockMetadata() & SOLID_PASS) != 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class Provider extends TileTesrTranslucentTest {}
|
||||
|
||||
public static class Model extends TileTesrTranslucentTest {}
|
||||
|
||||
public static class Decal extends TileTesrTranslucentTest {}
|
||||
|
||||
public static class Glow extends TileTesrTranslucentTest {}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.gtnewhorizons.angelica.compat.spark;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModContainer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class SparkKnownSources {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
private static final String PLUGIN = "me/lucko/spark/forge/plugin/Forge1710SparkPlugin";
|
||||
private static final String SOURCE_METADATA = "me.lucko.spark.common.sampler.source.SourceMetadata";
|
||||
|
||||
private static MethodHandle constructor;
|
||||
private static boolean failed;
|
||||
|
||||
private SparkKnownSources() {}
|
||||
|
||||
public static boolean mixinApplies() {
|
||||
try (InputStream stream = SparkKnownSources.class.getResourceAsStream("/" + PLUGIN + ".class")) {
|
||||
if (stream == null) {
|
||||
return false;
|
||||
}
|
||||
final boolean[] declared = new boolean[1];
|
||||
new ClassReader(stream).accept(new ClassVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
if ("getKnownSources".equals(name) && "()Ljava/util/Collection;".equals(desc)) {
|
||||
declared[0] = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
if (declared[0]) {
|
||||
LOGGER.info("spark already reports mod sources; skipping");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
LOGGER.warn("Could not inspect spark plugin class; not adding mod sources", t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Collection<Object> gather(ClassLoader loader) {
|
||||
if (failed) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
if (constructor == null) constructor = MethodHandles.publicLookup().findConstructor(Class.forName(SOURCE_METADATA, false, loader),
|
||||
MethodType.methodType(void.class, String.class, String.class, String.class, String.class));
|
||||
final List<ModContainer> mods = Loader.instance().getActiveModList();
|
||||
final List<Object> sources = new ArrayList<>(mods.size());
|
||||
for (int i = 0; i < mods.size(); i++) {
|
||||
final ModContainer mod = mods.get(i);
|
||||
sources.add(constructor.invoke(mod.getModId(), mod.getVersion(), (String) null, (String) null));
|
||||
}
|
||||
return sources;
|
||||
} catch (Throwable t) {
|
||||
failed = true;
|
||||
LOGGER.warn("Could not build spark mod sources; profiles will omit mod versions", t);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,6 +177,11 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static String[] entityShadowReplayExcludedClasses;
|
||||
|
||||
@Config.Comment("Tile entity or renderer classes whose glows Fancy translucency records as they draw instead of rendering the tile entity a second time. For renderers that advance an animation or other state on each render.")
|
||||
@Config.DefaultStringList({})
|
||||
@Config.RequiresMcRestart
|
||||
public static String[] glowDepthReplayExcludedClasses;
|
||||
|
||||
@Config.Comment("Skip the end-of-frame shader buffer copy by ping-ponging buffers. Disable if a shader pack misrenders.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.mixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.compat.spark.SparkKnownSources;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
@@ -300,6 +301,15 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_GLOW_DEPTH_REPLAY(new MixinBuilder("Drop particles and sounds a block entity renderer starts while Fancy translucency replays it into the glow depth")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins(
|
||||
"angelica.tesr.MixinEffectRenderer_GlowDepthReplay",
|
||||
"angelica.tesr.MixinSoundHandler_GlowDepthReplay"
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_TESR_PROVIDER_DISPATCH(new MixinBuilder("Route TesrMeshProvider renderers through the batched mesh cache, and observe renderers that mix model parts with their own draws")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableTESRProviderDispatch || AngelicaConfig.enableEntityBatching)
|
||||
@@ -550,6 +560,8 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinEntityRenderer"
|
||||
, "shaders.MixinGuiIngameForge"
|
||||
, "shaders.MixinFramebuffer"
|
||||
, "angelica.rendering.MixinEntityRenderer_TranslucencyLayers"
|
||||
, "angelica.rendering.MixinFramebuffer_TranslucencyLayers"
|
||||
, "shaders.MixinItem"
|
||||
, "shaders.MixinItemRenderer_ItemId"
|
||||
, "shaders.MixinLocale"
|
||||
@@ -641,6 +653,13 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
SPARK_MOD_SOURCES(new MixinBuilder("Mod versions in spark 1.7.10 profiles")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.SPARK)
|
||||
.setApplyIf(SparkKnownSources::mixinApplies)
|
||||
.addClientMixins("spark.MixinForge1710SparkPlugin")
|
||||
),
|
||||
|
||||
ANGELICA_TEXTURE(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins(
|
||||
|
||||
@@ -32,6 +32,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
OPENBLOCKS(null, "OpenBlocks"),
|
||||
RAILCRAFT(null, "Railcraft"),
|
||||
SECURITYCRAFT(null, "securitycraft"),
|
||||
SPARK(null, "spark"),
|
||||
STARMINER("jp.mc.ancientred.starminer.core.SMPlugin", "modJ_StarMiner"),
|
||||
SUPERNOVA("com.mitchej123.supernova.core.SupernovaCore", "supernova"),
|
||||
THAUMCRAFT(null, "Thaumcraft"),
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.common.BlockError;
|
||||
import com.gtnewhorizons.angelica.common.BlockIsbrhTest;
|
||||
import com.gtnewhorizons.angelica.common.TileEntityClipPlaneTest;
|
||||
import com.gtnewhorizons.angelica.common.TileTesrTranslucentTest;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.bettercrashes.BetterCrashesCompat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
@@ -75,6 +76,7 @@ import com.gtnewhorizons.angelica.rendering.ClipPlaneTestRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.FpsReducer;
|
||||
import com.gtnewhorizons.angelica.rendering.FramePacer;
|
||||
import com.gtnewhorizons.angelica.rendering.IsbrhTestRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.TesrTranslucentTestRenderers;
|
||||
import com.gtnewhorizons.angelica.rendering.TextureSamplers;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
@@ -150,6 +152,10 @@ public final class ClientProxy extends CommonProxy {
|
||||
BlockIsbrhTest.renderId = RenderingRegistry.getNextAvailableRenderId();
|
||||
RenderingRegistry.registerBlockHandler(BlockIsbrhTest.renderId, new IsbrhTestRenderer());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityClipPlaneTest.class, new ClipPlaneTestRenderer());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileTesrTranslucentTest.Provider.class, new TesrTranslucentTestRenderers.Provider());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileTesrTranslucentTest.Model.class, new TesrTranslucentTestRenderers.Model());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileTesrTranslucentTest.Decal.class, new TesrTranslucentTestRenderers.Decal());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileTesrTranslucentTest.Glow.class, new TesrTranslucentTestRenderers.Glow());
|
||||
}
|
||||
AngelicaJar.configureShaderDiskCache(event.getSourceFile());
|
||||
if (AngelicaConfig.enableIris) {
|
||||
|
||||
@@ -2,13 +2,18 @@ package com.gtnewhorizons.angelica.proxy;
|
||||
|
||||
import com.gtnewhorizons.angelica.common.BlockClipPlaneTest;
|
||||
import com.gtnewhorizons.angelica.common.BlockIsbrhTest;
|
||||
import com.gtnewhorizons.angelica.common.BlockTesrTranslucentTest;
|
||||
import com.gtnewhorizons.angelica.common.BlockTest;
|
||||
import com.gtnewhorizons.angelica.common.TileEntityClipPlaneTest;
|
||||
import com.gtnewhorizons.angelica.common.TileTesrTranslucentTest;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CommonProxy {
|
||||
|
||||
@@ -18,9 +23,18 @@ public class CommonProxy {
|
||||
GameRegistry.registerBlock(new BlockIsbrhTest(), "test_isbrh");
|
||||
GameRegistry.registerBlock(new BlockClipPlaneTest(), "test_clip_plane");
|
||||
GameRegistry.registerTileEntity(TileEntityClipPlaneTest.class, "angelica:test_clip_plane");
|
||||
registerTesrTest("test_tesr_provider", TileTesrTranslucentTest.Provider.class, TileTesrTranslucentTest.Provider::new);
|
||||
registerTesrTest("test_tesr_model", TileTesrTranslucentTest.Model.class, TileTesrTranslucentTest.Model::new);
|
||||
registerTesrTest("test_tesr_decal", TileTesrTranslucentTest.Decal.class, TileTesrTranslucentTest.Decal::new);
|
||||
registerTesrTest("test_tesr_glow", TileTesrTranslucentTest.Glow.class, TileTesrTranslucentTest.Glow::new);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerTesrTest(String name, Class<? extends TileEntity> tile, Supplier<TileEntity> factory) {
|
||||
GameRegistry.registerBlock(new BlockTesrTranslucentTest(factory), name);
|
||||
GameRegistry.registerTileEntity(tile, "angelica:" + name);
|
||||
}
|
||||
|
||||
public void init(FMLInitializationEvent event) {}
|
||||
|
||||
public void postInit(FMLPostInitializationEvent event) {}
|
||||
|
||||
@@ -103,8 +103,12 @@ public class PanoramaRenderer {
|
||||
}
|
||||
|
||||
public static <T> GlProgram<T> loadProgram(String name, String vertBase, String fragBase, Function<ShaderBindingContext, T> factory) {
|
||||
return loadProgram(name, vertBase, fragBase, ShaderConstants.EMPTY, factory);
|
||||
}
|
||||
|
||||
public static <T> GlProgram<T> loadProgram(String name, String vertBase, String fragBase, ShaderConstants fragConstants, Function<ShaderBindingContext, T> factory) {
|
||||
final GlShader vert = ShaderLoader.loadShader(ShaderType.VERTEX, vertBase + ".vert", ShaderConstants.EMPTY);
|
||||
final GlShader frag = ShaderLoader.loadShader(ShaderType.FRAGMENT, fragBase + ".frag", ShaderConstants.EMPTY);
|
||||
final GlShader frag = ShaderLoader.loadShader(ShaderType.FRAGMENT, fragBase + ".frag", fragConstants);
|
||||
try {
|
||||
final GlProgram<T> program = GlProgram.builder(name).attachShader(vert).attachShader(frag).link(factory);
|
||||
program.bind();
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMaterial;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMeshProvider;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMeshSink;
|
||||
import com.gtnewhorizons.angelica.common.TileTesrTranslucentTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDepthStencilAlpha;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.utils.WorkaroundUtils;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class TesrTranslucentTestRenderers {
|
||||
|
||||
private static final double HALF = 0.4375;
|
||||
|
||||
private TesrTranslucentTestRenderers() {}
|
||||
|
||||
private static void pane(Tessellator t, double half) {
|
||||
rect(t, -half, -half, half, half);
|
||||
}
|
||||
|
||||
private static void rect(Tessellator t, double x0, double y0, double x1, double y1) {
|
||||
t.setNormal(0f, 0f, -1f);
|
||||
t.addVertexWithUV(0.5 + x0, 0.5 + y0, 0.5, 0, 1);
|
||||
t.addVertexWithUV(0.5 + x0, 0.5 + y1, 0.5, 0, 0);
|
||||
t.addVertexWithUV(0.5 + x1, 0.5 + y1, 0.5, 1, 0);
|
||||
t.addVertexWithUV(0.5 + x1, 0.5 + y0, 0.5, 1, 1);
|
||||
}
|
||||
|
||||
private static void drawPane(double half) {
|
||||
final Tessellator t = Tessellator.instance;
|
||||
t.startDrawingQuads();
|
||||
pane(t, half);
|
||||
t.draw();
|
||||
}
|
||||
|
||||
private static void apply(TesrMaterial material) {
|
||||
if (material.transparency() == TesrMaterial.Transparency.ADDITIVE) {
|
||||
GLSMBlend.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
} else {
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
GLSMCurrentAttribs.glColor4f(material.colorRed(), material.colorGreen(), material.colorBlue(), material.colorAlpha());
|
||||
}
|
||||
|
||||
private abstract static class PaneRenderer extends TileEntitySpecialRenderer {
|
||||
|
||||
void begin(double x, double y, double z) {
|
||||
bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
|
||||
GLSMMatrices.glPushMatrix();
|
||||
GLSMMatrices.glTranslated(x, y, z);
|
||||
GLSMEnable.glDisable(GL11.GL_CULL_FACE);
|
||||
GLSMEnable.glEnable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
static void end() {
|
||||
GLSMCurrentAttribs.glColor4f(1f, 1f, 1f, 1f);
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMEnable.glDisable(GL11.GL_BLEND);
|
||||
GLSMEnable.glEnable(GL11.GL_CULL_FACE);
|
||||
GLSMMatrices.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Provider extends PaneRenderer implements TesrMeshProvider {
|
||||
|
||||
private static final TesrMaterial[] MATERIALS = {
|
||||
TesrMaterial.builder().color(1f, 0f, 0f, 0.5f).translucent().noCull().build(),
|
||||
TesrMaterial.builder().color(0.5f, 0f, 0.5f, 1f).additive().noCull().build() };
|
||||
private static final Object[] KEYS = { new Object(), new Object() };
|
||||
|
||||
private static int kind(TileEntity te) {
|
||||
return ((TileTesrTranslucentTest) te).variant() == 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object angelica$meshKey(TileEntity te) {
|
||||
return KEYS[kind(te)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$build(TesrMeshSink sink, TileEntity te) {
|
||||
pane(sink.angelica$bucket(DefaultVertexFormat.POSITION_TEXTURE_NORMAL, WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND, MATERIALS[kind(te)]), HALF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
begin(x, y, z);
|
||||
apply(MATERIALS[kind(te)]);
|
||||
drawPane(HALF);
|
||||
end();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Model extends PaneRenderer {
|
||||
|
||||
private static final TesrMaterial TRANSLUCENT_GREEN = TesrMaterial.builder().color(0f, 1f, 0f, 0.5f).translucent().build();
|
||||
private static final TesrMaterial TRANSLUCENT_YELLOW = TesrMaterial.builder().color(1f, 1f, 0f, 0.5f).translucent().build();
|
||||
private static final TesrMaterial TRANSLUCENT_MAGENTA = TesrMaterial.builder().color(1f, 0f, 1f, 0.5f).translucent().build();
|
||||
private static final TesrMaterial ADDITIVE_CYAN = TesrMaterial.builder().color(0f, 0.6f, 0.6f, 1f).additive().build();
|
||||
|
||||
private final ModelBase model = new ModelBase() {};
|
||||
private final ModelRenderer base = new ModelRenderer(model, 0, 0);
|
||||
private final ModelRenderer pane = paneAt(0f);
|
||||
private final ModelRenderer paneFar = paneAt(0.16f);
|
||||
private final ModelRenderer paneNear = paneAt(-0.16f);
|
||||
|
||||
public Model() {
|
||||
base.addBox(-1f, 7f, -1f, 2, 1, 2);
|
||||
}
|
||||
|
||||
private ModelRenderer paneAt(float z) {
|
||||
final ModelRenderer renderer = new ModelRenderer(model, 0, 0);
|
||||
renderer.addBox(-7f, -7f, z, 14, 14, 0);
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
final float scale = 1f / 16f;
|
||||
bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
|
||||
GLSMMatrices.glPushMatrix();
|
||||
GLSMMatrices.glTranslated(x + 0.5, y + 0.5, z + 0.5);
|
||||
GLSMCurrentAttribs.glColor4f(1f, 1f, 1f, 1f);
|
||||
base.render(scale);
|
||||
GLSMEnable.glEnable(GL11.GL_BLEND);
|
||||
switch (((TileTesrTranslucentTest) te).variant()) {
|
||||
case 0 -> {
|
||||
apply(TRANSLUCENT_GREEN);
|
||||
pane.render(scale);
|
||||
}
|
||||
case 1 -> {
|
||||
apply(TRANSLUCENT_YELLOW);
|
||||
GLSMMatrices.glTranslated(-0.5, -0.5, -0.5);
|
||||
drawPane(HALF);
|
||||
}
|
||||
default -> {
|
||||
apply(TRANSLUCENT_MAGENTA);
|
||||
paneFar.render(scale);
|
||||
apply(ADDITIVE_CYAN);
|
||||
pane.render(scale);
|
||||
apply(TRANSLUCENT_GREEN);
|
||||
paneNear.render(scale);
|
||||
}
|
||||
}
|
||||
end();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Decal extends PaneRenderer implements TesrMeshProvider {
|
||||
|
||||
private static final TesrMaterial WRITER = TesrMaterial.builder().color(1f, 1f, 1f, 0.5f).translucent().noCull().build();
|
||||
private static final TesrMaterial DECAL = TesrMaterial.builder().color(0f, 0f, 0f, 0.8f).translucent().depthEqual().noCull().build();
|
||||
private static final Object KEY = new Object();
|
||||
private static final double INNER = HALF / 2;
|
||||
|
||||
private static void writer(Tessellator t) {
|
||||
rect(t, -HALF, INNER, HALF, HALF);
|
||||
rect(t, -HALF, -HALF, HALF, -INNER);
|
||||
rect(t, -HALF, -INNER, -INNER, INNER);
|
||||
rect(t, INNER, -INNER, HALF, INNER);
|
||||
pane(t, INNER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object angelica$meshKey(TileEntity te) {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$build(TesrMeshSink sink, TileEntity te) {
|
||||
writer(sink.angelica$bucket(DefaultVertexFormat.POSITION_TEXTURE_NORMAL, WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND, WRITER));
|
||||
pane(sink.angelica$bucket(DefaultVertexFormat.POSITION_TEXTURE_NORMAL, WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND, DECAL), INNER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
begin(x, y, z);
|
||||
apply(WRITER);
|
||||
final Tessellator t = Tessellator.instance;
|
||||
t.startDrawingQuads();
|
||||
writer(t);
|
||||
t.draw();
|
||||
GLSMDepthStencilAlpha.glDepthFunc(GL11.GL_EQUAL);
|
||||
apply(DECAL);
|
||||
drawPane(INNER);
|
||||
GLSMDepthStencilAlpha.glDepthFunc(GL11.GL_LEQUAL);
|
||||
end();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Glow extends PaneRenderer {
|
||||
|
||||
private static final TesrMaterial TRANSLUCENT_ORANGE = TesrMaterial.builder().color(1f, 0.5f, 0f, 0.6f).translucent().build();
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
begin(x, y, z);
|
||||
apply(TRANSLUCENT_ORANGE);
|
||||
GLSMDepthStencilAlpha.glDepthMask(false);
|
||||
drawPane(HALF);
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
end();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -13,6 +13,7 @@ import com.gtnewhorizons.angelica.rendering.TextureSamplers;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucencyLayers;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
|
||||
import com.gtnewhorizons.angelica.rendering.voxelization.SdlShadowVoxelizationSink;
|
||||
@@ -196,6 +197,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
if (program != null) {
|
||||
program.bind();
|
||||
program.getInterface().setupState(pass);
|
||||
if (pass == AngelicaRenderPassConfiguration.TRANSLUCENT_PASS) TranslucencyLayers.INSTANCE.beginTerrainSplit(program.handle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +206,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
if (irisProgram != null) {
|
||||
endIrisPass(pass);
|
||||
} else {
|
||||
if (pass == AngelicaRenderPassConfiguration.TRANSLUCENT_PASS) TranslucencyLayers.INSTANCE.endTerrainSplit();
|
||||
super.end(pass);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -80,7 +80,8 @@ public class AngelicaRenderPassConfiguration {
|
||||
TRANSLUCENT_PASS = builderForRenderType(1, false, translucentCutoff, vertexType)
|
||||
.name("translucent")
|
||||
.useReverseOrder(true)
|
||||
.useTranslucencySorting(ClientProxy.options().performance.translucencySorting)
|
||||
.useTranslucencySorting(ClientProxy.options().performance.translucencyMode.sorts())
|
||||
.extraDefine("TRANSLUCENCY_SPLIT", "")
|
||||
.build();
|
||||
|
||||
TRANSLUCENT_MATERIAL = new Material(TRANSLUCENT_PASS, translucentCutoff, true);
|
||||
|
||||
+156
-38
@@ -1,19 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.bop.FogBiomeCache;
|
||||
import com.gtnewhorizons.angelica.render.CloudRenderer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
@@ -37,15 +37,22 @@ import org.embeddedt.embeddium.impl.render.viewport.frustum.SimpleFrustum;
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDepthStencilAlpha;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ITileEntityBoundingBoxCache;
|
||||
@@ -58,6 +65,8 @@ import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
|
||||
import com.gtnewhorizons.angelica.render.WeatherRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuTerrainCuller;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.AngelicaTesrMeshCache;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.GlowReplayExclusions;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ImmediateDrawOrder;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.PendingTesrDraws;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.SectionBuiltBlockEntity;
|
||||
@@ -66,6 +75,12 @@ import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHold
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrAttribution;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrLifecycle;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrProviderDispatch;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TranslucentPeel;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucencyLayers;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucentLayer;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
|
||||
public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, AngelicaRenderSectionManager, BlockRenderLayer, TileEntity, CeleritasWorldRenderer.TileEntityRenderContext> implements IDynamicLightWorldRenderer {
|
||||
@@ -75,6 +90,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
private static final Tracy.ZoneId Z_TESR_DISPATCH = Tracy.zoneId("tesrDispatch", Tracy.COLOR_CLIENT);
|
||||
private static final Tracy.ZoneId Z_TESR_MODEL_PARTS = Tracy.zoneId("tesrModelParts", Tracy.COLOR_CLIENT);
|
||||
private static final Tracy.ZoneId Z_TESR_RENDER_TE = Tracy.zoneId("tesrRenderTE", Tracy.COLOR_CLIENT);
|
||||
private static final long P_BLOCK_ENTITIES_RENDERED = Tracy.plotHandle("blockEntitiesRendered");
|
||||
|
||||
private final Minecraft mc;
|
||||
private static CeleritasWorldRenderer instance;
|
||||
@@ -94,10 +110,13 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
|
||||
private final ObjectArrayList<TileEntity> frameTEs = new ObjectArrayList<>();
|
||||
private final ObjectArrayList<TileEntity> frameOverride = new ObjectArrayList<>();
|
||||
private final ObjectArrayList<TileEntity> glowReplay = new ObjectArrayList<>();
|
||||
|
||||
// For sorting transparent TESRs
|
||||
private final TileEntityOrderer tileEntityOrderer = new TileEntityOrderer();
|
||||
private final ArrayList<TileEntity> sortedTileEntities = new ArrayList<>();
|
||||
private final TileEntityDepthOrder depthOrder = new TileEntityDepthOrder();
|
||||
private final Matrix4f savedTextureMatrix = new Matrix4f();
|
||||
private PassOne passOne = PassOne.NONE;
|
||||
|
||||
private enum PassOne { NONE, PENDING, DRAWN_EARLY }
|
||||
|
||||
private CeleritasWorldRenderer(Minecraft mc) {
|
||||
// Private constructor for singleton
|
||||
@@ -172,7 +191,6 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
DynamicLights.setActiveRenderer(null);
|
||||
this.frameTEs.clear();
|
||||
this.frameOverride.clear();
|
||||
this.sortedTileEntities.clear();
|
||||
ShadowRenderer.visibleTileEntities.clear();
|
||||
ShadowRenderer.globalTileEntities.clear();
|
||||
TesrLifecycle.reset();
|
||||
@@ -385,6 +403,15 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
return camera;
|
||||
}
|
||||
|
||||
public boolean hasVisibleLayer(BlockRenderLayer renderLayer) {
|
||||
final Collection<TerrainRenderPass> passes = this.renderSectionManager.getRenderPassConfiguration().vanillaRenderStages().get(renderLayer);
|
||||
if (passes == null) return false;
|
||||
for (final TerrainRenderPass pass : passes) {
|
||||
if (this.renderSectionManager.getRenderLists().hasPass(pass)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawChunkLayer(BlockRenderLayer renderLayer, double x, double y, double z) {
|
||||
if (DEBUG_WIREFRAME_MODE) {
|
||||
@@ -408,6 +435,116 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
GLSMCurrentAttribs.glColor4f(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
public void renderBlockEntitiesBeforeTranslucent(float partialTicks) {
|
||||
if (passOne != PassOne.PENDING) return;
|
||||
passOne = PassOne.DRAWN_EARLY;
|
||||
glowReplay.clear();
|
||||
if (!anyRendersInPassOne()) return;
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
final boolean layered = TranslucencyLayers.INSTANCE.active();
|
||||
final int previousPass = MinecraftForgeClient.getRenderPass();
|
||||
final int depth = GLSMAttribStack.pushState(StateSet.BLOCK_ENTITY_PASS);
|
||||
savedTextureMatrix.set(GLSMTextures.getTextures().getTextureUnitMatrix(0));
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLSMMatrices.glPushMatrix();
|
||||
try {
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
GLSMDepthStencilAlpha.enableAlphaTest();
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLSMTextures.enableTexture();
|
||||
GLSMCurrentAttribs.glColor4f(1f, 1f, 1f, 1f);
|
||||
// A layer reads its color as premultiplied, which a blend-disabled draw does not write (TranslucencyLayers).
|
||||
if (layered) {
|
||||
GLSMBlend.enableBlend();
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else if (mc.gameSettings.fancyGraphics) {
|
||||
GLSMBlend.disableBlend();
|
||||
}
|
||||
if (mc.gameSettings.fancyGraphics) GLSMRasterizer.glShadeModel(GL11.GL_FLAT);
|
||||
RenderHelper.enableStandardItemLighting();
|
||||
mc.entityRenderer.enableLightmap(partialTicks);
|
||||
ForgeHooksClient.setRenderPass(1);
|
||||
if (layered) TranslucencyLayers.INSTANCE.begin(TranslucentLayer.BLOCK_ENTITIES);
|
||||
try {
|
||||
renderBlockEntitiesInPhase(partialTicks);
|
||||
if (layered) replayGlowDepth(partialTicks);
|
||||
} finally {
|
||||
if (layered) TranslucencyLayers.INSTANCE.end();
|
||||
}
|
||||
} finally {
|
||||
ForgeHooksClient.setRenderPass(previousPass);
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLSMMatrices.glPopMatrix();
|
||||
GLSMMatrices.setTextureMatrix(0, savedTextureMatrix);
|
||||
GLSMAttribStack.popStateTo(depth);
|
||||
}
|
||||
}
|
||||
|
||||
private void replayGlowDepth(float partialTicks) {
|
||||
if (glowReplay.isEmpty()) return;
|
||||
try {
|
||||
if (!TranslucencyLayers.INSTANCE.beginGlowReplay()) return;
|
||||
try {
|
||||
for (int i = 0; i < glowReplay.size(); i++) {
|
||||
replayTE(glowReplay.get(i), partialTicks);
|
||||
}
|
||||
} finally {
|
||||
TranslucencyLayers.INSTANCE.endGlowReplay();
|
||||
}
|
||||
} finally {
|
||||
glowReplay.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the renderer directly, since a depth-only replay must not feed the dispatcher's lighting or
|
||||
/// batch-eligibility tracking.
|
||||
private static void replayTE(TileEntity tileEntity, float partialTicks) {
|
||||
final TileEntitySpecialRenderer renderer = TileEntityRendererDispatcher.instance.getSpecialRenderer(tileEntity);
|
||||
if (renderer == null) return;
|
||||
final double x = tileEntity.xCoord - TileEntityRendererDispatcher.staticPlayerX;
|
||||
final double y = tileEntity.yCoord - TileEntityRendererDispatcher.staticPlayerY;
|
||||
final double z = tileEntity.zCoord - TileEntityRendererDispatcher.staticPlayerZ;
|
||||
final int depth = GLSMAttribStack.pushState(StateSet.BLEND);
|
||||
GLSMCurrentAttribs.glColor4f(1f, 1f, 1f, 1f);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
TesrBatchRenderer.INSTANCE.beginScope();
|
||||
try {
|
||||
if (AngelicaConfig.enableTESRProviderDispatch && TesrProviderDispatch.tryRender(renderer, tileEntity, x, y, z)) return;
|
||||
renderer.renderTileEntityAt(tileEntity, x, y, z, partialTicks);
|
||||
} catch (RuntimeException e) {
|
||||
if (!tileEntity.isInvalid()) throw e;
|
||||
} finally {
|
||||
TesrBatchRenderer.INSTANCE.endScope();
|
||||
GLSMAttribStack.popStateTo(depth);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean anyRendersInPassOne() {
|
||||
for (int i = 0; i < frameOverride.size(); i++) {
|
||||
if (frameOverride.get(i).shouldRenderInPass(1)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean takeLatePassOne() {
|
||||
final boolean late = passOne != PassOne.DRAWN_EARLY;
|
||||
passOne = PassOne.NONE;
|
||||
return late;
|
||||
}
|
||||
|
||||
public void renderBlockEntitiesInPhase(float partialTicks) {
|
||||
if (Iris.enabled) {
|
||||
GbufferPrograms.beginBlockEntities();
|
||||
GbufferPrograms.setBlockEntityDefaults();
|
||||
}
|
||||
try {
|
||||
final int rendered = renderBlockEntities(partialTicks);
|
||||
if (Tracy.ENABLED) Tracy.plotInt(P_BLOCK_ENTITIES_RENDERED, rendered);
|
||||
} finally {
|
||||
if (Iris.enabled) GbufferPrograms.endBlockEntities();
|
||||
}
|
||||
}
|
||||
|
||||
public int renderBlockEntities(float partialTicks) {
|
||||
AngelicaTesrMeshCache.INSTANCE.tick();
|
||||
final int pass = MinecraftForgeClient.getRenderPass();
|
||||
@@ -420,6 +557,8 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
TileEntityRendererDispatcher.staticPlayerZ);
|
||||
ModelPartBatcher.INSTANCE.begin(ModelPartBatcher.Mode.BLOCK_ENTITIES);
|
||||
PendingTesrDraws.beginPass();
|
||||
ImmediateDrawOrder.beginPass();
|
||||
TranslucentPeel.beginPass();
|
||||
int count = 0;
|
||||
if (pass == 0) {
|
||||
teRenderContext.set(partialTicks, pass);
|
||||
@@ -442,6 +581,9 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
this.teTransform = null;
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_TESR_DISPATCH);
|
||||
try {
|
||||
if (ClientProxy.options().performance.translucencyMode.sorts()) {
|
||||
depthOrder.sortBackToFront(frameTEs, teCamX, teCamY, teCamZ);
|
||||
}
|
||||
for (int i = 0; i < frameTEs.size(); i++) {
|
||||
final TileEntity te = frameTEs.get(i);
|
||||
if (!(te instanceof OverridesShouldRenderInPass)) {
|
||||
@@ -458,28 +600,24 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
passOne = PassOne.PENDING;
|
||||
} else {
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_TESR_DISPATCH);
|
||||
try {
|
||||
sortedTileEntities.clear();
|
||||
for (int i = 0; i < frameOverride.size(); i++) {
|
||||
final TileEntity te = frameOverride.get(i);
|
||||
if (te.shouldRenderInPass(pass)) {
|
||||
sortedTileEntities.add(te);
|
||||
dispatchTE(te, partialTicks);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (sortedTileEntities.size() > 1 && ClientProxy.options().performance.translucencySorting) {
|
||||
sortedTileEntities.sort(tileEntityOrderer.setLastCameraState(lastCameraState));
|
||||
}
|
||||
for (int i = 0; i < sortedTileEntities.size(); i++) {
|
||||
dispatchTE(sortedTileEntities.get(i), partialTicks);
|
||||
}
|
||||
count = sortedTileEntities.size();
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
}
|
||||
ImmediateDrawOrder.endPass();
|
||||
PendingTesrDraws.endPass();
|
||||
TranslucentPeel.flush();
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_TESR_MODEL_PARTS);
|
||||
try {
|
||||
ModelPartBatcher.INSTANCE.flush();
|
||||
@@ -618,14 +756,17 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
}
|
||||
}
|
||||
PendingTesrDraws.beforeTileEntity(tileEntity);
|
||||
TranslucentPeel.beforeBlockEntity();
|
||||
try {
|
||||
final long start = Tracy.ENABLED ? System.nanoTime() : 0L;
|
||||
final boolean attribute = Tracy.ENABLED && TesrAttribution.currentRenderable == null;
|
||||
if (attribute) TesrAttribution.currentRenderable = tileEntity.getClass();
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_TESR_RENDER_TE);
|
||||
if (DepthEcho.armed() && !GlowReplayExclusions.excludes(tileEntity)) DepthEcho.beginTracking();
|
||||
try {
|
||||
TileEntityRendererDispatcher.instance.renderTileEntity(tileEntity, partialTicks);
|
||||
} finally {
|
||||
if (DepthEcho.endTracking()) glowReplay.add(tileEntity);
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
if (attribute) TesrAttribution.currentRenderable = null;
|
||||
if (Tracy.ENABLED) RenderClassTimings.TESR.add(teLabelClass(tileEntity), System.nanoTime() - start);
|
||||
@@ -673,27 +814,4 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TileEntityOrderer implements Comparator<TileEntity> {
|
||||
public CameraState lastCameraState;
|
||||
|
||||
public TileEntityOrderer() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TileEntityOrderer setLastCameraState(CameraState lastCameraState) {
|
||||
this.lastCameraState = lastCameraState;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(TileEntity te1, TileEntity te2) {
|
||||
final double x = this.lastCameraState.x();
|
||||
final double y = this.lastCameraState.y();
|
||||
final double z = this.lastCameraState.z();
|
||||
final double d1 = te1.getDistanceFrom(x, y, z);
|
||||
final double d2 = te2.getDistanceFrom(x, y, z);
|
||||
return Double.compare(d2, d1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
final class TileEntityDepthOrder {
|
||||
|
||||
private static final int RADIX_BITS = 8;
|
||||
private static final int BUCKETS = 1 << RADIX_BITS;
|
||||
|
||||
private final int[] counts = new int[BUCKETS];
|
||||
private int[] keys = new int[0];
|
||||
private int[] keysScratch = new int[0];
|
||||
private int[] order = new int[0];
|
||||
private int[] orderScratch = new int[0];
|
||||
private TileEntity[] entities = new TileEntity[0];
|
||||
|
||||
void sortBackToFront(ObjectArrayList<TileEntity> list, double camX, double camY, double camZ) {
|
||||
final int n = list.size();
|
||||
if (n < 2) return;
|
||||
final Object[] source = list.elements();
|
||||
if (keys.length < n) {
|
||||
final int capacity = source.length;
|
||||
keys = new int[capacity];
|
||||
keysScratch = new int[capacity];
|
||||
order = new int[capacity];
|
||||
orderScratch = new int[capacity];
|
||||
entities = new TileEntity[capacity];
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
final TileEntity te = (TileEntity) source[i];
|
||||
final double dx = te.xCoord + 0.5 - camX;
|
||||
final double dy = te.yCoord + 0.5 - camY;
|
||||
final double dz = te.zCoord + 0.5 - camZ;
|
||||
keys[i] = Integer.MAX_VALUE - Float.floatToRawIntBits((float) (dx * dx + dy * dy + dz * dz));
|
||||
order[i] = i;
|
||||
entities[i] = te;
|
||||
}
|
||||
final int[] sorted = sortOrder(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
source[i] = entities[sorted[i]];
|
||||
entities[sorted[i]] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private int[] sortOrder(int n) {
|
||||
int[] keyIn = keys, keyOut = keysScratch;
|
||||
int[] idxIn = order, idxOut = orderScratch;
|
||||
for (int shift = 0; shift < Integer.SIZE; shift += RADIX_BITS) {
|
||||
Arrays.fill(counts, 0);
|
||||
for (int i = 0; i < n; i++) counts[(keyIn[i] >>> shift) & (BUCKETS - 1)]++;
|
||||
if (counts[(keyIn[0] >>> shift) & (BUCKETS - 1)] == n) continue;
|
||||
int sum = 0;
|
||||
for (int b = 0; b < BUCKETS; b++) {
|
||||
final int c = counts[b];
|
||||
counts[b] = sum;
|
||||
sum += c;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
final int dst = counts[(keyIn[i] >>> shift) & (BUCKETS - 1)]++;
|
||||
keyOut[dst] = keyIn[i];
|
||||
idxOut[dst] = idxIn[i];
|
||||
}
|
||||
final int[] k = keyIn;
|
||||
keyIn = keyOut;
|
||||
keyOut = k;
|
||||
final int[] x = idxIn;
|
||||
idxIn = idxOut;
|
||||
idxOut = x;
|
||||
}
|
||||
keys = keyIn;
|
||||
keysScratch = keyOut;
|
||||
order = idxIn;
|
||||
orderScratch = idxOut;
|
||||
return idxIn;
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ final class BatchStateFallback {
|
||||
final long before = GLSMDraws.drawCalls;
|
||||
try {
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
if (models.hasQueuedGeometry() || tesrs.hasQueuedGeometry()) TranslucentPeel.flush();
|
||||
models.flushForStateChange();
|
||||
tesrs.flushForStateChange();
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
public final class GlowReplayExclusions {
|
||||
|
||||
private static final ReferenceOpenHashSet<Class<?>> registered = new ReferenceOpenHashSet<>();
|
||||
private static final Reference2ObjectOpenHashMap<Class<?>, Boolean> excluded = new Reference2ObjectOpenHashMap<>();
|
||||
|
||||
private GlowReplayExclusions() {}
|
||||
|
||||
public static void exclude(Class<?> type) {
|
||||
if (registered.add(type)) excluded.clear();
|
||||
}
|
||||
|
||||
public static boolean excludes(TileEntity tileEntity) {
|
||||
final Class<? extends TileEntity> type = tileEntity.getClass();
|
||||
Boolean result = excluded.get(type);
|
||||
if (result == null) {
|
||||
final TileEntitySpecialRenderer renderer = TileEntityRendererDispatcher.instance.getSpecialRendererByClass(type);
|
||||
result = matches(type) || renderer != null && matches(renderer.getClass());
|
||||
excluded.put(type, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean matches(Class<?> type) {
|
||||
final String[] names = AngelicaConfig.glowDepthReplayExcludedClasses;
|
||||
for (Class<?> c = type; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
if (registered.contains(c)) return true;
|
||||
if (names != null) for (String name : names) {
|
||||
if (c.getName().equals(name)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
/// Draws what the batchers have queued before a blended `glBegin`, display list, Tessellator or unbatched template draw
|
||||
/// of a block entity, which would otherwise land under batched geometry dispatched before it. Vertex array and buffer
|
||||
/// draws are not hooked.
|
||||
public final class ImmediateDrawOrder {
|
||||
|
||||
private static final Runnable FLUSH = ImmediateDrawOrder::flush;
|
||||
|
||||
private ImmediateDrawOrder() {}
|
||||
|
||||
public static void beginPass() {
|
||||
if (!ClientProxy.options().performance.translucencyMode.sorts() || TesrBatchRenderer.deferredPipeline() != null) return;
|
||||
BatchStateGuard.drawFlush = FLUSH;
|
||||
}
|
||||
|
||||
public static void endPass() {
|
||||
BatchStateGuard.drawFlush = null;
|
||||
}
|
||||
|
||||
static void flush() {
|
||||
if (!GLSMBlend.getBlendMode().isEnabled()) return;
|
||||
// A Tessellator draw triggers this from inside Tessellator.draw, and pending draws use the Tessellator
|
||||
if (!Tessellator.instance.isDrawing) PendingTesrDraws.flush();
|
||||
final ModelPartBatcher models = ModelPartBatcher.INSTANCE;
|
||||
final TesrBatchRenderer tesrs = TesrBatchRenderer.INSTANCE;
|
||||
if (!models.hasQueuedGeometry() && !tesrs.hasQueuedGeometry()) return;
|
||||
final int mode = GLSMMatrices.getMatrixMode().getMode();
|
||||
final int unit = GLSMTextures.getActiveTextureUnit();
|
||||
try {
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
TranslucentPeel.flush();
|
||||
models.flushForStateChange();
|
||||
tesrs.flushAndContinue();
|
||||
} finally {
|
||||
GLSMMatrices.glMatrixMode(mode);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-4
@@ -144,6 +144,14 @@ final class InstancedTemplateRenderer {
|
||||
* Draws a base run's recorded template upload again, every instance in one color.
|
||||
*/
|
||||
int redrawTemplates(TexRun base, int colorABGR) {
|
||||
return redrawTemplates(base, true, colorABGR);
|
||||
}
|
||||
|
||||
int replayTemplates(TexRun run) {
|
||||
return redrawTemplates(run, false, 0);
|
||||
}
|
||||
|
||||
private int redrawTemplates(TexRun base, boolean constantColor, int colorABGR) {
|
||||
long recordOffset = base.uploadOffset;
|
||||
neutralizeExtendedAttribs();
|
||||
GLSMPrograms.setFfpInstancing(Instancing.TEMPLATE);
|
||||
@@ -153,9 +161,9 @@ final class InstancedTemplateRenderer {
|
||||
bindRingTo(mesh.vao, TEMPLATE_FLAGS);
|
||||
InstancedAttribs.pointTemplate(recordOffset);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
setConstantColor(colorABGR);
|
||||
if (constantColor) setConstantColor(colorABGR);
|
||||
GLSMDraws.glDrawArraysInstanced(mesh.drawMode, 0, mesh.vertexCount, count);
|
||||
GLSMVertexInput.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
if (constantColor) GLSMVertexInput.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
recordOffset += (long) count * InstancedAttribs.STRIDE;
|
||||
}
|
||||
GLSMPrograms.setFfpInstancing(Instancing.NONE);
|
||||
@@ -220,15 +228,23 @@ final class InstancedTemplateRenderer {
|
||||
}
|
||||
|
||||
void redrawCubes(long offset, int instances, int colorABGR) {
|
||||
redrawCubes(offset, instances, true, colorABGR);
|
||||
}
|
||||
|
||||
void replayCubes(long offset, int instances) {
|
||||
redrawCubes(offset, instances, false, 0);
|
||||
}
|
||||
|
||||
private void redrawCubes(long offset, int instances, boolean constantColor, int colorABGR) {
|
||||
neutralizeExtendedAttribs();
|
||||
bindRingTo(UnitCubeMesh.vao(), UnitCubeMesh.VERTEX_FLAGS);
|
||||
CubeInstancedAttribs.pointInstanceAttribs(offset);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
setConstantColor(colorABGR);
|
||||
if (constantColor) setConstantColor(colorABGR);
|
||||
GLSMPrograms.setFfpInstancing(Instancing.CUBE);
|
||||
GLSMDraws.glDrawArraysInstanced(GL11.GL_QUADS, 0, UnitCubeMesh.VERTEX_COUNT, instances);
|
||||
GLSMPrograms.setFfpInstancing(Instancing.NONE);
|
||||
GLSMVertexInput.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
if (constantColor) GLSMVertexInput.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
GLSMVertexInput.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -329,13 +329,39 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
}
|
||||
|
||||
void queueShadows() {
|
||||
if (active && mode == Mode.BLOCK_ENTITIES && !shadow) shadows.flush(activeSource);
|
||||
}
|
||||
|
||||
void drawOpaque() {
|
||||
if (active && mode == Mode.BLOCK_ENTITIES && !shadow) activeSource.endBatchWithType(TransparencyType.OPAQUE, activeGroups);
|
||||
}
|
||||
|
||||
boolean beginPeel() {
|
||||
if (!active || mode != Mode.BLOCK_ENTITIES || shadow) return false;
|
||||
if (!activeSource.hasPendingLayers()) return false;
|
||||
activeSource.openDrawState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void drawPeeled(boolean replay) {
|
||||
activeGroups.setPeelReplay(!replay, replay);
|
||||
activeSource.drawPending(activeGroups);
|
||||
}
|
||||
|
||||
void endPeel() {
|
||||
if (!active || mode != Mode.BLOCK_ENTITIES || shadow) return;
|
||||
activeGroups.setPeelReplay(false, false);
|
||||
activeSource.discard();
|
||||
}
|
||||
|
||||
void flushForStateChange() {
|
||||
if (!active) return;
|
||||
flushNow(false);
|
||||
active = true;
|
||||
mvCaptured = false;
|
||||
texCaptured = false;
|
||||
activeGroups.beginPass(identity, 0, 0, 0, instancedThisCycle ? TesrBatchRenderer.INSTANCE.instancedRenderer : null, shaderPipeline);
|
||||
activeGroups.continuePass(identity, 0, 0, 0, instancedThisCycle ? TesrBatchRenderer.INSTANCE.instancedRenderer : null, shaderPipeline);
|
||||
}
|
||||
|
||||
private void flushNow(boolean allowHold) {
|
||||
@@ -860,6 +886,7 @@ public final class ModelPartBatcher {
|
||||
return result;
|
||||
}
|
||||
case DRAW_NOW_EXPECTED -> {
|
||||
BatchStateGuard.beforeDraw();
|
||||
BatchEligibility.beginExpectedDraws(GLSMDraws.drawCalls);
|
||||
try {
|
||||
return TessellatorStreamingDrawer.draw(tess);
|
||||
@@ -868,6 +895,7 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
BatchStateGuard.beforeDraw();
|
||||
return TessellatorStreamingDrawer.draw(tess);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,12 @@ public final class PendingTesrDraws {
|
||||
final Pending owner = pending;
|
||||
if (owner == null) return;
|
||||
pending = null;
|
||||
owner.flush();
|
||||
DepthEcho.beginImmediate();
|
||||
try {
|
||||
owner.flush();
|
||||
} finally {
|
||||
DepthEcho.endImmediate();
|
||||
}
|
||||
}
|
||||
|
||||
public interface Pending {
|
||||
|
||||
@@ -66,6 +66,13 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
private final TemplateBuffer[] budgetSeen = new TemplateBuffer[PROMOTE_MAX_TEMPLATES];
|
||||
private double camX, camY, camZ;
|
||||
private long frameMark;
|
||||
/// `frameMark` advances per segment, so limits measured in frames count passes here.
|
||||
private long passMark;
|
||||
private int segment;
|
||||
private boolean peelRecording;
|
||||
private boolean peelReplaying;
|
||||
private final ObjectArrayList<Group> deferredPromotions = new ObjectArrayList<>();
|
||||
private final ObjectArrayList<DepthEqualSet> deferredSwitches = new ObjectArrayList<>();
|
||||
GlintCapture glintCapture;
|
||||
|
||||
long rebakes;
|
||||
@@ -437,6 +444,11 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SegmentGroups {
|
||||
final ObjectArrayList<Group> groups = new ObjectArrayList<>();
|
||||
long frameMark = -1;
|
||||
}
|
||||
|
||||
static final class Group {
|
||||
final RenderLayer layer;
|
||||
final TesrMaterial material;
|
||||
@@ -451,6 +463,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
final Anchor anchor = new Anchor();
|
||||
boolean anchored;
|
||||
long frameMark = -1;
|
||||
long passMark = -1;
|
||||
long lastUsedMs;
|
||||
final int entityColor;
|
||||
final int itemId;
|
||||
@@ -623,6 +636,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
|
||||
private final Reference2ObjectOpenHashMap<RenderLayer, Reference2ObjectOpenHashMap<TesrMaterial, Object2ObjectOpenHashMap<DrawStateKey, Group>>> groups = new Reference2ObjectOpenHashMap<>();
|
||||
private final Reference2ObjectOpenHashMap<RenderLayer, ObjectArrayList<Group>> byLayer = new Reference2ObjectOpenHashMap<>();
|
||||
private final Reference2ObjectOpenHashMap<RenderLayer, SegmentGroups> segmentGroups = new Reference2ObjectOpenHashMap<>();
|
||||
private long openScope;
|
||||
private DepthEqualSet openDepthEqualSet;
|
||||
private final ObjectArrayList<Group> openWriters = new ObjectArrayList<>();
|
||||
@@ -648,6 +662,18 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void beginPass(Matrix4f base, double camX, double camY, double camZ, InstancedTemplateRenderer instancedRenderer, TesrInstancingPipeline deferred) {
|
||||
startSegment(base, camX, camY, camZ, instancedRenderer, deferred);
|
||||
passMark++;
|
||||
segment = 0;
|
||||
}
|
||||
|
||||
void continuePass(Matrix4f base, double camX, double camY, double camZ, InstancedTemplateRenderer instancedRenderer, TesrInstancingPipeline deferred) {
|
||||
startSegment(base, camX, camY, camZ, instancedRenderer, deferred);
|
||||
// Baked groups are keyed by segment, so a group split by a flush keeps one mesh per side instead of rebaking
|
||||
segment++;
|
||||
}
|
||||
|
||||
private void startSegment(Matrix4f base, double camX, double camY, double camZ, InstancedTemplateRenderer instancedRenderer, TesrInstancingPipeline deferred) {
|
||||
if (glintCapture != null) glintCapture.invalidate();
|
||||
glintCapture = null;
|
||||
baseMV.set(base);
|
||||
@@ -670,27 +696,28 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
|
||||
/** Mutable lookup avoids allocating a key for each queued model part. Stored keys are never mutated. */
|
||||
private static final class DrawStateKey {
|
||||
int entity, block, item, color;
|
||||
int entity, block, item, color, segment;
|
||||
|
||||
DrawStateKey set(int entity, int block, int item, int color) {
|
||||
DrawStateKey set(int entity, int block, int item, int color, int segment) {
|
||||
this.entity = entity;
|
||||
this.block = block;
|
||||
this.item = item;
|
||||
this.color = color;
|
||||
this.segment = segment;
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean matches(int entity, int block, int item, int color) {
|
||||
return this.entity == entity && this.block == block && this.item == item && this.color == color;
|
||||
boolean matches(int entity, int block, int item, int color, int segment) {
|
||||
return this.entity == entity && this.block == block && this.item == item && this.color == color && this.segment == segment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof DrawStateKey key && matches(key.entity, key.block, key.item, key.color);
|
||||
return other instanceof DrawStateKey key && matches(key.entity, key.block, key.item, key.color, key.segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { return ((entity * 31 + block) * 31 + item) * 31 + color; }
|
||||
public int hashCode() { return (((entity * 31 + block) * 31 + item) * 31 + color) * 31 + segment; }
|
||||
}
|
||||
|
||||
private final DrawStateKey lookupState = new DrawStateKey();
|
||||
@@ -729,7 +756,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
group = byState.get(lookupState);
|
||||
if (group == null) {
|
||||
final DrawStateKey key = new DrawStateKey().set(lookupState.entity, lookupState.block, lookupState.item, lookupState.color);
|
||||
final DrawStateKey key = new DrawStateKey().set(lookupState.entity, lookupState.block, lookupState.item, lookupState.color, lookupState.segment);
|
||||
group = new Group(layer, material, perInstance ? 0 : id, key);
|
||||
group.lastUsedMs = nowMs;
|
||||
byState.put(key, group);
|
||||
@@ -746,13 +773,13 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
|
||||
private void keyLookup(boolean perInstance, int id) {
|
||||
if (perInstance) {
|
||||
lookupState.set(0, 0, 0, 0);
|
||||
lookupState.set(0, 0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
final CapturedRenderingState captured = CapturedRenderingState.INSTANCE;
|
||||
final boolean entityKind = source.effectiveIdKind() == AngelicaBufferSource.GroupIdKind.ENTITY;
|
||||
lookupState.set(entityKind ? id : captured.getCurrentRenderedEntity(), entityKind ? captured.getCurrentRenderedBlockEntity() : id,
|
||||
captured.getCurrentRenderedItem(), AngelicaBufferSource.packEntityColor(captured.getCurrentEntityColor()));
|
||||
captured.getCurrentRenderedItem(), AngelicaBufferSource.packEntityColor(captured.getCurrentEntityColor()), segment);
|
||||
}
|
||||
|
||||
void forceStreaming(RenderLayer layer, TesrMaterial material, int blockEntityId) {
|
||||
@@ -866,6 +893,8 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
resetPromotion(group);
|
||||
}
|
||||
}
|
||||
// touch recorded this geometry as stream segments, but it draws with the baked columns
|
||||
TranslucentPeel.queued(layer, this, group);
|
||||
}
|
||||
toAnchorRelative(group, currentMV);
|
||||
final Matrix4f bakedTexMatrix = atlasSlot != null ? atlasSlot.uvMatrix : texMatrix;
|
||||
@@ -991,7 +1020,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
adoptAnchor(group, set);
|
||||
} else if (set.anchorFrame < 0) {
|
||||
set.anchor.set(group.anchor);
|
||||
set.anchorFrame = frameMark;
|
||||
set.anchorFrame = passMark;
|
||||
for (int i = 0, n = set.members.size(); i < n; i++) {
|
||||
final Group member = set.members.get(i);
|
||||
if (member.frameMark == frameMark && !member.queuedThisFrame) adoptAnchor(member, set);
|
||||
@@ -1023,13 +1052,13 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
private void refresh(DepthEqualSet set) {
|
||||
if (set.checkedFrame == frameMark) return;
|
||||
set.checkedFrame = frameMark;
|
||||
if (set.anchorFrame != frameMark && set.anchor.stale(camX, camY, camZ)) set.anchor.moveTo(camX, camY, camZ);
|
||||
set.anchorFrame = frameMark;
|
||||
if (set.checkedFrame == passMark) return;
|
||||
set.checkedFrame = passMark;
|
||||
if (set.anchorFrame != passMark && set.anchor.stale(camX, camY, camZ)) set.anchor.moveTo(camX, camY, camZ);
|
||||
set.anchorFrame = passMark;
|
||||
if ((set.streamMembers > 0 || set.instanced) && set.instanced != allCanInstance(set)) {
|
||||
set.instanced = !set.instanced;
|
||||
set.effectiveFrame = frameMark - 1;
|
||||
set.effectiveFrame = passMark - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1152,7 +1181,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
if (group.frameMark != frameMark) {
|
||||
final DepthEqualSet set = activeDepthEqualSet(group);
|
||||
if (set != null) refresh(set);
|
||||
if (frameMark - group.frameMark > IDLE_RESET_FRAMES) {
|
||||
if (passMark - group.passMark > IDLE_RESET_FRAMES) {
|
||||
resetPromotion(group);
|
||||
} else if (promoted(group)) {
|
||||
final boolean pinned = set != null && set.streamMembers > 0;
|
||||
@@ -1171,12 +1200,14 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
}
|
||||
if (set != null) {
|
||||
if (frameMark > set.effectiveFrame) adopt(group, set.instanced);
|
||||
if (passMark > set.effectiveFrame) adopt(group, set.instanced);
|
||||
} else if (group.stream && !group.streamPath) {
|
||||
resetPromotion(group);
|
||||
}
|
||||
group.queuedThisFrame = false;
|
||||
group.frameMark = frameMark;
|
||||
group.passMark = passMark;
|
||||
addToSegment(group);
|
||||
group.lastUsedMs = nowMs;
|
||||
group.dirty = false;
|
||||
group.clearInstances();
|
||||
@@ -1189,12 +1220,37 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
source.declareUse(layer);
|
||||
}
|
||||
if (drawsInstanced(group)) {
|
||||
TranslucentPeel.queuedUnordered(layer);
|
||||
} else if (group.streamPath) {
|
||||
TranslucentPeel.queued(layer, source, layer);
|
||||
} else {
|
||||
TranslucentPeel.queued(layer, this, group);
|
||||
}
|
||||
}
|
||||
|
||||
private void addToSegment(Group group) {
|
||||
SegmentGroups entry = segmentGroups.get(group.layer);
|
||||
if (entry == null) {
|
||||
entry = new SegmentGroups();
|
||||
segmentGroups.put(group.layer, entry);
|
||||
}
|
||||
if (entry.frameMark != frameMark) {
|
||||
entry.groups.clear();
|
||||
entry.frameMark = frameMark;
|
||||
}
|
||||
entry.groups.add(group);
|
||||
}
|
||||
|
||||
private ObjectArrayList<Group> segmentGroups(RenderLayer layer) {
|
||||
final SegmentGroups entry = segmentGroups.get(layer);
|
||||
return entry != null && entry.frameMark == frameMark ? entry.groups : null;
|
||||
}
|
||||
|
||||
private void switchSetNextFrame(DepthEqualSet set, boolean instanced) {
|
||||
if (set.instanced == instanced) return;
|
||||
set.instanced = instanced;
|
||||
set.effectiveFrame = frameMark;
|
||||
set.effectiveFrame = passMark;
|
||||
}
|
||||
|
||||
private static DepthEqualSet activeDepthEqualSet(Group group) {
|
||||
@@ -1205,7 +1261,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
private boolean allStable(DepthEqualSet set) {
|
||||
for (int i = 0, n = set.members.size(); i < n; i++) {
|
||||
final Group member = set.members.get(i);
|
||||
if (member.streamPath && frameMark - member.frameMark <= IDLE_RESET_FRAMES && (member.probe == null || member.probe.stableProbes < DEMOTE_AFTER_STABLE_PROBES)) return false;
|
||||
if (member.streamPath && passMark - member.passMark <= IDLE_RESET_FRAMES && (member.probe == null || member.probe.stableProbes < DEMOTE_AFTER_STABLE_PROBES)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1230,7 +1286,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
|
||||
@Override
|
||||
public boolean hasDraws(RenderLayer layer) {
|
||||
final ObjectArrayList<Group> list = byLayer.get(layer);
|
||||
final ObjectArrayList<Group> list = segmentGroups(layer);
|
||||
if (list == null) return false;
|
||||
for (int i = 0, n = list.size(); i < n; i++) {
|
||||
if (live(list.get(i))) return true;
|
||||
@@ -1241,7 +1297,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
@Override
|
||||
public void drawLayer(RenderLayer layer) {
|
||||
if (glintCapture != null) glintCapture.invalidate();
|
||||
final ObjectArrayList<Group> list = byLayer.get(layer);
|
||||
final ObjectArrayList<Group> list = segmentGroups(layer);
|
||||
if (list == null) return;
|
||||
boolean matrixAvailable = true;
|
||||
boolean cubeAvailable = true;
|
||||
@@ -1374,6 +1430,10 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
private void drawRetained(Group group) {
|
||||
if (peelReplaying) {
|
||||
draw(group);
|
||||
return;
|
||||
}
|
||||
final InstanceColumns cols = group.templateColumns;
|
||||
final Snapshot snapshot = group.snapshot;
|
||||
boolean instanceChanged = false;
|
||||
@@ -1394,9 +1454,13 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
if (instanceChanged && !group.stream && !group.streamPath && rebakesTooOften(group) && withinTemplateBudget(cols.templates)) {
|
||||
final DepthEqualSet set = activeDepthEqualSet(group);
|
||||
if (set != null) {
|
||||
if (set.streamMembers == 0 && allCanInstance(set)) switchSetNextFrame(set, true);
|
||||
if (set.streamMembers == 0 && allCanInstance(set)) {
|
||||
if (peelRecording) deferredSwitches.add(set);
|
||||
else switchSetNextFrame(set, true);
|
||||
}
|
||||
} else if (canInstance(group) || group.opaque && templatesMatchLayerDrawMode(group)) {
|
||||
promote(group);
|
||||
if (peelRecording) deferredPromotions.add(group);
|
||||
else promote(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1439,6 +1503,21 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
return ++group.promoteWindowRebakes >= PROMOTE_AFTER_REBAKES;
|
||||
}
|
||||
|
||||
void setPeelReplay(boolean recording, boolean replaying) {
|
||||
peelRecording = recording;
|
||||
peelReplaying = replaying;
|
||||
// Promoting deletes the mesh the peel replays draw, so promotions wait until the peel ends
|
||||
if (recording || replaying) return;
|
||||
for (int i = 0, n = deferredPromotions.size(); i < n; i++) promote(deferredPromotions.get(i));
|
||||
for (int i = 0, n = deferredSwitches.size(); i < n; i++) switchSetNextFrame(deferredSwitches.get(i), true);
|
||||
deferredPromotions.clear();
|
||||
deferredSwitches.clear();
|
||||
}
|
||||
|
||||
private boolean replays(TexRun run) {
|
||||
return peelReplaying && run.uploadPass == frameMark && instanced.canRedraw(run.uploadRingEpoch, run.uploadBuffer);
|
||||
}
|
||||
|
||||
private void drawTemplateRuns(InstanceColumns cols, boolean combineGlint, boolean itemStencil) {
|
||||
for (int r = 0, n = cols.runs.size(); r < n; r++) {
|
||||
final TexRun run = cols.runs.get(r);
|
||||
@@ -1457,9 +1536,13 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
if (canRedraw(run)) {
|
||||
instancedDraws += instanced.redrawTemplates(run.sourceRun, run.color);
|
||||
redrawnInstances += run.parts;
|
||||
} else if (replays(run)) {
|
||||
instancedDraws += instanced.replayTemplates(run);
|
||||
redrawnInstances += run.parts;
|
||||
} else {
|
||||
instancedDraws += instanced.drawTemplates(cols, run, nowMs, run.glintBase);
|
||||
if (run.glintBase) recordUpload(run);
|
||||
final boolean record = run.glintBase || peelRecording;
|
||||
instancedDraws += instanced.drawTemplates(cols, run, nowMs, record);
|
||||
if (record) recordUpload(run);
|
||||
}
|
||||
instancedInstances += run.parts;
|
||||
} finally {
|
||||
@@ -1480,9 +1563,12 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
if (canRedraw(run)) {
|
||||
instanced.redrawCubes(run.sourceRun.uploadOffset, run.instances, run.color);
|
||||
redrawnInstances += run.instances;
|
||||
} else if (replays(run)) {
|
||||
instanced.replayCubes(run.uploadOffset, run.instances);
|
||||
redrawnInstances += run.instances;
|
||||
} else {
|
||||
run.uploadOffset = instanced.drawCubes(cols, run);
|
||||
if (run.glintBase) recordUpload(run);
|
||||
if (run.glintBase || peelRecording) recordUpload(run);
|
||||
}
|
||||
instancedDraws++;
|
||||
cubeInstances += run.instances;
|
||||
@@ -1638,6 +1724,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
}
|
||||
byLayer.clear();
|
||||
segmentGroups.clear();
|
||||
groups.clear();
|
||||
openScope = 0;
|
||||
openDepthEqualSet = null;
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.api.tesr.TesrMaterial;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrShader;
|
||||
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.RenderLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
@@ -82,6 +83,9 @@ public final class TesrBatchRenderer {
|
||||
private RetainedTesrGroups pendingDeferredHook;
|
||||
|
||||
private final Matrix4f modelView = new Matrix4f();
|
||||
private final Matrix4f passBase = new Matrix4f();
|
||||
private double passCamX, passCamY, passCamZ;
|
||||
private InstancedTemplateRenderer passInstanced;
|
||||
private final Matrix4f identity = new Matrix4f();
|
||||
private final Vector3f scratchVec = new Vector3f();
|
||||
|
||||
@@ -112,7 +116,12 @@ public final class TesrBatchRenderer {
|
||||
bufferSource.capturePassLighting();
|
||||
BatchDrawDefaults.capturePassFramebuffer();
|
||||
final DeferredWorldRenderingPipeline deferred = deferredPipeline();
|
||||
retained[passKey].beginPass(baseMV, camX, camY, camZ, instancedCapable() ? instancedRenderer : null, deferred);
|
||||
passBase.set(baseMV);
|
||||
passCamX = camX;
|
||||
passCamY = camY;
|
||||
passCamZ = camZ;
|
||||
passInstanced = instancedCapable() ? instancedRenderer : null;
|
||||
retained[passKey].beginPass(baseMV, camX, camY, camZ, passInstanced, deferred);
|
||||
if (passKey == PASS_MAIN_0) {
|
||||
instancedRenderer.releaseRetired();
|
||||
final long now = System.currentTimeMillis();
|
||||
@@ -194,6 +203,7 @@ public final class TesrBatchRenderer {
|
||||
}
|
||||
|
||||
public void queue(TemplateBuffer template, ResourceLocation texture, TesrMaterial material) {
|
||||
if (DepthEcho.replaying() && cannotBlend(material)) return;
|
||||
modelView.set(GLSMMatrices.getModelViewMatrix());
|
||||
final int packedLight = currentPackedLight(material);
|
||||
final int colorABGR = resolveColor(material);
|
||||
@@ -216,6 +226,13 @@ public final class TesrBatchRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean cannotBlend(TesrMaterial material) {
|
||||
if (material == TesrMaterial.CURRENT_STATE) return false;
|
||||
final TesrMaterial.Transparency transparency = material.transparency();
|
||||
return transparency == TesrMaterial.Transparency.OPAQUE || transparency == TesrMaterial.Transparency.GLINT || material.special().isGlint()
|
||||
|| material.isDepthEqual() || material.isDepthOnly();
|
||||
}
|
||||
|
||||
private static Matrix4f captureTextureMatrix() {
|
||||
final Matrix4f texMatrix = GLSMTextures.getTextures().getTextureUnitMatrix(0);
|
||||
return MatrixHelper.isIdentity(texMatrix) ? null : texMatrix;
|
||||
@@ -401,6 +418,37 @@ public final class TesrBatchRenderer {
|
||||
BatchingFontRenderer.flushDeferredText();
|
||||
}
|
||||
|
||||
void drawOpaque() {
|
||||
if (activePass >= 0) bufferSource.endBatchWithType(TransparencyType.OPAQUE, retained[activePass]);
|
||||
}
|
||||
|
||||
boolean beginPeel() {
|
||||
if (activePass < 0) return false;
|
||||
if (!bufferSource.hasPendingLayers()) return false;
|
||||
bufferSource.openDrawState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void drawPeeled(boolean replay) {
|
||||
retained[activePass].setPeelReplay(!replay, replay);
|
||||
bufferSource.drawPending(retained[activePass]);
|
||||
}
|
||||
|
||||
void endPeel() {
|
||||
if (activePass < 0) return;
|
||||
retained[activePass].setPeelReplay(false, false);
|
||||
bufferSource.discard();
|
||||
}
|
||||
|
||||
void flushAndContinue() {
|
||||
if (activePass < 0) return;
|
||||
final RetainedTesrGroups hook = retained[activePass];
|
||||
bufferSource.endBatchWithType(TransparencyType.OPAQUE, hook);
|
||||
bufferSource.endBatch(hook);
|
||||
BatchingFontRenderer.flushDeferredText();
|
||||
hook.continuePass(passBase, passCamX, passCamY, passCamZ, passInstanced, null);
|
||||
}
|
||||
|
||||
private void flushNow() {
|
||||
final RetainedTesrGroups hook = activePass >= 0 ? retained[activePass] : null;
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_TESR_OPAQUE);
|
||||
@@ -613,6 +661,7 @@ public final class TesrBatchRenderer {
|
||||
try {
|
||||
layer.startDrawing();
|
||||
layerStarted = true;
|
||||
BatchStateGuard.beforeDraw();
|
||||
immediateMesh.upload(format, template.drawMode, immediateScratch, vertexCount);
|
||||
immediateMesh.render();
|
||||
} catch (Throwable t) {
|
||||
|
||||
@@ -10,6 +10,8 @@ public final class TesrLifecycle {
|
||||
|
||||
public static void reset() {
|
||||
TesrBatchRenderer.INSTANCE.clearRetained();
|
||||
TranslucentPeel.destroy();
|
||||
ImmediateDrawOrder.endPass();
|
||||
ModelPartBatcher.INSTANCE.clear();
|
||||
EntityTextureAtlas.INSTANCE.clear();
|
||||
DroppedItemInstancer.forgetItemDraws();
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.mojang.RenderLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDepthStencilAlpha;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMFramebuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMQueries;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.DepthBufferFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucencyLayers;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TranslucencyMode;
|
||||
import net.coderbot.batchedentityrendering.impl.TransparencyType;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gl.framebuffer.GlFramebuffer;
|
||||
import net.coderbot.iris.rendertarget.IRenderTargetExt;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/// Depth peels the translucent geometry the block entity batchers have queued ([DepthPeel]), since batches group it by
|
||||
/// layer and material rather than by depth. A flush whose translucent geometry comes from one block entity in one
|
||||
/// batch that keeps submission order is not peeled.
|
||||
///
|
||||
/// Each of the first [#SLOTS_PER_PASS] peeled flushes of a pass keeps its own peel count, and later ones peel
|
||||
/// [#MAX_PEELS]. Occlusion queries, read back frames later without stalling, lower a count one peel at a time while
|
||||
/// its last peel draws nothing and reset it when the rest pass draws.
|
||||
public final class TranslucentPeel {
|
||||
|
||||
private static final int MAX_PEELS = 4;
|
||||
private static final int SLOTS_PER_PASS = 32;
|
||||
|
||||
private static int width;
|
||||
private static int height;
|
||||
private static int depthVersion = -1;
|
||||
private static DepthBufferFormat depthFormat;
|
||||
private static Target[] peels;
|
||||
private static Target sceneDepth;
|
||||
private static StateSet clearState;
|
||||
private static final Slot[] slots = new Slot[2 * SLOTS_PER_PASS];
|
||||
private static int passFlushes;
|
||||
private static boolean replay;
|
||||
private static Object firstSource;
|
||||
private static Object firstBatch;
|
||||
private static int firstBlockEntity;
|
||||
private static int blockEntity;
|
||||
private static boolean mixed;
|
||||
|
||||
private TranslucentPeel() {}
|
||||
|
||||
static boolean enabled() {
|
||||
return ClientProxy.options().performance.translucencyMode == TranslucencyMode.FANCY && Iris.enabled
|
||||
&& TesrBatchRenderer.deferredPipeline() == null;
|
||||
}
|
||||
|
||||
public static void beginPass() {
|
||||
passFlushes = 0;
|
||||
resetBatches();
|
||||
}
|
||||
|
||||
public static void beforeBlockEntity() {
|
||||
blockEntity++;
|
||||
}
|
||||
|
||||
/// `source` and `batch` identify one draw: an `AngelicaBufferSource` and a layer, or [RetainedTesrGroups] and a
|
||||
/// group.
|
||||
public static void queued(RenderLayer layer, Object source, Object batch) {
|
||||
if (mixed || layer.getTransparencyType() == TransparencyType.OPAQUE) return;
|
||||
if (firstBatch == null) {
|
||||
firstSource = source;
|
||||
firstBatch = batch;
|
||||
firstBlockEntity = blockEntity;
|
||||
} else if (batch != firstBatch || source != firstSource || blockEntity != firstBlockEntity) {
|
||||
mixed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void queuedUnordered(RenderLayer layer) {
|
||||
if (layer.getTransparencyType() != TransparencyType.OPAQUE) mixed = true;
|
||||
}
|
||||
|
||||
private static void resetBatches() {
|
||||
firstSource = null;
|
||||
firstBatch = null;
|
||||
mixed = false;
|
||||
}
|
||||
|
||||
public static void flush() {
|
||||
final ModelPartBatcher models = ModelPartBatcher.INSTANCE;
|
||||
final TesrBatchRenderer tesrs = TesrBatchRenderer.INSTANCE;
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
models.drawOpaque();
|
||||
tesrs.drawOpaque();
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
if (!enabled()) {
|
||||
if (peels != null) destroy();
|
||||
return;
|
||||
}
|
||||
models.queueShadows();
|
||||
final boolean peeling = mixed;
|
||||
resetBatches();
|
||||
if (!peeling) return;
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
final boolean modelsQueued = models.beginPeel();
|
||||
final boolean tesrsQueued = tesrs.beginPeel();
|
||||
if (modelsQueued || tesrsQueued) peel(modelsQueued, tesrsQueued);
|
||||
} finally {
|
||||
try {
|
||||
tesrs.endPeel();
|
||||
} finally {
|
||||
try {
|
||||
models.endPeel();
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void draw(boolean models, boolean tesrs) {
|
||||
if (models) ModelPartBatcher.INSTANCE.drawPeeled(replay);
|
||||
if (tesrs) TesrBatchRenderer.INSTANCE.drawPeeled(replay);
|
||||
replay = true;
|
||||
}
|
||||
|
||||
private static void peel(boolean models, boolean tesrs) {
|
||||
replay = false;
|
||||
final int target = GLSMFramebuffer.getDrawFramebuffer();
|
||||
final int read = GLSMFramebuffer.getReadFramebuffer();
|
||||
if (!syncSize() || target != Minecraft.getMinecraft().getFramebuffer().framebufferObject && target != TranslucencyLayers.INSTANCE.openFramebuffer()) {
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, target);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, read);
|
||||
draw(models, tesrs);
|
||||
return;
|
||||
}
|
||||
final int unit = GLSMTextures.getActiveTextureUnit();
|
||||
final int previousPeelTexture = GLSMTextures.getTextures().getTextureUnitBindings(DepthPeel.TEXTURE_UNIT).getBinding();
|
||||
final int previousSceneTexture = GLSMTextures.getTextures().getTextureUnitBindings(DepthPeel.SCENE_TEXTURE_UNIT).getBinding();
|
||||
final Slot slot = slot(passFlushes++);
|
||||
final int count = slot != null ? slot.update() : MAX_PEELS;
|
||||
final boolean query = slot != null && slot.beginQueries(count);
|
||||
boolean completed = false;
|
||||
try {
|
||||
blitDepth(target, sceneDepth.framebuffer.getId());
|
||||
bindTexture(DepthPeel.SCENE_TEXTURE_UNIT, sceneDepth.depth);
|
||||
Target bound = sceneDepth;
|
||||
for (int i = 0; i < count; i++) {
|
||||
final Target current = peels[i % 2];
|
||||
clearDepth(current);
|
||||
current.framebuffer.bind();
|
||||
bindTexture(DepthPeel.TEXTURE_UNIT, bound.depth);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
DepthPeel.setPass(DepthPeel.Pass.DEPTH, i == 0);
|
||||
if (query) slot.begin(i);
|
||||
draw(models, tesrs);
|
||||
if (query) slot.end();
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, target);
|
||||
bindTexture(DepthPeel.TEXTURE_UNIT, current.depth);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
DepthPeel.setPass(DepthPeel.Pass.COLOR, false);
|
||||
draw(models, tesrs);
|
||||
bound = current;
|
||||
}
|
||||
DepthPeel.setPass(DepthPeel.Pass.REST, false);
|
||||
if (query) slot.begin(count);
|
||||
draw(models, tesrs);
|
||||
if (query) slot.end();
|
||||
if (DepthPeel.skippedApplicationDraws()) {
|
||||
DepthPeel.setPass(DepthPeel.Pass.APPLICATION, false);
|
||||
draw(models, tesrs);
|
||||
}
|
||||
completed = true;
|
||||
} finally {
|
||||
if (!completed && slot != null) slot.abandon();
|
||||
DepthPeel.setPass(DepthPeel.Pass.OFF, false);
|
||||
bindTexture(DepthPeel.TEXTURE_UNIT, previousPeelTexture);
|
||||
bindTexture(DepthPeel.SCENE_TEXTURE_UNIT, previousSceneTexture);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, target);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, read);
|
||||
}
|
||||
}
|
||||
|
||||
private static Slot slot(int flush) {
|
||||
if (flush >= SLOTS_PER_PASS) return null;
|
||||
final int index = (MinecraftForgeClient.getRenderPass() == 1 ? SLOTS_PER_PASS : 0) + flush;
|
||||
if (slots[index] == null) slots[index] = new Slot();
|
||||
return slots[index];
|
||||
}
|
||||
|
||||
private static void clearDepth(Target target) {
|
||||
if (clearState == null) clearState = StateSet.forMask(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_SCISSOR_BIT);
|
||||
final int saved = GLSMAttribStack.pushState(clearState);
|
||||
try {
|
||||
GLSMRasterizer.disableScissorTest();
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
GLSMDepthStencilAlpha.glClearDepth(0.0);
|
||||
target.framebuffer.bind();
|
||||
GLSMFramebuffer.glClear(GL11.GL_DEPTH_BUFFER_BIT);
|
||||
} finally {
|
||||
GLSMAttribStack.popStateTo(saved);
|
||||
}
|
||||
}
|
||||
|
||||
private static void blitDepth(int from, int to) {
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, from);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, to);
|
||||
GLSMFramebuffer.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
private static void bindTexture(int unit, int texture) {
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
private static boolean syncSize() {
|
||||
final Framebuffer main = Minecraft.getMinecraft().getFramebuffer();
|
||||
final IRenderTargetExt ext = (IRenderTargetExt) main;
|
||||
if (peels != null && main.framebufferWidth == width && main.framebufferHeight == height && ext.iris$getDepthBufferVersion() == depthVersion) return true;
|
||||
destroyTargets();
|
||||
width = main.framebufferWidth;
|
||||
height = main.framebufferHeight;
|
||||
depthVersion = ext.iris$getDepthBufferVersion();
|
||||
final int mainDepth = ext.iris$getDepthTextureId();
|
||||
if (width <= 0 || height <= 0 || mainDepth <= 0) return false;
|
||||
depthFormat = DepthBufferFormat.fromGlEnumOrDefault(TextureInfoCache.INSTANCE.getInfo(mainDepth).getInternalFormat());
|
||||
sceneDepth = new Target(depthFormat.getGlInternalFormat(), depthFormat.getGlType(), depthFormat.getGlFormat());
|
||||
peels = new Target[] { Target.float32(), Target.float32() };
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void destroy() {
|
||||
destroyTargets();
|
||||
for (int i = 0; i < slots.length; i++) {
|
||||
if (slots[i] != null) slots[i].delete();
|
||||
slots[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void destroyTargets() {
|
||||
if (peels != null) for (Target target : peels) target.destroy();
|
||||
if (sceneDepth != null) sceneDepth.destroy();
|
||||
peels = null;
|
||||
sceneDepth = null;
|
||||
}
|
||||
|
||||
private static final class Slot {
|
||||
private int count = MAX_PEELS;
|
||||
private final int[] queries = new int[MAX_PEELS + 1];
|
||||
private int issued = -1;
|
||||
private boolean querying;
|
||||
|
||||
int update() {
|
||||
if (issued < 0) return count;
|
||||
for (int i = 0; i <= issued; i++) {
|
||||
if (GLSMQueries.glGetQueryObjecti(queries[i], GL15.GL_QUERY_RESULT_AVAILABLE) == 0) return count;
|
||||
}
|
||||
int drawn = issued;
|
||||
for (int i = 0; i < issued; i++) {
|
||||
if (GLSMQueries.glGetQueryObjecti(queries[i], GL15.GL_QUERY_RESULT) == 0) {
|
||||
drawn = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (drawn == issued && GLSMQueries.glGetQueryObjecti(queries[issued], GL15.GL_QUERY_RESULT) != 0) {
|
||||
count = MAX_PEELS;
|
||||
} else if (drawn < count) {
|
||||
count = Math.max(1, count - 1);
|
||||
}
|
||||
issued = -1;
|
||||
return count;
|
||||
}
|
||||
|
||||
boolean beginQueries(int peels) {
|
||||
if (issued >= 0 || RENDER_BACKEND.isSDLGPU()) return false;
|
||||
if (queries[0] == 0) {
|
||||
try {
|
||||
for (int i = 0; i < queries.length; i++) queries[i] = GLSMQueries.glGenQueries();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
issued = peels;
|
||||
return true;
|
||||
}
|
||||
|
||||
void begin(int peel) {
|
||||
GLSMQueries.glBeginQuery(GL33.GL_ANY_SAMPLES_PASSED, queries[peel]);
|
||||
querying = true;
|
||||
}
|
||||
|
||||
void end() {
|
||||
querying = false;
|
||||
GLSMQueries.glEndQuery(GL33.GL_ANY_SAMPLES_PASSED);
|
||||
}
|
||||
|
||||
void abandon() {
|
||||
if (querying) end();
|
||||
issued = -1;
|
||||
}
|
||||
|
||||
void delete() {
|
||||
if (queries[0] == 0) return;
|
||||
for (int query : queries) GLSMQueries.glDeleteQueries(query);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Target {
|
||||
final int depth;
|
||||
final GlFramebuffer framebuffer = new GlFramebuffer();
|
||||
|
||||
Target(int internalFormat, int format, int type) {
|
||||
depth = texture(internalFormat, format, type);
|
||||
framebuffer.addDepthAttachment(depth);
|
||||
framebuffer.noDrawBuffers();
|
||||
RenderSystem.readBuffer(framebuffer.getId(), GL11.GL_NONE);
|
||||
}
|
||||
|
||||
static Target float32() {
|
||||
return new Target(GL30.GL_DEPTH_COMPONENT32F, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT);
|
||||
}
|
||||
|
||||
private static int texture(int internalFormat, int format, int type) {
|
||||
final int previous = GLSMQueries.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
final int texture = GLSMTextures.glGenTextures();
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_COMPARE_MODE, GL11.GL_NONE);
|
||||
GLSMTextures.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, (ByteBuffer) null);
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
framebuffer.destroy();
|
||||
GLSMTextures.glDeleteTextures(depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
+485
@@ -0,0 +1,485 @@
|
||||
package com.gtnewhorizons.angelica.rendering.translucency;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDepthStencilAlpha;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMFramebuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMQueries;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ColorMaskStack;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.DepthBufferFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.render.PanoramaRenderer;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gl.framebuffer.GlFramebuffer;
|
||||
import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
|
||||
import net.coderbot.iris.rendertarget.IRenderTargetExt;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.GlProgram;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderConstants;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/// Draws each [TranslucentLayer] into its own target, seeded with the main depth, and composites the drawn layers over
|
||||
/// the main framebuffer per pixel, farthest first. Layers hold premultiplied color with coverage alpha (GLSM layer
|
||||
/// blending), and the composite writes the nearest layer depth.
|
||||
///
|
||||
/// The terrain layer has a second sheet for fragments behind the block-entity layer, so a tile entity inside a
|
||||
/// block's own translucent model is covered only by the faces in front of it.
|
||||
///
|
||||
/// Glows (blended block-entity draws that write no depth) would sort at the opaque depth behind them, so
|
||||
/// [DepthEcho] records their depth separately, and it is merged into the block-entity layer before anything
|
||||
/// reads that depth.
|
||||
public final class TranslucencyLayers {
|
||||
|
||||
private static final int LAYERS = TranslucentLayer.values().length;
|
||||
private static final int FIRST_LAYER_UNIT = 1;
|
||||
private static final int TERRAIN_BEHIND_UNIT = FIRST_LAYER_UNIT + 2 * LAYERS;
|
||||
private static final int SPLIT_DEPTH_UNIT = TERRAIN_BEHIND_UNIT + 1;
|
||||
private static final int[] SINGLE_BUFFER = { 0 };
|
||||
private static final int[] SPLIT_BUFFERS = { 0, 1 };
|
||||
private static final int[] BEHIND_BUFFER = { 1 };
|
||||
|
||||
public static final TranslucencyLayers INSTANCE = new TranslucencyLayers(new MinecraftTarget());
|
||||
|
||||
interface MainTarget {
|
||||
int framebuffer();
|
||||
int depthTexture();
|
||||
int width();
|
||||
int height();
|
||||
int depthVersion();
|
||||
}
|
||||
|
||||
private final MainTarget main;
|
||||
private final Target[] layers = new Target[LAYERS];
|
||||
private Target depthSnapshot;
|
||||
private int width;
|
||||
private int height;
|
||||
private int depthVersion = -1;
|
||||
private DepthBufferFormat depthFormat;
|
||||
|
||||
private boolean decided;
|
||||
private boolean active;
|
||||
private int drawnMask;
|
||||
private TranslucentLayer open;
|
||||
private int savedDraw;
|
||||
private int savedRead;
|
||||
private boolean alphaMasked;
|
||||
|
||||
private boolean splitting;
|
||||
private boolean behindDrawn;
|
||||
|
||||
private Target glowDepth;
|
||||
private boolean replayingGlows;
|
||||
|
||||
private CompositeProgram program;
|
||||
private CompositeProgram behindProgram;
|
||||
private GlProgram<Object> glowMergeProgram;
|
||||
private int vao;
|
||||
private StateSet prepareState;
|
||||
private StateSet compositeState;
|
||||
private StateSet clearDepthState;
|
||||
private final Runnable compositePass = this::drawComposite;
|
||||
private final Runnable glowMergePass = this::drawGlowMerge;
|
||||
|
||||
TranslucencyLayers(MainTarget main) {
|
||||
this.main = main;
|
||||
}
|
||||
|
||||
/// Whether layering applies is decided on the first [#active] call, once Iris has prepared the frame's pipeline.
|
||||
public void beginFrame() {
|
||||
decided = false;
|
||||
resetFrame();
|
||||
}
|
||||
|
||||
void beginFrame(boolean active) {
|
||||
this.active = active;
|
||||
decided = true;
|
||||
resetFrame();
|
||||
}
|
||||
|
||||
private void resetFrame() {
|
||||
drawnMask = 0;
|
||||
behindDrawn = false;
|
||||
DepthEcho.takeEchoed();
|
||||
}
|
||||
|
||||
public boolean active() {
|
||||
if (!decided) {
|
||||
active = ClientProxy.options().performance.translucencyMode.composites() && Iris.enabled
|
||||
&& Iris.getPipelineManager().getPipelineNullable() instanceof FixedFunctionWorldRenderingPipeline;
|
||||
decided = true;
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
public boolean beginIfActive(TranslucentLayer layer) {
|
||||
if (!active()) return false;
|
||||
begin(layer);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void begin(TranslucentLayer layer) {
|
||||
if (open != null) throw new IllegalStateException("Translucency layer " + open + " is still open");
|
||||
BatchStateGuard.beforeChange();
|
||||
savedDraw = GLSMFramebuffer.getDrawFramebuffer();
|
||||
savedRead = GLSMFramebuffer.getReadFramebuffer();
|
||||
syncSize();
|
||||
if (layer == TranslucentLayer.TERRAIN) mergeGlowDepth();
|
||||
final Target target = layer(layer);
|
||||
final int bit = 1 << layer.ordinal();
|
||||
if ((drawnMask & bit) == 0) {
|
||||
drawnMask |= bit;
|
||||
prepare(target);
|
||||
if (layer == TranslucentLayer.BLOCK_ENTITIES) clearDepth(glowDepth());
|
||||
}
|
||||
target.framebuffer.bind();
|
||||
if (layer == TranslucentLayer.BLOCK_ENTITIES) DepthEcho.arm(glowDepth.framebuffer.getId(), target.framebuffer.getId());
|
||||
open = layer;
|
||||
GLSMBlend.setLayerBlending(true);
|
||||
final ColorMaskStack mask = GLSMBlend.getColorMask();
|
||||
alphaMasked = !mask.alpha;
|
||||
if (alphaMasked) GLSMBlend.glColorMask(mask.red, mask.green, mask.blue, true);
|
||||
}
|
||||
|
||||
public void end() {
|
||||
BatchStateGuard.beforeChange();
|
||||
DepthEcho.disarm();
|
||||
if (alphaMasked) {
|
||||
final ColorMaskStack mask = GLSMBlend.getColorMask();
|
||||
GLSMBlend.glColorMask(mask.red, mask.green, mask.blue, false);
|
||||
}
|
||||
GLSMBlend.setLayerBlending(false);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead);
|
||||
open = null;
|
||||
}
|
||||
|
||||
public boolean redirectsMainBind() {
|
||||
return open != null;
|
||||
}
|
||||
|
||||
public int openFramebuffer() {
|
||||
return open != null ? layers[open.ordinal()].framebuffer.getId() : 0;
|
||||
}
|
||||
|
||||
public void bindOpenLayer() {
|
||||
(replayingGlows ? glowDepth : layers[open.ordinal()]).framebuffer.bind();
|
||||
}
|
||||
|
||||
public boolean beginGlowReplay() {
|
||||
if (!DepthEcho.armed()) return false;
|
||||
BatchStateGuard.beforeChange();
|
||||
glowDepth.framebuffer.bind();
|
||||
replayingGlows = true;
|
||||
DepthEcho.beginReplay();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void endGlowReplay() {
|
||||
BatchStateGuard.beforeChange();
|
||||
DepthEcho.endReplay();
|
||||
replayingGlows = false;
|
||||
layers[open.ordinal()].framebuffer.bind();
|
||||
}
|
||||
|
||||
public void beginTerrainSplit(int program) {
|
||||
splitting = open == TranslucentLayer.TERRAIN && (drawnMask & (1 << TranslucentLayer.BLOCK_ENTITIES.ordinal())) != 0;
|
||||
GLSMPrograms.glUniform1i(GLSMPrograms.glGetUniformLocation(program, "u_SplitEnabled"), splitting ? 1 : 0);
|
||||
if (!splitting) return;
|
||||
GLSMPrograms.glUniform1i(GLSMPrograms.glGetUniformLocation(program, "u_SplitDepth"), SPLIT_DEPTH_UNIT);
|
||||
final Target terrain = layers[TranslucentLayer.TERRAIN.ordinal()];
|
||||
if (!behindDrawn) {
|
||||
behindDrawn = true;
|
||||
terrain.attachBehind(width, height);
|
||||
terrain.framebuffer.drawBuffers(BEHIND_BUFFER);
|
||||
clear(terrain);
|
||||
}
|
||||
final int unit = GLSMTextures.getActiveTextureUnit();
|
||||
bindTexture(SPLIT_DEPTH_UNIT, layers[TranslucentLayer.BLOCK_ENTITIES.ordinal()].depth);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
terrain.framebuffer.drawBuffers(SPLIT_BUFFERS);
|
||||
}
|
||||
|
||||
public void endTerrainSplit() {
|
||||
if (!splitting) return;
|
||||
splitting = false;
|
||||
layers[TranslucentLayer.TERRAIN.ordinal()].framebuffer.drawBuffers(SINGLE_BUFFER);
|
||||
final int unit = GLSMTextures.getActiveTextureUnit();
|
||||
bindTexture(SPLIT_DEPTH_UNIT, 0);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
}
|
||||
|
||||
public void composite() {
|
||||
if (drawnMask == 0) return;
|
||||
BatchStateGuard.beforeChange();
|
||||
syncSize();
|
||||
if (drawnMask == 0) return;
|
||||
try {
|
||||
mergeGlowDepth();
|
||||
fullscreenPass(compositePass);
|
||||
} finally {
|
||||
drawnMask = 0;
|
||||
behindDrawn = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void drawComposite() {
|
||||
final Target snapshot = depthSnapshot();
|
||||
blit(main.framebuffer(), snapshot.framebuffer.getId(), GL11.GL_DEPTH_BUFFER_BIT);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_FRAMEBUFFER, main.framebuffer());
|
||||
GLSMBlend.enableBlend();
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
GLSMDepthStencilAlpha.glDepthFunc(GL11.GL_ALWAYS);
|
||||
bindTexture(0, snapshot.depth);
|
||||
for (int i = 0; i < LAYERS; i++) {
|
||||
if ((drawnMask & (1 << i)) == 0) continue;
|
||||
bindTexture(FIRST_LAYER_UNIT + i, layers[i].color);
|
||||
bindTexture(FIRST_LAYER_UNIT + LAYERS + i, layers[i].depth);
|
||||
}
|
||||
if (behindDrawn) bindTexture(TERRAIN_BEHIND_UNIT, layers[TranslucentLayer.TERRAIN.ordinal()].behind);
|
||||
final CompositeProgram composite = behindDrawn ? behindProgram : program;
|
||||
GLSMPrograms.glUseProgram(composite.program.handle());
|
||||
GLSMPrograms.glUniform1i(composite.maskUniform, drawnMask);
|
||||
}
|
||||
|
||||
private void mergeGlowDepth() {
|
||||
if (!DepthEcho.takeEchoed() || (drawnMask & (1 << TranslucentLayer.BLOCK_ENTITIES.ordinal())) == 0) return;
|
||||
fullscreenPass(glowMergePass);
|
||||
}
|
||||
|
||||
private void drawGlowMerge() {
|
||||
layers[TranslucentLayer.BLOCK_ENTITIES.ordinal()].framebuffer.bind();
|
||||
GLSMBlend.disableBlend();
|
||||
GLSMBlend.glColorMask(false, false, false, false);
|
||||
GLSMDepthStencilAlpha.glDepthFunc(GL11.GL_LESS);
|
||||
bindTexture(0, glowDepth.depth);
|
||||
GLSMPrograms.glUseProgram(glowMergeProgram.handle());
|
||||
}
|
||||
|
||||
private void fullscreenPass(Runnable setup) {
|
||||
final int draw = GLSMFramebuffer.getDrawFramebuffer();
|
||||
final int read = GLSMFramebuffer.getReadFramebuffer();
|
||||
final int activeProgram = GLSMPrograms.getActiveProgram();
|
||||
final int activeVao = GLSMQueries.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING);
|
||||
if (program == null) initProgram();
|
||||
final int depth = GLSMAttribStack.pushState(compositeState);
|
||||
try {
|
||||
GLSMRasterizer.disableScissorTest();
|
||||
GLSMRasterizer.disableCull();
|
||||
GLSMDepthStencilAlpha.enableDepthTest();
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
setup.run();
|
||||
GLSMRasterizer.glViewport(0, 0, width, height);
|
||||
GLSMVertexInput.glBindVertexArray(vao);
|
||||
GLSMDraws.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
} finally {
|
||||
GLSMVertexInput.glBindVertexArray(activeVao);
|
||||
GLSMPrograms.glUseProgram(activeProgram);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, draw);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, read);
|
||||
GLSMAttribStack.popStateTo(depth);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
destroyTargets();
|
||||
if (program != null) {
|
||||
program.program.delete();
|
||||
behindProgram.program.delete();
|
||||
glowMergeProgram.delete();
|
||||
GLSMVertexInput.glDeleteVertexArrays(vao);
|
||||
program = null;
|
||||
behindProgram = null;
|
||||
glowMergeProgram = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initProgram() {
|
||||
program = new CompositeProgram("angelica:translucency_composite", ShaderConstants.EMPTY);
|
||||
behindProgram = new CompositeProgram("angelica:translucency_composite_behind", ShaderConstants.builder().add("TERRAIN_BEHIND").build());
|
||||
glowMergeProgram = PanoramaRenderer.loadProgram("angelica:translucency_glow_depth", "angelica:panorama_blur", "angelica:translucency_glow_depth", ctx -> ctx);
|
||||
final int merge = glowMergeProgram.handle();
|
||||
GLSMPrograms.glUseProgram(merge);
|
||||
GLSMPrograms.glUniform1i(GLSMPrograms.glGetUniformLocation(merge, "u_GlowDepth"), 0);
|
||||
GLSMPrograms.glUseProgram(0);
|
||||
vao = GLSMVertexInput.glGenVertexArrays();
|
||||
compositeState = StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS);
|
||||
}
|
||||
|
||||
private void prepare(Target target) {
|
||||
clear(target);
|
||||
blit(main.framebuffer(), target.framebuffer.getId(), depthFormat.isCombinedStencil() ? GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT : GL11.GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
private void clear(Target target) {
|
||||
if (prepareState == null) prepareState = StateSet.forMask(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_SCISSOR_BIT);
|
||||
final int depth = GLSMAttribStack.pushState(prepareState);
|
||||
try {
|
||||
GLSMRasterizer.disableScissorTest();
|
||||
GLSMBlend.glColorMask(true, true, true, true);
|
||||
target.framebuffer.bind();
|
||||
GLSMBlend.glClearColor(0f, 0f, 0f, 0f);
|
||||
GLSMFramebuffer.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
} finally {
|
||||
GLSMAttribStack.popStateTo(depth);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearDepth(Target target) {
|
||||
if (clearDepthState == null) clearDepthState = StateSet.forMask(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_SCISSOR_BIT);
|
||||
final int depth = GLSMAttribStack.pushState(clearDepthState);
|
||||
try {
|
||||
GLSMRasterizer.disableScissorTest();
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
GLSMDepthStencilAlpha.glClearDepth(1.0);
|
||||
target.framebuffer.bind();
|
||||
GLSMFramebuffer.glClear(GL11.GL_DEPTH_BUFFER_BIT);
|
||||
} finally {
|
||||
GLSMAttribStack.popStateTo(depth);
|
||||
}
|
||||
}
|
||||
|
||||
private void blit(int from, int to, int mask) {
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, from);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, to);
|
||||
GLSMFramebuffer.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
private static void bindTexture(int unit, int texture) {
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
private void syncSize() {
|
||||
if (main.width() == width && main.height() == height && main.depthVersion() == depthVersion) return;
|
||||
destroyTargets();
|
||||
width = main.width();
|
||||
height = main.height();
|
||||
depthVersion = main.depthVersion();
|
||||
depthFormat = DepthBufferFormat.fromGlEnumOrDefault(TextureInfoCache.INSTANCE.getInfo(main.depthTexture()).getInternalFormat());
|
||||
}
|
||||
|
||||
private Target layer(TranslucentLayer layer) {
|
||||
final int i = layer.ordinal();
|
||||
if (layers[i] == null) layers[i] = new Target(width, height, depthFormat, true);
|
||||
return layers[i];
|
||||
}
|
||||
|
||||
private Target glowDepth() {
|
||||
if (glowDepth == null) glowDepth = new Target(width, height, depthFormat, false);
|
||||
return glowDepth;
|
||||
}
|
||||
|
||||
private Target depthSnapshot() {
|
||||
if (depthSnapshot == null) depthSnapshot = new Target(width, height, depthFormat, false);
|
||||
return depthSnapshot;
|
||||
}
|
||||
|
||||
private void destroyTargets() {
|
||||
for (int i = 0; i < LAYERS; i++) {
|
||||
if (layers[i] != null) layers[i].destroy();
|
||||
layers[i] = null;
|
||||
}
|
||||
if (depthSnapshot != null) depthSnapshot.destroy();
|
||||
depthSnapshot = null;
|
||||
if (glowDepth != null) glowDepth.destroy();
|
||||
glowDepth = null;
|
||||
DepthEcho.disarm();
|
||||
resetFrame();
|
||||
}
|
||||
|
||||
private static final class CompositeProgram {
|
||||
final GlProgram<Object> program;
|
||||
final int maskUniform;
|
||||
|
||||
CompositeProgram(String name, ShaderConstants constants) {
|
||||
program = PanoramaRenderer.loadProgram(name, "angelica:panorama_blur", "angelica:translucency_composite", constants, ctx -> ctx);
|
||||
final int handle = program.handle();
|
||||
GLSMPrograms.glUseProgram(handle);
|
||||
GLSMPrograms.glUniform1i(GLSMPrograms.glGetUniformLocation(handle, "u_Depth"), 0);
|
||||
for (int i = 0; i < LAYERS; i++) {
|
||||
GLSMPrograms.glUniform1i(GLSMPrograms.glGetUniformLocation(handle, "u_Layer" + i), FIRST_LAYER_UNIT + i);
|
||||
GLSMPrograms.glUniform1i(GLSMPrograms.glGetUniformLocation(handle, "u_LayerDepth" + i), FIRST_LAYER_UNIT + LAYERS + i);
|
||||
}
|
||||
GLSMPrograms.glUniform1i(GLSMPrograms.glGetUniformLocation(handle, "u_TerrainBehind"), TERRAIN_BEHIND_UNIT);
|
||||
maskUniform = GLSMPrograms.glGetUniformLocation(handle, "u_LayerMask");
|
||||
GLSMPrograms.glUseProgram(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Target {
|
||||
final int color;
|
||||
final int depth;
|
||||
final GlFramebuffer framebuffer;
|
||||
int behind;
|
||||
|
||||
Target(int width, int height, DepthBufferFormat depthFormat, boolean withColor) {
|
||||
depth = texture(width, height, depthFormat.getGlInternalFormat(), depthFormat.getGlType(), depthFormat.getGlFormat());
|
||||
framebuffer = new GlFramebuffer();
|
||||
framebuffer.addDepthAttachment(depth);
|
||||
if (withColor) {
|
||||
color = texture(width, height, GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
framebuffer.addColorAttachment(0, color);
|
||||
framebuffer.drawBuffers(SINGLE_BUFFER);
|
||||
framebuffer.readBuffer(0);
|
||||
} else {
|
||||
color = 0;
|
||||
framebuffer.noDrawBuffers();
|
||||
RenderSystem.readBuffer(framebuffer.getId(), GL11.GL_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void attachBehind(int width, int height) {
|
||||
if (behind != 0) return;
|
||||
behind = texture(width, height, GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
framebuffer.addColorAttachment(1, behind);
|
||||
}
|
||||
|
||||
private static int texture(int width, int height, int internalFormat, int format, int type) {
|
||||
final int previous = GLSMQueries.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
final int texture = GLSMTextures.glGenTextures();
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLSMTextures.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, (ByteBuffer) null);
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
framebuffer.destroy();
|
||||
if (color != 0) GLSMTextures.glDeleteTextures(color);
|
||||
if (behind != 0) GLSMTextures.glDeleteTextures(behind);
|
||||
GLSMTextures.glDeleteTextures(depth);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class MinecraftTarget implements MainTarget {
|
||||
private static Framebuffer fb() {
|
||||
return Minecraft.getMinecraft().getFramebuffer();
|
||||
}
|
||||
|
||||
@Override public int framebuffer() { return fb().framebufferObject; }
|
||||
@Override public int depthTexture() { return ((IRenderTargetExt) fb()).iris$getDepthTextureId(); }
|
||||
@Override public int width() { return fb().framebufferWidth; }
|
||||
@Override public int height() { return fb().framebufferHeight; }
|
||||
@Override public int depthVersion() { return ((IRenderTargetExt) fb()).iris$getDepthBufferVersion(); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.rendering.translucency;
|
||||
|
||||
public enum TranslucentLayer {
|
||||
TERRAIN,
|
||||
BLOCK_ENTITIES,
|
||||
PARTICLES,
|
||||
WEATHER,
|
||||
CLOUDS
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.ParticleMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TexelSampling;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TextureFilterMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TranslucencyMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.AngelicaOptionsStorage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.CubicChunksOptionStorage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.MinecraftOptionsStorage;
|
||||
@@ -403,15 +404,23 @@ public class SodiumGameOptionPages {
|
||||
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
|
||||
.build()
|
||||
)
|
||||
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.translucency_sorting.name"))
|
||||
.setTooltip(I18n.format("sodium.options.translucency_sorting.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> opts.performance.translucencySorting = value, opts -> opts.performance.translucencySorting)
|
||||
.add(OptionImpl.createBuilder(TranslucencyMode.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.translucency_mode.name"))
|
||||
.setTooltip(I18n.format("sodium.options.translucency_mode.tooltip"))
|
||||
.setControl(o -> new CyclingControl<>(o, TranslucencyMode.class))
|
||||
.setBinding((opts, value) -> opts.performance.translucencyMode = value, opts -> opts.performance.translucencyMode)
|
||||
.setImpact(OptionImpact.MEDIUM)
|
||||
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
|
||||
.build()
|
||||
)
|
||||
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.block_entities_before_translucent_terrain.name"))
|
||||
.setTooltip(I18n.format("sodium.options.block_entities_before_translucent_terrain.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> opts.performance.blockEntitiesBeforeTranslucentTerrain = value, opts -> opts.performance.blockEntitiesBeforeTranslucentTerrain)
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.build()
|
||||
)
|
||||
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.use_entity_culling.name"))
|
||||
.setTooltip(I18n.format("sodium.options.use_entity_culling.tooltip"))
|
||||
|
||||
@@ -15,6 +15,7 @@ import me.jellysquid.mods.sodium.client.gui.options.named.MipmapGeneration;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TexelSampling;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TextureFilterMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TranslucencyMode;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
@@ -24,6 +25,7 @@ import org.embeddedt.embeddium.impl.render.chunk.occlusion.AsyncOcclusionMode;
|
||||
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -124,7 +126,7 @@ public class SodiumGameOptions {
|
||||
|
||||
if (Files.exists(path)) {
|
||||
try (FileReader reader = new FileReader(path.toFile())) {
|
||||
config = GSON.fromJson(reader, SodiumGameOptions.class);
|
||||
config = read(reader);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not parse config", e);
|
||||
} catch (JsonSyntaxException e) {
|
||||
@@ -142,23 +144,6 @@ public class SodiumGameOptions {
|
||||
|
||||
config.configPath = path;
|
||||
|
||||
if (config.advanced.streamingUploadStrategy == null) {
|
||||
config.advanced.streamingUploadStrategy = StreamingUploader.UploadStrategy.MAP_BUFFER_RANGE;
|
||||
}
|
||||
|
||||
if (config.quality.textureFilterMode == null) {
|
||||
config.quality.textureFilterMode = TextureFilterMode.RGSS_ANISOTROPIC;
|
||||
}
|
||||
|
||||
if (config.quality.texelSampling == null) {
|
||||
config.quality.texelSampling = TexelSampling.LINEAR;
|
||||
}
|
||||
config.quality.guiBlockSupersampling = Integer.highestOneBit(MathHelper.clamp_int(config.quality.guiBlockSupersampling, 1, 16));
|
||||
|
||||
if (config.quality.mipmapGeneration == null) {
|
||||
config.quality.mipmapGeneration = MipmapGeneration.MODERN_VANILLA;
|
||||
}
|
||||
|
||||
if (GLStateManager.capabilities != null) {
|
||||
if (!GLStateManager.capabilities.OpenGL32) {
|
||||
config.performance.cpuRenderAheadLimit = 0;
|
||||
@@ -179,6 +164,43 @@ public class SodiumGameOptions {
|
||||
return config;
|
||||
}
|
||||
|
||||
static SodiumGameOptions read(Reader reader) {
|
||||
final SodiumGameOptions config = GSON.fromJson(reader, SodiumGameOptions.class);
|
||||
config.normalize();
|
||||
return config;
|
||||
}
|
||||
|
||||
private void normalize() {
|
||||
if (this.advanced.streamingUploadStrategy == null) {
|
||||
this.advanced.streamingUploadStrategy = StreamingUploader.UploadStrategy.MAP_BUFFER_RANGE;
|
||||
}
|
||||
|
||||
if (this.quality.textureFilterMode == null) {
|
||||
this.quality.textureFilterMode = TextureFilterMode.RGSS_ANISOTROPIC;
|
||||
}
|
||||
|
||||
if (this.quality.texelSampling == null) {
|
||||
this.quality.texelSampling = TexelSampling.LINEAR;
|
||||
}
|
||||
this.quality.guiBlockSupersampling = Integer.highestOneBit(MathHelper.clamp_int(this.quality.guiBlockSupersampling, 1, 16));
|
||||
|
||||
if (this.quality.mipmapGeneration == null) {
|
||||
this.quality.mipmapGeneration = MipmapGeneration.MODERN_VANILLA;
|
||||
}
|
||||
|
||||
if (this.performance.translucencySorting != null) {
|
||||
this.performance.translucencyMode = this.performance.translucencySorting ? TranslucencyMode.FAST : TranslucencyMode.OFF;
|
||||
this.performance.translucencySorting = null;
|
||||
}
|
||||
if (this.performance.translucencyMode == null) {
|
||||
this.performance.translucencyMode = TranslucencyMode.FAST;
|
||||
}
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return GSON.toJson(this);
|
||||
}
|
||||
|
||||
private static boolean isLwjgl2() {
|
||||
return SodiumGameOptions.class.getClassLoader().getResource("org/lwjgl/Sys.class") != null;
|
||||
}
|
||||
@@ -196,7 +218,7 @@ public class SodiumGameOptions {
|
||||
throw new IOException("Not a directory: " + dir);
|
||||
}
|
||||
|
||||
Files.writeString(this.configPath, GSON.toJson(this));
|
||||
Files.writeString(this.configPath, toJson());
|
||||
if (Iris.enabled) {
|
||||
try {
|
||||
if (Iris.getIrisConfig() != null) {
|
||||
@@ -233,7 +255,9 @@ public class SodiumGameOptions {
|
||||
public boolean useFogOcclusion = true;
|
||||
public boolean useBlockFaceCulling = true;
|
||||
public boolean useCompactVertexFormat = true;
|
||||
public boolean translucencySorting = true;
|
||||
public TranslucencyMode translucencyMode = TranslucencyMode.FAST;
|
||||
public Boolean translucencySorting;
|
||||
public boolean blockEntitiesBeforeTranslucentTerrain = true;
|
||||
public boolean useRenderPassOptimization = true;
|
||||
public int cpuRenderAheadLimit = 3;
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package me.jellysquid.mods.sodium.client.gui.options.named;
|
||||
|
||||
public enum TranslucencyMode implements NamedState {
|
||||
OFF("options.off"),
|
||||
FAST("options.graphics.fast"),
|
||||
FANCY("options.graphics.fancy");
|
||||
|
||||
private final String name;
|
||||
|
||||
TranslucencyMode(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean sorts() {
|
||||
return this != OFF;
|
||||
}
|
||||
|
||||
public boolean composites() {
|
||||
return this == FANCY;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||
import com.gtnewhorizons.angelica.glsm.LightingSnapshot;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TranslucentPeel;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
@@ -66,12 +67,14 @@ public class AngelicaBufferSource implements Groupable {
|
||||
}
|
||||
|
||||
public SegmentedBufferBuilder getBuffer(RenderLayer type, int blockEntityId) {
|
||||
TranslucentPeel.queued(type, this, type);
|
||||
renderOrderManager.begin(type);
|
||||
int affinity = affinities.getAndMoveToLast(type);
|
||||
if (affinity == -1) {
|
||||
if (affinities.size() < builders.length) {
|
||||
affinity = affinities.size();
|
||||
} else {
|
||||
TranslucentPeel.queuedUnordered(affinities.firstKey());
|
||||
affinity = affinities.removeFirstInt();
|
||||
}
|
||||
affinities.put(type, affinity);
|
||||
@@ -163,16 +166,7 @@ public class AngelicaBufferSource implements Groupable {
|
||||
public void endBatch(LayerDrawHook hook) {
|
||||
ensurePrepared();
|
||||
try {
|
||||
int lastGlint = -1;
|
||||
for (int i = 0, n = order.size(); i < n; i++) {
|
||||
if (getTransparencyType(order.get(i)) == TransparencyType.DECAL) lastGlint = i;
|
||||
}
|
||||
for (int i = 0, n = order.size(); i < n; i++) {
|
||||
final RenderLayer layer = order.get(i);
|
||||
if (getTransparencyType(layer) != TransparencyType.AFTER_GLINT) drawLayer(layer, hook);
|
||||
if (i == lastGlint) drawAfterGlintLayers(hook);
|
||||
}
|
||||
if (lastGlint < 0) drawAfterGlintLayers(hook);
|
||||
drawOrder(hook);
|
||||
} catch (Throwable t) {
|
||||
discardAfterFailure(t);
|
||||
throw t;
|
||||
@@ -181,6 +175,29 @@ public class AngelicaBufferSource implements Groupable {
|
||||
finish();
|
||||
}
|
||||
|
||||
public void drawPending(LayerDrawHook hook) {
|
||||
ensurePrepared();
|
||||
drawOrder(hook);
|
||||
}
|
||||
|
||||
public void openDrawState() {
|
||||
ensureDrawState();
|
||||
saveIrisState();
|
||||
}
|
||||
|
||||
private void drawOrder(LayerDrawHook hook) {
|
||||
int lastGlint = -1;
|
||||
for (int i = 0, n = order.size(); i < n; i++) {
|
||||
if (getTransparencyType(order.get(i)) == TransparencyType.DECAL) lastGlint = i;
|
||||
}
|
||||
for (int i = 0, n = order.size(); i < n; i++) {
|
||||
final RenderLayer layer = order.get(i);
|
||||
if (getTransparencyType(layer) != TransparencyType.AFTER_GLINT) drawLayer(layer, hook);
|
||||
if (i == lastGlint) drawAfterGlintLayers(hook);
|
||||
}
|
||||
if (lastGlint < 0) drawAfterGlintLayers(hook);
|
||||
}
|
||||
|
||||
private void drawAfterGlintLayers(LayerDrawHook hook) {
|
||||
for (int i = 0, n = order.size(); i < n; i++) {
|
||||
final RenderLayer layer = order.get(i);
|
||||
@@ -198,6 +215,14 @@ public class AngelicaBufferSource implements Groupable {
|
||||
finish();
|
||||
}
|
||||
|
||||
private void ensureDrawState() {
|
||||
if (drawStateDepth >= 0) return;
|
||||
drawStateDepth = GLSMAttribStack.pushState(StateSet.BATCH);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
if (passLighting != null) GLSMLighting.applyLighting(passLighting);
|
||||
drawDefaults.apply();
|
||||
}
|
||||
|
||||
private void restoreDrawState() {
|
||||
if (drawStateDepth >= 0) {
|
||||
drawDefaults.restore();
|
||||
@@ -227,12 +252,7 @@ public class AngelicaBufferSource implements Groupable {
|
||||
final boolean hasDynamic = segments != null && !segments.isEmpty();
|
||||
final boolean hasRetained = hook != null && hook.hasDraws(layer);
|
||||
if (!hasDynamic && !hasRetained) return;
|
||||
if (drawStateDepth < 0) {
|
||||
drawStateDepth = GLSMAttribStack.pushState(StateSet.BATCH);
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
if (passLighting != null) GLSMLighting.applyLighting(passLighting);
|
||||
drawDefaults.apply();
|
||||
}
|
||||
ensureDrawState();
|
||||
saveIrisState();
|
||||
layer.startDrawing();
|
||||
Throwable failure = null;
|
||||
|
||||
@@ -57,8 +57,7 @@ sodium.options.use_particle_culling.name=Partikel-Culling
|
||||
sodium.options.use_particle_culling.tooltip=Zeigt nur Partikel innerhalb des Spielersichtfeldes an. Dies kann die Performance bemerkbar verbessern, wenn viele Partikel in der Nähe sind.
|
||||
sodium.options.animate_only_visible_textures.name=Nur sichtbare Texturen animieren
|
||||
sodium.options.animate_only_visible_textures.tooltip=Animiert nur Texturen, die auch im Sichtfeld des Spielers sind. Dies kann die Framerate mancher Hardware stark verbessern, besonders bei "schwereren" Ressourcenpaketen. Wenn du Probleme damit hast, dass Texturen nicht animiert werden, versuch, ob das hier auszuschalten hilft.
|
||||
sodium.options.translucency_sorting.name=Durchsichtigkeits-Sortierung
|
||||
sodium.options.translucency_sorting.tooltip=Aktiviert die korrekte Sortierung durchsichtiger Blöcke von hinten nach vorne für bessered Rendern,insbesondere bei überlappenden durchsichtigen Blöcken. Dies kann die Rechenlast erhöhen.
|
||||
sodium.options.translucency_mode.name=Durchsichtigkeits-Sortierung
|
||||
sodium.options.cpu_render_ahead_limit.name=CPU-Vorausrendern begrenzen
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=Die maximale Anzahl an Frames, die der CPU auf das Rendern des GPU warten darf. Sehr niedrige oder hohe Werte können die Bildrate destabilisieren.
|
||||
sodium.options.cpu_render_ahead_limit.value=%s Frame(s)
|
||||
|
||||
@@ -89,8 +89,10 @@ sodium.options.use_particle_culling.name=Particle Culling
|
||||
sodium.options.use_particle_culling.tooltip=If enabled, only particles which are determined to be visible will be rendered. This can provide a significant improvement to frame rates when many particles are nearby.
|
||||
sodium.options.animate_only_visible_textures.name=Animate Only Visible Textures
|
||||
sodium.options.animate_only_visible_textures.tooltip=If enabled, only animated textures determined to be visible will be updated. This can provide a significant boost to frame rates on some hardware, especially with heavier resource packs. If you experience issues with some textures not being animated, try disabling this option.
|
||||
sodium.options.translucency_sorting.name=Translucency Sorting
|
||||
sodium.options.translucency_sorting.tooltip=If enabled, translucent terrain (water, stained glass, ice) and translucent tile entities are sorted back to front so overlapping layers draw correctly. Disabling improves performance in scenes with lots of translucent blocks but causes layering artifacts. Changing this reloads chunks.
|
||||
sodium.options.translucency_mode.name=Translucency Sorting
|
||||
sodium.options.translucency_mode.tooltip=Fast sorts translucent terrain (water, stained glass, ice) and translucent tile entities back to front so overlapping layers draw correctly. Fancy also draws translucent terrain, translucent tile entities, particles, weather and clouds into separate layers that blend correctly over one another; it uses more video memory and GPU time and has no effect with a shader pack. Off is fastest but causes layering artifacts. Changing this reloads chunks.
|
||||
sodium.options.block_entities_before_translucent_terrain.name=Tile Entities Before Translucent Terrain
|
||||
sodium.options.block_entities_before_translucent_terrain.tooltip=If enabled, translucent tile entities are drawn before translucent terrain (water, stained glass, ice), so that the latter does not hide the former behind it. Tile Entities in front of terrain may hide the terrain, and glows in front of terrain may appear behind it. Always on with Fancy translucency sorting.
|
||||
sodium.options.cpu_render_ahead_limit.name=CPU Render-Ahead Limit
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=How many frames the CPU may run ahead of the GPU. Lower values cut input latency, higher values smooth out uneven frame times. Very low or high values may create frame rate instability. 0 disables it; the SDL-GPU backend ignores it and applies its own backpressure.
|
||||
sodium.options.cpu_render_ahead_limit.value=%s frame(s)
|
||||
|
||||
@@ -82,8 +82,7 @@ sodium.options.use_particle_culling.name=パーティクルカリング
|
||||
sodium.options.use_particle_culling.tooltip=有効にすると、見えると判断されたパーティクルのみが描画されるようになります。近くに多数のパーティクルがある場合にFPSが大幅に向上する可能性があります。
|
||||
sodium.options.animate_only_visible_textures.name=見えるアニメーションのみ描画
|
||||
sodium.options.animate_only_visible_textures.tooltip=有効にすると、表示可能と判断されたアニメーションのみが描画されます。特にリソースパックの負荷が高い場合、一部のハードウェアでFPSが大幅に向上する可能性があります。一部のアニメーションが動かないなどの問題が発生した場合は、この設定を無効にしてください。
|
||||
sodium.options.translucency_sorting.name=半透明ブロックの整理
|
||||
sodium.options.translucency_sorting.tooltip=有効にすると、半透明ブロックは奥から手前へと正しく並べ替えられ、特に複数の半透明ブロックが重なっている場合の品質が向上しますが、代わりに負荷が上昇します。
|
||||
sodium.options.translucency_mode.name=半透明ブロックの整理
|
||||
sodium.options.cpu_render_ahead_limit.name=CPUの先読み処理制限
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=CPUがGPUの描画完了まで待機できる最大フレーム数を設定します。極端に低い値、または高い値を設定すると、FPSが不安定になる可能性があります。
|
||||
sodium.options.cpu_render_ahead_limit.value=%s フレーム
|
||||
|
||||
@@ -89,8 +89,7 @@ sodium.options.use_particle_culling.name=Culling cząstek
|
||||
sodium.options.use_particle_culling.tooltip=Jeśli włączone, tylko cząstki, które uważane są za widoczne, będą renderowane. To może skutkować znaczną poprawą klatek na sekundę, gdy wiele cząstek znajduje się w polu widzenia.
|
||||
sodium.options.animate_only_visible_textures.name=Animuj tylko widoczne tekstury
|
||||
sodium.options.animate_only_visible_textures.tooltip=Jeśli włączone, tylko animowane tekstury, które uważane są za widoczne, będą aktualizowane. To może pomóc z wydajnością na niektórych systemach, szczególnie z cięższymi paczkami zasobów. Jeśli zauważyłeś, że niektóre tekstury nie są animowane, spróbuj wyłączyć tę opcję.
|
||||
sodium.options.translucency_sorting.name=Sortowanie przezroczystości
|
||||
sodium.options.translucency_sorting.tooltip=Jeśli włączone, przezroczysty teren (woda, kolorowe szkło, lód) i przezroczyste byty-bloki będą sortowane od tyłu do przodu, aby nakładające się warstwy były poprawnie wyświetlone. Wyłączenie tej opcji zwiększa wdyajność w sytuacjach z dużą ilością przezroczystych bloków, ale powoduje artefakty. Zmienienie tej opcji przeładowuje chunki.
|
||||
sodium.options.translucency_mode.name=Sortowanie przezroczystości
|
||||
sodium.options.cpu_render_ahead_limit.name=Limit renderowania klatek z wyprzedzeniem przez CPU
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=Określa maksymalną liczbę klatek na które CPU może czekać, aby zostały wyrenderowane przez GPU. Bardzo niskie lub wysokie wartości mogą powodować niestabilność klatek. 0 wyłącza tą opcję; Backend SDL-GPU ignoruje ją i stosuje własne rozwiązanie.
|
||||
sodium.options.cpu_render_ahead_limit.value=%s klat(ki/ek)
|
||||
|
||||
@@ -55,8 +55,7 @@ sodium.options.use_particle_culling.name=Отсечение Частиц
|
||||
sodium.options.use_particle_culling.tooltip=Если включено, будут отрисовываться только те частицы, которые определены как видимые. Это может значительно улучшить частоту кадров, когда рядом находится много частиц.
|
||||
sodium.options.animate_only_visible_textures.name=Анимировать Только Видимые Текстуры
|
||||
sodium.options.animate_only_visible_textures.tooltip=Если включено, будут обновляться только анимированные текстуры, определенные как видимые. Это может значительно увеличить частоту кадров на некотором оборудовании, особенно с более тяжелыми ресурс-паками. Если у вас возникают проблемы с некоторыми текстурами, которые не анимируются, попробуйте отключить эту опцию.
|
||||
sodium.options.translucency_sorting.name=Сортировка Прозрачности
|
||||
sodium.options.translucency_sorting.tooltip=Если включено, прозрачные блоки будут правильно отсортированы сзади вперед для обеспечения лучшей отрисовки, особенно когда в поле зрения находится несколько прозрачных блоков и они слоены. Это немного дорогостоящая операция.
|
||||
sodium.options.translucency_mode.name=Сортировка Прозрачности
|
||||
sodium.options.cpu_render_ahead_limit.name=Предел Предварительной Отрисовки CPU
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=Определяет максимальное количество кадров, которые CPU может ожидать завершения отрисовки на GPU. Очень низкие или высокие значения могут создать нестабильность частоты кадров.
|
||||
sodium.options.cpu_render_ahead_limit.value=%s кадр(ов)
|
||||
|
||||
@@ -73,8 +73,7 @@ sodium.options.use_particle_culling.name=Відсікання частинок
|
||||
sodium.options.use_particle_culling.tooltip=Якщо увімкнено, рендеритися будуть лише ті частинки, які є безпосередньо видимими. Це дозволяє суттєво підвищити частоту кадрів, коли поруч є велика кількість частинок.
|
||||
sodium.options.animate_only_visible_textures.name=Анімувати лише видимі текстури
|
||||
sodium.options.animate_only_visible_textures.tooltip=Якщо увімкнено, оновлюватимуться лише ті анімовані текстури, які є видимими для гравця. Це може суттєво підвищити частоту кадрів на деякому залізі, особливо з важкими ресурспаками. Якщо ви помітили, що деякі текстури перестали анімуватися, спробуйте вимкнути цю опцію.
|
||||
sodium.options.translucency_sorting.name=Сортування напівпрозорості
|
||||
sodium.options.translucency_sorting.tooltip=Якщо увімкнено, напівпрозорі блоки сортуватимуться у правильному порядку (від дальніх до ближніх) для коректнішого рендерингу. Це особливо важливо, коли кілька таких блоків накладаються один на одного шарами. Ця операція є досить ресурсомісткою.
|
||||
sodium.options.translucency_mode.name=Сортування напівпрозорості
|
||||
sodium.options.cpu_render_ahead_limit.name=Ліміт заздалегідь підготовлених кадрів CPU
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=Визначає, скільки кадрів процесор може підготувати наперед для відеокарти. Нижчі значення зменшують затримку введення, а вищі — згладжують нерівномірний час кадру. Занадто низькі або високі значення можуть викликати нестабільність частоти кадрів. Значення 0 вимикає цей параметр; бекенд SDL-GPU ігнорує його та застосовує власний механізм зворотного тиску.
|
||||
sodium.options.cpu_render_ahead_limit.value=%s кадр(ів)
|
||||
|
||||
@@ -59,8 +59,7 @@ sodium.options.use_particle_culling.name=使用粒子剔除
|
||||
sodium.options.use_particle_culling.tooltip=如果启用,则只会渲染可见的粒子。当附近有许多粒子时,可以显著提高帧率。
|
||||
sodium.options.animate_only_visible_textures.name=仅绘制可见动画纹理
|
||||
sodium.options.animate_only_visible_textures.tooltip=如果启用,则只会更新确定为可见的纹理动画。这可以显著提高某些硬件的帧率(FPS),尤其是对于大型的资源包。如果你遇到某些纹理失去动画的问题,请尝试禁用此选项。
|
||||
sodium.options.translucency_sorting.name=半透明排序
|
||||
sodium.options.translucency_sorting.tooltip=如果启用,半透明方块将从后向前正确排序,当多个分层的半透明方块在视野中时,可以提供更好的渲染效果。这个选项性能花销较大。
|
||||
sodium.options.translucency_mode.name=半透明排序
|
||||
sodium.options.cpu_render_ahead_limit.name=CPU 预渲染限制
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=指定CPU可以在GPU上等待完成渲染的最大帧数。非常低或非常高的值都可能会导致帧率(FPS)不稳定。
|
||||
sodium.options.cpu_render_ahead_limit.value=%s 帧
|
||||
|
||||
@@ -54,6 +54,13 @@ out vec4 fragColor; // The output fragment for the color framebuffer
|
||||
#define fragColor gl_FragColor
|
||||
#endif
|
||||
|
||||
#if !defined(LEGACY) && defined(TRANSLUCENCY_SPLIT)
|
||||
// Fragments behind the block-entity layer's depth go to a second sheet (TranslucencyLayers).
|
||||
uniform bool u_SplitEnabled;
|
||||
uniform sampler2D u_SplitDepth;
|
||||
layout(location = 1) out vec4 fragBehind;
|
||||
#endif
|
||||
|
||||
vec2 snapToTexelCentre(vec2 uv, vec2 texelSize, vec2 texelsPerPixel) {
|
||||
vec2 centreCoords = uv / texelSize - 0.5;
|
||||
vec2 lowerCentre = floor(centreCoords);
|
||||
@@ -237,4 +244,12 @@ void main() {
|
||||
#else // No fog
|
||||
fragColor = diffuseColor;
|
||||
#endif
|
||||
|
||||
#if !defined(LEGACY) && defined(TRANSLUCENCY_SPLIT)
|
||||
fragBehind = vec4(0.0);
|
||||
if (u_SplitEnabled && gl_FragCoord.z > texelFetch(u_SplitDepth, ivec2(gl_FragCoord.xy), 0).r) {
|
||||
fragBehind = fragColor;
|
||||
fragColor = vec4(0.0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#version 330 core
|
||||
|
||||
// A sample is skipped only when all zero, so additive light with no coverage still counts. The TERRAIN_BEHIND sheet
|
||||
// sorts at the block-entity depth where the front sheet has terrain, inserted first so a tie keeps it behind.
|
||||
|
||||
in vec2 v_TexCoord;
|
||||
|
||||
uniform sampler2D u_Depth;
|
||||
uniform sampler2D u_Layer0;
|
||||
uniform sampler2D u_Layer1;
|
||||
uniform sampler2D u_Layer2;
|
||||
uniform sampler2D u_Layer3;
|
||||
uniform sampler2D u_Layer4;
|
||||
uniform sampler2D u_LayerDepth0;
|
||||
uniform sampler2D u_LayerDepth1;
|
||||
uniform sampler2D u_LayerDepth2;
|
||||
uniform sampler2D u_LayerDepth3;
|
||||
uniform sampler2D u_LayerDepth4;
|
||||
uniform int u_LayerMask;
|
||||
#ifdef TERRAIN_BEHIND
|
||||
uniform sampler2D u_TerrainBehind;
|
||||
#define SHEETS 6
|
||||
#else
|
||||
#define SHEETS 5
|
||||
#endif
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec4 colors[SHEETS];
|
||||
float depths[SHEETS];
|
||||
int count = 0;
|
||||
float mainDepth;
|
||||
|
||||
void insertSheet(vec4 color, float depth) {
|
||||
if (depth > mainDepth) return;
|
||||
int j = count++;
|
||||
colors[j] = color;
|
||||
depths[j] = depth;
|
||||
while (j > 0 && depths[j] > depths[j - 1]) {
|
||||
vec4 c = colors[j];
|
||||
colors[j] = colors[j - 1];
|
||||
colors[j - 1] = c;
|
||||
float d = depths[j];
|
||||
depths[j] = depths[j - 1];
|
||||
depths[j - 1] = d;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
void insert(int bit, sampler2D colorTexture, sampler2D depthTexture) {
|
||||
if ((u_LayerMask & bit) == 0) return;
|
||||
vec4 color = texture(colorTexture, v_TexCoord);
|
||||
if (color == vec4(0.0)) return;
|
||||
insertSheet(color, texture(depthTexture, v_TexCoord).r);
|
||||
}
|
||||
|
||||
#ifdef TERRAIN_BEHIND
|
||||
void insertTerrain() {
|
||||
vec4 behind = texture(u_TerrainBehind, v_TexCoord);
|
||||
vec4 front = texture(u_Layer0, v_TexCoord);
|
||||
if (behind != vec4(0.0)) {
|
||||
insertSheet(behind, front == vec4(0.0) ? texture(u_LayerDepth0, v_TexCoord).r : texture(u_LayerDepth1, v_TexCoord).r);
|
||||
}
|
||||
if (front != vec4(0.0)) insertSheet(front, texture(u_LayerDepth0, v_TexCoord).r);
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
mainDepth = texture(u_Depth, v_TexCoord).r;
|
||||
#ifdef TERRAIN_BEHIND
|
||||
insertTerrain();
|
||||
#else
|
||||
insert(1, u_Layer0, u_LayerDepth0);
|
||||
#endif
|
||||
insert(2, u_Layer1, u_LayerDepth1);
|
||||
insert(4, u_Layer2, u_LayerDepth2);
|
||||
insert(8, u_Layer3, u_LayerDepth3);
|
||||
insert(16, u_Layer4, u_LayerDepth4);
|
||||
vec4 result = vec4(0.0);
|
||||
for (int i = 0; i < count; i++) {
|
||||
result = result * (1.0 - colors[i].a) + colors[i];
|
||||
}
|
||||
fragColor = result;
|
||||
gl_FragDepth = count > 0 ? depths[count - 1] : mainDepth;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#version 330 core
|
||||
|
||||
in vec2 v_TexCoord;
|
||||
|
||||
uniform sampler2D u_GlowDepth;
|
||||
|
||||
void main() {
|
||||
gl_FragDepth = texture(u_GlowDepth, v_TexCoord).r;
|
||||
}
|
||||
@@ -50,8 +50,7 @@
|
||||
"sodium.options.use_particle_culling.tooltip": "启用后,将仅渲染可见的颗粒。当周围有许多颗粒时,这可以显著地提高帧率。",
|
||||
"sodium.options.animate_only_visible_textures.name": "仅渲染可见的动态材质",
|
||||
"sodium.options.animate_only_visible_textures.tooltip": "启用后,将只会更新可见的动画材质。 这可以显着提高某些低配电脑的帧率。 尤其是对于较大的资源包。因此如果你看到动态材质不生效,请尝试禁用此选项。",
|
||||
"sodium.options.translucency_sorting.name": "透明度排序",
|
||||
"sodium.options.translucency_sorting.tooltip": "启用后,透明方块将被正确排序,从后向前渲染,以提供更好的渲染效果,特别是当你的视野中有多个重叠的透明方块时。此操作在性能上相对较昂贵。",
|
||||
"sodium.options.translucency_mode.name": "透明度排序",
|
||||
"sodium.options.cpu_render_ahead_limit.name": "CPU提前渲染限制",
|
||||
"sodium.options.cpu_render_ahead_limit.tooltip": "指定CPU可以在GPU上等待完成渲染的最大帧数。很低或很高的值可能会导致帧速率不稳定。",
|
||||
"sodium.options.cpu_render_ahead_limit.value": "%s帧",
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDepthStencilAlpha;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucencyLayers;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucentLayer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(EntityRenderer.class)
|
||||
public class MixinEntityRenderer_TranslucencyLayers {
|
||||
|
||||
@Inject(method = "renderWorld(FJ)V", at = @At("HEAD"))
|
||||
private void angelica$beginTranslucencyLayers(float partialTicks, long finishTimeNano, CallbackInfo ci) {
|
||||
TranslucencyLayers.INSTANCE.beginFrame();
|
||||
}
|
||||
|
||||
@Inject(method = "renderWorld(FJ)V", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/ForgeHooksClient;dispatchRenderLast(Lnet/minecraft/client/renderer/RenderGlobal;F)V", remap = false))
|
||||
private void angelica$compositeTranslucencyLayers(float partialTicks, long finishTimeNano, CallbackInfo ci) {
|
||||
TranslucencyLayers.INSTANCE.composite();
|
||||
}
|
||||
|
||||
@Surround(id = "translucencyParticles", method = "renderWorld(FJ)V", expect = 2, at = {
|
||||
@At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EffectRenderer;renderLitParticles(Lnet/minecraft/entity/Entity;F)V"),
|
||||
@At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EffectRenderer;renderParticles(Lnet/minecraft/entity/Entity;F)V") })
|
||||
private void angelica$particlesLayer() {
|
||||
@Surround.Carry boolean layered = TranslucencyLayers.INSTANCE.beginIfActive(TranslucentLayer.PARTICLES);
|
||||
}
|
||||
|
||||
@Surround.Finally("translucencyParticles")
|
||||
private void angelica$particlesLayerEnd(@Surround.Carry boolean layered) {
|
||||
if (layered) TranslucencyLayers.INSTANCE.end();
|
||||
}
|
||||
|
||||
@Surround(id = "translucencyWeather", method = "renderWorld(FJ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/EntityRenderer;renderRainSnow(F)V"))
|
||||
private void angelica$weatherLayer(@Surround.Local(argsOnly = true) float partialTicks) {
|
||||
final WorldClient world = Minecraft.getMinecraft().theWorld;
|
||||
@Surround.Carry boolean layered = (world.getRainStrength(partialTicks) > 0.0F || world.provider.getWeatherRenderer() != null)
|
||||
&& TranslucencyLayers.INSTANCE.beginIfActive(TranslucentLayer.WEATHER);
|
||||
// Rain writes no depth, which would leave the layer sorting at the opaque depth behind it.
|
||||
if (layered) GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
}
|
||||
|
||||
@Surround.Finally("translucencyWeather")
|
||||
private void angelica$weatherLayerEnd(@Surround.Carry boolean layered) {
|
||||
if (layered) TranslucencyLayers.INSTANCE.end();
|
||||
}
|
||||
|
||||
@Surround(id = "translucencyClouds", method = "renderWorld(FJ)V", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/client/renderer/EntityRenderer;renderCloudsCheck(Lnet/minecraft/client/renderer/RenderGlobal;F)V"))
|
||||
private void angelica$cloudsLayer() {
|
||||
@Surround.Carry boolean layered = Minecraft.getMinecraft().gameSettings.shouldRenderClouds() && TranslucencyLayers.INSTANCE.beginIfActive(TranslucentLayer.CLOUDS);
|
||||
}
|
||||
|
||||
@Surround.Finally("translucencyClouds")
|
||||
private void angelica$cloudsLayerEnd(@Surround.Carry boolean layered) {
|
||||
if (layered) TranslucencyLayers.INSTANCE.end();
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucencyLayers;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Framebuffer.class)
|
||||
public class MixinFramebuffer_TranslucencyLayers {
|
||||
|
||||
@Inject(method = "bindFramebuffer", at = @At("HEAD"), cancellable = true)
|
||||
private void angelica$bindOpenTranslucencyLayer(boolean viewport, CallbackInfo ci) {
|
||||
final Framebuffer framebuffer = (Framebuffer) (Object) this;
|
||||
if (!TranslucencyLayers.INSTANCE.redirectsMainBind() || framebuffer != Minecraft.getMinecraft().getFramebuffer()) return;
|
||||
TranslucencyLayers.INSTANCE.bindOpenLayer();
|
||||
if (viewport) GLSMRasterizer.glViewport(0, 0, framebuffer.framebufferWidth, framebuffer.framebufferHeight);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
+2
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.startup;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.core.GTNHLibCore;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.Tags;
|
||||
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
@@ -47,6 +48,7 @@ public class MixinInitGLStateManager {
|
||||
.noErrorChecks(AngelicaConfig.disableErrorChecks && GTNHLibCore.isObf() && !AngelicaMod.lwjglDebug)
|
||||
.enableDSA(AngelicaConfig.enableDSA)
|
||||
.postInitCallback(SelectionBoxRenderer::init)
|
||||
.buildLabel("Angelica " + Tags.VERSION)
|
||||
.build());
|
||||
BackendStartGuard.disarm();
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import net.minecraft.client.particle.EffectRenderer;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(EffectRenderer.class)
|
||||
public class MixinEffectRenderer_GlowDepthReplay {
|
||||
|
||||
@Inject(method = "addEffect", at = @At("HEAD"), cancellable = true)
|
||||
private void angelica$dropDuringGlowReplay(EntityFX effect, CallbackInfo ci) {
|
||||
if (DepthEcho.replaying()) ci.cancel();
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import net.minecraft.client.audio.ISound;
|
||||
import net.minecraft.client.audio.SoundHandler;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(SoundHandler.class)
|
||||
public class MixinSoundHandler_GlowDepthReplay {
|
||||
|
||||
@Inject(method = "playSound", at = @At("HEAD"), cancellable = true)
|
||||
private void angelica$dropDuringGlowReplay(ISound sound, CallbackInfo ci) {
|
||||
if (DepthEcho.replaying()) ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "playDelayedSound", at = @At("HEAD"), cancellable = true)
|
||||
private void angelica$dropDelayedDuringGlowReplay(ISound sound, int delay, CallbackInfo ci) {
|
||||
if (DepthEcho.replaying()) ci.cancel();
|
||||
}
|
||||
}
|
||||
+28
-17
@@ -9,6 +9,7 @@ import com.gtnewhorizons.angelica.glsm.GLSMEnable;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IRenderGlobalExt;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.AngelicaRenderQueue;
|
||||
import com.gtnewhorizons.angelica.rendering.RenderingState;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.BlockRenderLayer;
|
||||
@@ -16,9 +17,10 @@ import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucencyLayers;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucentLayer;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.HandRenderer;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPhase;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
@@ -34,6 +36,7 @@ import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import org.embeddedt.embeddium.impl.gl.device.RenderDevice;
|
||||
import org.embeddedt.embeddium.impl.render.terrain.SimpleWorldRenderer;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.Viewport;
|
||||
@@ -63,7 +66,6 @@ import static org.joml.Math.lerp;
|
||||
*/
|
||||
@Mixin(value = RenderGlobal.class, priority = 900)
|
||||
public class MixinRenderGlobal implements IRenderGlobalExt {
|
||||
@Unique private static final long P_BLOCK_ENTITIES_RENDERED = Tracy.plotHandle("blockEntitiesRendered");
|
||||
@Unique private static final Tracy.ZoneId Z_MT_QUEUE = Tracy.zoneId("mtQueue", Tracy.COLOR_TERRAIN);
|
||||
|
||||
@Shadow public Minecraft mc;
|
||||
@@ -146,10 +148,13 @@ public class MixinRenderGlobal implements IRenderGlobalExt {
|
||||
pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pass == 0) {
|
||||
pipeline.setPhase(WorldRenderingPhase.TERRAIN_SOLID);
|
||||
} else if (pass == 1) {
|
||||
if (!ShadowRenderingState.areShadowsCurrentlyBeingRendered()) {
|
||||
iris$beginTranslucents(pipeline, Camera.INSTANCE);
|
||||
}
|
||||
} else if (pass == 1 && !ShadowRenderingState.areShadowsCurrentlyBeingRendered()) {
|
||||
iris$beginTranslucents(pipeline, Camera.INSTANCE);
|
||||
}
|
||||
}
|
||||
if (pass == 1) {
|
||||
celeritas$renderBlockEntitiesBeforeTranslucent(partialTicks);
|
||||
if (pipeline != null) {
|
||||
pipeline.setPhase(WorldRenderingPhase.TERRAIN_TRANSLUCENT);
|
||||
this.renderEngine.bindTexture(TextureMap.locationBlocksTexture);
|
||||
}
|
||||
@@ -187,8 +192,13 @@ public class MixinRenderGlobal implements IRenderGlobalExt {
|
||||
this.celeritas$renderer.drawChunkLayer(BlockRenderLayer.CUTOUT_MIPPED, camX, camY, camZ);
|
||||
} else {
|
||||
mc.mcProfiler.endStartSection(shadow ? SECTION_SHADOW_TRANSLUCENT : SECTION_TRANSLUCENT);
|
||||
this.celeritas$renderer.drawChunkLayer(BlockRenderLayer.TRANSLUCENT, camX, camY, camZ);
|
||||
RenderChunkEvent.post();
|
||||
final boolean layered = (this.celeritas$renderer.hasVisibleLayer(BlockRenderLayer.TRANSLUCENT) || RenderChunkEvent.BUS.hasListeners()) && TranslucencyLayers.INSTANCE.beginIfActive(TranslucentLayer.TERRAIN);
|
||||
try {
|
||||
this.celeritas$renderer.drawChunkLayer(BlockRenderLayer.TRANSLUCENT, camX, camY, camZ);
|
||||
RenderChunkEvent.post();
|
||||
} finally {
|
||||
if (layered) TranslucencyLayers.INSTANCE.end();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
RenderDevice.exitManagedCode();
|
||||
@@ -276,17 +286,18 @@ public class MixinRenderGlobal implements IRenderGlobalExt {
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderHelper;enableStandardItemLighting()V", ordinal = 0))
|
||||
private void celeritas$enterBlockEntities() {}
|
||||
|
||||
@Unique
|
||||
private void celeritas$renderBlockEntitiesBeforeTranslucent(double partialTicks) {
|
||||
if (ShadowRenderingState.areShadowsCurrentlyBeingRendered()) return;
|
||||
if (!ClientProxy.options().performance.blockEntitiesBeforeTranslucentTerrain && !TranslucencyLayers.INSTANCE.active()) return;
|
||||
if (this.mc.entityRenderer.debugViewDirection != 0) return;
|
||||
this.celeritas$renderer.renderBlockEntitiesBeforeTranslucent((float) partialTicks);
|
||||
}
|
||||
|
||||
@Surround.Return("blockEntities")
|
||||
private void celeritas$renderTileEntities(@Surround.Local(argsOnly = true) float partialTicks) {
|
||||
if (Iris.enabled) {
|
||||
GbufferPrograms.beginBlockEntities();
|
||||
GbufferPrograms.setBlockEntityDefaults();
|
||||
}
|
||||
final int blockEntitiesRendered = this.celeritas$renderer.renderBlockEntities(partialTicks);
|
||||
if (Tracy.ENABLED) Tracy.plotInt(P_BLOCK_ENTITIES_RENDERED, blockEntitiesRendered);
|
||||
if (Iris.enabled) {
|
||||
GbufferPrograms.endBlockEntities();
|
||||
}
|
||||
if (MinecraftForgeClient.getRenderPass() == 1 && !this.celeritas$renderer.takeLatePassOne()) return;
|
||||
this.celeritas$renderer.renderBlockEntitiesInPhase(partialTicks);
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "renderEntities", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/RenderGlobal;tileEntities:Ljava/util/List;", opcode = Opcodes.GETFIELD))
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.gtnewhorizons.angelica.mixins.late.spark;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.spark.SparkKnownSources;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Pseudo;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Pseudo
|
||||
@Mixin(targets = "me.lucko.spark.forge.plugin.Forge1710SparkPlugin", remap = false)
|
||||
public abstract class MixinForge1710SparkPlugin {
|
||||
@Unique
|
||||
public Collection<Object> getKnownSources() {
|
||||
return SparkKnownSources.gather(getClass().getClassLoader());
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class TileEntityDepthOrderTest {
|
||||
|
||||
private static TileEntity at(int x, int y, int z) {
|
||||
final TileEntity te = new TileEntity();
|
||||
te.xCoord = x;
|
||||
te.yCoord = y;
|
||||
te.zCoord = z;
|
||||
return te;
|
||||
}
|
||||
|
||||
@Test
|
||||
void sortsFarthestFirstAndKeepsTiesInListOrder() {
|
||||
final TileEntity near = at(0, 0, 2);
|
||||
final TileEntity far = at(0, 0, 181);
|
||||
final TileEntity mid = at(0, 0, 5);
|
||||
final TileEntity midOpposite = at(0, 0, -5);
|
||||
final ObjectArrayList<TileEntity> list = new ObjectArrayList<>(Arrays.asList(near, far, mid, midOpposite));
|
||||
new TileEntityDepthOrder().sortBackToFront(list, 0.5, 0.5, 0.5);
|
||||
assertEquals(Arrays.asList(far, mid, midOpposite, near), list);
|
||||
}
|
||||
}
|
||||
+131
@@ -34,6 +34,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.rendering.items.ItemGlint;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.Group;
|
||||
@@ -41,6 +42,7 @@ import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.InstanceColu
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.TexRun;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.CountingPipeline;
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
@@ -101,6 +103,7 @@ class RetainedTesrGroupsGLTest {
|
||||
private static Runnable onBufferSetup;
|
||||
|
||||
private final long[] clock = {1_000L};
|
||||
private SodiumGameOptions previousOptions;
|
||||
private AngelicaBufferSource source;
|
||||
private InstanceRing ring;
|
||||
private InstancedTemplateRenderer instanced;
|
||||
@@ -140,6 +143,8 @@ class RetainedTesrGroupsGLTest {
|
||||
GLSMFog.disableFog();
|
||||
GLSMLighting.disableLighting();
|
||||
GLSMCurrentAttribs.glColor4f(1f, 1f, 1f, 1f);
|
||||
previousOptions = Reflect.getStatic(ClientProxy.class, "CONFIG");
|
||||
Reflect.setStatic(ClientProxy.class, "CONFIG", new SodiumGameOptions());
|
||||
|
||||
final ShaderManager ffp = ShaderManager.getInstance();
|
||||
ShaderManager.enable();
|
||||
@@ -155,6 +160,7 @@ class RetainedTesrGroupsGLTest {
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
onBufferSetup = null;
|
||||
Reflect.setStatic(ClientProxy.class, "CONFIG", previousOptions);
|
||||
source.discard();
|
||||
GLSMPrograms.setFfpInstancing(Instancing.NONE);
|
||||
GLSMVertexInput.glBindVertexArray(0);
|
||||
@@ -261,6 +267,131 @@ class RetainedTesrGroupsGLTest {
|
||||
assertEquals(0, groups.streamPromotions);
|
||||
}
|
||||
|
||||
@Test
|
||||
void groupSplitByAMidPassFlushBakesEachPartOnce() {
|
||||
final TemplateBuffer template = capturedQuad(0.1);
|
||||
final Matrix4f left = new Matrix4f().translation(-0.3f, 0f, 0f);
|
||||
final Matrix4f right = new Matrix4f().translation(0.3f, 0f, 0f);
|
||||
for (int frame = 0; frame < 8; frame++) {
|
||||
clock[0] += 1;
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
groups.queue(SCOPE, template, layer, BAKED_ADDITIVE, left, 0, -1, 0, 0L, 5, null);
|
||||
groups.drawLayer(layer);
|
||||
groups.continuePass(new Matrix4f(), 0, 0, 0, instanced, null);
|
||||
groups.queue(SCOPE, template, layer, BAKED_ADDITIVE, right, 0, -1, 0, 0L, 5, null);
|
||||
groups.drawLayer(layer);
|
||||
ring.endFrame();
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
assertEquals(2, groups.rebakes, "each side of the flush keeps its own mesh");
|
||||
assertEquals(0, groups.streamPromotions);
|
||||
}
|
||||
|
||||
@Test
|
||||
void idleResetCountsPassesNotFlushSegments() {
|
||||
final TemplateBuffer template = capturedQuad(0.1);
|
||||
final Matrix4f model = new Matrix4f().translation(0.3f, 0f, 0f);
|
||||
for (int frame = 0; frame < 4; frame++) {
|
||||
clock[0] += 1;
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
for (int i = 0; i <= RetainedTesrGroups.IDLE_RESET_FRAMES; i++) groups.continuePass(new Matrix4f(), 0, 0, 0, instanced, null);
|
||||
groups.queue(SCOPE, template, layer, BAKED_ADDITIVE, model, 0, -1, 0, 0L, 5, null);
|
||||
groups.drawLayer(layer);
|
||||
ring.endFrame();
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
assertEquals(1, groups.rebakes, "flushes within a pass must not age a group toward its idle reset");
|
||||
}
|
||||
|
||||
@Test
|
||||
void peelSkipsOnlyTranslucentsFromOneBatchOfOneBlockEntity() {
|
||||
final TemplateBuffer template = capturedQuad(0.1);
|
||||
final RenderLayer translucent = RetainedTesrTestSupport.layerFor(BAKED_TRANSLUCENT);
|
||||
final Matrix4f mv = new Matrix4f();
|
||||
try {
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0);
|
||||
TranslucentPeel.beginPass();
|
||||
groups.queue(SCOPE, template, translucent, BAKED_TRANSLUCENT, mv, 0, -1, 0, 0L, 5, null);
|
||||
groups.queue(SCOPE, template, translucent, BAKED_TRANSLUCENT, mv, 0, -1, 0, 0L, 5, null);
|
||||
assertFalse(peelMixed(), "one block entity's parts in one group keep their submission order");
|
||||
TranslucentPeel.beforeBlockEntity();
|
||||
groups.queue(SCOPE, template, translucent, BAKED_TRANSLUCENT, mv, 0, -1, 0, 0L, 5, null);
|
||||
assertTrue(peelMixed(), "a second block entity in the group can be in front of the first");
|
||||
|
||||
TranslucentPeel.beginPass();
|
||||
groups.queue(SCOPE, template, translucent, BAKED_TRANSLUCENT, mv, 0, -1, 0, 0L, 5, null);
|
||||
groups.queue(SCOPE, template, translucent, BAKED_TRANSLUCENT, mv, 0, -1, 0, 0L, 6, null);
|
||||
assertTrue(peelMixed(), "two groups draw in group order");
|
||||
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
TranslucentPeel.beginPass();
|
||||
groups.queue(SCOPE, template, translucent, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, 5, null);
|
||||
assertTrue(peelMixed(), "instanced groups draw by template, not in submission order");
|
||||
} finally {
|
||||
TranslucentPeel.beginPass();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean peelMixed() {
|
||||
return Reflect.getStatic(TranslucentPeel.class, "mixed");
|
||||
}
|
||||
|
||||
@Test
|
||||
void promotionFoundWhilePeelRecordingWaitsUntilThePeelEnds() {
|
||||
final TemplateBuffer template = capturedQuad(0.1);
|
||||
for (int frame = 0; frame < 6; frame++) {
|
||||
clock[0] += 1;
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
groups.queue(SCOPE, template, layer, BAKED_ADDITIVE, new Matrix4f().translation(frame * 0.03f, 0f, 0f), 0, -1, 0, 0L, 5, null);
|
||||
final long before = groups.streamPromotions;
|
||||
groups.setPeelReplay(true, false);
|
||||
groups.drawLayer(layer);
|
||||
assertEquals(before, groups.streamPromotions, "promoting deletes the mesh the replay passes still draw");
|
||||
groups.setPeelReplay(false, true);
|
||||
groups.drawLayer(layer);
|
||||
groups.setPeelReplay(false, false);
|
||||
ring.endFrame();
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
assertEquals(1, groups.streamPromotions, "the promotion applies once the peel ends");
|
||||
}
|
||||
|
||||
@Test
|
||||
void unpeeledFlushDrawsBothBatchersOpaqueLayersBeforeTranslucentOnes() {
|
||||
final TesrMaterial opaque = TesrMaterial.builder().build();
|
||||
final RenderLayer opaqueLayer = RetainedTesrTestSupport.layerFor(opaque);
|
||||
final RenderLayer translucentLayer = RetainedTesrTestSupport.layerFor(BAKED_TRANSLUCENT);
|
||||
GLSMBlend.disableBlend();
|
||||
GLSMDepthStencilAlpha.disableAlphaTest();
|
||||
GLSMTextures.disableTexture();
|
||||
GLSMEnable.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
FfpFixture.clear();
|
||||
final AngelicaBufferSource tesrSource = new AngelicaBufferSource();
|
||||
final RetainedTesrGroups tesrGroups = new RetainedTesrGroups(tesrSource, () -> clock[0]);
|
||||
final TesrBatchRenderer renderer = TesrBatchRenderer.INSTANCE;
|
||||
final RetainedTesrGroups[] retained = Reflect.get(renderer, "retained");
|
||||
final RetainedTesrGroups oldGroups = retained[0];
|
||||
retained[0] = tesrGroups;
|
||||
try {
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0);
|
||||
groups.queue(SCOPE, capturedQuad(0.5), translucentLayer, BAKED_TRANSLUCENT, new Matrix4f(), 0, 0x80FF0000, 0, 0L, 1, null);
|
||||
tesrGroups.beginPass(new Matrix4f(), 0, 0, 0);
|
||||
tesrGroups.queue(SCOPE, capturedQuad(0.5), opaqueLayer, opaque, new Matrix4f().translation(0f, 0f, 0.5f), 0, COLOR_ABGR, 0, 0L, 2, null);
|
||||
withBatcherFields(renderer, () -> withBatcherFields(ModelPartBatcher.INSTANCE, () -> {
|
||||
GLSMBlend.enableBlend();
|
||||
ImmediateDrawOrder.flush();
|
||||
final int[] pixel = FfpFixture.readPixel(400, 300);
|
||||
assertTrue(pixel[0] > 50 && pixel[2] > 50, "the near translucent part must blend over the far opaque one, got " + pixel[0] + "," + pixel[1] + "," + pixel[2]);
|
||||
}, "activeSource", source, "activeGroups", groups, "active", true, "mode", ModelPartBatcher.Mode.BLOCK_ENTITIES, "shadow", false),
|
||||
"bufferSource", tesrSource, "activePass", 0);
|
||||
} finally {
|
||||
retained[0] = oldGroups;
|
||||
tesrSource.discard();
|
||||
tesrGroups.clear();
|
||||
GLSMBlend.disableBlend();
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> nonOpaqueMaterials() {
|
||||
return Stream.of(Arguments.of("additive", BAKED_ADDITIVE), Arguments.of("translucent", BAKED_TRANSLUCENT));
|
||||
}
|
||||
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
package com.gtnewhorizons.angelica.rendering.translucency;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDepthStencilAlpha;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMFramebuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import net.coderbot.iris.gl.framebuffer.GlFramebuffer;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class TranslucencyCompositeGLTest {
|
||||
|
||||
private static final int WIDTH = 4;
|
||||
private static final String QUAD_VS = """
|
||||
#version 330 core
|
||||
uniform float u_Depth;
|
||||
void main() {
|
||||
vec2 p = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2);
|
||||
gl_Position = vec4(p * 2.0 - 1.0, u_Depth * 2.0 - 1.0, 1.0);
|
||||
}
|
||||
""";
|
||||
private static final String QUAD_FS = """
|
||||
#version 330 core
|
||||
uniform vec4 u_Color;
|
||||
out vec4 color;
|
||||
void main() { color = u_Color; }
|
||||
""";
|
||||
private static final String SPLIT_FS = """
|
||||
#version 330 core
|
||||
uniform vec4 u_Color;
|
||||
uniform bool u_SplitEnabled;
|
||||
uniform sampler2D u_SplitDepth;
|
||||
layout(location = 0) out vec4 front;
|
||||
layout(location = 1) out vec4 behind;
|
||||
void main() {
|
||||
bool back = u_SplitEnabled && gl_FragCoord.z > texelFetch(u_SplitDepth, ivec2(gl_FragCoord.xy), 0).r;
|
||||
front = back ? vec4(0.0) : u_Color;
|
||||
behind = back ? u_Color : vec4(0.0);
|
||||
}
|
||||
""";
|
||||
|
||||
private int colorTex;
|
||||
private int depthTex;
|
||||
private GlFramebuffer main;
|
||||
private int quadProgram;
|
||||
private int splitProgram;
|
||||
private int vao;
|
||||
private TranslucencyLayers layers;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
colorTex = texture(GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
depthTex = texture(GL30.GL_DEPTH24_STENCIL8, GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8);
|
||||
main = new GlFramebuffer();
|
||||
main.addColorAttachment(0, colorTex);
|
||||
main.addDepthAttachment(depthTex);
|
||||
main.drawBuffers(new int[] { 0 });
|
||||
main.readBuffer(0);
|
||||
main.bind();
|
||||
GLSMRasterizer.glViewport(0, 0, WIDTH, 1);
|
||||
quadProgram = GlTestSupport.program(QUAD_VS, QUAD_FS);
|
||||
splitProgram = GlTestSupport.program(QUAD_VS, SPLIT_FS);
|
||||
vao = GLSMVertexInput.glGenVertexArrays();
|
||||
layers = new TranslucencyLayers(new TranslucencyLayers.MainTarget() {
|
||||
@Override public int framebuffer() { return main.getId(); }
|
||||
@Override public int depthTexture() { return depthTex; }
|
||||
@Override public int width() { return WIDTH; }
|
||||
@Override public int height() { return 1; }
|
||||
@Override public int depthVersion() { return 0; }
|
||||
});
|
||||
GLSMDepthStencilAlpha.enableDepthTest();
|
||||
GLSMDepthStencilAlpha.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
GLSMBlend.glClearColor(1f, 0f, 0f, 1f);
|
||||
GLSMDepthStencilAlpha.glClearDepth(0.9);
|
||||
GLSMFramebuffer.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
layers.destroy();
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
main.destroy();
|
||||
GLSMTextures.glDeleteTextures(colorTex);
|
||||
GLSMTextures.glDeleteTextures(depthTex);
|
||||
GLSMPrograms.glUseProgram(0);
|
||||
GLSMPrograms.glDeleteProgram(quadProgram);
|
||||
GLSMPrograms.glDeleteProgram(splitProgram);
|
||||
GLSMVertexInput.glDeleteVertexArrays(vao);
|
||||
GLSMBlend.disableBlend();
|
||||
GLSMDepthStencilAlpha.disableDepthTest();
|
||||
GLSMDepthStencilAlpha.glDepthFunc(GL11.GL_LESS);
|
||||
GLSMBlend.glClearColor(0f, 0f, 0f, 0f);
|
||||
GLSMDepthStencilAlpha.glClearDepth(1.0);
|
||||
}
|
||||
|
||||
private static int texture(int internalFormat, int format, int type) {
|
||||
final int tex = GLSMTextures.glGenTextures();
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLSMTextures.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, WIDTH, 1, 0, format, type, (ByteBuffer) null);
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
return tex;
|
||||
}
|
||||
|
||||
private void drawInto(TranslucentLayer layer, int column, float depth, float r, float g, float b, float a) {
|
||||
drawInto(layer, column, depth, r, g, b, a, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
private void drawInto(TranslucentLayer layer, int column, float depth, float r, float g, float b, float a, int srcRgb, int dstRgb) {
|
||||
layers.begin(layer);
|
||||
drawBlended(quadProgram, column, depth, r, g, b, a, srcRgb, dstRgb);
|
||||
layers.end();
|
||||
}
|
||||
|
||||
private void drawBlended(int program, int column, float depth, float r, float g, float b, float a, int srcRgb, int dstRgb) {
|
||||
GLSMBlend.enableBlend();
|
||||
GLSMBlend.glBlendFunc(srcRgb, dstRgb);
|
||||
draw(program, column, depth, r, g, b, a);
|
||||
GLSMBlend.disableBlend();
|
||||
}
|
||||
|
||||
private void drawTerrain(int column, float depth, float r, float g, float b, float a) {
|
||||
drawBlended(splitProgram, column, depth, r, g, b, a, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
private void draw(int column, float depth, float r, float g, float b, float a) {
|
||||
draw(quadProgram, column, depth, r, g, b, a);
|
||||
}
|
||||
|
||||
private void draw(int program, int column, float depth, float r, float g, float b, float a) {
|
||||
GLSMRasterizer.glViewport(column, 0, 1, 1);
|
||||
GLSMPrograms.glUseProgram(program);
|
||||
GLSMPrograms.glUniform1f(GLSMPrograms.glGetUniformLocation(program, "u_Depth"), depth);
|
||||
GLSMPrograms.glUniform4f(GLSMPrograms.glGetUniformLocation(program, "u_Color"), r, g, b, a);
|
||||
GLSMVertexInput.glBindVertexArray(vao);
|
||||
GLSMDraws.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
GLSMVertexInput.glBindVertexArray(0);
|
||||
GLSMPrograms.glUseProgram(0);
|
||||
GLSMRasterizer.glViewport(0, 0, WIDTH, 1);
|
||||
}
|
||||
|
||||
private static void assertColumn(ByteBuffer rgba, int column, int r, int g, int b) {
|
||||
final int at = column * 4;
|
||||
assertEquals(r, rgba.get(at) & 0xFF, 2, "column " + column + " red");
|
||||
assertEquals(g, rgba.get(at + 1) & 0xFF, 2, "column " + column + " green");
|
||||
assertEquals(b, rgba.get(at + 2) & 0xFF, 2, "column " + column + " blue");
|
||||
}
|
||||
|
||||
private static ByteBuffer readColor() {
|
||||
final ByteBuffer rgba = BufferUtils.createByteBuffer(WIDTH * 4);
|
||||
GLSMFramebuffer.glReadPixels(0, 0, WIDTH, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, rgba);
|
||||
return rgba;
|
||||
}
|
||||
|
||||
private static FloatBuffer readDepth() {
|
||||
final FloatBuffer depth = BufferUtils.createFloatBuffer(WIDTH);
|
||||
GLSMFramebuffer.glReadPixels(0, 0, WIDTH, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, depth);
|
||||
return depth;
|
||||
}
|
||||
|
||||
@Test
|
||||
void sharedInstanceIsBuiltAfterTheConstantsItSizesFrom() {
|
||||
assertDoesNotThrow(TranslucencyLayers.INSTANCE::destroy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void compositesLayersByDepthAndWritesNearestDepth() {
|
||||
layers.beginFrame(true);
|
||||
drawInto(TranslucentLayer.TERRAIN, 0, 0.3f, 0f, 1f, 0f, 0.5f);
|
||||
drawInto(TranslucentLayer.PARTICLES, 0, 0.5f, 0f, 0f, 1f, 0.5f);
|
||||
drawInto(TranslucentLayer.TERRAIN, 1, 0.95f, 0f, 0f, 1f, 0.5f);
|
||||
drawInto(TranslucentLayer.CLOUDS, 2, 0.5f, 0f, 0.4f, 0f, 0f, GL11.GL_ONE, GL11.GL_ONE);
|
||||
drawInto(TranslucentLayer.TERRAIN, 3, 0.5f, 0f, 1f, 0f, 0.5f);
|
||||
draw(3, 0.2f, 0f, 0f, 1f, 1f);
|
||||
layers.composite();
|
||||
|
||||
final ByteBuffer color = readColor();
|
||||
assertColumn(color, 0, 64, 128, 64);
|
||||
assertColumn(color, 1, 255, 0, 0);
|
||||
assertColumn(color, 2, 255, 102, 0);
|
||||
assertColumn(color, 3, 0, 0, 255);
|
||||
final FloatBuffer depth = readDepth();
|
||||
final float[] expected = { 0.3f, 0.9f, 0.5f, 0.2f };
|
||||
for (int i = 0; i < WIDTH; i++) assertEquals(expected[i], depth.get(i), 1e-4f, "column " + i + " depth");
|
||||
|
||||
layers.composite();
|
||||
final ByteBuffer again = readColor();
|
||||
for (int i = 0; i < WIDTH * 4; i++) assertEquals(color.get(i), again.get(i), "a composite with no new layer draws changes nothing");
|
||||
}
|
||||
|
||||
@Test
|
||||
void terrainBehindTheBlockEntityLayerCompositesBehindIt() {
|
||||
layers.beginFrame(true);
|
||||
drawInto(TranslucentLayer.BLOCK_ENTITIES, 0, 0.5f, 0f, 0f, 1f, 0.5f);
|
||||
drawInto(TranslucentLayer.BLOCK_ENTITIES, 1, 0.5f, 0f, 0f, 1f, 0.5f);
|
||||
drawInto(TranslucentLayer.PARTICLES, 1, 0.55f, 0f, 1f, 0f, 0.5f);
|
||||
layers.begin(TranslucentLayer.TERRAIN);
|
||||
GLSMPrograms.glUseProgram(splitProgram);
|
||||
layers.beginTerrainSplit(splitProgram);
|
||||
drawTerrain(0, 0.6f, 0f, 1f, 0f, 0.5f);
|
||||
drawTerrain(0, 0.3f, 1f, 1f, 1f, 0.5f);
|
||||
drawTerrain(1, 0.6f, 1f, 1f, 1f, 0.5f);
|
||||
layers.endTerrainSplit();
|
||||
layers.end();
|
||||
layers.composite();
|
||||
|
||||
final ByteBuffer color = readColor();
|
||||
assertColumn(color, 0, 159, 159, 191);
|
||||
assertColumn(color, 1, 64, 96, 159);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = { false, true })
|
||||
void glowsCompositeAtTheirOwnDepthWithoutRejectingEachOther(boolean replayed) {
|
||||
layers.beginFrame(true);
|
||||
layers.begin(TranslucentLayer.BLOCK_ENTITIES);
|
||||
if (replayed) DepthEcho.beginTracking();
|
||||
drawGlows();
|
||||
BatchStateGuard.suspend();
|
||||
drawGlow(2, 0.3f, 0f, 0f, 1f);
|
||||
BatchStateGuard.resume();
|
||||
assertEquals(replayed, DepthEcho.endTracking());
|
||||
if (replayed) {
|
||||
assertTrue(layers.beginGlowReplay());
|
||||
drawGlows();
|
||||
layers.endGlowReplay();
|
||||
}
|
||||
layers.end();
|
||||
assertEquals(GLSMDepthStencilAlpha.getDepthState().isEnabled(), GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK));
|
||||
drawInto(TranslucentLayer.TERRAIN, 0, 0.6f, 0f, 1f, 0f, 0.5f);
|
||||
drawInto(TranslucentLayer.TERRAIN, 2, 0.6f, 0f, 1f, 0f, 0.5f);
|
||||
layers.composite();
|
||||
|
||||
final ByteBuffer color = readColor();
|
||||
assertColumn(color, 0, 64, 64, 128);
|
||||
assertColumn(color, 1, 64, 128, 64);
|
||||
assertColumn(color, 2, 64, 64, 128);
|
||||
}
|
||||
|
||||
private void drawGlows() {
|
||||
drawGlow(0, 0.3f, 0f, 0f, 1f);
|
||||
drawGlow(1, 0.3f, 0f, 0f, 1f);
|
||||
drawGlow(1, 0.5f, 0f, 1f, 0f);
|
||||
}
|
||||
|
||||
private void drawGlow(int column, float depth, float r, float g, float b) {
|
||||
GLSMDepthStencilAlpha.glDepthMask(false);
|
||||
drawBlended(quadProgram, column, depth, r, g, b, 0.5f, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package me.jellysquid.mods.sodium.client.gui;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TranslucencyMode;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
class SodiumGameOptionsMigrationTest {
|
||||
|
||||
@ParameterizedTest(name = "{0} -> {2}")
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
"legacy on | \"translucency_sorting\": true | FAST",
|
||||
"legacy off | \"translucency_sorting\": false | OFF",
|
||||
"mode stored | \"translucency_mode\": \"FANCY\" | FANCY",
|
||||
"unknown mode | \"translucency_mode\": \"BOGUS\" | FAST",
|
||||
})
|
||||
void migratesTranslucencySetting(String name, String stored, TranslucencyMode expected) {
|
||||
final SodiumGameOptions options = SodiumGameOptions.read(new StringReader("{ \"performance\": { " + stored + " } }"));
|
||||
|
||||
assertEquals(expected, options.performance.translucencyMode);
|
||||
assertFalse(options.toJson().contains("translucency_sorting"), "the legacy key is dropped on save");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user