mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05f01c17e6 | ||
|
|
f07d96316a | ||
|
|
5b704d1bc2 | ||
|
|
26135124f4 | ||
|
|
3abc89b7b2 | ||
|
|
03d908d658 | ||
|
|
76bf19c8f6 | ||
|
|
b27834cbd9 | ||
|
|
eb0ecb0483 | ||
|
|
9acb0e67a7 | ||
|
|
f4f9b56b17 | ||
|
|
2196439e6c | ||
|
|
dd68c64239 | ||
|
|
0decc515e4 |
No files matched your search
@@ -23,7 +23,7 @@ Targets GL 3.3+ core profile
|
||||
|
||||
# Required Dependencies
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.9.0
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.11.19
|
||||
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
|
||||
|
||||
# Known (temporary) Incompatibilities
|
||||
|
||||
+4
-1
@@ -76,7 +76,7 @@ dependencies {
|
||||
|
||||
// Iris Shaders
|
||||
compileOnly('org.jetbrains:annotations:26.0.2')
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.4:dev")
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.19:dev")
|
||||
|
||||
shadowImplementation(project(":glsm")) { transitive = false }
|
||||
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
|
||||
@@ -173,6 +173,9 @@ dependencies {
|
||||
testImplementation('org.junit.platform:junit-platform-launcher')
|
||||
|
||||
testImplementation('com.tngtech.archunit:archunit-junit5:1.4.1')
|
||||
|
||||
testRuntimeOnly("it.unimi.dsi:fastutil:8.5.18")
|
||||
testRuntimeOnly("org.joml:joml:1.10.8")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ dependencies {
|
||||
// Deps (normally from MC) - compile only, but needed at test runtime
|
||||
compileOnly("com.google.guava:guava:${property("guavaVersion")}")
|
||||
testRuntimeOnly("com.google.guava:guava:${property("guavaVersion")}")
|
||||
testRuntimeOnly("it.unimi.dsi:fastutil:${property("fastutilVersion")}")
|
||||
testRuntimeOnly("org.joml:joml:${property("jomlVersion")}") { isTransitive = false }
|
||||
|
||||
// Test
|
||||
testImplementation(sourceSets["stubs"].output)
|
||||
|
||||
@@ -4,7 +4,9 @@ celeritasVersion = 2.5.3-GTNH
|
||||
lombokVersion = 1.18.42
|
||||
log4jVersion = 2.0-beta9
|
||||
guavaVersion = 18.0
|
||||
gtnhlibVersion = 0.10.0
|
||||
gtnhlibVersion = 0.11.19
|
||||
fastutilVersion = 8.5.18
|
||||
jomlVersion = 1.10.8
|
||||
glslTransformLibVersion = 0.2.0-26.g5ee7d96-GTNH
|
||||
antlr4RuntimeVersion = 4.13.2
|
||||
jcppVersion = 1.4.14
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
import org.taumc.glsl.ShaderParser;
|
||||
import org.taumc.glsl.Transformer;
|
||||
|
||||
@@ -8,9 +13,11 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -182,8 +189,9 @@ public class CompatShaderTransformer {
|
||||
transformer.rename("gl_FrontColor", "angelica_FrontColor");
|
||||
transformer.prependMain("angelica_FrontColor = vec4(1.0);");
|
||||
|
||||
// Vertex attributes — replaces removed FFP vertex inputs with explicit in declarations
|
||||
transformVertexAttributes(transformer, source);
|
||||
final FFPInputs ffp = FFPInputs.detect(source);
|
||||
assignVertexInputLocations(transformer, ffp);
|
||||
transformVertexAttributes(transformer, source, ffp);
|
||||
} else {
|
||||
if (source.contains("gl_Color")) {
|
||||
transformer.injectVariable("in vec4 angelica_FrontColor;");
|
||||
@@ -310,25 +318,25 @@ public class CompatShaderTransformer {
|
||||
/**
|
||||
* Replace removed FFP vertex attributes with explicit {@code in} declarations at core profile attribute locations.
|
||||
*/
|
||||
private static void transformVertexAttributes(Transformer transformer, String source) {
|
||||
if (source.contains("gl_Vertex") || source.contains("ftransform")) {
|
||||
private static void transformVertexAttributes(Transformer transformer, String source, FFPInputs ffp) {
|
||||
if (ffp.position()) {
|
||||
transformer.injectVariable("layout(location = 0) in vec4 angelica_Vertex;");
|
||||
transformer.rename("gl_Vertex", "angelica_Vertex");
|
||||
}
|
||||
// gl_Color in vertex shaders is the per-vertex color attribute, distinct from the fragment gl_Color (interpolated gl_FrontColor) handled above
|
||||
if (source.contains("gl_Color")) {
|
||||
if (ffp.color()) {
|
||||
transformer.injectVariable("layout(location = 1) in vec4 angelica_Color;");
|
||||
transformer.rename("gl_Color", "angelica_Color");
|
||||
}
|
||||
if (source.contains("gl_MultiTexCoord0")) {
|
||||
if (ffp.uv0()) {
|
||||
transformer.injectVariable("layout(location = 2) in vec4 angelica_MultiTexCoord0;");
|
||||
transformer.rename("gl_MultiTexCoord0", "angelica_MultiTexCoord0");
|
||||
}
|
||||
if (source.contains("gl_MultiTexCoord1")) {
|
||||
if (ffp.uv1()) {
|
||||
transformer.injectVariable("layout(location = 3) in vec4 angelica_MultiTexCoord1;");
|
||||
transformer.rename("gl_MultiTexCoord1", "angelica_MultiTexCoord1");
|
||||
}
|
||||
if (source.contains("gl_Normal")) {
|
||||
if (ffp.normal()) {
|
||||
transformer.injectVariable("layout(location = 4) in vec3 angelica_Normal;");
|
||||
transformer.rename("gl_Normal", "angelica_Normal");
|
||||
}
|
||||
@@ -337,6 +345,145 @@ public class CompatShaderTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MAX_VERTEX_ATTRIBS = 16;
|
||||
|
||||
private record FFPInputs(boolean position, boolean color, boolean uv0, boolean uv1, boolean normal) {
|
||||
static FFPInputs detect(String source) {
|
||||
return new FFPInputs(
|
||||
source.contains("gl_Vertex") || source.contains("ftransform"),
|
||||
source.contains("gl_Color"),
|
||||
source.contains("gl_MultiTexCoord0"),
|
||||
source.contains("gl_MultiTexCoord1"),
|
||||
source.contains("gl_Normal"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Adaptation of Mesa's assign_attribute_or_color_locations */
|
||||
private static void assignVertexInputLocations(Transformer transformer, FFPInputs ffp) {
|
||||
int used = 0;
|
||||
if (ffp.position()) used |= 1 << Usage.POSITION.getAttributeLocation();
|
||||
if (ffp.color()) used |= 1 << Usage.COLOR.getAttributeLocation();
|
||||
if (ffp.uv0()) used |= 1 << Usage.PRIMARY_UV.getAttributeLocation();
|
||||
if (ffp.uv1()) used |= 1 << Usage.SECONDARY_UV.getAttributeLocation();
|
||||
if (ffp.normal()) used |= 1 << Usage.NORMAL.getAttributeLocation();
|
||||
|
||||
record Input(String name, String baseType, String arraySuffix, int slots, int index) {}
|
||||
final List<Input> toAssign = new ArrayList<>();
|
||||
int index = 0;
|
||||
|
||||
for (TerminalNode storage : transformer.collectStorage()) {
|
||||
final int tokenType = storage.getSymbol().getType();
|
||||
if (tokenType != GLSLLexer.ATTRIBUTE && tokenType != GLSLLexer.IN) continue;
|
||||
|
||||
ParseTree node = storage.getParent();
|
||||
while (node != null && !(node instanceof GLSLParser.Single_declarationContext)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
if (!(node instanceof GLSLParser.Single_declarationContext decl)) continue;
|
||||
if (decl.typeless_declaration() == null) continue;
|
||||
|
||||
final GLSLParser.Type_specifierContext typeSpec = decl.fully_specified_type().type_specifier();
|
||||
final String baseType = GlslTransformUtils.getFormattedShader(typeSpec.type_specifier_nonarray(), "").trim();
|
||||
final int typeArraySlots = arraySlots(typeSpec.array_specifier());
|
||||
final String typeArray = arraySuffix(typeSpec.array_specifier());
|
||||
|
||||
if (hasLayoutQualifier(decl)) {
|
||||
final int loc = explicitLocation(decl);
|
||||
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(decl.typeless_declaration().array_specifier());
|
||||
if (loc >= 0) used |= slotMask(slots) << loc;
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<GLSLParser.Typeless_declarationContext> declarators = new ArrayList<>();
|
||||
declarators.add(decl.typeless_declaration());
|
||||
if (decl.getParent() instanceof GLSLParser.Init_declarator_listContext list) {
|
||||
declarators.addAll(list.typeless_declaration());
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext d : declarators) {
|
||||
if (d.IDENTIFIER() == null) continue;
|
||||
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(d.array_specifier());
|
||||
final String arraySuffix = typeArray + arraySuffix(d.array_specifier());
|
||||
toAssign.add(new Input(d.IDENTIFIER().getText(), baseType, arraySuffix, slots, index++));
|
||||
}
|
||||
}
|
||||
|
||||
toAssign.sort((a, b) -> a.slots() != b.slots() ? b.slots() - a.slots() : a.index() - b.index());
|
||||
|
||||
for (Input in : toAssign) {
|
||||
final int loc = findAvailableSlots(used, in.slots());
|
||||
if (loc < 0) continue;
|
||||
used |= slotMask(in.slots()) << loc;
|
||||
transformer.removeVariable(in.name());
|
||||
transformer.variable = null;
|
||||
transformer.injectVariable("layout(location = " + loc + ") in " + in.baseType() + " " + in.name() + in.arraySuffix() + ";");
|
||||
}
|
||||
}
|
||||
|
||||
private static String arraySuffix(GLSLParser.Array_specifierContext arraySpec) {
|
||||
return arraySpec != null ? GlslTransformUtils.getFormattedShader(arraySpec, "").trim() : "";
|
||||
}
|
||||
|
||||
private static int arraySlots(GLSLParser.Array_specifierContext arraySpec) {
|
||||
if (arraySpec == null) return 1;
|
||||
int product = 1;
|
||||
for (GLSLParser.DimensionContext dim : arraySpec.dimension()) {
|
||||
if (dim.constant_expression() == null) continue;
|
||||
try {
|
||||
product *= Integer.decode(GlslTransformUtils.getFormattedShader(dim.constant_expression(), "").trim());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
private static int baseSlots(String type) {
|
||||
final String dims = type.startsWith("dmat") ? type.substring(4) : type.startsWith("mat") ? type.substring(3) : null;
|
||||
if (dims != null && !dims.isEmpty() && Character.isDigit(dims.charAt(0))) {
|
||||
return dims.charAt(0) - '0';
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int slotMask(int slots) {
|
||||
return (slots >= 32) ? -1 : (1 << slots) - 1;
|
||||
}
|
||||
|
||||
private static int findAvailableSlots(int used, int needed) {
|
||||
if (needed <= 0) return -1;
|
||||
final int mask = slotMask(needed);
|
||||
for (int i = 0; i <= MAX_VERTEX_ATTRIBS - needed; i++) {
|
||||
if (((mask << i) & ~used) == (mask << i)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int explicitLocation(GLSLParser.Single_declarationContext decl) {
|
||||
final GLSLParser.Type_qualifierContext qualifier = decl.fully_specified_type().type_qualifier();
|
||||
if (qualifier == null) return -1;
|
||||
for (GLSLParser.Single_type_qualifierContext single : qualifier.single_type_qualifier()) {
|
||||
final GLSLParser.Layout_qualifierContext layout = single.layout_qualifier();
|
||||
if (layout == null) continue;
|
||||
for (GLSLParser.Layout_qualifier_idContext id : layout.layout_qualifier_id_list().layout_qualifier_id()) {
|
||||
if (id.IDENTIFIER() != null && "location".equals(id.IDENTIFIER().getText()) && id.constant_expression() != null) {
|
||||
try {
|
||||
return Integer.decode(GlslTransformUtils.getFormattedShader(id.constant_expression(), "").trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean hasLayoutQualifier(GLSLParser.Single_declarationContext decl) {
|
||||
final GLSLParser.Type_qualifierContext qualifier = decl.fully_specified_type().type_qualifier();
|
||||
if (qualifier == null) return false;
|
||||
for (GLSLParser.Single_type_qualifierContext single : qualifier.single_type_qualifier()) {
|
||||
if (single.layout_qualifier() != null) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void dumpShader(String original, String transformed, boolean isFragment, boolean wasTransformed) {
|
||||
if (DUMP_DIR == null) return;
|
||||
final int id = dumpCounter.getAndIncrement();
|
||||
|
||||
@@ -133,7 +133,10 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static UnsupportedOperationException unsupportedInList(String what) {
|
||||
return new UnsupportedOperationException(what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
|
||||
final UnsupportedOperationException e = new UnsupportedOperationException( what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
|
||||
GLStateManager.warnOnce("unsupported:" + what, "Aborting display list {} compilation: {} unsupported during recording", glListId, what);
|
||||
abortCompilation();
|
||||
return e;
|
||||
}
|
||||
|
||||
public static String describeCurrentCompilation() {
|
||||
@@ -798,7 +801,8 @@ public class DisplayListManager {
|
||||
*/
|
||||
public static void glEndList() {
|
||||
if (glListMode == 0) {
|
||||
throw new RuntimeException("glEndList called outside of a display list!");
|
||||
GLStateManager.warnOnce("endlist-outside", "glEndList called outside of a display list!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
finishCurrentList();
|
||||
@@ -906,17 +910,56 @@ public class DisplayListManager {
|
||||
// Parent's COMPILING callback is now active again
|
||||
} else {
|
||||
// Not nested - clear all compilation state
|
||||
currentRecorder = null;
|
||||
recordingThread = null;
|
||||
accumulatedDraws = null;
|
||||
pendingDraw = null;
|
||||
transformCallback = null;
|
||||
compilationStackTrace = null;
|
||||
pendingTransformOps = null;
|
||||
multMatrixSources = null;
|
||||
drawRangeSources = null;
|
||||
glListId = -1;
|
||||
glListMode = 0;
|
||||
resetCompilationState();
|
||||
}
|
||||
}
|
||||
|
||||
/** Clear all root-level compilation state back to "not recording". */
|
||||
private static void resetCompilationState() {
|
||||
currentRecorder = null;
|
||||
recordingThread = null;
|
||||
accumulatedDraws = null;
|
||||
pendingDraw = null;
|
||||
transformCallback = null;
|
||||
compilationStackTrace = null;
|
||||
pendingTransformOps = null;
|
||||
multMatrixSources = null;
|
||||
drawRangeSources = null;
|
||||
glListId = -1;
|
||||
glListMode = 0;
|
||||
}
|
||||
|
||||
public static void abortCompilation() {
|
||||
if (glListMode == 0 && compilationStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int levels = compilationStack.size() + (glListMode != 0 ? 1 : 0);
|
||||
for (int i = 0; i < levels; i++) {
|
||||
try {
|
||||
TessellatorManager.stopCapturingDirect();
|
||||
} catch (IllegalStateException ignored) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRecorder != null) {
|
||||
currentRecorder.delete();
|
||||
}
|
||||
for (CompilationContext ctx : compilationStack) {
|
||||
if (ctx.recorder() != null) {
|
||||
ctx.recorder().delete();
|
||||
}
|
||||
}
|
||||
compilationStack.clear();
|
||||
|
||||
resetCompilationState();
|
||||
}
|
||||
|
||||
public static void abortIfLeaked() {
|
||||
if (glListMode != 0 || !compilationStack.isEmpty()) {
|
||||
GLStateManager.warnOnce("frame-leak", "Display list {} left open across a frame boundary - aborting to recover", glListId);
|
||||
abortCompilation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
@@ -94,6 +95,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
|
||||
@@ -119,6 +121,12 @@ public class GLStateManager {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("GLSM");
|
||||
|
||||
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public static void warnOnce(String key, String fmt, Object... args) {
|
||||
if (WARN_ONCE.add(key)) LOGGER.warn(fmt, args);
|
||||
}
|
||||
|
||||
// Thread Checking - must be early in static init order so isMainThread() works for state initialization
|
||||
@Getter private static final Thread MainThread = Thread.currentThread();
|
||||
|
||||
@@ -187,6 +195,14 @@ public class GLStateManager {
|
||||
private static boolean dirtyTexCoordAttrib;
|
||||
private static boolean dirtyLightmapAttrib = true;
|
||||
|
||||
private static boolean unit23TexCoordSetDuringDraw = false;
|
||||
|
||||
public static boolean consumeUnit23TexCoordSetDuringDraw() {
|
||||
boolean v = unit23TexCoordSetDuringDraw;
|
||||
unit23TexCoordSetDuringDraw = false;
|
||||
return v;
|
||||
}
|
||||
|
||||
// vertexFlags bits mark attribs the VBO already supplies; FFP supplies the rest via u_Current* uniforms.
|
||||
// Skip the backend vertexAttrib call in either case.
|
||||
public static void flushDeferredVertexAttribs(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
|
||||
@@ -1605,6 +1621,11 @@ public class GLStateManager {
|
||||
ImmediateModeRecorder.setColor(red, green, blue, alpha);
|
||||
}
|
||||
dirtyColorAttrib = true;
|
||||
// Only set while an enchantment-glint span is active
|
||||
final GlintColorHandler glintHandler = GLSMHooks.glintColorHandler;
|
||||
if (glintHandler != null) {
|
||||
glintHandler.onColorChanged(red, green, blue, alpha);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -2158,6 +2179,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glBegin(int mode) {
|
||||
unit23TexCoordSetDuringDraw = false;
|
||||
if (DisplayListManager.isRecording()) {
|
||||
ImmediateModeRecorder.begin(mode);
|
||||
return;
|
||||
@@ -2233,70 +2255,99 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ByteBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_BYTE, MemoryUtilities.memAddress(indices), indices.remaining());
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices.remaining(), GL11.GL_UNSIGNED_BYTE, indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices.remaining(), GL11.GL_UNSIGNED_BYTE, indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, IntBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_INT, MemoryUtilities.memAddress(indices), (long) indices.remaining() << 2);
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ShortBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_SHORT, MemoryUtilities.memAddress(indices), (long) indices.remaining() << 1);
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, count, type, MemoryUtilities.memAddress(indices), indices.remaining());
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, count, type, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
@@ -2317,14 +2368,16 @@ public class GLStateManager {
|
||||
FeedbackManager.processDrawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
public static void glDrawBuffer(int mode) {
|
||||
@@ -2417,6 +2470,19 @@ public class GLStateManager {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
private static void ffpClientArrayPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
|
||||
if (VAOManager.isGenericPointerEnabled(index)) return;
|
||||
VAOManager.setAttribute(index, size, type, normalized, stride, offset, boundVBO);
|
||||
VAOManager.markConventional(index);
|
||||
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset);
|
||||
}
|
||||
|
||||
private static void ffpClientArrayPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
|
||||
if (VAOManager.isGenericPointerEnabled(index)) return;
|
||||
VAOManager.setAttribute(index, size, type, normalized, stride, pointer);
|
||||
VAOManager.markConventional(index);
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, IntBuffer pointer) {
|
||||
glVertexPointer(size, GL11.GL_INT, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
@@ -2426,40 +2492,40 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, FloatBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer);
|
||||
ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer_buffer_offset);
|
||||
ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int stride, FloatBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_FLOAT, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_FLOAT, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_DOUBLE, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_DOUBLE, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, boolean unsigned, int stride, ByteBuffer pointer) {
|
||||
final int type = unsigned ? GL11.GL_UNSIGNED_BYTE : GL11.GL_BYTE;
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer_buffer_offset);
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int stride, FloatBuffer pointer) {
|
||||
@@ -2479,33 +2545,33 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, ByteBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer);
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, FloatBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, ShortBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_SHORT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_SHORT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, IntBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_INT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_INT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_DOUBLE, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_DOUBLE, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, long pointer_buffer_offset) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer_buffer_offset);
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, type, false, stride, pointer);
|
||||
ffpClientArrayPointer(loc, size, type, false, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int stride, FloatBuffer pointer) {
|
||||
@@ -2527,31 +2593,31 @@ public class GLStateManager {
|
||||
public static void glTexCoordPointer(int size, int type, int stride, FloatBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(loc, size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, ShortBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_SHORT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(loc, size, GL11.GL_SHORT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, IntBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_INT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(loc, size, GL11.GL_INT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, DoubleBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(loc, size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, type, false, stride, pointer_buffer_offset);
|
||||
ffpClientArrayPointer(loc, size, type, false, stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glEnableClientState(int cap) {
|
||||
@@ -5325,6 +5391,9 @@ public class GLStateManager {
|
||||
} else {
|
||||
final int unit = target - GL13.GL_TEXTURE0;
|
||||
if (unit >= 2 && unit < 4) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
unit23TexCoordSetDuringDraw = true;
|
||||
}
|
||||
ShaderManager.setCurrentTexCoord(unit, s, t, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
@@ -5774,6 +5843,9 @@ public class GLStateManager {
|
||||
generateVertexShaderIfNeeded(program);
|
||||
}
|
||||
RENDER_BACKEND.linkProgram(program);
|
||||
if (ShaderManager.getInstance().isEnabled() && RENDER_BACKEND.getProgrami(program, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
|
||||
LOGGER.warn("Program {} failed to link: {}", program, RENDER_BACKEND.getProgramInfoLog(program));
|
||||
}
|
||||
CompatUniformManager.onLinkProgram(program);
|
||||
}
|
||||
|
||||
@@ -6030,15 +6102,15 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glUniform1(int location, IntBuffer values) {
|
||||
if (values.remaining() >= 1) RENDER_BACKEND.uniform1i(location, values.get(values.position()));
|
||||
RENDER_BACKEND.uniform1iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform2(int location, FloatBuffer values) {
|
||||
if (values.remaining() >= 2) RENDER_BACKEND.uniform2f(location, values.get(values.position()), values.get(values.position() + 1));
|
||||
RENDER_BACKEND.uniform2(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform2(int location, IntBuffer values) {
|
||||
if (values.remaining() >= 2) RENDER_BACKEND.uniform2i(location, values.get(values.position()), values.get(values.position() + 1));
|
||||
RENDER_BACKEND.uniform2iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform3(int location, FloatBuffer values) {
|
||||
@@ -6046,7 +6118,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glUniform3(int location, IntBuffer values) {
|
||||
if (values.remaining() >= 3) RENDER_BACKEND.uniform3i(location, values.get(values.position()), values.get(values.position() + 1), values.get(values.position() + 2));
|
||||
RENDER_BACKEND.uniform3iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform4(int location, FloatBuffer values) {
|
||||
@@ -6054,7 +6126,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glUniform4(int location, IntBuffer values) {
|
||||
if (values.remaining() >= 4) RENDER_BACKEND.uniform4i(location, values.get(values.position()), values.get(values.position() + 1), values.get(values.position() + 2), values.get(values.position() + 3));
|
||||
RENDER_BACKEND.uniform4iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniformMatrix2(int location, boolean transpose, FloatBuffer matrices) {
|
||||
|
||||
@@ -822,6 +822,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
GL20.glUniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform2(int location, FloatBuffer value) {
|
||||
GL20.glUniform2(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform3(int location, FloatBuffer value) {
|
||||
GL20.glUniform3(location, value);
|
||||
@@ -832,6 +837,26 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
GL20.glUniform4(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform1iv(int location, IntBuffer value) {
|
||||
GL20.glUniform1(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform2iv(int location, IntBuffer value) {
|
||||
GL20.glUniform2(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform3iv(int location, IntBuffer value) {
|
||||
GL20.glUniform3(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform4iv(int location, IntBuffer value) {
|
||||
GL20.glUniform4(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniformMatrix3(int location, boolean transpose, FloatBuffer value) {
|
||||
GL20.glUniformMatrix3(location, transpose, value);
|
||||
|
||||
@@ -201,8 +201,13 @@ public abstract class RenderBackend {
|
||||
public abstract void uniform3i(int location, int v0, int v1, int v2);
|
||||
public abstract void uniform4f(int location, float v0, float v1, float v2, float v3);
|
||||
public abstract void uniform4i(int location, int v0, int v1, int v2, int v3);
|
||||
public abstract void uniform2(int location, FloatBuffer value);
|
||||
public abstract void uniform3(int location, FloatBuffer value);
|
||||
public abstract void uniform4(int location, FloatBuffer value);
|
||||
public abstract void uniform1iv(int location, IntBuffer value);
|
||||
public abstract void uniform2iv(int location, IntBuffer value);
|
||||
public abstract void uniform3iv(int location, IntBuffer value);
|
||||
public abstract void uniform4iv(int location, IntBuffer value);
|
||||
public abstract void uniformMatrix3(int location, boolean transpose, FloatBuffer value);
|
||||
public abstract void uniformMatrix4(int location, boolean transpose, FloatBuffer value);
|
||||
public void uniformMatrix2(int location, boolean transpose, FloatBuffer matrices) {}
|
||||
|
||||
@@ -135,7 +135,8 @@ public final class FragmentKey {
|
||||
int highestEnabled = -1;
|
||||
long unit0Bits = 0;
|
||||
for (int i = 0; i < MAX_UNITS; i++) {
|
||||
final boolean texEnabled = GLStateManager.getTextures().getTextureUnitStates(i).isEnabled();
|
||||
// Per Mesa - an enabled unit with no complete texture bound counts as disabled.
|
||||
final boolean texEnabled = GLStateManager.getTextures().getTextureUnitStates(i).isEnabled() && GLStateManager.getTextures().getTextureUnitBindings(i).getBinding() != 0;
|
||||
if (texEnabled) highestEnabled = i;
|
||||
|
||||
final long unitBits = packUnit(i, texEnabled);
|
||||
|
||||
@@ -78,7 +78,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;
|
||||
sb.append(" vec4 tex").append(i).append("Color = texture(u_Sampler").append(i).append(", v_TexCoord").append(i).append(".st);\n");
|
||||
sb.append(" vec4 tex").append(i).append("Color = textureProj(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ public final class VAOManager {
|
||||
a.offset = offset;
|
||||
a.vboId = vboId;
|
||||
a.clientPointer = null;
|
||||
a.genericPointer = true;
|
||||
if (wasClient && a.enabled) {
|
||||
clientSideEnabledCount--;
|
||||
}
|
||||
@@ -101,11 +102,24 @@ public final class VAOManager {
|
||||
a.offset = 0;
|
||||
a.vboId = 0;
|
||||
a.clientPointer = pointer;
|
||||
a.genericPointer = true;
|
||||
if (!wasClient && a.enabled) {
|
||||
clientSideEnabledCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isGenericPointerEnabled(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return false;
|
||||
final Attrib a = currentAttribs[index];
|
||||
return a != null && a.genericPointer && a.enabled;
|
||||
}
|
||||
|
||||
public static void markConventional(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = currentAttribs[index];
|
||||
if (a != null) a.genericPointer = false;
|
||||
}
|
||||
|
||||
public static void setEnabled(int index, boolean enabled) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = getAttrib(index);
|
||||
@@ -239,6 +253,7 @@ public final class VAOManager {
|
||||
public long offset;
|
||||
public int vboId;
|
||||
public ByteBuffer clientPointer;
|
||||
public boolean genericPointer;
|
||||
|
||||
public boolean isClientSide() {
|
||||
return enabled && clientPointer != null;
|
||||
@@ -253,6 +268,7 @@ public final class VAOManager {
|
||||
offset = 0;
|
||||
vboId = 0;
|
||||
clientPointer = null;
|
||||
genericPointer = false;
|
||||
}
|
||||
|
||||
public int effectiveStride() {
|
||||
|
||||
@@ -49,6 +49,7 @@ public final class VertexKey {
|
||||
private static final int BIT_WIDE_LINE = 36;
|
||||
// Per-unit texmat enable: bits 37-40 (unit 0..3). Unit 1 is the lightmap matrix.
|
||||
private static final int BIT_UNIT_TEXMAT_BASE = 37;
|
||||
private static final int BIT_UNIT23_UV_FROM_UNIT0 = 41;
|
||||
|
||||
public static final int TG_NONE = 0;
|
||||
public static final int TG_OBJ_LINEAR = 1;
|
||||
@@ -85,6 +86,7 @@ public final class VertexKey {
|
||||
public boolean hasVertexNormal() { return bit(BIT_HAS_VERTEX_NORMAL); }
|
||||
public boolean hasVertexTexCoord() { return bit(BIT_HAS_VERTEX_TEX); }
|
||||
public boolean hasVertexLightmap() { return bit(BIT_HAS_VERTEX_LIGHTMAP); }
|
||||
public boolean unit23UvFromUnit0() { return bit(BIT_UNIT23_UV_FROM_UNIT0); }
|
||||
/** Color material mode (CM_AMBIENT, CM_DIFFUSE, CM_SPECULAR, CM_EMISSION, CM_AMBIENT_AND_DIFFUSE). Only meaningful when colorMaterialEnabled(). */
|
||||
public int colorMaterialMode() { return (int)((packed >> BIT_COLOR_MAT_MODE) & 0x7); }
|
||||
|
||||
@@ -213,6 +215,10 @@ public final class VertexKey {
|
||||
if (hasTexCoord) bits |= (1L << BIT_HAS_VERTEX_TEX);
|
||||
if (hasLightmap) bits |= (1L << BIT_HAS_VERTEX_LIGHTMAP);
|
||||
|
||||
if (GLStateManager.consumeUnit23TexCoordSetDuringDraw() && hasTexCoord) {
|
||||
bits |= (1L << BIT_UNIT23_UV_FROM_UNIT0);
|
||||
}
|
||||
|
||||
if (GLStateManager.anyClipPlaneEnabled()) {
|
||||
bits |= (1L << BIT_CLIP_PLANES);
|
||||
}
|
||||
|
||||
@@ -106,8 +106,8 @@ public final class VertexShaderGenerator {
|
||||
if (key.unitTexCoordEnabled(0) && !key.hasVertexTexCoord()) {
|
||||
sb.append("uniform vec4 u_CurrentTexCoord0;\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("uniform vec4 u_CurrentTexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("uniform vec4 u_CurrentTexCoord3;\n");
|
||||
if (key.unitTexCoordEnabled(2) && !key.unit23UvFromUnit0()) sb.append("uniform vec4 u_CurrentTexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3) && !key.unit23UvFromUnit0()) sb.append("uniform vec4 u_CurrentTexCoord3;\n");
|
||||
|
||||
if (key.lightmapEnabled() && !key.hasVertexLightmap()) {
|
||||
sb.append("uniform vec2 u_CurrentLightmapCoord;\n");
|
||||
@@ -363,8 +363,10 @@ public final class VertexShaderGenerator {
|
||||
|
||||
for (int i = 2; i < VertexKey.MAX_UNITS; i++) {
|
||||
if (!key.unitTexCoordEnabled(i)) continue;
|
||||
final String src = "u_CurrentTexCoord" + i;
|
||||
if (key.unitTexMatEnabled(i)) {
|
||||
final String src = key.unit23UvFromUnit0()
|
||||
? "vec4(a_TexCoord0, 0.0, 1.0)"
|
||||
: "u_CurrentTexCoord" + i;
|
||||
if (!key.unit23UvFromUnit0() && key.unitTexMatEnabled(i)) {
|
||||
sb.append(" v_TexCoord").append(i).append(" = u_TextureMatrix").append(i).append(" * ").append(src).append(";\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord").append(i).append(" = ").append(src).append(";\n");
|
||||
@@ -383,7 +385,7 @@ public final class VertexShaderGenerator {
|
||||
|
||||
emitTexGenComponent(sb, key.texGenModeS(), "s", "S", key);
|
||||
emitTexGenComponent(sb, key.texGenModeT(), "t", "T", key);
|
||||
emitTexGenComponent(sb, key.texGenModeR(), "r", "R", key);
|
||||
emitTexGenComponent(sb, key.texGenModeR(), "p", "R", key);
|
||||
emitTexGenComponent(sb, key.texGenModeQ(), "q", "Q", key);
|
||||
|
||||
// Always apply texture matrix when texgen is active (forced on in VertexKey)
|
||||
|
||||
@@ -8,6 +8,7 @@ public final class GLSMHooks {
|
||||
public static DeferredBlendHandler blendHandler;
|
||||
public static DeferredAlphaHandler alphaHandler;
|
||||
public static DeferredDepthColorHandler depthColorHandler;
|
||||
public static GlintColorHandler glintColorHandler;
|
||||
|
||||
// EventBus instances
|
||||
public static final EventBus<TextureBindEvent> TEXTURE_BIND = EventBus.create(TextureBindEvent.class);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface GlintColorHandler {
|
||||
void onColorChanged(float red, float green, float blue, float alpha);
|
||||
}
|
||||
+53
-33
@@ -9,8 +9,6 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetLong;
|
||||
@@ -52,20 +50,14 @@ public final class IndexedDrawCapture {
|
||||
return freed;
|
||||
}
|
||||
|
||||
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
|
||||
|
||||
private static void warnOnce(String key, String fmt, Object... args) {
|
||||
if (WARN_ONCE.add(key)) GLStateManager.LOGGER.warn(fmt, args);
|
||||
}
|
||||
|
||||
public static IndexedDrawCapture create(int mode, int indicesCount, int srcIndexType, long indicesOffset, int eboId) {
|
||||
if (indicesCount == 0) return null;
|
||||
if (eboId == 0) {
|
||||
warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
|
||||
GLStateManager.warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
|
||||
return null;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
|
||||
warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int srcIndexSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
|
||||
@@ -74,29 +66,53 @@ public final class IndexedDrawCapture {
|
||||
final int prevEBO = GLStateManager.getBoundEBO();
|
||||
final int prevVBO = GLStateManager.getBoundVBO();
|
||||
ByteBuffer eboSrc = null;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (eboBufferSize <= 0) {
|
||||
GLStateManager.warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
|
||||
return null;
|
||||
}
|
||||
final long eboReadSize = (long) indicesCount * srcIndexSize;
|
||||
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
|
||||
GLStateManager.warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
|
||||
return null;
|
||||
}
|
||||
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
|
||||
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
|
||||
return bake(mode, indicesCount, srcIndexType, MemoryUtilities.memAddress0(eboSrc));
|
||||
} finally {
|
||||
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
}
|
||||
}
|
||||
|
||||
public static IndexedDrawCapture createFromClientIndices(int mode, int indicesCount, int srcIndexType, long indexSrcAddr, long srcByteLength) {
|
||||
if (indicesCount == 0) return null;
|
||||
if (indexSrcAddr == 0L) return null;
|
||||
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 - skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int idxSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
|
||||
if (idxSize <= 0) return null;
|
||||
if ((long) indicesCount * idxSize > srcByteLength) {
|
||||
GLStateManager.warnOnce("client-idx-range", "[IndexedDrawCapture] index count {} ({} bytes) exceeds client buffer {} bytes - skipping", indicesCount, (long) indicesCount * idxSize, srcByteLength);
|
||||
return null;
|
||||
}
|
||||
return bake(mode, indicesCount, srcIndexType, indexSrcAddr);
|
||||
}
|
||||
|
||||
private static IndexedDrawCapture bake(int mode, int indicesCount, int srcIndexType, long indexSrcAddr) {
|
||||
AttribSnapshot snap = null;
|
||||
ByteBuffer vertexData = null;
|
||||
ByteBuffer indexData = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (eboBufferSize <= 0) {
|
||||
warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
|
||||
return null;
|
||||
}
|
||||
final long eboReadSize = (long) indicesCount * srcIndexSize;
|
||||
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
|
||||
warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
|
||||
return null;
|
||||
}
|
||||
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
|
||||
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
|
||||
final long eboSrcAddr = MemoryUtilities.memAddress0(eboSrc);
|
||||
|
||||
final long minMax = QuadConverter.scanMinMaxIndex(eboSrcAddr, srcIndexType, indicesCount);
|
||||
final long minMax = QuadConverter.scanMinMaxIndex(indexSrcAddr, srcIndexType, indicesCount);
|
||||
if (minMax == -1L) {
|
||||
warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
|
||||
GLStateManager.warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
|
||||
return null;
|
||||
}
|
||||
final int minVtx = (int) (minMax & 0xFFFFFFFFL);
|
||||
@@ -110,7 +126,7 @@ public final class IndexedDrawCapture {
|
||||
if (snap.get(i) != null) enabledCount++;
|
||||
}
|
||||
if (enabledCount == 0) {
|
||||
warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
|
||||
GLStateManager.warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
|
||||
return null;
|
||||
}
|
||||
final int[] locations = new int[enabledCount];
|
||||
@@ -137,6 +153,13 @@ public final class IndexedDrawCapture {
|
||||
for (int a = 0; a < enabledCount; a++) {
|
||||
final AttribSnapshot.AttribDesc d = snap.get(locations[a]);
|
||||
final int relOffset = (int) (d.offset() - d.readBufferBaseOffset());
|
||||
|
||||
final long readStart = relOffset + (long) minVtx * d.effectiveStride();
|
||||
final long readEnd = relOffset + (long) maxVtx * d.effectiveStride() + (long) d.size() * d.typeSizeBytes();
|
||||
if (readStart < 0 || readEnd > d.readBuffer().capacity()) {
|
||||
GLStateManager.warnOnce("vtx-oob", "[IndexedDrawCapture] attrib {} read extent {} exceeds snapshot {} bytes - skipping", d.location(), readEnd, d.readBuffer().capacity());
|
||||
return null;
|
||||
}
|
||||
if (a == 0) {
|
||||
sharedSrcBuffer = d.readBuffer();
|
||||
sharedSrcStride = d.effectiveStride();
|
||||
@@ -169,12 +192,12 @@ public final class IndexedDrawCapture {
|
||||
final int quadCount = indicesCount / 4;
|
||||
bakedIndexCount = quadCount * 6;
|
||||
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
|
||||
QuadConverter.triangulateQuads(eboSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
|
||||
QuadConverter.triangulateQuads(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
|
||||
bakedDrawMode = GL11.GL_TRIANGLES;
|
||||
} else {
|
||||
bakedIndexCount = indicesCount;
|
||||
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
|
||||
QuadConverter.widenIndices(eboSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
|
||||
QuadConverter.widenIndices(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
|
||||
bakedDrawMode = mode;
|
||||
}
|
||||
|
||||
@@ -184,14 +207,11 @@ public final class IndexedDrawCapture {
|
||||
success = true;
|
||||
return out;
|
||||
} finally {
|
||||
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
|
||||
if (snap != null) snap.free();
|
||||
if (!success) {
|
||||
if (vertexData != null) MemoryUtilities.memFree(vertexData);
|
||||
if (indexData != null) MemoryUtilities.memFree(indexData);
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
class ArrayUniformUploadGLTest {
|
||||
|
||||
private static int compile(int type, String src) {
|
||||
final int s = GL20.glCreateShader(type);
|
||||
GL20.glShaderSource(s, src);
|
||||
GL20.glCompileShader(s);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), () -> "shader compile failed: " + GL20.glGetShaderInfoLog(s, 4096));
|
||||
return s;
|
||||
}
|
||||
|
||||
private static int link(String vs, String fs) {
|
||||
final int v = compile(GL20.GL_VERTEX_SHADER, vs);
|
||||
final int f = compile(GL20.GL_FRAGMENT_SHADER, fs);
|
||||
final int p = GL20.glCreateProgram();
|
||||
GL20.glAttachShader(p, v);
|
||||
GL20.glAttachShader(p, f);
|
||||
GL20.glLinkProgram(p);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "link failed: " + GL20.glGetProgramInfoLog(p, 4096));
|
||||
GL20.glDeleteShader(v);
|
||||
GL20.glDeleteShader(f);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static final String VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n";
|
||||
|
||||
@Test
|
||||
void glUniform2FloatBufferUploadsWholeArray() {
|
||||
// Runtime index keeps the whole array active so element locations stay queryable.
|
||||
final String fs = "#version 330 core\n"
|
||||
+ "uniform vec2 cosmicuvs[10];\n"
|
||||
+ "uniform int idx;\n"
|
||||
+ "out vec4 o;\n"
|
||||
+ "void main(){ o = vec4(cosmicuvs[idx], cosmicuvs[(idx+1)%10]); }\n";
|
||||
final int p = link(VS, fs);
|
||||
try {
|
||||
GL20.glUseProgram(p);
|
||||
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(20);
|
||||
for (int i = 0; i < 20; i++) buf.put(i + 1.0f); // 1..20, all distinct, all non-zero
|
||||
buf.flip();
|
||||
|
||||
GLStateManager.glUniform2(GL20.glGetUniformLocation(p, "cosmicuvs"), buf);
|
||||
|
||||
final FloatBuffer read = BufferUtils.createFloatBuffer(2);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
final int loc = GL20.glGetUniformLocation(p, "cosmicuvs[" + i + "]");
|
||||
assertTrue(loc >= 0, "element " + i + " optimized out");
|
||||
read.clear();
|
||||
GL20.glGetUniform(p, loc, read);
|
||||
assertEquals(2 * i + 1.0f, read.get(0), 0.0f, "cosmicuvs[" + i + "].x");
|
||||
assertEquals(2 * i + 2.0f, read.get(1), 0.0f, "cosmicuvs[" + i + "].y");
|
||||
}
|
||||
} finally {
|
||||
GL20.glUseProgram(0);
|
||||
GL20.glDeleteProgram(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void glUniform1IntBufferUploadsWholeArray() {
|
||||
final String fs = "#version 330 core\n"
|
||||
+ "uniform int vals[4];\n"
|
||||
+ "uniform int idx;\n"
|
||||
+ "out vec4 o;\n"
|
||||
+ "void main(){ o = vec4(float(vals[idx])); }\n";
|
||||
final int p = link(VS, fs);
|
||||
try {
|
||||
GL20.glUseProgram(p);
|
||||
|
||||
final IntBuffer buf = BufferUtils.createIntBuffer(4);
|
||||
buf.put(11).put(22).put(33).put(44).flip();
|
||||
GLStateManager.glUniform1(GL20.glGetUniformLocation(p, "vals"), buf);
|
||||
|
||||
final IntBuffer read = BufferUtils.createIntBuffer(1);
|
||||
final int[] expect = {11, 22, 33, 44};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final int loc = GL20.glGetUniformLocation(p, "vals[" + i + "]");
|
||||
assertTrue(loc >= 0, "element " + i + " optimized out");
|
||||
read.clear();
|
||||
GL20.glGetUniform(p, loc, read);
|
||||
assertEquals(expect[i], read.get(0), "vals[" + i + "]");
|
||||
}
|
||||
} finally {
|
||||
GL20.glUseProgram(0);
|
||||
GL20.glDeleteProgram(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -613,4 +615,239 @@ class CompatShaderTransformerTest {
|
||||
assertFalse(result.matches("(?s).*\\bnew\\b(?!\\s*\\().*"), "'new' as identifier must be renamed in core profile output\n\n" + result);
|
||||
assertTrue(result.contains("angelica_renamed_new"), "'new' should be renamed to angelica_renamed_new\n\n" + result);
|
||||
}
|
||||
|
||||
private static String compact(String s) {
|
||||
return s.replaceAll("\\s+", "");
|
||||
}
|
||||
|
||||
private static String core(String src) {
|
||||
return compact(CompatShaderTransformer.transform(src, false));
|
||||
}
|
||||
|
||||
private static void assertLoc(String c, int loc, String decl) {
|
||||
assertTrue(c.contains(compact("layout(location=" + loc + ")in" + decl)), decl + " expected at location " + loc + "\n\n" + c);
|
||||
}
|
||||
|
||||
private static void assertNoLoc(String c, int loc, String decl) {
|
||||
assertFalse(c.contains(compact("layout(location=" + loc + ")in" + decl)), decl + " must not be at location " + loc + "\n\n" + c);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomPositionAttributePinnedToLocationZero() {
|
||||
// Betweenlands blshader fix
|
||||
String src = """
|
||||
#version 120
|
||||
attribute vec4 a_position;
|
||||
uniform mat4 u_projMat;
|
||||
uniform vec2 u_outSize;
|
||||
varying vec2 v_texCoord;
|
||||
void main(){
|
||||
gl_Position = u_projMat * vec4(a_position.xy, 0.0, 1.0);
|
||||
v_texCoord = a_position.xy / u_outSize;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertLoc(compact(result), 0, "vec4 a_position");
|
||||
assertTrue(result.contains("#version 330 core"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleLocationlessInputsGetSequentialLocations() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_pos;
|
||||
attribute vec2 a_uv;
|
||||
varying vec2 v_uv;
|
||||
void main() {
|
||||
v_uv = a_uv;
|
||||
gl_Position = vec4(a_pos.xy, 0.0, 1.0);
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_pos");
|
||||
assertLoc(c, 1, "vec2 a_uv");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomInputReservesAroundGlVertex() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_foo;
|
||||
void main() {
|
||||
gl_Position = ftransform() + a_foo;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 angelica_Vertex");
|
||||
assertLoc(c, 1, "vec4 a_foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomInputSkipsFfpSlots() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_foo;
|
||||
void main() {
|
||||
gl_Position = vec4(a_foo.xy, 0.0, 1.0) + gl_Color;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_foo");
|
||||
assertLoc(c, 1, "vec4 angelica_Color");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExplicitLowLocationReserved() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 0) in vec4 a_x;
|
||||
attribute vec4 a_y;
|
||||
void main() {
|
||||
gl_Position = a_x + a_y;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_x");
|
||||
assertLoc(c, 1, "vec4 a_y");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatrixInputSortedFirst() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_a;
|
||||
attribute mat4 a_m;
|
||||
void main() {
|
||||
gl_Position = a_m * a_a;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "mat4 a_m"); // 4-slot matrix sorted first
|
||||
assertLoc(c, 4, "vec4 a_a");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInputNamedLocationStillPinned() {
|
||||
assertLoc(core("""
|
||||
#version 120
|
||||
attribute vec4 a_location;
|
||||
void main() {
|
||||
gl_Position = vec4(a_location.xy, 0.0, 1.0);
|
||||
}
|
||||
"""), 0, "vec4 a_location");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInputWithExplicitLayoutLeftAlone() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 3) in vec4 a_data;
|
||||
attribute vec4 a_position;
|
||||
varying vec2 v_uv;
|
||||
void main() {
|
||||
v_uv = a_data.xy;
|
||||
gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_position"); // sole location-less input
|
||||
assertLoc(c, 3, "vec4 a_data"); // explicit location preserved
|
||||
assertNoLoc(c, 0, "vec4 a_data");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPinnedShaderRemainsWellFormed() {
|
||||
String src = """
|
||||
#version 120
|
||||
attribute vec4 a_position;
|
||||
void main() {
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_Position = gl_ModelViewMatrix * vec4(a_position.xyz, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
String c = compact(result);
|
||||
assertLoc(c, 0, "vec4 a_position");
|
||||
assertLoc(c, 1, "vec4 angelica_Color");
|
||||
assertTrue(c.contains("uniformmat4angelica_ModelViewMatrix"), "matrix uniform injected\n\n" + result);
|
||||
assertFalse(c.contains("gl_ModelViewMatrix"), "builtin rewritten (not fallback)\n\n" + result);
|
||||
|
||||
final int mainIdx = result.indexOf("void main");
|
||||
assertTrue(mainIdx > 0, result);
|
||||
assertTrue(result.indexOf("angelica_ModelViewMatrix") < mainIdx, "uniform before main\n\n" + result);
|
||||
assertTrue(result.indexOf("a_position") < mainIdx, "pinned input before main\n\n" + result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGlVertexShaderDoesNotPinCustomAttribute() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute float activelights;
|
||||
void main() {
|
||||
gl_Position = ftransform();
|
||||
float x = activelights;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 angelica_Vertex");
|
||||
assertNoLoc(c, 0, "float activelights");
|
||||
}
|
||||
|
||||
@ParameterizedTest // array on the declarator (vec4 a[2]) and on the type (vec4[2] a) must both round-trip
|
||||
@ValueSource(strings = {"attribute vec4 positions[2];", "attribute vec4[2] positions;"})
|
||||
void testArrayAttributePreservedEitherSyntax(String decl) {
|
||||
String c = core("""
|
||||
#version 120
|
||||
%s
|
||||
attribute vec4 a_tail;
|
||||
void main() {
|
||||
gl_Position = positions[0] + positions[1] + a_tail;
|
||||
}
|
||||
""".formatted(decl));
|
||||
assertLoc(c, 0, "vec4 positions[2]"); // 2-slot array pinned to 0..1
|
||||
assertLoc(c, 2, "vec4 a_tail");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiDeclaratorSplitIntoSeparateLocations() {
|
||||
String result = CompatShaderTransformer.transform("""
|
||||
#version 120
|
||||
attribute vec4 a, b;
|
||||
void main() {
|
||||
gl_Position = a + b;
|
||||
}
|
||||
""", false);
|
||||
String c = compact(result);
|
||||
assertLoc(c, 0, "vec4 a");
|
||||
assertLoc(c, 1, "vec4 b");
|
||||
assertFalse(result.matches("(?s).*\\battribute\\b.*"), "no deprecated 'attribute' keyword may survive\n\n" + result);
|
||||
assertTrue(result.contains("#version 330 core"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiDeclaratorWithMixedArray() {
|
||||
String result = CompatShaderTransformer.transform("""
|
||||
#version 120
|
||||
attribute vec4 a, b[2];
|
||||
void main() {
|
||||
gl_Position = a + b[0] + b[1];
|
||||
}
|
||||
""", false);
|
||||
String c = compact(result);
|
||||
// b takes 2 slots; sorted before the 1-slot a, so b -> 0..1, a -> 2.
|
||||
assertLoc(c, 0, "vec4 b[2]");
|
||||
assertLoc(c, 2, "vec4 a");
|
||||
assertFalse(result.matches("(?s).*\\battribute\\b.*"), "no deprecated 'attribute' keyword may survive\n\n" + result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHexExplicitLocationReserved() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 0x1) in vec4 a_x;
|
||||
attribute vec4 a_y;
|
||||
void main() {
|
||||
gl_Position = a_x + a_y;
|
||||
}
|
||||
""");
|
||||
assertTrue(c.contains("layout(location=0x1)invec4a_x"), "hex explicit location preserved\n\n" + c);
|
||||
assertNoLoc(c, 1, "vec4 a_y"); // must not collide with the hex-reserved slot
|
||||
assertLoc(c, 0, "vec4 a_y");
|
||||
}
|
||||
}
|
||||
+28
@@ -9,9 +9,11 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/** Tests GLSM display list behavior matches OpenGL spec. */
|
||||
@@ -257,4 +259,30 @@ class DisplayListCompilationGLSMTest {
|
||||
|
||||
GLStateManager.glEndList();
|
||||
}
|
||||
|
||||
@Test
|
||||
void glsm_unsupportedOpInList_abortsCleanly() {
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
assertTrue(DisplayListManager.isRecording(), "recording after glNewList");
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, () -> GLStateManager.glReadBuffer(GL11.GL_FRONT));
|
||||
|
||||
assertFalse(DisplayListManager.isRecording(), "recording state must be reset after an in-list throw");
|
||||
assertEquals(DisplayListManager.RecordMode.NONE, DisplayListManager.getRecordMode());
|
||||
|
||||
assertDoesNotThrow(() -> GLStateManager.glReadBuffer(GL11.GL_FRONT));
|
||||
assertDoesNotThrow(GLStateManager::glEndList);
|
||||
}
|
||||
|
||||
@Test
|
||||
void glsm_abortIfLeaked_recoversOpenList() {
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
assertTrue(DisplayListManager.isRecording());
|
||||
|
||||
DisplayListManager.abortIfLeaked();
|
||||
|
||||
assertFalse(DisplayListManager.isRecording(), "frame-begin backstop must clear a leaked open list");
|
||||
}
|
||||
}
|
||||
@@ -122,6 +122,68 @@ public class GLSM_VAO_UnitTest {
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenericAttributeSupersedesFfpConventional() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexAttribPointer(0, 1, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
|
||||
Assertions.assertEquals(1, VAOManager.get(0).size, "enabled generic attribute must supersede the FFP conventional array at the same location");
|
||||
Assertions.assertTrue(VAOManager.get(0).genericPointer, "location must remain generic-sourced");
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenericAttributeWinsRegardlessOfOrder() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
GLStateManager.glVertexAttribPointer(0, 1, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
Assertions.assertEquals(1, VAOManager.get(0).size, "generic attribute set after the FFP conventional array must still win");
|
||||
Assertions.assertTrue(VAOManager.get(0).genericPointer, "location must remain generic-sourced");
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFfpConventionalAppliesWithoutGenericAttribute() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
|
||||
Assertions.assertEquals(3, VAOManager.get(0).size, "plain FFP conventional array must apply when no generic attribute occupies the location");
|
||||
Assertions.assertFalse(VAOManager.get(0).genericPointer, "location must be conventional-sourced");
|
||||
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
private static void testClientStates() {
|
||||
verifyState(GL11.GL_VERTEX_ARRAY, false, "GL_VERTEX_ARRAY Default State");
|
||||
verifyState(GL11.GL_TEXTURE_COORD_ARRAY, false, "GL_TEXTURE_COORD_ARRAY Default State");
|
||||
|
||||
@@ -28,6 +28,7 @@ class FFPCombineShaderTest {
|
||||
|
||||
private final List<Integer> shadersToDelete = new ArrayList<>();
|
||||
private final List<Integer> programsToDelete = new ArrayList<>();
|
||||
private final List<Integer> texturesToDelete = new ArrayList<>();
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
@@ -37,15 +38,25 @@ class FFPCombineShaderTest {
|
||||
programsToDelete.clear();
|
||||
shadersToDelete.clear();
|
||||
|
||||
// Reset texture units to defaults
|
||||
// Reset texture units to defaults (unbind so a later test does not see a stale binding)
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + i);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.getTextures().getTexEnvState(i).reset();
|
||||
if (i > 0) {
|
||||
GLStateManager.getTextures().getTextureUnitStates(i).disable();
|
||||
}
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
for (int t : texturesToDelete) GL11.glDeleteTextures(t);
|
||||
texturesToDelete.clear();
|
||||
}
|
||||
|
||||
private int bindUnitTexture() {
|
||||
final int tex = GL11.glGenTextures();
|
||||
texturesToDelete.add(tex);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
return tex;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,6 +124,7 @@ class FFPCombineShaderTest {
|
||||
void testSingleUnitCombineCompiles() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_MODULATE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_MODULATE);
|
||||
@@ -169,6 +181,7 @@ class FFPCombineShaderTest {
|
||||
void testAllCombineFunctionsCompile(String name, int glFunc, int numArgs) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, glFunc);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
@@ -202,6 +215,7 @@ class FFPCombineShaderTest {
|
||||
void testCombineScopeDeclaresColorOutsideBlock() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_MODULATE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_MODULATE);
|
||||
@@ -258,6 +272,7 @@ class FFPCombineShaderTest {
|
||||
// Unit 0: COMBINE / SUBTRACT
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL13.GL_SUBTRACT);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
@@ -273,6 +288,7 @@ class FFPCombineShaderTest {
|
||||
// Unit 2: COMBINE / ADD
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_ADD);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
@@ -286,6 +302,7 @@ class FFPCombineShaderTest {
|
||||
// Unit 3: simple MODULATE
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE3);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
|
||||
|
||||
// Restore active unit
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCoreExtension;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
class FFPUnboundTextureUnitTest {
|
||||
|
||||
private final List<Integer> texturesToDelete = new ArrayList<>();
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + i);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
if (i > 0) GLStateManager.getTextures().getTextureUnitStates(i).disable();
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
for (int t : texturesToDelete) GL11.glDeleteTextures(t);
|
||||
texturesToDelete.clear();
|
||||
}
|
||||
|
||||
private void bindUnitTexture() {
|
||||
final int tex = GL11.glGenTextures();
|
||||
texturesToDelete.add(tex);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
}
|
||||
|
||||
@Test
|
||||
void enabledUnitWithoutTextureIsExcluded() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
GLStateManager.enableTexture(); // enabled, nothing bound
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE3);
|
||||
GLStateManager.enableTexture(); // enabled, nothing bound
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
final FragmentKey fk = FragmentKey.fromState();
|
||||
assertEquals(1, fk.nrEnabledUnits(), "units 2/3 have no texture bound -> excluded -> only unit 0");
|
||||
assertEquals(0b0001, fk.enabledUnitMask(), "only unit 0 in the key; enabled-but-unbound units 2/3 excluded");
|
||||
assertTrue(fk.unitEnabled(0), "unit 0 (texture bound) stays enabled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void enabledUnitsWithTextureAreCounted() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
final FragmentKey fk = FragmentKey.fromState();
|
||||
assertTrue(fk.unitEnabled(0));
|
||||
assertTrue(fk.unitEnabled(2), "unit 2 with a bound texture must be counted");
|
||||
assertEquals(3, fk.nrEnabledUnits(), "highest enabled is unit 2 -> nrEnabledUnits=3");
|
||||
}
|
||||
}
|
||||
+154
-1
@@ -9,6 +9,9 @@ import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -17,7 +20,10 @@ import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -25,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
|
||||
@@ -521,7 +528,153 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
"listBA: batch[1] must match the second-recorded layout");
|
||||
}
|
||||
|
||||
// === local helpers ===
|
||||
private static Stream<Arguments> clientIndexedDrawCases() {
|
||||
return Stream.of(
|
||||
arguments("byte indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3);
|
||||
idx.put(new byte[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("short indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ShortBuffer idx = BufferUtils.createShortBuffer(3);
|
||||
idx.put(new short[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("int indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(3);
|
||||
idx.put(new int[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("explicit count+type", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3 * 2).order(ByteOrder.nativeOrder());
|
||||
idx.putShort((short) 0).putShort((short) 1).putShort((short) 2).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("quads triangulate at record", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(4);
|
||||
idx.put(new int[]{0, 1, 2, 3}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_QUADS, idx);
|
||||
}, GL11.GL_TRIANGLES, 6),
|
||||
arguments("compile-and-execute fires live and bakes", GL11.GL_COMPILE_AND_EXECUTE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(3);
|
||||
idx.put(new int[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("clientIndexedDrawCases")
|
||||
void clientIndexedDraw_capturedAndWidenedToUnsignedInt(String name, int listMode, Runnable draw, int expectedMode, int expectedCount) {
|
||||
final int list = recordClientIndexDraw(listMode, draw);
|
||||
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
|
||||
assertNotNull(cmd);
|
||||
assertEquals(expectedMode, cmd.getDrawMode());
|
||||
assertEquals(expectedCount, cmd.getIndexCount());
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, cmd.getIndexType(), "client indices always widen to UNSIGNED_INT");
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientVerticesAndIndices_pointersSetDuringRecording_bake() {
|
||||
final float[][] verts = {{0f, 0f, 0f}, {1f, 0f, 0f}, {1f, 1f, 0f}, {0f, 1f, 0f}};
|
||||
final ByteBuffer clientVerts = BufferUtils.createByteBuffer(verts.length * 12).order(ByteOrder.nativeOrder());
|
||||
for (float[] v : verts) {
|
||||
clientVerts.putFloat(v[0]);
|
||||
clientVerts.putFloat(v[1]);
|
||||
clientVerts.putFloat(v[2]);
|
||||
}
|
||||
clientVerts.flip();
|
||||
final IntBuffer indices = BufferUtils.createIntBuffer(6);
|
||||
indices.put(new int[]{0, 1, 2, 2, 3, 0}).flip();
|
||||
|
||||
final int vao = newVao();
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
VAOManager.setAttribute(0, 3, GL11.GL_FLOAT, false, 0, clientVerts);
|
||||
VAOManager.enableAttribute(0);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, indices);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
|
||||
assertNotNull(cmd);
|
||||
assertEquals(GL11.GL_TRIANGLES, cmd.getDrawMode());
|
||||
assertEquals(6, cmd.getIndexCount());
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, cmd.getIndexType());
|
||||
|
||||
final ByteBuffer owned = readBufferBytes(GL15.GL_ARRAY_BUFFER, firstBatch(list).getSharedVBO(), verts.length * 12);
|
||||
for (int i = 0; i < verts.length; i++) {
|
||||
assertEquals(verts[i][0], owned.getFloat(i * 12), 0f, "pos.x v=" + i);
|
||||
assertEquals(verts[i][1], owned.getFloat(i * 12 + 4), 0f, "pos.y v=" + i);
|
||||
assertEquals(verts[i][2], owned.getFloat(i * 12 + 8), 0f, "pos.z v=" + i);
|
||||
}
|
||||
MemoryUtilities.memFree(owned);
|
||||
}
|
||||
|
||||
@Test
|
||||
void quadOffsetDraw_compileAndExecute_doesNotPauseRecordingForRestOfList() {
|
||||
final int vao = setupPosColorVao(defaultQuadVbo());
|
||||
final int quadEbo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
|
||||
final int triEbo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, quadEbo);
|
||||
GLStateManager.glDrawElements(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
assertTrue(DisplayListManager.isRecording(), "recording must resume after a GL_QUADS offset draw");
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, triEbo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
assertEquals(2, allBatchedCmds(list).size(), "both the QUADS draw and the following triangle draw must be recorded");
|
||||
}
|
||||
|
||||
private enum MalformedClientDraw { OVERSIZED_COUNT, INDEX_PAST_VERTEX_BUFFER }
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@EnumSource(MalformedClientDraw.class)
|
||||
void malformedClientDraw_skippedAndRecordingSurvives(MalformedClientDraw scenario) {
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
switch (scenario) {
|
||||
case OVERSIZED_COUNT -> {
|
||||
GLStateManager.glBindVertexArray(setupPosColorVao(defaultQuadVbo()));
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3 * 2).order(ByteOrder.nativeOrder());
|
||||
idx.putShort((short) 0).putShort((short) 1).putShort((short) 2).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 100, GL11.GL_UNSIGNED_SHORT, idx);
|
||||
}
|
||||
case INDEX_PAST_VERTEX_BUFFER -> {
|
||||
final int vbo = uploadPosColorVbo(new float[]{0f, 0f, 1f, 0f}, new byte[][]{{1, 1, 1, 1}, {2, 2, 2, 2}});
|
||||
GLStateManager.glBindVertexArray(setupPosColorVao(vbo));
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(4);
|
||||
idx.put(new int[]{0, 1, 2, 3}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}
|
||||
}
|
||||
assertTrue(DisplayListManager.isRecording(), "recording survives a skipped malformed draw");
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
assertNull(firstBatchedCmd(list), "malformed draw must be skipped, not captured or over-read");
|
||||
}
|
||||
|
||||
private int recordClientIndexDraw(int listMode, Runnable draw) {
|
||||
final int vao = setupPosColorVao(defaultQuadVbo());
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, listMode);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
draw.run();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void assertUintIndices(ByteBuffer ib, int... expected) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
|
||||
@@ -34,6 +34,10 @@ repositories {
|
||||
name = "GTNH Maven"
|
||||
url = uri("https://nexus.gtnewhorizons.com/repository/public/")
|
||||
}
|
||||
maven {
|
||||
name = "Minecraft Libraries"
|
||||
url = uri("https://libraries.minecraft.net")
|
||||
}
|
||||
maven {
|
||||
name = "Forge"
|
||||
url = uri("https://maven.minecraftforge.net/")
|
||||
|
||||
+25
@@ -866,6 +866,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
GL20C.glUniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform2(int location, FloatBuffer value) {
|
||||
GL20C.glUniform2fv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform3(int location, FloatBuffer value) {
|
||||
GL20C.glUniform3fv(location, value);
|
||||
@@ -876,6 +881,26 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
GL20C.glUniform4fv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform1iv(int location, IntBuffer value) {
|
||||
GL20C.glUniform1iv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform2iv(int location, IntBuffer value) {
|
||||
GL20C.glUniform2iv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform3iv(int location, IntBuffer value) {
|
||||
GL20C.glUniform3iv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform4iv(int location, IntBuffer value) {
|
||||
GL20C.glUniform4iv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniformMatrix3(int location, boolean transpose, FloatBuffer value) {
|
||||
GL20C.glUniformMatrix3fv(location, transpose, value);
|
||||
|
||||
@@ -16,7 +16,7 @@ import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
|
||||
name = "Angelica",
|
||||
version = Tags.VERSION,
|
||||
dependencies = """
|
||||
required-after:gtnhlib@[0.8.21,);\
|
||||
required-after:gtnhlib@[0.11.19,);\
|
||||
before:lwjgl3ify@[1.5.3,);\
|
||||
after:hodgepodge@[2.4.4,);\
|
||||
after:CodeChickenCore@[1.2.0,);\
|
||||
|
||||
@@ -30,6 +30,7 @@ public class ModStatus {
|
||||
public static boolean isCosmeticArmorReworkedLoaded;
|
||||
public static boolean isFluidLoggedLoaded;
|
||||
public static boolean isBOPLoaded;
|
||||
public static boolean isEtFuturumLoaded;
|
||||
|
||||
public static void preInit() {
|
||||
if (Loader.isModLoaded("backhand")) {
|
||||
@@ -49,6 +50,7 @@ public class ModStatus {
|
||||
isBaublesLoaded = Loader.isModLoaded("Baubles");
|
||||
isCosmeticArmorReworkedLoaded = Loader.isModLoaded("cosmeticarmorreworked");
|
||||
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
|
||||
isEtFuturumLoaded = Loader.isModLoaded("etfuturum");
|
||||
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
isBOPLoaded = Loader.isModLoaded("BiomesOPlenty");
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.gtnewhorizons.angelica.compat.etfuturum;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.relauncher.ReflectionHelper;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER;
|
||||
|
||||
public final class EtFuturumCompat {
|
||||
private static final String IELYTRA_PLAYER = "ganymedes01.etfuturum.api.elytra.IElytraPlayer";
|
||||
|
||||
@Getter
|
||||
private static boolean loaded;
|
||||
private static Class<?> elytraPlayerClass;
|
||||
private static final MethodHandle isElytraFlyingHandle;
|
||||
|
||||
static {
|
||||
MethodHandle handle = null;
|
||||
try {
|
||||
if (Loader.isModLoaded("etfuturum")) {
|
||||
elytraPlayerClass = ReflectionHelper.getClass(EtFuturumCompat.class.getClassLoader(), IELYTRA_PLAYER);
|
||||
handle = MethodHandles.lookup().findVirtual(elytraPlayerClass, "etfu$isElytraFlying",
|
||||
MethodType.methodType(boolean.class));
|
||||
loaded = true;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Failed to load Et Futurum Requiem elytra compat", t);
|
||||
loaded = false;
|
||||
}
|
||||
isElytraFlyingHandle = handle;
|
||||
}
|
||||
|
||||
private EtFuturumCompat() {
|
||||
}
|
||||
|
||||
public static boolean isElytraFlying(EntityPlayer player) {
|
||||
if (!loaded || isElytraFlyingHandle == null || !elytraPlayerClass.isInstance(player)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return (boolean) isElytraFlyingHandle.invoke(player);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,8 +357,13 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean disableErrorChecks;
|
||||
|
||||
@Config.Comment("Fixes various issues with entity overlays, such as z-fighting, damage animations and eyes.")
|
||||
@Config.Comment("Fixes various issues with entity overlays, such as z-fighting and eyes.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean entityOverlayFixes;
|
||||
|
||||
@Config.Comment("Swap the vanilla damage overlay with one similar to modern. Fixes specific issues with z-fighting.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean entityModernDamageOverlay;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.gtnewhorizons.angelica.event;
|
||||
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public class ClientEvent extends MutableEvent {
|
||||
public static final EventBus<ClientEvent> BUS = EventBus.create(ClientEvent.class);
|
||||
|
||||
public final ClientEventType event;
|
||||
public final EntityRenderer renderer;
|
||||
public final float partialTicks;
|
||||
|
||||
private ClientEvent(ClientEventType event) {
|
||||
this.event = event;
|
||||
this.renderer = null;
|
||||
this.partialTicks = 0;
|
||||
}
|
||||
|
||||
private ClientEvent(ClientEventType event, EntityRenderer renderer, float partialTicks) {
|
||||
this.event = event;
|
||||
this.renderer = renderer;
|
||||
this.partialTicks = partialTicks;
|
||||
}
|
||||
|
||||
public static void post(ClientEventType event, EntityRenderer renderer, float partialTicks) {
|
||||
if (!BUS.hasListeners()) return;
|
||||
BUS.post(new ClientEvent(event, renderer, partialTicks));
|
||||
}
|
||||
|
||||
public static void post(ClientEventType event) {
|
||||
if (!BUS.hasListeners()) return;
|
||||
BUS.post(new ClientEvent(event));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.event;
|
||||
|
||||
public enum ClientEventType {
|
||||
PRE_RENDER_GUI,
|
||||
PRE_RENDER_HUD,
|
||||
POST_RENDER_HUD,
|
||||
ON_TICK,
|
||||
}
|
||||
@@ -6,11 +6,12 @@ import com.gtnewhorizon.gtnhlib.client.renderer.postprocessing.CustomFramebuffer
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.postprocessing.SharedDepthFramebuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VertexBufferType;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vbo.IVertexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.holoinventory.HoloInventoryReflectionCompat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.event.ClientEvent;
|
||||
import com.gtnewhorizons.angelica.event.ClientEventType;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.GuiIngameAccessor;
|
||||
@@ -65,6 +66,8 @@ public class HUDCaching {
|
||||
}
|
||||
|
||||
public static void renderCachedHud(EntityRenderer renderer, GuiIngame ingame, float partialTicks, boolean hasScreen, int mouseX, int mouseY) {
|
||||
ClientEvent.post(ClientEventType.PRE_RENDER_GUI, renderer, partialTicks);
|
||||
ClientEvent.post(ClientEventType.PRE_RENDER_HUD, renderer, partialTicks);
|
||||
if (ModStatus.isXaerosMinimapLoaded && ingame instanceof GuiIngameForge) {
|
||||
// this used to be called by asming into renderGameOverlay, but we removed it
|
||||
XaeroMinimapCore.beforeIngameGuiRender(partialTicks);
|
||||
@@ -78,6 +81,7 @@ public class HUDCaching {
|
||||
|
||||
if (!AngelicaConfig.hudCachingActive || mc.displayWidth < 16 || mc.displayHeight < 16) {
|
||||
ingame.renderGameOverlay(partialTicks, hasScreen, mouseX, mouseY);
|
||||
ClientEvent.post(ClientEventType.POST_RENDER_HUD, renderer, partialTicks);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,6 +174,8 @@ public class HUDCaching {
|
||||
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
mc.getTextureManager().bindTexture(Gui.icons);
|
||||
|
||||
ClientEvent.post(ClientEventType.POST_RENDER_HUD, renderer, partialTicks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -120,7 +120,10 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
"Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/com/gtnewhorizon/gtnhlib/client/renderer/VertexCallbackManager.class",
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.10.0 or newer! Download: https://modrinth.com/mod/gtnhlib")
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.10.0 or newer! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/it/unimi/dsi/fastutil/objects/ObjectBooleanBiConsumer.class",
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.11.19 or newer (fastutil 8.5.18+)! Download: https://modrinth.com/mod/gtnhlib")
|
||||
));
|
||||
} catch (RuntimeException ex) {
|
||||
fatalDependencyError(ex.getMessage());
|
||||
|
||||
@@ -64,7 +64,15 @@ public enum Mixins implements IMixins {
|
||||
.addExcludedMod(TargetedMod.CUSTOM_PLAYER_MODELS)
|
||||
.addClientMixins(
|
||||
"angelica.bugfixes.MixinRendererLivingEntity_EyeDepth"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_OverlayTint"
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_DAMAGE_OVERLAY(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.entityModernDamageOverlay)
|
||||
.addExcludedMod(TargetedMod.CUSTOM_PLAYER_MODELS)
|
||||
.addClientMixins(
|
||||
"angelica.bugfixes.MixinRendererLivingEntity_OverlayTint"
|
||||
)
|
||||
),
|
||||
|
||||
@@ -290,6 +298,7 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinRenderEndPortal"
|
||||
, "shaders.MixinTileEntityRendererDispatcher"
|
||||
, "shaders.MixinGlProgram"
|
||||
, "shaders.MixinTextureManager_ReloadCount"
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import jss.notfine.core.Settings;
|
||||
import jss.notfine.gui.options.named.GraphicsQualityOff;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.shaderpack.CloudSetting;
|
||||
import net.irisshaders.iris.api.v0.IrisApi;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
@@ -106,6 +109,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
private boolean cachedEmitTopFace, cachedEmitBottomFace, cachedCameraInsideCloud;
|
||||
private int cachedRenderRadius = -1;
|
||||
private float cachedCloudInteriorHeight = Float.NaN;
|
||||
private boolean geomBuilt;
|
||||
|
||||
private int cloudMode = -1, renderDistance = -1, cloudElevation = -1, scaleMult = -1;
|
||||
|
||||
@@ -193,6 +197,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
cachedEdgesGeneration = Integer.MIN_VALUE;
|
||||
cachedCloudInteriorHeight = Float.NaN;
|
||||
cachedVertexCount = 0;
|
||||
geomBuilt = false;
|
||||
}
|
||||
|
||||
private void initProgram() {
|
||||
@@ -233,6 +238,9 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
if (mc.theWorld == null || mc.renderViewEntity == null) return false;
|
||||
if (cloudMode != 2 || scaleMult <= 0) return false;
|
||||
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pipeline != null && pipeline.getCloudSetting() == CloudSetting.OFF) return false;
|
||||
|
||||
boolean textureSetupDone = false;
|
||||
if (edges == null) {
|
||||
if (mc.renderEngine == null) return false;
|
||||
@@ -271,7 +279,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
|
||||
final int renderRadius = (int) Math.ceil(renderDistance * 64.0f / (CELLS_PER_CHUNK * cloudInteriorWidth));
|
||||
|
||||
final boolean anchorInRange = vao != null
|
||||
final boolean anchorInRange = geomBuilt
|
||||
&& Math.abs(anchorX - anchorCellX) <= anchorMarginCells
|
||||
&& Math.abs(anchorZ - anchorCellZ) <= anchorMarginCells;
|
||||
final boolean geomCacheValid = anchorInRange
|
||||
@@ -294,6 +302,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
cachedCameraInsideCloud = cameraInsideCloud;
|
||||
cachedRenderRadius = renderRadius;
|
||||
cachedCloudInteriorHeight = cloudInteriorHeight;
|
||||
geomBuilt = true;
|
||||
}
|
||||
|
||||
if (vao == null || cachedVertexCount == 0) return true;
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.gtnewhorizons.angelica.shadercompat;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import net.irisshaders.iris.api.v0.IrisApi;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Maybe overcomplicated, but it's required to bridge the gap between the modern glint texture and white texture 1.7.10
|
||||
* has. Some shaders are able to render the glint just fine as they seem to have some amount of support for older
|
||||
* Minecraft, but others like AstraLex do *not* render correctly. It expects the color to be within the texture.
|
||||
* This might introduce some oddities that I'm not seeing, but for now this appears to fix the issue.
|
||||
*/
|
||||
public final class ShaderGlint {
|
||||
|
||||
private static final ResourceLocation GLINT_MASK = new ResourceLocation("textures/misc/enchanted_item_glint.png");
|
||||
private static final int MAX_CACHE = 64;
|
||||
|
||||
private static boolean maskLoaded;
|
||||
private static boolean maskFailed;
|
||||
private static int maskW;
|
||||
private static int maskH;
|
||||
private static int[] maskLum;
|
||||
|
||||
private static final Int2IntLinkedOpenHashMap colorToTexture = new Int2IntLinkedOpenHashMap();
|
||||
static {
|
||||
colorToTexture.defaultReturnValue(0);
|
||||
}
|
||||
|
||||
private static final GlintColorHandler COLOR_HANDLER = ShaderGlint::onColorChanged;
|
||||
|
||||
private static boolean injecting;
|
||||
private static boolean swapped;
|
||||
private static int depth;
|
||||
|
||||
private static int prevTexture;
|
||||
|
||||
private static ByteBuffer tintBuffer;
|
||||
|
||||
private ShaderGlint() {}
|
||||
|
||||
public static void beginGlint() {
|
||||
if (ShadowRenderer.ACTIVE || !IrisApi.getInstance().isShaderPackInUse()) return;
|
||||
|
||||
// If a glint span is already active, keep the outer span's saved state and
|
||||
// let the nested render ride on its handler.
|
||||
if (depth++ > 0) return;
|
||||
|
||||
swapped = false;
|
||||
injecting = false;
|
||||
|
||||
// Save the texture bound before the glint section so endGlint can put it back if we swap
|
||||
prevTexture = GLStateManager.getBoundTextureForServerState(0);
|
||||
|
||||
// Observe vertex-color changes for the rest of the glint section
|
||||
GLSMHooks.glintColorHandler = COLOR_HANDLER;
|
||||
}
|
||||
|
||||
public static void endGlint() {
|
||||
if (depth == 0 || --depth > 0) return;
|
||||
if (GLSMHooks.glintColorHandler != COLOR_HANDLER) return;
|
||||
GLSMHooks.glintColorHandler = null;
|
||||
|
||||
if (swapped) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, prevTexture);
|
||||
}
|
||||
swapped = false;
|
||||
injecting = false;
|
||||
}
|
||||
|
||||
private static void onColorChanged(float red, float green, float blue, float alpha) {
|
||||
if (injecting) return;
|
||||
|
||||
final int texture = getTintedTexture(red, green, blue);
|
||||
if (texture <= 0) return;
|
||||
|
||||
injecting = true;
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glColor4f(1.0F, 1.0F, 1.0F, alpha);
|
||||
injecting = false;
|
||||
swapped = true;
|
||||
}
|
||||
|
||||
private static int getTintedTexture(float r, float g, float b) {
|
||||
ensureMaskLoaded();
|
||||
if (!maskLoaded) return -1;
|
||||
|
||||
final int ri = clamp8(r);
|
||||
final int gi = clamp8(g);
|
||||
final int bi = clamp8(b);
|
||||
final int key = (ri << 16) | (gi << 8) | bi;
|
||||
|
||||
final int cached = colorToTexture.getAndMoveToLast(key);
|
||||
if (cached != 0) return cached;
|
||||
|
||||
if (colorToTexture.size() >= MAX_CACHE) {
|
||||
GLStateManager.glDeleteTextures(colorToTexture.remove(colorToTexture.firstIntKey()));
|
||||
}
|
||||
|
||||
final int texture = uploadTinted(ri / 255.0F, gi / 255.0F, bi / 255.0F);
|
||||
colorToTexture.putAndMoveToLast(key, texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static int uploadTinted(float r, float g, float b) {
|
||||
final ByteBuffer pixels = tintBuffer;
|
||||
pixels.clear();
|
||||
for (final int l : maskLum) {
|
||||
pixels.put((byte) Math.round(l * r));
|
||||
pixels.put((byte) Math.round(l * g));
|
||||
pixels.put((byte) Math.round(l * b));
|
||||
pixels.put((byte) 0xFF);
|
||||
}
|
||||
pixels.flip();
|
||||
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
// Match the vanilla glint texture
|
||||
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);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, maskW, maskH, 0,
|
||||
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static void ensureMaskLoaded() {
|
||||
if (maskLoaded || maskFailed) return;
|
||||
try (final InputStream in = Minecraft.getMinecraft().getResourceManager().getResource(GLINT_MASK).getInputStream()) {
|
||||
final BufferedImage img = ImageIO.read(in);
|
||||
maskW = img.getWidth();
|
||||
maskH = img.getHeight();
|
||||
maskLum = new int[maskW * maskH];
|
||||
final int[] argb = img.getRGB(0, 0, maskW, maskH, null, 0, maskW);
|
||||
for (int i = 0; i < argb.length; i++) {
|
||||
final int p = argb[i];
|
||||
final int rr = (p >> 16) & 0xFF;
|
||||
final int gg = (p >> 8) & 0xFF;
|
||||
final int bb = p & 0xFF;
|
||||
maskLum[i] = (rr + gg + bb) / 3;
|
||||
}
|
||||
tintBuffer = BufferUtils.createByteBuffer(maskW * maskH * 4);
|
||||
maskLoaded = true;
|
||||
} catch (final Exception e) {
|
||||
maskFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clamp8(float v) {
|
||||
final int i = Math.round(v * 255.0F);
|
||||
if (i < 0) return 0;
|
||||
return Math.min(i, 255);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import jss.notfine.core.Settings;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
@@ -19,6 +21,13 @@ public class Zoom {
|
||||
|
||||
@Getter
|
||||
private static float zoom = ZOOM_DEFAULT;
|
||||
|
||||
public static final float ZOOM_SMOOTH_SNAP_EPSILON = 0.001F;
|
||||
|
||||
@Getter
|
||||
private static float currentZoom = 1.0F;
|
||||
private static long zoomLerpLastNano = 0L;
|
||||
|
||||
@Getter
|
||||
private static final KeyBinding zoomKey = new KeyBinding("Zoom", 0, "key.categories.misc");
|
||||
|
||||
@@ -38,6 +47,29 @@ public class Zoom {
|
||||
zoom = MathHelper.clamp_float((float) (zoom * Math.pow(ZOOM_STEP, Integer.signum(eventDWheel))), ZOOM_MIN, ZOOM_MAX);
|
||||
}
|
||||
|
||||
public static void updateZoomLerp() {
|
||||
final long now = System.nanoTime();
|
||||
float dt = zoomLerpLastNano == 0L ? 0.0F : (now - zoomLerpLastNano) / 1.0e9F;
|
||||
zoomLerpLastNano = now;
|
||||
|
||||
final float target = zoomEnabled ? zoom : 1.0F;
|
||||
|
||||
if (dt <= 0.0F || !(boolean) Settings.ZOOM_SMOOTH.option.getStore()) {
|
||||
currentZoom = target;
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp dt so a long frame (alt-tab, GC pause) does not snap the zoom.
|
||||
dt = Math.min(dt, 0.1F);
|
||||
|
||||
final int rate = (int) Settings.ZOOM_SMOOTH_SPEED.option.getStore();
|
||||
currentZoom += (target - currentZoom) * (1.0F - (float) Math.exp(-dt * rate));
|
||||
|
||||
if (Math.abs(target - currentZoom) < ZOOM_SMOOTH_SNAP_EPSILON) {
|
||||
currentZoom = target;
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetMouseFilters(Minecraft mc) {
|
||||
if (mc.entityRenderer != null) {
|
||||
((IMouseFilterExt) mc.entityRenderer.mouseFilterXAxis).angelica$reset();
|
||||
@@ -79,4 +111,10 @@ public class Zoom {
|
||||
handleKeyEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderTick(TickEvent.RenderTickEvent e) {
|
||||
if (e.phase != TickEvent.Phase.START) return;
|
||||
updateZoomLerp();
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,9 @@ public enum Settings {
|
||||
RenderStars.reloadStarRenderList(Minecraft.getMinecraft().renderGlobal);
|
||||
}
|
||||
},
|
||||
VOID_FOG(new NotFineOptionTickBox(false, OptionImpact.LOW));
|
||||
VOID_FOG(new NotFineOptionTickBox(false, OptionImpact.LOW)),
|
||||
ZOOM_SMOOTH(new NotFineOptionTickBox(true, OptionImpact.LOW)),
|
||||
ZOOM_SMOOTH_SPEED(new NotFineOptionSlider(14, 1, 30, 1, OptionImpact.LOW));
|
||||
|
||||
public final NotFineOption<?> option;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package jss.notfine.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import jss.notfine.config.NotFineConfig;
|
||||
import jss.notfine.core.Settings;
|
||||
@@ -169,6 +170,12 @@ public class NotFineGameOptionPages {
|
||||
.build());
|
||||
}
|
||||
|
||||
if (AngelicaConfig.enableZoom) {
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.ZOOM_SMOOTH.option)
|
||||
.add(Settings.ZOOM_SMOOTH_SPEED.option)
|
||||
.build());
|
||||
}
|
||||
|
||||
return new OptionPage(I18n.format("options.video"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
@@ -532,6 +532,13 @@ public class SodiumGameOptionPages {
|
||||
.build())
|
||||
.build());
|
||||
|
||||
if (AngelicaConfig.enableZoom) {
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.ZOOM_SMOOTH.option)
|
||||
.add(Settings.ZOOM_SMOOTH_SPEED.option)
|
||||
.build());
|
||||
}
|
||||
|
||||
return new OptionPage(I18n.format("sodium.options.pages.appearance"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import net.coderbot.iris.shaderpack.option.Profile;
|
||||
import net.coderbot.iris.shaderpack.option.values.MutableOptionValues;
|
||||
import net.coderbot.iris.shaderpack.option.values.OptionValues;
|
||||
import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
@@ -567,11 +568,11 @@ public class Iris {
|
||||
|
||||
// If it's a folder-based shaderpack, just use the shaders subdirectory
|
||||
shaderPackPath = shaderPackRoot.resolve("shaders");
|
||||
}
|
||||
|
||||
if (!Files.exists(shaderPackPath)) {
|
||||
logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name);
|
||||
return false;
|
||||
if (!Files.exists(shaderPackPath)) {
|
||||
logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -762,6 +763,9 @@ public class Iris {
|
||||
// allows shaderpacks to be changed at runtime
|
||||
irisConfig.initialize();
|
||||
|
||||
// Reset the texture reload counter
|
||||
CapturedRenderingState.INSTANCE.resetTextureReloadCount();
|
||||
|
||||
// Destroy all allocated resources
|
||||
destroyEverything();
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ import java.util.function.BooleanSupplier;
|
||||
public enum FeatureFlags {
|
||||
SEPARATE_HARDWARE_SAMPLERS(() -> true, () -> true),
|
||||
PER_BUFFER_BLENDING(() -> true, RenderSystem::supportsBufferBlending),
|
||||
COMPUTE_SHADERS(() -> true, RenderSystem::supportsCompute),
|
||||
COMPUTE_SHADERS(() -> false, RenderSystem::supportsCompute),
|
||||
ENTITY_TRANSLUCENT(() -> true, () -> true),
|
||||
BLOCK_EMISSION_ATTRIBUTE(() -> true, () -> true),
|
||||
CUSTOM_IMAGES(() -> true, RenderSystem::supportsImageLoadStore),
|
||||
SSBO(() -> true, RenderSystem::supportsSSBO),
|
||||
HIGHER_SHADOWCOLOR(() -> true, () -> true),
|
||||
TESSELLATION_SHADERS(() -> true, RenderSystem::supportsTesselation),
|
||||
TESSELLATION_SHADERS(() -> false, RenderSystem::supportsTesselation),
|
||||
REVERSED_CULLING(() -> true, () -> true),
|
||||
CAN_DISABLE_WEATHER(() -> true, () -> true),
|
||||
UNKNOWN(() -> false, () -> false);
|
||||
|
||||
@@ -4,5 +4,7 @@ public enum GlVersion {
|
||||
GL_11,
|
||||
GL_12,
|
||||
GL_30,
|
||||
GL_31
|
||||
GL_31,
|
||||
GL_33,
|
||||
GL_41
|
||||
}
|
||||
@@ -132,6 +132,11 @@ public class StandardMacros {
|
||||
getBiomeCategoryDefines().forEach((category, ordinal) -> define(standardDefines, category, ordinal));
|
||||
getVanillaBiomeDefines().forEach((biome, id) -> define(standardDefines, biome, id));
|
||||
|
||||
// rain type constants
|
||||
define(standardDefines, "PPT_NONE", "0");
|
||||
define(standardDefines, "PPT_RAIN", "1");
|
||||
define(standardDefines, "PPT_SNOW", "2");
|
||||
|
||||
return ImmutableList.copyOf(standardDefines);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import net.coderbot.iris.gl.GlVersion;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL41;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -72,10 +74,16 @@ public enum InternalTextureFormat {
|
||||
RG32UI(GL30.GL_RG32UI, GlVersion.GL_30, PixelFormat.RG_INTEGER),
|
||||
RGB32UI(GL30.GL_RGB32UI, GlVersion.GL_30, PixelFormat.RGB_INTEGER),
|
||||
RGBA32UI(GL30.GL_RGBA32UI, GlVersion.GL_30, PixelFormat.RGBA_INTEGER),
|
||||
// 2-bit normalized
|
||||
RGBA2(GL11.GL_RGBA2, GlVersion.GL_11, PixelFormat.RGBA),
|
||||
// 4-bit normalized
|
||||
RGBA4(GL11.GL_RGBA4, GlVersion.GL_11, PixelFormat.RGBA),
|
||||
// Mixed
|
||||
R3_G3_B2(GL11.GL_R3_G3_B2, GlVersion.GL_11, PixelFormat.RGB),
|
||||
RGB5_A1(GL11.GL_RGB5_A1, GlVersion.GL_11, PixelFormat.RGBA),
|
||||
RGB565(GL41.GL_RGB565, GlVersion.GL_41, PixelFormat.RGB),
|
||||
RGB10_A2(GL11.GL_RGB10_A2, GlVersion.GL_11, PixelFormat.RGBA),
|
||||
RGB10_A2UI(GL33.GL_RGB10_A2UI, GlVersion.GL_33, PixelFormat.RGBA_INTEGER),
|
||||
R11F_G11F_B10F(GL30.GL_R11F_G11F_B10F, GlVersion.GL_30, PixelFormat.RGB),
|
||||
RGB9_E5(GL30.GL_RGB9_E5, GlVersion.GL_30, PixelFormat.RGB);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.coderbot.iris.gui.screen.ShaderPackScreen;
|
||||
import net.coderbot.iris.shaderpack.ShaderPack;
|
||||
import net.coderbot.iris.shaderpack.option.menu.OptionMenuContainer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -24,6 +25,12 @@ public class ShaderPackOptionList extends IrisGuiSlot {
|
||||
private OptionMenuContainer container;
|
||||
private final List<BaseEntry> entries = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private boolean searchModeActive = false;
|
||||
private String typedSearchQuery = "";
|
||||
private int reservedLeftWidth = 48; // Pixels reserved on the left of the header row for the search/clear button + gap.
|
||||
private GuiTextField searchBox = null;
|
||||
|
||||
public ShaderPackOptionList(ShaderPackScreen screen, NavigationController navigation, ShaderPack pack, Minecraft client, int width, int height, int top, int bottom, int left, int right) {
|
||||
super(client, width, height, top, bottom, 24);
|
||||
this.navigation = navigation;
|
||||
@@ -36,6 +43,74 @@ public class ShaderPackOptionList extends IrisGuiSlot {
|
||||
this.container = pack.getMenuContainer();
|
||||
}
|
||||
|
||||
|
||||
/** Applies {@code query} to the option container and rebuilds the list rows. */
|
||||
public void updateSearchQuery(String query) {
|
||||
typedSearchQuery = query != null ? query : "";
|
||||
if (this.container != null) {
|
||||
this.container.setSearchQuery(query);
|
||||
}
|
||||
this.rebuild();
|
||||
}
|
||||
|
||||
/** Resets all search state and clears the active filter without rebuilding entries. */
|
||||
public void disableSearchMode() {
|
||||
searchModeActive = false;
|
||||
typedSearchQuery = "";
|
||||
searchBox = null;
|
||||
if (this.container != null) {
|
||||
this.container.setSearchQuery(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void disableSearchModeAndRebuild() {
|
||||
disableSearchMode();
|
||||
this.rebuild();
|
||||
}
|
||||
|
||||
public void enableSearchModeAndRebuild() {
|
||||
searchModeActive = true;
|
||||
this.rebuild(); // First rebuild so that the button width is the current one
|
||||
int listWidth = getListWidth();
|
||||
int entryX = this.width / 2 - listWidth / 2;
|
||||
int boxX = entryX + reservedLeftWidth + 3;
|
||||
int boxY = this.top + 5; // got figured out by trial and error and works, don't question
|
||||
int boxW = Math.max(40, listWidth - reservedLeftWidth - 5);
|
||||
this.searchBox = new GuiTextField(Minecraft.getMinecraft().fontRenderer, boxX, boxY, boxW, 14);
|
||||
this.searchBox.setMaxStringLength(64);
|
||||
this.searchBox.setFocused(true);
|
||||
}
|
||||
|
||||
/** True when the navigation stack is non-empty (a sub-screen is open). */
|
||||
public boolean isOnSubScreen() {
|
||||
return navigation != null && navigation.getCurrentScreen() != null;
|
||||
}
|
||||
|
||||
public void setReservedLeftWidth(int w) {
|
||||
reservedLeftWidth = Math.max(0, w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes a keystroke into the live search text field, then propagates any text change
|
||||
* to the option container and rebuilds the filtered list.
|
||||
*/
|
||||
public void handleSearchKeyTyped(char typedChar, int keyCode) {
|
||||
if (searchBox == null || !searchModeActive) return;
|
||||
searchBox.textboxKeyTyped(typedChar, keyCode);
|
||||
String newText = searchBox.getText();
|
||||
if (!newText.equals(typedSearchQuery)) {
|
||||
updateSearchQuery(newText);
|
||||
}
|
||||
}
|
||||
|
||||
/** Draws the search text box overlay above the header row */
|
||||
public void drawSearchBox() {
|
||||
if (searchBox == null || !searchModeActive) return;
|
||||
// Hide when the header has scrolled out of view
|
||||
if (this.amountScrolled > 0.5f) return;
|
||||
searchBox.drawTextBox();
|
||||
}
|
||||
|
||||
public void rebuild() {
|
||||
this.entries.clear();
|
||||
this.amountScrolled = 0;
|
||||
@@ -56,7 +131,7 @@ public class ShaderPackOptionList extends IrisGuiSlot {
|
||||
}
|
||||
|
||||
public void addHeader(String text, boolean backButton) {
|
||||
this.addEntry(new HeaderEntry(this.screen, this.navigation, text, backButton));
|
||||
this.addEntry(new HeaderEntry(this.screen, this.navigation, text, backButton, this));
|
||||
}
|
||||
|
||||
public void addWidgets(int columns, List<AbstractElementWidget<?>> elements) {
|
||||
@@ -86,6 +161,15 @@ public class ShaderPackOptionList extends IrisGuiSlot {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(int mouseX, int mouseY, int mouseButton) {
|
||||
// Pass the click to the search box first so it can update its focus state
|
||||
if (searchModeActive && searchBox != null && amountScrolled <= 0.5f) {
|
||||
searchBox.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean elementClicked(int index, boolean doubleClick, int mouseX, int mouseY, int mouseButton) {
|
||||
final BaseEntry entry = this.entries.get(index);
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.MathHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
@@ -142,10 +143,15 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
|
||||
topButtonRow.allowEnableShadersButton = !names.isEmpty();
|
||||
|
||||
int index = 0;
|
||||
String selectedName = Iris.getIrisConfig().getShaderPackName().orElse(null);
|
||||
int targetSelectionIndex = -1;
|
||||
|
||||
for (String name : names) {
|
||||
this.addPackEntry(index, name);
|
||||
if (name.equals(selectedName)) {
|
||||
targetSelectionIndex = index;
|
||||
}
|
||||
index++;
|
||||
addPackEntry(index, name);
|
||||
}
|
||||
|
||||
// Try not to gaslight users in case they swap back and forth from lwjgl2 and lwjgl3
|
||||
@@ -153,6 +159,29 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
|
||||
? "pack.iris.list.label"
|
||||
: "pack.iris.list.label.lwjgl2";
|
||||
addLabelEntries(EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format(footerKey));
|
||||
|
||||
if (targetSelectionIndex != -1) {
|
||||
scrollToIndex(targetSelectionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the list to center the entry at {@code targetIndex} in the viewport.
|
||||
*/
|
||||
public void scrollToIndex(int targetIndex) {
|
||||
if (targetIndex < 0 || targetIndex >= this.getSize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.elementClicked(targetIndex, false, 0, 0);
|
||||
this.selectedElement = targetIndex;
|
||||
|
||||
int viewportCenterY = (this.bottom - this.top) / 2;
|
||||
int itemCenterPos = (targetIndex * this.slotHeight) + this.headerPadding + (this.slotHeight / 2);
|
||||
float targetScroll = (float) (itemCenterPos - viewportCenterY);
|
||||
float maxScroll = (float) this.func_148135_f();
|
||||
|
||||
this.amountScrolled = MathHelper.clamp_float(targetScroll, 0.0F, maxScroll);
|
||||
}
|
||||
|
||||
public void addPackEntry(int index, String name) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package net.coderbot.iris.gui.element.shaderoptions;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gui.GuiUtil;
|
||||
import net.coderbot.iris.gui.NavigationController;
|
||||
import net.coderbot.iris.gui.element.IrisElementRow;
|
||||
import net.coderbot.iris.gui.element.ShaderPackOptionList;
|
||||
import net.coderbot.iris.gui.screen.ShaderPackScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
@@ -16,58 +18,83 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class HeaderEntry extends BaseEntry {
|
||||
|
||||
public static final String BACK_BUTTON_TEXT = EnumChatFormatting.ITALIC + "< " + I18n.format("options.iris.back");
|
||||
public static final String RESET_BUTTON_TEXT_INACTIVE = EnumChatFormatting.GRAY + I18n.format("options.iris.reset");
|
||||
public static final String RESET_BUTTON_TEXT_ACTIVE = EnumChatFormatting.YELLOW + I18n.format("options.iris.reset");
|
||||
|
||||
public static final String RESET_HOLD_SHIFT_TOOLTIP = EnumChatFormatting.BOLD + I18n.format("options.iris.reset.tooltip.holdShift");
|
||||
public static final String RESET_TOOLTIP = EnumChatFormatting.RED + I18n.format("options.iris.reset.tooltip");
|
||||
public static final String IMPORT_TOOLTIP = I18n.format("options.iris.importSettings.tooltip");
|
||||
// .withStyle(style -> style.withColor(TextColor.fromRgb(0x4da6ff)));
|
||||
public static final String EXPORT_TOOLTIP = I18n.format("options.iris.exportSettings.tooltip");
|
||||
// .withStyle(style -> style.withColor(TextColor.fromRgb(0xfc7d3d)));
|
||||
private static String backButtonText() { return EnumChatFormatting.ITALIC + "< " + I18n.format("options.iris.back"); }
|
||||
private static String resetButtonInactive() { return EnumChatFormatting.GRAY + I18n.format("options.iris.reset"); }
|
||||
private static String resetButtonActive() { return EnumChatFormatting.YELLOW + I18n.format("options.iris.reset"); }
|
||||
private static String resetHoldShiftTooltip() { return EnumChatFormatting.BOLD + I18n.format("options.iris.reset.tooltip.holdShift"); }
|
||||
private static String resetTooltip() { return EnumChatFormatting.RED + I18n.format("options.iris.reset.tooltip"); }
|
||||
private static String importTooltip() { return I18n.format("options.iris.importSettings.tooltip"); }
|
||||
private static String exportTooltip() { return I18n.format("options.iris.exportSettings.tooltip"); }
|
||||
private static String searchButtonText() { return I18n.format("options.iris.search.button"); }
|
||||
private static String clearButtonText() { return I18n.format("options.iris.clear.button"); }
|
||||
private static String searchTooltip() { return I18n.format("options.iris.search.tooltip"); }
|
||||
private static String clearTooltip() { return I18n.format("options.iris.clear.tooltip"); }
|
||||
|
||||
private static final int MIN_SIDE_BUTTON_WIDTH = 42;
|
||||
private static final int BUTTON_HEIGHT = 16;
|
||||
private static final int SEARCH_BOX_GAP = 3;
|
||||
|
||||
private final ShaderPackScreen screen;
|
||||
private final ShaderPackOptionList optionList;
|
||||
private final @Nullable IrisElementRow backButton;
|
||||
/** Width of the left-side button, used to clip the title text away from it. */
|
||||
private final int leftButtonWidth;
|
||||
private final IrisElementRow utilityButtons = new IrisElementRow();
|
||||
private final IrisElementRow.TextButtonElement resetButton;
|
||||
private final IrisElementRow.IconButtonElement importButton;
|
||||
private final IrisElementRow.IconButtonElement exportButton;
|
||||
private final String text;
|
||||
|
||||
public HeaderEntry(ShaderPackScreen screen, NavigationController navigation, String text, boolean hasBackButton) {
|
||||
/** Non-null only on the main-screen header (search/clear toggle). */
|
||||
private final @Nullable IrisElementRow.TextButtonElement searchToggleButton;
|
||||
|
||||
public HeaderEntry(ShaderPackScreen screen, NavigationController navigation, String text, boolean hasBackButton, ShaderPackOptionList optionList) {
|
||||
super(navigation);
|
||||
|
||||
this.optionList = optionList;
|
||||
this.screen = screen;
|
||||
this.text = text;
|
||||
|
||||
IrisElementRow.TextButtonElement toggleRef = null;
|
||||
|
||||
if (hasBackButton) {
|
||||
this.backButton = new IrisElementRow().add(new IrisElementRow.TextButtonElement(BACK_BUTTON_TEXT, this::backButtonClicked),
|
||||
Math.max(MIN_SIDE_BUTTON_WIDTH, Minecraft.getMinecraft().fontRenderer.getStringWidth(BACK_BUTTON_TEXT) + 8));
|
||||
// Sub-screen: navigating here while search is active → force-disable so the search box disappears
|
||||
if (optionList.isSearchModeActive()) {
|
||||
optionList.disableSearchMode();
|
||||
}
|
||||
String backText = backButtonText();
|
||||
int w = Math.max(MIN_SIDE_BUTTON_WIDTH, Minecraft.getMinecraft().fontRenderer.getStringWidth(backText) + 8);
|
||||
this.backButton = new IrisElementRow().add(new IrisElementRow.TextButtonElement(backText, this::backButtonClicked), w);
|
||||
this.leftButtonWidth = w;
|
||||
} else {
|
||||
this.backButton = null;
|
||||
// Main screen: left button is the search/clear toggle
|
||||
String buttonText = optionList.isSearchModeActive() ? clearButtonText() : searchButtonText();
|
||||
IrisElementRow.TextButtonElement searchBtn = new IrisElementRow.TextButtonElement(buttonText, this::searchButtonClicked);
|
||||
int w = Math.max(MIN_SIDE_BUTTON_WIDTH, Minecraft.getMinecraft().fontRenderer.getStringWidth(buttonText) + 8);
|
||||
this.backButton = new IrisElementRow().add(searchBtn, w);
|
||||
toggleRef = searchBtn;
|
||||
this.leftButtonWidth = w;
|
||||
optionList.setReservedLeftWidth(w + SEARCH_BOX_GAP);
|
||||
}
|
||||
|
||||
this.resetButton = new IrisElementRow.TextButtonElement(RESET_BUTTON_TEXT_INACTIVE, this::resetButtonClicked);
|
||||
this.searchToggleButton = toggleRef;
|
||||
|
||||
this.resetButton = new IrisElementRow.TextButtonElement(resetButtonInactive(), this::resetButtonClicked);
|
||||
this.importButton = new IrisElementRow.IconButtonElement(GuiUtil.Icon.IMPORT, GuiUtil.Icon.IMPORT_COLORED, this::importSettingsButtonClicked);
|
||||
this.exportButton = new IrisElementRow.IconButtonElement(GuiUtil.Icon.EXPORT, GuiUtil.Icon.EXPORT_COLORED, this::exportSettingsButtonClicked);
|
||||
|
||||
this.utilityButtons.add(this.importButton, 15).add(this.exportButton, 15)
|
||||
.add(this.resetButton, Math.max(MIN_SIDE_BUTTON_WIDTH, Minecraft.getMinecraft().fontRenderer.getStringWidth((RESET_BUTTON_TEXT_INACTIVE) + 8)));
|
||||
|
||||
this.screen = screen;
|
||||
this.text = text;
|
||||
.add(this.resetButton, Math.max(MIN_SIDE_BUTTON_WIDTH, Minecraft.getMinecraft().fontRenderer.getStringWidth(resetButtonInactive()) + 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int slotWidth, int slotHeight, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
|
||||
Gui.drawRect(x - 3, (y + slotHeight) - 3, x + slotWidth, (y + slotHeight) - 2, 0x66BEBEBE);
|
||||
// Gui.drawRect leaves GL color set to the rect's color; reset to white so button textures aren't tinted.
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
final int tickDelta = 0;
|
||||
final FontRenderer font = Minecraft.getMinecraft().fontRenderer;
|
||||
|
||||
GuiUtil.drawScrollingText(font, text, x + (int) (slotWidth * 0.5), x + 5,
|
||||
((x + slotWidth) - 10) - this.utilityButtons.getWidth(), y + 5, y + 15, 0xFFFFFF);
|
||||
|
||||
GuiUtil.bindIrisWidgetsTexture();
|
||||
|
||||
// Draw back button if present
|
||||
@@ -75,26 +102,42 @@ public class HeaderEntry extends BaseEntry {
|
||||
backButton.drawScreen(x, y, BUTTON_HEIGHT, mouseX, mouseY, tickDelta, isMouseOver);
|
||||
}
|
||||
|
||||
if (optionList.isSearchModeActive() && searchToggleButton != null) {
|
||||
if (searchToggleButton.isHovered()) {
|
||||
ShaderPackScreen.TOP_LAYER_RENDER_QUEUE.add(() -> GuiUtil.drawTextPanel(font, clearTooltip(), mouseX, mouseY - 16));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GuiUtil.drawScrollingText(font, text, x + (int) (slotWidth * 0.5),
|
||||
x + leftButtonWidth + 5,
|
||||
((x + slotWidth) - 10) - this.utilityButtons.getWidth(),
|
||||
y + 5, y + 15, 0xFFFFFF);
|
||||
|
||||
final boolean shiftDown = GuiScreen.isShiftKeyDown();
|
||||
|
||||
// Set the appearance of the reset button
|
||||
this.resetButton.disabled = !shiftDown;
|
||||
this.resetButton.text = shiftDown ? RESET_BUTTON_TEXT_ACTIVE : RESET_BUTTON_TEXT_INACTIVE;
|
||||
this.resetButton.text = shiftDown ? resetButtonActive() : resetButtonInactive();
|
||||
|
||||
// Draw the utility buttons
|
||||
this.utilityButtons.renderRightAligned((x + slotWidth) - 3, y, BUTTON_HEIGHT, mouseX, mouseY, tickDelta, isMouseOver);
|
||||
|
||||
// Draw the reset button's tooltip
|
||||
if (this.resetButton.isHovered()) {
|
||||
final String tooltip = shiftDown ? RESET_TOOLTIP : RESET_HOLD_SHIFT_TOOLTIP;
|
||||
final String tooltip = shiftDown ? resetTooltip() : resetHoldShiftTooltip();
|
||||
queueBottomRightAnchoredTooltip(mouseX, mouseY, font, tooltip);
|
||||
}
|
||||
// Draw the import/export button tooltips
|
||||
if (this.importButton.isHovered()) {
|
||||
queueBottomRightAnchoredTooltip(mouseX, mouseY, font, IMPORT_TOOLTIP);
|
||||
queueBottomRightAnchoredTooltip(mouseX, mouseY, font, importTooltip());
|
||||
}
|
||||
if (this.exportButton.isHovered()) {
|
||||
queueBottomRightAnchoredTooltip(mouseX, mouseY, font, EXPORT_TOOLTIP);
|
||||
queueBottomRightAnchoredTooltip(mouseX, mouseY, font, exportTooltip());
|
||||
}
|
||||
// Search-button tooltip
|
||||
if (searchToggleButton != null && searchToggleButton.isHovered()) {
|
||||
ShaderPackScreen.TOP_LAYER_RENDER_QUEUE.add(() -> GuiUtil.drawTextPanel(font, searchTooltip(), mouseX, mouseY - 16));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +147,9 @@ public class HeaderEntry extends BaseEntry {
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(int mouseX, int mouseY, int button) {
|
||||
if (optionList.isSearchModeActive()) {
|
||||
return backButton != null && backButton.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
final boolean backButtonResult = backButton != null && backButton.mouseClicked(mouseX, mouseY, button);
|
||||
final boolean utilButtonResult = utilityButtons.mouseClicked(mouseX, mouseY, button);
|
||||
|
||||
@@ -122,6 +168,16 @@ public class HeaderEntry extends BaseEntry {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean searchButtonClicked(IrisElementRow.TextButtonElement button) {
|
||||
GuiUtil.playButtonClickSound();
|
||||
if (optionList.isSearchModeActive()) {
|
||||
optionList.disableSearchModeAndRebuild();
|
||||
} else {
|
||||
optionList.enableSearchModeAndRebuild();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean resetButtonClicked(IrisElementRow.TextButtonElement button) {
|
||||
if (GuiScreen.isShiftKeyDown()) {
|
||||
Iris.resetShaderPackOptionsOnNextReload();
|
||||
|
||||
@@ -10,9 +10,10 @@ import net.minecraft.client.resources.I18n;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TopButtonRowEntry extends BaseEntry {
|
||||
private static final String NONE_PRESENT_LABEL = I18n.format("options.iris.shaders.nonePresent"); //.withStyle(ChatFormatting.GRAY);
|
||||
private static final String SHADERS_DISABLED_LABEL = I18n.format("options.iris.shaders.disabled");
|
||||
private static final String SHADERS_ENABLED_LABEL = I18n.format("options.iris.shaders.enabled");
|
||||
|
||||
private static String nonePresentLabel() { return I18n.format("options.iris.shaders.nonePresent"); }
|
||||
private static String shadersDisabledLabel() { return I18n.format("options.iris.shaders.disabled"); }
|
||||
private static String shadersEnabledLabel() { return I18n.format("options.iris.shaders.enabled"); }
|
||||
|
||||
private final ShaderPackSelectionList shaderPackSelectionList;
|
||||
private final IrisElementRow buttons = new IrisElementRow();
|
||||
@@ -54,7 +55,7 @@ public class TopButtonRowEntry extends BaseEntry {
|
||||
}
|
||||
|
||||
private String getEnableDisableLabel() {
|
||||
return this.allowEnableShadersButton ? this.shadersEnabled ? SHADERS_ENABLED_LABEL : SHADERS_DISABLED_LABEL : NONE_PRESENT_LABEL;
|
||||
return this.allowEnableShadersButton ? this.shadersEnabled ? shadersEnabledLabel() : shadersDisabledLabel() : nonePresentLabel();
|
||||
}
|
||||
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
|
||||
@@ -4,16 +4,19 @@ import net.coderbot.iris.gui.GuiUtil;
|
||||
import net.coderbot.iris.gui.NavigationController;
|
||||
import net.coderbot.iris.gui.screen.ShaderPackScreen;
|
||||
import net.coderbot.iris.shaderpack.option.menu.OptionMenuElement;
|
||||
import net.coderbot.iris.shaderpack.option.menu.OptionMenuOptionElement;
|
||||
import net.coderbot.iris.shaderpack.option.menu.ShaderSearchEngine;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class BaseOptionElementWidget<T extends OptionMenuElement> extends CommentedElementWidget<T> {
|
||||
protected static final String SET_TO_DEFAULT = I18n.format("options.iris.setToDefault");
|
||||
protected static String setToDefault() { return I18n.format("options.iris.setToDefault"); }
|
||||
protected static final String DIVIDER = ": ";
|
||||
|
||||
protected String unmodifiedLabel;
|
||||
@@ -101,7 +104,7 @@ public abstract class BaseOptionElementWidget<T extends OptionMenuElement> exten
|
||||
|
||||
protected final void tryRenderTooltip(int mouseX, int mouseY, boolean hovered) {
|
||||
if (GuiScreen.isShiftKeyDown()) {
|
||||
renderTooltip(SET_TO_DEFAULT, mouseX, mouseY, hovered);
|
||||
renderTooltip(setToDefault(), mouseX, mouseY, hovered);
|
||||
} else if (this.isLabelTrimmed && !this.screen.isDisplayingComment()) {
|
||||
renderTooltip(this.unmodifiedLabel, mouseX, mouseY, hovered);
|
||||
}
|
||||
@@ -145,13 +148,39 @@ public abstract class BaseOptionElementWidget<T extends OptionMenuElement> exten
|
||||
|
||||
@Override
|
||||
public Optional<String> getCommentBody() {
|
||||
final String key = getCommentKey();
|
||||
if (key == null) {
|
||||
return Optional.empty();
|
||||
Optional<String> base = Optional.ofNullable(getCommentKey()).map(I18n::format);
|
||||
|
||||
if (this.screen == null || !this.screen.isOptionMenuSearchActive()) {
|
||||
return base;
|
||||
}
|
||||
final String translated = GuiUtil.translateLenient(key);
|
||||
// Don't show comment boxes without a translation, otherwise you get something like option.{commentname}.comment
|
||||
return translated.equals(key) ? Optional.empty() : Optional.of(translated);
|
||||
|
||||
String pathLabel = buildTranslatedPath();
|
||||
if (pathLabel == null) {
|
||||
return base;
|
||||
}
|
||||
|
||||
String breadcrumb = EnumChatFormatting.BOLD.toString() + EnumChatFormatting.ITALIC + pathLabel + EnumChatFormatting.RESET;
|
||||
if (base.isPresent()) {
|
||||
return Optional.of(breadcrumb + ". " + base.get());
|
||||
}
|
||||
return Optional.of(breadcrumb);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String buildTranslatedPath() {
|
||||
if (!(this.element instanceof OptionMenuOptionElement)) return null;
|
||||
OptionMenuOptionElement optEl = (OptionMenuOptionElement) this.element;
|
||||
String rawPath = optEl.container.getOptionPath(optEl.optionId);
|
||||
String[] segments = rawPath.split("/");
|
||||
StringBuilder display = new StringBuilder();
|
||||
for (String segment : segments) {
|
||||
if ("root".equals(segment)) continue;
|
||||
String translated = ShaderSearchEngine.getDisplaySettingsName(segment).replaceAll("\\s+>", "");
|
||||
String label = translated.isEmpty() ? segment : translated;
|
||||
if (display.length() > 0) display.append(" > ");
|
||||
display.append(label);
|
||||
}
|
||||
return display.length() > 0 ? display.toString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,10 +9,9 @@ import net.coderbot.iris.shaderpack.option.menu.OptionMenuBooleanOptionElement;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
public class BooleanElementWidget extends BaseOptionElementWidget<OptionMenuBooleanOptionElement> {
|
||||
private static final String TEXT_TRUE = I18n.format("label.iris.true");
|
||||
private static final String TEXT_FALSE = I18n.format("label.iris.false");
|
||||
private static final String TEXT_TRUE_DEFAULT = I18n.format("label.iris.true");
|
||||
private static final String TEXT_FALSE_DEFAULT = I18n.format("label.iris.false");
|
||||
|
||||
private static String textTrue() { return I18n.format("label.iris.true"); }
|
||||
private static String textFalse() { return I18n.format("label.iris.false"); }
|
||||
|
||||
private final BooleanOption option;
|
||||
|
||||
@@ -63,11 +62,7 @@ public class BooleanElementWidget extends BaseOptionElementWidget<OptionMenuBool
|
||||
// Compatibility Mode to "On" when not needed. Now we use white for
|
||||
// default to avoid giving a positive or negative connotation to a
|
||||
// default value.
|
||||
if (this.value == this.defaultValue) {
|
||||
return this.value ? TEXT_TRUE_DEFAULT : TEXT_FALSE_DEFAULT;
|
||||
}
|
||||
|
||||
return this.value ? TEXT_TRUE : TEXT_FALSE;
|
||||
return this.value ? textTrue() : textFalse();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,8 +16,9 @@ import net.minecraft.client.resources.I18n;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ProfileElementWidget extends BaseOptionElementWidget<OptionMenuProfileElement> {
|
||||
private static final String PROFILE_LABEL = I18n.format("options.iris.profile");
|
||||
private static final String PROFILE_CUSTOM = I18n.format("options.iris.profile.custom");
|
||||
|
||||
private static String profileLabel() { return I18n.format("options.iris.profile"); }
|
||||
private static String profileCustom() { return I18n.format("options.iris.profile.custom"); }
|
||||
|
||||
private Profile next;
|
||||
private Profile previous;
|
||||
@@ -30,7 +31,7 @@ public class ProfileElementWidget extends BaseOptionElementWidget<OptionMenuProf
|
||||
@Override
|
||||
public void init(ShaderPackScreen screen, NavigationController navigation) {
|
||||
super.init(screen, navigation);
|
||||
this.setLabel(PROFILE_LABEL);
|
||||
this.setLabel(profileLabel());
|
||||
boolean secondProfileSet = ProfileElementTracker.isSecondProfileSet(this.element);
|
||||
|
||||
final ProfileSet profiles = this.element.profiles;
|
||||
@@ -44,12 +45,12 @@ public class ProfileElementWidget extends BaseOptionElementWidget<OptionMenuProf
|
||||
final Optional<String> profileName = result.current.map(p -> p.name);
|
||||
|
||||
String translationKey = "profile" + (secondProfileSet ? "2." : ".") + profileName.orElse("custom");
|
||||
this.profileLabel = profileName.map(name -> GuiUtil.translateOrDefault(name, translationKey)).orElse(PROFILE_CUSTOM);
|
||||
this.profileLabel = profileName.map(name -> GuiUtil.translateOrDefault(name, translationKey)).orElse(profileCustom());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int x, int y, int width, int height, int mouseX, int mouseY, float tickDelta, boolean hovered) {
|
||||
this.updateRenderParams(width, width - (Minecraft.getMinecraft().fontRenderer.getStringWidth(PROFILE_LABEL) + 16));
|
||||
this.updateRenderParams(width, width - (Minecraft.getMinecraft().fontRenderer.getStringWidth(profileLabel()) + 16));
|
||||
|
||||
this.renderOptionWithValue(x, y, width, height, hovered);
|
||||
}
|
||||
@@ -61,12 +62,12 @@ public class ProfileElementWidget extends BaseOptionElementWidget<OptionMenuProf
|
||||
|
||||
@Override
|
||||
protected int getValueColor() {
|
||||
return PROFILE_CUSTOM.equals(this.profileLabel) ? 0xFFFF55 : 0xFFFFFF;
|
||||
return profileCustom().equals(this.profileLabel) ? 0xFFFF55 : 0xFFFFFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getCommentTitle() {
|
||||
return Optional.of(PROFILE_LABEL);
|
||||
return Optional.of(profileLabel());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,7 +28,7 @@ public class SliderElementWidget extends StringElementWidget {
|
||||
}
|
||||
|
||||
if (GuiScreen.isShiftKeyDown()) {
|
||||
renderTooltip(SET_TO_DEFAULT, mouseX, mouseY, hovered);
|
||||
renderTooltip(setToDefault(), mouseX, mouseY, hovered);
|
||||
} else if (!this.screen.isDisplayingComment()) {
|
||||
renderTooltip(this.unmodifiedLabel, mouseX, mouseY, hovered);
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
|
||||
*/
|
||||
public static final Set<Runnable> TOP_LAYER_RENDER_QUEUE = new HashSet<>();
|
||||
|
||||
private static final String SELECT_TITLE = EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format("pack.iris.select.title");
|
||||
private static final String CONFIGURE_TITLE = EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format("pack.iris.configure.title");
|
||||
private static String selectTitle() { return EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format("pack.iris.select.title"); }
|
||||
private static String configureTitle() { return EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format("pack.iris.configure.title"); }
|
||||
private static final int COMMENT_PANEL_WIDTH = 314;
|
||||
|
||||
private final GuiScreen parent;
|
||||
@@ -104,6 +104,11 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
|
||||
|
||||
refreshForChangedPack();
|
||||
}
|
||||
/** Returns true when the option menu is open and the user has an active search query. */
|
||||
public boolean isOptionMenuSearchActive() {
|
||||
return this.optionMenuOpen && this.shaderOptionList != null && this.shaderOptionList.isSearchModeActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float delta) {
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
@@ -124,6 +129,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
|
||||
if (!this.guiHidden) {
|
||||
if (optionMenuOpen && this.shaderOptionList != null) {
|
||||
this.shaderOptionList.drawScreen(mouseX, mouseY, delta);
|
||||
this.shaderOptionList.drawSearchBox();
|
||||
} else {
|
||||
this.shaderPackList.drawScreen(mouseX, mouseY, delta);
|
||||
}
|
||||
@@ -144,9 +150,9 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
|
||||
drawCenteredString(this.fontRendererObj, notificationDialog, (int) (this.width * 0.5), 21, 0xFFFFFF);
|
||||
} else {
|
||||
if (optionMenuOpen) {
|
||||
drawCenteredString(this.fontRendererObj, CONFIGURE_TITLE, (int) (this.width * 0.5), 21, 0xFFFFFF);
|
||||
drawCenteredString(this.fontRendererObj, configureTitle(), (int) (this.width * 0.5), 21, 0xFFFFFF);
|
||||
} else {
|
||||
drawCenteredString(this.fontRendererObj, SELECT_TITLE, (int) (this.width * 0.5), 21, 0xFFFFFF);
|
||||
drawCenteredString(this.fontRendererObj, selectTitle(), (int) (this.width * 0.5), 21, 0xFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +213,10 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
|
||||
if (Iris.getCurrentPack().isPresent() && this.navigation != null) {
|
||||
final ShaderPack currentPack = Iris.getCurrentPack().get();
|
||||
|
||||
if (currentPack.getMenuContainer() != null) {
|
||||
currentPack.getMenuContainer().setSearchQuery(null);
|
||||
}
|
||||
|
||||
this.shaderOptionList = new ShaderPackOptionList(this, this.navigation, currentPack, this.mc, this.width, this.height, 32, this.height - 58, 0, this.width);
|
||||
this.navigation.setActiveOptionList(this.shaderOptionList);
|
||||
|
||||
@@ -364,6 +374,31 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char typedChar, int keyCode) {
|
||||
// Route all input to the search box while search mode is active
|
||||
if (optionMenuOpen && this.shaderOptionList != null && this.shaderOptionList.isSearchModeActive()) {
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
this.shaderOptionList.disableSearchModeAndRebuild();
|
||||
return;
|
||||
}
|
||||
// Ctrl+F while search is open: toggle it closed
|
||||
if (keyCode == Keyboard.KEY_F && GuiScreen.isCtrlKeyDown()) {
|
||||
GuiUtil.playButtonClickSound();
|
||||
this.shaderOptionList.disableSearchModeAndRebuild();
|
||||
return;
|
||||
}
|
||||
this.shaderOptionList.handleSearchKeyTyped(typedChar, keyCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+F: open search on the main option screen
|
||||
if (optionMenuOpen && this.shaderOptionList != null
|
||||
&& keyCode == Keyboard.KEY_F && GuiScreen.isCtrlKeyDown()) {
|
||||
if (!this.shaderOptionList.isOnSubScreen()) {
|
||||
GuiUtil.playButtonClickSound();
|
||||
this.shaderOptionList.enableSearchModeAndRebuild();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (GuiScreen.isCtrlKeyDown() && keyCode == Keyboard.KEY_D) {
|
||||
this.mc.displayGuiScreen(new GuiYesNo((result, id) -> {
|
||||
Iris.setDebug(result);
|
||||
@@ -496,6 +531,10 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
|
||||
}
|
||||
|
||||
public void onClose() {
|
||||
if (this.shaderOptionList != null) {
|
||||
this.shaderOptionList.disableSearchModeAndRebuild();
|
||||
}
|
||||
|
||||
if (!dropChanges) {
|
||||
applyChanges();
|
||||
} else {
|
||||
|
||||
@@ -42,6 +42,7 @@ import net.coderbot.iris.gl.texture.DepthBufferFormat;
|
||||
import net.coderbot.iris.gl.texture.TextureType;
|
||||
import net.coderbot.iris.helpers.Tri;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.transform.GlintScrollInjector;
|
||||
import net.coderbot.iris.pipeline.transform.PatchShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.TransformPatcher;
|
||||
import net.coderbot.iris.postprocess.BufferFlipper;
|
||||
@@ -838,13 +839,16 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
|
||||
if (transformed == null) {
|
||||
// Fallback to synchronous transform if not pre-computed
|
||||
String vertex = source.getVertexSource().orElseThrow(NullPointerException::new);
|
||||
final boolean scrollGlint = GlintScrollInjector.shouldInject(id, source);
|
||||
transformed = TransformPatcher.patchAttributes(
|
||||
source.getVertexSource().orElseThrow(NullPointerException::new),
|
||||
vertex,
|
||||
source.getGeometrySource().orElse(null),
|
||||
source.getTessControlSource().orElse(null),
|
||||
source.getTessEvalSource().orElse(null),
|
||||
source.getFragmentSource().orElseThrow(NullPointerException::new),
|
||||
availability);
|
||||
availability,
|
||||
scrollGlint);
|
||||
}
|
||||
|
||||
String vertex = transformed.get(PatchShaderType.VERTEX);
|
||||
@@ -1784,9 +1788,12 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
ProgramSource source = resolver.resolveNullable(id);
|
||||
if (source != null && !processedSourceNames.contains(source.getName())) {
|
||||
processedSourceNames.add(source.getName());
|
||||
final ProgramSource src = source;
|
||||
final String vertexSource = src.getVertexSource().orElse(null);
|
||||
final boolean scrollGlint = GlintScrollInjector.shouldInject(id, src);
|
||||
for (InputAvailability avail : INPUT_AVAILABILITIES) {
|
||||
Pair<String, InputAvailability> key = Pair.of(source.getName(), avail);
|
||||
futures.put(key, Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchAttributes(source.getVertexSource().orElse(null), source.getGeometrySource().orElse(null), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getFragmentSource().orElse(null), avail)));
|
||||
Pair<String, InputAvailability> key = Pair.of(src.getName(), avail);
|
||||
futures.put(key, Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchAttributes(vertexSource, src.getGeometrySource().orElse(null), src.getTessControlSource().orElse(null), src.getTessEvalSource().orElse(null), src.getFragmentSource().orElse(null), avail, scrollGlint)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.coderbot.iris.pipeline.transform;
|
||||
import net.coderbot.iris.gl.shader.ShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.AttributeParameters;
|
||||
import org.taumc.glsl.Transformer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -13,6 +14,10 @@ import java.util.Map;
|
||||
*/
|
||||
class AttributeTransformer {
|
||||
public static void transform(Transformer transformer, AttributeParameters parameters, int version) {
|
||||
if (parameters.scrollGlint && parameters.type == ShaderType.VERTEX) {
|
||||
transformer.replaceExpression("gl_MultiTexCoord0", "(gl_TextureMatrix[0] * gl_MultiTexCoord0)", GLSLParser::postfix_expression);
|
||||
}
|
||||
|
||||
// Always core profile — minimum GLSL version is 330 (see ShaderTransformer.getStageMinimumVersion)
|
||||
CommonTransformer.transform(transformer, parameters, true, version);
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@ class CeleritasTransformer {
|
||||
vertexReplacements.put("gl_MultiTexCoord2", "iris_LightTexCoord");
|
||||
vertexReplacements.forEach(transformer::replaceExpression);
|
||||
|
||||
if (transformer.hasVariable("chunkOffset")) {
|
||||
transformer.removeVariable("chunkOffset");
|
||||
}
|
||||
|
||||
final Map<String, String> vertexRenames = new HashMap<>();
|
||||
vertexRenames.put("gl_Color", "_vert_color");
|
||||
vertexRenames.put("ftransform", "iris_ftransform");
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import net.coderbot.iris.gl.shader.ShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.Parameters;
|
||||
import org.taumc.glsl.Transformer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -29,6 +30,16 @@ public class CommonTransformer {
|
||||
root.rename("gl_Color", "iris_FrontColor");
|
||||
}
|
||||
|
||||
if (root.hasVariable("gl_TexCoord")) {
|
||||
if (parameters.type == ShaderType.VERTEX) {
|
||||
root.rename("gl_TexCoord", "irs_texCoords");
|
||||
root.injectVariable("out vec4 irs_texCoords[3];");
|
||||
} else if (parameters.type == ShaderType.FRAGMENT) {
|
||||
root.rename("gl_TexCoord", "irs_texCoords");
|
||||
root.injectVariable("in vec4 irs_texCoords[3];");
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters.type == ShaderType.FRAGMENT) {
|
||||
if (root.containsCall("gl_FragColor")) {
|
||||
root.replaceExpression("gl_FragColor", "gl_FragData[0]");
|
||||
@@ -91,6 +102,27 @@ public class CommonTransformer {
|
||||
root.renameAndWrapShadow("shadow2D", "texture");
|
||||
root.renameAndWrapShadow("shadow2DLod", "textureLod");
|
||||
|
||||
if (root.containsCall("textureLodOffset")) {
|
||||
root.injectFunction("vec4 iris_textureLodOffset(sampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }");
|
||||
root.injectFunction("float iris_textureLodOffset(sampler2DShadow iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, vec3(iris_tlo_c.xy + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_c.z), iris_tlo_l); }");
|
||||
root.injectFunction("vec4 iris_textureLodOffset(sampler3D iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec3 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec3(iris_tlo_o) / vec3(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }");
|
||||
root.injectFunction("vec4 iris_textureLodOffset(sampler2DArray iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, vec3(iris_tlo_c.xy + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l)).xy), iris_tlo_c.z), iris_tlo_l); }");
|
||||
root.injectFunction("ivec4 iris_textureLodOffset(isampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }");
|
||||
root.injectFunction("uvec4 iris_textureLodOffset(usampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }");
|
||||
root.renameFunctionCall("textureLodOffset", "iris_textureLodOffset");
|
||||
}
|
||||
|
||||
if (parameters.type == ShaderType.VERTEX) {
|
||||
root.mutateTree(tree -> {
|
||||
if (tree.children != null) {
|
||||
tree.children.removeIf(child ->
|
||||
child instanceof GLSLParser.External_declarationContext
|
||||
&& ((GLSLParser.External_declarationContext) child).declaration() != null
|
||||
&& child.getText().contains("gl_PerVertex"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (parameters.patch == Patch.ATTRIBUTES && parameters.type == ShaderType.VERTEX) {
|
||||
root.injectVariable("uniform bool angelica_ClipPlanesEnabled;");
|
||||
root.injectVariable("uniform vec4 angelica_ClipPlane[8];");
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.coderbot.iris.pipeline.transform;
|
||||
import net.coderbot.iris.gl.shader.ShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.Parameters;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.taumc.glsl.ShaderPrinter;
|
||||
import org.taumc.glsl.Transformer;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
@@ -109,7 +110,10 @@ public class CompatibilityTransformer {
|
||||
continue;
|
||||
}
|
||||
|
||||
prevTransformer.makeOutDeclaration(inDec.get(in), in);
|
||||
String outDeclaration = ShaderPrinter.getFormattedShader(inDec.get(in).fully_specified_type())
|
||||
+ " " + in + ";";
|
||||
prevTransformer.variable = null;
|
||||
prevTransformer.injectVariable(outDeclaration.replaceFirst("in", "out"));
|
||||
|
||||
if (!prevTransformer.hasAssigment(in)) {
|
||||
prevTransformer.initialize(inDec.get(in), in);
|
||||
@@ -126,6 +130,23 @@ public class CompatibilityTransformer {
|
||||
if (!prevTransformer.hasAssigment(in)) {
|
||||
prevTransformer.initialize(inDec.get(in), in);
|
||||
}
|
||||
} else {
|
||||
int outComp = vecComponents(outType.getText());
|
||||
int inComp = vecComponents(inType.getText());
|
||||
if (outComp < 0 || inComp < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String temp = "iris_template_" + in;
|
||||
prevTransformer.rename(in, temp);
|
||||
String newOut = ShaderPrinter.getFormattedShader(inDec.get(in).fully_specified_type())
|
||||
+ " " + in + ";";
|
||||
prevTransformer.variable = null;
|
||||
prevTransformer.injectVariable(newOut.replaceFirst("in", "out"));
|
||||
String cast = outComp < inComp
|
||||
? in + " = " + inType.getText() + "(" + temp + ", vec4(0));"
|
||||
: in + " = " + inType.getText() + "(" + temp + ");";
|
||||
prevTransformer.appendMain(cast);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,4 +154,13 @@ public class CompatibilityTransformer {
|
||||
prevType = type;
|
||||
}
|
||||
}
|
||||
|
||||
private static int vecComponents(String type) {
|
||||
return switch (type) {
|
||||
case "vec2" -> 2;
|
||||
case "vec3" -> 3;
|
||||
case "vec4" -> 4;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class CompositeDepthTransformer {
|
||||
final int type = transformer.findType("centerDepthSmooth");
|
||||
if (type != 0) {
|
||||
transformer.injectVariable("uniform sampler2D iris_centerDepthSmooth;");
|
||||
transformer.replaceExpression("centerDepthSmooth", "texture2D(iris_centerDepthSmooth, vec2(0.5)).r");
|
||||
transformer.replaceExpression("centerDepthSmooth", "texture(iris_centerDepthSmooth, vec2(0.5)).r");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,13 @@ class CoreTransformHelper {
|
||||
transformer.rename("gl_Vertex", "iris_Vertex");
|
||||
transformer.rename("gl_MultiTexCoord0", "iris_MultiTexCoord0");
|
||||
|
||||
for (int i = 1; i <= 7; i++) {
|
||||
transformer.replaceExpression("gl_MultiTexCoord" + i, "vec4(0.0, 0.0, 0.0, 1.0)");
|
||||
}
|
||||
|
||||
transformer.replaceExpression("gl_Color", "vec4(1.0, 1.0, 1.0, 1.0)");
|
||||
transformer.replaceExpression("gl_Normal", "vec3(0.0, 0.0, 1.0)");
|
||||
|
||||
transformer.renameFunctionCall("ftransform", "iris_ftransform");
|
||||
transformer.injectFunction("vec4 iris_ftransform() { return (iris_ProjectionMatrix * iris_ModelViewMatrix) * iris_Vertex; }");
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.coderbot.iris.pipeline.transform;
|
||||
|
||||
import net.coderbot.iris.shaderpack.ProgramSource;
|
||||
import net.coderbot.iris.shaderpack.loading.ProgramId;
|
||||
|
||||
/**
|
||||
* For some reason not all shaders read from gl_MultiTexCoord0 through the texture matrix to scroll the enchant glint?
|
||||
* Maybe a modern thing but a few shaders just look wrong without it. Technically not in spec, but it looks better.
|
||||
*/
|
||||
public final class GlintScrollInjector {
|
||||
|
||||
private GlintScrollInjector() {}
|
||||
|
||||
public static boolean shouldInject(ProgramId id, ProgramSource source) {
|
||||
if (id != ProgramId.ArmorGlint || source == null) return false;
|
||||
if (!ProgramId.ArmorGlint.getSourceName().equals(source.getName())) return false;
|
||||
|
||||
final String vertex = source.getVertexSource().orElse(null);
|
||||
if (vertex == null) return false;
|
||||
// Already scrolls through the texture matrix itself
|
||||
if (vertex.contains("gl_TextureMatrix[0]")) return false;
|
||||
return vertex.contains("gl_MultiTexCoord0");
|
||||
}
|
||||
}
|
||||
@@ -104,8 +104,12 @@ public class TransformPatcher {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Map<PatchShaderType, String> patchAttributes(String vertex, String geometry, String tessControl, String tessEval, String fragment, InputAvailability inputs, boolean scrollGlint) {
|
||||
return transform(vertex, geometry, tessControl, tessEval, fragment, new AttributeParameters(Patch.ATTRIBUTES, geometry != null, inputs, scrollGlint));
|
||||
}
|
||||
|
||||
public static Map<PatchShaderType, String> patchAttributes(String vertex, String geometry, String tessControl, String tessEval, String fragment, InputAvailability inputs) {
|
||||
return transform(vertex, geometry, tessControl, tessEval, fragment, new AttributeParameters(Patch.ATTRIBUTES, geometry != null, inputs));
|
||||
return patchAttributes(vertex, geometry, tessControl, tessEval, fragment, inputs, false);
|
||||
}
|
||||
|
||||
public static Map<PatchShaderType, String> patchAttributes(String vertex, String geometry, String fragment, InputAvailability inputs) {
|
||||
|
||||
+6
-1
@@ -7,12 +7,14 @@ import net.coderbot.iris.shaderpack.texture.TextureStage;
|
||||
public class AttributeParameters extends Parameters {
|
||||
public final boolean hasGeometry;
|
||||
public final InputAvailability inputs;
|
||||
public final boolean scrollGlint;
|
||||
// WARNING: adding new fields requires updating hashCode and equals methods!
|
||||
|
||||
public AttributeParameters(Patch patch, boolean hasGeometry, InputAvailability inputs) {
|
||||
public AttributeParameters(Patch patch, boolean hasGeometry, InputAvailability inputs, boolean scrollGlint) {
|
||||
super(patch, null);
|
||||
this.hasGeometry = hasGeometry;
|
||||
this.inputs = inputs;
|
||||
this.scrollGlint = scrollGlint;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -25,6 +27,7 @@ public class AttributeParameters extends Parameters {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + (hasGeometry ? 1231 : 1237);
|
||||
result = prime * result + (scrollGlint ? 1231 : 1237);
|
||||
result = prime * result + ((inputs == null) ? 0 : inputs.hashCode());
|
||||
return result;
|
||||
}
|
||||
@@ -40,6 +43,8 @@ public class AttributeParameters extends Parameters {
|
||||
AttributeParameters other = (AttributeParameters) obj;
|
||||
if (hasGeometry != other.hasGeometry)
|
||||
return false;
|
||||
if (scrollGlint != other.scrollGlint)
|
||||
return false;
|
||||
if (inputs == null) {
|
||||
if (other.inputs != null)
|
||||
return false;
|
||||
|
||||
@@ -77,11 +77,22 @@ public class IdMap {
|
||||
private static final Pattern LEGACY_DIRECTIVE_PATTERN = Pattern.compile(
|
||||
"(?m)^\\s*#\\s*(?:if|elif|ifdef|ifndef)\\b[^\\n]*\\bMC_VERSION\\b[^\\n]*\\b10710\\b");
|
||||
|
||||
/**
|
||||
* Detects whether a pack's block.properties has a dedicated 1.7.10 section.
|
||||
*/
|
||||
public static boolean detectLegacySection(Path shaderPath) {
|
||||
String rawBlockProperties = readProperties(shaderPath, "block.properties");
|
||||
return rawBlockProperties != null && LEGACY_DIRECTIVE_PATTERN.matcher(rawBlockProperties).find();
|
||||
}
|
||||
|
||||
public static String modernFallbackMcVersion() {
|
||||
return AngelicaConfig.modernFallbackMcVersion > 0
|
||||
? String.valueOf(AngelicaConfig.modernFallbackMcVersion) : "260101";
|
||||
}
|
||||
|
||||
IdMap(Path shaderPath, ShaderPackOptions shaderPackOptions, Iterable<StringPair> environmentDefines) {
|
||||
// Check if block.properties has a dedicated 1.7.10 section
|
||||
String rawBlockProperties = readProperties(shaderPath, "block.properties");
|
||||
this.hasLegacySection = rawBlockProperties != null
|
||||
&& LEGACY_DIRECTIVE_PATTERN.matcher(rawBlockProperties).find();
|
||||
this.hasLegacySection = detectLegacySection(shaderPath);
|
||||
|
||||
Iterable<StringPair> resolvedDefines;
|
||||
if (this.hasLegacySection) {
|
||||
@@ -100,9 +111,7 @@ public class IdMap {
|
||||
}
|
||||
}
|
||||
|
||||
String modernVersion = AngelicaConfig.modernFallbackMcVersion > 0
|
||||
? String.valueOf(AngelicaConfig.modernFallbackMcVersion) : "260101";
|
||||
modernDefines.add(new StringPair("MC_VERSION", modernVersion));
|
||||
modernDefines.add(new StringPair("MC_VERSION", modernFallbackMcVersion()));
|
||||
resolvedDefines = modernDefines;
|
||||
|
||||
loadProperties(shaderPath, "block.properties", shaderPackOptions, modernDefines).ifPresent(blockProperties -> {
|
||||
|
||||
@@ -55,7 +55,7 @@ public class PackRenderTargetDirectives {
|
||||
|
||||
renderTargetSettings.forEach((index, settings) -> {
|
||||
if (settings.shouldClear()) {
|
||||
buffersToBeCleared.add(index.intValue());
|
||||
buffersToBeCleared.add(index);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.coderbot.iris.shaderpack;
|
||||
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.features.FeatureFlags;
|
||||
import net.coderbot.iris.gl.blending.BlendModeOverride;
|
||||
import net.coderbot.iris.shaderpack.include.AbsolutePackPath;
|
||||
import net.coderbot.iris.shaderpack.loading.ProgramId;
|
||||
@@ -529,7 +530,8 @@ public class ProgramSet {
|
||||
|
||||
String tessControlSource = null;
|
||||
String tessEvalSource = null;
|
||||
if (programSet.pack.hasFeature(net.coderbot.iris.features.FeatureFlags.TESSELLATION_SHADERS)) {
|
||||
if (programSet.pack.hasFeature(FeatureFlags.TESSELLATION_SHADERS)
|
||||
&& FeatureFlags.TESSELLATION_SHADERS.isUsable()) {
|
||||
AbsolutePackPath tessControlPath = directory.resolve(program + ".tcs");
|
||||
tessControlSource = sourceProvider.apply(tessControlPath);
|
||||
|
||||
@@ -547,6 +549,10 @@ public class ProgramSet {
|
||||
private static ComputeSource readComputeSource(AbsolutePackPath directory,
|
||||
Function<AbsolutePackPath, String> sourceProvider, String program,
|
||||
ProgramSet programSet) {
|
||||
if (!FeatureFlags.COMPUTE_SHADERS.isUsable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AbsolutePackPath computePath = directory.resolve(program + ".csh");
|
||||
String computeSource = sourceProvider.apply(computePath);
|
||||
|
||||
|
||||
@@ -207,8 +207,16 @@ public class ShaderPack {
|
||||
this.shaderPackOptions = new ShaderPackOptions(graph, changedConfigs);
|
||||
graph = this.shaderPackOptions.getIncludes();
|
||||
|
||||
final String mcVersionOverride = IdMap.detectLegacySection(root) ? null : IdMap.modernFallbackMcVersion();
|
||||
|
||||
List<StringPair> finalEnvironmentDefines = new ArrayList<>();
|
||||
environmentDefines.forEach(finalEnvironmentDefines::add);
|
||||
for (StringPair define : environmentDefines) {
|
||||
if (mcVersionOverride != null && "MC_VERSION".equals(define.getKey())) {
|
||||
finalEnvironmentDefines.add(new StringPair("MC_VERSION", mcVersionOverride));
|
||||
} else {
|
||||
finalEnvironmentDefines.add(define);
|
||||
}
|
||||
}
|
||||
for (FeatureFlags flag : FeatureFlags.values()) {
|
||||
boolean usable = flag.isUsable();
|
||||
Iris.logger.info("FeatureFlag {} usable: {}", flag.name(), usable);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.coderbot.iris.shaderpack.loading;
|
||||
|
||||
public enum ProgramArrayId {
|
||||
Setup(ProgramGroup.Setup, 100),
|
||||
Begin(ProgramGroup.Begin, 100),
|
||||
ShadowComposite(ProgramGroup.ShadowComposite, 99),
|
||||
Prepare(ProgramGroup.Prepare, 99),
|
||||
Deferred(ProgramGroup.Deferred, 99),
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package net.coderbot.iris.shaderpack.loading;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum ProgramGroup {
|
||||
Setup("setup"),
|
||||
Begin("begin"),
|
||||
Shadow("shadow"),
|
||||
ShadowComposite("shadowcomp"),
|
||||
Prepare("prepare"),
|
||||
@@ -17,7 +22,4 @@ public enum ProgramGroup {
|
||||
this.baseName = baseName;
|
||||
}
|
||||
|
||||
public String getBaseName() {
|
||||
return baseName;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package net.coderbot.iris.shaderpack.materialmap;
|
||||
|
||||
import net.coderbot.iris.Iris;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -263,7 +263,19 @@ public final class PropertiesTokenizer {
|
||||
);
|
||||
}
|
||||
|
||||
final String[] splitStates = baseEntry.split(":");
|
||||
// Drop empty segments produced by consecutive colons (like "betterendforge::lumecorn")
|
||||
final String[] splitStates = Arrays.stream(baseEntry.split(":"))
|
||||
.filter(s -> !s.isEmpty())
|
||||
.toArray(String[]::new);
|
||||
|
||||
if (splitStates.length == 0) {
|
||||
return new ParsedBlockIdentifier(
|
||||
new NamespacedId("minecraft", baseEntry),
|
||||
Collections.emptySet(),
|
||||
Collections.emptyMap(),
|
||||
nbtProperties
|
||||
);
|
||||
}
|
||||
|
||||
// Vanilla block (minecraft namespaced)
|
||||
if (splitStates.length == 1) {
|
||||
@@ -277,7 +289,7 @@ public final class PropertiesTokenizer {
|
||||
|
||||
// Two-segment with no metas/state properties: "minecraft:stone"
|
||||
if (splitStates.length == 2
|
||||
&& !StringUtils.isNumeric(splitStates[1].substring(0, 1))
|
||||
&& !startsWithDigit(splitStates[1])
|
||||
&& !splitStates[1].contains("=")) {
|
||||
return new ParsedBlockIdentifier(
|
||||
new NamespacedId(splitStates[0], splitStates[1]),
|
||||
@@ -296,7 +308,7 @@ public final class PropertiesTokenizer {
|
||||
id = new NamespacedId("minecraft", splitStates[0]);
|
||||
statesStart = 1;
|
||||
|
||||
} else if (StringUtils.isNumeric(splitStates[1].substring(0, 1)) || splitStates[1].contains("=")) {
|
||||
} else if (startsWithDigit(splitStates[1]) || splitStates[1].contains("=")) {
|
||||
// "stone:0:something" or "stone:lit=true:something", likely will need this for snowy blocks in the future.
|
||||
id = new NamespacedId("minecraft", splitStates[0]);
|
||||
statesStart = 1;
|
||||
@@ -339,6 +351,10 @@ public final class PropertiesTokenizer {
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean startsWithDigit(String s) {
|
||||
return !s.isEmpty() && Character.isDigit(s.charAt(0));
|
||||
}
|
||||
|
||||
private static void appendCurrent(boolean inBracket, boolean readingValue,
|
||||
StringBuilder blockIdBuilder, StringBuilder nbtKey,
|
||||
StringBuilder nbtValue, char c) {
|
||||
|
||||
@@ -10,9 +10,12 @@ import net.coderbot.iris.shaderpack.option.ShaderPackOptions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class OptionMenuContainer {
|
||||
public final OptionMenuElementScreen mainScreen;
|
||||
@@ -24,6 +27,8 @@ public class OptionMenuContainer {
|
||||
private final Map<List<OptionMenuElement>, Integer> unusedOptionDumpQueue = new HashMap<>(); // Used by screens with "*" element
|
||||
@Getter private final ProfileSet profiles;
|
||||
@Getter private final ProfileSet profiles2;
|
||||
private final List<OptionMenuElement> originalMainElements = new ArrayList<>(); // Saved snapshot of mainScreen.elements before any search filter is applied.
|
||||
private final Map<String, String> cachedOptionPaths = new HashMap<>(); // Full "root/SCREEN1/SCREEN2" path for every option ID, built once after construction
|
||||
|
||||
public OptionMenuContainer(ShaderProperties shaderProperties, ShaderPackOptions shaderPackOptions, ProfileSet profiles) {
|
||||
this.profiles = profiles;
|
||||
@@ -69,6 +74,10 @@ public class OptionMenuContainer {
|
||||
|
||||
entry.getKey().addAll(entry.getValue(), elementsToInsert);
|
||||
}
|
||||
|
||||
// Save the original layout and build the path cache once the full tree is constructed
|
||||
this.originalMainElements.addAll(this.mainScreen.elements);
|
||||
generateAllPaths();
|
||||
}
|
||||
|
||||
// Screens will call this when they contain a "*" element, so that the list of
|
||||
@@ -85,4 +94,81 @@ public class OptionMenuContainer {
|
||||
|
||||
unusedOptions.remove(optionId);
|
||||
}
|
||||
|
||||
private void generateAllPaths() {
|
||||
cachedOptionPaths.clear();
|
||||
traverseScreen(this.mainScreen, "root", new HashSet<String>());
|
||||
}
|
||||
|
||||
private void traverseScreen(OptionMenuElementScreen screen, String currentPath, Set<String> visited) {
|
||||
if (screen == null) return;
|
||||
for (OptionMenuElement element : screen.elements) {
|
||||
if (element instanceof OptionMenuOptionElement optEl) {
|
||||
if (optEl.optionId != null) {
|
||||
cachedOptionPaths.putIfAbsent(optEl.optionId, currentPath);
|
||||
}
|
||||
} else if (element instanceof OptionMenuLinkElement link) {
|
||||
if (link.targetScreenId != null) {
|
||||
String targetId = link.targetScreenId;
|
||||
if (visited.add(targetId)) {
|
||||
OptionMenuElementScreen next = this.subScreens.get(targetId);
|
||||
if (next != null) {
|
||||
traverseScreen(next, currentPath + "/" + targetId, visited);
|
||||
}
|
||||
visited.remove(targetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getOptionPath(String optionId) {
|
||||
if (optionId == null) return "root";
|
||||
return cachedOptionPaths.getOrDefault(optionId, "root");
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters and re-orders the main-screen options to match {@code query}, or restores the
|
||||
* original layout when {@code query} is null or blank.
|
||||
*/
|
||||
public void setSearchQuery(String query) {
|
||||
if (query == null || query.trim().isEmpty()) {
|
||||
restoreOriginalLayout();
|
||||
return;
|
||||
}
|
||||
|
||||
String normalizedQuery = query.toLowerCase(Locale.ROOT).trim();
|
||||
|
||||
List<OptionMenuOptionElement> flatOptions = ShaderSearchEngine.getAllOptionsFlattened(usedOptionElements);
|
||||
List<ShaderSearchEngine.ScoredOptionElement> scored = new ArrayList<>();
|
||||
|
||||
for (OptionMenuOptionElement el : flatOptions) {
|
||||
int score = ShaderSearchEngine.computeMatchTier(el.optionId, normalizedQuery);
|
||||
if (score > 0) {
|
||||
scored.add(new ShaderSearchEngine.ScoredOptionElement(
|
||||
el,
|
||||
ShaderSearchEngine.getReadableTranslatedName(el.optionId),
|
||||
ShaderSearchEngine.getReadableDefaultName(el.optionId),
|
||||
getOptionPath(el.optionId),
|
||||
score,
|
||||
normalizedQuery
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(scored);
|
||||
applyFilteredLayout(scored);
|
||||
}
|
||||
|
||||
private void applyFilteredLayout(List<ShaderSearchEngine.ScoredOptionElement> sortedElements) {
|
||||
this.mainScreen.elements.clear();
|
||||
for (ShaderSearchEngine.ScoredOptionElement scored : sortedElements) {
|
||||
this.mainScreen.elements.add(scored.getElement());
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreOriginalLayout() {
|
||||
this.mainScreen.elements.clear();
|
||||
this.mainScreen.elements.addAll(this.originalMainElements);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package net.coderbot.iris.shaderpack.option.menu;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.shaderpack.ShaderPack;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Utility class for searching and scoring shader option elements based on a query string.
|
||||
* <br>By SpacEagle17
|
||||
*/
|
||||
public class ShaderSearchEngine {
|
||||
private static final String WHOLE_WORD_REGEX = "(?<=^|[^a-zA-Z0-9])%s(?=$|[^a-zA-Z0-9])";
|
||||
private static final String STARTS_WITH_REGEX = "(?<=^|[^a-zA-Z0-9])%s";
|
||||
private static final Pattern COLOR_CODE_PATTERN = Pattern.compile("§.");
|
||||
|
||||
/**
|
||||
* Bitmask of bits from readable-name (translated + default) comparisons.
|
||||
* Used by the comparator to separate name-match quality from rawId/comment noise.
|
||||
*/
|
||||
private static final int READABLE_NAME_BITS =
|
||||
(1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 3) | (1 << 2);
|
||||
|
||||
|
||||
private static String strippedLanguageString(String key) {
|
||||
String translated = I18n.format(key);
|
||||
if (translated.equals(key)) return "";
|
||||
return COLOR_CODE_PATTERN.matcher(translated).replaceAll("");
|
||||
}
|
||||
|
||||
/** Translated display name for a sub-screen ID (looks up {@code "screen.<screenId>"}). */
|
||||
public static String getDisplaySettingsName(String screenId) {
|
||||
return strippedLanguageString("screen." + screenId);
|
||||
}
|
||||
|
||||
/** Lowercase Minecraft-locale translation of an option's human-readable name. */
|
||||
public static String getReadableTranslatedName(String optionId) {
|
||||
return strippedLanguageString("option." + optionId).toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lowercase default (en_us from the shader pack's lang folder) translation.
|
||||
* Returns {@code ""} when the current pack has no en_us lang file or the key is missing.
|
||||
*/
|
||||
public static String getReadableDefaultName(String optionId) {
|
||||
try {
|
||||
Optional<ShaderPack> packOpt = Iris.getCurrentPack();
|
||||
if (packOpt.isEmpty()) return "";
|
||||
Map<String, String> translations = packOpt.get().getLanguageMap().getTranslations("en_us");
|
||||
if (translations == null) return "";
|
||||
String value = translations.get("option." + optionId);
|
||||
if (value == null) return "";
|
||||
return COLOR_CODE_PATTERN.matcher(value).replaceAll("").toLowerCase(Locale.ROOT);
|
||||
} catch (Throwable t) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOnlyAscii(String s) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) > 127) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a 14-bit bitmask score for how well {@code optionId} matches {@code query}.
|
||||
* Returns 0 when there is no match at all.
|
||||
* <br> Read code to know what each bit means lmao
|
||||
*/
|
||||
public static int computeMatchTier(String optionId, String query) {
|
||||
try {
|
||||
if (optionId == null || query == null) return 0;
|
||||
String q = query.toLowerCase(Locale.ROOT).trim();
|
||||
if (q.isEmpty()) return 0;
|
||||
|
||||
String translatedName = getReadableTranslatedName(optionId);
|
||||
String defaultName = getReadableDefaultName(optionId);
|
||||
|
||||
// Single ASCII char: only match when a readable name begins with it
|
||||
if (q.length() == 1 && isOnlyAscii(q)) {
|
||||
return (!translatedName.isEmpty() && translatedName.startsWith(q)) ? 1 : 0;
|
||||
}
|
||||
|
||||
String escaped = Pattern.quote(q);
|
||||
Pattern wholeWord = Pattern.compile(String.format(WHOLE_WORD_REGEX, escaped));
|
||||
Pattern startsWith = Pattern.compile(String.format(STARTS_WITH_REGEX, escaped));
|
||||
|
||||
String rawId = optionId.toLowerCase(Locale.ROOT);
|
||||
String commentText = strippedLanguageString("option." + optionId + ".comment").toLowerCase(Locale.ROOT);
|
||||
|
||||
int score = 0;
|
||||
if (!translatedName.isEmpty() && translatedName.equals(q)) score |= (1 << 13);
|
||||
if (!defaultName.isEmpty() && defaultName.equals(q)) score |= (1 << 12);
|
||||
if (!translatedName.isEmpty() && wholeWord.matcher(translatedName).find()) score |= (1 << 11);
|
||||
if (!defaultName.isEmpty() && wholeWord.matcher(defaultName).find()) score |= (1 << 10);
|
||||
if (!translatedName.isEmpty() && startsWith.matcher(translatedName).find()) score |= (1 << 9);
|
||||
if (!defaultName.isEmpty() && startsWith.matcher(defaultName).find()) score |= (1 << 8);
|
||||
if (wholeWord.matcher(rawId).find()) score |= (1 << 7);
|
||||
if (!commentText.isEmpty() && wholeWord.matcher(commentText).find()) score |= (1 << 6);
|
||||
if (startsWith.matcher(rawId).find()) score |= (1 << 5);
|
||||
if (!commentText.isEmpty() && startsWith.matcher(commentText).find()) score |= (1 << 4);
|
||||
if (!translatedName.isEmpty() && translatedName.contains(q)) score |= (1 << 3);
|
||||
if (!defaultName.isEmpty() && defaultName.contains(q)) score |= (1 << 2);
|
||||
if (rawId.contains(q)) score |= (1 << 1);
|
||||
if (!commentText.isEmpty() && commentText.contains(q)) score |= 1;
|
||||
|
||||
return score;
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** De-duplicates and flattens the pool of registered option elements. */
|
||||
public static List<OptionMenuOptionElement> getAllOptionsFlattened(List<OptionMenuOptionElement> usedOptionElements) {
|
||||
List<OptionMenuOptionElement> flat = new ArrayList<>();
|
||||
List<String> seen = new ArrayList<>();
|
||||
for (OptionMenuOptionElement el : usedOptionElements) {
|
||||
if (el == null) continue;
|
||||
String id = el.optionId != null ? el.optionId : el.toString();
|
||||
if (!seen.contains(id)) {
|
||||
seen.add(id);
|
||||
flat.add(el);
|
||||
}
|
||||
}
|
||||
return flat;
|
||||
}
|
||||
|
||||
/**
|
||||
* A scored search result carrying the original element and all data required for sorting.
|
||||
*/
|
||||
public static final class ScoredOptionElement implements Comparable<ScoredOptionElement> {
|
||||
@Getter
|
||||
private final OptionMenuOptionElement element;
|
||||
public final String readableTranslatedName;
|
||||
public final String readableDefaultName;
|
||||
public final String path;
|
||||
public final int score;
|
||||
public final String query;
|
||||
|
||||
public ScoredOptionElement(OptionMenuOptionElement element,
|
||||
String readableTranslatedName,
|
||||
String readableDefaultName,
|
||||
String path,
|
||||
int score,
|
||||
String query) {
|
||||
this.element = element;
|
||||
this.readableTranslatedName = readableTranslatedName;
|
||||
this.readableDefaultName = readableDefaultName;
|
||||
this.path = path;
|
||||
this.score = score;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull ScoredOptionElement other) {
|
||||
String q = this.query != null ? this.query.toLowerCase(Locale.ROOT).trim() : "";
|
||||
int r;
|
||||
if ((r = compareByReadableNameQuality(this, other)) != 0) return r;
|
||||
if ((r = compareByMatchedWordLength(this, other, q)) != 0) return r;
|
||||
if ((r = compareByWordCount(this, other)) != 0) return r;
|
||||
if ((r = compareByFullScore(this, other)) != 0) return r;
|
||||
if ((r = compareByPrefixBoost(this, other, q)) != 0) return r;
|
||||
if ((r = compareByPathDepth(this, other)) != 0) return r;
|
||||
return compareByAlphabetical(this, other);
|
||||
}
|
||||
|
||||
// 1. How well does the readable name (translated + default) match?
|
||||
private static int compareByReadableNameQuality(ScoredOptionElement a, ScoredOptionElement b) {
|
||||
return Integer.compare(b.score & READABLE_NAME_BITS, a.score & READABLE_NAME_BITS);
|
||||
}
|
||||
|
||||
// 2. Shorter matched word = query covers a higher fraction of that word.
|
||||
private static int compareByMatchedWordLength(ScoredOptionElement a, ScoredOptionElement b, String q) {
|
||||
if (q.isEmpty()) return 0;
|
||||
String aw = findMatchingWord(a.readableTranslatedName, q);
|
||||
if (aw == null) aw = findMatchingWord(a.readableDefaultName, q);
|
||||
String bw = findMatchingWord(b.readableTranslatedName, q);
|
||||
if (bw == null) bw = findMatchingWord(b.readableDefaultName, q);
|
||||
int aLen = aw != null ? aw.length() : Integer.MAX_VALUE;
|
||||
int bLen = bw != null ? bw.length() : Integer.MAX_VALUE;
|
||||
return Integer.compare(aLen, bLen);
|
||||
}
|
||||
|
||||
// 3. Fewer words = more precise ("Bloom" before "Bloom Strength").
|
||||
private static int compareByWordCount(ScoredOptionElement a, ScoredOptionElement b) {
|
||||
String ae = !a.readableTranslatedName.isEmpty() ? a.readableTranslatedName : a.readableDefaultName;
|
||||
String be = !b.readableTranslatedName.isEmpty() ? b.readableTranslatedName : b.readableDefaultName;
|
||||
return Integer.compare(countWords(ae), countWords(be));
|
||||
}
|
||||
|
||||
// 4. Higher raw score wins.
|
||||
private static int compareByFullScore(ScoredOptionElement a, ScoredOptionElement b) {
|
||||
return Integer.compare(b.score, a.score);
|
||||
}
|
||||
|
||||
// 5. Prefix boost: readable name starts with the exact query string.
|
||||
private static int compareByPrefixBoost(ScoredOptionElement a, ScoredOptionElement b, String q) {
|
||||
if (q.isEmpty()) return 0;
|
||||
boolean ap = (a.readableTranslatedName != null && a.readableTranslatedName.startsWith(q))
|
||||
|| (a.readableDefaultName != null && a.readableDefaultName.startsWith(q));
|
||||
boolean bp = (b.readableTranslatedName != null && b.readableTranslatedName.startsWith(q))
|
||||
|| (b.readableDefaultName != null && b.readableDefaultName.startsWith(q));
|
||||
if (ap == bp) return 0;
|
||||
return ap ? -1 : 1;
|
||||
}
|
||||
|
||||
// 6. Shallower option path wins (fewer slashes = closer to root menu).
|
||||
private static int compareByPathDepth(ScoredOptionElement a, ScoredOptionElement b) {
|
||||
return Integer.compare(countSlashes(a.path), countSlashes(b.path));
|
||||
}
|
||||
|
||||
// 7. Alphabetical tie-breaker.
|
||||
private static int compareByAlphabetical(ScoredOptionElement a, ScoredOptionElement b) {
|
||||
String aId = a.element != null ? a.element.optionId : null;
|
||||
String bId = b.element != null ? b.element.optionId : null;
|
||||
if (aId != null && bId != null) return aId.compareTo(bId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static String findMatchingWord(String name, String query) {
|
||||
if (name == null || query.isEmpty()) return null;
|
||||
for (String word : name.split("\\s+")) {
|
||||
if (word.startsWith(query)) return word;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int countWords(String s) {
|
||||
if (s == null || s.trim().isEmpty()) return 0;
|
||||
return s.trim().split("\\s+").length;
|
||||
}
|
||||
|
||||
private static int countSlashes(String path) {
|
||||
if (path == null) return 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
if (path.charAt(i) == '/') count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class PropertiesPreprocessor {
|
||||
// Not super efficient, but this removes trailing whitespace on lines, fixing an issue with whitespace after
|
||||
// line continuations (see PreprocessorTest#testWeirdPropertiesLineContinuation)
|
||||
// Required for Voyager Shader
|
||||
source = Arrays.stream(source.split("\\R")).map(String::trim).filter(s -> !s.isEmpty())
|
||||
source = Arrays.stream(source.split("\\r\\n|\\n|\\r")).map(String::trim).filter(s -> !s.isEmpty())
|
||||
.map(line -> {
|
||||
if (line.startsWith("#")) {
|
||||
for (PreprocessorCommand command : PreprocessorCommand.values()) {
|
||||
|
||||
@@ -13,6 +13,9 @@ public class CapturedRenderingState {
|
||||
@Getter
|
||||
private float tickDelta;
|
||||
@Getter
|
||||
private int textureReloadCount;
|
||||
private Runnable textureReloadListener = null;
|
||||
@Getter
|
||||
private int currentRenderedBlockEntity;
|
||||
private Runnable blockEntityIdListener = null;
|
||||
|
||||
@@ -31,6 +34,18 @@ public class CapturedRenderingState {
|
||||
private CapturedRenderingState() {
|
||||
}
|
||||
|
||||
public void incrementTextureReloadCount() {
|
||||
this.textureReloadCount++;
|
||||
|
||||
if (this.textureReloadListener != null) {
|
||||
this.textureReloadListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
public void resetTextureReloadCount() {
|
||||
this.textureReloadCount = 0;
|
||||
}
|
||||
|
||||
public void setCurrentBlockEntity(int entity) {
|
||||
this.currentRenderedBlockEntity = entity;
|
||||
|
||||
@@ -80,5 +95,9 @@ public class CapturedRenderingState {
|
||||
return listener -> this.itemIdListener = listener;
|
||||
}
|
||||
|
||||
public ValueUpdateNotifier getTextureReloadNotifier() {
|
||||
return listener -> this.textureReloadListener = listener;
|
||||
}
|
||||
|
||||
public ValueUpdateNotifier getEntityColorNotifier() { return listener -> this.entityColorListener = listener; }
|
||||
}
|
||||
@@ -96,6 +96,12 @@ public final class CommonUniforms {
|
||||
|
||||
}, StateUpdateNotifiers.bindTextureNotifier);
|
||||
|
||||
uniforms.uniform1i("gtextureId", () -> GLStateManager.getBoundTextureForServerState(0),
|
||||
StateUpdateNotifiers.bindTextureNotifier);
|
||||
|
||||
uniforms.uniform1i("textureReloadCount", CapturedRenderingState.INSTANCE::getTextureReloadCount,
|
||||
CapturedRenderingState.INSTANCE.getTextureReloadNotifier());
|
||||
|
||||
uniforms.uniform4i("blendFunc", () -> {
|
||||
if(GLStateManager.getBlendMode().isEnabled()) {
|
||||
final BlendState blend = GLStateManager.getBlendState();
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
package net.coderbot.iris.uniforms;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.etfuturum.EtFuturumCompat;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.Camera;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.GameModeUtil;
|
||||
import com.gtnewhorizons.angelica.render.CloudRenderer;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
|
||||
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
|
||||
import net.coderbot.iris.gl.uniform.UniformHolder;
|
||||
import net.coderbot.iris.gl.uniform.UniformUpdateFrequency;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.effect.EntityLightningBolt;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import org.joml.Math;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3dc;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import java.util.List;
|
||||
@@ -21,9 +30,17 @@ public class IrisExclusiveUniforms {
|
||||
// Reusable vectors to avoid allocations every frame
|
||||
private static final Vector3d eyePositionCache = new Vector3d();
|
||||
private static final Vector3d relativeEyePositionCache = new Vector3d();
|
||||
private static final Vector3d playerLookCache = new Vector3d();
|
||||
private static final Vector3d playerBodyCache = new Vector3d();
|
||||
private static final Vector3d vehicleLookCache = new Vector3d();
|
||||
private static final Vector3d relativeVehiclePositionCache = new Vector3d();
|
||||
private static final Vector3f selectedBlockPosCache = new Vector3f();
|
||||
private static final Vector4f lightningBoltPositionCache = new Vector4f();
|
||||
private static final Vector4f ZERO_VECTOR_4f = new Vector4f(0, 0, 0, 0);
|
||||
|
||||
private static int cachedSelectedFrame = -1;
|
||||
private static int cachedSelectedBlockId;
|
||||
|
||||
public static void addIrisExclusiveUniforms(UniformHolder uniforms) {
|
||||
WorldInfoUniforms.addWorldInfoUniforms(uniforms);
|
||||
|
||||
@@ -42,6 +59,21 @@ public class IrisExclusiveUniforms {
|
||||
uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "relativeEyePosition", IrisExclusiveUniforms::getRelativeEyePosition);
|
||||
uniforms.uniform4f(UniformUpdateFrequency.PER_TICK, "lightningBoltPosition", IrisExclusiveUniforms::getLightningBoltPosition);
|
||||
uniforms.uniform1f(UniformUpdateFrequency.PER_FRAME, "cloudTime", IrisExclusiveUniforms::getCloudTime);
|
||||
uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "feetInWater", IrisExclusiveUniforms::isFeetInWater);
|
||||
uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "isRiding", IrisExclusiveUniforms::isRiding);
|
||||
uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "vehicleInWater", IrisExclusiveUniforms::isVehicleInWater);
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_TICK, "vehicleId", IrisExclusiveUniforms::getVehicleId);
|
||||
uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "vehicleLookVector", IrisExclusiveUniforms::getVehicleLookVector);
|
||||
uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "relativeVehiclePosition", IrisExclusiveUniforms::getRelativeVehiclePosition);
|
||||
uniforms.uniform1f(UniformUpdateFrequency.PER_TICK, "currentPlayerArmor", IrisExclusiveUniforms::getCurrentArmor);
|
||||
uniforms.uniform1f(UniformUpdateFrequency.PER_TICK, "maxPlayerArmor", () -> 50);
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "currentSelectedBlockId", IrisExclusiveUniforms::getCurrentSelectedBlockId);
|
||||
uniforms.uniform3f(UniformUpdateFrequency.PER_FRAME, "currentSelectedBlockPos", IrisExclusiveUniforms::getCurrentSelectedBlockPos);
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "seaLevel", IrisExclusiveUniforms::getSeaLevel);
|
||||
uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "playerLookVector", IrisExclusiveUniforms::getPlayerLookVector);
|
||||
uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "playerBodyVector", IrisExclusiveUniforms::getPlayerBodyVector);
|
||||
// Target the Elytra from EFR
|
||||
uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "isElytraFlying", IrisExclusiveUniforms::isElytraFlying);
|
||||
}
|
||||
|
||||
private static float getCloudTime() {
|
||||
@@ -137,6 +169,149 @@ public class IrisExclusiveUniforms {
|
||||
return ZERO_VECTOR_4f;
|
||||
}
|
||||
|
||||
private static EntityLivingBase cameraEntity() {
|
||||
return Minecraft.getMinecraft().renderViewEntity;
|
||||
}
|
||||
|
||||
private static Vector3d forwardVector(Vector3d dest, float yawDeg, float pitchDeg) {
|
||||
final double yr = -yawDeg * 0.017453292 - Math.PI;
|
||||
final double pr = -pitchDeg * 0.017453292;
|
||||
final double cy = Math.cos(yr);
|
||||
final double sy = Math.sin(yr);
|
||||
final double cp = -Math.cos(pr);
|
||||
final double sp = Math.sin(pr);
|
||||
return dest.set(sy * cp, sp, cy * cp);
|
||||
}
|
||||
|
||||
private static boolean isFeetInWater() {
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
return player != null && player.isInWater();
|
||||
}
|
||||
|
||||
private static boolean isElytraFlying() {
|
||||
return EtFuturumCompat.isElytraFlying(Minecraft.getMinecraft().thePlayer);
|
||||
}
|
||||
|
||||
private static boolean isRiding() {
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
return player != null && player.ridingEntity != null;
|
||||
}
|
||||
|
||||
private static boolean isVehicleInWater() {
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
return player != null && player.ridingEntity != null && player.ridingEntity.isInWater();
|
||||
}
|
||||
|
||||
private static int getVehicleId() {
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
if (player == null || player.ridingEntity == null) return 0;
|
||||
final int id = EntityIdHelper.getEntityId(player.ridingEntity);
|
||||
return Math.max(id, 0);
|
||||
}
|
||||
|
||||
private static Vector3d getVehicleLookVector() {
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
if (player == null || player.ridingEntity == null) return vehicleLookCache.set(0, 0, 0);
|
||||
final Entity v = player.ridingEntity;
|
||||
return forwardVector(vehicleLookCache, v.rotationYaw, v.rotationPitch);
|
||||
}
|
||||
|
||||
private static Vector3d getRelativeVehiclePosition() {
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
if (player == null || player.ridingEntity == null) return relativeVehiclePositionCache.set(0, 0, 0);
|
||||
final Entity v = player.ridingEntity;
|
||||
final float delta = CapturedRenderingState.INSTANCE.getTickDelta();
|
||||
final double vx = v.prevPosX + (v.posX - v.prevPosX) * delta;
|
||||
final double vy = v.prevPosY + (v.posY - v.prevPosY) * delta;
|
||||
final double vz = v.prevPosZ + (v.posZ - v.prevPosZ) * delta;
|
||||
// Match Angelica's relativeEyePosition convention (entity - camera).
|
||||
final Vector3dc cam = CameraUniforms.getUnshiftedCameraPosition();
|
||||
return relativeVehiclePositionCache.set(vx - cam.x(), vy - cam.y(), vz - cam.z());
|
||||
}
|
||||
|
||||
private static float getCurrentArmor() {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc.thePlayer == null || !mc.playerController.gameIsSurvivalOrAdventure()) {
|
||||
return -1;
|
||||
}
|
||||
return mc.thePlayer.getTotalArmorValue() / 50.0f;
|
||||
}
|
||||
|
||||
private static Vector3d getPlayerLookVector() {
|
||||
final EntityLivingBase e = cameraEntity();
|
||||
if (e == null) return playerLookCache.set(0, 0, 0);
|
||||
final float delta = CapturedRenderingState.INSTANCE.getTickDelta();
|
||||
final float yaw = e.prevRotationYaw + (e.rotationYaw - e.prevRotationYaw) * delta;
|
||||
final float pitch = e.prevRotationPitch + (e.rotationPitch - e.prevRotationPitch) * delta;
|
||||
return forwardVector(playerLookCache, yaw, pitch);
|
||||
}
|
||||
|
||||
private static Vector3d getPlayerBodyVector() {
|
||||
final EntityLivingBase e = cameraEntity();
|
||||
if (e == null) return playerBodyCache.set(0, 0, 0);
|
||||
return forwardVector(playerBodyCache, e.renderYawOffset, 0.0f);
|
||||
}
|
||||
|
||||
private static int getSeaLevel() {
|
||||
// 1.7.10 has no per-world sea level accessor, best to guess 63?
|
||||
return 63;
|
||||
}
|
||||
|
||||
private static void updateSelectedBlock() {
|
||||
final int frame = SystemTimeUniforms.COUNTER.getAsInt();
|
||||
if (frame == cachedSelectedFrame) {
|
||||
return;
|
||||
}
|
||||
cachedSelectedFrame = frame;
|
||||
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
final MovingObjectPosition hit = mc.objectMouseOver;
|
||||
if (mc.theWorld == null || hit == null || hit.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) {
|
||||
cachedSelectedBlockId = 0;
|
||||
selectedBlockPosCache.set(-256.0f, -256.0f, -256.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
final Vector3dc cam = CameraUniforms.getUnshiftedCameraPosition();
|
||||
selectedBlockPosCache.set(
|
||||
(float) ((hit.blockX + 0.5) - cam.x()),
|
||||
(float) ((hit.blockY + 0.5) - cam.y()),
|
||||
(float) ((hit.blockZ + 0.5) - cam.z()));
|
||||
|
||||
final Block block = mc.theWorld.getBlock(hit.blockX, hit.blockY, hit.blockZ);
|
||||
if (block == null || block.isAir(mc.theWorld, hit.blockX, hit.blockY, hit.blockZ)) {
|
||||
cachedSelectedBlockId = 0;
|
||||
return;
|
||||
}
|
||||
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
|
||||
if (blockMetaMatches == null) {
|
||||
cachedSelectedBlockId = 0;
|
||||
return;
|
||||
}
|
||||
final Int2IntMap metaMap = blockMetaMatches.get(block);
|
||||
if (metaMap == null) {
|
||||
cachedSelectedBlockId = 0;
|
||||
return;
|
||||
}
|
||||
final int meta = mc.theWorld.getBlockMetadata(hit.blockX, hit.blockY, hit.blockZ);
|
||||
cachedSelectedBlockId = Math.max(BlockMaterialMapping.resolveId(metaMap, meta), 0);
|
||||
}
|
||||
|
||||
private static int getCurrentSelectedBlockId() {
|
||||
updateSelectedBlock();
|
||||
return cachedSelectedBlockId;
|
||||
}
|
||||
|
||||
private static Vector3f getCurrentSelectedBlockPos() {
|
||||
updateSelectedBlock();
|
||||
return selectedBlockPosCache;
|
||||
}
|
||||
|
||||
private static int worldHeight() {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
return (level != null && level.provider != null) ? level.provider.getHeight() : 256;
|
||||
}
|
||||
|
||||
public static class WorldInfoUniforms {
|
||||
public static void addWorldInfoUniforms(UniformHolder uniforms) {
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "bedrockLevel", () -> 0);
|
||||
@@ -148,14 +323,8 @@ public class IrisExclusiveUniforms {
|
||||
return 192.0;
|
||||
}
|
||||
});
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "heightLimit", () -> {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
if (level != null && level.provider != null) {
|
||||
return level.provider.getHeight();
|
||||
} else {
|
||||
return 256;
|
||||
}
|
||||
});
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "heightLimit", IrisExclusiveUniforms::worldHeight);
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "logicalHeightLimit", IrisExclusiveUniforms::worldHeight);
|
||||
uniforms.uniform1b(UniformUpdateFrequency.PER_FRAME, "hasCeiling", () -> {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
if (level != null && level.provider != null) {
|
||||
|
||||
@@ -3,7 +3,10 @@ package net.coderbot.iris.uniforms;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.gl.uniform.UniformHolder;
|
||||
import net.coderbot.iris.gl.uniform.UniformUpdateFrequency;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
@@ -16,19 +19,47 @@ public final class SystemTimeUniforms {
|
||||
public static final Timer TIMER = new Timer();
|
||||
public static final FrameCounter COUNTER = new FrameCounter();
|
||||
|
||||
private static LocalDateTime cachedDateTime = LocalDateTime.now();
|
||||
private static long cachedDateTimeAtMs = 0L;
|
||||
|
||||
private SystemTimeUniforms() {
|
||||
}
|
||||
|
||||
private static LocalDateTime dateTimeSnapshot() {
|
||||
final long now = System.currentTimeMillis();
|
||||
if (now - cachedDateTimeAtMs >= 50L) {
|
||||
cachedDateTime = LocalDateTime.now();
|
||||
cachedDateTimeAtMs = now;
|
||||
}
|
||||
return cachedDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes system time uniforms available to the given program
|
||||
*
|
||||
* @param uniforms the program to make the uniforms available to
|
||||
*/
|
||||
public static void addSystemTimeUniforms(UniformHolder uniforms) {
|
||||
final Vector3i date = new Vector3i();
|
||||
final Vector3i time = new Vector3i();
|
||||
final Vector2i yearTime = new Vector2i();
|
||||
uniforms
|
||||
.uniform1i(UniformUpdateFrequency.PER_FRAME, "frameCounter", COUNTER)
|
||||
.uniform1f(UniformUpdateFrequency.PER_FRAME, "frameTime", TIMER::getLastFrameTime)
|
||||
.uniform1f(UniformUpdateFrequency.PER_FRAME, "frameTimeCounter", TIMER::getFrameTimeCounter);
|
||||
.uniform1f(UniformUpdateFrequency.PER_FRAME, "frameTimeCounter", TIMER::getFrameTimeCounter)
|
||||
.uniform3i(UniformUpdateFrequency.PER_TICK, "currentDate", () -> {
|
||||
final LocalDateTime dt = dateTimeSnapshot();
|
||||
return date.set(dt.getYear(), dt.getMonthValue(), dt.getDayOfMonth());
|
||||
})
|
||||
.uniform3i(UniformUpdateFrequency.PER_TICK, "currentTime", () -> {
|
||||
final LocalDateTime dt = dateTimeSnapshot();
|
||||
return time.set(dt.getHour(), dt.getMinute(), dt.getSecond());
|
||||
})
|
||||
.uniform2i(UniformUpdateFrequency.PER_TICK, "currentYearTime", () -> {
|
||||
final LocalDateTime dt = dateTimeSnapshot();
|
||||
final int elapsed = ((dt.getDayOfYear() - 1) * 86400) + (dt.getHour() * 3600) + (dt.getMinute() * 60) + dt.getSecond();
|
||||
return yearTime.set(elapsed, (dt.toLocalDate().lengthOfYear() * 86400) - elapsed);
|
||||
});
|
||||
}
|
||||
|
||||
public static void addFloatFrameMod8Uniform(UniformHolder uniforms) {
|
||||
|
||||
@@ -145,3 +145,7 @@ pack.iris.select.title=Auswählen
|
||||
pack.iris.configure.title=Konfigurieren
|
||||
label.iris.true=An
|
||||
label.iris.false=Aus
|
||||
options.iris.search.button=Suchen
|
||||
options.iris.clear.button=Schließen
|
||||
options.iris.search.tooltip=Shader-Einstellungen durchsuchen (Strg + F)
|
||||
options.iris.clear.tooltip=Shader-Suche beenden (Esc)
|
||||
@@ -139,6 +139,10 @@ options.iris.shadowDistance.enabled=Allows you to change the maximum distance fo
|
||||
options.iris.shadowDistance.disabled=Your current shader pack has already set a render distance for shadows; you cannot change it.
|
||||
options.iris.gui.hide=Hide GUI
|
||||
options.iris.gui.show=Show GUI
|
||||
options.iris.search.button=Search
|
||||
options.iris.clear.button=Clear
|
||||
options.iris.search.tooltip=Search Shader Settings (Ctrl + F)
|
||||
options.iris.clear.tooltip=Exit Shader Search (Esc)
|
||||
|
||||
options.dynamic_lights=Dynamic Lights
|
||||
options.dynamic_lights.tooltip=Enables dynamic lights. Modes: OFF - off, FASTEST - 500ms delay, FAST - 250ms delay, FANCY - 50ms delay, REALTIME - no delay.
|
||||
|
||||
@@ -132,3 +132,7 @@ pack.iris.select.title=Выбрать
|
||||
pack.iris.configure.title=Настроить
|
||||
label.iris.true=Вкл
|
||||
label.iris.false=Выкл
|
||||
options.iris.search.button=Поиск
|
||||
options.iris.clear.button=Очистить
|
||||
options.iris.search.tooltip=Поиск настроек шейдеров (Ctrl + F)
|
||||
options.iris.clear.tooltip=Выход из поиска шейдеров (Esc)
|
||||
@@ -138,6 +138,10 @@ options.iris.shadowDistance.enabled=更改渲染阴影的最远距离。超出
|
||||
options.iris.shadowDistance.disabled=当前光影包已重设阴影渲染距离,无法更改
|
||||
options.iris.gui.hide=隐藏 GUI
|
||||
options.iris.gui.show=显示 GUI
|
||||
options.iris.search.button=搜索
|
||||
options.iris.clear.button=清除搜索栏
|
||||
options.iris.search.tooltip=搜索光影包设定 (Ctrl + F)
|
||||
options.iris.clear.tooltip=退出搜索 (Esc)
|
||||
|
||||
options.dynamic_lights=动态光源
|
||||
options.dynamic_lights.tooltip=启用动态光源,模式选择:OFF-关闭,FASTEST-500ms延迟,FAST-250ms延迟,FANCY-50ms延迟,REALTIME-无延迟
|
||||
|
||||
@@ -78,3 +78,7 @@ options.void_fog=Void Fog
|
||||
options.void_fog.tooltip=Void Fog
|
||||
options.hurt_shake=Hurt Screen Shake
|
||||
options.background.random=Random
|
||||
options.zoom_smooth=Smooth Zoom
|
||||
options.zoom_smooth.tooltip=Smoothly interpolates the camera toward the zoom level instead of snapping instantly.
|
||||
options.zoom_smooth_speed=Smooth Zoom Speed
|
||||
options.zoom_smooth_speed.tooltip=How fast smooth zoom converges to the target zoom. Higher is snappier, lower is floatier. No effect when Smooth Zoom is off.
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -20,6 +21,7 @@ public class MixinMinecraft_FrameHook {
|
||||
|
||||
@Inject(method = "runGameLoop", at = @At("HEAD"))
|
||||
private void angelica$onFrameBegin(CallbackInfo ci) {
|
||||
DisplayListManager.abortIfLeaked();
|
||||
RENDER_BACKEND.onFrameBegin();
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -1,5 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.event.ClientEvent;
|
||||
import com.gtnewhorizons.angelica.event.ClientEventType;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
@@ -15,6 +17,7 @@ public class MixinMinecraft_IconifyGuard {
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/EntityRenderer;updateCameraAndRender(F)V")
|
||||
)
|
||||
private void angelica$skipRenderWhenIconified(EntityRenderer entityRenderer, float partialTicks) {
|
||||
ClientEvent.post(ClientEventType.ON_TICK);
|
||||
final Framebuffer fb = Minecraft.getMinecraft().getFramebuffer();
|
||||
if (fb == null || fb.framebufferWidth < 16 || fb.framebufferHeight < 16) {
|
||||
return;
|
||||
|
||||
+12
-70
@@ -13,7 +13,6 @@ import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -23,6 +22,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Single-pass damage-overlay tint, modeled on modern Minecraft's OverlayTexture.
|
||||
* The tint is applied only while the main body model renders.
|
||||
*/
|
||||
@Mixin(RendererLivingEntity.class)
|
||||
public class MixinRendererLivingEntity_OverlayTint {
|
||||
@@ -33,8 +33,6 @@ public class MixinRendererLivingEntity_OverlayTint {
|
||||
@Unique private boolean angelica$ffpOverlayActive;
|
||||
@Unique private boolean angelica$shaderEntityColorActive;
|
||||
|
||||
@Unique private boolean angelica$emissivePass;
|
||||
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE",
|
||||
@@ -64,20 +62,13 @@ public class MixinRendererLivingEntity_OverlayTint {
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderEquippedItems(Lnet/minecraft/entity/EntityLivingBase;F)V"),
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderModel(Lnet/minecraft/entity/EntityLivingBase;FFFFFF)V",
|
||||
shift = At.Shift.AFTER),
|
||||
require = 1, expect = 1
|
||||
)
|
||||
private void angelica$teardownOverlay(EntityLivingBase entity, double x, double y, double z,
|
||||
float yaw, float partialTick, CallbackInfo ci) {
|
||||
if (angelica$ffpOverlayActive) {
|
||||
GLStateManager.setOverlayColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
angelica$ffpOverlayActive = false;
|
||||
}
|
||||
if (angelica$shaderEntityColorActive) {
|
||||
// Don't tint held items
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
angelica$shaderEntityColorActive = false;
|
||||
}
|
||||
private void angelica$clearBodyTint(EntityLivingBase entity, double x, double y, double z,
|
||||
float yaw, float partialTick, CallbackInfo ci) {
|
||||
angelica$clearOverlayColor();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,64 +91,17 @@ public class MixinRendererLivingEntity_OverlayTint {
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent bleed by saving last state and comparing with current blend state.
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;shouldRenderPass(Lnet/minecraft/entity/EntityLivingBase;IF)I"),
|
||||
require = 1, expect = 1
|
||||
)
|
||||
private int angelica$detectEmissivePass(RendererLivingEntity self, EntityLivingBase entity, int pass,
|
||||
float partialTick, Operation<Integer> original) {
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
final int result = original.call(self, entity, pass, partialTick);
|
||||
final int srcAfter = GLStateManager.glGetInteger(GL14.GL_BLEND_SRC_RGB);
|
||||
final int dstAfter = GLStateManager.glGetInteger(GL14.GL_BLEND_DST_RGB);
|
||||
angelica$emissivePass = srcAfter == GL11.GL_ONE && dstAfter == GL11.GL_ONE;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the overlay on emissive eye pass.
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V"),
|
||||
slice = @Slice(
|
||||
from = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;shouldRenderPass(Lnet/minecraft/entity/EntityLivingBase;IF)I"),
|
||||
to = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderEquippedItems(Lnet/minecraft/entity/EntityLivingBase;F)V")
|
||||
),
|
||||
require = 1, expect = 1
|
||||
)
|
||||
private void angelica$untintEmissivePass(ModelBase model, Entity entity,
|
||||
float p1, float p2, float p3, float p4, float p5, float p6, Operation<Void> original) {
|
||||
if (!angelica$skipReRender || !angelica$emissivePass) {
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
return;
|
||||
}
|
||||
if (angelica$ffpOverlayActive) {
|
||||
GLStateManager.setOverlayColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
GLStateManager.setOverlayColor(1.0F, 0.0F, 0.0F, ANGELICA$RED_MIX);
|
||||
} else if (angelica$shaderEntityColorActive) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(1.0F, 0.0F, 0.0F, ANGELICA$RED_MIX);
|
||||
} else {
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void angelica$reset(CallbackInfo ci) {
|
||||
angelica$clearOverlayColor();
|
||||
angelica$skipReRender = false;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$clearOverlayColor() {
|
||||
if (angelica$ffpOverlayActive) {
|
||||
GLStateManager.setOverlayColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
angelica$ffpOverlayActive = false;
|
||||
@@ -166,8 +110,6 @@ public class MixinRendererLivingEntity_OverlayTint {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
angelica$shaderEntityColorActive = false;
|
||||
}
|
||||
angelica$skipReRender = false;
|
||||
angelica$emissivePass = false;
|
||||
}
|
||||
|
||||
@Unique
|
||||
|
||||
+2
-4
@@ -16,10 +16,8 @@ public class MixinEntityRenderer_Zoom {
|
||||
|
||||
@ModifyReturnValue(method = "getFOVModifier", at = @At("RETURN"))
|
||||
private float angelica$modifyFOV(float original) {
|
||||
if (Zoom.getZoomKey().getIsKeyPressed()) {
|
||||
return original / Zoom.getZoom();
|
||||
}
|
||||
return original;
|
||||
final float current = Zoom.getCurrentZoom();
|
||||
return current > 1.0001F ? original / current : original;
|
||||
}
|
||||
|
||||
@ModifyConstant(method = "updateCameraAndRender", constant = @Constant(floatValue = 8.0F))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.mojang.InteractionHand;
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.HandRenderer;
|
||||
@@ -63,6 +64,7 @@ public class MixinItemRenderer {
|
||||
private void iris$glintStart(CallbackInfo ci) {
|
||||
ItemIdManager.resetItemId();
|
||||
GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.GLINT);
|
||||
ShaderGlint.beginGlint();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,5 +77,6 @@ public class MixinItemRenderer {
|
||||
)
|
||||
private void iris$glintEnd(CallbackInfo ci) {
|
||||
GbufferPrograms.teardownSpecialRenderCondition();
|
||||
ShaderGlint.endGlint();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.uniforms.ItemIdManager;
|
||||
@@ -41,6 +42,7 @@ public class MixinRenderItem {
|
||||
private void iris$glintStart(CallbackInfo ci) {
|
||||
ItemIdManager.resetItemId();
|
||||
GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.GLINT);
|
||||
ShaderGlint.beginGlint();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,5 +55,6 @@ public class MixinRenderItem {
|
||||
)
|
||||
private void iris$glintEnd(CallbackInfo ci) {
|
||||
GbufferPrograms.teardownSpecialRenderCondition();
|
||||
ShaderGlint.endGlint();
|
||||
}
|
||||
}
|
||||
+3
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
@@ -80,6 +81,7 @@ public class MixinRendererLivingEntity {
|
||||
)
|
||||
private void iris$glintStart(CallbackInfo ci) {
|
||||
GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.GLINT);
|
||||
ShaderGlint.beginGlint();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +94,7 @@ public class MixinRendererLivingEntity {
|
||||
)
|
||||
private void iris$glintEnd(CallbackInfo ci) {
|
||||
GbufferPrograms.teardownSpecialRenderCondition();
|
||||
ShaderGlint.endGlint();
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Hook into TextureManger to increment the reload count.
|
||||
*/
|
||||
@Mixin(TextureManager.class)
|
||||
public class MixinTextureManager_ReloadCount {
|
||||
|
||||
@Inject(method = "onResourceManagerReload", at = @At("TAIL"))
|
||||
private void angelica$bumpTextureReloadCount(IResourceManager resourceManager, CallbackInfo ci) {
|
||||
CapturedRenderingState.INSTANCE.incrementTextureReloadCount();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user