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
@@ -1,14 +1,14 @@
|
||||
## Summary
|
||||
<!-- Briefly describe what this PR changes and why. -->
|
||||
# Description of your *PR* and other info
|
||||
<!-- Please include a summary of the change -->
|
||||
<!-- Any details that you think are important to review this PR? -->
|
||||
<!-- Are there any Issues / PRs related to this one? -->
|
||||
|
||||
<!-- Add a screenshot or a video demonstration for new features -->
|
||||
|
||||
<details open><summary>Template</summary> <!-- OPEN is intentional -->
|
||||
|
||||
## Checklist
|
||||
- [ ] I have tested this PR in DevEnv
|
||||
- [ ] I have tested this PR in Fullpack
|
||||
- [ ] This PR is in compliance with the [GTNH AI Policy](https://github.com/GTNewHorizons/GTNH-Dev-Doc/blob/master/AI_POLICY.md)
|
||||
- [ ] I have at least [skimmed](https://github.com/GTNewHorizons/Angelica/search?q=is%3Apr&type=Issues) the tracker for similar pull requests
|
||||
- [ ] This PR requires another PR in order to merge
|
||||
### Before submitting a *pull request* make sure you have:
|
||||
- [ ] Read the [AI Contribution Policy](https://github.com/GTNewHorizons/Angelica/blob/master/AI_POLICY.md)
|
||||
- [ ] At least [skimmed](https://github.com/GTNewHorizons/Angelica/search?q=is%3Apr&type=Issues) the tracker for similar pull requests
|
||||
|
||||
</details>
|
||||
@@ -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
|
||||
@@ -175,14 +175,8 @@ val glsmRedirectAgent: Configuration by configurations.creating {
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
val glsmMutableStaticsAgent: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
glsmRedirectAgent(project(":glsm", "redirectAgent"))
|
||||
glsmMutableStaticsAgent(project(":glsm", "mutableStaticsAgent"))
|
||||
archScanAsm(libs.asm.tree.test)
|
||||
archScanAsm(libs.asm.commons.test)
|
||||
}
|
||||
@@ -195,7 +189,6 @@ tasks.test {
|
||||
useJUnitPlatform { excludeTags = setOf("gl-core") }
|
||||
configureAngelicaJava8()
|
||||
classpath = archScanAsm + classpath
|
||||
javaAgent(glsmMutableStaticsAgent)
|
||||
jvmArgs("-Dceleritas.lwjglService=org.taumc.celeritas.lwjgl.HeadlessTestLWJGLService")
|
||||
dependsOn(":glsm:classes", ":lwjgl3-backend:classes", ":sdl-gpu:classes")
|
||||
}
|
||||
|
||||
@@ -158,7 +158,6 @@ dependencies {
|
||||
testFixturesApi(platform(libs.junit.bom))
|
||||
testFixturesApi(libs.junit.jupiter.api)
|
||||
testFixturesApi(libs.lwjgl2)
|
||||
testFixturesCompileOnly(libs.asm.tree.test)
|
||||
|
||||
// Test
|
||||
testImplementation(sourceSets["stubs"].output)
|
||||
@@ -258,13 +257,6 @@ fun Test.configureGlsmJava8() {
|
||||
}
|
||||
}
|
||||
|
||||
val mutableStaticsAgentJar by tasks.registering(Jar::class) {
|
||||
archiveClassifier = "mutable-statics-agent"
|
||||
manifest {
|
||||
attributes("Premain-Class" to "com.gtnewhorizons.angelica.glsm.testutil.MutableStatics")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform {
|
||||
excludeTags = GL_TASK_TAGS.values.toSet() + "lwjgl3"
|
||||
@@ -273,7 +265,6 @@ tasks.test {
|
||||
excludeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
|
||||
}
|
||||
configureGlsmJava8()
|
||||
javaAgent(files(mutableStaticsAgentJar))
|
||||
}
|
||||
|
||||
val redirectAgentJar by tasks.registering(Jar::class) {
|
||||
@@ -288,14 +279,8 @@ val redirectAgent by configurations.creating {
|
||||
isCanBeResolved = false
|
||||
}
|
||||
|
||||
val mutableStaticsAgent by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add(redirectAgent.name, redirectAgentJar)
|
||||
add(mutableStaticsAgent.name, mutableStaticsAgentJar)
|
||||
}
|
||||
|
||||
val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
|
||||
@@ -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<>();
|
||||
@@ -275,7 +274,6 @@ public class GLContextState {
|
||||
public float savedFilterAniso = -1.0f;
|
||||
public boolean lineQuadsActive = false;
|
||||
public boolean lineStippleActive = false;
|
||||
public int textureSupersamplingSamples = 1;
|
||||
public Instancing ffpInstancing = Instancing.NONE;
|
||||
public float lastBrightnessX;
|
||||
public float lastBrightnessY;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -158,8 +158,8 @@ public class RenderSystem {
|
||||
maxSSBOBindings = 0;
|
||||
}
|
||||
|
||||
supportsSamplerObjects = isGLES ? glesVersion >= 300
|
||||
: GLStateManager.capabilities.OpenGL33 || GLStateManager.capabilities.GL_ARB_sampler_objects;
|
||||
// Check for sampler objects support (GL 3.3+ or ARB extension)
|
||||
supportsSamplerObjects = GLStateManager.capabilities.OpenGL33 || GLStateManager.capabilities.GL_ARB_sampler_objects;
|
||||
if (supportsSamplerObjects) {
|
||||
maxCombinedTextureImageUnits = RENDER_BACKEND.getInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -713,7 +663,12 @@ public class RenderSystem {
|
||||
|
||||
public static void unbindAllSamplers() {
|
||||
if (!supportsSamplerObjects) return;
|
||||
GLStateManager.unbindAllSamplers();
|
||||
final int count = GLStateManager.getSamplerUnitCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (GLStateManager.getSamplerBinding(i) != 0) {
|
||||
GLStateManager.glBindSampler(i, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460". Strips
|
||||
|
||||
+2
-3
@@ -11,14 +11,13 @@ import java.util.function.IntSupplier;
|
||||
* @param samplerNames samplers the graphics program may not have, because only the stripped writes read them
|
||||
* @param samplerUnits per sampler name, the texture unit it reads at each draw, or -1 to use samplerTextures
|
||||
* @param samplerTextures per sampler name, its texture when samplerUnits is -1
|
||||
* @param samplerObjects per sampler name, supplies its current GL sampler object when samplerUnits is -1;
|
||||
* null for unassigned or external bindings, and returns 0 to use the texture's own filtering
|
||||
* @param samplerObjects per sampler name, the GL sampler object to sample it with when samplerUnits is -1, or 0
|
||||
* @param vertexBufferBinding storage buffer binding of the first vertex buffer the replay program declares
|
||||
* @param vertexBufferCount how many vertex buffers it declares, at consecutive bindings
|
||||
* @param indexBufferBinding storage buffer binding of its index buffer
|
||||
*/
|
||||
public record VertexWriteReplaySetup(int replayProgram, String[] inputNames, int[] inputLocations,
|
||||
IntSupplier[] images, int writtenImageCount,
|
||||
String[] samplerNames, int[] samplerUnits, IntSupplier[] samplerTextures, IntSupplier[] samplerObjects,
|
||||
String[] samplerNames, int[] samplerUnits, IntSupplier[] samplerTextures, int[] samplerObjects,
|
||||
int vertexBufferBinding, int vertexBufferCount, int indexBufferBinding) {
|
||||
}
|
||||
@@ -104,7 +104,6 @@ public final class FFPUniformBlock {
|
||||
public static final int TEX_ENV_COLOR_3 = vec4("u_TexEnvColor3");
|
||||
public static final int OVERLAY_COLOR = vec4("u_OverlayColor");
|
||||
public static final int SECONDARY_COLOR = vec3("u_SecondaryColor");
|
||||
public static final int ATLAS_GUTTER = flt("u_AtlasGutter");
|
||||
public static final int FOG_PARAMS = vec4("u_FogParams");
|
||||
public static final int FOG_COLOR = vec4("u_FogColor");
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
@@ -83,7 +82,7 @@ public final class FragmentKey {
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
private static final int BIT_SEPARATE_SPECULAR = 6; // 1 bit
|
||||
private static final int BIT_TEXTURE_SUPERSAMPLING = 7; // 3 bits, log2(samples)
|
||||
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
|
||||
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
|
||||
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
|
||||
private static final int BIT_COLOR_SUM = 12; // 1 bit
|
||||
@@ -173,10 +172,7 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
final int nrEnabled = highestEnabled + 1;
|
||||
if (glCtx.textureSupersamplingSamples > 1 && (unit0Bits & 1L) != 0
|
||||
&& TextureInfoCache.INSTANCE.getInfo(textures.getTextureUnitBindings(0).getBinding()).getAtlasGutter() > 0) {
|
||||
global |= (long) Integer.numberOfTrailingZeros(glCtx.textureSupersamplingSamples) << BIT_TEXTURE_SUPERSAMPLING;
|
||||
}
|
||||
global |= ((long) (nrEnabled & 0x7)) << BIT_NR_ENABLED_UNITS;
|
||||
global |= (unit0Bits << GLOBAL_BITS);
|
||||
out[0] = global;
|
||||
|
||||
@@ -260,13 +256,12 @@ public final class FragmentKey {
|
||||
public int fogMode() { return (int) (packed[0] & 0x3); }
|
||||
public boolean combinedGlint() { return (packed[0] & (1L << BIT_COMBINED_GLINT)) != 0; }
|
||||
public boolean glintReplaceAlpha() { return (packed[0] & (1L << BIT_GLINT_REPLACE_ALPHA)) != 0; }
|
||||
public int textureSupersamplingSamples() { return 1 << (int) ((packed[0] >> BIT_TEXTURE_SUPERSAMPLING) & 0x7); }
|
||||
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
|
||||
public boolean colorSum() { return ((packed[0] >> BIT_COLOR_SUM) & 1) != 0; }
|
||||
public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; }
|
||||
public int nrEnabledUnits() { return packed.length == 1 && !unitEnabled(0) ? 0 : packed.length; }
|
||||
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
|
||||
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
|
||||
public boolean overlayInstanced() { return ((packed[0] >> BIT_OVERLAY_INSTANCED) & 1) != 0; }
|
||||
|
||||
@@ -405,7 +400,6 @@ public final class FragmentKey {
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(),
|
||||
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(),
|
||||
overlayEnabled() || overlayInstanced(), nrEnabledUnits()));
|
||||
if (textureSupersamplingSamples() > 1) sb.append(" samples=").append(textureSupersamplingSamples());
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
|
||||
@@ -28,10 +28,6 @@ public final class FragmentShaderGenerator {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append("out vec4 fragColor;\n\n");
|
||||
if (key.textureSupersamplingSamples() > 1) {
|
||||
sb.append("#define TEXTURE_SUPERSAMPLING_SAMPLES ").append(key.textureSupersamplingSamples()).append('\n');
|
||||
sb.append(ShaderLoader.getShaderSource("angelica:texture_supersampling.glsl"));
|
||||
}
|
||||
|
||||
sb.append("void main() {\n");
|
||||
emitLineStipple(sb, key);
|
||||
@@ -90,8 +86,7 @@ public final class FragmentShaderGenerator {
|
||||
private static void emitTextureSampling(StringBuilder sb, FragmentKey key) {
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (!key.unitEnabled(i)) continue;
|
||||
final String sample = i == 0 && key.textureSupersamplingSamples() > 1 ? "sampleSupersampledTexture" : "textureProj";
|
||||
sb.append(" vec4 tex").append(i).append("Color = ").append(sample).append("(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n");
|
||||
sb.append(" vec4 tex").append(i).append("Color = textureProj(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,10 +190,6 @@ public final class ShaderManager {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isTextureSupersampling() {
|
||||
return currentProgram != null && currentProgram.getFragmentKey().textureSupersamplingSamples() > 1;
|
||||
}
|
||||
|
||||
public static void endFrame() {
|
||||
final ShaderManager sm = GLStateManager.ctx().ffp;
|
||||
sm.lastFramePreDrawCalls = sm.preDrawCalls;
|
||||
@@ -203,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++;
|
||||
|
||||
@@ -11,7 +11,6 @@ import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.UniformRingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import org.joml.Math;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -50,7 +49,6 @@ public class Uniforms {
|
||||
private int viewportX = Integer.MIN_VALUE, viewportY = Integer.MIN_VALUE, viewportWidth = -1, viewportHeight = -1;
|
||||
private int lineStipple = -1;
|
||||
private int weatherGen = -1;
|
||||
private int atlasGutter = -1;
|
||||
|
||||
int blockWrites;
|
||||
int blockSkips;
|
||||
@@ -172,15 +170,6 @@ public class Uniforms {
|
||||
stagedFragment++;
|
||||
dirty = true;
|
||||
}
|
||||
if (glCtx.textureSupersamplingSamples > 1 && ffp.isTextureSupersampling()) {
|
||||
final int gutter = TextureInfoCache.INSTANCE.getInfo(glCtx.textures.getTextureUnitBindings(0).getBinding()).getAtlasGutter();
|
||||
if (gutter != atlasGutter) {
|
||||
putFloat(FFPUniformBlock.ATLAS_GUTTER, gutter);
|
||||
atlasGutter = gutter;
|
||||
stagedMisc++;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
final LineState line = glCtx.lineState;
|
||||
final float lw = line.getWidth();
|
||||
if (lw != lineWidth) {
|
||||
|
||||
@@ -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,15 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import lombok.Getter;
|
||||
|
||||
public class SamplerUnitArray {
|
||||
|
||||
private static final int MIN_UNITS = 16;
|
||||
|
||||
private int[] samplers;
|
||||
@Getter
|
||||
private int highestBoundUnit = -1;
|
||||
|
||||
public int get(int unit) {
|
||||
final int[] local = samplers;
|
||||
@@ -25,27 +22,15 @@ public class SamplerUnitArray {
|
||||
}
|
||||
if (unit >= local.length) return false;
|
||||
local[unit] = sampler;
|
||||
if (sampler != 0) {
|
||||
highestBoundUnit = Math.max(highestBoundUnit, unit);
|
||||
} else if (unit == highestBoundUnit) {
|
||||
trimUnboundUnits();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void unbindEverywhere(int sampler) {
|
||||
final int[] local = samplers;
|
||||
if (local == null) return;
|
||||
for (int i = 0; i <= highestBoundUnit; i++) {
|
||||
for (int i = 0; i < local.length; i++) {
|
||||
if (local[i] == sampler) local[i] = 0;
|
||||
}
|
||||
trimUnboundUnits();
|
||||
}
|
||||
|
||||
private void trimUnboundUnits() {
|
||||
while (highestBoundUnit >= 0 && samplers[highestBoundUnit] == 0) {
|
||||
highestBoundUnit--;
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
|
||||
-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) {
|
||||
|
||||
@@ -21,7 +21,6 @@ public class TextureInfo {
|
||||
protected int resolvedInternalFormat = -1;
|
||||
protected int width = -1;
|
||||
protected int height = -1;
|
||||
@Getter @Setter private int atlasGutter;
|
||||
|
||||
protected int minFilter = UNSET_INT;
|
||||
protected int magFilter = UNSET_INT;
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
const vec2 SAMPLE_OFFSETS[TEXTURE_SUPERSAMPLING_SAMPLES] = vec2[](
|
||||
#if TEXTURE_SUPERSAMPLING_SAMPLES == 2
|
||||
vec2(0.25, 0.25), vec2(-0.25, -0.25)
|
||||
#elif TEXTURE_SUPERSAMPLING_SAMPLES == 4
|
||||
vec2(0.125, 0.375), vec2(-0.125, -0.375),
|
||||
vec2(0.375, -0.125), vec2(-0.375, 0.125)
|
||||
#elif TEXTURE_SUPERSAMPLING_SAMPLES == 8
|
||||
vec2(-0.1875, -0.0625), vec2( 0.3125, 0.0625),
|
||||
vec2(-0.0625, 0.1875), vec2(-0.3125, -0.1875),
|
||||
vec2( 0.1875, -0.3125), vec2( 0.4375, -0.4375),
|
||||
vec2(-0.4375, 0.3125), vec2( 0.0625, 0.4375)
|
||||
#elif TEXTURE_SUPERSAMPLING_SAMPLES == 16
|
||||
vec2( 0.0625, 0.0625), vec2(-0.0625, -0.1875),
|
||||
vec2(-0.1875, 0.1250), vec2( 0.2500, -0.0625),
|
||||
vec2(-0.3125, -0.1250), vec2( 0.1250, 0.3125),
|
||||
vec2( 0.3125, 0.1875), vec2( 0.1875, -0.3125),
|
||||
vec2(-0.1250, 0.3750), vec2( 0.0000, -0.4375),
|
||||
vec2(-0.2500, -0.3750), vec2(-0.3750, 0.2500),
|
||||
vec2(-0.5000, 0.0000), vec2( 0.4375, -0.2500),
|
||||
vec2( 0.3750, 0.4375), vec2(-0.4375, -0.5000)
|
||||
#endif
|
||||
);
|
||||
|
||||
vec4 sampleSupersampledTexture(sampler2D tex, vec4 coord) {
|
||||
vec2 uv = coord.xy / coord.w;
|
||||
vec2 dx = dFdx(uv);
|
||||
vec2 dy = dFdy(uv);
|
||||
ivec2 size = textureSize(tex, 0);
|
||||
vec2 offsets[TEXTURE_SUPERSAMPLING_SAMPLES];
|
||||
vec2 extent = vec2(0.0);
|
||||
for (int i = 0; i < TEXTURE_SUPERSAMPLING_SAMPLES; i++) {
|
||||
offsets[i] = SAMPLE_OFFSETS[i].x * dx + SAMPLE_OFFSETS[i].y * dy;
|
||||
extent = max(extent, abs(offsets[i]));
|
||||
}
|
||||
extent *= vec2(size);
|
||||
float footprint = max(extent.x, extent.y);
|
||||
if (footprint >= u_AtlasGutter) return textureGrad(tex, uv, dx, dy);
|
||||
float supersampling = 1.0 - smoothstep(0.75 * u_AtlasGutter, u_AtlasGutter, footprint);
|
||||
|
||||
// The atlas gutter is 2^maxMipLevel.
|
||||
float texelsPerPixel = max(length(dx * vec2(size)), length(dy * vec2(size)));
|
||||
int lod = int(min(ceil(log2(max(texelsPerPixel / sqrt(float(TEXTURE_SUPERSAMPLING_SAMPLES)), 1.0))),
|
||||
log2(u_AtlasGutter)));
|
||||
|
||||
// Transparent texels must not tint the visible samples.
|
||||
vec4 color = vec4(0.0);
|
||||
for (int i = 0; i < TEXTURE_SUPERSAMPLING_SAMPLES; i++) {
|
||||
ivec2 texel = min(ivec2(fract(uv + offsets[i]) * vec2(size)), size - 1);
|
||||
vec4 sampleColor = texelFetch(tex, texel >> lod, lod);
|
||||
color += vec4(sampleColor.rgb * sampleColor.a, sampleColor.a);
|
||||
}
|
||||
color /= float(TEXTURE_SUPERSAMPLING_SAMPLES);
|
||||
if (supersampling < 1.0) {
|
||||
vec4 filtered = textureGrad(tex, uv, dx, dy);
|
||||
color = mix(vec4(filtered.rgb * filtered.a, filtered.a), color, supersampling);
|
||||
}
|
||||
return vec4(color.rgb / max(color.a, 0.00001), color.a);
|
||||
}
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/** The executed half of a GL_COMPILE_AND_EXECUTE glDrawArrays sees transforms issued since the last matrix barrier. */
|
||||
@GLCoreTest
|
||||
public class GLSM_DisplayList_CompileAndExecuteArrays_GLTest {
|
||||
|
||||
private static final int W = 32;
|
||||
|
||||
private static final int POSITION_LOC = VertexFormatElement.Usage.POSITION.getAttributeLocation();
|
||||
|
||||
private final ByteBuffer pixels = BufferUtils.createByteBuffer(W * 4);
|
||||
private int savedX, savedY, savedW, savedH;
|
||||
private int vao, vbo, list;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
final ViewportState vp = GLStateManager.getViewportState();
|
||||
savedX = vp.x;
|
||||
savedY = vp.y;
|
||||
savedW = vp.width;
|
||||
savedH = vp.height;
|
||||
|
||||
GLStateManager.glUseProgram(0);
|
||||
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
final ByteBuffer verts = ByteBuffer.allocateDirect(3 * 12).order(ByteOrder.nativeOrder());
|
||||
verts.putFloat(0f).putFloat(0.5f).putFloat(0f).putFloat(W).putFloat(0.5f).putFloat(0f).flip();
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, verts, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(POSITION_LOC, 3, GL11.GL_FLOAT, false, 12, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(POSITION_LOC);
|
||||
|
||||
GLStateManager.glViewport(0, 0, W, 1);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glOrtho(0, W, 0, 1, -1, 1);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glColor4f(1f, 1f, 1f, 1f);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (list > 0) GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glViewport(savedX, savedY, savedW, savedH);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (vbo != 0) GLStateManager.glDeleteBuffers(vbo);
|
||||
if (vao != 0) GLStateManager.glDeleteVertexArrays(vao);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
private boolean[] readCoverage() {
|
||||
pixels.clear();
|
||||
GLStateManager.glReadPixels(0, 0, W, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL error during readback");
|
||||
|
||||
final boolean[] covered = new boolean[W];
|
||||
for (int i = 0; i < W; i++) {
|
||||
covered[i] = (pixels.get(i * 4) & 0xFF) > 127;
|
||||
}
|
||||
return covered;
|
||||
}
|
||||
|
||||
private static boolean[] leftHalf() {
|
||||
final boolean[] e = new boolean[W];
|
||||
for (int i = 0; i < W / 2; i++) e[i] = true;
|
||||
return e;
|
||||
}
|
||||
|
||||
/** The scale is issued inside the list, after the push that flushed everything before it - AE2 monitor's shape. */
|
||||
private void compileScaledDraw(int mode) {
|
||||
list = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(list, mode);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glScalef(0.5f, 1f, 1f);
|
||||
GLStateManager.glDrawArrays(GL11.GL_LINES, 0, 2);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glEndList();
|
||||
}
|
||||
|
||||
// Replay is not asserted here: a compiled list plays back through FFP client arrays, which this core context lacks,
|
||||
// and in the compat context this attribute setup draws nothing at all.
|
||||
@Test
|
||||
void compileAndExecuteDrawsWithPendingScale() {
|
||||
clear();
|
||||
compileScaledDraw(GL11.GL_COMPILE_AND_EXECUTE);
|
||||
assertArrayEquals(leftHalf(), readCoverage(), "executed half of GL_COMPILE_AND_EXECUTE");
|
||||
}
|
||||
}
|
||||
-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());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -11,7 +10,6 @@ import org.lwjgl.opengl.GL33;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
@@ -117,88 +115,37 @@ class GLSM_SamplerBinding_GLTest {
|
||||
@Test
|
||||
void unbindAllSamplersClearsUnitsBoundThroughGlsm() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
final Runnable previousFlush = BatchStateGuard.flush;
|
||||
final AtomicInteger flushes = new AtomicInteger();
|
||||
try {
|
||||
GLStateManager.glBindSampler(1, sampler);
|
||||
GLStateManager.glBindSampler(3, sampler);
|
||||
BatchStateGuard.flush = () -> {
|
||||
assertEquals(sampler, driverBinding(1), "deferred draws must see the original bindings");
|
||||
assertEquals(sampler, driverBinding(3), "deferred draws must see the original bindings");
|
||||
flushes.incrementAndGet();
|
||||
};
|
||||
|
||||
RenderSystem.unbindAllSamplers();
|
||||
RenderSystem.unbindAllSamplers();
|
||||
|
||||
assertEquals(1, flushes.get(), "bulk unbind should drain deferred draws once");
|
||||
assertEquals(0, driverBinding(1), "unit 1 still bound on the driver");
|
||||
assertEquals(0, driverBinding(3), "unit 3 still bound on the driver");
|
||||
assertEquals(0, GLStateManager.getSamplerBinding(1));
|
||||
assertEquals(0, GLStateManager.getSamplerBinding(3));
|
||||
} finally {
|
||||
BatchStateGuard.flush = previousFlush;
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void replayForwardsCachedSamplerBindings() {
|
||||
final int highUnit = RenderSystem.getMaxCombinedTextureImageUnits() - 1;
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(2, sampler);
|
||||
GLStateManager.glBindSampler(highUnit, sampler);
|
||||
GL33.glBindSampler(2, 0);
|
||||
GL33.glBindSampler(highUnit, 0);
|
||||
assertEquals(0, driverBinding(2), "setup failed to diverge the driver");
|
||||
assertEquals(0, driverBinding(highUnit), "setup failed to diverge the driver");
|
||||
|
||||
GLStateManager.replayStateToBackend();
|
||||
|
||||
assertEquals(sampler, driverBinding(2), "replay dropped a cached sampler binding");
|
||||
assertEquals(sampler, driverBinding(highUnit), "replay skipped the highest bound unit");
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void sparseBindingsKeepTheCleanupLimitAccurateAfterUnbindAndDeletion() {
|
||||
final int highUnit = RenderSystem.getMaxCombinedTextureImageUnits() - 1;
|
||||
final int low = RenderSystem.genSampler();
|
||||
final int high = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(1, low);
|
||||
GLStateManager.glBindSampler(3, high);
|
||||
GLStateManager.glBindSampler(highUnit, high);
|
||||
assertEquals(highUnit, GLStateManager.ctx().samplerUnits.getHighestBoundUnit());
|
||||
|
||||
GLStateManager.glBindSampler(highUnit, 0);
|
||||
assertEquals(3, GLStateManager.ctx().samplerUnits.getHighestBoundUnit());
|
||||
assertEquals(0, driverBinding(highUnit));
|
||||
|
||||
GLStateManager.glBindSampler(highUnit, high);
|
||||
RenderSystem.destroySampler(high);
|
||||
assertEquals(1, GLStateManager.ctx().samplerUnits.getHighestBoundUnit());
|
||||
assertEquals(0, driverBinding(highUnit));
|
||||
assertEquals(0, driverBinding(3));
|
||||
|
||||
RenderSystem.unbindAllSamplers();
|
||||
assertEquals(-1, GLStateManager.ctx().samplerUnits.getHighestBoundUnit());
|
||||
assertEquals(0, driverBinding(1));
|
||||
|
||||
GLStateManager.glBindSampler(highUnit, low);
|
||||
RenderSystem.unbindAllSamplers();
|
||||
RenderSystem.unbindAllSamplers();
|
||||
assertEquals(-1, GLStateManager.ctx().samplerUnits.getHighestBoundUnit());
|
||||
assertEquals(0, driverBinding(highUnit));
|
||||
} finally {
|
||||
RenderSystem.destroySampler(low);
|
||||
RenderSystem.destroySampler(high);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void intBufferGenFillsFromPositionWithBindableSamplers() {
|
||||
final IntBuffer buf = ints(-1, 0, 0);
|
||||
|
||||
+4
@@ -202,6 +202,7 @@ public class GLSM_SharedDrawableContext_GLTest {
|
||||
|
||||
DrawableContexts.run(sharedDrawable, "SharedDrawableContext-Reentry-Thread", () -> {
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
final boolean wasEnabled = ShaderManager.isEnabled();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
try {
|
||||
@@ -227,6 +228,7 @@ public class GLSM_SharedDrawableContext_GLTest {
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
} finally {
|
||||
sm.deactivate();
|
||||
if (!wasEnabled) ShaderManager.disable();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -271,6 +273,7 @@ public class GLSM_SharedDrawableContext_GLTest {
|
||||
void indexedListCompiledOnSharedDrawableReplaysOnDisplay() throws Exception {
|
||||
final int list = compileIndexedListOnSharedDrawable();
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
final boolean wasEnabled = ShaderManager.isEnabled();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
try {
|
||||
@@ -280,6 +283,7 @@ public class GLSM_SharedDrawableContext_GLTest {
|
||||
assertEquals(indexedBatch().getSharedEBO(), GL11.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING));
|
||||
} finally {
|
||||
sm.deactivate();
|
||||
if (!wasEnabled) ShaderManager.disable();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ class FFPVertexLightingGLTest {
|
||||
GLStateManager.glDisable(GL11.GL_COLOR_MATERIAL);
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm.isActive()) sm.deactivate();
|
||||
ShaderManager.disable();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
if (vbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
|
||||
-302
@@ -1,302 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.TEXTURE_BIT;
|
||||
import static org.joml.Math.clamp;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class TextureSupersamplingGLTest {
|
||||
private static final int SIZE = 200;
|
||||
private int texture;
|
||||
private int vao;
|
||||
private int vbo;
|
||||
|
||||
private static int dotColor(int x, int y) {
|
||||
final boolean dot = (x == 3 || x == 6 || x == 9 || x == 12)
|
||||
&& (y == 3 || y == 6 || y == 9 || y == 12)
|
||||
&& (x == 3 || x == 12 || y == 3 || y == 12);
|
||||
return dot ? 0x444444FF : 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
private static int uploadTexture(int spriteSize, int gutter, IntBinaryOperator color) {
|
||||
final int atlasSize = Integer.highestOneBit(spriteSize + 2 * gutter) << 1;
|
||||
final ByteBuffer pixels = BufferUtils.createByteBuffer(atlasSize * atlasSize * 4);
|
||||
for (int y = 0; y < atlasSize; y++) {
|
||||
for (int x = 0; x < atlasSize; x++) {
|
||||
final int rgba = x < spriteSize + 2 * gutter && y < spriteSize + 2 * gutter
|
||||
? color.applyAsInt(clamp(0, spriteSize - 1, x - gutter), clamp(0, spriteSize - 1, y - gutter))
|
||||
: 0xFF00FFFF;
|
||||
pixels.put((byte) (rgba >>> 24)).put((byte) (rgba >>> 16)).put((byte) (rgba >>> 8)).put((byte) rgba);
|
||||
}
|
||||
}
|
||||
pixels.flip();
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, atlasSize, atlasSize, 0,
|
||||
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static void vertex(ByteBuffer vertices, float x, float z, float u, float v) {
|
||||
vertices.putFloat(x).putFloat(0.5f).putFloat(z).putFloat(u).putFloat(v);
|
||||
}
|
||||
|
||||
private static int countDotRegions(int[] pixels) {
|
||||
final boolean[] remaining = new boolean[pixels.length];
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
final int gray = pixels[i] & 255;
|
||||
remaining[i] = gray > 0 && gray < 250
|
||||
&& ((pixels[i] >> 8) & 255) == gray && ((pixels[i] >> 16) & 255) == gray;
|
||||
}
|
||||
int regions = 0;
|
||||
final ArrayDeque<Integer> pending = new ArrayDeque<>();
|
||||
for (int i = 0; i < remaining.length; i++) {
|
||||
if (!remaining[i]) continue;
|
||||
regions++;
|
||||
remaining[i] = false;
|
||||
pending.add(i);
|
||||
while (!pending.isEmpty()) {
|
||||
final int pixel = pending.removeFirst();
|
||||
for (int neighbor : new int[]{pixel - 1, pixel + 1, pixel - SIZE, pixel + SIZE}) {
|
||||
if (neighbor >= 0 && neighbor < remaining.length && remaining[neighbor]) {
|
||||
remaining[neighbor] = false;
|
||||
pending.add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return regions;
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
FfpFixture.resetFfpState();
|
||||
GLStateManager.setTextureSupersamplingSamples(1);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glDisable(GL11.GL_FOG);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glOrtho(0, SIZE, SIZE, 0, -100, 100);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
FfpFixture.activateFfp();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
GLStateManager.setTextureSupersamplingSamples(1);
|
||||
FfpFixture.resetFfpState();
|
||||
if (texture != 0) GLStateManager.glDeleteTextures(texture);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (vbo != 0) GLStateManager.glDeleteBuffers(vbo);
|
||||
if (vao != 0) GLStateManager.glDeleteVertexArrays(vao);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glViewport(0, 0, 800, 600);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {4, 16})
|
||||
void recoversTwelveDotsAtScaleThreeAndRestoresNearestAfterward(int samples) {
|
||||
createDotAtlas();
|
||||
|
||||
final int[] nearest = drawTop(3);
|
||||
assertEquals(0, countDotRegions(nearest));
|
||||
final int previous = GLStateManager.setTextureSupersamplingSamples(samples);
|
||||
assertEquals(12, countDotRegions(drawTop(3)));
|
||||
GLStateManager.setTextureSupersamplingSamples(previous);
|
||||
assertArrayEquals(nearest, drawTop(3));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {4, 16})
|
||||
void highResolutionCheckerboardsStaySmoothWithTheDefaultAtlasGutter(int samples) {
|
||||
createAtlas(256, 16, (x, y) -> ((x ^ y) & 1) == 0 ? 0xFFFFFFFF : 0x000000FF);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 4);
|
||||
GLStateManager.glGenerateMipmap(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
|
||||
final int[] filtered = drawFlat();
|
||||
GLStateManager.setTextureSupersamplingSamples(samples);
|
||||
final int[] supersampled = drawFlat();
|
||||
for (int y = 97; y <= 102; y++) {
|
||||
for (int x = 97; x <= 102; x++) {
|
||||
final int index = y * SIZE + x;
|
||||
assertEquals(128, filtered[index] & 255, 1, "The mip-filtered checkerboard must average to gray");
|
||||
assertEquals(filtered[index] & 255, supersampled[index] & 255, 1,
|
||||
"Supersampling must preserve the mip average instead of aliasing the base-level checkerboard");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {4, 16})
|
||||
void transparentTexelsDoNotTintPartialCoverage(int samples) {
|
||||
createAtlas(16, (x, y) -> x < 8 ? 0xFF0000FF : 0x0000FF00);
|
||||
GLStateManager.glEnable(GL11.GL_BLEND);
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLStateManager.setTextureSupersamplingSamples(samples);
|
||||
final int[] pixels = drawTop(3);
|
||||
int partialPixels = 0;
|
||||
for (int pixel : pixels) {
|
||||
final int red = pixel & 255;
|
||||
if (red > 0 && red < 255) {
|
||||
partialPixels++;
|
||||
assertEquals(0, (pixel >> 8) & 255);
|
||||
assertEquals(0, (pixel >> 16) & 255, "Transparent blue texels must not tint the red edge");
|
||||
}
|
||||
}
|
||||
assertTrue(partialPixels > 0, "The test must cover a partially transparent pixel");
|
||||
}
|
||||
|
||||
@Test
|
||||
void modelTexturesKeepTheirOwnFilteringWhileSupersamplingIsActive() {
|
||||
createDotAtlas();
|
||||
final int modelTexture = uploadTexture(16, 1, TextureSupersamplingGLTest::dotColor);
|
||||
try {
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
||||
final int[] normal = drawTop(3);
|
||||
GLStateManager.setTextureSupersamplingSamples(16);
|
||||
assertArrayEquals(normal, drawTop(3));
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
assertFalse(Arrays.equals(normal, drawTop(3)));
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, modelTexture);
|
||||
assertArrayEquals(normal, drawTop(3));
|
||||
} finally {
|
||||
GLStateManager.glDeleteTextures(modelTexture);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void atlasFetchesIgnoreSamplerFilteringAndRepeatAcrossNegativeUvs() {
|
||||
createAtlas(16, 4, TextureSupersamplingGLTest::dotColor);
|
||||
GLStateManager.setTextureSupersamplingSamples(16);
|
||||
final int[] nearest = drawTop(3);
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
|
||||
RenderSystem.bindSamplerToUnit(0, sampler);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glTranslatef(-2, 3, 0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
assertArrayEquals(nearest, drawTop(3));
|
||||
} finally {
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
RenderSystem.bindSamplerToUnit(0, 0);
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void changingTheAtlasGutterSwitchesBetweenMipFilteringAndFullFootprintSampling() {
|
||||
createAtlas(32, 4, (x, y) -> 0xFF0000FF);
|
||||
final int[] baseLevel = drawFlat();
|
||||
for (int level = 1; level <= 6; level++) {
|
||||
final int size = 64 >> level;
|
||||
final ByteBuffer pixels = BufferUtils.createByteBuffer(size * size * 4);
|
||||
for (int i = 0; i < size * size; i++) pixels.putInt(0xFFFF0000);
|
||||
pixels.flip();
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, size, size, 0,
|
||||
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
}
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 6);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST_MIPMAP_NEAREST);
|
||||
final int[] mipFiltered = drawFlat();
|
||||
assertFalse(Arrays.equals(baseLevel, mipFiltered));
|
||||
GLStateManager.setTextureSupersamplingSamples(16);
|
||||
assertArrayEquals(baseLevel, drawFlat());
|
||||
GLStateManager.markTextureAsAtlas(texture, 1);
|
||||
assertArrayEquals(mipFiltered, drawFlat());
|
||||
GLStateManager.markTextureAsAtlas(texture, 4);
|
||||
assertArrayEquals(baseLevel, drawFlat());
|
||||
}
|
||||
|
||||
private int[] drawFlat() {
|
||||
FfpFixture.clear();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(100.25f, 100.125f, 0);
|
||||
GLStateManager.glScalef(11, 11, 1);
|
||||
GLStateManager.glRotatef(90, 1, 0, 0);
|
||||
GLStateManager.glDrawArrays(GL11.GL_QUADS, 0, 4);
|
||||
return FfpFixture.readRegion(SIZE);
|
||||
}
|
||||
|
||||
private void createDotAtlas() {
|
||||
createAtlas(16, TextureSupersamplingGLTest::dotColor);
|
||||
}
|
||||
|
||||
private void createAtlas(int spriteSize, IntBinaryOperator color) {
|
||||
createAtlas(spriteSize, 1, color);
|
||||
}
|
||||
|
||||
private void createAtlas(int spriteSize, int gutter, IntBinaryOperator color) {
|
||||
texture = uploadTexture(spriteSize, gutter, color);
|
||||
GLStateManager.markTextureAsAtlas(texture, gutter);
|
||||
final int atlasSize = Integer.highestOneBit(spriteSize + 2 * gutter) << 1;
|
||||
final float minUv = (gutter + 0.01f) / atlasSize;
|
||||
final float maxUv = (spriteSize + gutter - 0.01f) / atlasSize;
|
||||
final ByteBuffer vertices = BufferUtils.createByteBuffer(4 * 20);
|
||||
vertex(vertices, -0.5f, -0.5f, minUv, minUv);
|
||||
vertex(vertices, 0.5f, -0.5f, maxUv, minUv);
|
||||
vertex(vertices, 0.5f, 0.5f, maxUv, maxUv);
|
||||
vertex(vertices, -0.5f, 0.5f, minUv, maxUv);
|
||||
vertices.flip();
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
|
||||
FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, 20, 0);
|
||||
FfpFixture.attrib(2, 2, GL11.GL_FLOAT, false, 20, 12);
|
||||
VAOManager.setCurrentVertexFlags(TEXTURE_BIT);
|
||||
}
|
||||
|
||||
private int[] drawTop(int scale) {
|
||||
FfpFixture.clear();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(100 + 8 * scale, 100 + 8 * scale, 0);
|
||||
GLStateManager.glScalef(10 * scale, 10 * scale, -10 * scale);
|
||||
GLStateManager.glRotatef(210, 1, 0, 0);
|
||||
GLStateManager.glRotatef(45, 0, 1, 0);
|
||||
GLStateManager.glDrawArrays(GL11.GL_QUADS, 0, 4);
|
||||
return FfpFixture.readRegion(SIZE);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
|
||||
|
||||
/**
|
||||
@@ -83,11 +82,8 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
public void afterEach(ExtensionContext context) {
|
||||
final int error = GL11.glGetError();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
final boolean ffpDisabled = !ShaderManager.isEnabled();
|
||||
if (ffpDisabled) ShaderManager.enable();
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
assertEquals(GL11.GL_NO_ERROR, error, () -> "GL Error: 0x" + Integer.toHexString(error));
|
||||
if (ffpDisabled) fail("test left FFP disabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.loading.ClassDump;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.ModRedirector;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.MutableStatics;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
@@ -28,7 +27,6 @@ public final class GlsmRedirectAgent {
|
||||
}
|
||||
|
||||
public static void install(Instrumentation instrumentation) {
|
||||
MutableStatics.install(instrumentation);
|
||||
final ModRedirector redirector = new ModRedirector(ClassDump.DISABLED);
|
||||
addTransformer(instrumentation, (className, classfile) -> {
|
||||
if (!className.startsWith(GTNHLIB)) return null;
|
||||
|
||||
@@ -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) {
|
||||
@@ -237,6 +233,7 @@ public final class FfpFixture {
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm.isActive()) sm.deactivate();
|
||||
ShaderManager.disable();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.testutil;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/* Test agent: strips ACC_FINAL from listed static finals at load to avoid JIT inlining them when tests want to mutate them */
|
||||
public final class MutableStatics {
|
||||
|
||||
private static final Map<String, Set<String>> FIELDS_BY_OWNER = new HashMap<>();
|
||||
|
||||
static {
|
||||
add("com/gtnewhorizons/angelica/glsm/backend/BackendManager", "RENDER_BACKEND");
|
||||
add("com/gtnewhorizons/angelica/glsm/GLStateManager", "MainThread");
|
||||
add("com/gtnewhorizons/angelica/config/SystemProperties", "UNMAPPED_GL", "SDL_DISABLE_IN_PASS_CLEAR");
|
||||
add("net/coderbot/iris/Iris", "enabled");
|
||||
}
|
||||
|
||||
private MutableStatics() {}
|
||||
|
||||
private static void add(String owner, String... fields) {
|
||||
FIELDS_BY_OWNER.put(owner, new HashSet<>(Arrays.asList(fields)));
|
||||
}
|
||||
|
||||
public static void premain(String args, Instrumentation instrumentation) {
|
||||
install(instrumentation);
|
||||
}
|
||||
|
||||
public static void install(Instrumentation instrumentation) {
|
||||
GlsmRedirectAgent.addTransformer(instrumentation, (className, classfile) -> {
|
||||
final Set<String> fields = FIELDS_BY_OWNER.get(className);
|
||||
return fields == null ? null : stripFinal(classfile, fields);
|
||||
});
|
||||
}
|
||||
|
||||
private static byte[] stripFinal(byte[] classfile, Set<String> fields) {
|
||||
final ClassNode node = new ClassNode();
|
||||
new ClassReader(classfile).accept(node, 0);
|
||||
int patched = 0;
|
||||
for (FieldNode field : node.fields) {
|
||||
if (fields.contains(field.name)) {
|
||||
field.access &= ~Opcodes.ACC_FINAL;
|
||||
patched++;
|
||||
}
|
||||
}
|
||||
if (patched != fields.size()) throw new IllegalStateException(node.name + " lacks one of " + fields);
|
||||
final ClassWriter writer = new ClassWriter(0);
|
||||
node.accept(writer);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm.testutil;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
|
||||
import com.gtnewhorizon.gtnhlib.reflect.Fields;
|
||||
import com.gtnewhorizon.gtnhlib.reflect.Fields.LookupType;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public final class Reflect {
|
||||
|
||||
@@ -59,13 +59,7 @@ public final class Reflect {
|
||||
}
|
||||
|
||||
public static <F> void setStaticFinal(Class<?> owner, String name, Class<F> type, F value) {
|
||||
final Field f = field(owner, name);
|
||||
if (f.getType() != type) throw new AssertionError(owner.getName() + "." + name + " is " + f.getType().getName() + ", not " + type.getName());
|
||||
if (Modifier.isFinal(f.getModifiers())) {
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
throw new AssertionError(owner.getName() + "." + name + " is still final; add it to MutableStatics");
|
||||
}
|
||||
setStatic(owner, name, value);
|
||||
Fields.ofClass(owner).getField(LookupType.DECLARED_IN_HIERARCHY, name, type).setValue(null, value);
|
||||
}
|
||||
|
||||
public static void setDeclared(Class<?> owner, Object target, String name, Object value) {
|
||||
|
||||
+6
-6
@@ -47,6 +47,7 @@ public final class VertexWriteReplayer {
|
||||
private static final int MAX_DIVISOR = 0xFFFF;
|
||||
private static final int UNCHANGED = -1;
|
||||
private static final int TEXTURE_UNITS = 32;
|
||||
private static final int SAMPLER_OBJECT_OF_DRAW = -1;
|
||||
|
||||
public static final class Replay {
|
||||
final int replayProgram;
|
||||
@@ -59,7 +60,7 @@ public final class VertexWriteReplayer {
|
||||
int[] uniformTargets;
|
||||
int[] samplerUnits;
|
||||
IntSupplier[] samplerSources;
|
||||
IntSupplier[] samplerObjects;
|
||||
int[] samplerObjects;
|
||||
int[] packStorageSlots;
|
||||
int[] packRwStorageSlots;
|
||||
int[] uniformBlockIndices;
|
||||
@@ -142,7 +143,7 @@ public final class VertexWriteReplayer {
|
||||
private void resolveSamplers(ShaderManager.ProgramObject graphics, ShaderManager.ProgramObject replay, List<String> names) {
|
||||
final IntArrayList units = new IntArrayList();
|
||||
final List<IntSupplier> sources = new ArrayList<>();
|
||||
final List<IntSupplier> objects = new ArrayList<>();
|
||||
final IntArrayList objects = new IntArrayList();
|
||||
final IntArrayList ownTextureIndices = new IntArrayList();
|
||||
final List<String> ownTextureNames = new ArrayList<>();
|
||||
final List<String> pipelineNames = Arrays.asList(setup.samplerNames());
|
||||
@@ -163,7 +164,7 @@ public final class VertexWriteReplayer {
|
||||
if (!units.contains(unit)) {
|
||||
units.add(unit);
|
||||
sources.add(null);
|
||||
objects.add(null);
|
||||
objects.add(SAMPLER_OBJECT_OF_DRAW);
|
||||
}
|
||||
}
|
||||
int free = TEXTURE_UNITS - 1;
|
||||
@@ -178,7 +179,7 @@ public final class VertexWriteReplayer {
|
||||
}
|
||||
samplerUnits = units.toIntArray();
|
||||
samplerSources = sources.toArray(new IntSupplier[0]);
|
||||
samplerObjects = objects.toArray(new IntSupplier[0]);
|
||||
samplerObjects = objects.toIntArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,8 +306,7 @@ public final class VertexWriteReplayer {
|
||||
final int unit = replay.samplerUnits[i];
|
||||
final IntSupplier source = replay.samplerSources[i];
|
||||
c.samplerTextures[i] = source != null ? source.getAsInt() : st.boundTextures[unit];
|
||||
final IntSupplier sampler = replay.samplerObjects[i];
|
||||
final int samplerObject = sampler != null ? sampler.getAsInt() : st.boundSamplerObjects[unit];
|
||||
final int samplerObject = replay.samplerObjects[i] != SAMPLER_OBJECT_OF_DRAW ? replay.samplerObjects[i] : st.boundSamplerObjects[unit];
|
||||
c.samplerHandles[i] = computeBinder.samplerHandle(samplerObject, c.samplerTextures[i]);
|
||||
}
|
||||
if (c.imageTextures.length != images.length) c.imageTextures = new int[images.length];
|
||||
|
||||
+12
-2
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
@@ -13,9 +12,11 @@ import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.BLACK;
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED;
|
||||
@@ -33,8 +34,17 @@ class GlsmSdlAttachmentClearTest {
|
||||
return GlsmSdlHeadlessRig.frameManager().frame();
|
||||
}
|
||||
|
||||
// Reflect.setStaticFinal rejects primitive fields.
|
||||
private static void setDisableInPassClear(boolean value) {
|
||||
Reflect.setStaticFinal(SystemProperties.class, "SDL_DISABLE_IN_PASS_CLEAR", boolean.class, value);
|
||||
try {
|
||||
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
theUnsafe.setAccessible(true);
|
||||
final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
|
||||
final Field field = SystemProperties.class.getDeclaredField("SDL_DISABLE_IN_PASS_CLEAR");
|
||||
unsafe.putBoolean(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears colour to black and depth to 0.9, then draws a quad so a render pass is open. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.common;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.TextureAtlasType;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -40,7 +39,7 @@ public class BlockError extends Block {
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void onTextureStitch(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() == TextureAtlasType.BLOCK) {
|
||||
if (event.map.getTextureType() == 0) {
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
icons[i] = event.map.registerIcon("angelica:error_block_" + i);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -206,11 +192,6 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableTESRProviderDispatch;
|
||||
|
||||
@Config.Comment("Cache the lightmap the tile-entity dispatcher looks up per tile per frame, refreshing it once a tick")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableTESRLightCache;
|
||||
|
||||
@Config.Comment("Enable full RGB color support (16.7M colors) using &#RRGGBB syntax in text")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableRGBColors;
|
||||
@@ -295,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
|
||||
@@ -567,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);
|
||||
|
||||
@@ -68,12 +68,7 @@ public enum Mixins implements IMixins {
|
||||
, "angelica.optimizations.MixinRenderBiped_ArmorResource"
|
||||
, "angelica.optimizations.MixinRendererLivingEntity"
|
||||
, "angelica.rendering.MixinRenderGlobal_SelectionBox"
|
||||
, "angelica.textures.MixinForgeHooksClient_GuiSupersampling"
|
||||
, "angelica.textures.MixinRenderBlocks_GuiSupersampling"
|
||||
, "angelica.textures.MixinRenderItem_GuiSupersampling"
|
||||
, "angelica.textures.MixinTileEntityRendererDispatcher_GuiSupersampling"
|
||||
, "angelica.gui.MixinGuiIngameForge_ModernF3"
|
||||
, "angelica.textures.MixinRenderItem_GuiSampler"
|
||||
)
|
||||
),
|
||||
|
||||
@@ -212,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)
|
||||
@@ -457,7 +443,6 @@ public enum Mixins implements IMixins {
|
||||
, "celeritas.frustum.MixinClippingHelperImpl"
|
||||
, "celeritas.frustum.MixinFrustrum"
|
||||
, "celeritas.features.culling.MixinEffectRenderer"
|
||||
, "celeritas.features.mipmaps.MixinEffectRenderer"
|
||||
, "celeritas.features.mipmaps.MixinTextureAtlasSprite"
|
||||
, "celeritas.features.mipmaps.MixinTextureMetadataSection"
|
||||
, "celeritas.features.mipmaps.MixinTextureMetadataSectionSerializer"
|
||||
@@ -632,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(
|
||||
@@ -663,11 +656,6 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.optimizeWorldUpdateLight)
|
||||
.addCommonMixins("angelica.lighting.MixinWorld_FixLightUpdateLag")),
|
||||
|
||||
TESR_LIGHT_CACHE(new MixinBuilder("Cache the tile-entity dispatcher's per-tile lightmap lookup for one tick")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableTESRLightCache)
|
||||
.addClientMixins("angelica.lighting.MixinTileEntityRendererDispatcher_LightCache")),
|
||||
|
||||
SPEEDUP_VANILLA_ANIMATIONS(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
|
||||
@@ -896,11 +884,6 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins("client.dragonapi.MixinThrottleableEffectRenderer")
|
||||
),
|
||||
DRAGONAPI_PARTICLE_CUTOFF(new MixinBuilder("Modern 26.2 mipmap alpha cutoff for DragonAPI's terrain particles")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.DRAGON_API)
|
||||
.addClientMixins("client.dragonapi.MixinThrottleableEffectRenderer_MipmapCutoff")
|
||||
),
|
||||
DRAGONAPI_PARTICLE_BATCH(new MixinBuilder("Particle instancing for DragonAPI's replacement particle renderer")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.DRAGON_API)
|
||||
|
||||
@@ -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"),
|
||||
|
||||
-4
@@ -7,8 +7,4 @@ public interface TextureMetadataExtension {
|
||||
void angelica$setMipmapStrategy(MipmapStrategy strategy);
|
||||
|
||||
MipmapStrategy angelica$getMipmapStrategy();
|
||||
|
||||
void angelica$setAlphaCutoffBias(float bias);
|
||||
|
||||
float angelica$getAlphaCutoffBias();
|
||||
}
|
||||
@@ -51,7 +51,6 @@ import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.AngelicaTesrMeshCache;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityTextureAtlas;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrLightCache;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.ConfigMigrator;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
@@ -79,7 +78,6 @@ import com.gtnewhorizons.angelica.rendering.AngelicaBlockSafetyRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.FpsReducer;
|
||||
import com.gtnewhorizons.angelica.rendering.FramePacer;
|
||||
import com.gtnewhorizons.angelica.rendering.IsbrhTestRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.TextureSamplers;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasDebugScreenHandler;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
@@ -170,7 +168,6 @@ public final class ClientProxy extends CommonProxy {
|
||||
HUDCaching.init();
|
||||
}
|
||||
CeleritasSetup.ensureInitialized();
|
||||
TextureSamplers.initRenderer();
|
||||
DarkModeUtils.init();
|
||||
final SodiumGameOptions opts = options();
|
||||
if (opts.advanced.vsyncMode == null) {
|
||||
@@ -423,10 +420,6 @@ public final class ClientProxy extends CommonProxy {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onTick(TickEvent.ClientTickEvent event) {
|
||||
if (event.phase == TickEvent.Phase.END) {
|
||||
TesrLightCache.onClientTick();
|
||||
}
|
||||
|
||||
if (event.phase == TickEvent.Phase.END && mc.theWorld != null) {
|
||||
CloudRenderer.getCloudRenderer().checkSettings();
|
||||
}
|
||||
|
||||
@@ -646,7 +646,7 @@ final class CloudVertexMesh {
|
||||
vao.bind();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vao.getVBO().getId());
|
||||
GLStateManager.glEnableVertexAttribArray(FACE_SHADE_ATTRIB);
|
||||
GLStateManager.glVertexAttribPointer(FACE_SHADE_ATTRIB, 4, GL11.GL_UNSIGNED_BYTE, true, vertexStride, normalOffset);
|
||||
GLStateManager.glVertexAttribPointer(FACE_SHADE_ATTRIB, 1, GL11.GL_UNSIGNED_BYTE, true, vertexStride, normalOffset + 3);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.render;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.TextureAtlasType;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
@@ -23,7 +22,7 @@ public class EmissiveTextureAutoloader {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onTextureStitchPre(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() != TextureAtlasType.BLOCK) return;
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
|
||||
TextureMap map = event.map;
|
||||
EmissiveTextureHelper.clearCache();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
|
||||
public final class GuiBlockSupersampling {
|
||||
private GuiBlockSupersampling() {}
|
||||
|
||||
public static int begin() {
|
||||
if (!RenderingState.INSTANCE.isRenderingGuiItem() || RenderingState.INSTANCE.isRenderingWorld()) return 0;
|
||||
final int samples = ClientProxy.options().quality.guiBlockSupersampling;
|
||||
return samples > 1 ? GLStateManager.setTextureSupersamplingSamples(samples) : 0;
|
||||
}
|
||||
|
||||
public static void end(int previous) {
|
||||
if (previous != 0) GLStateManager.setTextureSupersamplingSamples(previous);
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,6 @@ public class RenderingState {
|
||||
@Getter
|
||||
@Setter
|
||||
private float fov;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean renderingWorld;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean renderingGuiItem;
|
||||
|
||||
|
||||
public void setCameraPosition(double x, double y, double z) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
public final class TextureAtlasType {
|
||||
public static final int BLOCK = 0;
|
||||
public static final int ITEM = 1;
|
||||
|
||||
private TextureAtlasType() {}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.gl.sampler.GlSampler;
|
||||
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
|
||||
|
||||
public final class TextureSamplers {
|
||||
private static final GlSampler[][] TERRAIN_SAMPLERS = new GlSampler[2][17];
|
||||
@Getter
|
||||
private static GlSampler guiSampler;
|
||||
|
||||
private TextureSamplers() {
|
||||
}
|
||||
|
||||
public static void initRenderer() {
|
||||
guiSampler = new GlSampler(false, false, false, false, false, true);
|
||||
}
|
||||
|
||||
public static GlSampler getTerrainSampler(boolean linear, int anisotropy) {
|
||||
final GlSampler[] samplers = TERRAIN_SAMPLERS[linear ? 1 : 0];
|
||||
GlSampler sampler = samplers[anisotropy];
|
||||
if (sampler == null) {
|
||||
sampler = new GlSampler(linear, true, true, false, false, true);
|
||||
if (anisotropy > 1) {
|
||||
RenderSystem.samplerParameterf(sampler.getId(), EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
|
||||
}
|
||||
samplers[anisotropy] = sampler;
|
||||
}
|
||||
return sampler;
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -72,12 +72,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
private final float[] fluidQuadPos = new float[12];
|
||||
private ModelQuadFacing fluidQuadLightFace;
|
||||
|
||||
@Getter
|
||||
private final WorldClient world;
|
||||
|
||||
public AngelicaChunkBuildContext(RenderPassConfiguration<?> renderPassConfiguration, WorldClient world) {
|
||||
super(renderPassConfiguration);
|
||||
this.world = world;
|
||||
this.textureAtlas = (TextureMapExtension) Minecraft.getMinecraft().getTextureMapBlocks();
|
||||
this.worldSlice = new WorldSlice(world);
|
||||
// Shade by the quad normal so sloped quads blend the face shades instead of snapping to the dominant axis;
|
||||
@@ -101,7 +97,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
smoothLightPipeline.reset();
|
||||
flatLightPipeline.reset();
|
||||
lightPipelineReady = true;
|
||||
cachedSkylightSubtracted = this.world.skylightSubtracted;
|
||||
cachedSkylightSubtracted = Minecraft.getMinecraft().theWorld.skylightSubtracted;
|
||||
}
|
||||
|
||||
public WorldSlice swapWorldSlice(WorldSlice replacement) {
|
||||
|
||||
+1
-5
@@ -267,7 +267,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
}
|
||||
} else {
|
||||
// Normal block rendering
|
||||
canRender = !canRenderOffThread || block.canRenderInPass(pass);
|
||||
canRender = block.canRenderInPass(pass);
|
||||
}
|
||||
|
||||
if (canRender) {
|
||||
@@ -407,10 +407,6 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
final DeferredBlock deferred = blocks.get(i);
|
||||
blockPos.set(deferred.x(), deferred.y(), deferred.z());
|
||||
slice.setRenderingBlock(deferred.block());
|
||||
if (!deferred.isShaderPackOverride()) {
|
||||
setRenderPass(deferred.pass());
|
||||
if (!deferred.block().canRenderInPass(deferred.pass())) continue;
|
||||
}
|
||||
renderBlock(deferred.block(), deferred.meta(), deferred.x(), deferred.y(), deferred.z(), deferred.pass(),
|
||||
tessellator, renderBlocks, m.buffers, ctx, blockRenderContext, minX, minY, minZ,
|
||||
deferred.materialOverride(), deferred.isShaderPackOverride(), m.teMap, m.currentTick);
|
||||
|
||||
+97
-38
@@ -8,7 +8,6 @@ import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuTerrainCuller;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.TextureSamplers;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
|
||||
@@ -21,7 +20,6 @@ import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gl.program.ComputeProgram;
|
||||
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.samplers.IrisSamplers;
|
||||
import net.coderbot.iris.shadows.ShadowRenderingState;
|
||||
import org.embeddedt.embeddium.impl.gl.device.CommandList;
|
||||
import org.embeddedt.embeddium.impl.gl.device.DirectMultiDrawBatch;
|
||||
@@ -54,6 +52,9 @@ import org.embeddedt.embeddium.impl.render.chunk.terrain.TerrainRenderPass;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.CameraTransform;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -64,7 +65,15 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
private static final Tracy.ZoneId Z_CHUNK_BEGIN = Tracy.zoneId("chunkBegin", Tracy.COLOR_TERRAIN);
|
||||
private static final Tracy.ZoneId Z_CHUNK_ASSEMBLE_REGION = Tracy.zoneId("chunkAssembleRegion", Tracy.COLOR_TERRAIN);
|
||||
|
||||
private GlProgram<ChunkShaderInterface> irisProgram;
|
||||
private GlProgram<? extends ChunkShaderInterface> irisProgram;
|
||||
private boolean usingIrisProgram;
|
||||
private int terrainSampler;
|
||||
private boolean terrainSamplerResolved;
|
||||
private int terrainSamplerAnisotropy = -1;
|
||||
private boolean terrainSamplerNearest;
|
||||
private int packTerrainSampler;
|
||||
private boolean packTerrainSamplerResolved;
|
||||
private int packTerrainSamplerAnisotropy = -1;
|
||||
private boolean terrainSamplerBound;
|
||||
private final GpuTerrainCuller culler;
|
||||
private final SdlShadowVoxelizationSink shadowVoxelSink = new SdlShadowVoxelizationSink();
|
||||
@@ -151,19 +160,21 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
|
||||
// Check if Iris shaders are active and we have an override
|
||||
if (provider != null && provider.isShadersEnabled()) {
|
||||
final GlProgram<ChunkShaderInterface> override = provider.getShaderOverride(pass);
|
||||
final GlProgram<? extends ChunkShaderInterface> override = provider.getShaderOverride(pass);
|
||||
if (override != null) {
|
||||
pass.startDrawing();
|
||||
this.irisProgram = override;
|
||||
this.activeProgram = override;
|
||||
override.bind();
|
||||
override.getInterface().setupState(pass);
|
||||
this.activeProgram = (GlProgram<ChunkShaderInterface>) override;
|
||||
this.irisProgram = override;
|
||||
this.usingIrisProgram = true;
|
||||
bindPackTerrainSampler();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to default shader
|
||||
this.usingIrisProgram = false;
|
||||
this.irisProgram = null;
|
||||
beginDefault(pass);
|
||||
bindTerrainSampler();
|
||||
@@ -200,43 +211,85 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
|
||||
@Override
|
||||
protected void end(TerrainRenderPass pass) {
|
||||
if (irisProgram != null) {
|
||||
endIrisPass(pass);
|
||||
} else {
|
||||
super.end(pass);
|
||||
}
|
||||
}
|
||||
unbindTerrainSampler();
|
||||
|
||||
private void endIrisPass(TerrainRenderPass pass) {
|
||||
final GlProgram<ChunkShaderInterface> program = irisProgram;
|
||||
irisProgram = null;
|
||||
activeProgram = null;
|
||||
try {
|
||||
program.getInterface().restoreState();
|
||||
} finally {
|
||||
try {
|
||||
program.unbind();
|
||||
} finally {
|
||||
pass.endDrawing();
|
||||
}
|
||||
if (usingIrisProgram && irisProgram != null) {
|
||||
irisProgram.getInterface().restoreState();
|
||||
irisProgram.unbind();
|
||||
irisProgram = null;
|
||||
usingIrisProgram = false;
|
||||
this.activeProgram = null;
|
||||
pass.endDrawing();
|
||||
return;
|
||||
}
|
||||
|
||||
super.end(pass);
|
||||
}
|
||||
|
||||
private void bindTerrainSampler() {
|
||||
if (SodiumGameOptions.usesTerrainTexelSnap()) {
|
||||
bindBlockSampler(TextureSamplers.getTerrainSampler(!ClientProxy.options().quality.texelSampling.isNearest(),
|
||||
SodiumGameOptions.resolvedAnisotropicFiltering()).getId());
|
||||
if (!SodiumGameOptions.usesTerrainTexelSnap()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int anisotropy = SodiumGameOptions.resolvedAnisotropicFiltering();
|
||||
final boolean nearest = ClientProxy.options().quality.texelSampling.isNearest();
|
||||
|
||||
if (!terrainSamplerResolved || terrainSamplerAnisotropy != anisotropy
|
||||
|| terrainSamplerNearest != nearest) {
|
||||
if (!terrainSamplerResolved) {
|
||||
terrainSampler = RenderSystem.genSampler();
|
||||
if (terrainSampler == 0) {
|
||||
AngelicaMod.LOGGER.warn("Sampler objects unavailable; terrain filtering will fall back to the atlas texture's own parameters");
|
||||
}
|
||||
}
|
||||
terrainSamplerResolved = true;
|
||||
terrainSamplerAnisotropy = anisotropy;
|
||||
terrainSamplerNearest = nearest;
|
||||
|
||||
configureTerrainSampler(terrainSampler, nearest, anisotropy);
|
||||
}
|
||||
|
||||
if (terrainSampler != 0) {
|
||||
RenderSystem.bindSamplerToUnit(BLOCK_TEXTURE_UNIT, terrainSampler);
|
||||
terrainSamplerBound = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void bindPackTerrainSampler() {
|
||||
bindBlockSampler(IrisSamplers.getTerrainSampler(SodiumGameOptions.resolvedAnisotropicFiltering()).getId());
|
||||
final int anisotropy = SodiumGameOptions.resolvedAnisotropicFiltering();
|
||||
|
||||
if (!packTerrainSamplerResolved || packTerrainSamplerAnisotropy != anisotropy) {
|
||||
if (!packTerrainSamplerResolved) {
|
||||
packTerrainSampler = RenderSystem.genSampler();
|
||||
if (packTerrainSampler == 0) {
|
||||
AngelicaMod.LOGGER.warn("Sampler objects unavailable; shader pack terrain filtering will fall back to the atlas texture's own parameters");
|
||||
}
|
||||
}
|
||||
packTerrainSamplerResolved = true;
|
||||
packTerrainSamplerAnisotropy = anisotropy;
|
||||
|
||||
configureTerrainSampler(packTerrainSampler, true, anisotropy);
|
||||
}
|
||||
|
||||
if (packTerrainSampler != 0) {
|
||||
RenderSystem.bindSamplerToUnit(BLOCK_TEXTURE_UNIT, packTerrainSampler);
|
||||
terrainSamplerBound = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void bindBlockSampler(int id) {
|
||||
if (id != 0) {
|
||||
RenderSystem.bindSamplerToUnit(BLOCK_TEXTURE_UNIT, id);
|
||||
terrainSamplerBound = true;
|
||||
private static void configureTerrainSampler(int sampler, boolean nearest, int anisotropy) {
|
||||
if (sampler == 0) {
|
||||
return;
|
||||
}
|
||||
RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
|
||||
RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_MIN_FILTER,
|
||||
nearest ? GL11.GL_NEAREST_MIPMAP_LINEAR : GL11.GL_LINEAR_MIPMAP_LINEAR);
|
||||
RenderSystem.samplerParameteri(sampler, GL11.GL_TEXTURE_MAG_FILTER,
|
||||
nearest ? GL11.GL_NEAREST : GL11.GL_LINEAR);
|
||||
if (SodiumGameOptions.anisotropySupported()) {
|
||||
RenderSystem.samplerParameterf(sampler,
|
||||
EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, (float) anisotropy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,6 +311,15 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
shadowVoxelSink.delete();
|
||||
|
||||
unbindTerrainSampler();
|
||||
RenderSystem.destroySampler(terrainSampler);
|
||||
terrainSampler = 0;
|
||||
terrainSamplerResolved = false;
|
||||
terrainSamplerAnisotropy = -1;
|
||||
terrainSamplerNearest = false;
|
||||
RenderSystem.destroySampler(packTerrainSampler);
|
||||
packTerrainSampler = 0;
|
||||
packTerrainSamplerResolved = false;
|
||||
packTerrainSamplerAnisotropy = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -295,11 +357,8 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
voxelizeShadowTerrain(matrices, renderLists, renderPass, occlusionCamera, camera);
|
||||
super.render(matrices, commandList, renderLists, renderPass, occlusionCamera, camera);
|
||||
} finally {
|
||||
try {
|
||||
if (irisProgram != null) endIrisPass(renderPass);
|
||||
} finally {
|
||||
unbindTerrainSampler();
|
||||
if (culler != null) culler.endPass();
|
||||
if (culler != null) {
|
||||
culler.endPass();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -410,7 +469,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
|
||||
@Override
|
||||
protected void configureShaderInterface(ChunkShaderInterface shader) {
|
||||
if (irisProgram == null) {
|
||||
if (!usingIrisProgram) {
|
||||
shader.setTextureSlot(ChunkShaderTextureSlot.BLOCK, 0);
|
||||
shader.setTextureSlot(ChunkShaderTextureSlot.LIGHT, 1);
|
||||
}
|
||||
|
||||
+12
-18
@@ -8,7 +8,6 @@ import com.gtnewhorizons.angelica.utils.SpritePadding;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MipmapGeneration;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderPassConfiguration;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.sorting.QuadPrimitiveType;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.terrain.TerrainRenderPass;
|
||||
@@ -28,11 +27,9 @@ public class AngelicaRenderPassConfiguration {
|
||||
@Getter
|
||||
private static boolean rgssEnabled;
|
||||
|
||||
private static TerrainRenderPass.TerrainRenderPassBuilder builderForRenderType(int pass, boolean disableAlphaTest,
|
||||
AlphaCutoffParameter alphaCutoff, ChunkVertexType vertexType) {
|
||||
private static TerrainRenderPass.TerrainRenderPassBuilder builderForRenderType(int pass, boolean disableAlphaTest, ChunkVertexType vertexType) {
|
||||
final TerrainRenderPass.TerrainRenderPassBuilder builder = TerrainRenderPass.builder()
|
||||
.pipelineState(new AngelicaPipelineState(pass, disableAlphaTest, alphaCutoff))
|
||||
.fragmentDiscard(!disableAlphaTest && alphaCutoff != AlphaCutoffParameter.ZERO)
|
||||
.pipelineState(new AngelicaPipelineState(pass, disableAlphaTest))
|
||||
.vertexType(vertexType)
|
||||
.primitiveType(QuadPrimitiveType.TRIANGULATED);
|
||||
|
||||
@@ -64,25 +61,27 @@ public class AngelicaRenderPassConfiguration {
|
||||
|
||||
public static RenderPassConfiguration<BlockRenderLayer> build(ChunkVertexType vertexType) {
|
||||
rgssEnabled = SodiumGameOptions.effectiveTextureFilterMode().usesRgss();
|
||||
final AlphaCutoffParameter translucentCutoff = MipmapGeneration.alphaFloorCutoff();
|
||||
|
||||
SOLID_PASS = builderForRenderType(0, true, AlphaCutoffParameter.HALF, vertexType)
|
||||
SOLID_PASS = builderForRenderType(0, true, vertexType)
|
||||
.name("solid")
|
||||
.fragmentDiscard(false)
|
||||
.useReverseOrder(false)
|
||||
.build();
|
||||
|
||||
CUTOUT_MIPPED_PASS = builderForRenderType(0, false, AlphaCutoffParameter.HALF, vertexType)
|
||||
CUTOUT_MIPPED_PASS = builderForRenderType(0, false, vertexType)
|
||||
.name("cutout_mipped")
|
||||
.fragmentDiscard(true)
|
||||
.useReverseOrder(false)
|
||||
.build();
|
||||
|
||||
TRANSLUCENT_PASS = builderForRenderType(1, false, translucentCutoff, vertexType)
|
||||
TRANSLUCENT_PASS = builderForRenderType(1, false, vertexType)
|
||||
.name("translucent")
|
||||
.fragmentDiscard(false)
|
||||
.useReverseOrder(true)
|
||||
.useTranslucencySorting(ClientProxy.options().performance.translucencySorting)
|
||||
.build();
|
||||
|
||||
TRANSLUCENT_MATERIAL = new Material(TRANSLUCENT_PASS, translucentCutoff, true);
|
||||
TRANSLUCENT_MATERIAL = new Material(TRANSLUCENT_PASS, AlphaCutoffParameter.ZERO, true);
|
||||
SOLID_MATERIAL = new Material(SOLID_PASS, AlphaCutoffParameter.ZERO, true);
|
||||
CUTOUT_MIPPED_MATERIAL = new Material(CUTOUT_MIPPED_PASS, AlphaCutoffParameter.HALF, true);
|
||||
CUTOUT_MATERIAL = new Material(CUTOUT_MIPPED_PASS, AlphaCutoffParameter.ONE_TENTH, false);
|
||||
@@ -106,29 +105,24 @@ public class AngelicaRenderPassConfiguration {
|
||||
private static final class AngelicaPipelineState implements TerrainRenderPass.PipelineState {
|
||||
private final int pass;
|
||||
private final boolean disableAlphaTest;
|
||||
private final float alphaCutoff;
|
||||
private int alphaStateDepth = -1;
|
||||
|
||||
private AngelicaPipelineState(int pass, boolean disableAlphaTest, AlphaCutoffParameter alphaCutoff) {
|
||||
private AngelicaPipelineState(int pass, boolean disableAlphaTest) {
|
||||
this.pass = pass;
|
||||
this.disableAlphaTest = disableAlphaTest;
|
||||
this.alphaCutoff = alphaCutoff.cutoff();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
GLStateManager.glDepthMask(true);
|
||||
|
||||
if (alphaCutoff > 0.0f) {
|
||||
if (pass == 0) {
|
||||
if (alphaStateDepth >= 0) {
|
||||
GLStateManager.popStateTo(alphaStateDepth);
|
||||
alphaStateDepth = -1;
|
||||
}
|
||||
alphaStateDepth = GLStateManager.pushState(StateSet.ALPHA);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, alphaCutoff);
|
||||
if (pass == 1) {
|
||||
GLStateManager.enableAlphaTest();
|
||||
}
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, AlphaCutoffParameter.HALF.cutoff());
|
||||
}
|
||||
if (disableAlphaTest) {
|
||||
GLStateManager.disableAlphaTest();
|
||||
|
||||
@@ -11,11 +11,7 @@ public interface SpriteExtension {
|
||||
|
||||
/** One-shot: returns true if marked, then resets. */
|
||||
boolean celeritas$shouldUpdate();
|
||||
void celeritas$setMipmapStrategy(MipmapStrategy strategy, boolean explicit);
|
||||
|
||||
void angelica$setTextureType(int textureType);
|
||||
|
||||
void angelica$setAlphaCutoffBias(float bias);
|
||||
void celeritas$setMipmapStrategy(MipmapStrategy strategy, boolean explicit, int textureType);
|
||||
|
||||
/** Width of the replicated-edge border reserved around this sprite in its stitcher slot. */
|
||||
int angelica$getGutterWidth();
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ public interface IrisShaderProvider {
|
||||
|
||||
boolean shouldUseFaceCulling();
|
||||
|
||||
@Nullable GlProgram<ChunkShaderInterface> getShaderOverride(TerrainRenderPass pass);
|
||||
@Nullable GlProgram<? extends ChunkShaderInterface> getShaderOverride(TerrainRenderPass pass);
|
||||
|
||||
ChunkVertexType getVertexType(ChunkVertexType defaultType);
|
||||
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class ThreadedAngelicaChunkBuilderMeshingTask extends AngelicaChunkBuilde
|
||||
buildContext.setupLightPipeline(minX, minY, minZ);
|
||||
} else {
|
||||
// Fallback: direct world access (main thread only)
|
||||
final var world = buildContext.getWorld();
|
||||
final var world = Minecraft.getMinecraft().theWorld;
|
||||
this.blockAccess = new ChunkCache(world, minX - 1, minY - 1, minZ - 1, minX + 17, minY + 17, minZ + 17, 1);
|
||||
this.biomeColorCache = ((WorldClientExtension) world).celeritas$getSmoothBiomeColorCache();
|
||||
this.biomeColorCache.update(new SectionPos(this.render.getChunkX(), this.render.getChunkY(), this.render.getChunkZ()));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering.particles;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MipmapGeneration;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.parameters.AlphaCutoffParameter;
|
||||
|
||||
public final class ParticleAlphaCutoff {
|
||||
|
||||
private static final int TERRAIN_PARTICLE_LAYER = 1;
|
||||
|
||||
private ParticleAlphaCutoff() {
|
||||
}
|
||||
|
||||
public static float forLayer(float original, int layer) {
|
||||
if (layer != TERRAIN_PARTICLE_LAYER) {
|
||||
return original;
|
||||
}
|
||||
final AlphaCutoffParameter cutoff = MipmapGeneration.alphaFloorCutoff();
|
||||
return cutoff == AlphaCutoffParameter.ZERO ? original : cutoff.cutoff();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* Remembers the lightmap the tile entity dispatcher looks up per tile per frame, for one client tick.
|
||||
* Light only changes inside the tick, so the cache is dropped by age instead of by light updates.
|
||||
*/
|
||||
public final class TesrLightCache {
|
||||
|
||||
private static final int MISSING = Integer.MIN_VALUE;
|
||||
|
||||
private static final Long2IntOpenHashMap CACHE = new Long2IntOpenHashMap();
|
||||
|
||||
static {
|
||||
CACHE.defaultReturnValue(MISSING);
|
||||
}
|
||||
|
||||
private static int tick;
|
||||
private static int cachedTick = Integer.MIN_VALUE;
|
||||
|
||||
private TesrLightCache() {}
|
||||
|
||||
public static void onClientTick() {
|
||||
tick++;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
CACHE.clear();
|
||||
cachedTick = Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public static int getLightBrightnessForSkyBlocks(World world, int x, int y, int z, int minBlockLight) {
|
||||
if (minBlockLight != 0 || !GLStateManager.isMainThread()) {
|
||||
return world.getLightBrightnessForSkyBlocks(x, y, z, minBlockLight);
|
||||
}
|
||||
|
||||
if (tick != cachedTick) {
|
||||
CACHE.clear();
|
||||
cachedTick = tick;
|
||||
}
|
||||
|
||||
final long key = key(x, y, z);
|
||||
int packed = CACHE.get(key);
|
||||
if (packed == MISSING) {
|
||||
packed = world.getLightBrightnessForSkyBlocks(x, y, z, 0);
|
||||
CACHE.put(key, packed);
|
||||
}
|
||||
return packed;
|
||||
}
|
||||
|
||||
// 26 bits for x and z cover the world border, 8 bits for y; no two positions share a key
|
||||
private static long key(int x, int y, int z) {
|
||||
return ((long) (x & 0x3FFFFFF) << 34) | ((long) (y & 0xFF) << 26) | (long) (z & 0x3FFFFFF);
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import org.embeddedt.embeddium.api.util.ColorARGB;
|
||||
import org.embeddedt.embeddium.impl.texture.MipmapHelper;
|
||||
|
||||
import static com.gtnewhorizons.angelica.utils.MipmapGenerator.ALPHA_BIAS;
|
||||
import static org.joml.Math.clamp;
|
||||
|
||||
final class CeleritasMipmapGenerator {
|
||||
|
||||
private CeleritasMipmapGenerator() {
|
||||
}
|
||||
|
||||
static int[][] generateMipLevels(int mipLevel, int width, int[][] currentMips,
|
||||
boolean isCutout, boolean darkCutout, float cutoutRef) {
|
||||
final int[][] result = new int[mipLevel + 1][];
|
||||
final int[] base = currentMips[0];
|
||||
|
||||
if (mipLevel > 0 && (currentMips.length == 1 || currentMips[1] == null)) {
|
||||
if (darkCutout) {
|
||||
MipmapGenerator.fillEmptyAreasWithDarkColor(base, 1);
|
||||
} else {
|
||||
solidify(base, width, base.length / width);
|
||||
}
|
||||
}
|
||||
|
||||
result[0] = base;
|
||||
if (mipLevel <= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
final float originalCoverage = isCutout ? alphaTestCoverage(base, width, cutoutRef, 1.0f) : 0.0f;
|
||||
|
||||
for (int level = 1; level <= mipLevel; level++) {
|
||||
final int levelWidth = width >> level;
|
||||
final int[] provided = level < currentMips.length ? currentMips[level] : null;
|
||||
|
||||
if (provided != null) {
|
||||
result[level] = provided;
|
||||
} else {
|
||||
final int[] input = result[level - 1];
|
||||
final int prevWidth = width >> (level - 1);
|
||||
final int prevHeight = input.length / prevWidth;
|
||||
final int levelHeight = prevHeight >> 1;
|
||||
final int[] output = new int[levelWidth * levelHeight];
|
||||
|
||||
for (int y = 0; y < levelHeight; y++) {
|
||||
for (int x = 0; x < levelWidth; x++) {
|
||||
final int i = x * 2 + y * 2 * prevWidth;
|
||||
output[x + y * levelWidth] = MipmapHelper.weightedAverageColor(
|
||||
MipmapHelper.weightedAverageColor(input[i], input[i + 1]),
|
||||
MipmapHelper.weightedAverageColor(input[i + prevWidth], input[i + prevWidth + 1]));
|
||||
}
|
||||
}
|
||||
result[level] = output;
|
||||
}
|
||||
|
||||
if (isCutout) {
|
||||
scaleAlphaToCoverage(result[level], levelWidth, originalCoverage, cutoutRef);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void solidify(int[] image, int width, int height) {
|
||||
final int size = Math.min(width * height, image.length);
|
||||
if (size <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasTransparent = false;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (ColorARGB.unpackAlpha(image[i]) == 0) {
|
||||
hasTransparent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasTransparent) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int[] color = new int[size];
|
||||
final IntArrayFIFOQueue queue = new IntArrayFIFOQueue(64);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int pixel = image[i];
|
||||
if (ColorARGB.unpackAlpha(pixel) == 0) {
|
||||
continue;
|
||||
}
|
||||
color[i] = pixel;
|
||||
if (hasTransparentNeighbor(image, i, width, size)) {
|
||||
queue.enqueue(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (queue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
final int index = queue.dequeueInt();
|
||||
final int x = index % width;
|
||||
final int source = color[index];
|
||||
|
||||
if (x > 0) {
|
||||
fill(index - 1, source, color, queue);
|
||||
}
|
||||
if (x < width - 1) {
|
||||
fill(index + 1, source, color, queue);
|
||||
}
|
||||
if (index >= width) {
|
||||
fill(index - width, source, color, queue);
|
||||
}
|
||||
if (index + width < size) {
|
||||
fill(index + width, source, color, queue);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (ColorARGB.unpackAlpha(image[i]) == 0) {
|
||||
image[i] = color[i] & 0x00FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasTransparentNeighbor(int[] image, int index, int width, int size) {
|
||||
final int x = index % width;
|
||||
return (x > 0 && ColorARGB.unpackAlpha(image[index - 1]) == 0)
|
||||
|| (x < width - 1 && ColorARGB.unpackAlpha(image[index + 1]) == 0)
|
||||
|| (index >= width && ColorARGB.unpackAlpha(image[index - width]) == 0)
|
||||
|| (index + width < size && ColorARGB.unpackAlpha(image[index + width]) == 0);
|
||||
}
|
||||
|
||||
private static void fill(int neighbor, int source, int[] color, IntArrayFIFOQueue queue) {
|
||||
if (color[neighbor] == 0) {
|
||||
color[neighbor] = source;
|
||||
queue.enqueue(neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
private static float alphaTestCoverage(int[] image, int width, float alphaRef, float alphaScale) {
|
||||
final int height = image.length / width;
|
||||
final float alphaFactor = alphaScale / 255.0f;
|
||||
float total = 0.0f;
|
||||
int quads = 0;
|
||||
|
||||
for (int y = 0; y < height - 1; y++) {
|
||||
for (int x = 0; x < width - 1; x++) {
|
||||
final int i = x + y * width;
|
||||
final float a00 = scaledAlpha(image[i], alphaFactor);
|
||||
final float a10 = scaledAlpha(image[i + 1], alphaFactor);
|
||||
final float a01 = scaledAlpha(image[i + width], alphaFactor);
|
||||
final float a11 = scaledAlpha(image[i + width + 1], alphaFactor);
|
||||
|
||||
final float lo = Math.min(Math.min(a00, a10), Math.min(a01, a11));
|
||||
if (lo > alphaRef) {
|
||||
total += 1.0f;
|
||||
quads++;
|
||||
continue;
|
||||
}
|
||||
final float hi = Math.max(Math.max(a00, a10), Math.max(a01, a11));
|
||||
if (hi <= alphaRef) {
|
||||
quads++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int hits = 0;
|
||||
for (int sy = 0; sy < 4; sy++) {
|
||||
final float fy = (sy + 0.5f) / 4.0f;
|
||||
for (int sx = 0; sx < 4; sx++) {
|
||||
final float fx = (sx + 0.5f) / 4.0f;
|
||||
final float top = a00 + (a10 - a00) * fx;
|
||||
final float bottom = a01 + (a11 - a01) * fx;
|
||||
if (top + (bottom - top) * fy > alphaRef) {
|
||||
hits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
total += hits / 16.0f;
|
||||
quads++;
|
||||
}
|
||||
}
|
||||
|
||||
if (quads == 0) {
|
||||
return scaledAlpha(image[0], alphaFactor) > alphaRef ? 1.0f : 0.0f;
|
||||
}
|
||||
return total / quads;
|
||||
}
|
||||
|
||||
private static float scaledAlpha(int color, float alphaFactor) {
|
||||
return clamp(0.0f, 1.0f, ColorARGB.unpackAlpha(color) * alphaFactor);
|
||||
}
|
||||
|
||||
private static void scaleAlphaToCoverage(int[] image, int width, float desiredCoverage, float alphaRef) {
|
||||
float min = 0.0f;
|
||||
float max = 4.0f;
|
||||
float scale = 1.0f;
|
||||
float bestScale = 1.0f;
|
||||
float bestError = Float.POSITIVE_INFINITY;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final float coverage = alphaTestCoverage(image, width, alphaRef, scale);
|
||||
final float error = Math.abs(coverage - desiredCoverage);
|
||||
if (error < bestError) {
|
||||
bestError = error;
|
||||
bestScale = scale;
|
||||
}
|
||||
|
||||
if (coverage < desiredCoverage) {
|
||||
min = scale;
|
||||
} else if (!(coverage > desiredCoverage)) {
|
||||
break;
|
||||
} else {
|
||||
max = scale;
|
||||
}
|
||||
scale = (min + max) / 2.0f;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.length; i++) {
|
||||
final int color = image[i];
|
||||
final int rawAlpha = ColorARGB.unpackAlpha(color);
|
||||
final float bias = rawAlpha > 0 ? ALPHA_BIAS : 0.0f;
|
||||
final int alpha = (int) Math.floor(clamp(0.0f, 1.0f, rawAlpha / 255.0f * bestScale + bias) * 255.0f);
|
||||
image[i] = ColorARGB.withAlpha(color, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MipmapGeneration;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import org.embeddedt.embeddium.api.util.ColorARGB;
|
||||
import org.embeddedt.embeddium.impl.texture.MipmapHelper;
|
||||
|
||||
import static org.joml.Math.clamp;
|
||||
|
||||
/**
|
||||
* Mipmap downsampling for atlas sprites, replacing vanilla's {@code TextureUtil.generateMipmapData}.
|
||||
*/
|
||||
public final class MipmapGenerator {
|
||||
|
||||
static final float ALPHA_BIAS = 0.025f;
|
||||
private static final float CUTOUT_ALPHA_REF = 0.5f;
|
||||
private static final float STRICT_CUTOUT_ALPHA_REF = 0.3f;
|
||||
private static final float ALPHA_BIAS = 0.025f;
|
||||
|
||||
private MipmapGenerator() {
|
||||
}
|
||||
@@ -22,29 +25,101 @@ public final class MipmapGenerator {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
private static boolean isCutoutStrategy(MipmapStrategy resolved) {
|
||||
static boolean isCutoutStrategy(MipmapStrategy strategy, boolean hasTransparentPixel) {
|
||||
final MipmapStrategy resolved = resolve(strategy, hasTransparentPixel);
|
||||
return resolved == MipmapStrategy.CUTOUT
|
||||
|| resolved == MipmapStrategy.STRICT_CUTOUT
|
||||
|| resolved == MipmapStrategy.DARK_CUTOUT;
|
||||
}
|
||||
|
||||
// Scan order controls which equally dark color wins.
|
||||
static void fillEmptyAreasWithDarkColor(int[] image, int columns) {
|
||||
final int rows = image.length / columns;
|
||||
int darkest = ColorARGB.pack(255, 255, 255, 255);
|
||||
int darkestSum = Integer.MAX_VALUE;
|
||||
static void solidify(int[] image, int width, int height) {
|
||||
final int size = Math.min(width * height, image.length);
|
||||
if (size <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int x = 0; x < columns; x++) {
|
||||
for (int y = 0; y < rows; y++) {
|
||||
final int pixel = image[x + y * columns];
|
||||
if (ColorARGB.unpackAlpha(pixel) == 0) {
|
||||
continue;
|
||||
}
|
||||
final int sum = ColorARGB.unpackRed(pixel) + ColorARGB.unpackGreen(pixel) + ColorARGB.unpackBlue(pixel);
|
||||
if (sum < darkestSum) {
|
||||
darkestSum = sum;
|
||||
darkest = pixel;
|
||||
}
|
||||
boolean hasTransparent = false;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (ColorARGB.unpackAlpha(image[i]) == 0) {
|
||||
hasTransparent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasTransparent) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int[] color = new int[size];
|
||||
final IntArrayFIFOQueue queue = new IntArrayFIFOQueue(64);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int pixel = image[i];
|
||||
if (ColorARGB.unpackAlpha(pixel) == 0) {
|
||||
continue;
|
||||
}
|
||||
color[i] = pixel;
|
||||
if (hasTransparentNeighbour(image, i, width, size)) {
|
||||
queue.enqueue(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (queue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
final int index = queue.dequeueInt();
|
||||
final int x = index % width;
|
||||
final int source = color[index];
|
||||
|
||||
if (x > 0) {
|
||||
fill(index - 1, source, color, queue);
|
||||
}
|
||||
if (x < width - 1) {
|
||||
fill(index + 1, source, color, queue);
|
||||
}
|
||||
if (index >= width) {
|
||||
fill(index - width, source, color, queue);
|
||||
}
|
||||
if (index + width < size) {
|
||||
fill(index + width, source, color, queue);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (ColorARGB.unpackAlpha(image[i]) == 0) {
|
||||
image[i] = color[i] & 0x00FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasTransparentNeighbour(int[] image, int index, int width, int size) {
|
||||
final int x = index % width;
|
||||
return (x > 0 && ColorARGB.unpackAlpha(image[index - 1]) == 0)
|
||||
|| (x < width - 1 && ColorARGB.unpackAlpha(image[index + 1]) == 0)
|
||||
|| (index >= width && ColorARGB.unpackAlpha(image[index - width]) == 0)
|
||||
|| (index + width < size && ColorARGB.unpackAlpha(image[index + width]) == 0);
|
||||
}
|
||||
|
||||
private static void fill(int neighbour, int source, int[] color, IntArrayFIFOQueue queue) {
|
||||
if (color[neighbour] == 0) {
|
||||
color[neighbour] = source;
|
||||
queue.enqueue(neighbour);
|
||||
}
|
||||
}
|
||||
|
||||
static void fillEmptyAreasWithDarkColor(int[] image) {
|
||||
int darkest = ColorARGB.pack(255, 255, 255, 255);
|
||||
int darkestSum = 255 * 3;
|
||||
|
||||
for (final int pixel : image) {
|
||||
if (ColorARGB.unpackAlpha(pixel) == 0) {
|
||||
continue;
|
||||
}
|
||||
final int sum = ColorARGB.unpackRed(pixel) + ColorARGB.unpackGreen(pixel) + ColorARGB.unpackBlue(pixel);
|
||||
if (sum < darkestSum) {
|
||||
darkestSum = sum;
|
||||
darkest = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,22 +133,152 @@ public final class MipmapGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates the fraction of the image that survives, with bilinear filtering modeled by
|
||||
* supersampling each texel quad on a 4x4 interior grid.
|
||||
*/
|
||||
static float alphaTestCoverage(int[] image, int width, float alphaRef, float alphaScale) {
|
||||
final int height = image.length / width;
|
||||
final float alphaFactor = alphaScale / 255.0f;
|
||||
float total = 0.0f;
|
||||
int quads = 0;
|
||||
|
||||
for (int y = 0; y < height - 1; y++) {
|
||||
for (int x = 0; x < width - 1; x++) {
|
||||
final int i = x + y * width;
|
||||
final float a00 = scaledAlpha(image[i], alphaFactor);
|
||||
final float a10 = scaledAlpha(image[i + 1], alphaFactor);
|
||||
final float a01 = scaledAlpha(image[i + width], alphaFactor);
|
||||
final float a11 = scaledAlpha(image[i + width + 1], alphaFactor);
|
||||
|
||||
final float lo = Math.min(Math.min(a00, a10), Math.min(a01, a11));
|
||||
if (lo > alphaRef) {
|
||||
total += 1.0f;
|
||||
quads++;
|
||||
continue;
|
||||
}
|
||||
final float hi = Math.max(Math.max(a00, a10), Math.max(a01, a11));
|
||||
if (hi <= alphaRef) {
|
||||
quads++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int hits = 0;
|
||||
for (int sy = 0; sy < 4; sy++) {
|
||||
final float fy = (sy + 0.5f) / 4.0f;
|
||||
for (int sx = 0; sx < 4; sx++) {
|
||||
final float fx = (sx + 0.5f) / 4.0f;
|
||||
final float top = a00 + (a10 - a00) * fx;
|
||||
final float bottom = a01 + (a11 - a01) * fx;
|
||||
if (top + (bottom - top) * fy > alphaRef) {
|
||||
hits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
total += hits / 16.0f;
|
||||
quads++;
|
||||
}
|
||||
}
|
||||
|
||||
if (quads == 0) {
|
||||
return scaledAlpha(image[0], alphaFactor) > alphaRef ? 1.0f : 0.0f;
|
||||
}
|
||||
return total / quads;
|
||||
}
|
||||
|
||||
private static float scaledAlpha(int color, float alphaFactor) {
|
||||
return clamp(0.0f, 1.0f, ColorARGB.unpackAlpha(color) * alphaFactor);
|
||||
}
|
||||
|
||||
static void scaleAlphaToCoverage(int[] image, int width, float desiredCoverage, float alphaRef) {
|
||||
float min = 0.0f;
|
||||
float max = 4.0f;
|
||||
float scale = 1.0f;
|
||||
float bestScale = 1.0f;
|
||||
float bestError = Float.POSITIVE_INFINITY;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final float coverage = alphaTestCoverage(image, width, alphaRef, scale);
|
||||
final float error = Math.abs(coverage - desiredCoverage);
|
||||
if (error < bestError) {
|
||||
bestError = error;
|
||||
bestScale = scale;
|
||||
}
|
||||
|
||||
if (coverage < desiredCoverage) {
|
||||
min = scale;
|
||||
} else if (!(coverage > desiredCoverage)) {
|
||||
break;
|
||||
} else {
|
||||
max = scale;
|
||||
}
|
||||
scale = (min + max) / 2.0f;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.length; i++) {
|
||||
final int color = image[i];
|
||||
final int rawAlpha = ColorARGB.unpackAlpha(color);
|
||||
final float bias = rawAlpha > 0 ? ALPHA_BIAS : 0.0f;
|
||||
final int alpha = (int) Math.floor(clamp(0.0f, 1.0f, rawAlpha / 255.0f * bestScale + bias) * 255.0f);
|
||||
image[i] = ColorARGB.withAlpha(color, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public static int[][] generateMipLevels(int mipLevel, int width, int[][] currentMips,
|
||||
MipmapStrategy strategy, boolean hasTransparentPixel, MipmapGeneration generation,
|
||||
float alphaCutoffBias, boolean isItem) {
|
||||
MipmapStrategy strategy, boolean hasTransparentPixel) {
|
||||
|
||||
if (generation == MipmapGeneration.VANILLA_1_7_10) {
|
||||
return VanillaMipmapGenerator.generateMipLevels(mipLevel, width, currentMips);
|
||||
}
|
||||
final MipmapStrategy resolved = resolve(strategy, hasTransparentPixel);
|
||||
final boolean isCutout = isCutoutStrategy(resolved);
|
||||
final boolean isCutout = isCutoutStrategy(strategy, hasTransparentPixel);
|
||||
final boolean darkCutout = resolved == MipmapStrategy.DARK_CUTOUT;
|
||||
final float cutoutRef = resolved == MipmapStrategy.STRICT_CUTOUT ? STRICT_CUTOUT_ALPHA_REF : CUTOUT_ALPHA_REF;
|
||||
if (generation == MipmapGeneration.MODERN_VANILLA) {
|
||||
return ModernMipmapGenerator.generateMipLevels(mipLevel, width, currentMips,
|
||||
isCutout, darkCutout, cutoutRef, alphaCutoffBias, isItem);
|
||||
|
||||
final int[][] result = new int[mipLevel + 1][];
|
||||
final int[] base = currentMips[0];
|
||||
|
||||
if (mipLevel > 0 && (currentMips.length == 1 || currentMips[1] == null)) {
|
||||
if (darkCutout) {
|
||||
fillEmptyAreasWithDarkColor(base);
|
||||
} else {
|
||||
solidify(base, width, base.length / width);
|
||||
}
|
||||
}
|
||||
|
||||
return CeleritasMipmapGenerator.generateMipLevels(mipLevel, width, currentMips, isCutout, darkCutout, cutoutRef);
|
||||
result[0] = base;
|
||||
if (mipLevel <= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
final float cutoutRef = resolved == MipmapStrategy.STRICT_CUTOUT ? STRICT_CUTOUT_ALPHA_REF : CUTOUT_ALPHA_REF;
|
||||
final float originalCoverage = isCutout ? alphaTestCoverage(base, width, cutoutRef, 1.0f) : 0.0f;
|
||||
|
||||
for (int level = 1; level <= mipLevel; level++) {
|
||||
final int levelWidth = width >> level;
|
||||
final int[] provided = level < currentMips.length ? currentMips[level] : null;
|
||||
|
||||
if (provided != null) {
|
||||
result[level] = provided;
|
||||
} else {
|
||||
final int[] input = result[level - 1];
|
||||
final int prevWidth = width >> (level - 1);
|
||||
final int prevHeight = input.length / prevWidth;
|
||||
final int levelHeight = prevHeight >> 1;
|
||||
final int[] output = new int[levelWidth * levelHeight];
|
||||
|
||||
for (int y = 0; y < levelHeight; y++) {
|
||||
for (int x = 0; x < levelWidth; x++) {
|
||||
final int i = x * 2 + y * 2 * prevWidth;
|
||||
output[x + y * levelWidth] = MipmapHelper.weightedAverageColor(
|
||||
MipmapHelper.weightedAverageColor(input[i], input[i + 1]),
|
||||
MipmapHelper.weightedAverageColor(input[i + prevWidth], input[i + prevWidth + 1]));
|
||||
}
|
||||
}
|
||||
result[level] = output;
|
||||
}
|
||||
|
||||
if (isCutout) {
|
||||
scaleAlphaToCoverage(result[level], levelWidth, originalCoverage, cutoutRef);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.TextureAtlasType;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockFlower;
|
||||
@@ -56,7 +55,7 @@ public final class MipmapStrategies {
|
||||
}
|
||||
|
||||
MipmapStrategy strategy = STRATEGY_STACK.get().peek();
|
||||
if (strategy == null && textureType == TextureAtlasType.BLOCK && iconName.contains("leaves")) {
|
||||
if (strategy == null && textureType == 0 && iconName.contains("leaves")) {
|
||||
strategy = MipmapStrategy.DARK_CUTOUT;
|
||||
}
|
||||
if (strategy == null) {
|
||||
@@ -68,7 +67,7 @@ public final class MipmapStrategies {
|
||||
}
|
||||
|
||||
public static MipmapStrategy inheritedFor(int textureType, String iconName) {
|
||||
if (iconName == null || textureType != TextureAtlasType.BLOCK) {
|
||||
if (iconName == null || textureType != 0) {
|
||||
return null;
|
||||
}
|
||||
return SPRITE_RULES.get(iconName);
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import org.embeddedt.embeddium.api.util.ColorARGB;
|
||||
|
||||
/**
|
||||
* Minecraft 26.2's ARGB.meanLinear and MipmapGenerator.darkenedAlphaBlend, using its 10-bit linear color tables.
|
||||
*/
|
||||
final class ModernMipmapBlender {
|
||||
private static final int[] SRGB_TO_LINEAR = new int[256];
|
||||
private static final float[] SRGB_TO_LINEAR_FLOAT = new float[256];
|
||||
private static final int[] LINEAR_TO_SRGB = new int[1024];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < SRGB_TO_LINEAR.length; i++) {
|
||||
final float channel = i / 255.0f;
|
||||
final float linear = channel >= 0.04045f ? (float) Math.pow((channel + 0.055) / 1.055, 2.4) : channel / 12.92f;
|
||||
SRGB_TO_LINEAR[i] = Math.round(linear * 1023.0f);
|
||||
SRGB_TO_LINEAR_FLOAT[i] = SRGB_TO_LINEAR[i] / 1023.0f;
|
||||
}
|
||||
for (int i = 0; i < LINEAR_TO_SRGB.length; i++) {
|
||||
final float channel = i / 1023.0f;
|
||||
final float srgb = channel >= 0.0031308f ? (float) (1.055 * Math.pow(channel, 1.0 / 2.4) - 0.055) : 12.92f * channel;
|
||||
LINEAR_TO_SRGB[i] = Math.round(srgb * 255.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private ModernMipmapBlender() {
|
||||
}
|
||||
|
||||
static int blend(int a, int b, int c, int d, boolean darkCutout) {
|
||||
if (darkCutout) {
|
||||
return darkenedAlphaBlend(a, b, c, d);
|
||||
}
|
||||
return ColorARGB.pack(meanChannel(a, b, c, d, 16), meanChannel(a, b, c, d, 8), meanChannel(a, b, c, d, 0), (ColorARGB.unpackAlpha(a) + ColorARGB.unpackAlpha(b) + ColorARGB.unpackAlpha(c) + ColorARGB.unpackAlpha(d)) / 4);
|
||||
}
|
||||
|
||||
private static int meanChannel(int a, int b, int c, int d, int shift) {
|
||||
final int linear = (SRGB_TO_LINEAR[(a >>> shift) & 255] + SRGB_TO_LINEAR[(b >>> shift) & 255] + SRGB_TO_LINEAR[(c >>> shift) & 255] + SRGB_TO_LINEAR[(d >>> shift) & 255]) / 4;
|
||||
return LINEAR_TO_SRGB[linear];
|
||||
}
|
||||
|
||||
private static int darkenedAlphaBlend(int a, int b, int c, int d) {
|
||||
float alpha = 0.0f;
|
||||
float red = 0.0f;
|
||||
float green = 0.0f;
|
||||
float blue = 0.0f;
|
||||
if (ColorARGB.unpackAlpha(a) != 0) {
|
||||
alpha += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackAlpha(a)];
|
||||
red += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackRed(a)];
|
||||
green += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackGreen(a)];
|
||||
blue += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackBlue(a)];
|
||||
}
|
||||
if (ColorARGB.unpackAlpha(b) != 0) {
|
||||
alpha += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackAlpha(b)];
|
||||
red += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackRed(b)];
|
||||
green += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackGreen(b)];
|
||||
blue += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackBlue(b)];
|
||||
}
|
||||
if (ColorARGB.unpackAlpha(c) != 0) {
|
||||
alpha += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackAlpha(c)];
|
||||
red += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackRed(c)];
|
||||
green += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackGreen(c)];
|
||||
blue += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackBlue(c)];
|
||||
}
|
||||
if (ColorARGB.unpackAlpha(d) != 0) {
|
||||
alpha += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackAlpha(d)];
|
||||
red += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackRed(d)];
|
||||
green += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackGreen(d)];
|
||||
blue += SRGB_TO_LINEAR_FLOAT[ColorARGB.unpackBlue(d)];
|
||||
}
|
||||
return ColorARGB.pack(toSrgb(red), toSrgb(green), toSrgb(blue), toSrgb(alpha));
|
||||
}
|
||||
|
||||
private static int toSrgb(float sum) {
|
||||
return LINEAR_TO_SRGB[(int) (sum * 0.25f * 1023.0f)];
|
||||
}
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrays;
|
||||
import org.embeddedt.embeddium.api.util.ColorARGB;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.gtnewhorizons.angelica.utils.MipmapGenerator.ALPHA_BIAS;
|
||||
import static org.joml.Math.clamp;
|
||||
|
||||
/**
|
||||
* Minecraft 26.2's mipmap generation and texture preparation.
|
||||
*/
|
||||
final class ModernMipmapGenerator {
|
||||
|
||||
private static final float[] NORMALIZED_ALPHA = new float[256];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < NORMALIZED_ALPHA.length; i++) {
|
||||
NORMALIZED_ALPHA[i] = i / 255.0f;
|
||||
}
|
||||
}
|
||||
|
||||
private ModernMipmapGenerator() {
|
||||
}
|
||||
|
||||
static int[][] generateMipLevels(int mipLevel, int width, int[][] currentMips, boolean isCutout, boolean darkCutout, float cutoutRef, float alphaCutoffBias, boolean isItem) {
|
||||
final int[] base = currentMips[0];
|
||||
|
||||
// Unlike modern NativeImage arrays, 1.7.10 preallocates null slots for missing mip levels.
|
||||
boolean hasProvidedMips = false;
|
||||
boolean complete = mipLevel < currentMips.length;
|
||||
int maxPixels = base.length;
|
||||
for (int level = 1; level < currentMips.length; level++) {
|
||||
if (currentMips[level] != null) {
|
||||
hasProvidedMips = true;
|
||||
if (level <= mipLevel) {
|
||||
maxPixels = Math.max(maxPixels, currentMips[level].length);
|
||||
}
|
||||
} else if (level <= mipLevel) {
|
||||
complete = false;
|
||||
}
|
||||
}
|
||||
if (!hasProvidedMips && !isItem && isCutout) {
|
||||
if (darkCutout) {
|
||||
MipmapGenerator.fillEmptyAreasWithDarkColor(base, width);
|
||||
} else {
|
||||
solidify(base, width);
|
||||
}
|
||||
}
|
||||
|
||||
if (complete) {
|
||||
return currentMips.length == mipLevel + 1 ? currentMips : Arrays.copyOf(currentMips, mipLevel + 1);
|
||||
}
|
||||
|
||||
final int[][] result = Arrays.copyOf(currentMips, mipLevel + 1);
|
||||
final float[] alphaScratch = isCutout ? new float[maxPixels] : null;
|
||||
final float originalCoverage = isCutout ? alphaTestCoverage(base, width, cutoutRef, 1.0f, alphaScratch) : 0.0f;
|
||||
|
||||
for (int level = 1; level <= mipLevel; level++) {
|
||||
final int levelWidth = width >> level;
|
||||
if (levelWidth == 0) {
|
||||
if (result[level] == null) {
|
||||
result[level] = IntArrays.EMPTY_ARRAY;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (result[level] == null) {
|
||||
final int[] input = result[level - 1];
|
||||
final int prevWidth = width >> (level - 1);
|
||||
final int levelHeight = (input.length / prevWidth) >> 1;
|
||||
if (levelHeight == 0) {
|
||||
result[level] = IntArrays.EMPTY_ARRAY;
|
||||
continue;
|
||||
}
|
||||
final int[] output = new int[levelWidth * levelHeight];
|
||||
for (int y = 0; y < levelHeight; y++) {
|
||||
for (int x = 0; x < levelWidth; x++) {
|
||||
final int i = x * 2 + y * 2 * prevWidth;
|
||||
output[x + y * levelWidth] = ModernMipmapBlender.blend(input[i], input[i + 1], input[i + prevWidth], input[i + prevWidth + 1], darkCutout);
|
||||
}
|
||||
}
|
||||
result[level] = output;
|
||||
}
|
||||
if (isCutout && result[level].length > 0) {
|
||||
scaleAlphaToCoverage(result[level], levelWidth, originalCoverage, cutoutRef, alphaCutoffBias, alphaScratch);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void solidify(int[] image, int width) {
|
||||
boolean hasTransparentPixel = false;
|
||||
boolean hasOpaquePixel = false;
|
||||
for (int color : image) {
|
||||
if (ColorARGB.unpackAlpha(color) == 0) {
|
||||
hasTransparentPixel = true;
|
||||
} else {
|
||||
hasOpaquePixel = true;
|
||||
}
|
||||
if (hasTransparentPixel && hasOpaquePixel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasTransparentPixel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasOpaquePixel) {
|
||||
Arrays.fill(image, 0);
|
||||
return;
|
||||
}
|
||||
final int height = image.length / width;
|
||||
final int[] nearestColor = new int[image.length];
|
||||
final IntArrayFIFOQueue queue = new IntArrayFIFOQueue(64);
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
final int i = x + y * width;
|
||||
if (ColorARGB.unpackAlpha(image[i]) != 0) {
|
||||
nearestColor[i] = image[i];
|
||||
if (hasTransparentNeighbor(image, width, i, x)) {
|
||||
queue.enqueue(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!queue.isEmpty()) {
|
||||
final int i = queue.dequeueInt();
|
||||
final int x = i % width;
|
||||
final int color = nearestColor[i];
|
||||
if (x < width - 1) {
|
||||
fill(i + 1, color, nearestColor, queue);
|
||||
}
|
||||
if (x > 0) {
|
||||
fill(i - 1, color, nearestColor, queue);
|
||||
}
|
||||
if (i + width < image.length) {
|
||||
fill(i + width, color, nearestColor, queue);
|
||||
}
|
||||
if (i >= width) {
|
||||
fill(i - width, color, nearestColor, queue);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < image.length; i++) {
|
||||
if (ColorARGB.unpackAlpha(image[i]) == 0) {
|
||||
image[i] = nearestColor[i] & 0x00FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasTransparentNeighbor(int[] image, int width, int i, int x) {
|
||||
return (x < width - 1 && ColorARGB.unpackAlpha(image[i + 1]) == 0) || (x > 0 && ColorARGB.unpackAlpha(image[i - 1]) == 0) || (i + width < image.length && ColorARGB.unpackAlpha(image[i + width]) == 0)
|
||||
|| (i >= width && ColorARGB.unpackAlpha(image[i - width]) == 0);
|
||||
}
|
||||
|
||||
private static void fill(int i, int color, int[] nearestColor, IntArrayFIFOQueue queue) {
|
||||
if (nearestColor[i] == 0) {
|
||||
nearestColor[i] = color;
|
||||
queue.enqueue(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static float alphaTestCoverage(int[] image, int width, float alphaRef, float alphaScale, float[] alphaScratch) {
|
||||
final int height = image.length / width;
|
||||
for (int i = 0; i < image.length; i++) {
|
||||
alphaScratch[i] = clamp(0.0f, 1.0f, NORMALIZED_ALPHA[ColorARGB.unpackAlpha(image[i])] * alphaScale);
|
||||
}
|
||||
final float margin = 8.0f * Math.ulp(alphaRef);
|
||||
final float safelyAbove = alphaRef + margin;
|
||||
final float safelyBelow = alphaRef - margin;
|
||||
float coverage = 0.0f;
|
||||
for (int y = 0; y < height - 1; y++) {
|
||||
for (int x = 0; x < width - 1; x++) {
|
||||
final int i = x + y * width;
|
||||
final float a00 = alphaScratch[i];
|
||||
final float a10 = alphaScratch[i + 1];
|
||||
final float a01 = alphaScratch[i + width];
|
||||
final float a11 = alphaScratch[i + width + 1];
|
||||
if (a00 > safelyAbove && a10 > safelyAbove && a01 > safelyAbove && a11 > safelyAbove) {
|
||||
coverage += 1.0f;
|
||||
continue;
|
||||
}
|
||||
if (a00 < safelyBelow && a10 < safelyBelow && a01 < safelyBelow && a11 < safelyBelow) {
|
||||
continue;
|
||||
}
|
||||
float texelCoverage = 0.0f;
|
||||
for (int sy = 0; sy < 4; sy++) {
|
||||
final float fy = (sy + 0.5f) / 4.0f;
|
||||
for (int sx = 0; sx < 4; sx++) {
|
||||
final float fx = (sx + 0.5f) / 4.0f;
|
||||
final float alpha = a00 * (1.0f - fx) * (1.0f - fy) + a10 * fx * (1.0f - fy) + a01 * (1.0f - fx) * fy + a11 * fx * fy;
|
||||
if (alpha > alphaRef) {
|
||||
texelCoverage++;
|
||||
}
|
||||
}
|
||||
}
|
||||
coverage += texelCoverage / 16.0f;
|
||||
}
|
||||
}
|
||||
return coverage / ((width - 1) * (height - 1));
|
||||
}
|
||||
|
||||
private static void scaleAlphaToCoverage(int[] image, int width, float desiredCoverage, float alphaRef, float alphaCutoffBias, float[] alphaScratch) {
|
||||
float min = 0.0f;
|
||||
float max = 4.0f;
|
||||
float scale = 1.0f;
|
||||
float bestScale = 1.0f;
|
||||
float bestError = Float.MAX_VALUE;
|
||||
|
||||
if (width > 1 && image.length / width > 1) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final float coverage = alphaTestCoverage(image, width, alphaRef, scale, alphaScratch);
|
||||
final float error = Math.abs(coverage - desiredCoverage);
|
||||
if (error < bestError) {
|
||||
bestError = error;
|
||||
bestScale = scale;
|
||||
}
|
||||
if (coverage < desiredCoverage) {
|
||||
min = scale;
|
||||
} else if (coverage > desiredCoverage) {
|
||||
max = scale;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
scale = (min + max) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.length; i++) {
|
||||
final int color = image[i];
|
||||
final float alpha = NORMALIZED_ALPHA[ColorARGB.unpackAlpha(color)] * bestScale + alphaCutoffBias + ALPHA_BIAS;
|
||||
image[i] = ColorARGB.withAlpha(color, (int) (clamp(0.0f, 1.0f, alpha) * 255.0f));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrays;
|
||||
import org.embeddedt.embeddium.api.util.ColorARGB;
|
||||
|
||||
/**
|
||||
* Minecraft 1.7.10's TextureUtil mipmap generator without its shared mutable color buffer.
|
||||
*/
|
||||
final class VanillaMipmapGenerator {
|
||||
|
||||
private static final float[] TO_LINEAR = new float[256];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < TO_LINEAR.length; i++) {
|
||||
TO_LINEAR[i] = (float) Math.pow(i / 255.0f, 2.2);
|
||||
}
|
||||
}
|
||||
|
||||
private VanillaMipmapGenerator() {
|
||||
}
|
||||
|
||||
static int[][] generateMipLevels(int mipLevel, int width, int[][] currentMips) {
|
||||
final int[][] result = new int[mipLevel + 1][];
|
||||
result[0] = currentMips[0];
|
||||
if (mipLevel <= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean hasTransparentPixel = false;
|
||||
for (int i = 0; i < Math.min(currentMips.length, result[0].length); i++) {
|
||||
if (ColorARGB.unpackAlpha(result[0][i]) == 0) {
|
||||
hasTransparentPixel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int level = 1; level <= mipLevel; level++) {
|
||||
final int[] provided = level < currentMips.length ? currentMips[level] : null;
|
||||
if (provided != null) {
|
||||
result[level] = provided;
|
||||
continue;
|
||||
}
|
||||
|
||||
final int levelWidth = width >> level;
|
||||
if (levelWidth == 0) {
|
||||
result[level] = IntArrays.EMPTY_ARRAY;
|
||||
continue;
|
||||
}
|
||||
final int[] input = result[level - 1];
|
||||
final int[] output = new int[input.length >> 2];
|
||||
final int levelHeight = output.length / levelWidth;
|
||||
final int stride = levelWidth << 1;
|
||||
for (int y = 0; y < levelHeight; y++) {
|
||||
for (int x = 0; x < levelWidth; x++) {
|
||||
final int i = 2 * (x + y * stride);
|
||||
output[x + y * levelWidth] = blend(input[i], input[i + 1], input[i + stride], input[i + stride + 1], hasTransparentPixel);
|
||||
}
|
||||
}
|
||||
result[level] = output;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int blend(int a, int b, int c, int d, boolean hasTransparentPixel) {
|
||||
if (hasTransparentPixel) {
|
||||
int alpha = transparentChannel(a, b, c, d, 24);
|
||||
if (alpha < 96) {
|
||||
alpha = 0;
|
||||
}
|
||||
return ColorARGB.pack(transparentChannel(a, b, c, d, 16), transparentChannel(a, b, c, d, 8), transparentChannel(a, b, c, d, 0), alpha);
|
||||
}
|
||||
return ColorARGB.pack(meanChannel(a, b, c, d, 16), meanChannel(a, b, c, d, 8), meanChannel(a, b, c, d, 0), meanChannel(a, b, c, d, 24));
|
||||
}
|
||||
|
||||
private static int meanChannel(int a, int b, int c, int d, int shift) {
|
||||
final float sum = TO_LINEAR[(a >>> shift) & 255] + TO_LINEAR[(b >>> shift) & 255] + TO_LINEAR[(c >>> shift) & 255] + TO_LINEAR[(d >>> shift) & 255];
|
||||
final float value = (float) Math.pow(sum * 0.25, 1.0 / 2.2);
|
||||
return (int) (value * 255.0);
|
||||
}
|
||||
|
||||
private static int transparentChannel(int a, int b, int c, int d, int shift) {
|
||||
float sum = 0.0f;
|
||||
if (ColorARGB.unpackAlpha(a) != 0) {
|
||||
sum += TO_LINEAR[(a >>> shift) & 255];
|
||||
}
|
||||
if (ColorARGB.unpackAlpha(b) != 0) {
|
||||
sum += TO_LINEAR[(b >>> shift) & 255];
|
||||
}
|
||||
if (ColorARGB.unpackAlpha(c) != 0) {
|
||||
sum += TO_LINEAR[(c >>> shift) & 255];
|
||||
}
|
||||
if (ColorARGB.unpackAlpha(d) != 0) {
|
||||
sum += TO_LINEAR[(d >>> shift) & 255];
|
||||
}
|
||||
return (int) (Math.pow(sum / 4.0f, 1.0 / 2.2) * 255.0);
|
||||
}
|
||||
}
|
||||
@@ -12,44 +12,38 @@ import jss.notfine.config.MCPatcherForgeConfig;
|
||||
|
||||
public class GlassPaneRenderer {
|
||||
|
||||
private static final ThreadLocal<GlassPaneRenderer> STATE = ThreadLocal.withInitial(GlassPaneRenderer::new);
|
||||
public static boolean skipPaneRendering;
|
||||
public static boolean skipTopEdgeRendering;
|
||||
public static boolean skipBottomEdgeRendering;
|
||||
|
||||
public boolean skipPaneRendering;
|
||||
public boolean skipTopEdgeRendering;
|
||||
public boolean skipBottomEdgeRendering;
|
||||
private static final IIcon[] icons = new IIcon[6];
|
||||
|
||||
private final IIcon[] icons = new IIcon[6];
|
||||
private static double uLeft; // left edge
|
||||
private static double uLeftThin; // 7/16 point
|
||||
private static double uRightThin; // 9/16 point
|
||||
private static double uRight; // right edge
|
||||
private static double vTop; // top edge
|
||||
private static double vBottom; // bottom edge
|
||||
|
||||
private double uLeft; // left edge
|
||||
private double uLeftThin; // 7/16 point
|
||||
private double uRightThin; // 9/16 point
|
||||
private double uRight; // right edge
|
||||
private double vTop; // top edge
|
||||
private double vBottom; // bottom edge
|
||||
private static double u1Scaled;
|
||||
private static double u2Scaled;
|
||||
|
||||
private double u1Scaled;
|
||||
private double u2Scaled;
|
||||
|
||||
public static GlassPaneRenderer renderThin(RenderBlocks renderBlocks, Block blockPane, IIcon origIcon, int x, int y, int z,
|
||||
public static void renderThin(RenderBlocks renderBlocks, Block blockPane, IIcon origIcon, int x, int y, int z,
|
||||
boolean connectNorth, boolean connectSouth, boolean connectWest, boolean connectEast) {
|
||||
GlassPaneRenderer state = STATE.get();
|
||||
if (state.setupIcons(renderBlocks, blockPane, origIcon, x, y, z)) {
|
||||
state.render(x, y, z, connectNorth, connectSouth, connectWest, connectEast, 0.0, 0.0, 0.0, false);
|
||||
if (setupIcons(renderBlocks, blockPane, origIcon, x, y, z)) {
|
||||
render(x, y, z, connectNorth, connectSouth, connectWest, connectEast, 0.0, 0.0, 0.0, false);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
public static GlassPaneRenderer renderThick(RenderBlocks renderBlocks, Block blockPane, IIcon origIcon, int x, int y, int z,
|
||||
public static void renderThick(RenderBlocks renderBlocks, Block blockPane, IIcon origIcon, int x, int y, int z,
|
||||
boolean connectNorth, boolean connectSouth, boolean connectWest, boolean connectEast) {
|
||||
GlassPaneRenderer state = STATE.get();
|
||||
if (state.setupIcons(renderBlocks, blockPane, origIcon, x, y, z)) {
|
||||
state.setupPaneEdges(renderBlocks.blockAccess, blockPane, x, y, z);
|
||||
state.render(x, y, z, connectNorth, connectSouth, connectWest, connectEast, 0.0625, 1.0, 0.001, true);
|
||||
if (setupIcons(renderBlocks, blockPane, origIcon, x, y, z)) {
|
||||
setupPaneEdges(renderBlocks.blockAccess, blockPane, x, y, z);
|
||||
render(x, y, z, connectNorth, connectSouth, connectWest, connectEast, 0.0625, 1.0, 0.001, true);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
private boolean setupIcons(RenderBlocks renderBlocks, Block blockPane, IIcon origIcon, int x, int y, int z) {
|
||||
private static boolean setupIcons(RenderBlocks renderBlocks, Block blockPane, IIcon origIcon, int x, int y, int z) {
|
||||
skipPaneRendering = skipBottomEdgeRendering = skipTopEdgeRendering = false;
|
||||
if (!MCPatcherForgeConfig.ConnectedTextures.glassPane) {
|
||||
return false;
|
||||
@@ -67,7 +61,7 @@ public class GlassPaneRenderer {
|
||||
return skipPaneRendering;
|
||||
}
|
||||
|
||||
private void setupPaneEdges(IBlockAccess blockAccess, Block blockPane, int x, int y, int z) {
|
||||
private static void setupPaneEdges(IBlockAccess blockAccess, Block blockPane, int x, int y, int z) {
|
||||
int metadata = blockAccess.getBlockMetadata(x, y, z);
|
||||
skipBottomEdgeRendering = blockAccess.getBlock(x, y - 1, z) == blockPane
|
||||
&& blockAccess.getBlockMetadata(x, y - 1, z) == metadata;
|
||||
@@ -75,7 +69,7 @@ public class GlassPaneRenderer {
|
||||
&& blockAccess.getBlockMetadata(x, y + 1, z) == metadata;
|
||||
}
|
||||
|
||||
private void render(int x, int y, int z, boolean connectNorth, boolean connectSouth, boolean connectWest,
|
||||
private static void render(int x, int y, int z, boolean connectNorth, boolean connectSouth, boolean connectWest,
|
||||
boolean connectEast, double thickness, double uOffset, double yOffset, boolean edges) {
|
||||
final double west = x;
|
||||
final double westThin = x + 0.5 - thickness;
|
||||
@@ -232,7 +226,7 @@ public class GlassPaneRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private void setupTileCoords(int face) {
|
||||
private static void setupTileCoords(int face) {
|
||||
final IIcon icon = icons[face];
|
||||
uLeft = icon.getMinU();
|
||||
uLeftThin = icon.getInterpolatedU(u1Scaled);
|
||||
@@ -252,9 +246,8 @@ public class GlassPaneRenderer {
|
||||
}
|
||||
|
||||
protected static void clear() {
|
||||
GlassPaneRenderer state = STATE.get();
|
||||
Arrays.fill(state.icons, null);
|
||||
state.skipPaneRendering = false;
|
||||
Arrays.fill(icons, null);
|
||||
skipPaneRendering = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,7 +42,6 @@ import me.jellysquid.mods.sodium.client.gui.options.named.BiomeBlendMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.GraphicsMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.GraphicsQuality;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.LightingQuality;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MipmapGeneration;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.ParticleMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TexelSampling;
|
||||
@@ -287,15 +286,6 @@ public class SodiumGameOptionPages {
|
||||
.add(texelSampling)
|
||||
.add(anisotropicFilteringSlider(vanillaOpts, textureFilterMode::getValue),
|
||||
SodiumGameOptions.anisotropySupported())
|
||||
.add(OptionImpl.createBuilder(int.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.gui_block_supersampling.name"))
|
||||
.setTooltip(I18n.format("sodium.options.gui_block_supersampling.tooltip"))
|
||||
.setControl(option -> new SliderControl(option, 0, 4, 1,
|
||||
value -> value == 0 ? I18n.format("options.off") : (1 << value) + "x"))
|
||||
.setBinding((opts, value) -> opts.quality.guiBlockSupersampling = 1 << value,
|
||||
opts -> Integer.numberOfTrailingZeros(opts.quality.guiBlockSupersampling))
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.build())
|
||||
.add(biomeBlendRadius)
|
||||
.add(biomeBlendMode)
|
||||
// TODO
|
||||
@@ -314,14 +304,6 @@ public class SodiumGameOptionPages {
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(mipmapLevels)
|
||||
.add(OptionImpl.createBuilder(MipmapGeneration.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.mipmap_generation.name"))
|
||||
.setTooltip(I18n.format("sodium.options.mipmap_generation.tooltip"))
|
||||
.setControl(option -> new CyclingControl<>(option, MipmapGeneration.class))
|
||||
.setBinding((opts, value) -> opts.quality.mipmapGeneration = value, opts -> opts.quality.mipmapGeneration)
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.setFlags(OptionFlag.REQUIRES_ASSET_RELOAD)
|
||||
.build())
|
||||
.build());
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.MODE_GLINT_INV.option)
|
||||
@@ -774,6 +756,7 @@ public class SodiumGameOptionPages {
|
||||
(opts, value) -> opts.anisotropicFiltering = 1 << MathHelper.clamp_int(value, min, max),
|
||||
opts -> MathHelper.clamp_int(Integer.numberOfTrailingZeros(Math.max(opts.anisotropicFiltering, 1)), min, max))
|
||||
.setImpact(OptionImpact.MEDIUM)
|
||||
.setFlags(OptionFlag.REQUIRES_ASSET_RELOAD)
|
||||
.build();
|
||||
|
||||
option.iris$dynamicallyEnable(() -> mode.get().usesAnisotropy() && SodiumGameOptions.hasAnisotropyRange());
|
||||
@@ -864,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.BiomeBlendMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.GraphicsQuality;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MipmapGeneration;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TexelSampling;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.TextureFilterMode;
|
||||
@@ -62,8 +61,7 @@ public class SodiumGameOptions {
|
||||
final TextureMap atlas = mc.getTextureMapBlocks();
|
||||
|
||||
atlas.setMipmapLevels(mc.gameSettings.mipmapLevels);
|
||||
// Drivers may ignore NEAREST when anisotropy is enabled
|
||||
atlas.setAnisotropicFiltering(1);
|
||||
atlas.setAnisotropicFiltering(resolvedAnisotropicFiltering());
|
||||
}
|
||||
|
||||
public static int resolvedAnisotropicFiltering() {
|
||||
@@ -153,11 +151,6 @@ public class SodiumGameOptions {
|
||||
if (config.quality.texelSampling == null) {
|
||||
config.quality.texelSampling = TexelSampling.LINEAR;
|
||||
}
|
||||
config.quality.guiBlockSupersampling = Integer.highestOneBit(MathHelper.clamp_int(config.quality.guiBlockSupersampling, 1, 16));
|
||||
|
||||
if (config.quality.mipmapGeneration == null) {
|
||||
config.quality.mipmapGeneration = MipmapGeneration.MODERN_VANILLA;
|
||||
}
|
||||
|
||||
if (GLStateManager.capabilities != null) {
|
||||
if (!GLStateManager.capabilities.OpenGL32) {
|
||||
@@ -257,8 +250,6 @@ public class SodiumGameOptions {
|
||||
public boolean useCeleritasSmoothLighting = true;
|
||||
public TextureFilterMode textureFilterMode = TextureFilterMode.RGSS_ANISOTROPIC;
|
||||
public TexelSampling texelSampling = TexelSampling.LINEAR;
|
||||
public MipmapGeneration mipmapGeneration = MipmapGeneration.MODERN_VANILLA;
|
||||
public int guiBlockSupersampling = 1;
|
||||
}
|
||||
|
||||
public static class ReducerSettings {
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package me.jellysquid.mods.sodium.client.gui.options.named;
|
||||
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.parameters.AlphaCutoffParameter;
|
||||
|
||||
public enum MipmapGeneration implements NamedState {
|
||||
VANILLA_1_7_10("sodium.options.mipmap_generation.vanilla_1_7_10"),
|
||||
CELERITAS("sodium.options.mipmap_generation.celeritas"),
|
||||
MODERN_VANILLA("sodium.options.mipmap_generation.modern_vanilla");
|
||||
|
||||
private final String name;
|
||||
|
||||
MipmapGeneration(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static MipmapGeneration current() {
|
||||
return ClientProxy.options().quality.mipmapGeneration;
|
||||
}
|
||||
|
||||
public static boolean usesModernMipmaps() {
|
||||
return current() == MODERN_VANILLA;
|
||||
}
|
||||
|
||||
public static AlphaCutoffParameter alphaFloorCutoff() {
|
||||
return usesModernMipmaps() ? AlphaCutoffParameter.ONE_TENTH : AlphaCutoffParameter.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,6 @@ public class BlockRenderingSettings {
|
||||
private float ambientOcclusionLevel;
|
||||
private boolean disableDirectionalShading;
|
||||
private boolean useSeparateAo;
|
||||
@Setter private boolean breaksAnisotropy;
|
||||
private boolean useExtendedVertexFormat;
|
||||
@Setter
|
||||
private boolean hasSnowyEntries;
|
||||
@@ -214,10 +213,6 @@ public class BlockRenderingSettings {
|
||||
return useSeparateAo;
|
||||
}
|
||||
|
||||
public boolean breaksAnisotropy() {
|
||||
return breaksAnisotropy;
|
||||
}
|
||||
|
||||
public void setUseSeparateAo(boolean useSeparateAo) {
|
||||
if (useSeparateAo == this.useSeparateAo) {
|
||||
return;
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class IrisCeleritasChunkProgramOverrides {
|
||||
private final EnumMap<IrisTerrainPass, GlProgram<ChunkShaderInterface>> programs = new EnumMap<>(IrisTerrainPass.class);
|
||||
private final EnumMap<IrisTerrainPass, GlProgram<IrisCeleritasChunkShaderInterface>> programs = new EnumMap<>(IrisTerrainPass.class);
|
||||
private boolean shadersCreated = false;
|
||||
private int versionCounterForShaderReload = -1;
|
||||
|
||||
@@ -80,19 +80,19 @@ public class IrisCeleritasChunkProgramOverrides {
|
||||
return key;
|
||||
}
|
||||
|
||||
private static final class SavedProgram extends GlProgram<ChunkShaderInterface> {
|
||||
SavedProgram(int program, Function<ShaderBindingContext, ChunkShaderInterface> factory) {
|
||||
private static final class SavedProgram extends GlProgram<IrisCeleritasChunkShaderInterface> {
|
||||
SavedProgram(int program, Function<ShaderBindingContext, IrisCeleritasChunkShaderInterface> factory) {
|
||||
super(program, factory);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private GlProgram<ChunkShaderInterface> createShader(IrisTerrainPass pass, CeleritasTerrainPipeline pipeline, ChunkVertexType vertexType) {
|
||||
private GlProgram<IrisCeleritasChunkShaderInterface> createShader(IrisTerrainPass pass, CeleritasTerrainPipeline pipeline, ChunkVertexType vertexType) {
|
||||
final CeleritasTerrainPipeline.PassInfo passInfo = pipeline.getPassInfo(pass);
|
||||
final String name = "iris:celeritas-chunk-" + pass.getName();
|
||||
final BlendModeOverride blendOverride = passInfo.blendModeOverride();
|
||||
final List<BufferBlendOverride> bufferOverrides = passInfo.bufferBlendOverrides();
|
||||
final Function<ShaderBindingContext, ChunkShaderInterface> factory = context -> new IrisCeleritasChunkShaderInterface(
|
||||
final Function<ShaderBindingContext, IrisCeleritasChunkShaderInterface> factory = context -> new IrisCeleritasChunkShaderInterface(
|
||||
((GlObject) context).handle(), context, pipeline, pass.isShadow(), blendOverride, bufferOverrides, pipeline.getCustomUniforms());
|
||||
|
||||
final ProgramBinaryCache.Key cacheKey = cacheKey(passInfo, vertexType);
|
||||
@@ -130,7 +130,7 @@ public class IrisCeleritasChunkProgramOverrides {
|
||||
builder.bindAttribute(attr.getName(), attrIndex++);
|
||||
}
|
||||
|
||||
final GlProgram<ChunkShaderInterface> program = builder.link(factory);
|
||||
final GlProgram<IrisCeleritasChunkShaderInterface> program = builder.link(factory);
|
||||
if (cacheKey != null) {
|
||||
ProgramBinaryCache.save(cacheKey, program.handle());
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public class IrisCeleritasChunkProgramOverrides {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GlProgram<ChunkShaderInterface> getProgramOverride(TerrainRenderPass pass, RenderPassConfiguration<?> configuration) {
|
||||
public GlProgram<? extends ChunkShaderInterface> getProgramOverride(TerrainRenderPass pass, RenderPassConfiguration<?> configuration) {
|
||||
// Check for shader pack reload
|
||||
if (versionCounterForShaderReload != Iris.getPipelineManager().getVersionCounterForSodiumShaderReload()) {
|
||||
versionCounterForShaderReload = Iris.getPipelineManager().getVersionCounterForSodiumShaderReload();
|
||||
@@ -186,13 +186,23 @@ public class IrisCeleritasChunkProgramOverrides {
|
||||
createShaders(celeritasPipeline, configuration);
|
||||
}
|
||||
|
||||
final boolean isShadow = ShadowRenderingState.areShadowsCurrentlyBeingRendered();
|
||||
if (isShadow) {
|
||||
if (ShadowRenderingState.areShadowsCurrentlyBeingRendered()) {
|
||||
if (celeritasPipeline != null && !celeritasPipeline.hasShadowPass()) {
|
||||
throw new IllegalStateException("Shadow program requested, but shader pack has no shadow pass");
|
||||
}
|
||||
if (pass.isReverseOrder()) {
|
||||
return programs.get(IrisTerrainPass.SHADOW_TRANSLUCENT);
|
||||
}
|
||||
return programs.get(pass.supportsFragmentDiscard() ? IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW);
|
||||
} else {
|
||||
if (pass.supportsFragmentDiscard()) {
|
||||
return programs.get(IrisTerrainPass.GBUFFER_CUTOUT);
|
||||
} else if (pass.isReverseOrder()) {
|
||||
return programs.get(IrisTerrainPass.GBUFFER_TRANSLUCENT);
|
||||
} else {
|
||||
return programs.get(IrisTerrainPass.GBUFFER_SOLID);
|
||||
}
|
||||
}
|
||||
return programs.get(IrisTerrainPass.fromTerrainPass(pass, isShadow));
|
||||
}
|
||||
|
||||
public void deleteShaders() {
|
||||
|
||||
@@ -241,7 +241,20 @@ public class IrisCeleritasChunkShaderInterface implements ChunkShaderInterface {
|
||||
}
|
||||
|
||||
final boolean isShadow = ShadowRenderingState.areShadowsCurrentlyBeingRendered();
|
||||
final IrisTerrainPass irisPass = IrisTerrainPass.fromTerrainPass(pass, isShadow);
|
||||
final IrisTerrainPass irisPass;
|
||||
if (isShadow) {
|
||||
if (pass.isReverseOrder()) {
|
||||
irisPass = IrisTerrainPass.SHADOW_TRANSLUCENT;
|
||||
} else {
|
||||
irisPass = pass.supportsFragmentDiscard() ? IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW;
|
||||
}
|
||||
} else if (pass.isReverseOrder()) {
|
||||
irisPass = IrisTerrainPass.GBUFFER_TRANSLUCENT;
|
||||
} else if (pass.supportsFragmentDiscard()) {
|
||||
irisPass = IrisTerrainPass.GBUFFER_CUTOUT;
|
||||
} else {
|
||||
irisPass = IrisTerrainPass.GBUFFER_SOLID;
|
||||
}
|
||||
|
||||
final GlFramebuffer framebuffer = pipeline.getPassInfo(irisPass).framebuffer();
|
||||
if (framebuffer != null) {
|
||||
|
||||
@@ -40,7 +40,7 @@ public class IrisCeleritasShaderProvider implements IrisShaderProvider {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public GlProgram<ChunkShaderInterface> getShaderOverride(TerrainRenderPass pass) {
|
||||
public GlProgram<? extends ChunkShaderInterface> getShaderOverride(TerrainRenderPass pass) {
|
||||
if (!isShadersEnabled() || renderPassConfiguration == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ public enum IrisTerrainPass {
|
||||
return SHADOW_TRANSLUCENT;
|
||||
}
|
||||
return pass.supportsFragmentDiscard() ? SHADOW_CUTOUT : SHADOW;
|
||||
} else if (pass.isReverseOrder()) {
|
||||
return GBUFFER_TRANSLUCENT;
|
||||
} else if (pass.supportsFragmentDiscard()) {
|
||||
return GBUFFER_CUTOUT;
|
||||
} else if (pass.isReverseOrder()) {
|
||||
return GBUFFER_TRANSLUCENT;
|
||||
} else {
|
||||
return GBUFFER_SOLID;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ public class AlphaTestStorage {
|
||||
@Getter
|
||||
private static boolean alphaTestLocked;
|
||||
|
||||
@Getter
|
||||
private static boolean overrideHeld;
|
||||
|
||||
public static void overrideAlphaTest(AlphaTest override) {
|
||||
|
||||
@@ -93,7 +93,6 @@ public final class ComputeProgram extends GlResource {
|
||||
|
||||
public static void unbind() {
|
||||
ProgramUniforms.clearActiveUniforms();
|
||||
ProgramSamplers.clearActiveSamplers();
|
||||
GLStateManager.glUseProgram(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.gtnewhorizons.angelica.glsm.texture.TextureType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ProgramBuilder extends ProgramUniforms.Builder implements SamplerHolder, ImageHolder {
|
||||
private final int program;
|
||||
@@ -197,17 +196,17 @@ public class ProgramBuilder extends ProgramUniforms.Builder implements SamplerHo
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, Supplier<GlSampler> sampler, String... names) {
|
||||
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
|
||||
return samplers.addDefaultSampler(type, texture, notifier, sampler, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, Supplier<GlSampler> sampler, String... names) {
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, String... names) {
|
||||
return samplers.addDynamicSampler(type, texture, sampler, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, Supplier<GlSampler> sampler, String... names) {
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
|
||||
return samplers.addDynamicSampler(type, texture, notifier, sampler, names);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ProgramSamplers {
|
||||
private static ProgramSamplers active;
|
||||
@@ -40,25 +39,23 @@ public class ProgramSamplers {
|
||||
|
||||
active = this;
|
||||
|
||||
int activeTexture = GLStateManager.getActiveTextureUnit();
|
||||
try {
|
||||
if (initializer != null) {
|
||||
for (GlUniform1iCall call : initializer) {
|
||||
RenderSystem.uniform1i(call.getLocation(), call.getValue());
|
||||
}
|
||||
|
||||
initializer = null;
|
||||
if (initializer != null) {
|
||||
for (GlUniform1iCall call : initializer) {
|
||||
RenderSystem.uniform1i(call.getLocation(), call.getValue());
|
||||
}
|
||||
|
||||
for (int i = 0, n = samplerBindings.size(); i < n; i++) {
|
||||
samplerBindings.get(i).update();
|
||||
}
|
||||
} catch (RuntimeException | Error e) {
|
||||
clearActiveSamplers();
|
||||
throw e;
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + activeTexture);
|
||||
initializer = null;
|
||||
}
|
||||
|
||||
// We need to keep the active texture intact, since if we mess it up
|
||||
// in the middle of RenderType setup, bad things will happen.
|
||||
int activeTexture = GLStateManager.getActiveTextureUnit();
|
||||
|
||||
for (int i = 0, n = samplerBindings.size(); i < n; i++) {
|
||||
samplerBindings.get(i).update();
|
||||
}
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + activeTexture);
|
||||
}
|
||||
|
||||
public void removeListeners() {
|
||||
@@ -73,7 +70,6 @@ public class ProgramSamplers {
|
||||
if (active != null) {
|
||||
active.removeListeners();
|
||||
}
|
||||
RenderSystem.unbindAllSamplers();
|
||||
}
|
||||
|
||||
public static Builder builder(int program, Set<Integer> reservedTextureUnits) {
|
||||
@@ -152,30 +148,31 @@ public class ProgramSamplers {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, Supplier<GlSampler> sampler, String... names) {
|
||||
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
|
||||
if (nextUnit != 0) {
|
||||
// TODO: Relax this restriction!
|
||||
throw new IllegalStateException("Texture unit 0 is already used.");
|
||||
}
|
||||
|
||||
if (notifier != null) {
|
||||
notifiersToReset.add(notifier);
|
||||
}
|
||||
|
||||
return addDynamicSampler(type, texture, sampler, true, notifier, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, Supplier<GlSampler> sampler, String... names) {
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, String... names) {
|
||||
return addDynamicSampler(type, texture, sampler, false, null, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, Supplier<GlSampler> sampler, String... names) {
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
|
||||
notifiersToReset.add(notifier);
|
||||
return addDynamicSampler(type, texture, sampler, false, notifier, names);
|
||||
}
|
||||
|
||||
private boolean addDynamicSampler(TextureType type, IntSupplier texture, Supplier<GlSampler> sampler, boolean used, ValueUpdateNotifier notifier, String... names) {
|
||||
if (notifier != null) {
|
||||
notifiersToReset.add(notifier);
|
||||
}
|
||||
|
||||
private boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, boolean used, ValueUpdateNotifier notifier, String... names) {
|
||||
for (String name : names) {
|
||||
int location = RenderSystem.getUniformLocation(program, name);
|
||||
|
||||
@@ -239,22 +236,26 @@ public class ProgramSamplers {
|
||||
this.deactivatedOverrides = deactivatedOverrides.build();
|
||||
}
|
||||
|
||||
private TextureAccess getOverride(String... names) {
|
||||
private IntSupplier getOverride(IntSupplier existing, String... names) {
|
||||
for (String name : names) {
|
||||
if (customTextureIds.containsKey(name) && !deactivatedOverrides.contains(name)) {
|
||||
return customTextureIds.get(name);
|
||||
return customTextureIds.get(name).getTextureId();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return existing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExternalSampler(int textureUnit, String... names) {
|
||||
TextureAccess override = getOverride(names);
|
||||
IntSupplier override = getOverride(null, names);
|
||||
|
||||
if (override != null) {
|
||||
addOverride(override, textureUnit == 0, names);
|
||||
if (textureUnit == 0) {
|
||||
samplerHolder.addDefaultSampler(override, names);
|
||||
} else {
|
||||
samplerHolder.addDynamicSampler(override, names);
|
||||
}
|
||||
} else {
|
||||
samplerHolder.addExternalSampler(textureUnit, names);
|
||||
}
|
||||
@@ -266,34 +267,21 @@ public class ProgramSamplers {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, Supplier<GlSampler> sampler, String... names) {
|
||||
return addSampler(type, texture, notifier, sampler, true, names);
|
||||
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
|
||||
texture = getOverride(texture, names);
|
||||
return samplerHolder.addDefaultSampler(type, texture, notifier, sampler, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, Supplier<GlSampler> sampler, String... names) {
|
||||
return addSampler(type, texture, null, sampler, false, names);
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, String... names) {
|
||||
texture = getOverride(texture, names);
|
||||
return samplerHolder.addDynamicSampler(type, texture, sampler, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, Supplier<GlSampler> sampler, String... names) {
|
||||
return addSampler(type, texture, notifier, sampler, false, names);
|
||||
}
|
||||
|
||||
private boolean addSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, Supplier<GlSampler> sampler, boolean defaultSampler, String... names) {
|
||||
final TextureAccess override = getOverride(names);
|
||||
if (override != null) {
|
||||
return addOverride(override, defaultSampler, names);
|
||||
}
|
||||
return defaultSampler
|
||||
? samplerHolder.addDefaultSampler(type, texture, notifier, sampler, names)
|
||||
: samplerHolder.addDynamicSampler(type, texture, notifier, sampler, names);
|
||||
}
|
||||
|
||||
private boolean addOverride(TextureAccess override, boolean defaultSampler, String... names) {
|
||||
return defaultSampler
|
||||
? samplerHolder.addDefaultSampler(override.getType(), override.getTextureId(), null, null, names)
|
||||
: samplerHolder.addDynamicSampler(override.getType(), override.getTextureId(), null, names);
|
||||
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
|
||||
texture = getOverride(texture, names);
|
||||
return samplerHolder.addDynamicSampler(type, texture, notifier, sampler, names);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,8 @@ import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class GlSampler extends GlObject {
|
||||
public GlSampler(boolean linear, boolean linearMips, boolean mipmapped, boolean shadow, boolean hardwareShadow, boolean clamp) {
|
||||
public GlSampler(boolean linear, boolean mipmapped, boolean shadow, boolean hardwareShadow) {
|
||||
if (!RenderSystem.supportsSamplerObjects()) {
|
||||
this.setHandle(0);
|
||||
return;
|
||||
@@ -18,16 +16,13 @@ public class GlSampler extends GlObject {
|
||||
|
||||
this.setHandle(RenderSystem.genSampler());
|
||||
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_MIN_FILTER, linear ? GL11.GL_LINEAR : GL11.GL_NEAREST);
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_MAG_FILTER, linear ? GL11.GL_LINEAR : GL11.GL_NEAREST);
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_WRAP_S, clamp ? GL12.GL_CLAMP_TO_EDGE : GL11.GL_REPEAT);
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_WRAP_T, clamp ? GL12.GL_CLAMP_TO_EDGE : GL11.GL_REPEAT);
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
|
||||
|
||||
if (mipmapped) {
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_MIN_FILTER, linearMips
|
||||
? (linear ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_NEAREST_MIPMAP_LINEAR)
|
||||
: (linear ? GL11.GL_LINEAR_MIPMAP_NEAREST : GL11.GL_NEAREST_MIPMAP_NEAREST));
|
||||
} else {
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_MIN_FILTER, linear ? GL11.GL_LINEAR : GL11.GL_NEAREST);
|
||||
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_MIN_FILTER, linear ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_NEAREST_MIPMAP_NEAREST);
|
||||
}
|
||||
|
||||
if (hardwareShadow) {
|
||||
@@ -43,9 +38,4 @@ public class GlSampler extends GlObject {
|
||||
public int getId() {
|
||||
return handle();
|
||||
}
|
||||
|
||||
public static int idOf(Supplier<GlSampler> supplier) {
|
||||
final GlSampler sampler = supplier == null ? null : supplier.get();
|
||||
return sampler == null ? 0 : sampler.getId();
|
||||
}
|
||||
}
|
||||
@@ -5,21 +5,20 @@ import net.coderbot.iris.gl.state.ValueUpdateNotifier;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureType;
|
||||
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SamplerBinding {
|
||||
private final Runnable updateListener = this::updateSampler;
|
||||
private final TextureType textureType;
|
||||
private final int textureUnit;
|
||||
private final IntSupplier texture;
|
||||
private final Supplier<GlSampler> sampler;
|
||||
private final int sampler;
|
||||
private final ValueUpdateNotifier notifier;
|
||||
|
||||
public SamplerBinding(TextureType type, int textureUnit, IntSupplier texture, Supplier<GlSampler> sampler, ValueUpdateNotifier notifier) {
|
||||
public SamplerBinding(TextureType type, int textureUnit, IntSupplier texture, GlSampler sampler, ValueUpdateNotifier notifier) {
|
||||
this.textureType = type;
|
||||
this.textureUnit = textureUnit;
|
||||
this.texture = texture;
|
||||
this.sampler = sampler;
|
||||
this.sampler = sampler == null ? 0 : sampler.getId();
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
@@ -32,7 +31,7 @@ public class SamplerBinding {
|
||||
}
|
||||
|
||||
private void updateSampler() {
|
||||
RenderSystem.bindSamplerToUnit(textureUnit, GlSampler.idOf(sampler));
|
||||
RenderSystem.bindSamplerToUnit(textureUnit, sampler);
|
||||
RenderSystem.bindTextureToUnit(textureType.getGlType(), textureUnit, texture.getAsInt());
|
||||
}
|
||||
}
|
||||
Loaded 100 of 152 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user