mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b704d1bc2 | ||
|
|
26135124f4 | ||
|
|
3abc89b7b2 | ||
|
|
03d908d658 | ||
|
|
76bf19c8f6 |
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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1613,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;
|
||||
@@ -2242,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) {
|
||||
@@ -2326,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) {
|
||||
@@ -2426,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));
|
||||
}
|
||||
@@ -2435,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) {
|
||||
@@ -2488,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) {
|
||||
@@ -2536,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) {
|
||||
@@ -5786,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -298,6 +298,7 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinRenderEndPortal"
|
||||
, "shaders.MixinTileEntityRendererDispatcher"
|
||||
, "shaders.MixinGlProgram"
|
||||
, "shaders.MixinTextureManager_ReloadCount"
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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