mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 17:55:08 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29f8d718cc | ||
|
|
52b0db2566 | ||
|
|
e9eb45f4f2 | ||
|
|
92e28a4cd5 | ||
|
|
f46a86889c | ||
|
|
c827d446d5 | ||
|
|
00ef355ce8 |
No files matched your search
@@ -5814,5 +5814,7 @@ public class GLStateManager {
|
||||
public static void glGetUniform(int program, int location, IntBuffer params) {
|
||||
RENDER_BACKEND.getUniformiv(program, location, params);
|
||||
}
|
||||
|
||||
public static void glGetTexImage(int target, int level, int format, int type, long pixels) {
|
||||
GL11.glGetTexImage(target, level, format, type, pixels);
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,11 @@ public class GlslTransformUtils {
|
||||
/** Reserved words added in later GLSL versions that may appear as identifiers in older shaders. */
|
||||
private record ReservedWordRename(Pattern pattern, String replacement) {}
|
||||
private static final Map<Integer, List<ReservedWordRename>> VERSIONED_RESERVED_WORDS = Map.of(
|
||||
// Always rename sample, glsl-transformation-lib doesn't like it
|
||||
0, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsample\\b"), RENAMED_PREFIX + "sample")
|
||||
),
|
||||
400, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsample\\b"), RENAMED_PREFIX + "sample"),
|
||||
new ReservedWordRename(Pattern.compile("\\bsampler\\b(?!\\d)"), RENAMED_PREFIX + "sampler")
|
||||
)
|
||||
);
|
||||
|
||||
@@ -273,21 +273,23 @@ public final class FragmentShaderGenerator {
|
||||
sb.append(" // Alpha test\n");
|
||||
final int func = FragmentKey.decodeAlphaFunc(key.alphaTestFunc());
|
||||
|
||||
sb.append(" float qa = round(color.a * 255.0);\n");
|
||||
sb.append(" float qr = round(u_AlphaRef * 255.0);\n");
|
||||
switch (func) {
|
||||
case GL11.GL_NEVER ->
|
||||
sb.append(" discard;\n");
|
||||
case GL11.GL_LESS ->
|
||||
sb.append(" if (color.a >= u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa >= qr) discard;\n");
|
||||
case GL11.GL_EQUAL ->
|
||||
sb.append(" if (color.a != u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa != qr) discard;\n");
|
||||
case GL11.GL_LEQUAL ->
|
||||
sb.append(" if (color.a > u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa > qr) discard;\n");
|
||||
case GL11.GL_GREATER ->
|
||||
sb.append(" if (color.a <= u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa <= qr) discard;\n");
|
||||
case GL11.GL_NOTEQUAL ->
|
||||
sb.append(" if (color.a == u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa == qr) discard;\n");
|
||||
case GL11.GL_GEQUAL ->
|
||||
sb.append(" if (color.a < u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa < qr) discard;\n");
|
||||
case GL11.GL_ALWAYS -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -79,6 +80,9 @@ public class GLSMRedirector {
|
||||
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_SCISSOR_TEST, "ScissorTest");
|
||||
|
||||
final var gl11 = RedirectMap.newMap()
|
||||
// glEnable/Disable - Special cased in GLSMRedirector, but included here for the external API
|
||||
.add("glEnable")
|
||||
.add("glDisable")
|
||||
.add("glAlphaFunc")
|
||||
.add("glBegin")
|
||||
.add("glBindTexture")
|
||||
@@ -495,6 +499,22 @@ public class GLSMRedirector {
|
||||
return CORE_EXCLUSIONS.clone();
|
||||
}
|
||||
|
||||
/** The internal-name prefix used to match GL version classes (e.g., {@code "org/lwjgl/opengl/GL"}). */
|
||||
public static String getGLPrefix() { return GL_PREFIX; }
|
||||
|
||||
/** The internal name of the redirect target class. */
|
||||
public static String getTargetClassName() { return GLStateManager; }
|
||||
|
||||
/** Method redirects for GL-prefix classes. Key: original method name, Value: GLStateManager method name. */
|
||||
public static Map<String, String> getGLPrefixMethodRedirects() {
|
||||
return Collections.unmodifiableMap(glMethodRedirects);
|
||||
}
|
||||
|
||||
/** Method redirects for named (non-GL-prefix) classes. Key: internal class name, Value: per-method redirect map. */
|
||||
public static Map<String, Map<String, String>> getNamedClassMethodRedirects() {
|
||||
return Collections.unmodifiableMap(methodRedirects);
|
||||
}
|
||||
|
||||
public boolean shouldTransform(byte[] basicClass) {
|
||||
return cstPoolParser.find(basicClass, true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Public API for querying Angelica's GL call redirections.
|
||||
* <p>
|
||||
* Angelica's bytecode transformer redirects GL calls (e.g., {@code GL11.glPushMatrix()}) to
|
||||
* {@link com.gtnewhorizons.angelica.glsm.GLStateManager GLStateManager} for state tracking and
|
||||
* core profile compatibility. This works for normal Java bytecode loaded through the Forge classloader,
|
||||
* but callers that bypass class transformation (e.g., Nashorn/script engines, reflection, MethodHandles)
|
||||
* need to perform their own redirection.
|
||||
* <p>
|
||||
* This class provides the redirect mapping data and a precompiled regex pattern for transforming
|
||||
* script source text before evaluation.
|
||||
*
|
||||
* <h3>Example: transforming a Nashorn script before eval</h3>
|
||||
* Given a script that uses {@code importPackage(Packages.org.lwjgl.opengl)} and calls
|
||||
* {@code GL11.glPushMatrix()}, the text transformation rewrites those calls before evaluation:
|
||||
* <pre>{@code
|
||||
* // Make GLStateManager available to the script -- either via importPackage:
|
||||
* script = "importPackage(Packages.com.gtnewhorizons.angelica.glsm);\n" + script;
|
||||
* // ... or via Java.type for an explicit binding:
|
||||
* // script = "var GLStateManager = Java.type('" + GLRedirects.TARGET_CLASS + "');\n" + script;
|
||||
*
|
||||
* // Rewrite: GL11.glPushMatrix() -> GLStateManager.glPushMatrix()
|
||||
* script = GLRedirects.getMethodRedirectPattern().matcher(script).replaceAll(mr -> {
|
||||
* String target = GLRedirects.getTargetMethodName(mr.group(0));
|
||||
* return target != null ? "GLStateManager." + target : mr.group(0);
|
||||
* });
|
||||
*
|
||||
* engine.eval(script);
|
||||
* }</pre>
|
||||
*/
|
||||
public final class GLRedirects {
|
||||
/** Fully qualified name of the GLStateManager class that all methods redirect to. */
|
||||
@SuppressWarnings("unused")
|
||||
public static final String TARGET_CLASS = "com.gtnewhorizons.angelica.glsm.GLStateManager";
|
||||
|
||||
private static final String SHORT_GL_PREFIX;
|
||||
private static final Map<String, Map<String, String>> NAMED_CLASS_SHORT_NAME_MAP;
|
||||
private static final Map<String, String> GL_PREFIX_METHOD_REDIRECTS;
|
||||
private static final Pattern METHOD_REDIRECT_PATTERN;
|
||||
|
||||
static {
|
||||
SHORT_GL_PREFIX = buildShortGLPrefix();
|
||||
NAMED_CLASS_SHORT_NAME_MAP = buildNamedClassShortNameMap();
|
||||
GL_PREFIX_METHOD_REDIRECTS = GLSMRedirector.getGLPrefixMethodRedirects();
|
||||
METHOD_REDIRECT_PATTERN = buildMethodRedirectPattern();
|
||||
}
|
||||
|
||||
private GLRedirects() {}
|
||||
|
||||
/**
|
||||
* Returns a precompiled pattern that matches redirected {@code ClassName.methodName} references
|
||||
* using short (simple) class names, as they might typically appear in script text.
|
||||
*
|
||||
* @return compiled regex pattern
|
||||
*/
|
||||
public static Pattern getMethodRedirectPattern() {
|
||||
return METHOD_REDIRECT_PATTERN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the GLStateManager target method name for a short-name reference.
|
||||
*
|
||||
* @param classAndMethod short class name and method, e.g., {@code "GL11.glPushMatrix"}
|
||||
* @return the target method name on GLStateManager (e.g., {@code "glPushMatrix"}), or {@code null} if this reference is not redirected
|
||||
*/
|
||||
public static String getTargetMethodName(String classAndMethod) {
|
||||
final int dot = classAndMethod.indexOf('.');
|
||||
if (dot < 0) return null;
|
||||
|
||||
final String className = classAndMethod.substring(0, dot);
|
||||
final String methodName = classAndMethod.substring(dot + 1);
|
||||
|
||||
final Map<String, String> classRedirects = NAMED_CLASS_SHORT_NAME_MAP.get(className);
|
||||
if (classRedirects != null) {
|
||||
return classRedirects.get(methodName);
|
||||
}
|
||||
|
||||
if (className.startsWith(SHORT_GL_PREFIX)) {
|
||||
return GL_PREFIX_METHOD_REDIRECTS.get(methodName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String buildShortGLPrefix() {
|
||||
final String glPrefix = GLSMRedirector.getGLPrefix();
|
||||
return glPrefix.substring(glPrefix.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
private static Map<String, Map<String, String>> buildNamedClassShortNameMap() {
|
||||
final Map<String, Map<String, String>> named = new HashMap<>();
|
||||
for (final var entry : GLSMRedirector.getNamedClassMethodRedirects().entrySet()) {
|
||||
final String internalName = entry.getKey();
|
||||
final String shortName = internalName.substring(internalName.lastIndexOf('/') + 1);
|
||||
named.put(shortName, entry.getValue());
|
||||
}
|
||||
return named;
|
||||
}
|
||||
|
||||
private static Pattern buildMethodRedirectPattern() {
|
||||
final TreeSet<String> alternatives = new TreeSet<>();
|
||||
alternatives.add(Pattern.quote(SHORT_GL_PREFIX) + "\\w+");
|
||||
for (final String shortName : NAMED_CLASS_SHORT_NAME_MAP.keySet()) {
|
||||
alternatives.add(Pattern.quote(shortName));
|
||||
}
|
||||
final String classAlt = String.join("|", alternatives);
|
||||
return Pattern.compile("\\b(" + classAlt + ")\\.\\w+\\b");
|
||||
}
|
||||
}
|
||||
+47
-20
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.client.rendering;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.ITessellatorInstance;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -33,10 +34,30 @@ class DeferredBatchTessellator extends DirectTessellator {
|
||||
private final Vector3f scratch = new Vector3f();
|
||||
private Matrix4fc currentTransform;
|
||||
|
||||
private Tessellator parentTessellator;
|
||||
private boolean inIntercept;
|
||||
|
||||
DeferredBatchTessellator(ByteBuffer buffer) {
|
||||
super(buffer, false); // deleteAfter=false: keep buffer across resets for reuse
|
||||
}
|
||||
|
||||
void setParentTessellator(Tessellator parent) {
|
||||
this.parentTessellator = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int draw() {
|
||||
if (!inIntercept) {
|
||||
final Tessellator parent = parentTessellator;
|
||||
if (parent != null && parent.isDrawing) {
|
||||
interceptDraw(parent);
|
||||
((ITessellatorInstance) parent).discard();
|
||||
}
|
||||
}
|
||||
isDrawing = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long writeVertexData(VertexFormat format, int[] rawBuffer, int rawBufferIndex) {
|
||||
if (currentTransform != null) {
|
||||
@@ -53,27 +74,32 @@ class DeferredBatchTessellator extends DirectTessellator {
|
||||
|
||||
@Override
|
||||
protected int interceptDraw(Tessellator tess) {
|
||||
// Compute delta: if current MV differs from default, pre-transform vertices
|
||||
final Matrix4f currentMV = GLStateManager.getModelViewMatrix();
|
||||
if (!currentMV.equals(defaultModelview)) {
|
||||
defaultModelviewInverse.mul(currentMV, deltaMatrix);
|
||||
this.currentTransform = deltaMatrix;
|
||||
} else {
|
||||
this.currentTransform = null;
|
||||
inIntercept = true;
|
||||
try {
|
||||
// Compute delta: if current MV differs from default, pre-transform vertices
|
||||
final Matrix4f currentMV = GLStateManager.getModelViewMatrix();
|
||||
if (!currentMV.equals(defaultModelview)) {
|
||||
defaultModelviewInverse.mul(currentMV, deltaMatrix);
|
||||
this.currentTransform = deltaMatrix;
|
||||
} else {
|
||||
this.currentTransform = null;
|
||||
}
|
||||
|
||||
final int byteOffsetBefore = bufferLimit();
|
||||
final int vertCount = tess.vertexCount;
|
||||
final int drawMode = tess.drawMode;
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
|
||||
final long stateKey = DeferredDrawBatcher.captureStateKey();
|
||||
|
||||
final int result = super.interceptDraw(tess); // repacks int[] -> ByteBuffer + transforms if vertexTransform set
|
||||
|
||||
final int byteOffsetAfter = bufferLimit();
|
||||
ranges.add(new DrawRange(stateKey, byteOffsetBefore, byteOffsetAfter - byteOffsetBefore, vertCount, drawMode, flags));
|
||||
this.currentTransform = null; // clear for next draw
|
||||
return result;
|
||||
} finally {
|
||||
inIntercept = false;
|
||||
}
|
||||
|
||||
final int byteOffsetBefore = bufferLimit();
|
||||
final int vertCount = tess.vertexCount;
|
||||
final int drawMode = tess.drawMode;
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
|
||||
final long stateKey = DeferredDrawBatcher.captureStateKey();
|
||||
|
||||
final int result = super.interceptDraw(tess); // repacks int[] → ByteBuffer + transforms if vertexTransform set
|
||||
|
||||
final int byteOffsetAfter = bufferLimit();
|
||||
ranges.add(new DrawRange(stateKey, byteOffsetBefore, byteOffsetAfter - byteOffsetBefore, vertCount, drawMode, flags));
|
||||
this.currentTransform = null; // clear for next draw
|
||||
return result;
|
||||
}
|
||||
|
||||
List<DrawRange> getRanges() { return ranges; }
|
||||
@@ -86,6 +112,7 @@ class DeferredBatchTessellator extends DirectTessellator {
|
||||
|
||||
@Override
|
||||
protected void onRemovedFromStack() {
|
||||
parentTessellator = null;
|
||||
clearRanges();
|
||||
super.onRemovedFromStack(); // calls reset(), keeps baseBuffer (deleteAfter=false)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.client.rendering;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.Comparator;
|
||||
@@ -37,6 +38,7 @@ public class DeferredDrawBatcher {
|
||||
batchTessellator = new DeferredBatchTessellator(memAlloc(INITIAL_BUFFER_SIZE));
|
||||
}
|
||||
batchTessellator.clearRanges();
|
||||
batchTessellator.setParentTessellator(Tessellator.instance);
|
||||
batchTessellator.snapshotDefaultModelview();
|
||||
TessellatorManager.startCapturingDirect(batchTessellator);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public class AngelicaRfbPlugin implements RfbPlugin {
|
||||
@Override
|
||||
public void onConstruction(@NotNull PluginContext ctx) {
|
||||
Launch.blackboard.put("angelica.rfbPluginLoaded", Boolean.TRUE);
|
||||
Launch.classLoader.addClassLoaderExclusion("com.gtnewhorizons.angelica.glsm.redirect.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,7 +37,10 @@ public enum Mixins implements IMixins {
|
||||
, "angelica.MixinMinecraft_FrameHook"
|
||||
, "angelica.MixinMinecraftServer"
|
||||
, "angelica.bugfixes.MixinItemRenderer_EdgeDepth"
|
||||
, "angelica.bugfixes.MixinModelWither_ArmorCentering"
|
||||
, "angelica.bugfixes.MixinRenderBlocks_CrossedSquaresNormal"
|
||||
, "angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
|
||||
, "angelica.bugfixes.MixinRenderWither_ArmorCentering"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_EyeDepth"
|
||||
, "angelica.debug.MixinMinecraft_FPSCap"
|
||||
, "angelica.ffp.MixinTessellator_CoreProfile"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
public class WitherArmorState {
|
||||
public static boolean pendingInflate = false;
|
||||
}
|
||||
@@ -200,14 +200,17 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
public DeferredWorldRenderingPipeline(ProgramSet programs) {
|
||||
Objects.requireNonNull(programs);
|
||||
|
||||
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> prepareTransformFutures = submitCompositeTransforms(programs.getPrepare());
|
||||
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> deferredTransformFutures = submitCompositeTransforms(programs.getDeferred());
|
||||
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> compositeTransformFutures = submitCompositeTransforms(programs.getComposite());
|
||||
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> prepareTransformFutures =
|
||||
submitCompositeTransforms(programs.getPrepare(), TextureStage.PREPARE, programs.getPackDirectives().getTextureMap());
|
||||
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> deferredTransformFutures =
|
||||
submitCompositeTransforms(programs.getDeferred(), TextureStage.DEFERRED, programs.getPackDirectives().getTextureMap());
|
||||
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> compositeTransformFutures =
|
||||
submitCompositeTransforms(programs.getComposite(), TextureStage.COMPOSITE_AND_FINAL, programs.getPackDirectives().getTextureMap());
|
||||
|
||||
final CompletableFuture<Map<PatchShaderType, String>> finalTransformFuture =
|
||||
programs.getCompositeFinal()
|
||||
.filter(ProgramSource::isValid)
|
||||
.map(DeferredWorldRenderingPipeline::submitCompositeTransform)
|
||||
.map(source -> submitCompositeTransform(source, TextureStage.COMPOSITE_AND_FINAL, programs.getPackDirectives().getTextureMap()))
|
||||
.orElse(null);
|
||||
|
||||
resolver = new ProgramFallbackResolver(programs);
|
||||
@@ -1710,11 +1713,20 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
private static final InputAvailability INPUT_TEXTURE_LIGHTMAP = new InputAvailability(true, true);
|
||||
private static final InputAvailability[] INPUT_AVAILABILITIES = { INPUT_NONE, INPUT_TEXTURE, INPUT_TEXTURE_LIGHTMAP };
|
||||
|
||||
private static CompletableFuture<Map<PatchShaderType, String>> submitCompositeTransform(ProgramSource source) {
|
||||
return Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchComposite(source.getVertexSource().orElse(null), source.getGeometrySource().orElse(null), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getFragmentSource().orElse(null)));
|
||||
private static CompletableFuture<Map<PatchShaderType, String>> submitCompositeTransform(ProgramSource source, TextureStage stage,
|
||||
Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap) {
|
||||
return Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchComposite(
|
||||
source.getVertexSource().orElse(null),
|
||||
source.getGeometrySource().orElse(null),
|
||||
source.getTessControlSource().orElse(null),
|
||||
source.getTessEvalSource().orElse(null),
|
||||
source.getFragmentSource().orElse(null),
|
||||
stage,
|
||||
textureMap));
|
||||
}
|
||||
|
||||
private static Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> submitCompositeTransforms(ProgramSource[] sources) {
|
||||
private static Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> submitCompositeTransforms(ProgramSource[] sources,
|
||||
TextureStage stage, Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap) {
|
||||
// Count valid sources for initial capacity
|
||||
int count = 0;
|
||||
for (ProgramSource source : sources) {
|
||||
@@ -1723,7 +1735,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> futures = new HashMap<>(count);
|
||||
for (int i = 0; i < sources.length; i++) {
|
||||
if (sources[i] != null && sources[i].isValid()) {
|
||||
futures.put(i, submitCompositeTransform(sources[i]));
|
||||
futures.put(i, submitCompositeTransform(sources[i], stage, textureMap));
|
||||
}
|
||||
}
|
||||
return futures;
|
||||
|
||||
@@ -448,6 +448,7 @@ public class ShaderTransformer {
|
||||
default:
|
||||
throw new IllegalStateException("Unknown patch type: " + patchType.name());
|
||||
}
|
||||
TextureTransformer.transform(transformer, parameters);
|
||||
CompatibilityTransformer.transformEach(transformer, parameters);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.coderbot.iris.pipeline.transform;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import net.coderbot.iris.gl.texture.TextureType;
|
||||
import net.coderbot.iris.helpers.Tri;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.Parameters;
|
||||
import net.coderbot.iris.shaderpack.texture.TextureStage;
|
||||
import org.taumc.glsl.Transformer;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
|
||||
final class TextureTransformer {
|
||||
private TextureTransformer() {
|
||||
}
|
||||
|
||||
static void transform(Transformer transformer, Parameters parameters) {
|
||||
final Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap = parameters.getTextureMap();
|
||||
if (textureMap == null || textureMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final TextureStage stage = parameters.getTextureStage();
|
||||
textureMap.forEach((key, replacement) -> {
|
||||
if (key.third() != stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String name = key.first();
|
||||
final int type = transformer.findType(name);
|
||||
if (type != 0 && isTypeValid(key.second(), type)) {
|
||||
transformer.rename(name, replacement);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isTypeValid(TextureType expectedType, int extractedType) {
|
||||
return switch (expectedType) {
|
||||
case TEXTURE_1D -> extractedType == GLSLLexer.SAMPLER1D ||
|
||||
extractedType == GLSLLexer.ISAMPLER1D ||
|
||||
extractedType == GLSLLexer.USAMPLER1D;
|
||||
case TEXTURE_RECTANGLE -> extractedType == GLSLLexer.SAMPLER2DRECT ||
|
||||
extractedType == GLSLLexer.ISAMPLER2DRECT ||
|
||||
extractedType == GLSLLexer.USAMPLER2DRECT;
|
||||
case TEXTURE_2D -> extractedType == GLSLLexer.SAMPLER2D ||
|
||||
extractedType == GLSLLexer.ISAMPLER2D ||
|
||||
extractedType == GLSLLexer.USAMPLER2D;
|
||||
case TEXTURE_3D -> extractedType == GLSLLexer.SAMPLER3D ||
|
||||
extractedType == GLSLLexer.ISAMPLER3D ||
|
||||
extractedType == GLSLLexer.USAMPLER3D;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -120,10 +120,6 @@ public class TransformPatcher {
|
||||
return patchComposite(vertex, geometry, null, null, fragment, TextureStage.COMPOSITE_AND_FINAL, null);
|
||||
}
|
||||
|
||||
public static Map<PatchShaderType, String> patchComposite(String vertex, String geometry, String tessControl, String tessEval, String fragment) {
|
||||
return patchComposite(vertex, geometry, tessControl, tessEval, fragment, TextureStage.COMPOSITE_AND_FINAL, null);
|
||||
}
|
||||
|
||||
public static Map<PatchShaderType, String> patchComposite(String vertex, String geometry, String tessControl, String tessEval, String fragment, TextureStage stage, Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap) {
|
||||
return transform(vertex, geometry, tessControl, tessEval, fragment, new TextureStageParameters(Patch.COMPOSITE, stage, textureMap));
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ public class FinalPassRenderer {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
private static Map<PatchShaderType, String> getTransformed(ProgramSource source, CompletableFuture<Map<PatchShaderType, String>> precomputedTransformFuture, String stageName) {
|
||||
private Map<PatchShaderType, String> getTransformed(ProgramSource source, CompletableFuture<Map<PatchShaderType, String>> precomputedTransformFuture, String stageName) {
|
||||
if (precomputedTransformFuture != null) {
|
||||
try {
|
||||
final Map<PatchShaderType, String> result = precomputedTransformFuture.join();
|
||||
@@ -157,7 +157,14 @@ public class FinalPassRenderer {
|
||||
throw new RuntimeException("Shader transformation failed for '" + source.getName() + "' in stage '" + stageName + "'", e.getCause() != null ? e.getCause() : e);
|
||||
}
|
||||
}
|
||||
return TransformPatcher.patchComposite(source.getVertexSource().orElseThrow(NullPointerException::new), source.getGeometrySource().orElse(null), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getFragmentSource().orElseThrow(NullPointerException::new));
|
||||
return TransformPatcher.patchComposite(
|
||||
source.getVertexSource().orElseThrow(NullPointerException::new),
|
||||
source.getGeometrySource().orElse(null),
|
||||
source.getTessControlSource().orElse(null),
|
||||
source.getTessEvalSource().orElse(null),
|
||||
source.getFragmentSource().orElseThrow(NullPointerException::new),
|
||||
TextureStage.COMPOSITE_AND_FINAL,
|
||||
pipeline != null ? pipeline.getTextureMap() : null);
|
||||
}
|
||||
|
||||
private static final class Pass {
|
||||
|
||||
@@ -263,7 +263,9 @@ public class ShadowCompositeRenderer {
|
||||
source.getGeometrySource().orElse(null),
|
||||
source.getTessControlSource().orElse(null),
|
||||
source.getTessEvalSource().orElse(null),
|
||||
source.getFragmentSource().orElseThrow(NullPointerException::new));
|
||||
source.getFragmentSource().orElseThrow(NullPointerException::new),
|
||||
TextureStage.SHADOWCOMP,
|
||||
pipeline.getTextureMap());
|
||||
String vertex = transformed.get(PatchShaderType.VERTEX);
|
||||
String geometry = transformed.get(PatchShaderType.GEOMETRY);
|
||||
String tessControl = transformed.get(PatchShaderType.TESS_CONTROL);
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.WitherArmorState;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.client.model.ModelWither;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Creates a centered armor model for the Wither.
|
||||
*
|
||||
* Vanilla 1.7.10 uses glScalef(1.1) on the base model for the armor overlay, which
|
||||
* shifts off-center and rotating parts away from their correct positions. Modern Minecraft
|
||||
* (1.21+) replaced this with a separate model using CubeDeformation(0.5).
|
||||
* This mixin backports that approach.
|
||||
*/
|
||||
@Mixin(ModelWither.class)
|
||||
public class MixinModelWither_ArmorCentering {
|
||||
|
||||
// Contains body segments: [0] shoulder, [1] upper body + ribs, [2] tail
|
||||
@Shadow private ModelRenderer[] field_82905_a;
|
||||
|
||||
// Heads: [0] center, [1] left, [2] right
|
||||
@Shadow private ModelRenderer[] field_82904_b;
|
||||
|
||||
private static final float ARMOR_INFLATE = 0.5F;
|
||||
|
||||
// If the armor inflate flag is set, rebuild all boxes with uniform inflate.
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void angelica$applyInflate(CallbackInfo ci) {
|
||||
if (!WitherArmorState.pendingInflate) return;
|
||||
WitherArmorState.pendingInflate = false;
|
||||
|
||||
ModelWither self = (ModelWither) (Object) this;
|
||||
|
||||
// [0] Shoulder
|
||||
field_82905_a[0] = new ModelRenderer(self, 0, 16);
|
||||
field_82905_a[0].addBox(-10.0F, 3.9F, -0.5F, 20, 3, 3, ARMOR_INFLATE);
|
||||
|
||||
// [1] Upper body (spine + ribs things)
|
||||
field_82905_a[1] = (new ModelRenderer(self)).setTextureSize(self.textureWidth, self.textureHeight);
|
||||
field_82905_a[1].setRotationPoint(-2.0F, 6.9F, -0.5F);
|
||||
field_82905_a[1].setTextureOffset(0, 22).addBox(0.0F, 0.0F, 0.0F, 3, 10, 3, ARMOR_INFLATE);
|
||||
field_82905_a[1].setTextureOffset(24, 22).addBox(-4.0F, 1.5F, 0.5F, 11, 2, 2, ARMOR_INFLATE);
|
||||
field_82905_a[1].setTextureOffset(24, 22).addBox(-4.0F, 4.0F, 0.5F, 11, 2, 2, ARMOR_INFLATE);
|
||||
field_82905_a[1].setTextureOffset(24, 22).addBox(-4.0F, 6.5F, 0.5F, 11, 2, 2, ARMOR_INFLATE);
|
||||
|
||||
// [2] Tail
|
||||
field_82905_a[2] = new ModelRenderer(self, 12, 22);
|
||||
field_82905_a[2].addBox(0.0F, 0.0F, 0.0F, 3, 6, 3, ARMOR_INFLATE);
|
||||
|
||||
// [0] Center head
|
||||
field_82904_b[0] = new ModelRenderer(self, 0, 0);
|
||||
field_82904_b[0].addBox(-4.0F, -4.0F, -4.0F, 8, 8, 8, ARMOR_INFLATE);
|
||||
|
||||
// [1] Left head
|
||||
field_82904_b[1] = new ModelRenderer(self, 32, 0);
|
||||
field_82904_b[1].addBox(-4.0F, -4.0F, -4.0F, 6, 6, 6, ARMOR_INFLATE);
|
||||
field_82904_b[1].rotationPointX = -8.0F;
|
||||
field_82904_b[1].rotationPointY = 4.0F;
|
||||
|
||||
// [2] Right head
|
||||
field_82904_b[2] = new ModelRenderer(self, 32, 0);
|
||||
field_82904_b[2].addBox(-4.0F, -4.0F, -4.0F, 6, 6, 6, ARMOR_INFLATE);
|
||||
field_82904_b[2].rotationPointX = 10.0F;
|
||||
field_82904_b[2].rotationPointY = 4.0F;
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
|
||||
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(RenderBlocks.class)
|
||||
public abstract class MixinRenderBlocks_CrossedSquaresNormal {
|
||||
|
||||
@Shadow public IIcon overrideBlockTexture;
|
||||
@Shadow public abstract boolean hasOverrideBlockTexture();
|
||||
|
||||
/**
|
||||
* Vanilla sets a single (0, -1, 0) normal for all crossed-square vertices in renderBlockAsItem.
|
||||
* This downward-pointing normal causes shader packs to compute incorrect shadow bias (pushing the
|
||||
* shadow sample into the parent entity's body, e.g. mooshroom and anything that extends mooshrooms)
|
||||
* and zero diffuse sun lighting.
|
||||
*
|
||||
* This mixin replaces the singular normal with per-quad face normals perpendicular to each diagonal plane.
|
||||
* This gives each face correct directional lighting and shadow bias that pushes samples away from the face rather
|
||||
* than into the entity body.
|
||||
*/
|
||||
@Redirect(
|
||||
method = "renderBlockAsItem(Lnet/minecraft/block/Block;IF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;drawCrossedSquares(Lnet/minecraft/util/IIcon;DDDF)V")
|
||||
)
|
||||
private void angelica$drawCrossedSquaresWithPerFaceNormals(RenderBlocks instance, IIcon icon, double x, double y, double z, float scale) {
|
||||
if (this.hasOverrideBlockTexture()) {
|
||||
icon = this.overrideBlockTexture;
|
||||
}
|
||||
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
final double minU = icon.getMinU();
|
||||
final double minV = icon.getMinV();
|
||||
final double maxU = icon.getMaxU();
|
||||
final double maxV = icon.getMaxV();
|
||||
|
||||
final double d7 = 0.45D * scale;
|
||||
final double x0 = x + 0.5D - d7;
|
||||
final double x1 = x + 0.5D + d7;
|
||||
final double z0 = z + 0.5D - d7;
|
||||
final double z1 = z + 0.5D + d7;
|
||||
final double yTop = y + (double) scale;
|
||||
|
||||
final float inv_sqrt_2 = (float) (1.0 / Math.sqrt(2.0));
|
||||
|
||||
tess.setNormal(inv_sqrt_2, 0.0F, -inv_sqrt_2);
|
||||
tess.addVertexWithUV(x0, yTop, z0, minU, minV);
|
||||
tess.addVertexWithUV(x0, y, z0, minU, maxV);
|
||||
tess.addVertexWithUV(x1, y, z1, maxU, maxV);
|
||||
tess.addVertexWithUV(x1, yTop, z1, maxU, minV);
|
||||
|
||||
tess.setNormal(-inv_sqrt_2, 0.0F, inv_sqrt_2);
|
||||
tess.addVertexWithUV(x1, yTop, z1, minU, minV);
|
||||
tess.addVertexWithUV(x1, y, z1, minU, maxV);
|
||||
tess.addVertexWithUV(x0, y, z0, maxU, maxV);
|
||||
tess.addVertexWithUV(x0, yTop, z0, maxU, minV);
|
||||
|
||||
tess.setNormal(-inv_sqrt_2, 0.0F, -inv_sqrt_2);
|
||||
tess.addVertexWithUV(x0, yTop, z1, minU, minV);
|
||||
tess.addVertexWithUV(x0, y, z1, minU, maxV);
|
||||
tess.addVertexWithUV(x1, y, z0, maxU, maxV);
|
||||
tess.addVertexWithUV(x1, yTop, z0, maxU, minV);
|
||||
|
||||
tess.setNormal(inv_sqrt_2, 0.0F, inv_sqrt_2);
|
||||
tess.addVertexWithUV(x1, yTop, z0, minU, minV);
|
||||
tess.addVertexWithUV(x1, y, z0, minU, maxV);
|
||||
tess.addVertexWithUV(x0, y, z1, maxU, maxV);
|
||||
tess.addVertexWithUV(x0, yTop, z1, maxU, minV);
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.WitherArmorState;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.model.ModelWither;
|
||||
import net.minecraft.client.renderer.entity.RenderWither;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Replaces the Wither's glScalef(1.1) armor approach with a per-box inflated model.
|
||||
* This fixes the armor being visually off-center on off-center and rotating parts.
|
||||
* Not exactly mod friendly but we can figure that out later.
|
||||
*/
|
||||
@Mixin(RenderWither.class)
|
||||
public class MixinRenderWither_ArmorCentering {
|
||||
|
||||
@Unique
|
||||
private ModelWither angelica$armorModel;
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void angelica$createArmorModel(CallbackInfo ci) {
|
||||
WitherArmorState.pendingInflate = true;
|
||||
angelica$armorModel = new ModelWither();
|
||||
}
|
||||
|
||||
// Use the inflated armor model instead of mainModel.
|
||||
@Redirect(
|
||||
method = "shouldRenderPass(Lnet/minecraft/entity/boss/EntityWither;IF)I",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RenderWither;setRenderPassModel(Lnet/minecraft/client/model/ModelBase;)V")
|
||||
)
|
||||
private void angelica$useInflatedModel(RenderWither instance, ModelBase model) {
|
||||
((RenderWither) (Object) this).setRenderPassModel(angelica$armorModel);
|
||||
}
|
||||
|
||||
// No-op glTranslatef
|
||||
@Redirect(
|
||||
method = "shouldRenderPass(Lnet/minecraft/entity/boss/EntityWither;IF)I",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lorg/lwjgl/opengl/GL11;glTranslatef(FFF)V", ordinal = 1, remap = false)
|
||||
)
|
||||
private void angelica$skipTranslate(float x, float y, float z) {
|
||||
}
|
||||
|
||||
// No-op glScalef
|
||||
@Redirect(
|
||||
method = "shouldRenderPass(Lnet/minecraft/entity/boss/EntityWither;IF)I",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lorg/lwjgl/opengl/GL11;glScalef(FFF)V", remap = false)
|
||||
)
|
||||
private void angelica$skipScale(float x, float y, float z) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class GLRedirectsTest {
|
||||
|
||||
@ParameterizedTest(name = "{0}.{1} -> {2}")
|
||||
@CsvSource({
|
||||
"GL11, glPushMatrix, glPushMatrix",
|
||||
"GL11, glPopMatrix, glPopMatrix",
|
||||
"GL11, glTranslatef, glTranslatef",
|
||||
"GL11, glRotatef, glRotatef",
|
||||
"GL11, glScalef, glScalef",
|
||||
"GL11, glEnable, glEnable",
|
||||
"GL11, glDisable, glDisable",
|
||||
"GL11, glBegin, glBegin",
|
||||
"GL11, glEnd, glEnd",
|
||||
"GL11, glVertex3f, glVertex3f",
|
||||
"GL11, glColor4f, glColor4f",
|
||||
"GL11, glNormal3f, glNormal3f",
|
||||
"GL11, glTexCoord2f, glTexCoord2f",
|
||||
"GL11, glBindTexture, glBindTexture",
|
||||
"GL11, glBlendFunc, glBlendFunc",
|
||||
"GL11, glDepthMask, glDepthMask",
|
||||
"GL11, glDepthFunc, glDepthFunc",
|
||||
"GL11, glAlphaFunc, glAlphaFunc",
|
||||
"GL11, glMatrixMode, glMatrixMode",
|
||||
"GL11, glLoadIdentity, glLoadIdentity",
|
||||
"GL11, glShadeModel, glShadeModel",
|
||||
"GL11, glDrawArrays, glDrawArrays",
|
||||
"GL11, glDrawElements, glDrawElements",
|
||||
"GL11, glNewList, glNewList",
|
||||
"GL11, glEndList, glEndList",
|
||||
"GL11, glCallList, glCallList",
|
||||
"GL11, glViewport, glViewport",
|
||||
"GL12, glTexImage3D, glTexImage3D",
|
||||
"GL13, glActiveTexture, glActiveTexture",
|
||||
"GL13, glMultiTexCoord2f, glMultiTexCoord2f",
|
||||
"GL14, glBlendFuncSeparate, tryBlendFuncSeparate",
|
||||
"GL14, glBlendColor, glBlendColor",
|
||||
"GL15, glGenBuffers, glGenBuffers",
|
||||
"GL15, glBindBuffer, glBindBuffer",
|
||||
"GL15, glBufferData, glBufferData",
|
||||
"GL15, glDeleteBuffers, glDeleteBuffers",
|
||||
"GL20, glUseProgram, glUseProgram",
|
||||
"GL20, glCreateShader, glCreateShader",
|
||||
"GL20, glShaderSource, glShaderSource",
|
||||
"GL20, glLinkProgram, glLinkProgram",
|
||||
"GL20, glUniform1f, glUniform1f",
|
||||
"GL20, glUniform4f, glUniform4f",
|
||||
"GL20, glGetUniformLocation, glGetUniformLocation",
|
||||
"GL20, glVertexAttribPointer, glVertexAttribPointer",
|
||||
"GL30, glGenFramebuffers, glGenFramebuffers",
|
||||
"GL30, glBindFramebuffer, glBindFramebuffer",
|
||||
"GL30, glGenVertexArrays, glGenVertexArrays",
|
||||
"GL30, glBindVertexArray, glBindVertexArray",
|
||||
"GL30, glGenerateMipmap, glGenerateMipmap",
|
||||
"GL31, glDrawElementsInstanced, glDrawElementsInstanced",
|
||||
"GL33, glGenSamplers, glGenSamplers",
|
||||
"GL33, glBindSampler, glBindSampler",
|
||||
"GL42, glTexStorage2D, glTexStorage2D",
|
||||
"GL43, glDispatchCompute, glDispatchCompute",
|
||||
"GL44, glBufferStorage, glBufferStorage",
|
||||
})
|
||||
void glVersionClassMethodRedirects(String className, String method, String expected) {
|
||||
assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}.{1} -> {2}")
|
||||
@CsvSource({
|
||||
"GL11C, glDrawArrays, glDrawArrays",
|
||||
"GL20C, glUseProgram, glUseProgram",
|
||||
"GL30C, glBindFramebuffer, glBindFramebuffer",
|
||||
"GL33C, glBindSampler, glBindSampler",
|
||||
})
|
||||
void cVariantsUseGLPrefixMap(String className, String method, String expected) {
|
||||
assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}.{1} -> {2}")
|
||||
@CsvSource({
|
||||
"EXTBlendFuncSeparate, glBlendFuncSeparateEXT, tryBlendFuncSeparate",
|
||||
"ARBMultitexture, glActiveTextureARB, glActiveTextureARB",
|
||||
"ARBShaderObjects, glUseProgramObjectARB, glUseProgram",
|
||||
"ARBShaderObjects, glCreateShaderObjectARB, glCreateShader",
|
||||
"ARBShaderObjects, glCompileShaderARB, glCompileShader",
|
||||
"ARBShaderObjects, glAttachObjectARB, glAttachShader",
|
||||
"ARBShaderObjects, glUniform1fARB, glUniform1f",
|
||||
"ARBShaderObjects, glUniformMatrix4ARB, glUniformMatrix4",
|
||||
"ARBShaderObjects, glGetUniformLocationARB, glGetUniformLocation",
|
||||
"ARBVertexArrayObject, glBindVertexArray, glBindVertexArray",
|
||||
"ARBInstancedArrays, glVertexAttribDivisorARB, glVertexAttribDivisorARB",
|
||||
"APPLEVertexArrayObject, glBindVertexArrayAPPLE, glBindVertexArray",
|
||||
"UniversalVAO, bindVertexArray, glBindVertexArray",
|
||||
"UniversalVAO, deleteVertexArrays, glDeleteVertexArrays",
|
||||
"OpenGlHelper, glBlendFunc, tryBlendFuncSeparate",
|
||||
"OpenGlHelper, setActiveTexture, setActiveTexture",
|
||||
"OpenGlHelper, setLightmapTextureCoords, setLightmapTextureCoords",
|
||||
"OpenGlHelper, isFramebufferEnabled, isFramebufferEnabled",
|
||||
"GLU, gluPerspective, gluPerspective",
|
||||
"GLU, gluLookAt, gluLookAt",
|
||||
"GLU, gluOrtho2D, gluOrtho2D",
|
||||
"Project, gluPerspective, gluPerspective",
|
||||
"Project, gluPickMatrix, gluPickMatrix",
|
||||
})
|
||||
void namedClassMethodRedirects(String className, String method, String expected) {
|
||||
assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0} -> null")
|
||||
@CsvSource({
|
||||
"GL11.GL_TEXTURE_2D",
|
||||
"GL11.GL_BLEND",
|
||||
"GL20.GL_FRAGMENT_SHADER",
|
||||
"Foo.bar",
|
||||
"String.valueOf",
|
||||
"ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB",
|
||||
})
|
||||
void nonRedirectedReturnsNull(String classAndMethod) {
|
||||
assertNull(GLRedirects.getTargetMethodName(classAndMethod));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullDotReturnsNull() {
|
||||
assertNull(GLRedirects.getTargetMethodName("noDotHere"));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "pattern matches {0}")
|
||||
@CsvSource({
|
||||
"GL11.glPushMatrix",
|
||||
"GL20C.glUseProgram",
|
||||
"GL44.glBufferStorage",
|
||||
"ARBShaderObjects.glUseProgramObjectARB",
|
||||
"OpenGlHelper.setActiveTexture",
|
||||
"GLU.gluPerspective",
|
||||
"UniversalVAO.bindVertexArray",
|
||||
})
|
||||
void patternMatchesRedirectedClasses(String input) {
|
||||
assertTrue(GLRedirects.getMethodRedirectPattern().matcher(input).find(),
|
||||
"Pattern should match: " + input);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "pattern does not match {0}")
|
||||
@CsvSource({
|
||||
"String.valueOf",
|
||||
"Math.abs",
|
||||
"Tessellator.instance",
|
||||
"System.currentTimeMillis",
|
||||
})
|
||||
void patternDoesNotMatchUnrelatedClasses(String input) {
|
||||
assertFalse(GLRedirects.getMethodRedirectPattern().matcher(input).find(),
|
||||
"Pattern should not match: " + input);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endToEndScriptTransformation() {
|
||||
String script = "GL11.glPushMatrix();\nGL11.glTranslatef(1,2,3);\nGL11.glEnable(GL11.GL_BLEND);";
|
||||
|
||||
String result = GLRedirects.getMethodRedirectPattern().matcher(script).replaceAll(mr -> {
|
||||
String target = GLRedirects.getTargetMethodName(mr.group(0));
|
||||
return target != null ? "GLStateManager." + target : mr.group(0);
|
||||
});
|
||||
|
||||
assertTrue(result.contains("GLStateManager.glPushMatrix()"), "glPushMatrix not redirected: " + result);
|
||||
assertTrue(result.contains("GLStateManager.glTranslatef(1,2,3)"), "glTranslatef not redirected: " + result);
|
||||
assertTrue(result.contains("GLStateManager.glEnable(GL11.GL_BLEND)"), "glEnable not redirected or GL_BLEND mangled: " + result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void patternIsNotNull() {
|
||||
assertNotNull(GLRedirects.getMethodRedirectPattern());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user