mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b095c2f648 | ||
|
|
f959648090 |
No files matched your search
@@ -17,7 +17,7 @@
|
||||
* HUD caching
|
||||
* Custom fonts and text effects
|
||||
* MCPatcher - connected textures, custom item textures, custom colors, random mobs, better skies
|
||||
* Bundled NotFine, zoom, FPS reducer
|
||||
* Bundled NotFine, zoom, FPS reducer, menu blur
|
||||
|
||||
## Status
|
||||
* OpenGL + Celeritas terrain - Default
|
||||
@@ -129,5 +129,7 @@ Targets GL 3.3+ core profile
|
||||
* FFP vertex shader generation inspired by ffvertex_prog.c (Copyright 2007 VMware, Inc.)
|
||||
* FFP fragment shader generation inspired by ff_fragment_shader.c (Copyright 2007, 2009 VMware, Inc. / 2010-2011 Intel Corporation)
|
||||
* Fog parameter optimization inspired by st_nir_lower_fog.c (Copyright 2023 Google LLC) and prog_statevars.c (Copyright 1999-2007 Brian Paul)
|
||||
* Blur
|
||||
* tterrag for [Blur](https://github.com/tterrag1098/Blur) - Menu blur based on Blur under the MIT license
|
||||
* RGSS Terrain Texture Filtering
|
||||
* Indiesoft for [defold-sharp-sprite](https://github.com/indiesoftby/defold-sharp-sprite), used as the reference implementation under the MIT license
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class ExitState {
|
||||
public static final int POLYGON_OFFSET_ENABLE_WRITTEN = 1;
|
||||
public static final int POLYGON_OFFSET_VALUES_WRITTEN = 2;
|
||||
final Values entry = new Values();
|
||||
final Values value = new Values();
|
||||
final MaterialState frontMaterial = new MaterialState(GL11.GL_FRONT);
|
||||
final MaterialState backMaterial = new MaterialState(GL11.GL_BACK);
|
||||
long writes, dependencies;
|
||||
boolean valid;
|
||||
int textureDeleteCount;
|
||||
|
||||
public void clear() {
|
||||
valid = false;
|
||||
writes = dependencies = 0;
|
||||
}
|
||||
|
||||
static final class Values {
|
||||
final Color4 color = new Color4();
|
||||
final Vector3f normal = new Vector3f();
|
||||
final Vector4f texCoord = new Vector4f();
|
||||
final Vector4f shaderColor = new Vector4f();
|
||||
final Vector4f overlayColor = new Vector4f();
|
||||
final BlendState blend = new BlendState();
|
||||
final AlphaState alpha = new AlphaState();
|
||||
final ColorMask colorMask = new ColorMask();
|
||||
final Matrix4f textureMatrix = new Matrix4f();
|
||||
long enabled;
|
||||
int activeUnit, clientActiveUnit, matrixMode, texture, depthFunction, cullFace, frontFace, shadeModel;
|
||||
int materialFace, materialParameter;
|
||||
boolean depthMask;
|
||||
float lightmapX, lightmapY, offsetFactor, offsetUnits, offsetClamp;
|
||||
}
|
||||
}
|
||||
@@ -1,597 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredAlphaHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Records the GL state a block of rendering code leaves behind, so a later run of the same block can be skipped and
|
||||
* its exit state applied instead.
|
||||
*/
|
||||
final class ExitStateCapture {
|
||||
// Fields 0..12 are the capabilities, in CAPS order. FRONT_* and BACK_* are four contiguous fields each, ordered like
|
||||
// vector(): apply() and finish() address them as 15L << FRONT_AMBIENT and (field - FRONT_AMBIENT) & 3.
|
||||
static final int BLEND = 0, ALPHA = 1, DEPTH = 2, CULL = 3, LIGHTING = 4, NORMALIZE = 5, RESCALE = 6, LIGHT0 = 7, LIGHT1 = 8, TEXTURE0 = 9, TEXTURE1 = 10, OFFSET = 11, COLOR_MATERIAL = 12, COLOR = 13, NORMAL = 14, TEXCOORD = 15,
|
||||
LIGHTMAP = 16, SHADER_COLOR = 17, BLEND_FUNC = 18, ALPHA_FUNC = 19, DEPTH_FUNC = 20, DEPTH_MASK = 21, COLOR_MASK = 22, CULL_FACE = 23, FRONT_FACE = 24, SHADE_MODEL = 25, ACTIVE_UNIT = 26, TEXTURE_BINDING = 27, MATRIX_MODE = 28,
|
||||
TEXTURE_MATRIX = 29, OFFSET_VALUES = 30, COLOR_MATERIAL_SETTINGS = 31, FRONT_AMBIENT = 32, FRONT_DIFFUSE = 33, FRONT_SPECULAR = 34, FRONT_EMISSION = 35, BACK_AMBIENT = 36, BACK_DIFFUSE = 37, BACK_SPECULAR = 38,
|
||||
BACK_EMISSION = 39, OVERLAY_COLOR = 40, CLIENT_ACTIVE_UNIT = 41;
|
||||
private static final int FIELD_COUNT = 42;
|
||||
private static final long IMPLICIT_INPUTS = bits(ACTIVE_UNIT, MATRIX_MODE);
|
||||
private static final long MATERIAL_BAKE_INPUTS = bits(COLOR, COLOR_MATERIAL_SETTINGS);
|
||||
private static final long DRAW_INPUTS = bits(CULL, TEXTURE0, TEXTURE1, COLOR, LIGHTMAP, OVERLAY_COLOR, CULL_FACE, FRONT_FACE, TEXTURE_BINDING);
|
||||
private static final long VERTEX_INPUTS = bits(NORMAL, TEXCOORD);
|
||||
private static final long MATERIAL_INPUTS = bits(BLEND, ALPHA, DEPTH, BLEND_FUNC, ALPHA_FUNC, DEPTH_FUNC, DEPTH_MASK, COLOR_MASK);
|
||||
private static final int[] CAPS = {GL11.GL_BLEND, GL11.GL_ALPHA_TEST, GL11.GL_DEPTH_TEST, GL11.GL_CULL_FACE, GL11.GL_LIGHTING, GL11.GL_NORMALIZE, GL12.GL_RESCALE_NORMAL, GL11.GL_LIGHT0, GL11.GL_LIGHT1, GL11.GL_TEXTURE_2D,
|
||||
GL11.GL_TEXTURE_2D, GL11.GL_POLYGON_OFFSET_FILL, GL11.GL_COLOR_MATERIAL};
|
||||
|
||||
private static final AtomicInteger openCaptures = new AtomicInteger();
|
||||
|
||||
private final GLContextState context;
|
||||
private final Matrix4f modelView = new Matrix4f();
|
||||
private final long[] attribWrites = new long[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
private final long[] matrixWrites = new long[GLStateManager.MAX_TEXTURE_STACK_DEPTH];
|
||||
private final boolean[] matrixInherited = new boolean[GLStateManager.MAX_TEXTURE_STACK_DEPTH];
|
||||
private final ExitState.Values current = new ExitState.Values();
|
||||
private final Reference2LongOpenHashMap<StateSet> restoredFieldsBySet = new Reference2LongOpenHashMap<>();
|
||||
private ExitState result;
|
||||
private int suspended, attribDepth, modelViewDepth, textureDepth, entryOverrides;
|
||||
private int filterTexture, filterMin, filterMag;
|
||||
private float filterAniso;
|
||||
private boolean filterOperation;
|
||||
private boolean textureInherited;
|
||||
|
||||
ExitStateCapture(GLContextState context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
static boolean anyOpen() {
|
||||
return openCaptures.get() != 0;
|
||||
}
|
||||
|
||||
private static boolean blendWritesReachVanilla() {
|
||||
final DeferredBlendHandler handler = GLSMHooks.blendHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isBlendLocked();
|
||||
}
|
||||
|
||||
private static boolean alphaWritesReachVanilla() {
|
||||
final DeferredAlphaHandler handler = GLSMHooks.alphaHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isAlphaTestLocked();
|
||||
}
|
||||
|
||||
private static boolean depthColorWritesReachVanilla() {
|
||||
final DeferredDepthColorHandler handler = GLSMHooks.depthColorHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isDepthColorLocked();
|
||||
}
|
||||
|
||||
private static int overridesInControl() {
|
||||
return (blendWritesReachVanilla() ? 0 : 1) | (alphaWritesReachVanilla() ? 0 : 2) | (depthColorWritesReachVanilla() ? 0 : 4);
|
||||
}
|
||||
|
||||
private static boolean same(int field, ExitState.Values a, ExitState.Values b) {
|
||||
return switch (field) {
|
||||
case COLOR -> a.color.sameAs(b.color);
|
||||
case NORMAL -> a.normal.equals(b.normal);
|
||||
case TEXCOORD -> a.texCoord.equals(b.texCoord);
|
||||
case LIGHTMAP -> a.lightmapX == b.lightmapX && a.lightmapY == b.lightmapY;
|
||||
case OVERLAY_COLOR -> a.overlayColor.equals(b.overlayColor);
|
||||
case BLEND_FUNC -> a.blend.sameAs(b.blend);
|
||||
case ALPHA_FUNC -> a.alpha.sameAs(b.alpha);
|
||||
case DEPTH_FUNC -> a.depthFunction == b.depthFunction;
|
||||
case DEPTH_MASK -> a.depthMask == b.depthMask;
|
||||
case COLOR_MASK -> a.colorMask.sameAs(b.colorMask);
|
||||
case CULL_FACE -> a.cullFace == b.cullFace;
|
||||
case FRONT_FACE -> a.frontFace == b.frontFace;
|
||||
case ACTIVE_UNIT -> a.activeUnit == b.activeUnit;
|
||||
case TEXTURE_BINDING -> a.texture == b.texture;
|
||||
case MATRIX_MODE -> a.matrixMode == b.matrixMode;
|
||||
case TEXTURE_MATRIX -> a.textureMatrix.equals(b.textureMatrix);
|
||||
case COLOR_MATERIAL_SETTINGS -> a.materialFace == b.materialFace && a.materialParameter == b.materialParameter;
|
||||
default -> throw new IllegalArgumentException("Unsupported renderer state dependency " + field);
|
||||
};
|
||||
}
|
||||
|
||||
private static Vector4f vector(MaterialState state, int component) {
|
||||
return switch (component) {
|
||||
case 0 -> state.ambient;
|
||||
case 1 -> state.diffuse;
|
||||
case 2 -> state.specular;
|
||||
default -> state.emission;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean has(ExitState state, int field) {
|
||||
return has(state.writes, field);
|
||||
}
|
||||
|
||||
private static boolean has(long fields, int field) {
|
||||
return (fields & (1L << field)) != 0;
|
||||
}
|
||||
|
||||
private static long bits(int... fields) {
|
||||
long bits = 0;
|
||||
for (int field : fields) bits |= 1L << field;
|
||||
return bits;
|
||||
}
|
||||
|
||||
boolean active() {
|
||||
return result != null && result.valid && suspended == 0 && !context.poppingAttributes && !BatchStateGuard.isSuspended();
|
||||
}
|
||||
|
||||
void suspend() {
|
||||
suspended++;
|
||||
}
|
||||
|
||||
void resume() {
|
||||
if (suspended == 0) throw new IllegalStateException("Exit capture suspension underflow");
|
||||
suspended--;
|
||||
}
|
||||
|
||||
void begin(ExitState out) {
|
||||
out.clear();
|
||||
if (result != null) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
result = out;
|
||||
openCaptures.incrementAndGet();
|
||||
filterTexture = 0;
|
||||
filterOperation = false;
|
||||
out.valid = suspended == 0 && !BatchStateGuard.isSuspended() && GLStateManager.isCachingEnabled() && !DisplayListManager.isRecording() && context.savedFilterTextureId == 0;
|
||||
if (!out.valid) return;
|
||||
out.dependencies = IMPLICIT_INPUTS;
|
||||
capture(out.entry, IMPLICIT_INPUTS | DRAW_INPUTS | MATERIAL_INPUTS | MATERIAL_BAKE_INPUTS | VERTEX_INPUTS | (1L << TEXTURE_MATRIX));
|
||||
textureInherited = true;
|
||||
modelView.set(context.modelViewMatrix);
|
||||
attribDepth = context.attribDepth;
|
||||
modelViewDepth = GLStateManager.getMatrixStackDepth(context.modelViewMatrix);
|
||||
textureDepth = GLStateManager.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0));
|
||||
entryOverrides = overridesInControl();
|
||||
}
|
||||
|
||||
boolean finish(ExitState out) {
|
||||
if (result != out) {
|
||||
out.clear();
|
||||
return false;
|
||||
}
|
||||
if (textureInherited && has(out, TEXTURE_MATRIX)) out.dependencies |= 1L << TEXTURE_MATRIX;
|
||||
result = null;
|
||||
openCaptures.decrementAndGet();
|
||||
if (out.valid) out.valid = suspended == 0 && filterTexture == 0 && stacksMatchEntry() && overridesInControl() == entryOverrides;
|
||||
if (out.valid) {
|
||||
capture(out.value, out.writes);
|
||||
if ((out.writes & (15L << FRONT_AMBIENT)) != 0) out.frontMaterial.set(context.frontMaterial);
|
||||
if ((out.writes & (15L << BACK_AMBIENT)) != 0) out.backMaterial.set(context.backMaterial);
|
||||
out.textureDeleteCount = TextureInfoCache.INSTANCE.deleteCount();
|
||||
}
|
||||
filterTexture = 0;
|
||||
return out.valid;
|
||||
}
|
||||
|
||||
private boolean stacksMatchEntry() {
|
||||
return context.attribDepth == attribDepth && GLStateManager.getMatrixStackDepth(context.modelViewMatrix) == modelViewDepth
|
||||
&& GLStateManager.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0)) == textureDepth && modelView.equals(context.modelViewMatrix);
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
if (result != null) {
|
||||
result.clear();
|
||||
openCaptures.decrementAndGet();
|
||||
}
|
||||
result = null;
|
||||
filterTexture = 0;
|
||||
filterOperation = false;
|
||||
}
|
||||
|
||||
void reject() {
|
||||
if (active()) result.valid = false;
|
||||
}
|
||||
|
||||
// Callers record before their cache's unchanged-value early return
|
||||
void write(int field) {
|
||||
if (active()) record(field);
|
||||
}
|
||||
|
||||
void writeBlend() {
|
||||
if (active() && blendWritesReachVanilla()) record(ExitStateCapture.BLEND_FUNC);
|
||||
}
|
||||
|
||||
void writeAlpha() {
|
||||
if (active() && alphaWritesReachVanilla()) record(ExitStateCapture.ALPHA_FUNC);
|
||||
}
|
||||
|
||||
void writeDepthColor(int field) {
|
||||
if (active() && depthColorWritesReachVanilla()) record(field);
|
||||
}
|
||||
|
||||
private void record(int field) {
|
||||
result.writes |= 1L << field;
|
||||
}
|
||||
|
||||
void requireDrawState(boolean materialFromState) {
|
||||
if (!active()) return;
|
||||
long fields = (materialFromState ? DRAW_INPUTS | MATERIAL_INPUTS : DRAW_INPUTS) & ~(result.writes | result.dependencies);
|
||||
if ((fields & (1L << BLEND_FUNC)) != 0 && !context.blendMode.isEffectivelyEnabled()) fields &= ~(1L << BLEND_FUNC);
|
||||
if ((fields & (1L << ALPHA_FUNC)) != 0 && !context.alphaTest.isEffectivelyEnabled()) fields &= ~(1L << ALPHA_FUNC);
|
||||
result.dependencies |= fields;
|
||||
if (textureInherited) result.dependencies |= 1L << TEXTURE_MATRIX;
|
||||
}
|
||||
|
||||
void requireMaterialBakeInputs() {
|
||||
if (active()) result.dependencies |= MATERIAL_BAKE_INPUTS & ~result.writes;
|
||||
}
|
||||
|
||||
void requireVertexDefaults(boolean normal, boolean texCoord, boolean color, boolean lightmap) {
|
||||
if (!active()) return;
|
||||
final long fields = (normal ? 1L << NORMAL : 0) | (texCoord ? 1L << TEXCOORD : 0) | (color ? 1L << COLOR : 0) | (lightmap ? 1L << LIGHTMAP : 0);
|
||||
result.dependencies |= fields & ~result.writes;
|
||||
}
|
||||
|
||||
boolean beginTextureFilter(boolean nearest) {
|
||||
if (filterOperationBlocked()) return false;
|
||||
if (!nearest || filterTexture != 0 || context.savedFilterTextureId <= 0 || context.activeTextureUnit.getValue() != 0 || context.savedFilterMin < 0 || context.savedFilterMag < 0 || !(context.savedFilterAniso >= 1)) {
|
||||
reject();
|
||||
return false;
|
||||
}
|
||||
filterTexture = context.savedFilterTextureId;
|
||||
filterMin = context.savedFilterMin;
|
||||
filterMag = context.savedFilterMag;
|
||||
filterAniso = context.savedFilterAniso;
|
||||
startFilterOperation();
|
||||
return true;
|
||||
}
|
||||
|
||||
void finishTextureFilterSave() {
|
||||
endFilterOperation();
|
||||
if (active() && (savedFilterDiffers() || context.activeTextureUnit.getValue() != 0 || context.textures.getTextureUnitBindings(0).getBinding() != filterTexture || filterDiffersFrom(GL11.GL_NEAREST, GL11.GL_NEAREST, 1))) reject();
|
||||
}
|
||||
|
||||
boolean beginTextureFilterRestore() {
|
||||
if (filterOperationBlocked()) return false;
|
||||
if (savedFilterDiffers()) {
|
||||
reject();
|
||||
return false;
|
||||
}
|
||||
startFilterOperation();
|
||||
return true;
|
||||
}
|
||||
|
||||
void finishTextureFilterRestore() {
|
||||
endFilterOperation();
|
||||
if (active() && (savedFilterDiffers() || filterDiffersFrom(filterMin, filterMag, filterAniso))) reject();
|
||||
filterTexture = 0;
|
||||
}
|
||||
|
||||
private boolean filterOperationBlocked() {
|
||||
if (filterOperation) {
|
||||
failTextureFilter();
|
||||
return true;
|
||||
}
|
||||
return !active();
|
||||
}
|
||||
|
||||
private void startFilterOperation() {
|
||||
filterOperation = true;
|
||||
suspend();
|
||||
}
|
||||
|
||||
private void endFilterOperation() {
|
||||
resume();
|
||||
filterOperation = false;
|
||||
}
|
||||
|
||||
void failTextureFilter() {
|
||||
if (result != null) result.valid = false;
|
||||
}
|
||||
|
||||
void requireTextureFilter(int texture, boolean nearest) {
|
||||
if (filterTexture != 0 && texture == filterTexture && !nearest && active()) reject();
|
||||
}
|
||||
|
||||
private boolean savedFilterDiffers() {
|
||||
return filterTexture == 0 || context.savedFilterTextureId != filterTexture || context.savedFilterMin != filterMin || context.savedFilterMag != filterMag || context.savedFilterAniso != filterAniso;
|
||||
}
|
||||
|
||||
private boolean filterDiffersFrom(int min, int mag, float aniso) {
|
||||
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(filterTexture);
|
||||
return info == null || info.getMinFilter() != min || info.getMagFilter() != mag || info.getMaxAnisotropy() != aniso;
|
||||
}
|
||||
|
||||
boolean lightingWritten() {
|
||||
return active() && has(result, LIGHTING);
|
||||
}
|
||||
|
||||
int polygonOffsetWrites() {
|
||||
if (!active()) return 0;
|
||||
return (has(result, OFFSET) ? ExitState.POLYGON_OFFSET_ENABLE_WRITTEN : 0) | (has(result, OFFSET_VALUES) ? ExitState.POLYGON_OFFSET_VALUES_WRITTEN : 0);
|
||||
}
|
||||
|
||||
void capability(int cap, int unit) {
|
||||
if (!active()) return;
|
||||
int field = -1;
|
||||
for (int i = 0; i < CAPS.length; i++) {
|
||||
if (CAPS[i] == cap) {
|
||||
field = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (field == TEXTURE0) field = unit == 0 ? TEXTURE0 : unit == 1 ? TEXTURE1 : -1;
|
||||
if (field < 0) result.valid = false;
|
||||
else if (field == BLEND ? blendWritesReachVanilla() : field != ALPHA || alphaWritesReachVanilla()) record(field);
|
||||
}
|
||||
|
||||
void material(int face, int parameter) {
|
||||
if (!active()) return;
|
||||
int first = switch (parameter) {
|
||||
case GL11.GL_AMBIENT, GL11.GL_AMBIENT_AND_DIFFUSE -> 0;
|
||||
case GL11.GL_DIFFUSE -> 1;
|
||||
case GL11.GL_SPECULAR -> 2;
|
||||
case GL11.GL_EMISSION -> 3;
|
||||
default -> -1;
|
||||
};
|
||||
if (first < 0) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (face != GL11.GL_BACK) {
|
||||
record(FRONT_AMBIENT + first);
|
||||
if (parameter == GL11.GL_AMBIENT_AND_DIFFUSE) record(FRONT_DIFFUSE);
|
||||
}
|
||||
if (face != GL11.GL_FRONT) {
|
||||
record(BACK_AMBIENT + first);
|
||||
if (parameter == GL11.GL_AMBIENT_AND_DIFFUSE) record(BACK_DIFFUSE);
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes and pops check result rather than active()
|
||||
void pushAttrib() {
|
||||
if (result == null) return;
|
||||
if (context.attribDepth < attribWrites.length) attribWrites[context.attribDepth] = result.writes;
|
||||
else result.valid = false;
|
||||
}
|
||||
|
||||
void popAttrib(StateSet set) {
|
||||
if (result == null) return;
|
||||
if (context.attribDepth <= attribDepth) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
final long restored = restoredFields(set);
|
||||
result.writes = (result.writes & ~restored) | (attribWrites[context.attribDepth - 1] & restored);
|
||||
}
|
||||
|
||||
private long restoredFields(StateSet set) {
|
||||
long restored = restoredFieldsBySet.getOrDefault(set, -1L);
|
||||
if (restored >= 0) return restored;
|
||||
restored = 0;
|
||||
for (int field = 0; field < FIELD_COUNT; field++) {
|
||||
final CowStateStack<?> stack = stack(field);
|
||||
if (stack != null && set.contains(stack.stackId())) restored |= 1L << field;
|
||||
}
|
||||
restoredFieldsBySet.put(set, restored);
|
||||
return restored;
|
||||
}
|
||||
|
||||
private CowStateStack<?> stack(int field) {
|
||||
return switch (field) {
|
||||
case BLEND -> context.blendMode;
|
||||
case ALPHA -> context.alphaTest;
|
||||
case DEPTH -> context.depthTest;
|
||||
case CULL -> context.cullState;
|
||||
case LIGHTING -> context.lightingState;
|
||||
case NORMALIZE -> context.normalizeState;
|
||||
case RESCALE -> context.rescaleNormalState;
|
||||
case LIGHT0, LIGHT1 -> context.lightStates[field - LIGHT0];
|
||||
case TEXTURE0, TEXTURE1 -> context.textures.getTextureUnitStates(field - TEXTURE0);
|
||||
case OFFSET -> context.polygonOffsetFillState;
|
||||
case COLOR_MATERIAL -> context.colorMaterial;
|
||||
case COLOR -> context.color;
|
||||
case NORMAL -> context.ffp.normalStack;
|
||||
case TEXCOORD -> context.ffp.texCoordStack;
|
||||
case BLEND_FUNC -> context.blendState;
|
||||
case ALPHA_FUNC -> context.alphaState;
|
||||
case DEPTH_FUNC, DEPTH_MASK -> context.depthState;
|
||||
case COLOR_MASK -> context.colorMask;
|
||||
case CULL_FACE, FRONT_FACE, OFFSET_VALUES -> context.polygonState;
|
||||
case SHADE_MODEL -> context.shadeModelState;
|
||||
case ACTIVE_UNIT -> context.activeTextureUnit;
|
||||
case TEXTURE_BINDING -> context.textures.getTextureUnitBindings(0);
|
||||
case MATRIX_MODE -> context.matrixMode;
|
||||
// Face and parameter are restored together (GL_LIGHTING_BIT and the batch set hold both).
|
||||
case COLOR_MATERIAL_SETTINGS -> context.colorMaterialFace;
|
||||
case FRONT_AMBIENT, FRONT_DIFFUSE, FRONT_SPECULAR, FRONT_EMISSION -> context.frontMaterial;
|
||||
case BACK_AMBIENT, BACK_DIFFUSE, BACK_SPECULAR, BACK_EMISSION -> context.backMaterial;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
void matrixChange(boolean absolute) {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
if ((absolute || mode != GL11.GL_MODELVIEW) && active()) recordMatrixChange(mode, context.activeTextureUnit.getValue(), absolute);
|
||||
}
|
||||
|
||||
void matrixChange(int mode, int unit, boolean absolute) {
|
||||
if (active()) recordMatrixChange(mode, unit, absolute);
|
||||
}
|
||||
|
||||
private void recordMatrixChange(int mode, int unit, boolean absolute) {
|
||||
if (mode == GL11.GL_MODELVIEW) {
|
||||
if (absolute) result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (mode != GL11.GL_TEXTURE || unit != 0) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
record(TEXTURE_MATRIX);
|
||||
if (absolute) textureInherited = false;
|
||||
}
|
||||
|
||||
void pushMatrix() {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
if (mode == GL11.GL_TEXTURE && context.activeTextureUnit.getValue() == 0) {
|
||||
final int depth = GLStateManager.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0));
|
||||
if (depth < matrixWrites.length) {
|
||||
matrixWrites[depth] = result.writes;
|
||||
matrixInherited[depth] = textureInherited;
|
||||
} else {
|
||||
result.valid = false;
|
||||
}
|
||||
} else if (mode != GL11.GL_MODELVIEW) {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
||||
void popMatrix() {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
final boolean texture = mode == GL11.GL_TEXTURE && context.activeTextureUnit.getValue() == 0;
|
||||
final Matrix4fStack matrix;
|
||||
final int entryDepth;
|
||||
if (mode == GL11.GL_MODELVIEW) {
|
||||
matrix = context.modelViewMatrix;
|
||||
entryDepth = modelViewDepth;
|
||||
} else if (texture) {
|
||||
matrix = context.textures.getTextureUnitMatrix(0);
|
||||
entryDepth = textureDepth;
|
||||
} else {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
final int depth = GLStateManager.getMatrixStackDepth(matrix);
|
||||
if (depth <= entryDepth) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (texture) {
|
||||
final long bit = 1L << TEXTURE_MATRIX;
|
||||
result.writes = (result.writes & ~bit) | (matrixWrites[depth - 1] & bit);
|
||||
textureInherited = matrixInherited[depth - 1];
|
||||
}
|
||||
}
|
||||
|
||||
private void capture(ExitState.Values out, long fields) {
|
||||
out.enabled = 0;
|
||||
for (int field = 0; field < CAPS.length; field++) {
|
||||
if (has(fields, field) && ((BooleanStateStack) Objects.requireNonNull(stack(field))).isEffectivelyEnabled()) out.enabled |= 1L << field;
|
||||
}
|
||||
if (has(fields, COLOR)) out.color.set(context.color);
|
||||
if (has(fields, NORMAL)) out.normal.set(context.ffp.currentNormal);
|
||||
if (has(fields, TEXCOORD)) out.texCoord.set(context.ffp.currentTexCoords[0]);
|
||||
if (has(fields, LIGHTMAP)) {
|
||||
out.lightmapX = context.lastBrightnessX;
|
||||
out.lightmapY = context.lastBrightnessY;
|
||||
}
|
||||
if (has(fields, SHADER_COLOR)) out.shaderColor.set(context.shaderColorR, context.shaderColorG, context.shaderColorB, context.shaderColorA);
|
||||
if (has(fields, OVERLAY_COLOR)) out.overlayColor.set(context.overlayR, context.overlayG, context.overlayB, context.overlayA);
|
||||
if (has(fields, BLEND_FUNC)) context.blendState.readEffective(out.blend);
|
||||
if (has(fields, ALPHA_FUNC)) context.alphaState.readEffective(out.alpha);
|
||||
if (has(fields, COLOR_MASK)) context.colorMask.readEffective(out.colorMask);
|
||||
if (has(fields, DEPTH_FUNC)) out.depthFunction = context.depthState.getFunc();
|
||||
if (has(fields, DEPTH_MASK)) out.depthMask = context.depthState.isEffectiveMaskEnabled();
|
||||
if (has(fields, CULL_FACE)) out.cullFace = context.polygonState.getCullFaceMode();
|
||||
if (has(fields, FRONT_FACE)) out.frontFace = context.polygonState.getFrontFace();
|
||||
if (has(fields, OFFSET_VALUES)) {
|
||||
out.offsetFactor = context.polygonState.getOffsetFactor();
|
||||
out.offsetUnits = context.polygonState.getOffsetUnits();
|
||||
out.offsetClamp = context.polygonState.getOffsetClamp();
|
||||
}
|
||||
if (has(fields, SHADE_MODEL)) out.shadeModel = context.shadeModelState.getValue();
|
||||
if (has(fields, ACTIVE_UNIT)) out.activeUnit = context.activeTextureUnit.getValue();
|
||||
if (has(fields, CLIENT_ACTIVE_UNIT)) out.clientActiveUnit = context.clientActiveTextureUnit;
|
||||
if (has(fields, MATRIX_MODE)) out.matrixMode = context.matrixMode.getMode();
|
||||
if (has(fields, TEXTURE_BINDING)) out.texture = context.textures.getTextureUnitBindings(0).getBinding();
|
||||
if (has(fields, TEXTURE_MATRIX)) out.textureMatrix.set(context.textures.getTextureUnitMatrix(0));
|
||||
if (has(fields, COLOR_MATERIAL_SETTINGS)) {
|
||||
out.materialFace = context.colorMaterialFace.getValue();
|
||||
out.materialParameter = context.colorMaterialParameter.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
boolean canApply(ExitState in) {
|
||||
if (!in.valid || result != null || !GLStateManager.isCachingEnabled() || DisplayListManager.isRecording() || context.savedFilterTextureId != 0) return false;
|
||||
if (in.textureDeleteCount != TextureInfoCache.INSTANCE.deleteCount()) return false;
|
||||
capture(current, in.dependencies);
|
||||
if (((current.enabled ^ in.entry.enabled) & in.dependencies & ((1L << CAPS.length) - 1)) != 0) return false;
|
||||
long remaining = in.dependencies & -(1L << CAPS.length);
|
||||
while (remaining != 0) {
|
||||
final int field = Long.numberOfTrailingZeros(remaining);
|
||||
if (!same(field, in.entry, current)) return false;
|
||||
remaining &= remaining - 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void apply(ExitState state) {
|
||||
if (!state.valid) throw new IllegalStateException("Invalid renderer exit state");
|
||||
if (result != null) throw new IllegalStateException("Exit state applied while a capture is open");
|
||||
final ExitState.Values in = state.value;
|
||||
final long fields = state.writes;
|
||||
final int liveUnit = context.activeTextureUnit.getValue();
|
||||
// Capabilities go first so the glColor4f below bakes into the material only if color material is on at exit.
|
||||
for (int field = 0; field < CAPS.length; field++) {
|
||||
if (!has(fields, field)) continue;
|
||||
if (field == TEXTURE0 || field == TEXTURE1) GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + field - TEXTURE0);
|
||||
// glEnable would bake the live color, not the exit color, into the material.
|
||||
if (field == COLOR_MATERIAL) {
|
||||
context.colorMaterial.setEnabled((in.enabled & (1L << field)) != 0);
|
||||
} else if ((in.enabled & (1L << field)) != 0) GLStateManager.glEnable(CAPS[field]);
|
||||
else GLStateManager.glDisable(CAPS[field]);
|
||||
}
|
||||
if (has(fields, COLOR)) GLStateManager.glColor4f(in.color.getRed(), in.color.getGreen(), in.color.getBlue(), in.color.getAlpha());
|
||||
if (has(fields, NORMAL)) GLStateManager.glNormal3f(in.normal.x, in.normal.y, in.normal.z);
|
||||
if (has(fields, TEXCOORD)) GLStateManager.glTexCoord4f(in.texCoord.x, in.texCoord.y, in.texCoord.z, in.texCoord.w);
|
||||
if (has(fields, LIGHTMAP)) GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, in.lightmapX, in.lightmapY);
|
||||
if (has(fields, SHADER_COLOR)) GLStateManager.setShaderColor(in.shaderColor.x, in.shaderColor.y, in.shaderColor.z, in.shaderColor.w);
|
||||
if (has(fields, OVERLAY_COLOR)) GLStateManager.setOverlayColor(in.overlayColor.x, in.overlayColor.y, in.overlayColor.z, in.overlayColor.w);
|
||||
if (has(fields, BLEND_FUNC)) GLStateManager.tryBlendFuncSeparate(in.blend.getSrcRgb(), in.blend.getDstRgb(), in.blend.getSrcAlpha(), in.blend.getDstAlpha());
|
||||
if (has(fields, ALPHA_FUNC)) GLStateManager.glAlphaFunc(in.alpha.getFunction(), in.alpha.getReference());
|
||||
if (has(fields, DEPTH_FUNC)) GLStateManager.glDepthFunc(in.depthFunction);
|
||||
if (has(fields, DEPTH_MASK)) GLStateManager.glDepthMask(in.depthMask);
|
||||
if (has(fields, COLOR_MASK)) GLStateManager.glColorMask(in.colorMask.red, in.colorMask.green, in.colorMask.blue, in.colorMask.alpha);
|
||||
if (has(fields, CULL_FACE)) GLStateManager.glCullFace(in.cullFace);
|
||||
if (has(fields, FRONT_FACE)) GLStateManager.glFrontFace(in.frontFace);
|
||||
if (has(fields, SHADE_MODEL)) GLStateManager.glShadeModel(in.shadeModel);
|
||||
if (has(fields, OFFSET_VALUES)) GLStateManager.glPolygonOffsetClamp(in.offsetFactor, in.offsetUnits, in.offsetClamp);
|
||||
if (has(fields, TEXTURE_BINDING)) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, in.texture);
|
||||
}
|
||||
if (has(fields, TEXTURE_MATRIX)) GLStateManager.setTextureMatrix(0, in.textureMatrix);
|
||||
if (has(fields, COLOR_MATERIAL_SETTINGS)) GLStateManager.glColorMaterial(in.materialFace, in.materialParameter);
|
||||
boolean materialChanged = false;
|
||||
for (int field = FRONT_AMBIENT; field <= BACK_EMISSION; field++) {
|
||||
if (!has(fields, field)) continue;
|
||||
final boolean front = field < BACK_AMBIENT;
|
||||
final int component = (field - FRONT_AMBIENT) & 3;
|
||||
final Vector4f source = vector(front ? state.frontMaterial : state.backMaterial, component);
|
||||
final var target = front ? context.frontMaterial : context.backMaterial;
|
||||
if (vector(target, component).equals(source)) continue;
|
||||
target.beforeModify();
|
||||
vector(target, component).set(source);
|
||||
materialChanged = true;
|
||||
}
|
||||
if (materialChanged) context.lightingGeneration++;
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + (has(fields, ACTIVE_UNIT) ? in.activeUnit : liveUnit));
|
||||
if (has(fields, CLIENT_ACTIVE_UNIT)) GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + in.clientActiveUnit);
|
||||
if (has(fields, MATRIX_MODE)) GLStateManager.glMatrixMode(in.matrixMode);
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GLContextState {
|
||||
final ExitStateCapture exitCapture = new ExitStateCapture(this);
|
||||
private final List<IStateStack<?>> allStacksBuilder = new ArrayList<>();
|
||||
public IStateStack<?>[] allStacks;
|
||||
final List<BooleanStateStack> allBooleanStates = new ArrayList<>();
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -198,17 +198,14 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void generateMipmaps(int texture, int mipmapTarget) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.generateMipmaps(texture, mipmapTarget);
|
||||
}
|
||||
|
||||
public static void bindAttributeLocation(int program, int index, CharSequence name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.bindAttribLocation(program, index, name);
|
||||
}
|
||||
|
||||
public static void texImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
final GLStateManager.GLESTexImageRemap remap = GLStateManager.remapTexImageForGLES(internalformat, format, type);
|
||||
internalformat = remap.internalFormat();
|
||||
format = remap.format();
|
||||
@@ -223,7 +220,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void uniformMatrix4fv(int location, boolean transpose, FloatBuffer matrix) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniformMatrix4(location, transpose, matrix);
|
||||
}
|
||||
|
||||
@@ -232,78 +228,61 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void uniform1f(int location, float v0) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1f(location, v0);
|
||||
}
|
||||
|
||||
public static void uniform1i(int location, int v0) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1i(location, v0);
|
||||
}
|
||||
|
||||
public static void uniform2f(int location, float v0, float v1) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2f(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void uniform2i(int location, int v0, int v1) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2i(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void uniform3f(int location, float v0, float v1, float v2) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3f(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void uniform3i(int location, int v0, int v1, int v2) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3i(location, v0, v1, v2);
|
||||
}
|
||||
public static void uniform3i(int location, int v0, int v1, int v2) {RENDER_BACKEND.uniform3i(location, v0, v1, v2);}
|
||||
|
||||
public static void uniform4f(int location, float v0, float v1, float v2, float v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4f(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void uniform4i(int location, int v0, int v1, int v2, int v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static int getAttribLocation(int programId, String name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getAttribLocation(programId, name);
|
||||
}
|
||||
|
||||
public static int getUniformLocation(int programId, String name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getUniformLocation(programId, name);
|
||||
}
|
||||
|
||||
public static void texParameteriv(int texture, int target, int pname, IntBuffer params) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameteriv(texture, target, pname, params);
|
||||
}
|
||||
|
||||
public static void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.copyTexSubImage2D(destTexture, target, i, i1, i2, i3, i4, width, height);
|
||||
if (i == 0) TextureInfoCache.INSTANCE.onContentsChanged(destTexture);
|
||||
}
|
||||
|
||||
public static void texParameteri(int texture, int target, int pname, int param) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameteri(texture, target, pname, param);
|
||||
}
|
||||
|
||||
public static void texParameterf(int texture, int target, int pname, float param) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameterf(texture, target, pname, param);
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
@@ -311,7 +290,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
@@ -319,7 +297,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
@@ -327,7 +304,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
@@ -335,7 +311,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void texImage1D(int texture, int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage1D(target, level, internalformat, width, border, format, type, pixels);
|
||||
@@ -343,7 +318,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void texImage3D(int texture, int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
||||
@@ -351,32 +325,26 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static String getProgramInfoLog(int program) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getProgramInfoLog(program, RENDER_BACKEND.getProgrami(program, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
|
||||
public static String getShaderInfoLog(int shader) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getShaderInfoLog(shader, RENDER_BACKEND.getShaderi(shader, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
|
||||
public static void drawBuffers(int framebuffer, IntBuffer buffers) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.drawBuffers(framebuffer, buffers);
|
||||
}
|
||||
|
||||
public static void readBuffer(int framebuffer, int buffer) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.readBuffer(framebuffer, buffer);
|
||||
}
|
||||
|
||||
public static String getActiveUniform(int program, int index, int maxLength, IntBuffer sizeType) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getActiveUniform(program, index, maxLength, sizeType);
|
||||
}
|
||||
|
||||
public static void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelPackBuffer();
|
||||
@@ -387,37 +355,30 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.bufferStorage(target, data, usage);
|
||||
}
|
||||
|
||||
public static void vertexAttrib4f(int index, float v0, float v1, float v2, float v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.vertexAttrib4f(index, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void detachShader(int program, int shader) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.detachShader(program, shader);
|
||||
}
|
||||
|
||||
public static void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.framebufferTexture2D(fb, fbtarget, attachment, target, texture, levels);
|
||||
}
|
||||
|
||||
public static int getTexParameteri(int texture, int target, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexParameteri(texture, target, pname);
|
||||
}
|
||||
|
||||
public static float getTexParameterf(int texture, int target, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexParameterf(texture, target, pname);
|
||||
}
|
||||
|
||||
public static int getTexLevelParameteri(int texture, int level, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexLevelParameteri(texture, level, pname);
|
||||
}
|
||||
|
||||
@@ -514,21 +475,18 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void getProgramiv(int program, int value, IntBuffer storage) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.getProgramiv(program, value, storage);
|
||||
}
|
||||
|
||||
public static void dispatchCompute(int workX, int workY, int workZ) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.dispatchCompute(workX, workY, workZ);
|
||||
}
|
||||
|
||||
public static void dispatchCompute(Vector3i workGroups) {
|
||||
dispatchCompute(workGroups.x, workGroups.y, workGroups.z);
|
||||
RENDER_BACKEND.dispatchCompute(workGroups.x, workGroups.y, workGroups.z);
|
||||
}
|
||||
|
||||
public static void dispatchComputeIndirect(long offset) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.dispatchComputeIndirect(offset);
|
||||
}
|
||||
|
||||
@@ -537,7 +495,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void memoryBarrier(int barriers) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
if (supportsCompute) {
|
||||
RENDER_BACKEND.memoryBarrier(barriers);
|
||||
}
|
||||
@@ -560,14 +517,12 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int unit, int texture) {
|
||||
if (unit == 0) GLStateManager.recordExitTextureBind(GL11.GL_TEXTURE_2D, 0);
|
||||
else if (GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
if (unit != 0 && GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
dsaState.bindTextureToUnit(unit, texture);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int target, int unit, int texture) {
|
||||
if (unit == 0) GLStateManager.recordExitTextureBind(target, 0);
|
||||
else if (GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
if (unit != 0 && GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
dsaState.bindTextureToUnit(target, unit, texture);
|
||||
}
|
||||
|
||||
@@ -587,7 +542,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void blitFramebuffer(int source, int dest, int offsetX, int offsetY, int width, int height, int offsetX2, int offsetY2, int width2, int height2, int bufferChoice, int filter) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
final boolean scissor = GLStateManager.glIsEnabled(GL11.GL_SCISSOR_TEST);
|
||||
if (scissor) GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
try {
|
||||
@@ -598,12 +552,10 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int createFramebuffer() {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.createFramebuffer();
|
||||
}
|
||||
|
||||
public static int createTexture(int target) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.createTexture(target);
|
||||
}
|
||||
|
||||
@@ -621,7 +573,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int getIndexedBufferBinding(int target, int index) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return GLStateManager.getIndexedBufferBinding(target, index);
|
||||
}
|
||||
|
||||
@@ -653,7 +604,6 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void clearTexImage(int texture, int target, int level, int format, int type) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.clearTexImage(texture, level, format, type);
|
||||
}
|
||||
|
||||
|
||||
@@ -199,21 +199,18 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
public void setNormal(float x, float y, float z) {
|
||||
GLStateManager.recordExitNormal();
|
||||
normalStack.beforeModify();
|
||||
currentNormal.set(x, y, z);
|
||||
normalGeneration++;
|
||||
}
|
||||
|
||||
public void setTexCoord(float s, float t, float r, float q) {
|
||||
GLStateManager.recordExitTexCoord(0);
|
||||
texCoordStack.beforeModify();
|
||||
currentTexCoords[0].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
public void setTexCoord(int unit, float s, float t, float r, float q) {
|
||||
GLStateManager.recordExitTexCoord(unit);
|
||||
if (unit == 0) texCoordStack.beforeModify();
|
||||
currentTexCoords[unit].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredAlphaHandler {
|
||||
boolean isAlphaTestLocked();
|
||||
boolean isOverrideHeld();
|
||||
boolean deferAlphaTestToggle(boolean enabled);
|
||||
boolean deferAlphaFunc(int function, float reference);
|
||||
}
|
||||
@@ -47,31 +47,6 @@ public final class TextureUtilHooks {
|
||||
GLStateManager.ctx().savedFilterTextureId = 0;
|
||||
}
|
||||
|
||||
public static boolean beginFilterSave(boolean blurred, boolean mipmap, float aniso) {
|
||||
captureBoundFilterTexture();
|
||||
return GLStateManager.beginExitTextureFilter(!blurred && !mipmap && aniso == 1);
|
||||
}
|
||||
|
||||
public static void endFilterSave(boolean captured) {
|
||||
if (captured) GLStateManager.finishExitTextureFilterSave();
|
||||
}
|
||||
|
||||
public static boolean beginFilterRestore() {
|
||||
return GLStateManager.beginExitTextureFilterRestore();
|
||||
}
|
||||
|
||||
public static void endFilterRestore(boolean captured) {
|
||||
try {
|
||||
if (captured) GLStateManager.finishExitTextureFilterRestore();
|
||||
} finally {
|
||||
clearSavedFilterTexture();
|
||||
}
|
||||
}
|
||||
|
||||
public static void filterFailed(boolean captured) {
|
||||
if (captured) GLStateManager.failExitTextureFilter();
|
||||
}
|
||||
|
||||
public static void restoreFilter(int min, int mag) {
|
||||
final int id = GLStateManager.ctx().savedFilterTextureId;
|
||||
if (id != 0 && DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.NONE) {
|
||||
|
||||
@@ -96,7 +96,6 @@ public class BooleanStateStack extends BooleanState implements CowStateStack<Boo
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
final GLContextState o = cow.owner;
|
||||
GLStateManager.recordExitCapability(o, glCap, -1);
|
||||
if (o != null && o.attribDepth > 0
|
||||
&& (stateUnknown || !GLStateManager.isCachingEnabled() || enabled != vanillaValue())) {
|
||||
beforeModify();
|
||||
|
||||
-1
@@ -24,7 +24,6 @@ public final class TextureUnitBooleanStateStack extends BooleanStateStack {
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
GLStateManager.recordExitCapability(cowDepths().owner, glCap, unitIndex);
|
||||
if (!GLStateManager.isCachingEnabled() || enabled != this.enabled) {
|
||||
GLStateManager.beforeUncapturedTextureUnitChange(glCap, unitIndex);
|
||||
beforeModify();
|
||||
|
||||
-1
@@ -197,7 +197,6 @@ public class TessellatorStreamingDrawer {
|
||||
final boolean faceNormals = !tess.hasNormals && normalHandler != null
|
||||
&& ImmediateExtendedAttribHandler.extPrimVerts(tess.drawMode, vertexCount) == 4 && normalHandler.wantsFaceNormals();
|
||||
final boolean fillNormal = !tess.hasNormals && !faceNormals;
|
||||
GLStateManager.requireExitVertexDefaults(fillNormal, !tess.hasTexture, !tess.hasColor, !tess.hasBrightness);
|
||||
|
||||
final int defaultNormal;
|
||||
if (fillNormal) {
|
||||
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class GLSM_ExitCaptureCoverage_UnitTest {
|
||||
|
||||
private static final String PACKAGE = "com/gtnewhorizons/angelica/glsm/";
|
||||
private static final String MANAGER = PACKAGE + "GLStateManager";
|
||||
private static final String RENDER_SYSTEM = PACKAGE + "RenderSystem";
|
||||
private static final String CAPTURE = PACKAGE + "ExitStateCapture";
|
||||
|
||||
private static final Set<String> RECORDING = Set.of(
|
||||
"reject", "write", "writeBlend", "writeAlpha", "writeDepthColor", "capability", "material",
|
||||
"matrixChange", "pushAttrib", "popAttrib", "pushMatrix", "popMatrix",
|
||||
"requireDrawState", "requireMaterialBakeInputs", "requireVertexDefaults", "requireTextureFilter",
|
||||
"beginTextureFilter", "finishTextureFilterSave", "beginTextureFilterRestore", "finishTextureFilterRestore",
|
||||
"failTextureFilter");
|
||||
|
||||
private static final Set<String> IGNORED = Set.of(
|
||||
// glBegin and glEnd reject
|
||||
"glVertex2d", "glVertex2f", "glVertex2i", "glVertex3d", "glVertex3f", "glVertex3i",
|
||||
// No render state
|
||||
"glDebugMessageCallback", "glDebugMessageControl", "glDebugMessageInsert", "glObjectLabel",
|
||||
"glPushDebugGroup", "glPopDebugGroup",
|
||||
// Others
|
||||
"glInitNames", "glPushName", "glPopName", "glLoadName", "isFramebufferEnabled", "gluUnProject", "gluErrorString");
|
||||
|
||||
private final Map<String, ClassNode> classes = new HashMap<>();
|
||||
private final Set<String> reaching = new HashSet<>();
|
||||
private final Set<String> notReaching = new HashSet<>();
|
||||
private Set<String> counted = RECORDING;
|
||||
|
||||
@Test
|
||||
void everyEntryPointRejectsOrRecordsTheExitCapture() {
|
||||
final Set<String> entryPoints = new HashSet<>(GLSMRedirector.getGLPrefixMethodRedirects().values());
|
||||
GLSMRedirector.getNamedClassMethodRedirects().values().forEach(redirects -> entryPoints.addAll(redirects.values()));
|
||||
final List<String> missing = missing(MANAGER, name -> name.startsWith("gl") || entryPoints.contains(name), method -> reachesCapture(MANAGER, method));
|
||||
assertTrue(missing.isEmpty(), "Entry points that neither reject nor record an exit capture:\n " + String.join("\n ", missing));
|
||||
}
|
||||
|
||||
// None of RenderSystem's writes are modeled. A method that drives the backend or DSA itself must reject itself, since
|
||||
// a GLSM call it also makes (glBindTexture) may only record.
|
||||
@Test
|
||||
void everyRenderSystemCallRejectsTheExitCapture() {
|
||||
counted = Set.of("reject");
|
||||
final List<String> missing = missing(RENDER_SYSTEM, name -> !name.equals("initRenderer") && !name.equals("getVRAM")
|
||||
&& !name.matches("(supports|has|is|getMax|getGl|parse|probe).*"), method -> reachesCapture(RENDER_SYSTEM, method)
|
||||
&& (!calls(method, owner -> owner.startsWith(PACKAGE + "backend/") || owner.startsWith(PACKAGE + "dsa/"))
|
||||
|| calls(method, owner -> owner.equals(MANAGER), "rejectExitCapture", "beforeUncapturedStateChange")));
|
||||
assertTrue(missing.isEmpty(), "RenderSystem methods that do not reject an exit capture:\n " + String.join("\n ", missing));
|
||||
}
|
||||
|
||||
private List<String> missing(String owner, Predicate<String> checked, Predicate<MethodNode> covered) {
|
||||
final List<String> missing = new ArrayList<>();
|
||||
for (MethodNode method : load(owner).methods) {
|
||||
if ((method.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC)) != (Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC)) continue;
|
||||
if ((method.access & Opcodes.ACC_SYNTHETIC) != 0) continue;
|
||||
if (!checked.test(method.name) || IGNORED.contains(method.name)) continue;
|
||||
if (!covered.test(method)) missing.add(method.name + method.desc);
|
||||
}
|
||||
missing.sort(null);
|
||||
return missing;
|
||||
}
|
||||
|
||||
private static boolean calls(MethodNode method, Predicate<String> owner, String... names) {
|
||||
final Set<String> wanted = Set.of(names);
|
||||
for (AbstractInsnNode insn : method.instructions) {
|
||||
if (insn instanceof MethodInsnNode call && owner.test(call.owner) && (wanted.isEmpty() || wanted.contains(call.name))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean reachesCapture(String owner, MethodNode root) {
|
||||
final String key = owner + '.' + root.name + root.desc;
|
||||
if (reaching.contains(key)) return true;
|
||||
if (notReaching.contains(key)) return false;
|
||||
final Set<String> visited = new HashSet<>();
|
||||
if (search(owner, root, visited)) {
|
||||
reaching.add(key);
|
||||
return true;
|
||||
}
|
||||
notReaching.addAll(visited);
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean search(String owner, MethodNode method, Set<String> visited) {
|
||||
final String key = owner + '.' + method.name + method.desc;
|
||||
if (reaching.contains(key)) return true;
|
||||
if (notReaching.contains(key) || !visited.add(key)) return false;
|
||||
for (AbstractInsnNode insn : method.instructions) {
|
||||
if (!(insn instanceof MethodInsnNode call)) continue;
|
||||
if (call.owner.equals(CAPTURE)) {
|
||||
if (counted.contains(call.name)) return true;
|
||||
continue;
|
||||
}
|
||||
if (!call.owner.startsWith(PACKAGE)) continue;
|
||||
final String[] declaring = new String[1];
|
||||
final MethodNode target = resolve(call.owner, call.name, call.desc, declaring);
|
||||
if (target != null && search(declaring[0], target, visited)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MethodNode resolve(String owner, String name, String desc, String[] declaring) {
|
||||
for (String type = owner; type != null && type.startsWith(PACKAGE); ) {
|
||||
final ClassNode node = load(type);
|
||||
for (MethodNode method : node.methods) {
|
||||
if (method.name.equals(name) && method.desc.equals(desc)) {
|
||||
if (method.instructions.size() == 0) return null;
|
||||
declaring[0] = type;
|
||||
return method;
|
||||
}
|
||||
}
|
||||
type = node.superName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ClassNode load(String internalName) {
|
||||
return classes.computeIfAbsent(internalName, name -> {
|
||||
try (InputStream in = GLSM_ExitCaptureCoverage_UnitTest.class.getClassLoader().getResourceAsStream(name + ".class")) {
|
||||
if (in == null) throw new IllegalStateException("Missing class file for " + name);
|
||||
final ClassNode node = new ClassNode();
|
||||
new ClassReader(in).accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
return node;
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,561 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector4f;
|
||||
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.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class GLSM_ExitState_GLTest {
|
||||
private final Matrix4f modelView = new Matrix4f();
|
||||
private final Matrix4f projection = new Matrix4f();
|
||||
private final Matrix4f texture = new Matrix4f();
|
||||
private final Vector4f overlay = new Vector4f();
|
||||
private final Vector4f shaderColor = new Vector4f();
|
||||
private int outer;
|
||||
private int clientActiveUnit;
|
||||
private float lightmapX, lightmapY;
|
||||
private ImmediateExtendedAttribHandler extendedHandler;
|
||||
private DeferredBlendHandler blendHandler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
final GLContextState context = GLStateManager.ctx();
|
||||
modelView.set(context.modelViewMatrix);
|
||||
projection.set(context.projectionMatrix);
|
||||
texture.set(context.textures.getTextureUnitMatrix(0));
|
||||
overlay.set(context.overlayR, context.overlayG, context.overlayB, context.overlayA);
|
||||
shaderColor.set(context.shaderColorR, context.shaderColorG, context.shaderColorB, context.shaderColorA);
|
||||
lightmapX = context.lastBrightnessX;
|
||||
lightmapY = context.lastBrightnessY;
|
||||
extendedHandler = GLSMHooks.immediateExtendedHandler;
|
||||
blendHandler = GLSMHooks.blendHandler;
|
||||
clientActiveUnit = context.clientActiveTextureUnit;
|
||||
outer = GLStateManager.pushState(StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.setTextureMatrix(0, new Matrix4f());
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE);
|
||||
GLStateManager.disableColorMaterial();
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(2, 3);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.cancelExitCapture();
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + clientActiveUnit);
|
||||
GLSMHooks.blendHandler = blendHandler;
|
||||
GLSMHooks.immediateExtendedHandler = extendedHandler;
|
||||
GLStateManager.getBlendMode().setVanillaLayer(null);
|
||||
GLStateManager.getBlendState().setVanillaLayer(null);
|
||||
GLStateManager.setModelViewMatrix(modelView);
|
||||
GLStateManager.setProjectionMatrix(projection);
|
||||
GLStateManager.setTextureMatrix(0, texture);
|
||||
GLStateManager.setOverlayColor(overlay.x, overlay.y, overlay.z, overlay.w);
|
||||
GLStateManager.setShaderColor(shaderColor.x, shaderColor.y, shaderColor.z, shaderColor.w);
|
||||
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, lightmapX, lightmapY);
|
||||
GLStateManager.popStateTo(outer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unchangedWritesStillApplyAndLeaveOtherPolygonStateAlone() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(7, 11);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertTrue(GLStateManager.getCullState().isEnabled());
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode());
|
||||
assertTrue(GLStateManager.getPolygonOffsetFillState().isEnabled());
|
||||
assertEquals(7, GLStateManager.getPolygonState().getOffsetFactor());
|
||||
assertEquals(11, GLStateManager.getPolygonState().getOffsetUnits());
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribPopUndoesTheWritesItRestores() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
assertEquals(ExitState.POLYGON_OFFSET_ENABLE_WRITTEN, GLStateManager.getExitPolygonOffsetWrites());
|
||||
GLStateManager.glPushAttrib(GL11.GL_POLYGON_BIT | GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glPolygonOffset(5, 6);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
assertEquals(3, GLStateManager.getExitPolygonOffsetWrites());
|
||||
GLStateManager.glPopAttrib();
|
||||
assertEquals(ExitState.POLYGON_OFFSET_ENABLE_WRITTEN, GLStateManager.getExitPolygonOffsetWrites());
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glColor4f(0.25F, 0.5F, 0.75F, 1);
|
||||
GLStateManager.glPolygonOffset(8, 9);
|
||||
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertFalse(GLStateManager.getCullState().isEnabled());
|
||||
assertEquals(0.25F, GLStateManager.getColor().getRed());
|
||||
assertEquals(8, GLStateManager.getPolygonState().getOffsetFactor());
|
||||
assertTrue(GLStateManager.getPolygonOffsetFillState().isEnabled());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void drawsDependOnTheAlphaFunctionOnlyWhenTheyReadItFromState(boolean materialFromState) {
|
||||
GLStateManager.enableAlphaTest();
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
GLStateManager.requireExitDrawState(materialFromState);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.6F);
|
||||
assertEquals(!materialFromState, GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.disableCull();
|
||||
assertFalse(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2})
|
||||
void blendAndAlphaFunctionsMatterOnlyToDrawsWithThemEnabled(int mode) {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.requireExitDrawState(true);
|
||||
if (mode != 0) {
|
||||
if (mode == 2) {
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
}
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.requireExitDrawState(true);
|
||||
}
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertEquals(mode != 1, GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5F);
|
||||
assertEquals(mode != 1, GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void settingTheClientTextureUnitToItsCurrentValueIsStillRecorded() {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushAttrib(GL11.GL_TEXTURE_BIT);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(0, GLStateManager.getClientActiveTextureUnit());
|
||||
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawAfterAnAttribPopDependsOnTheOriginalCullState() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.disableCull();
|
||||
assertFalse(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightingAndShaderStateAreNotDrawDependencies() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertFalse(GLStateManager.isExitLightingWritten());
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
GLStateManager.enableLighting();
|
||||
assertTrue(GLStateManager.isExitLightingWritten());
|
||||
GLStateManager.glPopAttrib();
|
||||
assertFalse(GLStateManager.isExitLightingWritten());
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.getLightingState().setEnabled(!GLStateManager.getLightingState().isEnabled());
|
||||
GLStateManager.getNormalizeState().setEnabled(!GLStateManager.getNormalizeState().isEnabled());
|
||||
GLStateManager.getRescaleNormalState().setEnabled(!GLStateManager.getRescaleNormalState().isEnabled());
|
||||
GLStateManager.getLightStates()[0].setEnabled(!GLStateManager.getLightStates()[0].isEnabled());
|
||||
GLStateManager.getLightStates()[1].setEnabled(!GLStateManager.getLightStates()[1].isEnabled());
|
||||
GLStateManager.glShadeModel(GLStateManager.getShadeModelState().getValue() == GL11.GL_FLAT ? GL11.GL_SMOOTH : GL11.GL_FLAT);
|
||||
GLStateManager.setShaderColor(0.1F, 0.2F, 0.3F, 0.4F);
|
||||
GLStateManager.glNormal3f(0.5F, 0.25F, 0.75F);
|
||||
GLStateManager.glTexCoord2f(0.75F, 0.25F);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2, 3})
|
||||
void tessellatorDrawsDependOnlyOnTheCurrentValuesTheyFillIn(int mode) {
|
||||
final Tessellator tess = new Tessellator();
|
||||
tess.rawBuffer = new int[32];
|
||||
tess.vertexCount = 4;
|
||||
tess.drawMode = GL11.GL_QUADS;
|
||||
tess.hasNormals = mode != 1 && mode != 3;
|
||||
tess.hasTexture = mode != 2;
|
||||
tess.hasColor = tess.hasBrightness = true;
|
||||
GLSMHooks.immediateExtendedHandler = mode == 3 ? new FaceNormals() : null;
|
||||
GLStateManager.glNormal3f(0, 1, 0);
|
||||
GLStateManager.glTexCoord2f(0.25F, 0.5F);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
TessellatorStreamingDrawer.fillMissingAttributes(tess);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
if (mode == 1) assertEquals(0x7F00, tess.rawBuffer[6]);
|
||||
if (mode == 2) assertEquals(Float.floatToRawIntBits(0.25F), tess.rawBuffer[3]);
|
||||
if (mode == 3) assertEquals(0x7F, tess.rawBuffer[6]);
|
||||
GLStateManager.glNormal3f(1, 0, 0);
|
||||
GLStateManager.glTexCoord2f(0.75F, 0.5F);
|
||||
assertEquals(mode == 0 || mode == 3, GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void deletingTheBoundTextureBlocksApplyingEvenIfTheCaptureUnboundIt(boolean explicitlyUnbound) {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
if (explicitlyUnbound) GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.glDeleteTextures(id);
|
||||
assertFalse(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void textureMatrixMustMatchOnlyWhenTheCaptureMultipliedOntoIt(boolean absolute) {
|
||||
GLStateManager.setTextureMatrix(0, new Matrix4f().translation(1, 2, 0));
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glPushMatrix();
|
||||
if (absolute) GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(3, 4, 0);
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
final Matrix4f mainTexture = new Matrix4f().translation(8, 9, 0);
|
||||
GLStateManager.setTextureMatrix(0, mainTexture);
|
||||
assertEquals(absolute, GLStateManager.canApplyExitState(state));
|
||||
if (absolute) {
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(mainTexture, GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void changedOrUnbalancedViewMatricesReject() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glTranslatef(1, 0, 0);
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.setModelViewMatrix(modelView);
|
||||
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushMatrix();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(2, 3, 4);
|
||||
GLStateManager.glPopMatrix();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glPopMatrix();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2, 3, 4, 5})
|
||||
void untrackedCallsAndStateQueriesReject(int operation) {
|
||||
final ExitState state = new ExitState();
|
||||
final int program = GLStateManager.getActiveProgram();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
switch (operation) {
|
||||
case 0 -> GLStateManager.glIsEnabled(GL11.GL_CULL_FACE);
|
||||
case 1 -> GLStateManager.glGetInteger(GL11.GL_DEPTH_FUNC);
|
||||
case 2 -> GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
case 3 -> GLStateManager.glUseProgram(program);
|
||||
case 4 -> GLStateManager.glBlendEquation(GL14.GL_FUNC_ADD);
|
||||
case 5 -> GLStateManager.fogColor((float) GLStateManager.getFogState().getFogColor().x,
|
||||
(float) GLStateManager.getFogState().getFogColor().y, (float) GLStateManager.getFogState().getFogColor().z,
|
||||
GLStateManager.getFogState().getFogAlpha());
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void suspendedCallsAreNeitherRecordedNorRejected() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
assertFalse(GLStateManager.isExitCaptureActive());
|
||||
GLStateManager.glIsEnabled(GL11.GL_CULL_FACE);
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
assertFalse(GLStateManager.isExitCaptureActive());
|
||||
GLStateManager.glGetInteger(GL11.GL_DEPTH_FUNC);
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
assertTrue(GLStateManager.isExitCaptureActive());
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glColor4f(0.5F, 1, 1, 1);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(0.5F, GLStateManager.getColor().getRed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribPushedWhileSuspendedRestoresThatCapturesWrites() {
|
||||
final ExitState earlier = new ExitState();
|
||||
GLStateManager.beginExitCapture(earlier);
|
||||
GLStateManager.glColor4f(0.5F, 1, 1, 1);
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertTrue(GLStateManager.finishExitCapture(earlier));
|
||||
|
||||
GLStateManager.glColor4f(0.25F, 1, 1, 1);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.glColor4f(0.75F, 1, 1, 1);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(0.75F, GLStateManager.getColor().getRed(), "the pop restored color, so the capture leaves it alone");
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribPoppedWhileSuspendedUndoesTheCapturesWrites() {
|
||||
GLStateManager.glColor4f(0.25F, 1, 1, 1);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
GLStateManager.glPopAttrib();
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.glColor4f(0.75F, 1, 1, 1);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(0.75F, GLStateManager.getColor().getRed(), "the pop restored color, so the capture leaves it alone");
|
||||
}
|
||||
|
||||
@Test
|
||||
void separateAndReusedCapturesKeepOnlyTheirOwnWrites() {
|
||||
final ExitState first = new ExitState();
|
||||
final ExitState second = new ExitState();
|
||||
GLStateManager.beginExitCapture(first);
|
||||
GLStateManager.glColor4f(0.25F, 1, 1, 1);
|
||||
GLStateManager.setOverlayColor(0.1F, 0.2F, 0.3F, 0.4F);
|
||||
assertTrue(GLStateManager.finishExitCapture(first));
|
||||
GLStateManager.beginExitCapture(second);
|
||||
GLStateManager.glColor4f(0.75F, 1, 1, 1);
|
||||
assertTrue(GLStateManager.finishExitCapture(second));
|
||||
GLStateManager.applyExitState(first);
|
||||
assertEquals(0.25F, GLStateManager.getColor().getRed());
|
||||
assertEquals(0.4F, GLStateManager.getOverlayA());
|
||||
GLStateManager.applyExitState(second);
|
||||
assertEquals(0.75F, GLStateManager.getColor().getRed());
|
||||
first.clear();
|
||||
assertFalse(GLStateManager.canApplyExitState(first));
|
||||
GLStateManager.beginExitCapture(first);
|
||||
assertTrue(GLStateManager.finishExitCapture(first));
|
||||
GLStateManager.glColor4f(0.5F, 1, 1, 1);
|
||||
GLStateManager.applyExitState(first);
|
||||
assertEquals(0.5F, GLStateManager.getColor().getRed());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void toggledColorMaterialKeepsItsMaterialWritesUnlessAnAttribPopUndoesThem(boolean scoped) {
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.disableColorMaterial();
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
if (scoped) GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.disableColorMaterial();
|
||||
if (scoped) GLStateManager.glPopAttrib();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
final var changed = BufferUtils.createFloatBuffer(4);
|
||||
changed.put(new float[]{0.2F, 0.3F, 0.4F, 1}).flip();
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, changed);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(scoped ? 0.2F : 1, GLStateManager.getFrontMaterial().ambient.x);
|
||||
assertEquals(scoped ? 0.2F : 1, GLStateManager.getBackMaterial().diffuse.x);
|
||||
assertFalse(GLStateManager.getColorMaterial().isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorMaterialSettingsTheCaptureNeverTouchedAreLeftAlone() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glColorMaterial(GL11.GL_BACK, GL11.GL_EMISSION);
|
||||
GLStateManager.enableColorMaterial();
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertTrue(GLStateManager.getColorMaterial().isEnabled());
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getColorMaterialFace().getValue());
|
||||
assertEquals(GL11.GL_EMISSION, GLStateManager.getColorMaterialParameter().getValue());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void colorMaterialFaceMustMatchOnlyWhenTheCaptureDidNotSetIt(boolean establishesContext) {
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
if (establishesContext) GLStateManager.glColorMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE);
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.disableColorMaterial();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glColorMaterial(GL11.GL_BACK, GL11.GL_EMISSION);
|
||||
assertEquals(establishesContext, GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void writesUnderALockedIrisOverrideRecordAndApplyTheVanillaValues() {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.getBlendMode().beforeModify();
|
||||
GLStateManager.getBlendState().beforeModify();
|
||||
final LockedBlend lock = new LockedBlend();
|
||||
GLSMHooks.blendHandler = lock;
|
||||
GLStateManager.getBlendMode().setVanillaLayer(lock);
|
||||
GLStateManager.getBlendState().setVanillaLayer(lock);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
lock.enabled = false;
|
||||
lock.blend.setAll(GL11.GL_DST_COLOR, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertTrue(lock.enabled);
|
||||
assertEquals(GL11.GL_SRC_ALPHA, lock.blend.getSrcRgb());
|
||||
assertFalse(GLStateManager.getBlendMode().isEnabled());
|
||||
assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb());
|
||||
assertEquals(GL11.GL_ZERO, GLStateManager.getBlendState().getDstRgb());
|
||||
}
|
||||
|
||||
private static final class LockedBlend implements DeferredBlendHandler, VanillaBooleanLayer, VanillaStateLayer<BlendState> {
|
||||
boolean enabled = true;
|
||||
final BlendState blend = new BlendState(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
@Override public boolean isBlendLocked() { return true; }
|
||||
@Override public boolean isOverrideHeld() { return true; }
|
||||
@Override public void deferBlendModeToggle(boolean value) { enabled = value; }
|
||||
@Override public void deferBlendFunc(int sr, int dr, int sa, int da) { blend.setAll(sr, dr, sa, da); }
|
||||
@Override public void flushDeferredBlend() {}
|
||||
@Override public boolean getVanilla() { return enabled; }
|
||||
@Override public void setVanilla(boolean value) { enabled = value; }
|
||||
@Override public void readVanilla(BlendState into) { into.set(blend); }
|
||||
@Override public void writeVanilla(BlendState from) { blend.set(from); }
|
||||
}
|
||||
|
||||
private static final class FaceNormals implements ImmediateExtendedAttribHandler {
|
||||
@Override public boolean wantsExtended() { return false; }
|
||||
@Override public boolean wantsTangent() { return false; }
|
||||
@Override public boolean wantsEntityIds() { return false; }
|
||||
@Override public boolean wantsExtendedCapture() { return false; }
|
||||
@Override public boolean wantsFaceNormals() { return true; }
|
||||
@Override public void writeFaceNormals(int[] data, int count) {
|
||||
for (int i = 0; i < count; i++) data[i * 8 + 6] = 0x7F;
|
||||
}
|
||||
@Override public void build(int[] data, int count, int primitive, int normal, long destination, int stride) {}
|
||||
@Override public void buildPacked(long source, int stride, int position, int texture, int normal,
|
||||
int count, int primitive, long destination, int destinationStride) {}
|
||||
}
|
||||
}
|
||||
-208
@@ -1,208 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.TextureUtilHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
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.opengl.EXTTextureFilterAnisotropic;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class GLSM_ExitTextureFilter_GLTest {
|
||||
private int outer, texture, otherTexture, savedMin, savedMag;
|
||||
private float savedAniso;
|
||||
private boolean failOperation;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
outer = GLStateManager.pushState(StateSet.forMask(GL11.GL_TEXTURE_BIT));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
texture = GLStateManager.glGenTextures();
|
||||
otherTexture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, otherTexture);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.cancelExitCapture();
|
||||
TextureUtilHooks.clearSavedFilterTexture();
|
||||
GLStateManager.glDeleteTextures(texture);
|
||||
GLStateManager.glDeleteTextures(otherTexture);
|
||||
GLStateManager.popStateTo(outer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nearestFilterSaveAndRestoreIsAllowedAndApplyLeavesFiltersAlone() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(save(false, false, 1));
|
||||
assertTrue(GLStateManager.isExitCaptureActive());
|
||||
GLStateManager.requireExitTextureFilter(otherTexture, false);
|
||||
GLStateManager.requireExitTextureFilter(texture, true);
|
||||
restore();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
assertFilters(GL11.GL_LINEAR_MIPMAP_LINEAR, GL11.GL_LINEAR);
|
||||
assertEquals(0, GLStateManager.ctx().savedFilterTextureId);
|
||||
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertFilters(GL11.GL_LINEAR, GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void drawingTheTemporarilyFilteredTextureRequiresNearestFiltering(boolean nearest) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
save(false, false, 1);
|
||||
GLStateManager.requireExitTextureFilter(texture, nearest);
|
||||
restore();
|
||||
assertEquals(nearest, GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2})
|
||||
void savingAnythingButNearestFilteringRejects(int request) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertFalse(save(request == 0, request == 1, request == 2 ? 2 : 1));
|
||||
restore();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2})
|
||||
void textureParameterReadsAndWritesStillReject(int operation) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
save(false, false, 1);
|
||||
switch (operation) {
|
||||
case 0 -> GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
case 1 -> GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
case 2 -> GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
restore();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingRestoreRejectsAndDoesNotAffectTheNextCapture() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
save(false, false, 1);
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
restore();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondSaveBeforeRestoreRejects() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(save(false, false, 1));
|
||||
assertFalse(save(false, false, 1));
|
||||
restore();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void restoreMustUseTheSameTextureAndOriginalParameters(boolean wrongTexture) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
save(false, false, 1);
|
||||
if (wrongTexture) GLStateManager.ctx().savedFilterTextureId = otherTexture;
|
||||
else savedMin = GL11.GL_NEAREST;
|
||||
restore();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void failedSaveOrRestoreRejectsAndTheNextCaptureStillWorks(boolean restoring) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
if (restoring) save(false, false, 1);
|
||||
failOperation = true;
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
if (restoring) restore();
|
||||
else save(false, false, 1);
|
||||
});
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
TextureUtilHooks.clearSavedFilterTexture();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(GLStateManager.isExitCaptureActive());
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void openFilterSaveBlocksCapturingAndApplying() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
save(false, false, 1);
|
||||
assertFalse(GLStateManager.canApplyExitState(state));
|
||||
final ExitState nested = new ExitState();
|
||||
GLStateManager.beginExitCapture(nested);
|
||||
assertFalse(GLStateManager.finishExitCapture(nested));
|
||||
restore();
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
private boolean save(boolean blurred, boolean mipmap, float aniso) {
|
||||
final boolean captured = TextureUtilHooks.beginFilterSave(blurred, mipmap, aniso);
|
||||
try {
|
||||
savedMin = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
savedMag = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER);
|
||||
savedAniso = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
|
||||
blurred ? (mipmap ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_LINEAR)
|
||||
: (mipmap ? GL11.GL_NEAREST_MIPMAP_LINEAR : GL11.GL_NEAREST));
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, blurred ? GL11.GL_LINEAR : GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
if (failOperation) throw new IllegalStateException("filter save failed");
|
||||
} catch (RuntimeException | Error failure) {
|
||||
TextureUtilHooks.filterFailed(captured);
|
||||
throw failure;
|
||||
} finally {
|
||||
TextureUtilHooks.endFilterSave(captured);
|
||||
}
|
||||
return captured;
|
||||
}
|
||||
|
||||
private void restore() {
|
||||
final boolean captured = TextureUtilHooks.beginFilterRestore();
|
||||
try {
|
||||
TextureUtilHooks.restoreFilter(savedMin, savedMag);
|
||||
TextureUtilHooks.restoreAniso(savedAniso);
|
||||
if (failOperation) throw new IllegalStateException("filter restore failed");
|
||||
} catch (RuntimeException | Error failure) {
|
||||
TextureUtilHooks.filterFailed(captured);
|
||||
throw failure;
|
||||
} finally {
|
||||
TextureUtilHooks.endFilterRestore(captured);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFilters(int min, int mag) {
|
||||
final var info = TextureInfoCache.INSTANCE.getInfo(texture);
|
||||
assertEquals(min, info.getMinFilter());
|
||||
assertEquals(mag, info.getMagFilter());
|
||||
}
|
||||
}
|
||||
@@ -179,10 +179,6 @@ public final class FfpFixture {
|
||||
}
|
||||
|
||||
public static void assertPixelParity(String subject, int[] reference, int[] candidate, int size, int background, int minCoverageDivisor, int channelTolerance, IntFunction<String> describe) {
|
||||
assertPixelParity(subject, reference, candidate, size, background, minCoverageDivisor, channelTolerance, 0, describe);
|
||||
}
|
||||
|
||||
public static void assertPixelParity(String subject, int[] reference, int[] candidate, int size, int background, int minCoverageDivisor, int channelTolerance, int allowedMismatches, IntFunction<String> describe) {
|
||||
int covered = 0;
|
||||
for (int pixel : reference) {
|
||||
if (pixel != background) covered++;
|
||||
@@ -200,7 +196,7 @@ public final class FfpFixture {
|
||||
}
|
||||
final int index = firstMismatch;
|
||||
final int differing = mismatches;
|
||||
assertTrue(mismatches <= allowedMismatches, () -> subject + " differ from baked quads in " + differing + " of " + reference.length + " pixels, first at (" + (index % size) + "," + (index / size) + "): reference " + describe.apply(reference[index]) + " instanced " + describe.apply(candidate[index]));
|
||||
assertEquals(0, mismatches, () -> subject + " differ from baked quads in " + differing + " of " + reference.length + " pixels, first at (" + (index % size) + "," + (index / size) + "): reference " + describe.apply(reference[index]) + " instanced " + describe.apply(candidate[index]));
|
||||
}
|
||||
|
||||
private static boolean withinTolerance(int a, int b, int tolerance) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
The menu blur in this package is based on [Blur](https://github.com/tterrag1098/Blur), used under the following license:
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 tterrag
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.gtnewhorizons.angelica.blur;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.rendering.GlUniformFloat2v;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.render.PanoramaRenderer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.GlProgram;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderBindingContext;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloat;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Blurs the world behind open GUIs and fades in a configurable background gradient.
|
||||
* Based on Blur by tterrag (https://github.com/tterrag1098/Blur) under the MIT license, see LICENSE-Blur.md.
|
||||
* Reimplemented on top of Angelica's own blur shader instead of a vanilla post-processing shader.
|
||||
*/
|
||||
public class MenuBlur {
|
||||
|
||||
private static final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
private static GlProgram<BlurUniforms> blurProgram;
|
||||
private static StateSet allState;
|
||||
private static int emptyVao;
|
||||
// The blur runs on half resolution copies of the screen, which is much cheaper and looks the same once blurred
|
||||
private static int horizontalFbo, horizontalTexture, verticalFbo, verticalTexture;
|
||||
private static int sourceWidth, sourceHeight, halfWidth, halfHeight;
|
||||
|
||||
private static boolean blurring;
|
||||
private static long fadeStartMillis;
|
||||
|
||||
/** Called right after the world has been drawn, before the HUD and the open GUI. */
|
||||
public static void renderBlur() {
|
||||
final boolean shouldBlur = shouldBlur();
|
||||
if (shouldBlur && !blurring) {
|
||||
fadeStartMillis = System.currentTimeMillis();
|
||||
}
|
||||
blurring = shouldBlur;
|
||||
if (!blurring) return;
|
||||
|
||||
// The radius grows with the fade, so the blur gets stronger over the first moments
|
||||
final float radius = (float) Math.floor(AngelicaConfig.menuBlurRadius * getProgress());
|
||||
if (radius < 1.0F) return;
|
||||
|
||||
final Framebuffer mainFramebuffer = mc.getFramebuffer();
|
||||
if (blurProgram == null) init();
|
||||
|
||||
final int previousProgram = GLStateManager.getActiveProgram();
|
||||
final int previousVao = GLStateManager.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING);
|
||||
final int previousFilter = mainFramebuffer.framebufferFilter;
|
||||
final int stateDepth = GLStateManager.pushState(allState);
|
||||
try {
|
||||
ensureBlurTargets(mainFramebuffer.framebufferWidth, mainFramebuffer.framebufferHeight);
|
||||
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.glDepthMask(false);
|
||||
GLStateManager.glColorMask(true, true, true, true);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.disableScissorTest();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
blurProgram.bind();
|
||||
GLStateManager.glBindVertexArray(emptyVao);
|
||||
final BlurUniforms uniforms = blurProgram.getInterface();
|
||||
// Each half resolution pixel covers two screen pixels, so half the radius gives the same strength
|
||||
uniforms.radius.setFloat(radius * 0.5F);
|
||||
|
||||
// Horizontal pass, shrinking the screen to half size. Linear filtering makes every
|
||||
// half size pixel average the four screen pixels it covers instead of picking one.
|
||||
mainFramebuffer.setFramebufferFilter(GL11.GL_LINEAR);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, horizontalFbo);
|
||||
GLStateManager.glViewport(0, 0, halfWidth, halfHeight);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, mainFramebuffer.framebufferTexture);
|
||||
uniforms.direction.set(1.0F / halfWidth, 0.0F);
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
|
||||
// Vertical pass, still at half size
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, verticalFbo);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, horizontalTexture);
|
||||
uniforms.direction.set(0.0F, 1.0F / halfHeight);
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
|
||||
// Stretch the result back over the screen. A radius of 0 makes the blur shader a plain copy.
|
||||
mainFramebuffer.bindFramebuffer(true);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, verticalTexture);
|
||||
uniforms.radius.setFloat(0.0F);
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
} finally {
|
||||
mainFramebuffer.setFramebufferFilter(previousFilter);
|
||||
GLStateManager.glBindVertexArray(previousVao);
|
||||
GLStateManager.glUseProgram(previousProgram);
|
||||
GLStateManager.popStateTo(stateDepth);
|
||||
mainFramebuffer.bindFramebuffer(true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Replaces one of the two vanilla GUI background gradient colors while the blur is showing. */
|
||||
public static int getBackgroundColor(int vanillaColor, String configuredColor) {
|
||||
if (!blurring) return vanillaColor;
|
||||
|
||||
final int color;
|
||||
try {
|
||||
color = Integer.parseUnsignedInt(configuredColor, 16);
|
||||
} catch (NumberFormatException e) {
|
||||
return vanillaColor;
|
||||
}
|
||||
|
||||
final float progress = getProgress();
|
||||
final int alpha = (int) ((color >>> 24) * progress);
|
||||
final int red = (int) ((color >> 16 & 0xFF) * progress);
|
||||
final int green = (int) ((color >> 8 & 0xFF) * progress);
|
||||
final int blue = (int) ((color & 0xFF) * progress);
|
||||
return alpha << 24 | red << 16 | green << 8 | blue;
|
||||
}
|
||||
|
||||
private static boolean shouldBlur() {
|
||||
return AngelicaConfig.enableMenuBlur
|
||||
&& mc.theWorld != null
|
||||
&& mc.currentScreen != null
|
||||
&& !ArrayUtils.contains(AngelicaConfig.menuBlurExcludedGuis, mc.currentScreen.getClass().getName());
|
||||
}
|
||||
|
||||
private static float getProgress() {
|
||||
if (AngelicaConfig.menuBlurFadeTime <= 0) return 1.0F;
|
||||
return Math.min((System.currentTimeMillis() - fadeStartMillis) / (float) AngelicaConfig.menuBlurFadeTime, 1.0F);
|
||||
}
|
||||
|
||||
private static void init() {
|
||||
blurProgram = PanoramaRenderer.loadProgram("angelica:menu_blur", "angelica:panorama_blur", "angelica:panorama_blur", BlurUniforms::new);
|
||||
allState = StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS);
|
||||
emptyVao = GLStateManager.glGenVertexArrays();
|
||||
}
|
||||
|
||||
private static void ensureBlurTargets(int width, int height) {
|
||||
if (horizontalFbo != 0 && width == sourceWidth && height == sourceHeight) return;
|
||||
|
||||
if (horizontalFbo != 0) {
|
||||
GLStateManager.glDeleteFramebuffers(horizontalFbo);
|
||||
GLStateManager.glDeleteFramebuffers(verticalFbo);
|
||||
GLStateManager.glDeleteTextures(horizontalTexture);
|
||||
GLStateManager.glDeleteTextures(verticalTexture);
|
||||
}
|
||||
sourceWidth = width;
|
||||
sourceHeight = height;
|
||||
halfWidth = Math.max((width + 1) / 2, 1);
|
||||
halfHeight = Math.max((height + 1) / 2, 1);
|
||||
|
||||
horizontalTexture = createTexture(halfWidth, halfHeight);
|
||||
horizontalFbo = createFramebuffer(horizontalTexture);
|
||||
verticalTexture = createTexture(halfWidth, halfHeight);
|
||||
verticalFbo = createFramebuffer(verticalTexture);
|
||||
}
|
||||
|
||||
private static int createTexture(int width, int height) {
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static int createFramebuffer(int colorTexture) {
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, colorTexture, 0);
|
||||
final int status = GLStateManager.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER);
|
||||
if (status != GL30.GL_FRAMEBUFFER_COMPLETE) {
|
||||
throw new RuntimeException("Menu blur FBO incomplete: 0x" + Integer.toHexString(status));
|
||||
}
|
||||
return fbo;
|
||||
}
|
||||
|
||||
private static class BlurUniforms {
|
||||
final GlUniformFloat2v direction;
|
||||
final GlUniformFloat radius;
|
||||
|
||||
BlurUniforms(ShaderBindingContext ctx) {
|
||||
direction = ctx.bindUniform("u_Direction", GlUniformFloat2v::new);
|
||||
radius = ctx.bindUniform("u_Radius", GlUniformFloat::new);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchDrawDefaults;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
@@ -1252,7 +1251,6 @@ public class BatchingFontRenderer {
|
||||
if (string == null || string.length() == 0) {
|
||||
return anchorX + (enableShadow ? 1.0f : 0.0f);
|
||||
}
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
final int color = buttonSectionActive
|
||||
? DarkModeUtils.recolorButtonText(inputColor, buttonEnabledColor, buttonHoveredColor, buttonDisabledColor)
|
||||
: inputColor;
|
||||
|
||||
@@ -167,20 +167,6 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableEntityTextureAtlas;
|
||||
|
||||
@Config.Comment("Replay entity draws recorded in the shader shadow pass in the main view instead of running each entity renderer again (experimental, requires entity batching)")
|
||||
@Config.DefaultBoolean(false)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableEntityShadowReplay;
|
||||
|
||||
@Config.Comment("Entity classes that run their renderer again in the main view instead of replaying shadow pass draws. Players are always excluded.")
|
||||
@Config.DefaultStringList({
|
||||
"chylex.hee.entity.boss.EntityBossDragon",
|
||||
"twilightforest.entity.passive.EntityTFMobileFirefly",
|
||||
"gregtech.common.entity.EntityDrone"
|
||||
})
|
||||
@Config.RequiresMcRestart
|
||||
public static String[] entityShadowReplayExcludedClasses;
|
||||
|
||||
@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
|
||||
@@ -290,6 +276,32 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableZoom;
|
||||
|
||||
@Config.Comment("Blur the world behind open GUIs.")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean enableMenuBlur;
|
||||
|
||||
@Config.Comment("How strong the menu blur is, in pixels.")
|
||||
@Config.DefaultInt(12)
|
||||
@Config.RangeInt(min = 1, max = 100)
|
||||
public static int menuBlurRadius;
|
||||
|
||||
@Config.Comment("How long the menu blur takes to fade in, in milliseconds.")
|
||||
@Config.DefaultInt(200)
|
||||
@Config.RangeInt(min = 0, max = 2000)
|
||||
public static int menuBlurFadeTime;
|
||||
|
||||
@Config.Comment("GUI classes that never get the menu blur.")
|
||||
@Config.DefaultStringList({"net.minecraft.client.gui.GuiChat", "net.coderbot.iris.gui.screen.ShaderPackScreen"})
|
||||
public static String[] menuBlurExcludedGuis;
|
||||
|
||||
@Config.Comment("Top color of the GUI background gradient while the menu blur is showing, as ARGB hex.")
|
||||
@Config.DefaultString("75000000")
|
||||
public static String menuBlurGradientTopColor;
|
||||
|
||||
@Config.Comment("Bottom color of the GUI background gradient while the menu blur is showing, as ARGB hex.")
|
||||
@Config.DefaultString("75000000")
|
||||
public static String menuBlurGradientBottomColor;
|
||||
|
||||
@Config.Comment("Optimizes in-world item rendering")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
@@ -562,10 +574,6 @@ public class AngelicaConfig {
|
||||
return enableEntityBatching && enableCubeInstancing;
|
||||
}
|
||||
|
||||
public static boolean entityShadowReplayEnabled() {
|
||||
return enableEntityBatching && enableEntityShadowReplay;
|
||||
}
|
||||
|
||||
public static boolean sdlGpuConfigured() {
|
||||
return renderBackend == RenderBackendChoice.SDL_GPU;
|
||||
}
|
||||
|
||||
@@ -67,15 +67,8 @@ public class IrisGLSMBridge {
|
||||
public static void installImmediateExtendedHandler() {
|
||||
GLSMHooks.immediateExtendedHandler = new ImmediateExtendedAttribs();
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
state.setEntityIdsListener(() -> {
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
GLStateManager.setEntityIds(
|
||||
state.getCurrentRenderedEntity(), state.getCurrentRenderedBlockEntity(), state.getCurrentRenderedItem());
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
});
|
||||
state.setEntityIdsListener(() -> GLStateManager.setEntityIds(
|
||||
state.getCurrentRenderedEntity(), state.getCurrentRenderedBlockEntity(), state.getCurrentRenderedItem()));
|
||||
}
|
||||
|
||||
public static void installPostTransformHook() {
|
||||
@@ -168,11 +161,6 @@ public class IrisGLSMBridge {
|
||||
return Iris.enabled && AlphaTestStorage.isAlphaTestLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverrideHeld() {
|
||||
return Iris.enabled && AlphaTestStorage.isOverrideHeld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deferAlphaTestToggle(boolean enabled) {
|
||||
return AlphaTestStorage.deferAlphaTestToggle(enabled);
|
||||
|
||||
@@ -207,15 +207,6 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_ENTITY_SHADOW_REPLAY(new MixinBuilder("Replay entity draws recorded in the shader shadow pass in the main view instead of running each entity renderer again")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris && AngelicaConfig.entityShadowReplayEnabled())
|
||||
.addClientMixins(
|
||||
"angelica.entity.MixinBossStatus_EntityReplay",
|
||||
"angelica.entity.MixinRenderGlobal_EntityReplay"
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_CUBE_INSTANCING(new MixinBuilder("Capture cuboid parameters so model cubes can be drawn from a shared unit cube")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(AngelicaConfig::cubeInstancingEnabled)
|
||||
@@ -626,6 +617,14 @@ public enum Mixins implements IMixins {
|
||||
"angelica.zoom.MixinMouseFilter"
|
||||
)),
|
||||
|
||||
ANGELICA_MENU_BLUR(new MixinBuilder("Blur the world behind open GUIs")
|
||||
.setPhase(Phase.EARLY)
|
||||
.addExcludedMod(TargetedMod.BLUR)
|
||||
.addClientMixins(
|
||||
"angelica.blur.MixinEntityRenderer_MenuBlur",
|
||||
"angelica.blur.MixinGuiScreen_MenuBlur"
|
||||
)),
|
||||
|
||||
ANGELICA_FPS_REDUCER(new MixinBuilder("Reduce frame rate and volume while unfocused, minimized, or idle")
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins(
|
||||
|
||||
@@ -9,6 +9,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
|
||||
ARCHAICFIX("org.embeddedt.archaicfix.ArchaicCore", "archaicfix"),
|
||||
BACKHAND("xonin.backhand.coremod.BackhandLoadingPlugin", "backhand"),
|
||||
BLUR("com.tterrag.blur.BlurPlugin", "blur"),
|
||||
CAMPFIRE_BACKPORT(null, "campfirebackport"),
|
||||
CHISEL(null, "chisel"),
|
||||
COFHCORE( "cofh.asm.LoadingPlugin", "CoFHCore"),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
@@ -9,8 +8,6 @@ import net.coderbot.iris.uniforms.EntityIdHelper;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -40,7 +37,6 @@ public class DeferredEntityOverlay {
|
||||
private static final List<DeferredEntry> pool = new ArrayList<>(INITIAL_CAPACITY);
|
||||
|
||||
private static final FloatBuffer MATRIX_BUF = BufferUtils.createFloatBuffer(16);
|
||||
private static final Matrix4f replayMatrix = new Matrix4f();
|
||||
|
||||
// Set by the shouldRenderPass inject, consumed by the doRender WrapOperation.
|
||||
@Getter
|
||||
@@ -72,9 +68,6 @@ public class DeferredEntityOverlay {
|
||||
|
||||
public static void clearStaleOverlayFlag() {
|
||||
overlayPassActive = false;
|
||||
pendingShouldRenderPass = null;
|
||||
pendingRenderer = null;
|
||||
pendingEntity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,23 +76,21 @@ public class DeferredEntityOverlay {
|
||||
*/
|
||||
public static void deferRender(float limbSwing, float limbSwingAmount, float ageInTicks,
|
||||
float headYaw, float headPitch, float scale) {
|
||||
overlayPassActive = false;
|
||||
|
||||
DeferredEntry entry = acquireEntry();
|
||||
entry.set(pendingShouldRenderPass, pendingRenderer, pendingEntity, pendingPartialTick,
|
||||
limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch, scale);
|
||||
|
||||
GLStateManager.getModelViewMatrix().get(entry.matrix);
|
||||
MATRIX_BUF.clear();
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MATRIX_BUF);
|
||||
MATRIX_BUF.get(entry.matrix);
|
||||
|
||||
deferred.add(entry);
|
||||
EntityShadowReplay.recordDeferredOverlay(entry);
|
||||
|
||||
clearStaleOverlayFlag();
|
||||
}
|
||||
|
||||
public static void queueReplay(DeferredEntry captured, Matrix4fc mainFromShadow) {
|
||||
final DeferredEntry entry = acquireEntry();
|
||||
entry.set(captured);
|
||||
replayMatrix.set(captured.matrix).mulLocal(mainFromShadow).get(entry.matrix);
|
||||
deferred.add(entry);
|
||||
pendingShouldRenderPass = null;
|
||||
pendingRenderer = null;
|
||||
pendingEntity = null;
|
||||
}
|
||||
|
||||
private static DeferredEntry acquireEntry() {
|
||||
@@ -112,7 +103,9 @@ public class DeferredEntityOverlay {
|
||||
|
||||
private static void recycle() {
|
||||
for (DeferredEntry entry : deferred) {
|
||||
entry.clear();
|
||||
entry.shouldRenderPass = null;
|
||||
entry.renderer = null;
|
||||
entry.entity = null;
|
||||
pool.add(entry);
|
||||
}
|
||||
deferred.clear();
|
||||
@@ -120,7 +113,7 @@ public class DeferredEntityOverlay {
|
||||
|
||||
public static void clear() {
|
||||
recycle();
|
||||
clearStaleOverlayFlag();
|
||||
overlayPassActive = false;
|
||||
replaying = false;
|
||||
}
|
||||
|
||||
@@ -145,7 +138,7 @@ public class DeferredEntityOverlay {
|
||||
|
||||
// Restore the entity's lightmap coords
|
||||
int brightness = entity.getBrightnessForRender(entry.partialTick);
|
||||
GLStateManager.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit,
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit,
|
||||
brightness % 65536, (float) brightness / 65536);
|
||||
|
||||
// Restore the saved modelview matrix
|
||||
@@ -190,7 +183,7 @@ public class DeferredEntityOverlay {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class DeferredEntry {
|
||||
private static class DeferredEntry {
|
||||
final float[] matrix = new float[16];
|
||||
ShouldRenderPassFn shouldRenderPass;
|
||||
RendererLivingEntity renderer;
|
||||
@@ -203,22 +196,6 @@ public class DeferredEntityOverlay {
|
||||
float headPitch;
|
||||
float scale;
|
||||
|
||||
public void copyFrom(DeferredEntry source) {
|
||||
set(source);
|
||||
System.arraycopy(source.matrix, 0, matrix, 0, matrix.length);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
shouldRenderPass = null;
|
||||
renderer = null;
|
||||
entity = null;
|
||||
}
|
||||
|
||||
private void set(DeferredEntry source) {
|
||||
set(source.shouldRenderPass, source.renderer, source.entity, source.partialTick,
|
||||
source.limbSwing, source.limbSwingAmount, source.ageInTicks, source.headYaw, source.headPitch, source.scale);
|
||||
}
|
||||
|
||||
void set(ShouldRenderPassFn shouldRenderPass, RendererLivingEntity renderer,
|
||||
EntityLivingBase entity, float partialTick,
|
||||
float limbSwing, float limbSwingAmount, float ageInTicks,
|
||||
|
||||
@@ -32,23 +32,22 @@ public final class SkippedGlintBlock {
|
||||
}
|
||||
|
||||
private static void applyExitState(float red, float green, float blue, float alpha, int srcRgb, int dstRgb, int srcAlpha, int dstAlpha, boolean resetTextureMatrix, boolean restoreDepthMask) {
|
||||
final boolean recording = GLStateManager.isExitCaptureActive();
|
||||
final Color4 color = GLStateManager.getColor();
|
||||
if (recording || color.getRed() != red || color.getGreen() != green || color.getBlue() != blue || color.getAlpha() != alpha) {
|
||||
if (color.getRed() != red || color.getGreen() != green || color.getBlue() != blue || color.getAlpha() != alpha) {
|
||||
GLStateManager.glColor4f(red, green, blue, alpha);
|
||||
}
|
||||
if (resetTextureMatrix && (recording || !MatrixHelper.isIdentity(GLStateManager.getTextures().getTextureUnitMatrix(0)))) {
|
||||
if (resetTextureMatrix && !MatrixHelper.isIdentity(GLStateManager.getTextures().getTextureUnitMatrix(0))) {
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
if (recording || GLStateManager.getMatrixMode().getMode() != GL11.GL_MODELVIEW) GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
if (restoreDepthMask && (recording || !GLStateManager.isEffectiveDepthMaskEnabled())) GLStateManager.glDepthMask(true);
|
||||
if (recording || !GLStateManager.getLightingState().isEnabled()) GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
if (GLStateManager.getMatrixMode().getMode() != GL11.GL_MODELVIEW) GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
if (restoreDepthMask && !GLStateManager.isEffectiveDepthMaskEnabled()) GLStateManager.glDepthMask(true);
|
||||
if (!GLStateManager.getLightingState().isEnabled()) GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
GLStateManager.getEffectiveBlendState(blend);
|
||||
if (recording || GLStateManager.getBlendState().isFuncUnknown() || blend.getSrcRgb() != srcRgb || blend.getDstRgb() != dstRgb || blend.getSrcAlpha() != srcAlpha || blend.getDstAlpha() != dstAlpha) {
|
||||
if (GLStateManager.getBlendState().isFuncUnknown() || blend.getSrcRgb() != srcRgb || blend.getDstRgb() != dstRgb || blend.getSrcAlpha() != srcAlpha || blend.getDstAlpha() != dstAlpha) {
|
||||
GLStateManager.tryBlendFuncSeparate(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
}
|
||||
if (recording || GLStateManager.isEffectiveBlendEnabled()) GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
if (recording || GLStateManager.getDepthState().getFunc() != GL11.GL_LEQUAL) GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
if (GLStateManager.isEffectiveBlendEnabled()) GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
if (GLStateManager.getDepthState().getFunc() != GL11.GL_LEQUAL) GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityMaterials;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.GlintCapture;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TemplateBuffer;
|
||||
import com.gtnewhorizons.angelica.utils.AnimationsRenderUtils;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
@@ -150,7 +149,6 @@ public final class DroppedItemInstancer {
|
||||
}
|
||||
final boolean allowed = BatchEligibility.batchingAllowed();
|
||||
if (allowed && ModelPartBatcher.INSTANCE.isShadowPass()) {
|
||||
EntityShadowReplay.rejectShadowOnly();
|
||||
BatchEligibility.onPartQueued();
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,6 @@ public final class BatchEligibility {
|
||||
static boolean insideRenderer() { return depth != 0; }
|
||||
|
||||
static void onUncapturedState() {
|
||||
EntityShadowReplay.rejectUncapturedState();
|
||||
if (depth == 0) return;
|
||||
uncapturedState = true;
|
||||
allowed = false;
|
||||
@@ -126,7 +125,6 @@ public final class BatchEligibility {
|
||||
}
|
||||
|
||||
static void onBail(ModelPartBatcher.BailReason reason) {
|
||||
EntityShadowReplay.rejectBatcherBail();
|
||||
if (depth == 0 || !allowed) return;
|
||||
if (reason == ModelPartBatcher.BailReason.INACTIVE) {
|
||||
allowed = false;
|
||||
@@ -140,7 +138,6 @@ public final class BatchEligibility {
|
||||
}
|
||||
|
||||
public static void onPartFallback(long drawsBefore, long drawsNow) {
|
||||
EntityShadowReplay.rejectPartFallback();
|
||||
parts++;
|
||||
if (!allowed) {
|
||||
expectedDraws += drawsNow - drawsBefore;
|
||||
|
||||
@@ -44,7 +44,6 @@ public final class EntityMaterials {
|
||||
}
|
||||
|
||||
static TesrMaterial fromCurrentState(boolean texIdentity) {
|
||||
GLStateManager.requireExitDrawState(true);
|
||||
final boolean textured = GLStateManager.getTextures().getTextureUnitStates(0).isEnabled();
|
||||
final AlphaState alpha = GLStateManager.getEffectiveAlphaState(effectiveAlpha);
|
||||
final ColorMask colorMask = GLStateManager.getEffectiveColorMask(effectiveColorMask);
|
||||
@@ -59,7 +58,6 @@ public final class EntityMaterials {
|
||||
}
|
||||
|
||||
public static TesrMaterial itemFromCurrentState(boolean unfilteredAtlas) {
|
||||
GLStateManager.requireExitDrawState(true);
|
||||
final BlendState blend = GLStateManager.getEffectiveBlendState(effectiveBlend);
|
||||
final AlphaState alpha = GLStateManager.getEffectiveAlphaState(effectiveAlpha);
|
||||
return itemFromState(unfilteredAtlas, GLStateManager.isEffectiveBlendEnabled(), blend.getSrcRgb(), blend.getDstRgb(),
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMaterial;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.RenderLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.CubeParams;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.rendering.DeferredEntityOverlay;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay.Rejection;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.InstanceColumns;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.InstanceRange;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.gtnewhorizons.angelica.rendering.items.ItemGlint.canResetStencil;
|
||||
|
||||
/**
|
||||
* Ordered, pooled submissions captured before the main entity pass.
|
||||
*/
|
||||
final class EntityRenderCommands {
|
||||
private final ObjectArrayList<Command> commands = new ObjectArrayList<>();
|
||||
private final Pool<Model> models = new Pool<>(Model::new, Model::clear);
|
||||
private final Pool<ModelGlint> modelGlints = new Pool<>(ModelGlint::new, ModelGlint::clear);
|
||||
private final Pool<ItemGlint> itemGlints = new Pool<>(ItemGlint::new, ItemGlint::clear);
|
||||
private final Pool<Stream> streams = new Pool<>(Stream::new, Stream::clear);
|
||||
private final Pool<Overlay> overlays = new Pool<>(Overlay::new, Overlay::clear);
|
||||
private final Pool<InstanceRange> ranges = new Pool<>(InstanceRange::new, InstanceRange::clear);
|
||||
private final Matrix4f glintPose = new Matrix4f();
|
||||
long sharedParts, sharedRanges, streamEntries;
|
||||
private int mergeStart;
|
||||
|
||||
int size() {
|
||||
return commands.size();
|
||||
}
|
||||
|
||||
int split() {
|
||||
mergeStart = size();
|
||||
return mergeStart;
|
||||
}
|
||||
|
||||
Model model(int recordStart, InstanceColumns source, RenderLayer layer, DrawInputs inputs, int entityId, boolean cubesInsideTexture, Matrix4fc modelView, Matrix4fc texMatrix) {
|
||||
if (source != null && size() > recordStart && size() > mergeStart && commands.get(size() - 1) instanceof Model previous) {
|
||||
if (previous.source == source && previous.sourceVersion == source.version && previous.end == source.size - 1 && previous.shadowLayer == layer && previous.inputs.matches(inputs) && previous.entityId == entityId
|
||||
&& previous.cubesInsideTexture == cubesInsideTexture && previous.glint == null && (texMatrix == null ? !previous.hasTextureMatrix : previous.hasTextureMatrix && previous.textureMatrix.equals(texMatrix))) {
|
||||
previous.end = source.size;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
final Model model = models.acquire();
|
||||
commands.add(model);
|
||||
model.inputs.copyFrom(inputs);
|
||||
model.entityId = entityId;
|
||||
model.cubesInsideTexture = cubesInsideTexture;
|
||||
model.hasTextureMatrix = texMatrix != null;
|
||||
if (texMatrix != null) model.textureMatrix.set(texMatrix);
|
||||
model.source = source;
|
||||
model.shadowLayer = layer;
|
||||
if (source == null) {
|
||||
model.modelView.set(modelView);
|
||||
} else {
|
||||
model.sourceVersion = source.version;
|
||||
model.start = source.size - 1;
|
||||
model.end = source.size;
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
ModelGlint modelGlint(int start, int end, DrawInputs inputs) {
|
||||
for (int i = start; i < end; i++) {
|
||||
if (!(commands.get(i) instanceof Model model) || model.cannotAddGlint()) return null;
|
||||
}
|
||||
final ModelGlint glint = modelGlints.acquire();
|
||||
commands.add(glint);
|
||||
glint.inputs.copyFrom(inputs);
|
||||
glint.base = (Model) commands.get(start);
|
||||
Model previous = null;
|
||||
for (int i = start; i < end; i++) {
|
||||
final Model model = (Model) commands.get(i);
|
||||
addGlint(model, glint, true);
|
||||
if (previous != null) previous.nextGlintBase = model;
|
||||
previous = model;
|
||||
}
|
||||
return glint;
|
||||
}
|
||||
|
||||
ItemGlint itemGlint(Matrix4fc modelView, DrawInputs inputs) {
|
||||
if (!(commands.get(size() - 1) instanceof Model base) || base.cannotAddGlint() || base.drawsCubes() || base.sharedRangeChanged()) return null;
|
||||
final Matrix4fc pose = base.source != null ? base.source.pose(base.end - 1, glintPose) : base.modelView;
|
||||
if (!pose.equals(modelView)) return null;
|
||||
final ItemGlint glint = itemGlints.acquire();
|
||||
commands.add(glint);
|
||||
glint.inputs.copyFrom(inputs);
|
||||
glint.base = base;
|
||||
addGlint(base, glint, false);
|
||||
return glint;
|
||||
}
|
||||
|
||||
private void addGlint(Model base, Glint glint, boolean reuseUpload) {
|
||||
base.glint = glint;
|
||||
base.replayed = ranges.acquire();
|
||||
base.replayed.reuseUpload = reuseUpload;
|
||||
}
|
||||
|
||||
void stream(Matrix4fc modelView, int[] vertices, int vertexCount, int entityId, DrawInputs inputs) {
|
||||
final Stream stream = streams.acquire();
|
||||
commands.add(stream);
|
||||
stream.inputs.copyFrom(inputs);
|
||||
stream.modelView.set(modelView);
|
||||
stream.entityId = entityId;
|
||||
stream.capture(vertices, vertexCount);
|
||||
}
|
||||
|
||||
void overlay(DeferredEntityOverlay.DeferredEntry source) {
|
||||
final Overlay overlay = overlays.acquire();
|
||||
commands.add(overlay);
|
||||
// The shadow overlay queue is recycled before main-view entity
|
||||
overlay.captured.copyFrom(source);
|
||||
}
|
||||
|
||||
Rejection validate(int start, int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
final Rejection reason = commands.get(i).validate();
|
||||
if (reason != null) return reason;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void replay(int start, int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
final Command command = commands.get(i);
|
||||
command.replay();
|
||||
if (Tracy.ENABLED) {
|
||||
if (command instanceof Model model && model.source != null) {
|
||||
sharedParts += model.end - model.start;
|
||||
sharedRanges++;
|
||||
} else if (command instanceof Stream) {
|
||||
streamEntries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
commands.clear();
|
||||
models.clear();
|
||||
modelGlints.clear();
|
||||
itemGlints.clear();
|
||||
streams.clear();
|
||||
overlays.clear();
|
||||
ranges.clear();
|
||||
mergeStart = 0;
|
||||
}
|
||||
|
||||
void release() {
|
||||
clear();
|
||||
models.release();
|
||||
modelGlints.release();
|
||||
itemGlints.release();
|
||||
streams.release();
|
||||
overlays.release();
|
||||
ranges.release();
|
||||
}
|
||||
|
||||
abstract static class Command {
|
||||
Rejection validate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract void replay();
|
||||
}
|
||||
|
||||
static final class DrawInputs {
|
||||
ResourceLocation texture;
|
||||
TesrMaterial material;
|
||||
PassOverride pass;
|
||||
int textureId, glintSlot, cull;
|
||||
float offsetFactor, offsetUnits;
|
||||
boolean offsetEnabled, lit, lightmap, afterGlint;
|
||||
boolean inheritedOffsetEnable, inheritedOffsetValues, inheritedLighting;
|
||||
|
||||
DrawInputs set(ResourceLocation texture, int textureId, TesrMaterial material, PassOverride pass, boolean offsetEnabled, float offsetFactor, float offsetUnits, int glintSlot, int cull, boolean lit, boolean lightmap, boolean afterGlint) {
|
||||
this.texture = texture;
|
||||
this.textureId = textureId;
|
||||
this.material = material;
|
||||
this.pass = pass;
|
||||
this.offsetEnabled = offsetEnabled;
|
||||
this.offsetFactor = offsetFactor;
|
||||
this.offsetUnits = offsetUnits;
|
||||
this.glintSlot = glintSlot;
|
||||
this.cull = cull;
|
||||
this.lit = lit;
|
||||
this.lightmap = lightmap;
|
||||
this.afterGlint = afterGlint;
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean offsetValuesUsed() {
|
||||
return !inheritedOffsetValues && (offsetEnabled || inheritedOffsetEnable);
|
||||
}
|
||||
|
||||
boolean matches(DrawInputs other) {
|
||||
return textureId == other.textureId && Objects.equals(texture, other.texture) && material == other.material && pass == other.pass && (!offsetValuesUsed() || Float.floatToIntBits(offsetFactor)
|
||||
== Float.floatToIntBits(other.offsetFactor) && Float.floatToIntBits(offsetUnits) == Float.floatToIntBits(other.offsetUnits)) && offsetEnabled == other.offsetEnabled && glintSlot == other.glintSlot && cull == other.cull
|
||||
&& lit == other.lit && lightmap == other.lightmap && afterGlint == other.afterGlint && inheritedOffsetEnable == other.inheritedOffsetEnable && inheritedOffsetValues == other.inheritedOffsetValues && inheritedLighting
|
||||
== other.inheritedLighting;
|
||||
}
|
||||
|
||||
void copyFrom(DrawInputs other) {
|
||||
set(other.texture, other.textureId, other.material, other.pass, other.offsetEnabled, other.offsetFactor, other.offsetUnits, other.glintSlot, other.cull, other.lit, other.lightmap, other.afterGlint);
|
||||
inheritedOffsetEnable = other.inheritedOffsetEnable;
|
||||
inheritedOffsetValues = other.inheritedOffsetValues;
|
||||
inheritedLighting = other.inheritedLighting;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
texture = null;
|
||||
material = null;
|
||||
pass = null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Instance {
|
||||
TemplateBuffer template;
|
||||
int light, color, overlay;
|
||||
long entityInfo;
|
||||
|
||||
void set(TemplateBuffer template, int light, int color, int overlay, long entityInfo) {
|
||||
this.template = template;
|
||||
this.light = light;
|
||||
this.color = color;
|
||||
this.overlay = overlay;
|
||||
this.entityInfo = entityInfo;
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class Draw extends Command {
|
||||
final DrawInputs inputs = new DrawInputs();
|
||||
int entityId;
|
||||
|
||||
void clear() {
|
||||
inputs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
static final class Model extends Draw {
|
||||
final Matrix4f modelView = new Matrix4f();
|
||||
final Matrix4f textureMatrix = new Matrix4f();
|
||||
final Instance instance = new Instance();
|
||||
InstanceColumns source;
|
||||
long sourceVersion;
|
||||
int start, end;
|
||||
RenderLayer shadowLayer;
|
||||
CubeParams[] cubes;
|
||||
boolean cubesInsideTexture, hasTextureMatrix;
|
||||
float scale;
|
||||
Glint glint;
|
||||
Model nextGlintBase;
|
||||
InstanceRange replayed;
|
||||
|
||||
boolean cannotAddGlint() {
|
||||
final TesrMaterial material = inputs.material;
|
||||
return material == null || !material.isStream() || material.shader() != null || glint != null;
|
||||
}
|
||||
|
||||
boolean drawsCubes() {
|
||||
return source != null ? !source.cubes.isEmpty() : cubes != null;
|
||||
}
|
||||
|
||||
boolean sharedRangeChanged() {
|
||||
return source != null && (source.version != sourceVersion || end > source.size);
|
||||
}
|
||||
|
||||
@Override
|
||||
Rejection validate() {
|
||||
if (sharedRangeChanged()) return Rejection.SHARED_RANGE_CHANGED;
|
||||
if (glint != null && !ModelPartBatcher.INSTANCE.canInstanceBaseAndGlint(this, glint.inputs.lightmap)) return Rejection.NOT_READY;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void replay() {
|
||||
ModelPartBatcher.INSTANCE.replay(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
void clear() {
|
||||
super.clear();
|
||||
source = null;
|
||||
shadowLayer = null;
|
||||
instance.template = null;
|
||||
cubes = null;
|
||||
glint = null;
|
||||
nextGlintBase = null;
|
||||
replayed = null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class Glint extends Draw {
|
||||
Model base;
|
||||
|
||||
@Override
|
||||
Rejection validate() {
|
||||
if (!ModelPartBatcher.INSTANCE.canReplayGlint(inputs.material)) return Rejection.NOT_READY;
|
||||
return inputs.material == EntityMaterials.ITEM_GLINT && !canResetStencil() ? Rejection.UNCAPTURED_STATE : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void clear() {
|
||||
super.clear();
|
||||
base = null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ModelGlint extends Glint {
|
||||
@Override
|
||||
void replay() {
|
||||
ModelPartBatcher.INSTANCE.replay(this);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ItemGlint extends Glint {
|
||||
final Instance instance = new Instance();
|
||||
|
||||
@Override
|
||||
void replay() {
|
||||
ModelPartBatcher.INSTANCE.replay(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
void clear() {
|
||||
super.clear();
|
||||
instance.template = null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Stream extends Draw {
|
||||
final Matrix4f modelView = new Matrix4f();
|
||||
int[] vertices;
|
||||
int vertexCount, entity, blockEntity, item;
|
||||
float red, green, blue, alpha;
|
||||
|
||||
void capture(int[] vertices, int vertexCount) {
|
||||
final int length = vertexCount * ModelQuadUtil.VERTEX_SIZE;
|
||||
if (this.vertices == null) {
|
||||
this.vertices = new int[length];
|
||||
} else if (this.vertices.length < length) {
|
||||
this.vertices = new int[Math.max(length, this.vertices.length << 1)];
|
||||
}
|
||||
System.arraycopy(vertices, 0, this.vertices, 0, length);
|
||||
this.vertexCount = vertexCount;
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
entity = state.getCurrentRenderedEntity();
|
||||
blockEntity = state.getCurrentRenderedBlockEntity();
|
||||
item = state.getCurrentRenderedItem();
|
||||
final Vector4fc color = state.getCurrentEntityColor();
|
||||
red = color.x();
|
||||
green = color.y();
|
||||
blue = color.z();
|
||||
alpha = color.w();
|
||||
}
|
||||
|
||||
@Override
|
||||
void replay() {
|
||||
ModelPartBatcher.INSTANCE.replay(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Overlay extends Command {
|
||||
final DeferredEntityOverlay.DeferredEntry captured = new DeferredEntityOverlay.DeferredEntry();
|
||||
|
||||
@Override
|
||||
void replay() {
|
||||
DeferredEntityOverlay.queueReplay(captured, ModelPartBatcher.INSTANCE.replayTransform);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
captured.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,409 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.RenderLayer;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.ExitState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.rendering.DeferredEntityOverlay;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityRenderCommands.DrawInputs;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.InstanceColumns;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import lombok.Setter;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import net.minecraft.client.renderer.culling.Frustrum;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.boss.BossStatus;
|
||||
import net.minecraft.entity.boss.IBossDisplayData;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Records a shadow-pass entity call, then submits its models, fire, and deferred overlays in the main pass.
|
||||
*/
|
||||
public final class EntityShadowReplay {
|
||||
private static final Tracy.ZoneId Z_REPLAY = Tracy.zoneId("entityReplay", Tracy.COLOR_CLIENT);
|
||||
private static final long P_REPLAYED = Tracy.plotHandle("entityReplay.replayed");
|
||||
private static final long P_SHARED_PARTS = Tracy.plotHandle("entityReplay.sharedParts");
|
||||
private static final long P_SHARED_RANGES = Tracy.plotHandle("entityReplay.sharedRanges");
|
||||
private static final long P_STREAM_ENTRIES = Tracy.plotHandle("entityReplay.streamEntries");
|
||||
private static final long P_RECORDED = Tracy.plotHandle("entityReplay.recorded");
|
||||
private static final long P_OUTSIDE_MAIN_VIEW = Tracy.plotHandle("entityReplay.outsideMainView");
|
||||
private static final Rejection[] REJECTIONS = Rejection.values();
|
||||
private static final long[] rejected = new long[REJECTIONS.length];
|
||||
private static final Reference2ObjectOpenHashMap<Entity, Record> records = new Reference2ObjectOpenHashMap<>();
|
||||
private static final Reference2ObjectOpenHashMap<Class<?>, Boolean> excluded = new Reference2ObjectOpenHashMap<>();
|
||||
private static final Reference2ObjectOpenHashMap<Class<?>, Reference2IntOpenHashMap<Class<?>>> loggedRejections = new Reference2ObjectOpenHashMap<>();
|
||||
private static final Pool<Record> recordPool = new Pool<>(Record::new, Record::clear);
|
||||
private static final EntityRenderCommands commands = new EntityRenderCommands();
|
||||
private static final Matrix4f shadowInverse = new Matrix4f();
|
||||
private static final Matrix4f mainFromShadow = new Matrix4f();
|
||||
private static Record recording;
|
||||
@Setter private static Frustrum mainView;
|
||||
private static Class<?> lastType;
|
||||
private static boolean lastExcluded;
|
||||
private static long drawsAtStart, replayed, outsideMainView;
|
||||
private static boolean shadowViewValid, shadowAvailable, mainReady, logRejections;
|
||||
private static double shadowX, shadowY, shadowZ;
|
||||
private static float shadowYaw, shadowPitch;
|
||||
private static float capturedBossHealthScale;
|
||||
private static int capturedBossBarTime;
|
||||
private static String capturedBossName;
|
||||
private static boolean capturedBossColorModifier;
|
||||
|
||||
private EntityShadowReplay() {
|
||||
}
|
||||
|
||||
public static void beginFrame() {
|
||||
if (Tracy.ENABLED) {
|
||||
Tracy.plotInt(P_REPLAYED, replayed);
|
||||
Tracy.plotInt(P_SHARED_PARTS, commands.sharedParts);
|
||||
Tracy.plotInt(P_SHARED_RANGES, commands.sharedRanges);
|
||||
Tracy.plotInt(P_STREAM_ENTRIES, commands.streamEntries);
|
||||
Tracy.plotInt(P_RECORDED, recordPool.size());
|
||||
Tracy.plotInt(P_OUTSIDE_MAIN_VIEW, outsideMainView);
|
||||
for (Rejection reason : REJECTIONS) Tracy.plotInt(reason.plot, rejected[reason.ordinal()]);
|
||||
}
|
||||
replayed = outsideMainView = 0;
|
||||
commands.sharedParts = commands.sharedRanges = commands.streamEntries = 0;
|
||||
Arrays.fill(rejected, 0);
|
||||
resetRecords();
|
||||
mainView = null;
|
||||
logRejections = SystemProperties.debugTooling();
|
||||
}
|
||||
|
||||
private static void resetRecords() {
|
||||
if (recording != null && recording.capturingState) GLStateManager.cancelExitCapture();
|
||||
records.clear();
|
||||
commands.clear();
|
||||
recordPool.clear();
|
||||
recording = null;
|
||||
capturedBossName = null;
|
||||
shadowViewValid = shadowAvailable = mainReady = false;
|
||||
}
|
||||
|
||||
static void beginPass(boolean shadow, int renderPass) {
|
||||
if (shadow) {
|
||||
resetRecords();
|
||||
shadowInverse.set(GLStateManager.getModelViewMatrix()).invert();
|
||||
shadowViewValid = shadowInverse.isFinite();
|
||||
shadowX = RenderManager.renderPosX;
|
||||
shadowY = RenderManager.renderPosY;
|
||||
shadowZ = RenderManager.renderPosZ;
|
||||
shadowYaw = RenderManager.instance.playerViewY;
|
||||
shadowPitch = RenderManager.instance.playerViewX;
|
||||
} else {
|
||||
mainReady = shadowAvailable && renderPass == 0;
|
||||
if (mainReady) {
|
||||
mainFromShadow.set(GLStateManager.getModelViewMatrix()).translate(
|
||||
(float) (shadowX - RenderManager.renderPosX), (float) (shadowY - RenderManager.renderPosY),
|
||||
(float) (shadowZ - RenderManager.renderPosZ)).mul(shadowInverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean beginEntity(Entity entity, float partialTicks) {
|
||||
if (recording != null) {
|
||||
reject(Rejection.FOREIGN_DRAW);
|
||||
return false;
|
||||
}
|
||||
if (!shadowViewValid || !ShadowRenderer.ACTIVE || !ModelPartBatcher.INSTANCE.entitiesActive()) return false;
|
||||
shadowAvailable = true;
|
||||
// replay() rejects these without a record
|
||||
if (isExcluded(entity.getClass()) || entity.shouldRenderInPass(1)) return false;
|
||||
if (!inMainView(entity)) {
|
||||
outsideMainView++;
|
||||
return false;
|
||||
}
|
||||
final Record record = recordPool.acquire();
|
||||
record.start = record.end = commands.size();
|
||||
record.entity = entity;
|
||||
record.partialTicks = partialTicks;
|
||||
record.hasBossStatus = false;
|
||||
record.bossColorModifier = false;
|
||||
record.rejection = null;
|
||||
records.put(entity, record);
|
||||
recording = record;
|
||||
record.capturingState = true;
|
||||
GLStateManager.beginExitCapture(record.exitState);
|
||||
drawsAtStart = GLStateManager.drawCalls;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void endEntity(boolean completed) {
|
||||
if (recording == null) return;
|
||||
if (recording.capturingState) {
|
||||
recording.capturingState = false;
|
||||
if (!GLStateManager.finishExitCapture(recording.exitState)) reject(Rejection.EXIT_STATE_UNSUPPORTED);
|
||||
}
|
||||
if (GLStateManager.drawCalls != drawsAtStart) reject(Rejection.FOREIGN_DRAW);
|
||||
if (recording.renderer == null) reject(Rejection.UNSAFE_RENDERER);
|
||||
if (!completed) reject(Rejection.RENDER_FAILED);
|
||||
if (recording.entity instanceof IBossDisplayData && (!recording.hasBossStatus || bossStatusChanged())) {
|
||||
reject(Rejection.UNCAPTURED_STATE);
|
||||
}
|
||||
recording.end = commands.size();
|
||||
if (recording.start == recording.end) reject(Rejection.EMPTY_RECORD);
|
||||
if (recording.rejection == null) recording.texture.capture();
|
||||
recording = null;
|
||||
capturedBossName = null;
|
||||
}
|
||||
|
||||
public static boolean beginRenderer(Render renderer, Entity entity, float partialTicks, byte batchState) {
|
||||
if (recording == null) return false;
|
||||
if (recording.renderer != null || recording.entity != entity) {
|
||||
reject(Rejection.FOREIGN_DRAW);
|
||||
return false;
|
||||
}
|
||||
recording.renderer = renderer;
|
||||
if (recording.partialTicks != partialTicks) reject(Rejection.PARTIAL_TICKS_CHANGED);
|
||||
if (batchState != BatchEligibility.SAFE) reject(Rejection.UNSAFE_RENDERER);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void endRenderer(boolean completed, byte batchState) {
|
||||
if (!completed) reject(Rejection.RENDER_FAILED);
|
||||
if (batchState != BatchEligibility.SAFE) reject(Rejection.UNSAFE_RENDERER);
|
||||
}
|
||||
|
||||
private static boolean inMainView(Entity entity) {
|
||||
return mainView == null || entity.isInRangeToRender3d(mainView.xPosition, mainView.yPosition, mainView.zPosition)
|
||||
&& (entity.ignoreFrustumCheck || mainView.isBoundingBoxInFrustum(entity.boundingBox));
|
||||
}
|
||||
|
||||
static boolean isExcluded(Class<?> type) {
|
||||
if (type == lastType) return lastExcluded;
|
||||
Boolean result = excluded.get(type);
|
||||
if (result == null) {
|
||||
result = EntityPlayer.class.isAssignableFrom(type);
|
||||
for (Class<?> parent = type; !result && parent != null; parent = parent.getSuperclass()) {
|
||||
final String[] names = AngelicaConfig.entityShadowReplayExcludedClasses;
|
||||
if (names != null) for (String name : names) {
|
||||
if (parent.getName().equals(name)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
excluded.put(type, result);
|
||||
}
|
||||
lastType = type;
|
||||
lastExcluded = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean replay(RenderManager manager, Entity entity, float partialTicks, int renderPass) {
|
||||
if (!shadowAvailable || ShadowRenderer.ACTIVE) return false;
|
||||
final Record record = records.get(entity);
|
||||
if (record == null && !Tracy.ENABLED && !logRejections) return false;
|
||||
Rejection reason = renderPass != 0 ? Rejection.RENDER_PASS
|
||||
: record != null ? record.rejection
|
||||
: isExcluded(entity.getClass()) ? Rejection.EXCLUDED
|
||||
: entity.shouldRenderInPass(1) ? Rejection.RENDER_PASS : Rejection.NOT_IN_SHADOW_PASS;
|
||||
if (reason == null) {
|
||||
if (!mainReady || !ModelPartBatcher.INSTANCE.canReplayEntity()) reason = Rejection.NOT_READY;
|
||||
else if (record.renderer != manager.getEntityRenderObject(entity)) reason = Rejection.RENDERER_CHANGED;
|
||||
else if (record.partialTicks != partialTicks) reason = Rejection.PARTIAL_TICKS_CHANGED;
|
||||
else if (shadowYaw != RenderManager.instance.playerViewY || shadowPitch != RenderManager.instance.playerViewX) reason = Rejection.VIEW_CHANGED;
|
||||
else if (!GLStateManager.canApplyExitState(record.exitState)) reason = Rejection.ENTRY_STATE_CHANGED;
|
||||
else if (GLStateManager.getPolygonState().getOffsetClamp() != 0) reason = Rejection.UNCAPTURED_STATE;
|
||||
}
|
||||
if (reason == null) {
|
||||
reason = commands.validate(record.start, record.end);
|
||||
}
|
||||
if (reason != null) {
|
||||
rejected[reason.ordinal()]++;
|
||||
logRejection(entity.getClass(), record == null || record.renderer == null ? null : record.renderer.getClass(), reason);
|
||||
return false;
|
||||
}
|
||||
replayed++;
|
||||
if (Tracy.FINE_ZONES) Tracy.beginZone(Z_REPLAY);
|
||||
try {
|
||||
if (record.hasBossStatus) BossStatus.setBossStatus((IBossDisplayData) record.entity, record.bossColorModifier);
|
||||
ModelPartBatcher.INSTANCE.beginReplay(mainFromShadow);
|
||||
commands.replay(record.start, record.end);
|
||||
GLStateManager.applyExitState(record.exitState);
|
||||
record.texture.apply();
|
||||
} finally {
|
||||
if (Tracy.FINE_ZONES) Tracy.endZone();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void logRejection(Class<?> entity, Class<?> renderer, Rejection reason) {
|
||||
if (!logRejections) return;
|
||||
Reference2IntOpenHashMap<Class<?>> reasons = loggedRejections.get(entity);
|
||||
if (reasons == null) {
|
||||
reasons = new Reference2IntOpenHashMap<>();
|
||||
loggedRejections.put(entity, reasons);
|
||||
}
|
||||
final int bit = 1 << reason.ordinal();
|
||||
final int logged = reasons.getInt(renderer);
|
||||
if ((logged & bit) != 0) return;
|
||||
reasons.put(renderer, logged | bit);
|
||||
AngelicaMod.LOGGER.info("Entity shadow replay fallback: entity={} capturedRenderer={} reason={}: {}",
|
||||
entity.getName(), renderer == null ? "<not captured>" : renderer.getName(), reason.label, reason.description);
|
||||
}
|
||||
|
||||
static EntityRenderCommands.Model recordModel(InstanceColumns source, RenderLayer layer, DrawInputs inputs, int entityId,
|
||||
boolean cubesInsideTexture, Matrix4fc modelView, Matrix4fc texMatrix) {
|
||||
return captureInheritedState(inputs)
|
||||
? commands.model(recording.start, source, layer, inputs, entityId, cubesInsideTexture, modelView, texMatrix) : null;
|
||||
}
|
||||
|
||||
static void recordStream(Matrix4fc modelView, int[] vertices, int vertexCount, int groupId, DrawInputs inputs) {
|
||||
if (captureInheritedState(inputs)) commands.stream(modelView, vertices, vertexCount, groupId, inputs);
|
||||
}
|
||||
|
||||
static EntityRenderCommands.ModelGlint recordArmorGlint(int start, int end, DrawInputs inputs) {
|
||||
return captureInheritedState(inputs) && start >= recording.start && end > start && end <= commands.size()
|
||||
? commands.modelGlint(start, end, inputs) : null;
|
||||
}
|
||||
|
||||
static EntityRenderCommands.ItemGlint recordItemGlint(Matrix4fc modelView, DrawInputs inputs) {
|
||||
return captureInheritedState(inputs) && commands.size() > recording.start ? commands.itemGlint(modelView, inputs) : null;
|
||||
}
|
||||
|
||||
private static boolean captureInheritedState(DrawInputs inputs) {
|
||||
if (GLStateManager.getPolygonState().getOffsetClamp() != 0) reject(Rejection.UNCAPTURED_STATE);
|
||||
if (!isRecording()) return false;
|
||||
final int offsetWrites = GLStateManager.getExitPolygonOffsetWrites();
|
||||
inputs.inheritedOffsetEnable = (offsetWrites & ExitState.POLYGON_OFFSET_ENABLE_WRITTEN) == 0;
|
||||
inputs.inheritedOffsetValues = (offsetWrites & ExitState.POLYGON_OFFSET_VALUES_WRITTEN) == 0;
|
||||
inputs.inheritedLighting = !GLStateManager.isExitLightingWritten();
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean isRecording() {
|
||||
return recording != null && recording.rejection == null;
|
||||
}
|
||||
|
||||
static int splitCommands() {
|
||||
return isRecording() ? commands.split() : -1;
|
||||
}
|
||||
|
||||
public static void recordDeferredOverlay(DeferredEntityOverlay.DeferredEntry overlay) {
|
||||
if (isRecording()) commands.overlay(overlay);
|
||||
}
|
||||
|
||||
public static void recordBossStatus(IBossDisplayData boss, boolean colorModifier) {
|
||||
if (!isRecording()) return;
|
||||
if (boss != recording.entity || recording.hasBossStatus) {
|
||||
reject(Rejection.UNCAPTURED_STATE);
|
||||
return;
|
||||
}
|
||||
recording.hasBossStatus = true;
|
||||
recording.bossColorModifier = colorModifier;
|
||||
capturedBossHealthScale = BossStatus.healthScale;
|
||||
capturedBossBarTime = BossStatus.statusBarTime;
|
||||
capturedBossName = BossStatus.bossName;
|
||||
capturedBossColorModifier = BossStatus.hasColorModifier;
|
||||
}
|
||||
|
||||
private static boolean bossStatusChanged() {
|
||||
return Float.floatToIntBits(capturedBossHealthScale) != Float.floatToIntBits(BossStatus.healthScale)
|
||||
|| capturedBossBarTime != BossStatus.statusBarTime
|
||||
|| !Objects.equals(capturedBossName, BossStatus.bossName)
|
||||
|| capturedBossColorModifier != BossStatus.hasColorModifier;
|
||||
}
|
||||
|
||||
private static void reject(Rejection reason) {
|
||||
if (recording != null && recording.rejection == null) {
|
||||
recording.rejection = reason;
|
||||
if (recording.capturingState) {
|
||||
GLStateManager.cancelExitCapture();
|
||||
recording.capturingState = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void rejectForeignDraw() {
|
||||
reject(Rejection.FOREIGN_DRAW);
|
||||
}
|
||||
|
||||
public static void rejectRenderFailed() {
|
||||
reject(Rejection.RENDER_FAILED);
|
||||
}
|
||||
|
||||
public static void rejectShadowOnly() {
|
||||
reject(Rejection.SHADOW_ONLY_SKIP);
|
||||
}
|
||||
|
||||
static void rejectUncapturedState() {
|
||||
reject(Rejection.UNCAPTURED_STATE);
|
||||
}
|
||||
|
||||
static void rejectPartFallback() {
|
||||
reject(Rejection.PART_FALLBACK);
|
||||
}
|
||||
|
||||
static void rejectBatcherBail() {
|
||||
reject(Rejection.BATCHER_BAIL);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
resetRecords();
|
||||
mainView = null;
|
||||
commands.release();
|
||||
recordPool.release();
|
||||
excluded.clear();
|
||||
lastType = null;
|
||||
loggedRejections.clear();
|
||||
}
|
||||
|
||||
enum Rejection {
|
||||
EXCLUDED( "excluded", "Player or configured excluded class."),
|
||||
FOREIGN_DRAW( "foreignDraw", "Uncaptured draw or nested entity rendering."),
|
||||
SHADOW_ONLY_SKIP( "shadowOnlySkip", "Shadow rendering skipped work needed in the main view."),
|
||||
RENDER_PASS( "renderPass", "Entity requires a render pass outside the supported pass 0."),
|
||||
NOT_IN_SHADOW_PASS( "notInShadowPass", "No shadow record this frame; shadow range or culling can cause this!"),
|
||||
UNSAFE_RENDERER( "unsafeRenderer", "Renderer is not marked safe for batching, or no renderer ran."),
|
||||
EMPTY_RECORD( "emptyRecord", "No replayable rendering commands were captured."),
|
||||
UNCAPTURED_STATE( "uncapturedState", "Unsupported render state or boss HUD side effects."),
|
||||
PART_FALLBACK( "partFallback", "A model part used the live rendering path."),
|
||||
BATCHER_BAIL( "batcherBail", "A draw could not use the model batcher."),
|
||||
RENDER_FAILED( "renderFailed", "The captured renderer call did not complete successfully."),
|
||||
NOT_READY( "notReady", "Main-pass batching is inactive or a required instanced shader variant is missing."),
|
||||
RENDERER_CHANGED( "rendererChanged", "The main renderer differs from the captured renderer."),
|
||||
PARTIAL_TICKS_CHANGED( "partialTicksChanged", "Animation time differs from the one the shadow pass recorded."),
|
||||
VIEW_CHANGED( "viewChanged", "Camera angles changed between shadow and main views."),
|
||||
EXIT_STATE_UNSUPPORTED( "exitStateUnsupported", "Renderer state includes an unsupported operation, state query, or unbalanced stack."),
|
||||
ENTRY_STATE_CHANGED( "entryStateChanged", "Main renderer entry state differs from a captured dependency, or a texture was deleted."),
|
||||
SHARED_RANGE_CHANGED( "sharedRangeChanged", "Captured instance storage was rebuilt or shortened.");
|
||||
|
||||
final long plot;
|
||||
final String label, description;
|
||||
|
||||
Rejection(String name, String description) {
|
||||
plot = Tracy.plotHandle("entityReplay." + name);
|
||||
label = name;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Record {
|
||||
final ExitState exitState = new ExitState();
|
||||
final ModelPartBatcher.BoundTexture texture = new ModelPartBatcher.BoundTexture();
|
||||
int start, end;
|
||||
Render renderer;
|
||||
Entity entity;
|
||||
float partialTicks;
|
||||
boolean hasBossStatus, bossColorModifier, capturingState;
|
||||
Rejection rejection;
|
||||
|
||||
void clear() {
|
||||
renderer = null;
|
||||
entity = null;
|
||||
texture.location = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,10 +247,9 @@ public final class EntityTextureAtlas {
|
||||
private Page newPage() {
|
||||
if (pages.size() == MAX_PAGES) return null;
|
||||
final int previous = GLStateManager.getBoundTextureForServerState();
|
||||
final int texture;
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
texture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
@@ -270,15 +269,15 @@ public final class EntityTextureAtlas {
|
||||
}
|
||||
|
||||
private void copy(int texture, Slot slot) {
|
||||
if (readFbo == 0) {
|
||||
readFbo = GLStateManager.glGenFramebuffers();
|
||||
drawFbo = GLStateManager.glGenFramebuffers();
|
||||
}
|
||||
final int previousRead = GLStateManager.getReadFramebuffer();
|
||||
final int previousDraw = GLStateManager.getDrawFramebuffer();
|
||||
final boolean scissor = GLStateManager.getScissorTest().isEnabled();
|
||||
final boolean scissor = GLStateManager.glIsEnabled(GL11.GL_SCISSOR_TEST);
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
if (readFbo == 0) {
|
||||
readFbo = GLStateManager.glGenFramebuffers();
|
||||
drawFbo = GLStateManager.glGenFramebuffers();
|
||||
}
|
||||
if (scissor) GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, readFbo);
|
||||
GLStateManager.attachTextureForReading(GL30.GL_COLOR_ATTACHMENT0, texture);
|
||||
|
||||
@@ -36,7 +36,6 @@ public final class GlintCapture {
|
||||
private boolean poseRestored;
|
||||
private int restoredGeneration;
|
||||
private boolean uniformLight;
|
||||
private int replayStart = -1, replayEnd = -1;
|
||||
|
||||
public void reset() {
|
||||
if (owner != null && owner.glintCapture == this) owner.glintCapture = null;
|
||||
@@ -52,7 +51,6 @@ public final class GlintCapture {
|
||||
base = false;
|
||||
trusted = false;
|
||||
poseRestored = false;
|
||||
replayStart = replayEnd = -1;
|
||||
}
|
||||
|
||||
boolean begin(RetainedTesrGroups groups) {
|
||||
@@ -81,10 +79,6 @@ public final class GlintCapture {
|
||||
return true;
|
||||
}
|
||||
|
||||
void beginShadowReplay() {
|
||||
replayStart = EntityShadowReplay.splitCommands();
|
||||
}
|
||||
|
||||
/** For records queued without running model or mod code; the caller replays before any GL state changes. */
|
||||
boolean beginTrusted(RetainedTesrGroups groups) {
|
||||
if (!open(groups)) return false;
|
||||
@@ -107,7 +101,6 @@ public final class GlintCapture {
|
||||
void invalidate() { valid = false; }
|
||||
void allowBase() { base = true; }
|
||||
boolean capturesBase() { return base && valid; }
|
||||
boolean keepsBaseUpload() { return capturesBase() && replayStart < 0; }
|
||||
|
||||
void record(RetainedTesrGroups.Group group, RetainedTesrGroups.InstanceColumns columns, RetainedTesrGroups.TexRun source, int start, int end, int instances) {
|
||||
if (!valid) return;
|
||||
@@ -151,7 +144,6 @@ public final class GlintCapture {
|
||||
valid &= GLStateManager.drawCalls == draws && used > 0;
|
||||
this.poseRestored = poseRestored;
|
||||
restoredGeneration = GLStateManager.getMvGeneration();
|
||||
if (replayStart >= 0) replayEnd = EntityShadowReplay.splitCommands();
|
||||
}
|
||||
|
||||
private boolean sameParentPose() {
|
||||
@@ -173,7 +165,7 @@ public final class GlintCapture {
|
||||
}
|
||||
for (int i = 0; i < used; i++) {
|
||||
final Range range = ranges.get(i);
|
||||
groups.replayRange(range.group, range.columns, range.source, range.start, range.end, range.instances, textureMatrix);
|
||||
range.group.replayRange(range.columns, range.source, range.start, range.end, range.instances, textureMatrix);
|
||||
}
|
||||
valid = false;
|
||||
return true;
|
||||
@@ -192,23 +184,12 @@ public final class GlintCapture {
|
||||
|
||||
boolean queueBothLayers(ModelPartBatcher batcher, RetainedTesrGroups groups, ResourceLocation glintTexture) {
|
||||
if (!baseStillValid(groups) || !sameLightAndOverlay(batcher.currentOverlay()) || !batcher.prepareSkippedArmorGlint(glintTexture)) return false;
|
||||
queueLayers(batcher);
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean recordBothLayers(ModelPartBatcher batcher, RetainedTesrGroups groups, ResourceLocation glintTexture) {
|
||||
if (!baseStillValid(groups) || !sameLightAndOverlay(batcher.currentOverlay())
|
||||
|| !batcher.recordArmorGlint(replayStart, replayEnd, glintTexture)) return false;
|
||||
valid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void queueLayers(ModelPartBatcher batcher) {
|
||||
for (int i = 0; i < used; i++) {
|
||||
final Range range = ranges.get(i);
|
||||
batcher.queueArmorGlintLayers(range.columns, range.source, range.start, range.end);
|
||||
}
|
||||
valid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean baseStillValid(RetainedTesrGroups groups) {
|
||||
|
||||
@@ -35,8 +35,6 @@ import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.coderbot.batchedentityrendering.impl.TransparencyType;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.RenderCondition;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
@@ -52,14 +50,12 @@ import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.ToLongFunction;
|
||||
|
||||
public final class ModelPartBatcher {
|
||||
|
||||
@@ -95,8 +91,6 @@ public final class ModelPartBatcher {
|
||||
private final RetainedTesrGroups groups = new RetainedTesrGroups(bufferSource);
|
||||
private final RetainedTesrGroups entityGroups = new RetainedTesrGroups(entitySource);
|
||||
private final RetainedTesrGroups shadowGroups = new RetainedTesrGroups(bufferSource);
|
||||
private final RetainedTesrGroups shadowEntityGroups = new RetainedTesrGroups(bufferSource);
|
||||
private final RetainedTesrGroups[] allGroups = {groups, entityGroups, shadowGroups, shadowEntityGroups};
|
||||
private RetainedTesrGroups activeGroups = groups;
|
||||
private AngelicaBufferSource activeSource = bufferSource;
|
||||
private boolean entityTranslucentsHeld;
|
||||
@@ -105,12 +99,6 @@ public final class ModelPartBatcher {
|
||||
private final Matrix4f identity = new Matrix4f();
|
||||
private final Matrix4f modelView = new Matrix4f();
|
||||
private final Matrix4f texMatrixScratch = new Matrix4f();
|
||||
private final Matrix4f replayView = new Matrix4f();
|
||||
private final EntityRenderCommands.DrawInputs drawInputs = new EntityRenderCommands.DrawInputs();
|
||||
Matrix4fc replayTransform;
|
||||
private boolean replayOffsetEnabled;
|
||||
private float replayOffsetFactor, replayOffsetUnits;
|
||||
private boolean loggedDroppedGlint;
|
||||
private int lastMvGeneration;
|
||||
private boolean mvCaptured;
|
||||
private int texGeneration;
|
||||
@@ -139,17 +127,11 @@ public final class ModelPartBatcher {
|
||||
public long statArmorLoopSkips() { return armorLoopSkips; }
|
||||
public long statArmorSectionSkips() { return armorSectionSkips; }
|
||||
public long statLiveFallbacks() { return liveFallbacks; }
|
||||
public long statInstancedDraws() { return sumGroups(g -> g.instancedDraws); }
|
||||
public long statInstancedInstances() { return sumGroups(g -> g.instancedInstances); }
|
||||
public long statCubeInstances() { return sumGroups(g -> g.cubeInstances); }
|
||||
public long statRedrawnInstances() { return sumGroups(g -> g.redrawnInstances); }
|
||||
public long statTexMatrixRuns() { return sumGroups(g -> g.texMatrixRuns); }
|
||||
|
||||
private long sumGroups(ToLongFunction<RetainedTesrGroups> stat) {
|
||||
long sum = 0;
|
||||
for (RetainedTesrGroups group : allGroups) sum += stat.applyAsLong(group);
|
||||
return sum;
|
||||
}
|
||||
public long statInstancedDraws() { return groups.instancedDraws + entityGroups.instancedDraws + shadowGroups.instancedDraws; }
|
||||
public long statInstancedInstances() { return groups.instancedInstances + entityGroups.instancedInstances + shadowGroups.instancedInstances; }
|
||||
public long statCubeInstances() { return groups.cubeInstances + entityGroups.cubeInstances + shadowGroups.cubeInstances; }
|
||||
public long statRedrawnInstances() { return groups.redrawnInstances + entityGroups.redrawnInstances + shadowGroups.redrawnInstances; }
|
||||
public long statTexMatrixRuns() { return groups.texMatrixRuns + entityGroups.texMatrixRuns + shadowGroups.texMatrixRuns; }
|
||||
public long statBail(BailReason reason) { return bails[reason.ordinal()]; }
|
||||
public long statShadowQuads() { return shadows.statQuads(); }
|
||||
public long statShadowDraws() { return shadows.statDraws(); }
|
||||
@@ -238,24 +220,6 @@ public final class ModelPartBatcher {
|
||||
private static int lastBoundGlId = -1;
|
||||
private static int lastBoundGlintSlot = ShaderGlint.NO_TINT;
|
||||
|
||||
static final class BoundTexture {
|
||||
ResourceLocation location;
|
||||
int id, glintSlot;
|
||||
|
||||
void capture() {
|
||||
id = GLStateManager.getBoundTextureForServerState(0);
|
||||
location = id == lastBoundGlId ? lastBoundLocation : null;
|
||||
glintSlot = id == lastBoundGlId ? lastBoundGlintSlot : ShaderGlint.NO_TINT;
|
||||
}
|
||||
|
||||
void apply() {
|
||||
if (GLStateManager.getBoundTextureForServerState(0) != id) return;
|
||||
lastBoundLocation = location;
|
||||
lastBoundGlId = id;
|
||||
lastBoundGlintSlot = glintSlot;
|
||||
}
|
||||
}
|
||||
|
||||
private ModelPartBatcher() {}
|
||||
|
||||
public static void onTextureSwap(int glId, int glintSlot) {
|
||||
@@ -294,17 +258,17 @@ public final class ModelPartBatcher {
|
||||
final long nowMs = System.currentTimeMillis();
|
||||
mvCaptured = false;
|
||||
texCaptured = false;
|
||||
final boolean replayEntities = mode == Mode.ENTITIES && AngelicaConfig.entityShadowReplayEnabled();
|
||||
activeGroups = shadow ? (replayEntities ? shadowEntityGroups : shadowGroups) : entityPass ? entityGroups : groups;
|
||||
activeGroups = shadow ? shadowGroups : entityPass ? entityGroups : groups;
|
||||
activeSource = entityPass ? entitySource : bufferSource;
|
||||
activeSource.setIdKind(mode == Mode.ENTITIES ? AngelicaBufferSource.GroupIdKind.ENTITY : AngelicaBufferSource.GroupIdKind.BLOCK_ENTITY);
|
||||
activeSource.capturePassLighting();
|
||||
BatchDrawDefaults.capturePassFramebuffer();
|
||||
activeGroups.beginPass(identity, 0, 0, 0, instancedThisCycle ? TesrBatchRenderer.INSTANCE.instancedRenderer : null, shaderPipeline);
|
||||
if (replayEntities && shaderPipeline != null) EntityShadowReplay.beginPass(shadow, MinecraftForgeClient.getRenderPass());
|
||||
if (nowMs - lastSweepMs >= SWEEP_INTERVAL_MS) {
|
||||
lastSweepMs = nowMs;
|
||||
for (RetainedTesrGroups group : allGroups) group.sweep(nowMs);
|
||||
groups.sweep(nowMs);
|
||||
entityGroups.sweep(nowMs);
|
||||
shadowGroups.sweep(nowMs);
|
||||
sweepTemplates(nowMs);
|
||||
}
|
||||
}
|
||||
@@ -438,19 +402,8 @@ public final class ModelPartBatcher {
|
||||
|
||||
/** Skipped glint blocks queue each layer as its own instanced draw, so a shader pack only needs its pass program current. */
|
||||
public boolean canQueueGlint() {
|
||||
return !shadow && canBatchGlint();
|
||||
}
|
||||
|
||||
private boolean cannotQueueOrRecordGlint() {
|
||||
return shadow && (mode != Mode.ENTITIES || !EntityShadowReplay.isRecording()) || !canBatchGlint();
|
||||
}
|
||||
|
||||
private boolean canBatchGlint() {
|
||||
return active && instancedThisCycle && BatchEligibility.batchingAllowed() && !GLStateManager.isRecordingDisplayList() && passProgramBound();
|
||||
}
|
||||
|
||||
private boolean passProgramBound() {
|
||||
return GLStateManager.getActiveProgram() == (shaderPipeline != null ? shaderPipeline.getActivePassProgramId() : 0);
|
||||
return active && instancedThisCycle && !shadow && BatchEligibility.batchingAllowed() && !GLStateManager.isRecordingDisplayList()
|
||||
&& GLStateManager.getActiveProgram() == (shaderPipeline != null ? shaderPipeline.getActivePassProgramId() : 0);
|
||||
}
|
||||
|
||||
public boolean replayGlint(GlintCapture capture) {
|
||||
@@ -473,25 +426,7 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
|
||||
public boolean beginArmorCapture(GlintCapture capture) {
|
||||
if (cannotQueueOrRecordGlint() || !capture.beginBase(activeGroups, currentOverlay())) return false;
|
||||
if (shadow) capture.beginShadowReplay();
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean canReplayGlint(TesrMaterial material) {
|
||||
return entityPassActive() && instancedThisCycle && activeGroups.glintCapture == null
|
||||
&& activeGroups.canCopyInstances(material);
|
||||
}
|
||||
|
||||
boolean canInstanceBaseAndGlint(EntityRenderCommands.Model base, boolean glintLightmap) {
|
||||
final EntityRenderCommands.DrawInputs inputs = base.inputs;
|
||||
if (!activeGroups.canCopyInstances(inputs.material)) return false;
|
||||
if (shaderPipeline == null) return true;
|
||||
final RenderCondition condition = inputs.pass.entityCondition(inputs.material.transparency() == TesrMaterial.Transparency.TRANSLUCENT);
|
||||
if (condition == null) return false;
|
||||
final Instancing kind = base.drawsCubes() ? Instancing.CUBE : Instancing.TEMPLATE;
|
||||
return shaderPipeline.hasInstancedVariant(condition, InputAvailability.of(inputs.texture != null, inputs.lightmap), kind)
|
||||
&& shaderPipeline.hasInstancedVariant(RenderCondition.GLINT, InputAvailability.of(true, glintLightmap), kind);
|
||||
return canQueueGlint() && capture.beginBase(activeGroups, currentOverlay());
|
||||
}
|
||||
|
||||
public boolean reuseArmorBase(GlintCapture base, GlintCapture glint) {
|
||||
@@ -511,29 +446,20 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
|
||||
public boolean queueSkippedArmorGlint(GlintCapture base, ResourceLocation glintTexture) {
|
||||
if (cannotQueueOrRecordGlint() || GLStateManager.getActiveTextureUnit() != 0 || !GLStateManager.getTextures().getTextureUnitStates(0).isEnabled()) return false;
|
||||
if (!(shadow ? base.recordBothLayers(this, activeGroups, glintTexture) : base.queueBothLayers(this, activeGroups, glintTexture))) return false;
|
||||
if (!shadow) {
|
||||
armorBaseReuses++;
|
||||
glintPassReuses++;
|
||||
armorSectionSkips++;
|
||||
}
|
||||
if (!canQueueGlint() || GLStateManager.getActiveTextureUnit() != 0 || !GLStateManager.getTextures().getTextureUnitStates(0).isEnabled() || !base.queueBothLayers(this, activeGroups, glintTexture)) return false;
|
||||
armorBaseReuses++;
|
||||
glintPassReuses++;
|
||||
armorSectionSkips++;
|
||||
BatchEligibility.onPartQueued();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean queueSkippedItemGlint(TemplateBuffer template, TesrMaterial material) {
|
||||
if (cannotQueueOrRecordGlint() || GLStateManager.getActiveTextureUnit() != 0 || !activeGroups.canCopyInstances(material) || !MatrixHelper.isIdentity(GLStateManager.getTextures().getTextureUnitMatrix(0))) return false;
|
||||
final EntityRenderCommands.DrawInputs inputs = glintInputs(ItemGlint.texture(), material, ShaderGlint.NO_TINT, false);
|
||||
if (shadow) {
|
||||
if (material == EntityMaterials.ITEM_GLINT && !ItemGlint.canResetStencil()) return false;
|
||||
final EntityRenderCommands.ItemGlint entry = EntityShadowReplay.recordItemGlint(currentModelView(), inputs);
|
||||
if (entry == null) return false;
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
entry.instance.set(template, GLSMConfig.packedLastBrightness(), VANILLA_GLINT_COLOR, currentOverlay(), currentEntityInfo(0));
|
||||
return true;
|
||||
}
|
||||
final RenderLayer layer = layerFor(ItemGlint.texture(), inputs);
|
||||
if (!canQueueGlint() || GLStateManager.getActiveTextureUnit() != 0 || !activeGroups.canCopyInstances(material) || !MatrixHelper.isIdentity(GLStateManager.getTextures().getTextureUnitMatrix(0))) return false;
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
final int cullCode = DrawState.liveCull(material);
|
||||
final RenderLayer layer = layerFor(ItemGlint.texture(), material, PassOverride.NONE, offset ? polygon.getOffsetFactor() : 0, offset ? polygon.getOffsetUnits() : 0, ShaderGlint.NO_TINT, cullCode, false, DrawState.liveLightmap());
|
||||
activeGroups.queueGlintLayers(template, layer, material, currentModelView(), GLSMConfig.packedLastBrightness(), VANILLA_GLINT_COLOR, currentOverlay(), currentEntityInfo(0),
|
||||
ItemGlint.firstMatrix(), ItemGlint.secondMatrix());
|
||||
parts++;
|
||||
@@ -541,23 +467,6 @@ public final class ModelPartBatcher {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean recordArmorGlint(int start, int end, ResourceLocation texture) {
|
||||
if (EntityShadowReplay.recordArmorGlint(start, end, glintInputs(texture, EntityMaterials.GLINT, ShaderGlint.NO_TINT, false)) == null) return false;
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private EntityRenderCommands.DrawInputs glintInputs(ResourceLocation texture, TesrMaterial material, int glintSlot, boolean lit) {
|
||||
return liveInputs(texture, 0, material, PassOverride.NONE, glintSlot, DrawState.liveCull(material), lit, DrawState.liveLightmap(), false);
|
||||
}
|
||||
|
||||
private EntityRenderCommands.DrawInputs liveInputs(ResourceLocation texture, int textureId, TesrMaterial material, PassOverride pass,
|
||||
int glintSlot, int cull, boolean lit, boolean lightmap, boolean afterGlint) {
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
return drawInputs.set(texture, textureId, material, pass, GLStateManager.getPolygonOffsetFillState().isEnabled(),
|
||||
polygon.getOffsetFactor(), polygon.getOffsetUnits(), glintSlot, cull, lit, lightmap, afterGlint);
|
||||
}
|
||||
|
||||
private Matrix4f currentModelView() {
|
||||
final int mvGen = GLStateManager.getMvGeneration();
|
||||
if (!mvCaptured || mvGen != lastMvGeneration) {
|
||||
@@ -591,7 +500,10 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
|
||||
private void setArmorGlint(ResourceLocation texture, int glintSlot, boolean lit, Matrix4f textureMatrix, int color) {
|
||||
armorGlintLayer = layerFor(texture, glintInputs(texture, EntityMaterials.GLINT, glintSlot, lit));
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
final int cullCode = DrawState.liveCull(EntityMaterials.GLINT);
|
||||
armorGlintLayer = layerFor(texture, EntityMaterials.GLINT, PassOverride.NONE, offset ? polygon.getOffsetFactor() : 0, offset ? polygon.getOffsetUnits() : 0, glintSlot, cullCode, lit, DrawState.liveLightmap());
|
||||
armorGlintTexture.set(textureMatrix);
|
||||
armorGlintColor = color;
|
||||
armorGlintLight = GLSMConfig.packedLastBrightness();
|
||||
@@ -616,10 +528,6 @@ public final class ModelPartBatcher {
|
||||
return entitiesActive() && !shadow;
|
||||
}
|
||||
|
||||
boolean canReplayEntity() {
|
||||
return entityPassActive() && passProgramBound();
|
||||
}
|
||||
|
||||
public boolean entitiesActive() {
|
||||
return active && mode == Mode.ENTITIES;
|
||||
}
|
||||
@@ -628,10 +536,6 @@ public final class ModelPartBatcher {
|
||||
return active && shadow;
|
||||
}
|
||||
|
||||
boolean recordsEntitySubmissions(RetainedTesrGroups groups) {
|
||||
return active && shadow && mode == Mode.ENTITIES && activeGroups == groups;
|
||||
}
|
||||
|
||||
public static boolean recordShadow(World world, Entity entity, double x, double y, double z, float shadowAlpha, float partialTicks, float shadowSize) {
|
||||
return INSTANCE.shadows.record(world, entity, x, y, z, shadowAlpha, partialTicks, shadowSize);
|
||||
}
|
||||
@@ -664,7 +568,6 @@ public final class ModelPartBatcher {
|
||||
return true;
|
||||
}
|
||||
if (shadow && isDepthEqualDecalState()) {
|
||||
EntityShadowReplay.rejectShadowOnly();
|
||||
return true;
|
||||
}
|
||||
return queueTemplate(entry.template, entry.cubes, entry.cubesInsideTexture, scale, null, false);
|
||||
@@ -716,14 +619,17 @@ public final class ModelPartBatcher {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
final Matrix4f texMatrix = texture != null && !unitTexIdentity() ? texMatrixScratch : null;
|
||||
final PassOverride pass = shaderPipeline != null ? PassOverride.capture() : PassOverride.NONE;
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
final float offsetFactor = offset ? polygon.getOffsetFactor() : 0.0f;
|
||||
final float offsetUnits = offset ? polygon.getOffsetUnits() : 0.0f;
|
||||
final int glintSlot = material.special().isGlint() ? lastBoundGlintSlot : ShaderGlint.NO_TINT;
|
||||
final EntityRenderCommands.DrawInputs inputs = liveInputs(texture, lastBoundGlId, material, pass, glintSlot,
|
||||
DrawState.liveCull(material), DrawState.liveLit(material), DrawState.liveLightmap(), afterGlint);
|
||||
final int cullCode = DrawState.liveCull(material);
|
||||
final boolean lit = DrawState.liveLit(material);
|
||||
final EntityTextureAtlas.Slot atlasSlot = texMatrix == null && cubesInsideTexture ? atlasSlotFor(material) : null;
|
||||
final RenderLayer layer = layerFor(atlasSlot != null ? Objects.requireNonNull(atlasSlot.page).location : texture, inputs);
|
||||
final RenderLayer layer = layerFor(atlasSlot != null ? Objects.requireNonNull(atlasSlot.page).location : texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit, DrawState.liveLightmap(), afterGlint);
|
||||
// Entities nested inside a TESR (mob spawner, OpenBlocks trophy) run in the block entity pass but carry an entity id
|
||||
final int entityId = groupId(shaderPipeline != null, mode == Mode.ENTITIES || pass.isEntityPhase(), CapturedRenderingState.INSTANCE.getCurrentRenderedEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity());
|
||||
final Color4 color = GLStateManager.getColor();
|
||||
@@ -731,110 +637,18 @@ public final class ModelPartBatcher {
|
||||
final int overlayABGR = currentOverlay();
|
||||
final long entityInfo = currentEntityInfo(CapturedRenderingState.INSTANCE.getCurrentRenderedItem());
|
||||
final int packedLight = GLSMConfig.packedLastBrightness();
|
||||
final Matrix4f modelView = currentModelView();
|
||||
activeGroups.queueAndRecord(template, cubes, scale, layer, modelView, packedLight, colorABGR, overlayABGR, entityInfo,
|
||||
entityId, texMatrix, atlasSlot, cubesInsideTexture, inputs);
|
||||
activeGroups.queue(0L, template, cubes, scale, layer, material, currentModelView(), packedLight, colorABGR, overlayABGR, entityInfo, entityId, texMatrix, atlasSlot);
|
||||
parts++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private EntityTextureAtlas.Slot atlasSlotFor(TesrMaterial material) {
|
||||
return atlasSlotFor(material, lastBoundGlId);
|
||||
}
|
||||
|
||||
private EntityTextureAtlas.Slot atlasSlotFor(TesrMaterial material, int textureId) {
|
||||
if (!AngelicaConfig.enableEntityTextureAtlas) return null;
|
||||
// Glint, overlay and custom shaders sample their own textures
|
||||
if (material.special() != TesrMaterial.SpecialRender.NONE || material.shader() != null || material.isUnfilteredAtlas() || material == EntityMaterials.OVERLAY) return null;
|
||||
if (!activeGroups.instancesCubes(material)) return null;
|
||||
if (shaderPipeline != null && !shaderPipeline.hasInstancedVariant(Instancing.CUBE)) return null;
|
||||
return EntityTextureAtlas.INSTANCE.slotFor(textureId, shaderPipeline != null);
|
||||
}
|
||||
|
||||
void beginReplay(Matrix4fc mainFromShadow) {
|
||||
replayTransform = mainFromShadow;
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
replayOffsetEnabled = GLStateManager.getPolygonOffsetFillState().isEnabled();
|
||||
replayOffsetFactor = polygon.getOffsetFactor();
|
||||
replayOffsetUnits = polygon.getOffsetUnits();
|
||||
}
|
||||
|
||||
private RenderLayer replayLayer(EntityRenderCommands.Draw entry, EntityTextureAtlas.Slot slot) {
|
||||
final EntityRenderCommands.DrawInputs inputs = entry.inputs;
|
||||
final boolean enabled = inputs.inheritedOffsetEnable ? replayOffsetEnabled : inputs.offsetEnabled;
|
||||
final float factor = inputs.inheritedOffsetValues ? replayOffsetFactor : inputs.offsetFactor;
|
||||
final float units = inputs.inheritedOffsetValues ? replayOffsetUnits : inputs.offsetUnits;
|
||||
final boolean lit = inputs.inheritedLighting ? DrawState.liveLit(inputs.material) : inputs.lit;
|
||||
return layerFor(slot != null ? Objects.requireNonNull(slot.page).location : inputs.texture,
|
||||
inputs.material, inputs.pass, enabled, factor, units, inputs.glintSlot, inputs.cull, lit, inputs.lightmap, inputs.afterGlint);
|
||||
}
|
||||
|
||||
void replay(EntityRenderCommands.Model entry) {
|
||||
final RetainedTesrGroups.InstanceRange replayed = entry.glint != null ? entry.replayed : null;
|
||||
final TesrMaterial material = entry.inputs.material;
|
||||
// begin() may have recycled the shadow pass's atlas region.
|
||||
// A reused base upload must keep the original UVs for the glint texture.
|
||||
final EntityTextureAtlas.Slot slot = (replayed == null || !replayed.reuseUpload) && !entry.hasTextureMatrix && entry.cubesInsideTexture
|
||||
? atlasSlotFor(material, entry.inputs.textureId) : null;
|
||||
final RenderLayer layer = replayLayer(entry, slot);
|
||||
final Matrix4f texMatrix = entry.hasTextureMatrix ? entry.textureMatrix : null;
|
||||
if (entry.source != null) {
|
||||
activeGroups.replayInstances(entry.source, entry.start, entry.end, layer, material, entry.entityId,
|
||||
replayTransform, texMatrix, slot, replayed);
|
||||
parts += entry.end - entry.start;
|
||||
} else {
|
||||
replayTransform.mul(entry.modelView, replayView);
|
||||
final EntityRenderCommands.Instance instance = entry.instance;
|
||||
activeGroups.queueReplay(instance.template, entry.cubes, entry.scale, layer, material, replayView,
|
||||
instance.light, instance.color, instance.overlay, instance.entityInfo, entry.entityId, texMatrix, slot, replayed);
|
||||
parts++;
|
||||
}
|
||||
}
|
||||
|
||||
void replay(EntityRenderCommands.ModelGlint entry) {
|
||||
final RenderLayer layer = replayLayer(entry, null);
|
||||
for (EntityRenderCommands.Model base = entry.base; base != null; base = base.nextGlintBase) {
|
||||
final RetainedTesrGroups.InstanceRange range = base.replayed;
|
||||
if (glintBaseMissing(range)) continue;
|
||||
activeGroups.referenceGlintLayers(range.columns, range.run, range.start, range.end, layer, entry.inputs.material,
|
||||
VANILLA_GLINT_COLOR, GlintClock.firstArmorMatrix(), GlintClock.secondArmorMatrix());
|
||||
parts += range.end - range.start;
|
||||
}
|
||||
}
|
||||
|
||||
void replay(EntityRenderCommands.ItemGlint entry) {
|
||||
final RetainedTesrGroups.InstanceRange base = entry.base.replayed;
|
||||
if (glintBaseMissing(base)) return;
|
||||
final RenderLayer layer = replayLayer(entry, null);
|
||||
base.columns.pose(base.end - 1, replayView);
|
||||
final EntityRenderCommands.Instance instance = entry.instance;
|
||||
activeGroups.queueGlintLayers(instance.template, layer, entry.inputs.material, replayView,
|
||||
instance.light, instance.color, instance.overlay, instance.entityInfo, ItemGlint.firstMatrix(), ItemGlint.secondMatrix());
|
||||
parts++;
|
||||
}
|
||||
|
||||
private boolean glintBaseMissing(RetainedTesrGroups.InstanceRange base) {
|
||||
if (base.columns != null) return false;
|
||||
if (!loggedDroppedGlint) {
|
||||
loggedDroppedGlint = true;
|
||||
AngelicaMod.LOGGER.warn("Entity shadow replay dropped a glint render because its base was not drawn instanced");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void replay(EntityRenderCommands.Stream entry) {
|
||||
final RenderLayer layer = replayLayer(entry, null);
|
||||
replayTransform.mul(entry.modelView, replayView);
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
state.pushEntityState();
|
||||
try {
|
||||
state.setCurrentEntityAndItem(entry.entity, entry.item);
|
||||
state.setCurrentBlockEntity(entry.blockEntity);
|
||||
state.setCurrentEntityColor(entry.red, entry.green, entry.blue, entry.alpha);
|
||||
activeGroups.streamVertices(layer, entry.entityId, entry.vertices, entry.vertexCount, replayView);
|
||||
} finally {
|
||||
state.popEntityState();
|
||||
}
|
||||
return EntityTextureAtlas.INSTANCE.slotFor(lastBoundGlId, shaderPipeline != null);
|
||||
}
|
||||
|
||||
private enum TessellatorRoute { DRAW_NOW, DRAW_NOW_EXPECTED, QUEUE }
|
||||
@@ -870,27 +684,23 @@ public final class ModelPartBatcher {
|
||||
if (!entityFire && (mode != Mode.BLOCK_ENTITIES || !BatchEligibility.insideRenderer())) return TessellatorRoute.DRAW_NOW;
|
||||
if (!tess.isDrawing || tess.drawMode != GL11.GL_QUADS || tess.vertexCount == 0 || (tess.vertexCount & 3) != 0) return TessellatorRoute.DRAW_NOW;
|
||||
if (shaderPipeline != null && TessellatorEntityIds.hasIdChanges(tess)) return TessellatorRoute.DRAW_NOW;
|
||||
if (DisplayListManager.isRecording() || !GLStateManager.getDepthTest().isEnabled() || !unitTexIdentity()) return TessellatorRoute.DRAW_NOW;
|
||||
if (!passProgramBound()) return TessellatorRoute.DRAW_NOW;
|
||||
if (DisplayListManager.isRecording() || !GLStateManager.glIsEnabled(GL11.GL_DEPTH_TEST) || !unitTexIdentity()) return TessellatorRoute.DRAW_NOW;
|
||||
if (GLStateManager.getActiveProgram() != (shaderPipeline != null ? shaderPipeline.getActivePassProgramId() : 0)) return TessellatorRoute.DRAW_NOW;
|
||||
final TesrMaterial material = EntityMaterials.fromCurrentState(true);
|
||||
if (material == null || material == EntityMaterials.OVERLAY) return TessellatorRoute.DRAW_NOW;
|
||||
if (lastBoundLocation == null || GLStateManager.getBoundTextureForServerState() != lastBoundGlId) return TessellatorRoute.DRAW_NOW;
|
||||
if (!entityFire && !BatchEligibility.batchingAllowed()) return TessellatorRoute.DRAW_NOW_EXPECTED;
|
||||
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
final PassOverride pass = shaderPipeline != null ? PassOverride.capture() : PassOverride.NONE;
|
||||
final EntityRenderCommands.DrawInputs inputs = liveInputs(lastBoundLocation, lastBoundGlId, material, pass, ShaderGlint.NO_TINT,
|
||||
DrawState.liveCull(material), DrawState.liveLit(material), DrawState.liveLightmap(), false);
|
||||
final RenderLayer layer = layerFor(lastBoundLocation, inputs);
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
final int cullCode = DrawState.liveCull(material);
|
||||
final RenderLayer layer = layerFor(lastBoundLocation, material, pass, offset ? polygon.getOffsetFactor() : 0.0f, offset ? polygon.getOffsetUnits() : 0.0f,
|
||||
ShaderGlint.NO_TINT, cullCode, DrawState.liveLit(material), DrawState.liveLightmap());
|
||||
// Flames carry entity_flame, which some packs may voxelize as a light source in the shadow pass
|
||||
final int groupId = groupId(shaderPipeline != null, mode == Mode.ENTITIES || pass.isEntityPhase(), CapturedRenderingState.INSTANCE.getCurrentRenderedEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity());
|
||||
TessellatorStreamingDrawer.fillMissingAttributes(tess);
|
||||
final Matrix4f modelView = currentModelView();
|
||||
if (entityFire) {
|
||||
activeGroups.streamAndRecord(layer, groupId, tess.rawBuffer, tess.vertexCount, modelView, inputs);
|
||||
} else {
|
||||
activeGroups.streamVertices(layer, groupId, tess.rawBuffer, tess.vertexCount, modelView);
|
||||
}
|
||||
activeGroups.streamVertices(layer, groupId, tess.rawBuffer, tess.vertexCount, currentModelView());
|
||||
return TessellatorRoute.QUEUE;
|
||||
}
|
||||
|
||||
@@ -983,13 +793,8 @@ public final class ModelPartBatcher {
|
||||
entry.cubesInsideTexture = CubeParams.allInsideTexture(cubes);
|
||||
}
|
||||
|
||||
private RenderLayer layerFor(ResourceLocation texture, EntityRenderCommands.DrawInputs inputs) {
|
||||
return layerFor(texture, inputs.material, inputs.pass, inputs.offsetEnabled, inputs.offsetFactor, inputs.offsetUnits,
|
||||
inputs.glintSlot, inputs.cull, inputs.lit, inputs.lightmap, inputs.afterGlint);
|
||||
}
|
||||
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, boolean offsetEnabled, float offsetFactor, float offsetUnits, int glintSlot, int cullCode, boolean lit, boolean lightmap, boolean afterGlint) {
|
||||
return layerFor(texture, material, pass, offsetEnabled ? offsetFactor : 0, offsetEnabled ? offsetUnits : 0, glintSlot, cullCode, lit, lightmap, afterGlint);
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cullCode, boolean lit, boolean lightmap) {
|
||||
return layerFor(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit, lightmap, false);
|
||||
}
|
||||
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cullCode, boolean lit, boolean lightmap, boolean afterGlint) {
|
||||
@@ -1022,10 +827,11 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (AngelicaConfig.entityShadowReplayEnabled()) EntityShadowReplay.clear();
|
||||
entityFireDepth = 0;
|
||||
shadows.reset();
|
||||
for (RetainedTesrGroups group : allGroups) group.clear();
|
||||
groups.clear();
|
||||
entityGroups.clear();
|
||||
shadowGroups.clear();
|
||||
templates.clear();
|
||||
entitySource.discard();
|
||||
bufferSource.discard();
|
||||
@@ -1046,7 +852,7 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
|
||||
public String getDebugString() {
|
||||
final String line = String.format("Parts: %d grp, inst %d, live %d", sumGroups(RetainedTesrGroups::groupCount), parts - lastParts, liveFallbacks - lastLiveFallbacks);
|
||||
final String line = String.format("Parts: %d grp, inst %d, live %d", groups.groupCount() + entityGroups.groupCount() + shadowGroups.groupCount(), parts - lastParts, liveFallbacks - lastLiveFallbacks);
|
||||
lastParts = parts;
|
||||
lastLiveFallbacks = liveFallbacks;
|
||||
return line;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** Reuses per-frame objects. */
|
||||
final class Pool<T> {
|
||||
private final ObjectArrayList<T> entries = new ObjectArrayList<>();
|
||||
private final Supplier<T> factory;
|
||||
private final Consumer<T> reset;
|
||||
private int used;
|
||||
|
||||
Pool(Supplier<T> factory, Consumer<T> reset) {
|
||||
this.factory = factory;
|
||||
this.reset = reset;
|
||||
}
|
||||
|
||||
T acquire() {
|
||||
if (used == entries.size()) entries.add(factory.get());
|
||||
return entries.get(used++);
|
||||
}
|
||||
|
||||
int size() { return used; }
|
||||
|
||||
void clear() {
|
||||
for (int i = 0; i < used; i++) reset.accept(entries.get(i));
|
||||
used = 0;
|
||||
}
|
||||
|
||||
void release() {
|
||||
clear();
|
||||
entries.clear();
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
private final Matrix4f baseMV = new Matrix4f();
|
||||
private final Matrix4f baseMVInv = new Matrix4f();
|
||||
private final Matrix4f scratchMat = new Matrix4f();
|
||||
private final Matrix4f replayModelView = new Matrix4f();
|
||||
private final Vector3f scratchVec = new Vector3f();
|
||||
private final Matrix4f drawMV = new Matrix4f();
|
||||
private final Matrix4f savedMV = new Matrix4f();
|
||||
@@ -96,31 +95,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
final IntArrayList uploadCounts = new IntArrayList();
|
||||
}
|
||||
|
||||
static final class InstanceRange {
|
||||
InstanceColumns columns;
|
||||
TexRun run;
|
||||
int start, end;
|
||||
boolean reuseUpload;
|
||||
|
||||
void set(InstanceColumns columns, TexRun run, int start, int end) {
|
||||
this.columns = columns;
|
||||
this.run = run;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
columns = null;
|
||||
run = null;
|
||||
start = end = 0;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
reset();
|
||||
reuseUpload = false;
|
||||
}
|
||||
}
|
||||
|
||||
static final class InstanceColumns {
|
||||
final ObjectArrayList<TemplateBuffer> templates = new ObjectArrayList<>();
|
||||
final ObjectArrayList<CubeParams[]> cubes = new ObjectArrayList<>();
|
||||
@@ -135,20 +109,17 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
float[] matrices = new float[16 * 64];
|
||||
int size;
|
||||
int referencedParts;
|
||||
long version;
|
||||
|
||||
boolean hasInstances() {
|
||||
return size > 0 || referencedParts > 0;
|
||||
}
|
||||
|
||||
private void reserveMatrices(int count) {
|
||||
final int need = (size + count) * 16;
|
||||
if (need > matrices.length) matrices = Arrays.copyOf(matrices, Math.max(need, matrices.length << 1));
|
||||
}
|
||||
|
||||
void add(TemplateBuffer templateOrNull, Matrix4fc mv, int light, int color, int overlay, long info) {
|
||||
reserveMatrices(1);
|
||||
mv.get(matrices, size * 16);
|
||||
final int base = size * 16;
|
||||
if (base + 16 > matrices.length) {
|
||||
matrices = Arrays.copyOf(matrices, matrices.length << 1);
|
||||
}
|
||||
mv.get(matrices, base);
|
||||
if (templateOrNull != null) templates.add(templateOrNull);
|
||||
lights.add(light);
|
||||
colors.add(color);
|
||||
@@ -165,10 +136,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
return run.source != null ? null : atlasSlot(i);
|
||||
}
|
||||
|
||||
Matrix4f pose(int i, Matrix4f dest) {
|
||||
return dest.set(matrices, i * 16);
|
||||
}
|
||||
|
||||
void addCubes(TemplateBuffer templateOrNull, CubeParams[] cubeParams, float scale, Matrix4fc mv, int light, int color, int overlay, long info, EntityTextureAtlas.Slot atlasSlot) {
|
||||
cubes.add(cubeParams);
|
||||
atlasSlots.add(atlasSlot);
|
||||
@@ -179,14 +146,17 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
|
||||
void appendRange(InstanceColumns src, int start, int end, int light, int color, int overlay) {
|
||||
final int n = end - start;
|
||||
reserveMatrices(n);
|
||||
final int need = (size + n) * 16;
|
||||
if (need > matrices.length) {
|
||||
matrices = Arrays.copyOf(matrices, Math.max(need, matrices.length << 1));
|
||||
}
|
||||
System.arraycopy(src.matrices, start * 16, matrices, size * 16, n * 16);
|
||||
if (src.templates.size() == src.size) {
|
||||
for (int i = start; i < end; i++) templates.add(src.templates.get(i));
|
||||
}
|
||||
if (src.cubes.size() == src.size) {
|
||||
for (int i = start; i < end; i++) cubes.add(src.cubes.get(i));
|
||||
atlasSlots.size(size + n);
|
||||
atlasSlots.size(atlasSlots.size() + n);
|
||||
scales.addElements(scales.size(), src.scales.elements(), start, n);
|
||||
}
|
||||
fill(lights, n, light);
|
||||
@@ -196,52 +166,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
size += n;
|
||||
}
|
||||
|
||||
void appendReplayRange(InstanceColumns src, int start, int end, Matrix4fc transform, EntityTextureAtlas.Slot atlasSlot) {
|
||||
final int n = end - start;
|
||||
reserveMatrices(n);
|
||||
// Modelview matrices are affine, so only the 3x4 upper part needs multiplying.
|
||||
// Also, JOML would copy each matrix into a Matrix4f and back out, so this is faster to do by hand
|
||||
final float t00 = transform.m00(), t01 = transform.m01(), t02 = transform.m02();
|
||||
final float t10 = transform.m10(), t11 = transform.m11(), t12 = transform.m12();
|
||||
final float t20 = transform.m20(), t21 = transform.m21(), t22 = transform.m22();
|
||||
final float t30 = transform.m30(), t31 = transform.m31(), t32 = transform.m32();
|
||||
final float[] s = src.matrices;
|
||||
final float[] d = matrices;
|
||||
for (int in = start * 16, out = size * 16, stop = end * 16; in < stop; in += 16, out += 16) {
|
||||
for (int c = 0; c < 12; c += 4) {
|
||||
final float x = s[in + c], y = s[in + c + 1], z = s[in + c + 2];
|
||||
d[out + c] = t00 * x + t10 * y + t20 * z;
|
||||
d[out + c + 1] = t01 * x + t11 * y + t21 * z;
|
||||
d[out + c + 2] = t02 * x + t12 * y + t22 * z;
|
||||
d[out + c + 3] = 0.0f;
|
||||
}
|
||||
final float x = s[in + 12], y = s[in + 13], z = s[in + 14];
|
||||
d[out + 12] = t00 * x + t10 * y + t20 * z + t30;
|
||||
d[out + 13] = t01 * x + t11 * y + t21 * z + t31;
|
||||
d[out + 14] = t02 * x + t12 * y + t22 * z + t32;
|
||||
d[out + 15] = 1.0f;
|
||||
}
|
||||
appendObjects(templates, src.templates, start, n);
|
||||
lights.addElements(lights.size(), src.lights.elements(), start, n);
|
||||
colors.addElements(colors.size(), src.colors.elements(), start, n);
|
||||
overlays.addElements(overlays.size(), src.overlays.elements(), start, n);
|
||||
infos.addElements(infos.size(), src.infos.elements(), start, n);
|
||||
if (!src.cubes.isEmpty()) {
|
||||
appendObjects(cubes, src.cubes, start, n);
|
||||
scales.addElements(scales.size(), src.scales.elements(), start, n);
|
||||
atlasSlots.size(size + n);
|
||||
if (atlasSlot != null) {
|
||||
Arrays.fill(atlasSlots.elements(), size, size + n, atlasSlot);
|
||||
anyAtlasSlot = true;
|
||||
}
|
||||
}
|
||||
size += n;
|
||||
}
|
||||
|
||||
private static <T> void appendObjects(ObjectArrayList<T> dst, ObjectArrayList<T> src, int start, int count) {
|
||||
dst.addElements(dst.size(), src.elements(), start, count);
|
||||
}
|
||||
|
||||
private static void fill(IntArrayList list, int n, int value) {
|
||||
final int from = list.size();
|
||||
list.size(from + n);
|
||||
@@ -249,7 +173,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
version++;
|
||||
templates.clear();
|
||||
cubes.clear();
|
||||
atlasSlots.clear();
|
||||
@@ -541,7 +464,11 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
return head;
|
||||
}
|
||||
|
||||
private void replayRange(InstanceColumns cols, TexRun original, int start, int end, int instances, Matrix4f matrix, boolean stableMatrix) {
|
||||
void replayRange(InstanceColumns cols, TexRun original, int start, int end, int instances, Matrix4f matrix) {
|
||||
replayRange(cols, original, start, end, instances, matrix, false);
|
||||
}
|
||||
|
||||
void replayRange(InstanceColumns cols, TexRun original, int start, int end, int instances, Matrix4f matrix, boolean stableMatrix) {
|
||||
TexRun head = null;
|
||||
for (int i = cols.runs.size() - 1; i >= 0; i--) {
|
||||
final TexRun candidate = cols.runs.get(i);
|
||||
@@ -753,17 +680,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void streamVertices(RenderLayer layer, int id, int[] packed, int vertexCount, Matrix4fc mv) {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
storeVertices(layer, id, packed, vertexCount, mv);
|
||||
}
|
||||
|
||||
void streamAndRecord(RenderLayer layer, int id, int[] packed, int vertexCount, Matrix4fc mv, EntityRenderCommands.DrawInputs inputs) {
|
||||
final boolean record = recordsEntitySubmission(inputs);
|
||||
storeVertices(layer, id, packed, vertexCount, mv);
|
||||
if (record) EntityShadowReplay.recordStream(mv, packed, vertexCount, id, inputs);
|
||||
}
|
||||
|
||||
private void storeVertices(RenderLayer layer, int id, int[] packed, int vertexCount, Matrix4fc mv) {
|
||||
if (glintCapture != null) glintCapture.invalidate();
|
||||
source.getBuffer(layer, id).addPackedVertices(packed, vertexCount, mv, scratchVec);
|
||||
streamedInstances++;
|
||||
@@ -778,42 +694,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void queue(long scope, TemplateBuffer template, CubeParams[] cubes, float scale, RenderLayer layer, TesrMaterial material, Matrix4f currentMV, int packedLight, int colorABGR, int overlayABGR, long entityInfo, int id, Matrix4f texMatrix, EntityTextureAtlas.Slot atlasSlot) {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
storeModel(scope, template, cubes, scale, layer, material, currentMV, packedLight, colorABGR, overlayABGR, entityInfo, id, texMatrix, atlasSlot, null);
|
||||
}
|
||||
|
||||
void queueAndRecord(TemplateBuffer template, CubeParams[] cubes, float scale, RenderLayer layer, Matrix4f currentMV,
|
||||
int packedLight, int colorABGR, int overlayABGR, long entityInfo, int id, Matrix4f texMatrix,
|
||||
EntityTextureAtlas.Slot atlasSlot, boolean cubesInsideTexture, EntityRenderCommands.DrawInputs inputs) {
|
||||
final boolean record = recordsEntitySubmission(inputs);
|
||||
final InstanceColumns captured = storeModel(0, template, cubes, scale, layer, inputs.material, currentMV, packedLight,
|
||||
colorABGR, overlayABGR, entityInfo, id, texMatrix, atlasSlot, null);
|
||||
if (!record) return;
|
||||
// Replay copies templates by index, so a range can only be shared when every instance has one.
|
||||
final InstanceColumns source = captured != null && captured.templates.size() == captured.size ? captured : null;
|
||||
final EntityRenderCommands.Model entry = EntityShadowReplay.recordModel(source, layer, inputs, id, cubesInsideTexture, currentMV, texMatrix);
|
||||
if (entry != null && source == null) {
|
||||
entry.instance.set(template, packedLight, colorABGR, overlayABGR, entityInfo);
|
||||
entry.cubes = cubes;
|
||||
entry.scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean recordsEntitySubmission(EntityRenderCommands.DrawInputs inputs) {
|
||||
if (!EntityShadowReplay.isRecording()) return false;
|
||||
if (!ModelPartBatcher.INSTANCE.recordsEntitySubmissions(this)) {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
return false;
|
||||
}
|
||||
GLStateManager.requireExitTextureFilter(inputs.textureId, inputs.material.isUnfilteredAtlas());
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean keepsBaseUpload(InstanceRange replayed) {
|
||||
return replayed != null && replayed.reuseUpload || glintCapture != null && glintCapture.keepsBaseUpload();
|
||||
}
|
||||
|
||||
private InstanceColumns storeModel(long scope, TemplateBuffer template, CubeParams[] cubes, float scale, RenderLayer layer, TesrMaterial material, Matrix4f currentMV, int packedLight, int colorABGR, int overlayABGR, long entityInfo, int id, Matrix4f texMatrix, EntityTextureAtlas.Slot atlasSlot, InstanceRange replayed) {
|
||||
final Group group = groupFor(layer, material, id);
|
||||
touch(group, layer);
|
||||
if (group.couplable) linkDepthScope(group, scope);
|
||||
@@ -824,7 +704,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
if (group.probe != null && group.probe.probing && (instancedPath || segments)) {
|
||||
probe(group, template, currentMV, packedLight, colorABGR, texMatrix);
|
||||
}
|
||||
final boolean capturingBase = keepsBaseUpload(replayed);
|
||||
final boolean capturingBase = glintCapture != null && glintCapture.capturesBase();
|
||||
if (instancedPath && cubes != null && material.shader() == null) {
|
||||
final TexRun run = group.openOrExtendRun(group.cubeColumns, texMatrix, false, capturingBase && group.opaque);
|
||||
group.cubeColumns.addCubes(deferred != null ? template : null, cubes, scale, currentMV, packedLight, colorABGR, overlayABGR, entityInfo, atlasSlot);
|
||||
@@ -832,8 +712,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
run.parts++;
|
||||
run.instances += cubes.length;
|
||||
if (glintCapture != null) glintCapture.record(group, group.cubeColumns, run, group.cubeColumns.size - 1, group.cubeColumns.size, cubes.length);
|
||||
if (replayed != null) replayed.set(group.cubeColumns, run, group.cubeColumns.size - 1, group.cubeColumns.size);
|
||||
return deferred != null ? group.cubeColumns : null;
|
||||
return;
|
||||
}
|
||||
if (instancedPath) {
|
||||
final TexRun run = group.openOrExtendRun(group.templateColumns, texMatrix, false, capturingBase && group.opaque);
|
||||
@@ -842,14 +721,13 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
run.parts++;
|
||||
run.instances++;
|
||||
if (glintCapture != null) glintCapture.record(group, group.templateColumns, run, group.templateColumns.size - 1, group.templateColumns.size, 1);
|
||||
if (replayed != null) replayed.set(group.templateColumns, run, group.templateColumns.size - 1, group.templateColumns.size);
|
||||
return group.templateColumns;
|
||||
return;
|
||||
}
|
||||
if (group.stream && glintCapture != null) glintCapture.invalidate();
|
||||
if (segments) {
|
||||
source.getBuffer(layer, id).addTemplateInstance(template, currentMV, scratchVec, colorABGR, packedLight, texMatrix);
|
||||
streamedInstances++;
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
if (!group.stream) {
|
||||
final DepthEqualSet set = activeDepthEqualSet(group);
|
||||
@@ -869,62 +747,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
if (!group.dirty && !group.snapshot.matches(index, template, cols.matrices, index * 16, packedLight, colorABGR, bakedTexMatrix)) {
|
||||
group.dirty = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void replayInstances(InstanceColumns src, int start, int end, RenderLayer layer, TesrMaterial material, int id,
|
||||
Matrix4fc transform, Matrix4f texMatrix, EntityTextureAtlas.Slot atlasSlot, InstanceRange replayed) {
|
||||
if (replayed != null) replayed.reset();
|
||||
final boolean cubes = !src.cubes.isEmpty();
|
||||
final Group group = streamsInstanced(material) && (!cubes || material.shader() == null) ? groupFor(layer, material, id) : null;
|
||||
if (group != null) touch(group, layer);
|
||||
if (group != null && drawsInstanced(group) && (group.probe == null || !group.probe.probing)) {
|
||||
group.queuedThisFrame = true;
|
||||
final InstanceColumns dst = cubes ? group.cubeColumns : group.templateColumns;
|
||||
final TexRun run = group.openOrExtendRun(dst, texMatrix, false, keepsBaseUpload(replayed) && group.opaque);
|
||||
final int first = dst.size;
|
||||
dst.appendReplayRange(src, start, end, transform, atlasSlot);
|
||||
run.last.end = dst.size;
|
||||
run.parts += end - start;
|
||||
final int instances = instanceCount(src, start, end, cubes);
|
||||
run.instances += instances;
|
||||
if (glintCapture != null) glintCapture.record(group, dst, run, first, dst.size, instances);
|
||||
if (replayed != null) replayed.set(dst, run, first, dst.size);
|
||||
} else {
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
state.pushEntityState();
|
||||
try {
|
||||
for (int i = start; i < end; i++) {
|
||||
transform.mul(src.pose(i, replayModelView), replayModelView);
|
||||
storeReplay(src.templates.get(i), cubes ? src.cubes.get(i) : null, cubes ? src.scales.getFloat(i) : 1,
|
||||
layer, material, replayModelView, src.lights.getInt(i), src.colors.getInt(i), src.overlays.getInt(i),
|
||||
src.infos.getLong(i), id, texMatrix, atlasSlot, null);
|
||||
}
|
||||
} finally {
|
||||
state.popEntityState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void queueReplay(TemplateBuffer template, CubeParams[] cubes, float scale, RenderLayer layer, TesrMaterial material,
|
||||
Matrix4f modelView, int light, int color, int overlay, long entityInfo, int id, Matrix4f texMatrix, EntityTextureAtlas.Slot atlasSlot, InstanceRange replayed) {
|
||||
if (replayed != null) replayed.reset();
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
state.pushEntityState();
|
||||
try {
|
||||
storeReplay(template, cubes, scale, layer, material, modelView, light, color, overlay, entityInfo, id, texMatrix, atlasSlot, replayed);
|
||||
} finally {
|
||||
state.popEntityState();
|
||||
}
|
||||
}
|
||||
|
||||
private void storeReplay(TemplateBuffer template, CubeParams[] cubes, float scale, RenderLayer layer, TesrMaterial material,
|
||||
Matrix4f modelView, int light, int color, int overlay, long entityInfo, int id, Matrix4f texMatrix, EntityTextureAtlas.Slot atlasSlot, InstanceRange replayed) {
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
state.setCurrentEntityAndItem(InstancedAttribs.unpackId(entityInfo, 0), InstancedAttribs.unpackId(entityInfo, 32));
|
||||
state.setCurrentBlockEntity(InstancedAttribs.unpackId(entityInfo, 16));
|
||||
AngelicaBufferSource.setEntityColor(overlay);
|
||||
storeModel(0, template, cubes, scale, layer, material, modelView, light, color, overlay, entityInfo, id, texMatrix, atlasSlot, replayed);
|
||||
}
|
||||
|
||||
private void linkDepthScope(Group group, long scope) {
|
||||
@@ -1066,7 +888,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void copyInstances(InstanceColumns src, int start, int end, RenderLayer layer, TesrMaterial material, int packedLight, int colorABGR, int overlayABGR, Matrix4f texMatrix) {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
final Group group = groupFor(layer, material, 0);
|
||||
touch(group, layer);
|
||||
final boolean cubes = !src.cubes.isEmpty();
|
||||
@@ -1086,7 +907,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void referenceInstances(InstanceColumns src, int start, int end, RenderLayer layer, TesrMaterial material, int colorABGR, Matrix4f texMatrix) {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
final Group group = groupFor(layer, material, 0);
|
||||
touch(group, layer);
|
||||
final boolean cubes = !src.cubes.isEmpty();
|
||||
@@ -1101,7 +921,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void referenceGlintLayers(InstanceColumns src, TexRun sourceRun, int start, int end, RenderLayer layer, TesrMaterial material, int colorABGR, Matrix4f first, Matrix4f second) {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
final Group group = groupFor(layer, material, 0);
|
||||
touch(group, layer);
|
||||
final boolean cubes = !src.cubes.isEmpty();
|
||||
@@ -1116,7 +935,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void queueGlintLayers(TemplateBuffer template, RenderLayer layer, TesrMaterial material, Matrix4f currentMV, int packedLight, int colorABGR, int overlayABGR, long entityInfo, Matrix4f first, Matrix4f second) {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
final Group group = groupFor(layer, material, 0);
|
||||
touch(group, layer);
|
||||
final InstanceColumns dst = group.templateColumns;
|
||||
@@ -1129,11 +947,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
group.replayRange(dst, run, index, index + 1, 1, second, true);
|
||||
}
|
||||
|
||||
void replayRange(Group group, InstanceColumns columns, TexRun original, int start, int end, int instances, Matrix4f matrix) {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
group.replayRange(columns, original, start, end, instances, matrix, false);
|
||||
}
|
||||
|
||||
private static int instanceCount(InstanceColumns src, int start, int end, boolean cubes) {
|
||||
if (!cubes) return end - start;
|
||||
int instances = 0;
|
||||
@@ -1626,7 +1439,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
void clear() {
|
||||
for (final ObjectArrayList<Group> list : byLayer.values()) {
|
||||
for (int i = 0, n = list.size(); i < n; i++) {
|
||||
list.get(i).clearInstances();
|
||||
list.get(i).mesh.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler;
|
||||
import com.gtnewhorizons.angelica.iris.IrisDisplayListState;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import net.irisshaders.iris.api.v0.IrisApi;
|
||||
@@ -59,7 +58,6 @@ public final class ShaderGlint {
|
||||
private ShaderGlint() {}
|
||||
|
||||
public static void beginGlint() {
|
||||
if (ShadowRenderer.ACTIVE) EntityShadowReplay.rejectShadowOnly();
|
||||
IrisDisplayListState.recordGlintSpan(true);
|
||||
|
||||
// If a glint span is already active, keep the outer span's saved state and
|
||||
@@ -122,12 +120,7 @@ public final class ShaderGlint {
|
||||
}
|
||||
|
||||
public static void bindTintedGlint(int slot) {
|
||||
if (slot < 0) return;
|
||||
if (ShadowRenderer.ACTIVE) {
|
||||
EntityShadowReplay.rejectShadowOnly();
|
||||
return;
|
||||
}
|
||||
if (!IrisApi.getInstance().isShaderPackInUse()) return;
|
||||
if (slot < 0 || ShadowRenderer.ACTIVE || !IrisApi.getInstance().isShaderPackInUse()) return;
|
||||
final ITextureObject mask = Minecraft.getMinecraft().getTextureManager().getTexture(GLINT_MASK);
|
||||
if (mask == null || GLStateManager.getBoundTextureForServerState() != mask.getGlTextureId()) return;
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, slotTextures[slot]);
|
||||
|
||||
@@ -847,6 +847,29 @@ public class SodiumGameOptionPages {
|
||||
.build());
|
||||
}
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.menuBlur"))
|
||||
.setTooltip(I18n.format("options.angelica.menuBlur.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableMenuBlur = value, opts -> AngelicaConfig.enableMenuBlur)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(int.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.menuBlurRadius"))
|
||||
.setTooltip(I18n.format("options.angelica.menuBlurRadius.tooltip"))
|
||||
.setControl(option -> new SliderControl(option, 1, 100, 1, ControlValueFormatter.number()))
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.setBinding((opts, value) -> AngelicaConfig.menuBlurRadius = value, opts -> AngelicaConfig.menuBlurRadius)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(int.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.menuBlurFadeTime"))
|
||||
.setTooltip(I18n.format("options.angelica.menuBlurFadeTime.tooltip"))
|
||||
.setControl(option -> new SliderControl(option, 0, 2000, 50, ControlValueFormatter.milliseconds()))
|
||||
.setBinding((opts, value) -> AngelicaConfig.menuBlurFadeTime = value, opts -> AngelicaConfig.menuBlurFadeTime)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
return new OptionPage(I18n.format("sodium.options.pages.appearance"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -43,6 +43,10 @@ public interface ControlValueFormatter {
|
||||
return (v) -> v + "s";
|
||||
}
|
||||
|
||||
static ControlValueFormatter milliseconds() {
|
||||
return (v) -> v + "ms";
|
||||
}
|
||||
|
||||
static ControlValueFormatter quantity(String name) {
|
||||
return (v) -> I18n.format(name, v);
|
||||
}
|
||||
|
||||
@@ -205,13 +205,17 @@ public class AngelicaBufferSource implements Groupable {
|
||||
|
||||
private void restoreIrisState() {
|
||||
if (!irisStateSaved) return;
|
||||
CapturedRenderingState.INSTANCE.popEntityState();
|
||||
CapturedRenderingState.INSTANCE.popCurrentEntityColor();
|
||||
CapturedRenderingState.INSTANCE.popCurrentBlockEntity();
|
||||
CapturedRenderingState.INSTANCE.popCurrentEntityAndItem();
|
||||
irisStateSaved = false;
|
||||
}
|
||||
|
||||
private void saveIrisState() {
|
||||
if (irisStateSaved) return;
|
||||
CapturedRenderingState.INSTANCE.pushEntityState();
|
||||
CapturedRenderingState.INSTANCE.pushCurrentEntityAndItem();
|
||||
CapturedRenderingState.INSTANCE.pushCurrentBlockEntity();
|
||||
CapturedRenderingState.INSTANCE.pushCurrentEntityColor();
|
||||
irisStateSaved = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ public class AlphaTestStorage {
|
||||
@Getter
|
||||
private static boolean alphaTestLocked;
|
||||
|
||||
@Getter
|
||||
private static boolean overrideHeld;
|
||||
|
||||
public static void overrideAlphaTest(AlphaTest override) {
|
||||
|
||||
@@ -42,17 +42,12 @@ public final class Program extends GlResource {
|
||||
}
|
||||
|
||||
public void use() {
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
RenderSystem.memoryBarrier(GL42.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL42.GL_TEXTURE_FETCH_BARRIER_BIT | GL43.GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
bindManaged(getGlId());
|
||||
RenderSystem.memoryBarrier(GL42.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL42.GL_TEXTURE_FETCH_BARRIER_BIT | GL43.GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
bindManaged(getGlId());
|
||||
|
||||
uniforms.update();
|
||||
samplers.update();
|
||||
images.update();
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
uniforms.update();
|
||||
samplers.update();
|
||||
images.update();
|
||||
}
|
||||
|
||||
static void bindManaged(int program) {
|
||||
@@ -66,14 +61,9 @@ public final class Program extends GlResource {
|
||||
}
|
||||
|
||||
public static void unbind() {
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
ProgramUniforms.clearActiveUniforms();
|
||||
ProgramSamplers.clearActiveSamplers();
|
||||
GLStateManager.glUseProgram(0);
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
ProgramUniforms.clearActiveUniforms();
|
||||
ProgramSamplers.clearActiveSamplers();
|
||||
GLStateManager.glUseProgram(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.coderbot.iris.layer;
|
||||
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.RenderCondition;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPhase;
|
||||
|
||||
@@ -61,11 +60,6 @@ public final class PassOverride {
|
||||
return phase == WorldRenderingPhase.ENTITIES;
|
||||
}
|
||||
|
||||
public RenderCondition entityCondition(boolean defaultTranslucent) {
|
||||
if (special != null || phase != null && phase != WorldRenderingPhase.ENTITIES) return null;
|
||||
return (translucent != null ? translucent : defaultTranslucent) ? RenderCondition.ENTITIES_TRANSLUCENT : RenderCondition.ENTITIES;
|
||||
}
|
||||
|
||||
public String nameSuffix() {
|
||||
if (special == null && translucent == null && phase == null) {
|
||||
return "";
|
||||
|
||||
@@ -1437,24 +1437,10 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
|
||||
void rebindProgram() {
|
||||
if (program != null) program.use();
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
DeferredWorldRenderingPipeline.this.customUniforms.push(this);
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
DeferredWorldRenderingPipeline.this.customUniforms.push(this);
|
||||
}
|
||||
|
||||
private void useNow() {
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
applyState();
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyState() {
|
||||
DepthColorStorage.unlockDepthColor();
|
||||
|
||||
if (isBeforeTranslucent) {
|
||||
@@ -1499,19 +1485,14 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
}
|
||||
|
||||
public void stopUsing() {
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
DepthColorStorage.unlockDepthColor();
|
||||
DepthColorStorage.unlockDepthColor();
|
||||
|
||||
if (alphaTestOverride != null) {
|
||||
AlphaTestOverride.restore();
|
||||
}
|
||||
if (alphaTestOverride != null) {
|
||||
AlphaTestOverride.restore();
|
||||
}
|
||||
|
||||
if (blendModeOverride != null || (bufferBlendOverrides != null && !bufferBlendOverrides.isEmpty())) {
|
||||
BlendModeOverride.restore();
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
if (blendModeOverride != null || (bufferBlendOverrides != null && !bufferBlendOverrides.isEmpty())) {
|
||||
BlendModeOverride.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1623,11 +1604,6 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
return current != null && current.instancedVariant(kind) != null;
|
||||
}
|
||||
|
||||
public boolean hasInstancedVariant(RenderCondition condition, InputAvailability availability, Instancing kind) {
|
||||
final Pass pass = table.match(condition, availability);
|
||||
return pass != null && pass.instancedVariant(kind) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindInstancedVariant(Instancing kind) {
|
||||
activeInstancing = kind;
|
||||
@@ -2520,34 +2496,25 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
@Override
|
||||
public void onBindTexture(int id) {
|
||||
if (shouldBindPBR && isRenderingWorld) {
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
final PBRTextureHolder pbrHolder = PBRTextureManager.INSTANCE.getOrLoadHolder(id);
|
||||
currentNormalTexture = pbrHolder.getNormalTexture().getGlTextureId();
|
||||
currentSpecularTexture = pbrHolder.getSpecularTexture().getGlTextureId();
|
||||
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
updatePbrTextures(id);
|
||||
final TextureFormat textureFormat = TextureFormatLoader.getFormat();
|
||||
if (textureFormat != null) {
|
||||
textureFormat.setupTextureParameters(PBRType.NORMAL, pbrHolder.getNormalTexture());
|
||||
textureFormat.setupTextureParameters(PBRType.SPECULAR, pbrHolder.getSpecularTexture());
|
||||
}
|
||||
|
||||
PBRTextureManager.notifyPBRTexturesChanged();
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePbrTextures(int id) {
|
||||
final PBRTextureHolder pbrHolder = PBRTextureManager.INSTANCE.getOrLoadHolder(id);
|
||||
currentNormalTexture = pbrHolder.getNormalTexture().getGlTextureId();
|
||||
currentSpecularTexture = pbrHolder.getSpecularTexture().getGlTextureId();
|
||||
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
final TextureFormat textureFormat = TextureFormatLoader.getFormat();
|
||||
if (textureFormat != null) {
|
||||
textureFormat.setupTextureParameters(PBRType.NORMAL, pbrHolder.getNormalTexture());
|
||||
textureFormat.setupTextureParameters(PBRType.SPECULAR, pbrHolder.getSpecularTexture());
|
||||
}
|
||||
|
||||
PBRTextureManager.notifyPBRTexturesChanged();
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
}
|
||||
|
||||
private static CompletableFuture<Map<PatchShaderType, String>> submitCompositeTransform(ProgramSource source, TextureStage stage,
|
||||
Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap) {
|
||||
return Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchComposite(
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.profiling.RenderClassTimings;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.PendingTesrDraws;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.SectionBuiltBlockEntity;
|
||||
@@ -591,12 +590,9 @@ public class ShadowRenderer {
|
||||
}
|
||||
for (Entity entity : renderedEntitiesList) {
|
||||
final long start = Tracy.ENABLED ? System.nanoTime() : 0L;
|
||||
final boolean recording = AngelicaConfig.entityShadowReplayEnabled() && EntityShadowReplay.beginEntity(entity, tickDelta);
|
||||
boolean completed = false;
|
||||
try {
|
||||
completed = RenderManager.instance.renderEntitySimple(entity, tickDelta);
|
||||
RenderManager.instance.renderEntitySimple(entity, tickDelta);
|
||||
} finally {
|
||||
if (recording) EntityShadowReplay.endEntity(completed);
|
||||
if (Tracy.ENABLED) RenderClassTimings.SHADOW_ENTITY.add(entity.getClass(), System.nanoTime() - start);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,18 +161,6 @@ public class CapturedRenderingState {
|
||||
applyCurrentEntityColor(r, g, b, a);
|
||||
}
|
||||
|
||||
public void pushEntityState() {
|
||||
pushCurrentEntityAndItem();
|
||||
pushCurrentBlockEntity();
|
||||
pushCurrentEntityColor();
|
||||
}
|
||||
|
||||
public void popEntityState() {
|
||||
popCurrentEntityColor();
|
||||
popCurrentBlockEntity();
|
||||
popCurrentEntityAndItem();
|
||||
}
|
||||
|
||||
private void applyCurrentEntityColor(float r, float g, float b, float a) {
|
||||
if (currentEntityColor.equals(r, g, b, a)) return;
|
||||
this.currentEntityColor.set(r, g, b, a);
|
||||
|
||||
@@ -391,6 +391,12 @@ options.angelica.disablef3=Disable F3 Additions
|
||||
options.angelica.disablef3.tooltip=Disables Angelica's additions to the F3 debug screen.
|
||||
options.angelica.verbosef3=Verbose F3
|
||||
options.angelica.verbosef3.tooltip=Shows developer counters (fixed-function shaders, streaming, TESR batching, GPU transfer) on the F3 screen. Toggle in game with F3+V.
|
||||
options.angelica.menuBlur=Menu Blur
|
||||
options.angelica.menuBlur.tooltip=Blurs the world behind open menus like the inventory or the pause menu. Chat is never blurred. Excluded menus and background colors can be changed in the Angelica config.
|
||||
options.angelica.menuBlurRadius=Menu Blur Strength
|
||||
options.angelica.menuBlurRadius.tooltip=How strong the menu blur is. Higher values blur more.
|
||||
options.angelica.menuBlurFadeTime=Menu Blur Fade Time
|
||||
options.angelica.menuBlurFadeTime.tooltip=How long the menu blur takes to fade in after a menu opens. 0 shows it instantly.
|
||||
angelica.debug.verbose.on=Verbose F3: shown
|
||||
angelica.debug.verbose.off=Verbose F3: hidden
|
||||
options.angelica.texteffects.rgb_colors=RGB Colors
|
||||
|
||||
+1
-4
@@ -1,10 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.FrameHooks;
|
||||
import com.gtnewhorizons.angelica.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -29,9 +27,8 @@ public class MixinMinecraft_FrameHook {
|
||||
}
|
||||
|
||||
@Inject(method = "runGameLoop", at = @At("HEAD"))
|
||||
private void angelica$beginRenderFrame(CallbackInfo ci) {
|
||||
private void angelica$beginGlintFrame(CallbackInfo ci) {
|
||||
GlintClock.beginFrame(Minecraft.getSystemTime());
|
||||
if (AngelicaConfig.entityShadowReplayEnabled()) EntityShadowReplay.beginFrame();
|
||||
}
|
||||
|
||||
@Inject(method = "func_147120_f"/*resetSize*/, at = @At("HEAD"))
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.blur;
|
||||
|
||||
import com.gtnewhorizons.angelica.blur.MenuBlur;
|
||||
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_MenuBlur {
|
||||
|
||||
@Inject(
|
||||
method = "updateCameraAndRender",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/EntityRenderer;renderWorld(FJ)V", shift = At.Shift.AFTER))
|
||||
private void angelica$blurWorldBehindGui(float partialTicks, CallbackInfo ci) {
|
||||
MenuBlur.renderBlur();
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.blur;
|
||||
|
||||
import com.gtnewhorizons.angelica.blur.MenuBlur;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
|
||||
@Mixin(GuiScreen.class)
|
||||
public class MixinGuiScreen_MenuBlur {
|
||||
|
||||
@ModifyConstant(method = "drawWorldBackground", constant = @Constant(intValue = 0xC0101010))
|
||||
private int angelica$blurGradientTop(int original) {
|
||||
return MenuBlur.getBackgroundColor(original, AngelicaConfig.menuBlurGradientTopColor);
|
||||
}
|
||||
|
||||
@ModifyConstant(method = "drawWorldBackground", constant = @Constant(intValue = 0xD0101010))
|
||||
private int angelica$blurGradientBottom(int original) {
|
||||
return MenuBlur.getBackgroundColor(original, AngelicaConfig.menuBlurGradientBottomColor);
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import net.minecraft.entity.boss.BossStatus;
|
||||
import net.minecraft.entity.boss.IBossDisplayData;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(BossStatus.class)
|
||||
public class MixinBossStatus_EntityReplay {
|
||||
@Surround(method = "setBossStatus")
|
||||
private static void angelica$observeBossStatus() {
|
||||
}
|
||||
|
||||
@Surround.Return
|
||||
private static void angelica$recordBossStatus(IBossDisplayData boss, boolean colorModifier) {
|
||||
EntityShadowReplay.recordBossStatus(boss, colorModifier);
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Slice;
|
||||
|
||||
@Mixin(value = RenderGlobal.class, priority = 900) // Lower priority due to Tracy
|
||||
public class MixinRenderGlobal_EntityReplay {
|
||||
@Surround(method = "renderEntities",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/RenderManager;renderEntitySimple(Lnet/minecraft/entity/Entity;F)Z"),
|
||||
slice = @Slice(
|
||||
from = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/profiler/Profiler;endStartSection(Ljava/lang/String;)V", args = "ldc=entities"),
|
||||
to = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/profiler/Profiler;endStartSection(Ljava/lang/String;)V", args = "ldc=blockentities")))
|
||||
private void angelica$replayEntity(RenderManager manager, Entity entity, float partialTicks) {
|
||||
@Surround.Skip final boolean replayed = EntityShadowReplay.replay(manager, entity, partialTicks, MinecraftForgeClient.getRenderPass());
|
||||
}
|
||||
|
||||
@Surround.Skipped
|
||||
private boolean angelica$entityReplayed() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
-6
@@ -1,10 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IBatchEligibility;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -19,15 +17,11 @@ public class MixinRenderManager_BatchEligibility {
|
||||
private void angelica$observeDoRender(Render render, Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
final IBatchEligibility holder = (IBatchEligibility) render;
|
||||
final byte state = holder.angelica$batchState();
|
||||
final boolean recording = AngelicaConfig.entityShadowReplayEnabled() && EntityShadowReplay.beginRenderer(render, entity, partialTicks, state);
|
||||
BatchEligibility.begin(state, GLStateManager.drawCalls);
|
||||
boolean completed = false;
|
||||
try {
|
||||
render.doRender(entity, x, y, z, yaw, partialTicks);
|
||||
completed = true;
|
||||
} finally {
|
||||
final byte next = BatchEligibility.end(state, GLStateManager.drawCalls);
|
||||
if (recording) EntityShadowReplay.endRenderer(completed, next);
|
||||
if (next != state) {
|
||||
holder.angelica$setBatchState(next);
|
||||
BatchEligibility.onStateChange(render, next);
|
||||
|
||||
-2
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@@ -13,7 +12,6 @@ public abstract class MixinRender_NameTagDraws {
|
||||
|
||||
@Surround(method = "func_147906_a")
|
||||
private void angelica$countNameTagDraws() {
|
||||
EntityShadowReplay.rejectForeignDraw();
|
||||
BatchEligibility.beginExpectedDraws(GLStateManager.drawCalls);
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -5,7 +5,6 @@ import com.gtnewhorizons.angelica.helpers.RendererLivingEntityHelper;
|
||||
import com.gtnewhorizons.angelica.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.GlintCapture;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
@@ -143,7 +142,6 @@ public abstract class MixinRendererLivingEntity_GlintClock {
|
||||
@Inject(method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;error(Ljava/lang/String;Ljava/lang/Throwable;)V", remap = false))
|
||||
private void angelica$closeAfterFailedRender(CallbackInfo ci) {
|
||||
EntityShadowReplay.rejectRenderFailed();
|
||||
if (angelica$capturingBase) {
|
||||
angelica$capturingBase = false;
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
+6
-27
@@ -1,7 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.textures;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.TextureUtilHooks;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -21,29 +20,9 @@ public class MixinTextureUtil {
|
||||
TextureUtilHooks.setMaxLevel(mipmapLevels);
|
||||
}
|
||||
|
||||
@Surround(id = "saveFilter", method = "func_152777_a(ZZF)V")
|
||||
private static void angelica$beginFilterSave(boolean blurred, boolean mipmap, float aniso) {
|
||||
@Surround.Carry boolean captured = TextureUtilHooks.beginFilterSave(blurred, mipmap, aniso);
|
||||
}
|
||||
|
||||
@Surround.Catch("saveFilter")
|
||||
private static void angelica$filterSaveFailed(Throwable failure, @Surround.Carry boolean captured) {
|
||||
TextureUtilHooks.filterFailed(captured);
|
||||
}
|
||||
|
||||
@Surround.Finally("saveFilter")
|
||||
private static void angelica$finishFilterSave(@Surround.Carry boolean captured) {
|
||||
TextureUtilHooks.endFilterSave(captured);
|
||||
}
|
||||
|
||||
@Surround(id = "restoreFilter", method = "func_147945_b()V")
|
||||
private static void angelica$beginFilterRestore() {
|
||||
@Surround.Carry boolean captured = TextureUtilHooks.beginFilterRestore();
|
||||
}
|
||||
|
||||
@Surround.Catch("restoreFilter")
|
||||
private static void angelica$filterRestoreFailed(Throwable failure, @Surround.Carry boolean captured) {
|
||||
TextureUtilHooks.filterFailed(captured);
|
||||
@Inject(method = "func_152777_a(ZZF)V", at = @At("HEAD"))
|
||||
private static void angelica$captureSaveBoundId(boolean blurred, boolean mipmap, float aniso, CallbackInfo ci) {
|
||||
TextureUtilHooks.captureBoundFilterTexture();
|
||||
}
|
||||
|
||||
@Redirect(method = "func_147945_b()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/TextureUtil;func_147952_b(II)V"))
|
||||
@@ -56,8 +35,8 @@ public class MixinTextureUtil {
|
||||
TextureUtilHooks.restoreAniso(aniso);
|
||||
}
|
||||
|
||||
@Surround.Finally("restoreFilter")
|
||||
private static void angelica$finishFilterRestore(@Surround.Carry boolean captured) {
|
||||
TextureUtilHooks.endFilterRestore(captured);
|
||||
@Inject(method = "func_147945_b()V", at = @At("RETURN"))
|
||||
private static void angelica$clearSavedAfterRestore(CallbackInfo ci) {
|
||||
TextureUtilHooks.clearSavedFilterTexture();
|
||||
}
|
||||
}
|
||||
-3
@@ -1,11 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.mojang.Camera;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import jss.notfine.core.SettingsManager;
|
||||
import net.coderbot.iris.Iris;
|
||||
@@ -90,7 +88,6 @@ public abstract class MixinEntityRenderer implements IResourceManagerReloadListe
|
||||
if (renderer != null) {
|
||||
renderer.setShadowPassPlayerViewport(((ViewportProvider) playerFrustum).sodium$createViewport());
|
||||
}
|
||||
if (AngelicaConfig.entityShadowReplayEnabled()) EntityShadowReplay.setMainView(playerFrustum);
|
||||
Iris.getPipelineManager().getPipelineNullable().renderShadows((EntityRenderer) (Object) this, Camera.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
-1696
File diff suppressed because it is too large.
Load diff
@@ -1,13 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.ExitState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
@@ -16,9 +12,6 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
@@ -32,11 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.cubes;
|
||||
import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.onlyGroup;
|
||||
import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.template;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -75,7 +63,6 @@ class EntityTextureAtlasGLTest {
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.cancelExitCapture();
|
||||
EntityTextureAtlas.INSTANCE.freePages();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glDeleteFramebuffers(passFbo);
|
||||
@@ -88,66 +75,6 @@ class EntityTextureAtlasGLTest {
|
||||
return 0xFF000000 | (x * 50 + seed) << 16 | (y * 90 + seed) << 8 | 0x20;
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({"false, false", "true, false", "false, true", "true, true"})
|
||||
void entityReplayResolvesAtlasRegionsWithoutRemappingSharedGlintUploads(boolean shared, boolean glint) {
|
||||
final boolean atlasEnabled = AngelicaConfig.enableEntityTextureAtlas;
|
||||
final ModelPartBatcher batcher = ModelPartBatcher.INSTANCE;
|
||||
final RetainedTesrGroups previousGroups = Reflect.get(batcher, "activeGroups");
|
||||
final RetainedTesrGroups groups = new RetainedTesrGroups(new AngelicaBufferSource());
|
||||
final int otherTexture = texture(GL11.GL_NEAREST, 12);
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(new InstanceRing()),
|
||||
new RetainedTesrTestSupport.CountingPipeline(true, true));
|
||||
try {
|
||||
AngelicaConfig.enableEntityTextureAtlas = true;
|
||||
Reflect.set(batcher, "activeGroups", groups);
|
||||
final EntityTextureAtlas.Slot shadowSlot = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
assertNotNull(shadowSlot);
|
||||
EntityTextureAtlas.INSTANCE.onTextureDeleted(source);
|
||||
EntityTextureAtlas.INSTANCE.recycleReleasedRegions();
|
||||
assertSame(shadowSlot, EntityTextureAtlas.INSTANCE.slotFor(otherTexture, false));
|
||||
|
||||
final EntityRenderCommands.Model entry = new EntityRenderCommands.Model();
|
||||
entry.instance.template = template();
|
||||
entry.cubes = cubes(1);
|
||||
entry.cubesInsideTexture = true;
|
||||
entry.scale = 1;
|
||||
entry.inputs.material = EntityMaterials.CUTOUT;
|
||||
entry.inputs.texture = new ResourceLocation("angelica", "test/replay");
|
||||
entry.inputs.textureId = source;
|
||||
entry.inputs.pass = PassOverride.NONE;
|
||||
entry.inputs.glintSlot = -1;
|
||||
if (glint) {
|
||||
entry.glint = new EntityRenderCommands.ModelGlint();
|
||||
entry.replayed = new RetainedTesrGroups.InstanceRange();
|
||||
entry.replayed.reuseUpload = true;
|
||||
}
|
||||
if (shared) {
|
||||
entry.source = new RetainedTesrGroups.InstanceColumns();
|
||||
entry.source.addCubes(entry.instance.template, entry.cubes, 1, new Matrix4f(), 0, -1, 0, 0, shadowSlot);
|
||||
entry.end = 1;
|
||||
}
|
||||
batcher.beginReplay(new Matrix4f());
|
||||
batcher.replay(entry);
|
||||
|
||||
final RetainedTesrGroups.Group main = onlyGroup(groups);
|
||||
final EntityTextureAtlas.Slot mainSlot = main.cubeColumns.atlasSlot(0);
|
||||
if (glint) {
|
||||
assertNull(mainSlot, "glint reuses the armor upload, whose UVs must still address the original texture");
|
||||
assertEquals(entry.inputs.texture, main.layer.getTextureId());
|
||||
} else {
|
||||
assertNotNull(mainSlot);
|
||||
assertNotSame(shadowSlot, mainSlot);
|
||||
assertSame(EntityTextureAtlas.INSTANCE.slotFor(source, false), mainSlot);
|
||||
}
|
||||
} finally {
|
||||
Reflect.set(batcher, "activeGroups", previousGroups);
|
||||
AngelicaConfig.enableEntityTextureAtlas = atlasEnabled;
|
||||
groups.clear();
|
||||
GLStateManager.glDeleteTextures(otherTexture);
|
||||
}
|
||||
}
|
||||
|
||||
private static int texture(int filter, int seed) {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
@@ -187,10 +114,7 @@ class EntityTextureAtlasGLTest {
|
||||
|
||||
@Test
|
||||
void copiesTheTextureWithEdgeGuttersAndRestoresTheFramebuffers() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
final EntityTextureAtlas.Slot slot = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
assertTrue(GLStateManager.finishExitCapture(state), "first-use atlas resources are internal batch operations");
|
||||
assertNotNull(slot);
|
||||
assertEquals(passFbo, GLStateManager.getDrawFramebuffer(), "draw framebuffer restored");
|
||||
assertEquals(passFbo, GLStateManager.getReadFramebuffer(), "read framebuffer restored");
|
||||
|
||||
+1
-191
@@ -6,7 +6,6 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMaterial;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrShader;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.RenderLayer;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
@@ -21,7 +20,6 @@ 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.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.rendering.items.ItemGlint;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.Group;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.InstanceColumns;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.TexRun;
|
||||
@@ -29,13 +27,9 @@ import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.Countin
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import net.coderbot.iris.uniforms.CapturedStateScope;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -70,8 +64,6 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@GLCoreTest
|
||||
class RetainedTesrGroupsGLTest {
|
||||
@@ -763,10 +755,6 @@ class RetainedTesrGroupsGLTest {
|
||||
}
|
||||
|
||||
private static int createGlintTexture() {
|
||||
return createGlintTexture(false);
|
||||
}
|
||||
|
||||
private static int createGlintTexture(boolean opaque) {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
@@ -777,7 +765,7 @@ class RetainedTesrGroupsGLTest {
|
||||
final ByteBuffer texels = BufferUtils.createByteBuffer(4 * 4 * 4);
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
texels.put((byte) (64 + 48 * x)).put((byte) (64 + 48 * y)).put((byte) 200).put((byte) (opaque ? 255 : 64 + 48 * x));
|
||||
texels.put((byte) (64 + 48 * x)).put((byte) (64 + 48 * y)).put((byte) 200).put((byte) (64 + 48 * x));
|
||||
}
|
||||
}
|
||||
texels.flip();
|
||||
@@ -1012,184 +1000,6 @@ class RetainedTesrGroupsGLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"armorTemplate", "armorCubes", "held", "heldMirrored", "dropped", "droppedStack"})
|
||||
void shadowGlintReplayMatchesLiveMainPixels(String kind) {
|
||||
glintState();
|
||||
GLStateManager.glDeleteTextures(glintTexture);
|
||||
glintTexture = createGlintTexture(true);
|
||||
final boolean held = kind.startsWith("held"), dropped = kind.startsWith("dropped"), cubes = kind.equals("armorCubes");
|
||||
final TemplateBuffer baseTemplate;
|
||||
if (held || dropped) {
|
||||
final AngelicaTesrMeshCache.GtnhMeshBackend backend = new AngelicaTesrMeshCache.GtnhMeshBackend();
|
||||
ItemRenderer.renderItemIn2D(backend.beginCapture(DefaultVertexFormat.POSITION_TEXTURE_NORMAL), 0.75f, 0.1f, 0.25f, 0.9f, 16, 32, 0.0625f);
|
||||
baseTemplate = backend.endCaptureToTemplate();
|
||||
ItemRenderer.renderItemIn2D(backend.beginCapture(DefaultVertexFormat.POSITION_TEXTURE_NORMAL), 0, 0, 1, 1, 16, 32, 0.0625f);
|
||||
glintTemplate = backend.endCaptureToTemplate();
|
||||
} else {
|
||||
if (!cubes) glintTemplate = capturedQuad(0.5);
|
||||
baseTemplate = glintTemplate;
|
||||
}
|
||||
final ModelPartBatcher batcher = ModelPartBatcher.INSTANCE;
|
||||
final boolean previousBatching = AngelicaConfig.enableEntityBatching;
|
||||
final boolean previousReplay = AngelicaConfig.enableEntityShadowReplay;
|
||||
final boolean previousAtlas = AngelicaConfig.enableEntityTextureAtlas;
|
||||
final boolean previousShadow = ShadowRenderer.ACTIVE;
|
||||
final double previousX = RenderManager.renderPosX, previousY = RenderManager.renderPosY, previousZ = RenderManager.renderPosZ;
|
||||
final RetainedTesrGroups shadowGroups = new RetainedTesrGroups(new AngelicaBufferSource());
|
||||
final Matrix4f shadowView = new Matrix4f().rotateXYZ(0.6f, -0.7f, 0.2f).translate(2, -1, 3);
|
||||
final Matrix4f mainView = new Matrix4f().rotateXYZ(-0.1f, 0.2f, 0.17f);
|
||||
final Matrix4f pose = new Matrix4f().rotateXYZ(0.13f, 0.41f, -0.27f);
|
||||
if (held || dropped) pose.scale(0.8f, kind.equals("heldMirrored") ? -0.8f : 0.8f, 0.8f).translate(-0.5f, -0.5f, 0);
|
||||
final Entity entity = mock(Entity.class);
|
||||
final Render renderer = mock(Render.class);
|
||||
final RenderManager manager = mock(RenderManager.class);
|
||||
when(manager.getEntityRenderObject(entity)).thenReturn(renderer);
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
AngelicaConfig.enableEntityBatching = true;
|
||||
AngelicaConfig.enableEntityShadowReplay = true;
|
||||
AngelicaConfig.enableEntityTextureAtlas = false;
|
||||
EntityShadowReplay.beginFrame();
|
||||
Reflect.set(batcher, "active", true);
|
||||
Reflect.set(batcher, "mode", ModelPartBatcher.Mode.ENTITIES);
|
||||
Reflect.set(batcher, "instancedThisCycle", true);
|
||||
GLStateManager.getTextures().getTextureUnitStates(1).setEnabled(false);
|
||||
GLStateManager.setOverlayColor(0, 0, 0, 0);
|
||||
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
RenderManager.renderPosX = 1000000;
|
||||
RenderManager.renderPosY = RenderManager.renderPosZ = 0;
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
Reflect.set(batcher, "activeGroups", groups);
|
||||
Reflect.set(batcher, "shadow", false);
|
||||
ShadowRenderer.ACTIVE = false;
|
||||
GLStateManager.setModelViewMatrix(mainView);
|
||||
queueEntityGlint(batcher, kind, baseTemplate, pose, true);
|
||||
final int[][] reference = drawEntityGlint(kind);
|
||||
|
||||
shadowGroups.beginPass(new Matrix4f(), 0, 0, 0, instanced, new CountingPipeline(true, true));
|
||||
Reflect.set(batcher, "activeGroups", shadowGroups);
|
||||
Reflect.set(batcher, "shadow", true);
|
||||
ShadowRenderer.ACTIVE = true;
|
||||
GLStateManager.setModelViewMatrix(shadowView);
|
||||
EntityShadowReplay.beginPass(true, 0);
|
||||
BatchStateGuard.resume();
|
||||
try {
|
||||
assertTrue(EntityShadowReplay.beginEntity(entity, 0.25f));
|
||||
assertTrue(EntityShadowReplay.beginRenderer(renderer, entity, 0.25f, BatchEligibility.SAFE));
|
||||
queueEntityGlint(batcher, kind, baseTemplate, pose, false);
|
||||
EntityShadowReplay.endRenderer(true, BatchEligibility.SAFE);
|
||||
EntityShadowReplay.endEntity(true);
|
||||
} finally {
|
||||
BatchStateGuard.suspend();
|
||||
}
|
||||
assertEquals(1, shadowGroups.groupCount(), "glint must not add a shadow draw group");
|
||||
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
Reflect.set(batcher, "activeGroups", groups);
|
||||
Reflect.set(batcher, "shadow", false);
|
||||
ShadowRenderer.ACTIVE = false;
|
||||
GLStateManager.setModelViewMatrix(mainView);
|
||||
EntityShadowReplay.beginPass(false, 0);
|
||||
assertTrue(EntityShadowReplay.replay(manager, entity, 0.25f, 0));
|
||||
final int[][] replayed = drawEntityGlint(kind);
|
||||
FfpFixture.assertOverlayFootprint(reference[0], reference[1], BACKGROUND, "live glint");
|
||||
FfpFixture.assertOverlayFootprint(replayed[0], replayed[1], BACKGROUND, "replayed glint");
|
||||
// Replay reaches the main pose through the shadow view, about 1 ulp off the live pose. On llvmpipe that
|
||||
// flips a single glint pixel on the dropped item's thin edge strips.
|
||||
FfpFixture.assertPixelParity("replayed glint pixels", reference[1], replayed[1], SIZE, BACKGROUND, 8, 1, 2, RetainedTesrGroupsGLTest::describe);
|
||||
} finally {
|
||||
shadowGroups.clear();
|
||||
batcher.clear();
|
||||
EntityShadowReplay.clear();
|
||||
AngelicaConfig.enableEntityBatching = previousBatching;
|
||||
AngelicaConfig.enableEntityShadowReplay = previousReplay;
|
||||
AngelicaConfig.enableEntityTextureAtlas = previousAtlas;
|
||||
ShadowRenderer.ACTIVE = previousShadow;
|
||||
RenderManager.renderPosX = previousX;
|
||||
RenderManager.renderPosY = previousY;
|
||||
RenderManager.renderPosZ = previousZ;
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
}
|
||||
|
||||
private void queueEntityGlint(ModelPartBatcher batcher, String kind, TemplateBuffer baseTemplate, Matrix4f pose, boolean legacyDropped) {
|
||||
final int copies = kind.equals("droppedStack") ? 4 : 1;
|
||||
for (int copy = 0; copy < copies; copy++) {
|
||||
queueEntityGlintCopy(batcher, kind, baseTemplate, new Matrix4f(pose).translate(copy * 0.12f, copy * 0.06f, copy * 0.085f), legacyDropped);
|
||||
}
|
||||
}
|
||||
|
||||
private void queueEntityGlintCopy(ModelPartBatcher batcher, String kind, TemplateBuffer baseTemplate, Matrix4f pose, boolean legacyDropped) {
|
||||
final boolean held = kind.startsWith("held"), dropped = kind.startsWith("dropped"), cubes = kind.equals("armorCubes");
|
||||
bindBase();
|
||||
restoreTexMatrix();
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.applyMultMatrix(pose);
|
||||
GLStateManager.glColor4f(0.4f, 0.6f, 0.8f, 1);
|
||||
ModelPartBatcher.onTextureBind(new ResourceLocation("angelica", "test/base"), baseTexture);
|
||||
final GlintCapture capture = new GlintCapture();
|
||||
assertTrue(BatchEligibility.begin(BatchEligibility.SAFE, GLStateManager.drawCalls));
|
||||
try {
|
||||
if (!held && !dropped) assertTrue(batcher.beginArmorCapture(capture));
|
||||
assertTrue(Reflect.<Boolean>invoke(batcher, "queueTemplate",
|
||||
new Class<?>[] {TemplateBuffer.class, CubeParams[].class, boolean.class, float.class, TesrMaterial.class, boolean.class},
|
||||
baseTemplate, cubes ? glintCubes : null, false, CUBE_SCALE, held || dropped ? EntityMaterials.DROPPED_ITEM_CUTOUT : EntityMaterials.CUTOUT, false));
|
||||
if (held) {
|
||||
assertTrue(batcher.queueSkippedItemGlint(glintTemplate, EntityMaterials.ITEM_GLINT));
|
||||
} else if (dropped && legacyDropped) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, glintTexture);
|
||||
ModelPartBatcher.onTextureBind(GLINT_TEXTURE, glintTexture);
|
||||
GLStateManager.glColor4f(0.5f * 0.76f, 0.25f * 0.76f, 0.8f * 0.76f, 1);
|
||||
for (Matrix4f matrix : new Matrix4f[] {ItemGlint.firstMatrix(), ItemGlint.secondMatrix()}) {
|
||||
GLStateManager.setTextureMatrix(0, matrix);
|
||||
assertTrue(batcher.queueTemplate(glintTemplate, EntityMaterials.GLINT));
|
||||
}
|
||||
restoreTexMatrix();
|
||||
} else if (dropped) {
|
||||
assertTrue(batcher.queueSkippedItemGlint(glintTemplate, EntityMaterials.GLINT));
|
||||
} else {
|
||||
capture.endWithRestoredPose();
|
||||
assertTrue(batcher.queueSkippedArmorGlint(capture, GLINT_TEXTURE));
|
||||
}
|
||||
} finally {
|
||||
BatchEligibility.end(BatchEligibility.SAFE, GLStateManager.drawCalls);
|
||||
capture.reset();
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private int[][] drawEntityGlint(String kind) {
|
||||
final boolean held = kind.startsWith("held"), dropped = kind.startsWith("dropped");
|
||||
FfpFixture.clear();
|
||||
final int depth = GLStateManager.pushState(StateSet.BATCH);
|
||||
try {
|
||||
int[] base = null;
|
||||
for (TesrMaterial material : new TesrMaterial[] {held || dropped ? EntityMaterials.DROPPED_ITEM_CUTOUT : EntityMaterials.CUTOUT,
|
||||
held ? EntityMaterials.ITEM_GLINT : EntityMaterials.GLINT}) {
|
||||
for (var layers : RetainedTesrTestSupport.byLayer(groups).values()) for (Group group : layers) {
|
||||
if (group.material != material) continue;
|
||||
group.layer.getState().apply();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, base == null ? baseTexture : glintTexture);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilFunc(GL11.GL_ALWAYS, 3, 0xFF);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
|
||||
groups.drawLayer(group.layer);
|
||||
assertEquals(GL11.GL_ALWAYS, GL11.glGetInteger(GL11.GL_STENCIL_FUNC));
|
||||
assertEquals(3, GL11.glGetInteger(GL11.GL_STENCIL_REF));
|
||||
}
|
||||
if (base == null) base = FfpFixture.readRegion(SIZE);
|
||||
}
|
||||
ring.endFrame();
|
||||
return new int[][] {base, FfpFixture.readRegion(SIZE)};
|
||||
} finally {
|
||||
GLStateManager.popStateTo(depth);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void mirroredModelViewUnderCullFrontBatchesLikeTheImmediateDraw() {
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
|
||||
@@ -60,198 +60,6 @@ class RetainedTesrGroupsTest {
|
||||
layer = testLayer();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void replayRangeMatchesIndividualQueueingAcrossCapacityGrowth(boolean cubes) {
|
||||
final TemplateBuffer template = template();
|
||||
final var cubeParams = cubes ? cubes(2) : null;
|
||||
final var input = new RetainedTesrGroups.InstanceColumns();
|
||||
for (int i = 0; i < 80; i++) {
|
||||
final Matrix4f matrix = new Matrix4f().translation(i, -i, 2).rotateY(i * 0.1f).scale(0.5f);
|
||||
if (cubes) input.addCubes(template, cubeParams, i + 1, matrix, i, ~i, i * 2, i * 3L, null);
|
||||
else input.add(template, matrix, i, ~i, i * 2, i * 3L);
|
||||
}
|
||||
final var expected = new RetainedTesrGroups(new AngelicaBufferSource());
|
||||
final var pipeline = new RetainedTesrTestSupport.CountingPipeline(true, true);
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), pipeline);
|
||||
expected.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), pipeline);
|
||||
final RenderLayer layer = layerFor(STREAM);
|
||||
final Matrix4f transform = new Matrix4f().rotateX(0.2f).translate(5, 7, 3);
|
||||
final Matrix4f texture = new Matrix4f().translation(0.2f, 0.4f, 0);
|
||||
for (RetainedTesrGroups dst : new RetainedTesrGroups[] {groups, expected}) {
|
||||
dst.queue(0, template, cubeParams, 1, layer, STREAM, new Matrix4f(), 0, -1, 0, 0, 0, texture, null);
|
||||
}
|
||||
groups.replayInstances(input, 5, 75, layer, STREAM, 0, transform, texture, null, null);
|
||||
for (int i = 5; i < 75; i++) {
|
||||
final Matrix4f matrix = new Matrix4f(transform).mul(new Matrix4f().set(input.matrices, i * 16));
|
||||
expected.queue(0, template, cubeParams, cubes ? input.scales.getFloat(i) : 1, layer, STREAM, matrix,
|
||||
input.lights.getInt(i), input.colors.getInt(i), input.overlays.getInt(i), input.infos.getLong(i), 0, texture, null);
|
||||
}
|
||||
final var actualGroup = onlyGroup(groups);
|
||||
final var expectedGroup = onlyGroup(expected);
|
||||
final var actual = cubes ? actualGroup.cubeColumns : actualGroup.templateColumns;
|
||||
final var correct = cubes ? expectedGroup.cubeColumns : expectedGroup.templateColumns;
|
||||
assertEquals(correct.size, actual.size);
|
||||
for (int i = 0; i < actual.size * 16; i++) assertEquals(correct.matrices[i], actual.matrices[i], 0.00001f);
|
||||
assertEquals(correct.templates, actual.templates);
|
||||
assertEquals(correct.cubes, actual.cubes);
|
||||
assertEquals(correct.scales, actual.scales);
|
||||
assertEquals(correct.colors, actual.colors);
|
||||
assertEquals(correct.lights, actual.lights);
|
||||
assertEquals(correct.overlays, actual.overlays);
|
||||
assertEquals(correct.infos, actual.infos);
|
||||
assertEquals(correct.runs.getFirst().parts, actual.runs.getFirst().parts);
|
||||
assertEquals(correct.runs.getFirst().instances, actual.runs.getFirst().instances);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replayRangeRetainedFallbackPreservesTheModelTransform() {
|
||||
final TemplateBuffer triangles = new TemplateBuffer(new int[3 * VERTEX_SIZE], 3, GL11.GL_TRIANGLES);
|
||||
final Matrix4f model = new Matrix4f().translation(3, 2, -6).rotateY(0.7f).scale(0.5f);
|
||||
final Matrix4f transform = new Matrix4f().rotateX(0.2f).translate(5, 7, 3);
|
||||
final var input = new RetainedTesrGroups.InstanceColumns();
|
||||
input.add(triangles, model, 0, -1, 0, 0);
|
||||
final var expected = new RetainedTesrGroups(new AngelicaBufferSource());
|
||||
final RenderLayer layer = layerFor(STREAM);
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0);
|
||||
expected.beginPass(new Matrix4f(), 0, 0, 0);
|
||||
|
||||
groups.replayInstances(input, 0, 1, layer, STREAM, 0, transform, null, null, null);
|
||||
expected.queue(0, triangles, layer, STREAM, new Matrix4f(transform).mul(model), 0, -1, 0, 0, 0, null);
|
||||
|
||||
final var actual = onlyGroup(groups);
|
||||
assertEquals(0, groups.streamedInstances);
|
||||
assertEquals(1, actual.instTexMatrices.size(), "the draw-mode mismatch must take the retained fallback");
|
||||
assertTrue(new Matrix4f().set(onlyGroup(expected).templateColumns.matrices)
|
||||
.equals(new Matrix4f().set(actual.templateColumns.matrices), 0.00001f));
|
||||
assertTrue(model.equals(new Matrix4f().set(input.matrices), 0));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void replayRangeProbeMatchesIndividualQueueing(boolean queueBeforeReplay) {
|
||||
final TemplateBuffer template = template();
|
||||
final Matrix4f base = new Matrix4f().rotateX(0.3f).translate(4, -2, 5);
|
||||
final Matrix4f model = new Matrix4f().translation(3, 2, -6).rotateY(0.7f).scale(0.5f);
|
||||
final Matrix4f transform = new Matrix4f().rotateX(0.2f).translate(5, 7, 3);
|
||||
final var input = new RetainedTesrGroups.InstanceColumns();
|
||||
input.add(template, model, 0, -1, 0, 0);
|
||||
final var expected = new RetainedTesrGroups(new AngelicaBufferSource(), () -> clock[0]);
|
||||
final var renderer = new InstancedTemplateRenderer(ring);
|
||||
final var state = CapturedRenderingState.INSTANCE;
|
||||
try (CapturedStateScope ignored = new CapturedStateScope()) {
|
||||
state.setCurrentEntityAndItem(0, 0);
|
||||
state.setCurrentBlockEntity(0);
|
||||
state.setCurrentEntityColor(0, 0, 0, 0);
|
||||
for (RetainedTesrGroups dst : new RetainedTesrGroups[] {groups, expected}) {
|
||||
dst.beginPass(base, 100.5, 64.25, -100.75, renderer);
|
||||
dst.forceStreaming(layer, TesrMaterial.CURRENT_STATE, 0);
|
||||
dst.queue(0, template, layer, TesrMaterial.CURRENT_STATE, base, 0, -1, 0, 0, 0, null);
|
||||
}
|
||||
clock[0] += RetainedTesrGroups.STABILITY_PROBE_MS;
|
||||
for (RetainedTesrGroups dst : new RetainedTesrGroups[] {groups, expected}) {
|
||||
dst.beginPass(base, 100.5, 64.25, -100.75, renderer);
|
||||
if (queueBeforeReplay) {
|
||||
dst.queue(0, template, layer, TesrMaterial.CURRENT_STATE, base, 0, -1, 0, 0, 0, null);
|
||||
assertTrue(onlyGroup(dst).probe.probing);
|
||||
}
|
||||
}
|
||||
|
||||
groups.replayInstances(input, 0, 1, layer, TesrMaterial.CURRENT_STATE, 0, transform, null, null, null);
|
||||
expected.queue(0, template, layer, TesrMaterial.CURRENT_STATE, new Matrix4f(transform).mul(model), 0, -1, 0, 0, 0, null);
|
||||
|
||||
final var actual = onlyGroup(groups);
|
||||
final var correct = onlyGroup(expected);
|
||||
final int offset = queueBeforeReplay ? 16 : 0;
|
||||
assertTrue(actual.probe.probing);
|
||||
assertEquals(queueBeforeReplay ? 2 : 1, correct.probe.count);
|
||||
assertEquals(correct.probe.count, actual.probe.count);
|
||||
assertTrue(new Matrix4f().set(correct.probe.snapshot.matrices, offset)
|
||||
.equals(new Matrix4f().set(actual.probe.snapshot.matrices, offset), 0.00001f));
|
||||
assertTrue(new Matrix4f().set(correct.templateColumns.matrices, offset)
|
||||
.equals(new Matrix4f().set(actual.templateColumns.matrices, offset), 0.00001f));
|
||||
assertTrue(model.equals(new Matrix4f().set(input.matrices), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void replayRangeAfterIdleUsesRetainedStorage() {
|
||||
final TemplateBuffer template = template();
|
||||
final Matrix4f base = new Matrix4f().rotateX(0.3f).translate(4, -2, 5);
|
||||
final Matrix4f model = new Matrix4f().translation(3, 2, -6).rotateY(0.7f).scale(0.5f);
|
||||
final Matrix4f transform = new Matrix4f().rotateX(0.2f).translate(5, 7, 3);
|
||||
final var input = new RetainedTesrGroups.InstanceColumns();
|
||||
input.add(template, model, 0, -1, 0, 0);
|
||||
final var expected = new RetainedTesrGroups(new AngelicaBufferSource(), () -> clock[0]);
|
||||
final var renderer = new InstancedTemplateRenderer(ring);
|
||||
final var state = CapturedRenderingState.INSTANCE;
|
||||
try (CapturedStateScope ignored = new CapturedStateScope()) {
|
||||
state.setCurrentEntityAndItem(0, 0);
|
||||
state.setCurrentBlockEntity(0);
|
||||
state.setCurrentEntityColor(0, 0, 0, 0);
|
||||
for (RetainedTesrGroups dst : new RetainedTesrGroups[] {groups, expected}) {
|
||||
dst.beginPass(base, 100.5, 64.25, -100.75, renderer);
|
||||
dst.forceStreaming(layer, TesrMaterial.CURRENT_STATE, 0);
|
||||
dst.queue(0, template, layer, TesrMaterial.CURRENT_STATE, base, 0, -1, 0, 0, 0, null);
|
||||
assertTrue(onlyGroup(dst).streamPath);
|
||||
for (int i = 0; i <= RetainedTesrGroups.IDLE_RESET_FRAMES; i++) {
|
||||
dst.beginPass(base, 100.5, 64.25, -100.75, renderer);
|
||||
}
|
||||
}
|
||||
|
||||
groups.replayInstances(input, 0, 1, layer, TesrMaterial.CURRENT_STATE, 0, transform, null, null, null);
|
||||
expected.queue(0, template, layer, TesrMaterial.CURRENT_STATE, new Matrix4f(transform).mul(model), 0, -1, 0, 0, 0, null);
|
||||
|
||||
final var actual = onlyGroup(groups);
|
||||
final var correct = onlyGroup(expected);
|
||||
assertFalse(actual.streamPath);
|
||||
assertEquals(1, correct.instTexMatrices.size());
|
||||
assertEquals(correct.instTexMatrices.size(), actual.instTexMatrices.size());
|
||||
assertTrue(actual.templateColumns.runs.isEmpty());
|
||||
assertTrue(new Matrix4f().set(correct.templateColumns.matrices)
|
||||
.equals(new Matrix4f().set(actual.templateColumns.matrices), 0.00001f));
|
||||
assertTrue(model.equals(new Matrix4f().set(input.matrices), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void replayRangeUsesCapturedStateForNonStreamGroups(boolean liveGroupPromoted) {
|
||||
final TemplateBuffer template = template();
|
||||
final Matrix4f model = new Matrix4f().translation(3, 2, -6).rotateY(0.7f).scale(0.5f);
|
||||
final long info = InstancedAttribs.packEntityInfo(11, 21, 13);
|
||||
final int overlay = AngelicaBufferSource.packAbgr(1, 0, 0, 0.5f);
|
||||
final var input = new RetainedTesrGroups.InstanceColumns();
|
||||
input.add(template, model, 0, -1, overlay, info);
|
||||
final var state = CapturedRenderingState.INSTANCE;
|
||||
try (CapturedStateScope ignored = new CapturedStateScope()) {
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring));
|
||||
state.setCurrentEntityAndItem(11, 13);
|
||||
state.setCurrentBlockEntity(21);
|
||||
AngelicaBufferSource.setEntityColor(overlay);
|
||||
groups.forceStreaming(layer, TesrMaterial.CURRENT_STATE, 21);
|
||||
final var capturedGroup = onlyGroup(groups);
|
||||
|
||||
state.setCurrentEntityAndItem(12, 14);
|
||||
state.setCurrentBlockEntity(22);
|
||||
state.setCurrentEntityColor(0, 1, 0, 0.5f);
|
||||
final int liveOverlay = AngelicaBufferSource.packEntityColor(state.getCurrentEntityColor());
|
||||
if (liveGroupPromoted) groups.forceStreaming(layer, TesrMaterial.CURRENT_STATE, 21);
|
||||
|
||||
groups.replayInstances(input, 0, 1, layer, TesrMaterial.CURRENT_STATE, 21, new Matrix4f(), null, null, null);
|
||||
|
||||
assertEquals(liveGroupPromoted ? 2 : 1, groups.groupCount());
|
||||
assertEquals(1, capturedGroup.templateColumns.size);
|
||||
assertEquals(info, capturedGroup.templateColumns.infos.getLong(0));
|
||||
assertEquals(overlay, capturedGroup.templateColumns.overlays.getInt(0));
|
||||
assertEquals(12, state.getCurrentRenderedEntity());
|
||||
assertEquals(22, state.getCurrentRenderedBlockEntity());
|
||||
assertEquals(14, state.getCurrentRenderedItem());
|
||||
assertEquals(liveOverlay, AngelicaBufferSource.packEntityColor(state.getCurrentEntityColor()));
|
||||
assertTrue(model.equals(new Matrix4f().set(input.matrices), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void fallbackKeepsHighMaterialIdsAndTheUnmappedSentinel() {
|
||||
for (int id : new int[] { -1, 0, 32767, 32768, 45020, 50020, 50072, 65534 }) {
|
||||
|
||||
+2
-2
@@ -86,8 +86,8 @@ public final class RetainedTesrTestSupport {
|
||||
|
||||
static RenderLayer capturedLayer(TesrMaterial material) {
|
||||
return Reflect.invoke(ModelPartBatcher.INSTANCE, "layerFor",
|
||||
new Class<?>[] { ResourceLocation.class, TesrMaterial.class, PassOverride.class, float.class, float.class, int.class, int.class, boolean.class, boolean.class, boolean.class },
|
||||
null, material, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, DrawState.liveCull(material), DrawState.liveLit(material), DrawState.liveLightmap(), false);
|
||||
new Class<?>[] { ResourceLocation.class, TesrMaterial.class, PassOverride.class, float.class, float.class, int.class, int.class, boolean.class, boolean.class },
|
||||
null, material, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, DrawState.liveCull(material), DrawState.liveLit(material), DrawState.liveLightmap());
|
||||
}
|
||||
|
||||
static final class CountingPipeline implements TesrInstancingPipeline {
|
||||
|
||||
@@ -48,11 +48,6 @@ public final class VanillaLayerRig {
|
||||
return AlphaTestStorage.isAlphaTestLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverrideHeld() {
|
||||
return AlphaTestStorage.isOverrideHeld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deferAlphaTestToggle(boolean enabled) {
|
||||
return AlphaTestStorage.deferAlphaTestToggle(enabled);
|
||||
|
||||
Reference in new issue
Block a user