Compare commits

...
23 Commits
Author SHA1 Message Date
Jason Mitchell 87aa853f81 Attempt to fix deferred particle batching (#1871) 2026-06-16 22:16:26 -07:00
Jason Mitchell ab69eea654 Announce releases (#1867) 2026-06-15 14:50:47 -07:00
Jason Mitchell edccf63b66 Bump glsl-transformation-lib and buildscript (#1865) 2026-06-14 18:09:05 -07:00
MalTeeez 80413fb3de Add policy for AI contributions & a PR template (#1864) 2026-06-15 01:40:03 +02:00
Jason Mitchell f3ed9aa82b Force invalidate dh depth tex when reconnecting DH Textures (#1862) 2026-06-14 14:59:01 -07:00
Jason Mitchell 307c563b6d Rework DH matrix handling (#1861) 2026-06-14 14:20:19 -07:00
Eclipse-Sol f92ce1d24e Fix enchant glint fix so that it looks proper (#1856) 2026-06-13 17:28:12 -07:00
sisyphussy a7fac16d97 VAO stuff (#1858) 2026-06-13 17:26:09 -07:00
Jason Mitchell 20c3caaed4 A few redirects for calls that DH was using that we weren't redirecting (#1859) 2026-06-13 16:50:44 -07:00
Jason Mitchell bf08cd50d9 Support glPixelStore in display lists (#1854) 2026-06-11 20:29:14 -07:00
Eclipse-Sol 805a266059 Support extended metas for NEID and EID (#1853) 2026-06-11 17:58:22 -07:00
Eclipse-Sol d3c9963280 Fix ctm crash due to null (#1852) 2026-06-11 18:02:08 -04:00
Eclipse-Sol 4630bc4fd8 Fix shaders not loading the correct dim (#1844) 2026-06-09 23:12:13 -07:00
Jason Mitchell ce32a3d3df Emulate glTexParameter(pname=GL_GENERATE_MIPMAP) on core (#1845) 2026-06-09 23:03:42 -07:00
Eclipse-Sol 1991dc58ec Fix Steadfast not loading right (#1838) 2026-06-08 20:06:29 -07:00
Eclipse-Sol ae19236e1a Remove force hand animation (#1839) 2026-06-08 20:05:49 -07:00
Kotmatross 7ad368c937 Be less lazy (#1834) 2026-06-08 17:03:36 -07:00
Eclipse-Sol 5c86c87bcf Allow devs to target the dragon death rays as its own entity id (#1836) 2026-06-08 17:03:05 -07:00
tiffit 99ba7fa775 Remove CTM adding .png to tile paths (#1832) 2026-06-07 16:05:10 -07:00
Eclipse-Sol 5ae4ab8c5c More specific shader fixes pertaining to EP (#1831) 2026-06-07 11:19:30 -07:00
madoha ddd68e023f Add Japanese Localizations (#1830) 2026-06-07 11:14:20 -07:00
Eclipse-Sol f78cfdd5fb Update and fix Iris' shader menus (#1828) 2026-06-06 17:12:38 -07:00
Jason Mitchell 183bcfbf41 Narrow more transformer exclusions (#1827) 2026-06-06 15:38:05 -07:00
94 changed files with 2624 additions and 882 deletions

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
# Description or your *PR* and other info
<!-- Please include a summary of the change -->
<!-- Any details that you think are important to review this PR? -->
<!-- Are there any Issues / PRs related to this one? -->
<!-- Add a screenshot or a video demonstration for new features -->
<details open><summary>Template</summary> <!-- OPEN is intentional -->
### Before submitting a *pull request* make sure you have:
- [ ] Read the [AI Contribution Policy](https://github.com/GTNewHorizons/Angelica/blob/master/AI_POLICY.md)
- [ ] At least [skimmed](https://github.com/GTNewHorizons/Angelica/search?q=is%3Apr&type=Issues) the tracker for similar pull requests
</details>
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
: "${REPO:?REPO required}"
: "${TAG:?TAG required}"
if [ -z "${DISCORD_WEBHOOK_URL:-}" ]; then
echo "DISCORD_WEBHOOK_URL not set, skipping"
exit 0
fi
if ! release_json=$(gh release view "$TAG" --repo "$REPO" --json body,url,publishedAt 2>&1); then
echo "Could not read release $TAG in $REPO; skipping:"
printf '%s\n' "$release_json"
exit 0
fi
body=$(jq -r '.body // ""' <<<"$release_json")
url=$(jq -r '.url' <<<"$release_json")
ts=$(jq -r '.publishedAt // ""' <<<"$release_json")
[ -n "$ts" ] || ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
payload=$(jq -n \
--arg title "$TAG" \
--arg url "$url" \
--arg desc "$body" \
--arg ts "$ts" \
'{
embeds: [{
title: ("Angelica " + $title + " released"),
url: $url,
description: (if ($desc | length) > 3900
then $desc[0:3900] + "\n\n...[full changelog](" + $url + ")"
else $desc end),
color: 3066993,
footer: { text: "GitHub Releases" },
timestamp: $ts
}]
}')
curl -fsS -X POST -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK_URL"
+19
View File
@@ -12,3 +12,22 @@ jobs:
release-tags:
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/release-tags.yml@master
secrets: inherit
notify-discord:
needs: [release-tags]
runs-on: ubuntu-24.04
if: >-
${{ !endsWith(github.ref_name, '-snapshot')
&& !contains(github.event.head_commit.message, '[snapshot]')
&& !endsWith(github.ref_name, '-pre') }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Announce release to Discord
shell: bash
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
run: bash .github/scripts/announce-release.sh
+55
View File
@@ -0,0 +1,55 @@
# AI Usage Policy
This project has strict rules for AI usage:
* **The human-in-the-loop must fully understand all code.** If you
can't explain what your changes do and how they interact with the
greater system without the aid of AI tools, do not contribute
those changes.
* **Issues and discussions can use AI assistance but must have a full
human-in-the-loop.** This means that any content generated with AI
must have been reviewed *and edited* by a human before submission.
The author must do their research and trim this down.
* **No AI-generated media is allowed (textures, videos, audio, etc.).**
Text and code are the only acceptable AI-generated content, per the
other rules in this policy.
* **Commit messages, PR descriptions must read in your own voice.**
No AI-authored or AI co-authored commits, including Co-authored-by AI tags.
* **Only contribute fixes for bugs you have personally hit in-game.**
If you haven’t experienced or reproduced a bug firsthand, and don’t
already know the relevant code well, don’t throw AI at it and forward it to us.
Patches written for problems the author never actually experienced themselves
tend to fix the wrong thing and/or produce another.
* **The voice and intent of written content must be yours.**
For documentation and especially quests, the tone and the point you're trying to make
has to come from you, not an AI. Use AI to help with phrasing or cleanup if you like,
but if the substance and voice aren't yours, don't contribute it.
* **Bad AI use may be blocked.** People who produce bad contributions
that are clearly AI (slop) may be blocked from making future contributions.
We love to help junior developers learn and grow, but if you're interested
in that, don't use AI and we'll help you. We're sorry that bad AI
users have ruined this for you.
## There are Humans Here
Please remember that this project is maintained by humans.
Every discussion, issue, and pull request is read and reviewed by
humans (and sometimes machines, too). It is a boundary point at which
people interact with each other and work is done. It is rude and
disrespectful to approach this boundary with low-effort, unqualified
work, since it puts the burden of validation on the maintainer.
In a perfect world, AI would produce high-quality, accurate work
every time. But today, that reality depends on the user of the AI.
And today, many users of AI do not use it well. So, until that
changes, we need to have strict rules to protect maintainers.
Based on https://github.com/ghostty-org/ghostty/blob/main/AI_POLICY.md
+1 -1
View File
@@ -83,7 +83,7 @@ dependencies {
shadowImplementation("net.minecraftforge:eventbus:7.0-beta.15")
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-32.g7dd88a4-GTNH") {
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-33.g3ec381e") {
exclude module: "antlr4" // we only want to shadow the runtime module
}
shadowImplementation("org.antlr:antlr4-runtime:4.13.2")
@@ -59,7 +59,10 @@ public class CompatUniformManager {
static final int MAT_FIELDS = 5;
static final int LOC_MAT_BASE = LOC_LIGHT_BASE + 2 * LIGHT_FIELDS; // 43
static final int LOC_COUNT = LOC_MAT_BASE + MAT_FIELDS; // 48
static final int LOC_IRIS_MODELVIEW_INVERSE = LOC_MAT_BASE + MAT_FIELDS; // 48
static final int LOC_IRIS_PROJECTION_INVERSE = LOC_IRIS_MODELVIEW_INVERSE + 1; // 49
static final int LOC_COUNT = LOC_IRIS_PROJECTION_INVERSE + 1; // 50
private static final String[] LIGHT_FIELD_NAMES = {
"ambient", "diffuse", "specular", "position", "halfVector",
@@ -89,6 +92,8 @@ public class CompatUniformManager {
UNIFORM_NAMES[LOC_IRIS_NORMAL] = "iris_NormalMatrix";
UNIFORM_NAMES[LOC_IRIS_LIGHTMAP_TEXTURE_MATRIX] = "iris_LightmapTextureMatrix";
UNIFORM_NAMES[LOC_IRIS_TEXTURE_MATRIX] = "iris_TextureMatrix";
UNIFORM_NAMES[LOC_IRIS_MODELVIEW_INVERSE] = "iris_ModelViewMatrixInverse";
UNIFORM_NAMES[LOC_IRIS_PROJECTION_INVERSE] = "iris_ProjectionMatrixInverse";
UNIFORM_NAMES[LOC_ALPHA_TEST_REF] = "angelica_currentAlphaTest";
UNIFORM_NAMES[LOC_SCENE_COLOR] = "angelica_SceneColor";
UNIFORM_NAMES[LOC_CLIP_PLANES] = "angelica_ClipPlane[0]";
@@ -229,10 +234,15 @@ public class CompatUniformManager {
}
// ModelView Inverse
if (locs[LOC_MODELVIEW_INVERSE] != -1) {
if (locs[LOC_MODELVIEW_INVERSE] != -1 || locs[LOC_IRIS_MODELVIEW_INVERSE] != -1) {
mv.invert(scratchMatrix);
scratchMatrix.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW_INVERSE], false, mat4Buf);
if (locs[LOC_MODELVIEW_INVERSE] != -1) {
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW_INVERSE], false, mat4Buf);
}
if (locs[LOC_IRIS_MODELVIEW_INVERSE] != -1) {
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_MODELVIEW_INVERSE], false, mat4Buf);
}
}
}
@@ -266,10 +276,15 @@ public class CompatUniformManager {
}
// Projection Inverse
if (locs[LOC_PROJECTION_INVERSE] != -1) {
if (locs[LOC_PROJECTION_INVERSE] != -1 || locs[LOC_IRIS_PROJECTION_INVERSE] != -1) {
proj.invert(scratchMatrix);
scratchMatrix.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(locs[LOC_PROJECTION_INVERSE], false, mat4Buf);
if (locs[LOC_PROJECTION_INVERSE] != -1) {
RENDER_BACKEND.uniformMatrix4(locs[LOC_PROJECTION_INVERSE], false, mat4Buf);
}
if (locs[LOC_IRIS_PROJECTION_INVERSE] != -1) {
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_PROJECTION_INVERSE], false, mat4Buf);
}
}
}
@@ -111,6 +111,7 @@ public class DisplayListManager {
int listMode,
CommandRecorder recorder,
List<AccumulatedDraw> draws,
DisplayListCallback callback,
StackTraceElement[] stackTrace,
// Debug logging fields (only used when LOG_DISPLAY_LIST_COMPILATION)
@@ -131,6 +132,19 @@ public class DisplayListManager {
return currentRecorder != null && Thread.currentThread() == recordingThread;
}
public static UnsupportedOperationException unsupportedInList(String what) {
return new UnsupportedOperationException(what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
}
public static String describeCurrentCompilation() {
if (currentRecorder == null) {
return "(not compiling)";
}
final String mode = glListMode == GL11.GL_COMPILE_AND_EXECUTE ? "GL_COMPILE_AND_EXECUTE" : "GL_COMPILE";
final int depth = compilationStack.size() + 1;
return "while compiling display list " + glListId + " (" + mode + (depth > 1 ? ", nesting depth " + depth : "") + ")";
}
public static boolean isCompileAndExecute() {
return glListMode == GL11.GL_COMPILE_AND_EXECUTE;
}
@@ -159,7 +173,9 @@ public class DisplayListManager {
* Only use this if you are 100% that the transforms will not be of any use after the display list draw call.
*/
public static void discardMatrixTransforms() {
resetRelativeTransform();
if (isRecording()) {
resetRelativeTransform();
}
}
/**
@@ -748,7 +764,7 @@ public class DisplayListManager {
// Nested display list compilation violates OpenGL spec, but some of our optimizations require it
// Save current compilation context and start fresh for nested list
final CompilationContext parentContext = new CompilationContext(
glListId, glListMode, currentRecorder, accumulatedDraws,
glListId, glListMode, currentRecorder, accumulatedDraws, transformCallback,
compilationStackTrace, pendingTransformOps, multMatrixSources, drawRangeSources
);
compilationStack.push(parentContext);
@@ -784,9 +800,21 @@ public class DisplayListManager {
if (glListMode == 0) {
throw new RuntimeException("glEndList called outside of a display list!");
}
try {
finishCurrentList();
} finally {
// Non-null here means finishCurrentList threw before transferring or releasing
// ownership of the recorder.
if (currentRecorder != null) {
currentRecorder.delete();
}
// Must always run, even when compilation fails - a leaked context leaves
// isRecording() true forever and every guarded GL call throws afterward.
popCompilationContext();
}
}
final boolean isNested = !compilationStack.isEmpty();
private static void finishCurrentList() {
// Stop compiling mode (works for both root and nested lists now)
TessellatorManager.stopCapturingDirect();
@@ -803,8 +831,9 @@ public class DisplayListManager {
final boolean hasCommands = !commandBuffer.isEmpty();
if (hasCommands) {
final DisplayListVBO compiledBuffers = new DisplayListVBOBuilder().addDraws(accumulatedDraws).build();
DisplayListVBO compiledBuffers = null;
try {
compiledBuffers = new DisplayListVBOBuilder().addDraws(accumulatedDraws).build();
final IndexedDrawBatchBuilder indexedBuilder = commandBuffer.getIndexedDraws();
final List<IndexedDrawBatch> indexedBatches;
if (indexedBuilder.isEmpty()) {
@@ -824,16 +853,23 @@ public class DisplayListManager {
}
}
compiled = new CompiledDisplayList(commandBuffer.finish(), commandBuffer.getComplexObjects(), compiledBuffers, indexedBatches);
final DisplayListCommand[] complexObjects = commandBuffer.getComplexObjects();
compiled = new CompiledDisplayList(commandBuffer.finish(), complexObjects, compiledBuffers, indexedBatches);
currentRecorder = null;
} catch (Exception e) {
GLStateManager.LOGGER.error("Encountered a fatal issue while trying to compile a display list.");
e.printStackTrace();
commandBuffer.delete();
currentRecorder = null;
if (compiledBuffers != null) {
compiledBuffers.delete();
}
return;
}
} else {
// Free the recorder even if empty
commandBuffer.delete();
currentRecorder = null;
// Empty display list - per OpenGL spec, still valid after glNewList/glEndList
compiled = CompiledDisplayList.EMPTY;
}
@@ -846,6 +882,10 @@ public class DisplayListManager {
if (LOG_DISPLAY_LIST_COMPILATION) {
logCompiledDisplayList(glListId, compiled, compilationStackTrace);
}
}
private static void popCompilationContext() {
final boolean isNested = !compilationStack.isEmpty();
// Handle nested compilation - restore parent context
if (isNested) {
@@ -856,6 +896,7 @@ public class DisplayListManager {
glListMode = parentContext.listMode;
currentRecorder = parentContext.recorder;
accumulatedDraws = parentContext.draws;
transformCallback = parentContext.callback;
compilationStackTrace = parentContext.stackTrace;
pendingTransformOps = parentContext.pendingOps;
multMatrixSources = parentContext.matrixSources;
@@ -869,6 +910,7 @@ public class DisplayListManager {
recordingThread = null;
accumulatedDraws = null;
pendingDraw = null;
transformCallback = null;
compilationStackTrace = null;
pendingTransformOps = null;
multMatrixSources = null;
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
import org.joml.Matrix4f;
import org.joml.Vector4f;
@@ -9,13 +9,13 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* Software emulation of GL feedback mode
*/
@@ -127,12 +127,12 @@ public class FeedbackManager {
}
private static int positionStride() {
final int s = VertexAttribState.get(0).effectiveStride();
final int s = VAOManager.getAttribEffectiveStride(0);
return (s != 0) ? s : 12;
}
private static long positionOffset() {
return VertexAttribState.get(0).offset;
return VAOManager.getAttribOffset(0);
}
public static void processDrawArrays(int mode, int first, int count) {
@@ -151,7 +151,7 @@ public class FeedbackManager {
computeMVP();
final int stride = positionStride();
final int elementSize = VertexAttribState.Attrib.glTypeSizeBytes(type);
final int elementSize = VAOManager.Attrib.glTypeSizeBytes(type);
final int indexByteCount = indexCount * elementSize;
ensureIndexReadbackCapacity(indexByteCount);
indexReadbackBuf.clear().limit(indexByteCount);
@@ -255,7 +255,7 @@ public class FeedbackManager {
}
private static void readPositionBuffer(long offset, ByteBuffer destination) {
final int positionVbo = VertexAttribState.get(0).vboId;
final int positionVbo = VAOManager.getAttribVBO(0);
final int previousVbo = GLStateManager.getBoundVBO();
if (positionVbo != 0 && positionVbo != previousVbo) {
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizon.gtnhlib.bytebuf.Pointer;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
@@ -8,6 +9,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredAlphaHandler;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
@@ -39,13 +41,11 @@ import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import com.gtnewhorizons.angelica.glsm.states.TextureBinding;
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntStack;
@@ -56,13 +56,13 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Matrix4fStack;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.Display;
@@ -97,10 +97,10 @@ import java.util.Set;
import java.util.function.IntSupplier;
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import static com.gtnewhorizons.angelica.glsm.Vendor.INTEL;
import static com.gtnewhorizons.angelica.glsm.Vendor.MESA;
import static com.gtnewhorizons.angelica.glsm.Vendor.NVIDIA;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* OpenGL State Manager - Provides cached state tracking and management for Backend Renderer Actions
@@ -189,25 +189,25 @@ public class GLStateManager {
// vertexFlags bits mark attribs the VBO already supplies; FFP supplies the rest via u_Current* uniforms.
// Skip the backend vertexAttrib call in either case.
public static void flushDeferredVertexAttribs(int vertexFlags) {
if (dirtyColorAttrib && (vertexFlags & VertexFlags.COLOR_BIT) == 0) {
public static void flushDeferredVertexAttribs(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
if (!hasColor && dirtyColorAttrib) {
RENDER_BACKEND.vertexAttrib4f(Usage.COLOR.getAttributeLocation(), color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
dirtyColorAttrib = false;
}
if (dirtyLightmapAttrib && (vertexFlags & VertexFlags.BRIGHTNESS_BIT) == 0) {
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f);
dirtyLightmapAttrib = false;
}
if (dirtyNormalAttrib && (vertexFlags & VertexFlags.NORMAL_BIT) == 0) {
if (!hasNormal && dirtyNormalAttrib) {
final var n = ShaderManager.getCurrentNormal();
RENDER_BACKEND.vertexAttrib3f(Usage.NORMAL.getAttributeLocation(), n.x, n.y, n.z);
dirtyNormalAttrib = false;
}
if (dirtyTexCoordAttrib && (vertexFlags & VertexFlags.TEXTURE_BIT) == 0) {
if (!hasTexCoord && dirtyTexCoordAttrib) {
final var tc = ShaderManager.getCurrentTexCoord();
RENDER_BACKEND.vertexAttrib4f(Usage.PRIMARY_UV.getAttributeLocation(), tc.x, tc.y, tc.z, tc.w);
dirtyTexCoordAttrib = false;
}
if (!hasLightmap && dirtyLightmapAttrib) {
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f);
dirtyLightmapAttrib = false;
}
}
// Highest texture unit index that has ever had a non-zero binding; limits onDeleteTexture scan range
@@ -456,18 +456,16 @@ public class GLStateManager {
@Getter protected static int listBase = 0;
@Getter protected static int boundVBO;
@Getter protected static int boundEBO;
@Getter protected static int boundVAO;
private static int clientArraysVBO = 0;
private static int clientArraysVBOCapacity = 0;
private static final int[] clientArraysVBOOffsets = new int[VertexAttribState.MAX_ATTRIBS];
public static int getBoundEBO() {
return VAOManager.boundEBO;
}
private static int boundPixelUnpackBuffer;
private static int boundPixelPackBuffer;
private static final Int2IntOpenHashMap vaoEboMap = new Int2IntOpenHashMap();
private static PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
static {
vaoEboMap.defaultReturnValue(0);
}
@Getter private static int defaultVAO; // Non-zero on core profile
public static void reset() {
@@ -489,8 +487,6 @@ public class GLStateManager {
modelViewMatrix.clear();
projectionMatrix.clear();
vaoEboMap.clear();
VertexAttribState.reset();
}
/**
@@ -626,18 +622,22 @@ public class GLStateManager {
LOGGER.info("GLStateManager cache bypassed");
}
if (initConfig != null && initConfig.isLwjglDebug()) {
LOGGER.info("Enabling additional LWJGL debug output");
if (RENDER_BACKEND.supportsDebugOutput()) {
LOGGER.info("Enabling additional LWJGL debug output");
GLDebug.setupDebugMessageCallback();
GLDebug.initDebugState();
GLDebug.setupDebugMessageCallback();
GLDebug.initDebugState();
GLDebug.debugMessage("Angelica Debug Annotator Initialized");
GLDebug.debugMessage("Angelica Debug Annotator Initialized");
} else {
LOGGER.info("LWJGL debug output requested but unavailable in this GL context (requires GL 4.3 or GL_KHR_debug); skipping");
}
}
defaultVAO = RENDER_BACKEND.genVertexArrays();
RENDER_BACKEND.bindVertexArray(defaultVAO);
boundVAO = defaultVAO;
VertexAttribState.init(defaultVAO);
VAOManager.init(defaultVAO);
if (initCallback != null) {
initCallback.run();
}
@@ -1015,7 +1015,8 @@ public class GLStateManager {
case GL11.GL_LIST_MODE -> DisplayListManager.getListMode();
case GL11.GL_MATRIX_MODE -> matrixMode.getMode();
case GL11.GL_SHADE_MODEL -> shadeModelState.getValue();
case GL11.GL_TEXTURE_BINDING_2D -> getBoundTextureForServerState();
// GL_TEXTURE_2D makes no sense here, but some mod still queries it...
case GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_BINDING_2D -> getBoundTextureForServerState();
case GL11.GL_COLOR_MATERIAL_FACE -> colorMaterialFace.getValue();
case GL11.GL_COLOR_MATERIAL_PARAMETER -> colorMaterialParameter.getValue();
case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(modelViewMatrix);
@@ -1043,7 +1044,7 @@ public class GLStateManager {
case GL11.GL_STENCIL_CLEAR_VALUE -> stencilState.getClearValue();
case GL15.GL_ARRAY_BUFFER_BINDING -> boundVBO;
case GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING -> boundEBO;
case GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING -> VAOManager.boundEBO;
case GL21.GL_PIXEL_UNPACK_BUFFER_BINDING -> boundPixelUnpackBuffer;
case GL21.GL_PIXEL_PACK_BUFFER_BINDING -> boundPixelPackBuffer;
@@ -1192,6 +1193,22 @@ public class GLStateManager {
};
}
private static final FloatBuffer glGetFloatScratch = BufferUtils.createFloatBuffer(16);
public static void glGetFloat(int pname, float[] params) {
final FloatBuffer buf;
if (params.length <= glGetFloatScratch.capacity()) {
buf = glGetFloatScratch;
buf.clear();
buf.limit(params.length);
} else {
buf = BufferUtils.createFloatBuffer(params.length);
}
glGetFloat(pname, buf);
buf.position(0);
buf.get(params);
}
// GLStateManager Functions
public static void glBlendColor(float red, float green, float blue, float alpha) {
@@ -1316,7 +1333,7 @@ public class GLStateManager {
public static void glBlendEquation(int mode) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glBlendEquation in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glBlendEquation");
}
final boolean caching = isCachingEnabled();
final boolean bypass = BYPASS_CACHE || !caching;
@@ -1331,7 +1348,7 @@ public class GLStateManager {
public static void glBlendEquationSeparate(int modeRGB, int modeAlpha) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glBlendEquationSeparate in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glBlendEquationSeparate");
}
final boolean caching = isCachingEnabled();
final boolean bypass = BYPASS_CACHE || !caching;
@@ -1851,7 +1868,7 @@ public class GLStateManager {
internalformat = changeFormatIfDeprecated(internalformat);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromIntBuffer(target, level, internalformat, width, height, border, format, type, pixels));
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromIntBuffer(target, level, internalformat, width, height, border, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -1860,13 +1877,14 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, FloatBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromFloatBuffer(target, level, internalformat, width, height, border, format, type, pixels));
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromFloatBuffer(target, level, internalformat, width, height, border, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -1875,13 +1893,14 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, DoubleBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromDoubleBuffer(target, level, internalformat, width, height, border, format, type, pixels));
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromDoubleBuffer(target, level, internalformat, width, height, border, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -1890,13 +1909,14 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromByteBuffer(target, level, internalformat, width, height, border, format, type, pixels));
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromByteBuffer(target, level, internalformat, width, height, border, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -1905,15 +1925,17 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, long pixels_buffer_offset) {
internalformat = changeFormatIfDeprecated(internalformat);
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexImage2D with buffer offset in display lists not yet supported");
throw DisplayListManager.unsupportedInList("glTexImage2D with buffer offset");
}
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels_buffer_offset);
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels_buffer_offset);
maybeGenerateMipmap(target, level);
}
public static void glTexCoord1f(float s) {
@@ -2195,72 +2217,79 @@ public class GLStateManager {
}
}
public static void preDraw(int drawMode) {
prepareWideLineEmulation(drawMode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
}
public static void preDraw() {
disableWideLineEmulation();
ShaderManager.getInstance().preDraw();
prepareClientArrays();
}
public static void glDrawElements(int mode, ByteBuffer indices) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glDrawElements in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glDrawElements");
}
if (FeedbackManager.isFeedbackMode()) {
FeedbackManager.processDrawElements(mode, indices);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices);
}
public static void glDrawElements(int mode, IntBuffer indices) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glDrawElements in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glDrawElements");
}
if (FeedbackManager.isFeedbackMode()) {
FeedbackManager.processDrawElements(mode, indices);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices);
} else {
RENDER_BACKEND.drawElements(mode, indices);
return;
}
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices);
}
public static void glDrawElements(int mode, ShortBuffer indices) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glDrawElements in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glDrawElements");
}
if (FeedbackManager.isFeedbackMode()) {
FeedbackManager.processDrawElements(mode, indices);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices);
} else {
RENDER_BACKEND.drawElements(mode, indices);
return;
}
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices);
}
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glDrawElements in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glDrawElements");
}
if (FeedbackManager.isFeedbackMode()) {
FeedbackManager.processDrawElements(mode, count, type, indices);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
} else {
RENDER_BACKEND.drawElements(mode, count, type, indices);
return;
}
preDraw(mode);
RENDER_BACKEND.drawElements(mode, count, type, indices);
}
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
@@ -2269,7 +2298,7 @@ public class GLStateManager {
if (recordMode != RecordMode.NONE) {
// Core profile: a default VAO is generated at init and glBindVertexArray(0)
// is redirected to it, so a VAO is always bound here — no fallback branches.
final IndexedDrawCapture capture = IndexedDrawCapture.create(mode, indices_count, type, indices_buffer_offset, boundEBO);
final IndexedDrawCapture capture = IndexedDrawCapture.create(mode, indices_count, type, indices_buffer_offset, VAOManager.boundEBO);
if (capture != null) {
DisplayListManager.recordIndexedDrawCapture(capture);
}
@@ -2281,14 +2310,13 @@ public class GLStateManager {
FeedbackManager.processDrawElements(mode, indices_count, type, indices_buffer_offset);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
} else {
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
return;
}
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
}
@@ -2317,62 +2345,21 @@ public class GLStateManager {
}
public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
ShaderManager.getInstance().preDraw();
prepareClientArrays();
preDraw(mode);
RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex);
}
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
ShaderManager.getInstance().preDraw();
prepareClientArrays();
preDraw(mode);
RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount);
}
private static void prepareClientArrays() {
if (ShaderManager.getInstance().isEnabled() && VertexAttribState.hasAnyClientSideEnabledAttrib()) {
uploadClientArraysToVBO();
if (VAOManager.hasAnyClientSideEnabledAttrib()) {
VAOManager.uploadClientArraysToVBO();
}
}
/**
* If any enabled vertex attribute uses a client-side pointer (no VBO), upload all such
* attribs into a shared stream VBO so the draw succeeds under core profile.
*/
private static void uploadClientArraysToVBO() {
int totalBytes = 0;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
clientArraysVBOOffsets[i] = -1;
final VertexAttribState.Attrib a = VertexAttribState.get(i);
if (!a.enabled || a.clientPointer == null) continue;
clientArraysVBOOffsets[i] = totalBytes;
totalBytes += a.clientPointer.remaining();
}
if (totalBytes == 0) return;
if (clientArraysVBO == 0) {
clientArraysVBO = RENDER_BACKEND.genBuffers();
}
final int savedVBO = boundVBO;
glBindBuffer(GL15.GL_ARRAY_BUFFER, clientArraysVBO);
if (totalBytes > clientArraysVBOCapacity) {
int newCap = Math.max(4096, clientArraysVBOCapacity);
while (newCap < totalBytes) newCap *= 2;
clientArraysVBOCapacity = newCap;
}
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, clientArraysVBOCapacity, GL15.GL_STREAM_DRAW);
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
if (clientArraysVBOOffsets[i] < 0) continue;
final VertexAttribState.Attrib a = VertexAttribState.get(i);
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
}
glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
}
public static void glDrawArrays(int mode, int first, int count) {
CommandRecorder savedRecorder = null;
final RecordMode recordMode = DisplayListManager.getRecordMode();
@@ -2389,16 +2376,16 @@ public class GLStateManager {
FeedbackManager.processDrawArrays(mode, first, count);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadsAsTriangles(first, count);
} else if (mode == GL11.GL_QUAD_STRIP) {
preDraw();
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
} else if (mode == GL11.GL_POLYGON) {
preDraw();
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
} else {
preDraw(mode);
RENDER_BACKEND.drawArrays(mode, first, count);
}
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
@@ -2545,14 +2532,14 @@ public class GLStateManager {
final int location = clientStateToAttributeLocation(cap);
if (location >= 0) glEnableVertexAttribArray(location);
final int flag = clientStateToVertexFlag(cap);
if (flag != 0) ShaderManager.getInstance().enableClientVertexFlag(flag);
if (flag != 0) VAOManager.enableClientVertexFlag(flag);
}
public static void glDisableClientState(int cap) {
final int location = clientStateToAttributeLocation(cap);
if (location >= 0) glDisableVertexAttribArray(location);
final int flag = clientStateToVertexFlag(cap);
if (flag != 0) ShaderManager.getInstance().disableClientVertexFlag(flag);
if (flag != 0) VAOManager.disableClientVertexFlag(flag);
}
public static void glVertexAttrib2f(int index, float v0, float v1) {
@@ -2572,7 +2559,7 @@ public class GLStateManager {
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
VertexAttribState.set(index, size, type, normalized, stride, offset, boundVBO);
VAOManager.setAttribute(index, size, type, normalized, stride, offset, boundVBO);
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset);
}
@@ -2581,40 +2568,40 @@ public class GLStateManager {
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, FloatBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, DoubleBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, IntBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ShortBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, pointer, 0);
VAOManager.setAttribute(index, size, type, normalized, stride, pointer);
}
public static void glVertexAttribIPointer(int index, int size, int type, int stride, long offset) {
VertexAttribState.set(index, size, type, false, stride, offset, boundVBO);
VAOManager.setAttribute(index, size, type, false, stride, offset, boundVBO);
RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, offset);
}
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ByteBuffer pointer) {
VertexAttribState.set(index, size, type, false, stride, pointer, 0);
VAOManager.setAttribute(index, size, type, false, stride, pointer);
}
public static void glVertexAttribIPointer(int index, int size, int type, int stride, IntBuffer pointer) {
VertexAttribState.set(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ShortBuffer pointer) {
VertexAttribState.set(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribDivisor(int index, int divisor) { RENDER_BACKEND.vertexAttribDivisor(index, divisor); }
@@ -2625,12 +2612,12 @@ public class GLStateManager {
public static void glVertexAttribBinding(int attribindex, int bindingindex) { RENDER_BACKEND.vertexAttribBinding(attribindex, bindingindex); }
public static void glEnableVertexAttribArray(int index) {
VertexAttribState.setEnabled(index, true);
VAOManager.enableAttribute(index);
RENDER_BACKEND.enableVertexAttribArray(index);
}
public static void glDisableVertexAttribArray(int index) {
VertexAttribState.setEnabled(index, false);
VAOManager.disableAttribute(index);
RENDER_BACKEND.disableVertexAttribArray(index);
}
@@ -2665,7 +2652,7 @@ public class GLStateManager {
public static void glPushClientAttrib(int mask) {
if (clientAttribStackPointer < CLIENT_ATTRIB_STACK_DEPTH) {
clientAttribSavedTextureUnit[clientAttribStackPointer] = clientActiveTextureUnit;
clientAttribSavedVertexFlags[clientAttribStackPointer] = ShaderManager.getInstance().getCurrentVertexFlags();
clientAttribSavedVertexFlags[clientAttribStackPointer] = VAOManager.getCurrentVertexFlags();
clientAttribStackPointer++;
}
}
@@ -2674,7 +2661,7 @@ public class GLStateManager {
if (clientAttribStackPointer > 0) {
clientAttribStackPointer--;
clientActiveTextureUnit = clientAttribSavedTextureUnit[clientAttribStackPointer];
ShaderManager.getInstance().setCurrentVertexFlags(clientAttribSavedVertexFlags[clientAttribStackPointer]);
VAOManager.setCurrentVertexFlags(clientAttribSavedVertexFlags[clientAttribStackPointer]);
}
}
@@ -3451,6 +3438,21 @@ public class GLStateManager {
}
}
public static void setModelViewMatrix(Matrix4fc m) {
if (isCachingEnabled()) {
modelViewMatrix.set(m);
mvGeneration++;
mvLinearGeneration++;
}
}
public static void setProjectionMatrix(Matrix4fc m) {
if (isCachingEnabled()) {
projectionMatrix.set(m);
projGeneration++;
}
}
public static Matrix4fStack getMatrixStack() {
switch (matrixMode.getMode()) {
case GL11.GL_MODELVIEW -> {
@@ -3734,6 +3736,25 @@ public class GLStateManager {
return DisplayListManager.getListMode();
}
private static boolean handleRemovedTexParam(int target, int pname, int value) {
if (pname == GL14.GL_GENERATE_MIPMAP) {
if (target == GL11.GL_TEXTURE_2D) {
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(getBoundTextureForServerState());
if (info != null) info.setGenerateMipmap(value != 0);
}
return true;
}
return false;
}
private static void maybeGenerateMipmap(int target, int level) {
if (target != GL11.GL_TEXTURE_2D) return;
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(getBoundTextureForServerState());
if (info != null && info.isGenerateMipmap() && level == info.getBaseLevel() && level < info.getMaxLevel()) {
RENDER_BACKEND.generateMipmap(target);
}
}
public static boolean updateTexParameteriCache(int target, int texture, int pname, int param) {
if (target != GL11.GL_TEXTURE_2D) {
return true;
@@ -3759,6 +3780,10 @@ public class GLStateManager {
if (info.getWrapT() == param && !shouldBypassCache()) return false;
info.setWrapT(param);
}
case GL12.GL_TEXTURE_BASE_LEVEL -> {
if (info.getBaseLevel() == param && !shouldBypassCache()) return false;
info.setBaseLevel(param);
}
case GL12.GL_TEXTURE_MAX_LEVEL -> {
if (info.getMaxLevel() == param && !shouldBypassCache()) return false;
info.setMaxLevel(param);
@@ -3776,6 +3801,7 @@ public class GLStateManager {
}
public static void glTexParameter(int target, int pname, IntBuffer params) {
if (handleRemovedTexParam(target, pname, params.remaining() >= 1 ? params.get(params.position()) : 0)) return;
if (params.remaining() >= 1) {
final int val = params.get(params.position());
final int remapped = remapTexClamp(pname, val);
@@ -3786,6 +3812,7 @@ public class GLStateManager {
}
public static void glTexParameter(int target, int pname, FloatBuffer params) {
if (handleRemovedTexParam(target, pname, params.remaining() >= 1 ? (int) params.get(params.position()) : 0)) return;
if (params.remaining() >= 1) {
final float val = params.get(params.position());
final float remapped = remapTexClamp(pname, val);
@@ -3796,6 +3823,7 @@ public class GLStateManager {
}
public static void glTexParameteri(int target, int pname, int param) {
if (handleRemovedTexParam(target, pname, param)) return;
param = remapTexClamp(pname, param);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
@@ -3835,6 +3863,7 @@ public class GLStateManager {
}
public static void glTexParameterf(int target, int pname, float param) {
if (handleRemovedTexParam(target, pname, param > 0 ? (int) (param + 0.5f) : (int) (param - 0.5f))) return;
param = remapTexClamp(pname, param);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
@@ -3867,9 +3896,11 @@ public class GLStateManager {
case GL11.GL_TEXTURE_MAG_FILTER -> info.getMagFilter();
case GL11.GL_TEXTURE_WRAP_S -> info.getWrapS();
case GL11.GL_TEXTURE_WRAP_T -> info.getWrapT();
case GL12.GL_TEXTURE_BASE_LEVEL -> info.getBaseLevel();
case GL12.GL_TEXTURE_MAX_LEVEL -> info.getMaxLevel();
case GL12.GL_TEXTURE_MIN_LOD -> info.getMinLod();
case GL12.GL_TEXTURE_MAX_LOD -> info.getMaxLod();
case GL14.GL_GENERATE_MIPMAP -> info.isGenerateMipmap() ? GL11.GL_TRUE : GL11.GL_FALSE;
default -> {
if (isRecordingDisplayList()) {
throw new IllegalStateException(String.format(
@@ -4405,7 +4436,7 @@ public class GLStateManager {
public static void glTexImage1D(int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexImage1D in display lists not yet implemented");
throw DisplayListManager.unsupportedInList("glTexImage1D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage1D(target, level, internalformat, width, border, format, type, pixels);
@@ -4415,7 +4446,7 @@ public class GLStateManager {
public static void glTexImage3D(int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexImage3D in display lists not yet implemented");
throw DisplayListManager.unsupportedInList("glTexImage3D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
@@ -4425,7 +4456,7 @@ public class GLStateManager {
public static void glTexImage3D(int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, IntBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexImage3D in display lists not yet implemented");
throw DisplayListManager.unsupportedInList("glTexImage3D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
@@ -4434,7 +4465,7 @@ public class GLStateManager {
public static void glTexSubImage1D(int target, int level, int xoffset, int width, int format, int type, ByteBuffer pixels) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexSubImage1D in display lists not yet implemented");
throw DisplayListManager.unsupportedInList("glTexSubImage1D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage1D(target, level, xoffset, width, format, type, pixels);
@@ -4459,9 +4490,14 @@ public class GLStateManager {
}
public static void prepareWideLineEmulation(int drawMode) {
wideLineEmulationActive = wideLineEmulationEnabled
&& (drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP)
&& lineState.getWidth() > 1.0f;
wideLineEmulationActive =
(drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP)
&& lineState.getWidth() > 1.0f
&& wideLineEmulationEnabled;
}
public static void disableWideLineEmulation() {
wideLineEmulationActive = false;
}
public static void glFlush() {
@@ -4514,6 +4550,25 @@ public class GLStateManager {
glShaderSource(shader, sb.toString());
}
public static void nglShaderSource(int shader, int count, long strings, long lengths) {
if (count <= 0) return;
final CharSequence source;
if (count == 1) {
source = decodeShaderSourceString(strings, lengths, 0);
} else {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) sb.append(decodeShaderSourceString(strings, lengths, i));
source = sb.toString();
}
glShaderSource(shader, source);
}
private static String decodeShaderSourceString(long strings, long lengths, int i) {
final long strAddr = MemoryUtilities.memGetAddress(strings + (long) i * Pointer.POINTER_SIZE);
final int len = lengths == MemoryUtilities.NULL ? -1 : MemoryUtilities.memGetInt(lengths + (long) i * 4);
return len < 0 ? MemoryUtilities.memUTF8(strAddr) : MemoryUtilities.memUTF8(strAddr, len);
}
/** Check shader type to determine if fragment output transformation is needed. */
private static boolean isFragmentShader(int shader) {
return RENDER_BACKEND.getShaderi(shader, GL20.GL_SHADER_TYPE) == GL20.GL_FRAGMENT_SHADER;
@@ -4523,7 +4578,7 @@ public class GLStateManager {
public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexSubImage2DCmd.fromByteBuffer(target, level, xoffset, yoffset, width, height, format, type, pixels));
DisplayListManager.recordComplexCommand(TexSubImage2DCmd.fromByteBuffer(target, level, xoffset, yoffset, width, height, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -4531,12 +4586,13 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexSubImage2DCmd.fromIntBuffer(target, level, xoffset, yoffset, width, height, format, type, pixels));
DisplayListManager.recordComplexCommand(TexSubImage2DCmd.fromIntBuffer(target, level, xoffset, yoffset, width, height, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -4544,18 +4600,20 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, long pixels_buffer_offset) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexSubImage2D with buffer offset in display lists not yet supported");
throw DisplayListManager.unsupportedInList("glTexSubImage2D with buffer offset");
}
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels_buffer_offset);
maybeGenerateMipmap(target, level);
}
public static void glTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth, int format, int type, ByteBuffer pixels) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexSubImage3D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glTexSubImage3D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
@@ -4564,35 +4622,37 @@ public class GLStateManager {
public static void glCopyTexImage1D(int target, int level, int internalFormat, int x, int y, int width, int border) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexImage1D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexImage1D");
}
RENDER_BACKEND.copyTexImage1D(target, level, internalFormat, x, y, width, border);
}
public static void glCopyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexImage2D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexImage2D");
}
RENDER_BACKEND.copyTexImage2D(target, level, internalFormat, x, y, width, height, border);
maybeGenerateMipmap(target, level);
}
public static void glCopyTexSubImage1D(int target, int level, int xoffset, int x, int y, int width) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexSubImage1D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexSubImage1D");
}
RENDER_BACKEND.copyTexSubImage1D(target, level, xoffset, x, y, width);
}
public static void glCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, int width, int height) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexSubImage2D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexSubImage2D");
}
RENDER_BACKEND.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
maybeGenerateMipmap(target, level);
}
public static void glCopyTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int x, int y, int width, int height) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexSubImage3D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexSubImage3D");
}
RENDER_BACKEND.copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height);
}
@@ -4633,6 +4693,12 @@ public class GLStateManager {
}
public static void glHint(int target, int hint) {
// These hint targets are removed in core profile, ignore them
switch (target) {
case GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_POINT_SMOOTH_HINT, GL11.GL_FOG_HINT, GL14.GL_GENERATE_MIPMAP_HINT -> {
return;
}
}
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordHint(target, hint);
@@ -4721,7 +4787,7 @@ public class GLStateManager {
public static void glReadBuffer(int mode) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glReadBuffer in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glReadBuffer");
}
RENDER_BACKEND.readBuffer(mode);
}
@@ -4790,18 +4856,16 @@ public class GLStateManager {
}
}
// glPixelStore* isn'tcompiled into display lists, it executes immediately
public static void glPixelStorei(int pname, int param) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glPixelStorei in display lists not yet implemented - if you see this, please report!");
if (isCachingEnabled()) {
pixelUnpackState = pixelUnpackState.with(pname, param);
}
RENDER_BACKEND.pixelStorei(pname, param);
}
public static void glPixelStoref(int pname, float param) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glPixelStoref in display lists not yet implemented - if you see this, please report!");
}
RENDER_BACKEND.pixelStoref(pname, param);
glPixelStorei(pname, param < 0 ? -Math.round(-param) : Math.round(param));
}
// Display List Commands
@@ -4896,7 +4960,7 @@ public class GLStateManager {
// Multisample Commands
public static void glSampleCoverage(float value, boolean invert) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glSampleCoverage in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glSampleCoverage");
}
RENDER_BACKEND.sampleCoverage(value, invert);
}
@@ -5021,13 +5085,13 @@ public class GLStateManager {
private static void invalidateDeletedBuffer(int buffer) {
if (buffer == 0) return;
if (boundVBO == buffer) boundVBO = 0;
if (boundEBO == buffer) boundEBO = 0;
if (VAOManager.boundEBO == buffer) VAOManager.boundEBO = 0;
if (boundPixelUnpackBuffer == buffer) boundPixelUnpackBuffer = 0;
if (boundPixelPackBuffer == buffer) boundPixelPackBuffer = 0;
// Sweep all VAOs: an unbound VAO may later rebind with a dangling EBO id.
for (Int2IntMap.Entry e : vaoEboMap.int2IntEntrySet()) {
if (e.getIntValue() == buffer) e.setValue(0);
for (VAOManager.VAOData data : VAOManager.vaoMap.values()) {
if (data.ebo == buffer) data.ebo = 0;
}
}
@@ -5036,8 +5100,7 @@ public class GLStateManager {
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
boundVBO = buffer;
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
boundEBO = buffer;
vaoEboMap.put(boundVAO, buffer);
VAOManager.onBindEBO(buffer);
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
boundPixelUnpackBuffer = buffer;
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
@@ -5046,6 +5109,14 @@ public class GLStateManager {
RENDER_BACKEND.bindBuffer(target, buffer);
}
public static void forcePixelUnpackState(PixelUnpackState target) {
PixelUnpackState.applyDiff(pixelUnpackState, target);
}
public static void restorePixelUnpackState(PixelUnpackState applied) {
PixelUnpackState.applyDiff(applied, pixelUnpackState);
}
static void suspendPixelUnpackBuffer() {
if (boundPixelUnpackBuffer != 0) {
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
@@ -5113,24 +5184,17 @@ public class GLStateManager {
}
if (boundVAO != array) {
boundVAO = array;
boundEBO = vaoEboMap.get(array);
VertexAttribState.onBindVertexArray(array);
VAOManager.onBindVertexArrayPre(array);
RENDER_BACKEND.bindVertexArray(array);
if (ShaderManager.getInstance().isEnabled()) {
ShaderManager.getInstance().onBindVertexArray(array);
}
}
}
public static void glDeleteVertexArrays(int array) {
ShaderManager.getInstance().onDeleteVertexArray(array);
VertexAttribState.onDeleteVertexArray(array);
vaoEboMap.remove(array);
VAOManager.onDeleteVertexArray(array);
if (array == boundVAO) {
// Deleting the bound VAO implicitly unbinds it. Rebind the default VAO.
boundVAO = defaultVAO;
boundEBO = vaoEboMap.get(defaultVAO);
VertexAttribState.onBindVertexArray(defaultVAO);
VAOManager.onBindVertexArrayPre(defaultVAO);
RENDER_BACKEND.bindVertexArray(defaultVAO);
}
RENDER_BACKEND.deleteVertexArrays(array);
@@ -5140,6 +5204,10 @@ public class GLStateManager {
return RENDER_BACKEND.genVertexArrays();
}
public static boolean glIsVertexArray(int array) {
return RENDER_BACKEND.isVertexArray(array);
}
public static void glBindVertexArrayAPPLE(int array) {
glBindVertexArray(array);
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
@@ -9,7 +9,6 @@ import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
@@ -17,6 +16,7 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetShort;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutShort;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* Shared quad-to-triangle EBO for core profile GL_QUADS emulation.
@@ -83,6 +83,7 @@ public final class QuadConverter {
* @param vertexCount number of vertices (must be multiple of 4)
*/
public static void drawQuadsAsTriangles(int first, int vertexCount) {
GLStateManager.preDraw();
assert first % 4 == 0 : "QuadConverter: first (" + first + ") must be a multiple of 4";
assert vertexCount % 4 == 0 : "QuadConverter: vertexCount (" + vertexCount + ") must be a multiple of 4";
final int quadCount = vertexCount / 4;
@@ -106,6 +107,7 @@ public final class QuadConverter {
* @param bytesPerIndex bytes per index element
*/
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex) {
GLStateManager.preDraw();
final int needed = triIndexCount * bytesPerIndex;
final int prevEbo = GLStateManager.getBoundEBO();
@@ -144,7 +146,7 @@ public final class QuadConverter {
final int quadCount = indexCount / 4;
final int triIndexCount = quadCount * 6;
final int bytesPerIndex = VertexAttribState.Attrib.glTypeSizeBytes(type);
final int bytesPerIndex = VAOManager.Attrib.glTypeSizeBytes(type);
final ByteBuffer src = memAlloc(indexCount * bytesPerIndex);
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, src);
@@ -7,7 +7,6 @@ import com.gtnewhorizons.angelica.glsm.dsa.DSACore;
import com.gtnewhorizons.angelica.glsm.dsa.DSAEXT;
import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import lombok.Getter;
import org.jetbrains.annotations.Nullable;
@@ -507,11 +507,6 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL11.glPixelStorei(pname, param);
}
@Override
public void pixelStoref(int pname, float param) {
GL11.glPixelStoref(pname, param);
}
@Override
public void sampleCoverage(float value, boolean invert) {
GL13.glSampleCoverage(value, invert);
@@ -1043,6 +1038,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL30.glBindVertexArray(array);
}
@Override
public boolean isVertexArray(int array) {
return GL30.glIsVertexArray(array);
}
@Override
public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) {
GL20.glVertexAttribPointer(index, size, type, normalized, stride, pointer);
@@ -8,6 +8,8 @@ import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.Collections;
import java.util.List;
/**
* Abstract rendering backend.
@@ -25,6 +27,14 @@ public abstract class RenderBackend {
/** Higher priority backends are preferred. */
public int getPriority() { return 0; }
public boolean supportsFileDrop() { return false; }
public void startFileDrop() {}
public void stopFileDrop() {}
public List<String> pollDroppedFiles() { return Collections.emptyList(); }
public abstract int getMinGLSLVersion();
public abstract void flush();
@@ -115,7 +125,6 @@ public abstract class RenderBackend {
public abstract int getTexLevelParameteri(int target, int level, int pname);
public abstract void generateMipmap(int target);
public abstract void pixelStorei(int pname, int param);
public void pixelStoref(int pname, float param) {}
public void sampleCoverage(float value, boolean invert) {}
@@ -237,6 +246,7 @@ public abstract class RenderBackend {
public abstract int genVertexArrays();
public abstract void deleteVertexArrays(int array);
public abstract void bindVertexArray(int array);
public abstract boolean isVertexArray(int array);
public abstract void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer);
public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
vertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memAddress0(pointer));
@@ -1,15 +1,13 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizons.angelica.glsm.CompatUniformManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import com.gtnewhorizons.angelica.glsm.stacks.Vec3fStack;
import com.gtnewhorizons.angelica.glsm.stacks.Vec4fStack;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import lombok.Getter;
import org.joml.Vector3f;
import org.joml.Vector4f;
@@ -31,7 +29,6 @@ public final class ShaderManager {
private final ShaderCache cache = new ShaderCache();
private final Uniforms uniforms = new Uniforms();
private final Int2IntOpenHashMap vaoVertexFlags = new Int2IntOpenHashMap();
@Getter
private boolean active = false;
@@ -57,10 +54,8 @@ public final class ShaderManager {
public static Vector4f getCurrentTexCoord() { return currentTexCoords[0]; }
public static Vector4f getCurrentTexCoord(int unit) { return currentTexCoords[unit]; }
@Getter private boolean enabled = false;
private int currentVertexFlags = VertexFlags.TEXTURE_BIT | VertexFlags.COLOR_BIT | VertexFlags.NORMAL_BIT;
private ShaderManager() {
vaoVertexFlags.defaultReturnValue(-1);
if (Boolean.parseBoolean(System.getProperty("angelica.dumpShaders", "false"))) {
cache.setDumpDir(Paths.get("ffp_shaders"));
}
@@ -73,12 +68,6 @@ public final class ShaderManager {
public void enable() {
enabled = true;
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
currentVertexFlags = format.getVertexFlags();
vaoVertexFlags.put(GLStateManager.getBoundVAO(), currentVertexFlags);
return false;
});
GLStateManager.LOGGER.info("FFP shader emulation enabled");
}
@@ -99,7 +88,7 @@ public final class ShaderManager {
currentFKLen = 0;
}
public void preDraw(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
public void preDraw() {
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null) bh.flushDeferredBlend();
@@ -111,7 +100,12 @@ public final class ShaderManager {
}
}
GLStateManager.flushDeferredVertexAttribs(currentVertexFlags);
final int vertexFlags = VAOManager.getCurrentVertexFlags();
final boolean hasColor = (vertexFlags & VertexFlags.COLOR_BIT) != 0;
final boolean hasNormal = (vertexFlags & VertexFlags.NORMAL_BIT) != 0;
final boolean hasTexCoord = (vertexFlags & VertexFlags.TEXTURE_BIT) != 0;
final boolean hasLightmap = (vertexFlags & VertexFlags.BRIGHTNESS_BIT) != 0;
GLStateManager.flushDeferredVertexAttribs(hasColor, hasNormal, hasTexCoord, hasLightmap);
if (!active) return;
@@ -127,20 +121,6 @@ public final class ShaderManager {
uploadUniforms();
}
public void preDraw(int vertexFlags) {
currentVertexFlags = vertexFlags;
preDraw(
(vertexFlags & VertexFlags.COLOR_BIT) != 0,
(vertexFlags & VertexFlags.NORMAL_BIT) != 0,
(vertexFlags & VertexFlags.TEXTURE_BIT) != 0,
(vertexFlags & VertexFlags.BRIGHTNESS_BIT) != 0
);
}
public void preDraw() {
preDraw(currentVertexFlags);
}
private void commitVariant(long vkPacked, int fkLen) {
currentVertexKeyPacked = vkPacked;
System.arraycopy(currentFKScratch, 0, currentFKPacked, 0, fkLen);
@@ -177,22 +157,6 @@ public final class ShaderManager {
public static void bumpNormalGeneration() { normalGeneration++; }
public static void bumpTexCoordGeneration() { texCoordGeneration++; }
public void enableClientVertexFlag(int flag) {currentVertexFlags |= flag;}
public void disableClientVertexFlag(int flag) {currentVertexFlags &= ~flag;}
public int getCurrentVertexFlags() {return currentVertexFlags;}
public void setCurrentVertexFlags(int flags) {currentVertexFlags = flags;}
public void onBindVertexArray(int vaoId) {
final int flags = vaoVertexFlags.get(vaoId);
if (flags != -1) {
currentVertexFlags = flags;
}
}
public void onDeleteVertexArray(int vaoId) {
vaoVertexFlags.remove(vaoId);
}
public void destroy() {
cache.destroy();
uniforms.destroy();
@@ -0,0 +1,312 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
public final class VAOManager {
public static final int MAX_ATTRIBS = 16;
public static final Int2ObjectOpenHashMap<VAOData> vaoMap = new Int2ObjectOpenHashMap<>();
private static VAOData current = null;
private static int currentVertexFlags = 0;
private static Attrib[] currentAttribs = null;
public static int boundEBO = 0;
private static int clientSideEnabledCount = 0;
public static void init(int defaultVAO) {
final VAOData data = new VAOData();
current = data;
currentAttribs = data.attribs;
vaoMap.put(defaultVAO, data);
}
public static void onBindEBO(int ebo) {
boundEBO = ebo;
}
public static void onBindVertexArrayPre(int vaoId) {
// Save old VAO data
if (current != null) {
current.vertexFlags = currentVertexFlags;
current.ebo = boundEBO;
current.attribs = currentAttribs;
}
VAOData data = vaoMap.get(vaoId);
if (data == null) {
data = new VAOData();
vaoMap.put(vaoId, data);
}
current = data;
currentVertexFlags = data.vertexFlags;
currentAttribs = data.attribs;
boundEBO = data.ebo;
recomputeClientSideCount();
}
public static void onDeleteVertexArray(int vaoId) {
vaoMap.remove(vaoId);
}
public static void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
public static void disableClientVertexFlag(int flag) { currentVertexFlags &= ~flag; }
public static int getCurrentVertexFlags() { return currentVertexFlags; }
public static void setCurrentVertexFlags(int flags) { currentVertexFlags = flags; }
public static final class VAOData {
public Attrib[] attribs;
public int vertexFlags;
public int ebo;
public VAOData() {
attribs = new Attrib[MAX_ATTRIBS];
}
}
public static void setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
final boolean wasClient = a.clientPointer != null;
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = offset;
a.vboId = vboId;
a.clientPointer = null;
if (wasClient && a.enabled) {
clientSideEnabledCount--;
}
}
public static void setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
final boolean wasClient = a.clientPointer != null;
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = 0;
a.vboId = 0;
a.clientPointer = pointer;
if (!wasClient && a.enabled) {
clientSideEnabledCount++;
}
}
public static void setEnabled(int index, boolean enabled) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && a.enabled != enabled) {
clientSideEnabledCount += enabled ? 1 : -1;
}
a.enabled = enabled;
}
public static void enableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && !a.enabled) {
clientSideEnabledCount++;
}
a.enabled = true;
}
public static void disableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && a.enabled) {
clientSideEnabledCount--;
}
a.enabled = false;
}
// Lazily allocate each attrib. Most VAOs don't even have 4 attribs, let alone 16.
private static Attrib getAttrib(int index) {
Attrib a = currentAttribs[index];
if (a == null) {
a = new Attrib();
currentAttribs[index] = a;
}
return a;
}
public static boolean isClientSideAttrib(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null && attrib.isClientSide();
}
public static int getAttribEffectiveStride(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null ? attrib.effectiveStride() : 0;
}
public static long getAttribOffset(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null ? attrib.offset : 0;
}
public static int getAttribVBO(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null ? attrib.vboId : 0;
}
public static @Nullable Attrib get(int index) {
return currentAttribs[index];
}
private static void recomputeClientSideCount() {
int count = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (isClientSideAttrib(i)) count++;
}
clientSideEnabledCount = count;
}
/**
* Returns true if any currently enabled vertex attribute was registered without a VBO
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
* offsets into a non-existent buffer, causing a native crash at draw time.
*/
public static boolean hasAnyClientSideEnabledAttrib() {
return clientSideEnabledCount > 0;
}
/**
* If any enabled vertex attribute uses a client-side pointer (no VBO), upload all such
* attribs into a shared stream VBO so the draw succeeds under core profile.
*/
private static int clientArraysVBO = 0;
private static int clientArraysVBOCapacity = 0;
private static final int[] clientArraysVBOOffsets = new int[VAOManager.MAX_ATTRIBS];
public static void uploadClientArraysToVBO() {
int totalBytes = 0;
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
clientArraysVBOOffsets[i] = -1;
final VAOManager.Attrib a = VAOManager.get(i);
if (a == null || !a.enabled || a.clientPointer == null) continue;
clientArraysVBOOffsets[i] = totalBytes;
totalBytes += a.clientPointer.remaining();
}
if (totalBytes == 0) return;
if (clientArraysVBO == 0) {
clientArraysVBO = RENDER_BACKEND.genBuffers();
}
final int savedVBO = GLStateManager.getBoundVBO();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, clientArraysVBO);
if (totalBytes > clientArraysVBOCapacity) {
int newCap = Math.max(4096, clientArraysVBOCapacity);
while (newCap < totalBytes) newCap *= 2;
clientArraysVBOCapacity = newCap;
}
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, clientArraysVBOCapacity, GL15.GL_STREAM_DRAW);
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
if (clientArraysVBOOffsets[i] < 0) continue;
final VAOManager.Attrib a = VAOManager.get(i);
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
}
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
}
public static final class Attrib {
public boolean enabled;
public int size;
public int type;
public boolean normalized;
public int stride;
public long offset;
public int vboId;
public ByteBuffer clientPointer;
public boolean isClientSide() {
return enabled && clientPointer != null;
}
public void reset() {
enabled = false;
size = 0;
type = 0;
normalized = false;
stride = 0;
offset = 0;
vboId = 0;
clientPointer = null;
}
public int effectiveStride() {
return (stride != 0) ? stride : size * typeSizeBytes();
}
public int typeSizeBytes() {
return glTypeSizeBytes(type);
}
public static int glTypeSizeBytes(int glType) {
return switch (glType) {
case GL11.GL_FLOAT, GL11.GL_INT, GL11.GL_UNSIGNED_INT -> 4;
case GL11.GL_DOUBLE -> 8;
case GL11.GL_SHORT, GL11.GL_UNSIGNED_SHORT -> 2;
case GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE -> 1;
default -> 4;
};
}
public float readComponent(ByteBuffer buf, int base, int component) {
return readComponent(type, normalized, buf, base, component);
}
public static float readComponent(int type, boolean normalized, ByteBuffer buf, int base, int component) {
return switch (type) {
case GL11.GL_FLOAT -> buf.getFloat(base + component * 4);
case GL11.GL_DOUBLE -> (float) buf.getDouble(base + component * 8);
case GL11.GL_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? v / (float) Integer.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? (v & 0xFFFFFFFFL) / (float) 0xFFFFFFFFL : (float) (v & 0xFFFFFFFFL);
}
case GL11.GL_SHORT -> {
final short v = buf.getShort(base + component * 2);
yield normalized ? v / (float) Short.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_SHORT -> {
final int v = buf.getShort(base + component * 2) & 0xFFFF;
yield normalized ? v / (float) 0xFFFF : (float) v;
}
case GL11.GL_BYTE -> {
final byte v = buf.get(base + component);
yield normalized ? v / 127.0f : (float) v;
}
case GL11.GL_UNSIGNED_BYTE -> {
final int v = buf.get(base + component) & 0xFF;
yield normalized ? v / 255.0f : (float) v;
}
default -> 0.0f;
};
}
}
}
@@ -35,11 +35,30 @@ public final class EcosystemNarrowRules {
"com.fiskmods.lightsabers.asm.transformers"
)),
new NarrowRule("FiskHeroes", "com.fiskmods.heroes.asm", List.of(
"com.fiskmods.heroes.asm.SHLoadingPlugin",
"com.fiskmods.heroes.asm.transformers"
)),
new NarrowRule("Ears", "com.unascribed.ears", List.of(
"com.unascribed.ears.asm",
"com.unascribed.ears.common.agent"
)),
new NarrowRule("FoamFix", "pl.asie.foamfix", List.of(
"pl.asie.foamfix.bugfixmod",
"pl.asie.foamfix.coremod",
"pl.asie.foamfix.forkage",
"pl.asie.foamfix.ghostbuster",
"pl.asie.foamfix.FoamFixMod",
"pl.asie.foamfix.HackyMessageFactoryWrapper",
"pl.asie.foamfix.LaunchWrapperRuntimeFix",
"pl.asie.foamfix.ProxyClient",
"pl.asie.foamfix.ProxyCommon",
"pl.asie.foamfix.repack.com.unascribed.ears.asm",
"pl.asie.foamfix.repack.com.unascribed.ears.common.agent"
)),
new NarrowRule("Alfheim", "alfheim.common.core.asm.hook", List.of(
"alfheim.common.core.asm.hook.AlfheimFieldHookHandler",
"alfheim.common.core.asm.hook.AlfheimHPHooks",
@@ -2,7 +2,7 @@ package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL15;
@@ -15,11 +15,11 @@ public final class AttribSnapshot {
public record AttribDesc(int location, int size, int type, boolean normalized, int stride, long offset, int sourceVboId, ByteBuffer readBuffer, long readBufferBaseOffset) {
public int effectiveStride() {
return stride != 0 ? stride : size * VertexAttribState.Attrib.glTypeSizeBytes(type);
return stride != 0 ? stride : size * VAOManager.Attrib.glTypeSizeBytes(type);
}
public int typeSizeBytes() {
return VertexAttribState.Attrib.glTypeSizeBytes(type);
return VAOManager.Attrib.glTypeSizeBytes(type);
}
}
@@ -56,7 +56,7 @@ public final class AttribSnapshot {
* allocating any hashmaps. A second distinct VBO promotes to the map-backed fallback.
*/
public static AttribSnapshot snapshot(int firstVertex, int vertexCount) {
final AttribDesc[] out = new AttribDesc[VertexAttribState.MAX_ATTRIBS];
final AttribDesc[] out = new AttribDesc[VAOManager.MAX_ATTRIBS];
final List<ByteBuffer> allocated = new ArrayList<>();
final int prevVBO = GLStateManager.getBoundVBO();
@@ -67,11 +67,11 @@ public final class AttribSnapshot {
Int2ObjectOpenHashMap<long[]> vboRanges = null;
final long lastVtx = (long) firstVertex + vertexCount - 1;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
final VertexAttribState.Attrib a = VertexAttribState.get(i);
if (!a.enabled || a.vboId == 0) continue;
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
final VAOManager.Attrib a = VAOManager.get(i);
if (a == null || !a.enabled || a.vboId == 0) continue;
final int stride = a.effectiveStride();
final long typeBytes = (long) a.size * VertexAttribState.Attrib.glTypeSizeBytes(a.type);
final long typeBytes = (long) a.size * VAOManager.Attrib.glTypeSizeBytes(a.type);
final long start = a.offset + (long) firstVertex * stride;
final long end = a.offset + lastVtx * stride + typeBytes;
@@ -103,9 +103,9 @@ public final class AttribSnapshot {
Int2LongOpenHashMap vboReadOffsets = null;
try {
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
final VertexAttribState.Attrib a = VertexAttribState.get(i);
if (!a.enabled) continue;
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
final VAOManager.Attrib a = VAOManager.get(i);
if (a == null || !a.enabled) continue;
if (a.vboId != 0) {
ByteBuffer buf;
@@ -653,12 +653,19 @@ public final class CommandRecorder {
for (IndexedDrawCapture c : indexedDraws.getCaptures()) {
c.freeBuffers();
}
memFree(buffer);
for (DisplayListCommand cmd : complexObjects) {
cmd.delete();
}
complexObjects.clear();
if (buffer != null) {
memFree(buffer);
buffer = null;
}
}
/**
* Deletes every allocated object & returns a ByteBuffer with the capacity equal to its limit.
* This CommandRecorder object cannot be used after calling this anymore.
* This CommandRecorder object cannot be used after calling this anymore
*/
public ByteBuffer finish() {
for (IndexedDrawCapture c : indexedDraws.getCaptures()) {
@@ -666,7 +673,10 @@ public final class CommandRecorder {
}
final int size = size();
buffer.limit(size);
return memRealloc(buffer, size);
final ByteBuffer out = memRealloc(buffer, size);
buffer = null;
complexObjects.clear();
return out;
}
public DisplayListCommand[] getComplexObjects() {
@@ -5,7 +5,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
@@ -293,7 +293,39 @@ public final class ImmediateModeRecorder {
}
private static float readComponent(AttribReader r, int base, int component) {
return VertexAttribState.Attrib.readComponent(r.type, r.normalized, r.data, base, component);
return VAOManager.Attrib.readComponent(r.type, r.normalized, r.data, base, component);
}
public static float readComponent(int type, boolean normalized, ByteBuffer buf, int base, int component) {
return switch (type) {
case GL11.GL_FLOAT -> buf.getFloat(base + component * 4);
case GL11.GL_DOUBLE -> (float) buf.getDouble(base + component * 8);
case GL11.GL_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? v / (float) Integer.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? (v & 0xFFFFFFFFL) / (float) 0xFFFFFFFFL : (float) (v & 0xFFFFFFFFL);
}
case GL11.GL_SHORT -> {
final short v = buf.getShort(base + component * 2);
yield normalized ? v / (float) Short.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_SHORT -> {
final int v = buf.getShort(base + component * 2) & 0xFFFF;
yield normalized ? v / (float) 0xFFFF : (float) v;
}
case GL11.GL_BYTE -> {
final byte v = buf.get(base + component);
yield normalized ? v / 127.0f : (float) v;
}
case GL11.GL_UNSIGNED_BYTE -> {
final int v = buf.get(base + component) & 0xFF;
yield normalized ? v / 255.0f : (float) v;
}
default -> 0.0f;
};
}
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import java.util.Arrays;
@@ -24,7 +24,7 @@ public final class AttribLayoutKey {
int base = 0;
int maxAlign = 1;
for (int i = 0; i < n; i++) {
final int typeSize = VertexAttribState.Attrib.glTypeSizeBytes(types[i]);
final int typeSize = VAOManager.Attrib.glTypeSizeBytes(types[i]);
if (typeSize > maxAlign) maxAlign = typeSize;
base = (base + typeSize - 1) & -typeSize;
off[i] = base;
@@ -3,14 +3,14 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.QuadConverter;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.AttribSnapshot;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
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;
@@ -68,7 +68,7 @@ public final class IndexedDrawCapture {
warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
return null;
}
final int srcIndexSize = VertexAttribState.Attrib.glTypeSizeBytes(srcIndexType);
final int srcIndexSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
if (srcIndexSize <= 0) return null;
final int prevEBO = GLStateManager.getBoundEBO();
@@ -106,7 +106,7 @@ public final class IndexedDrawCapture {
snap = AttribSnapshot.snapshot(minVtx, vertexCount);
int enabledCount = 0;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
if (snap.get(i) != null) enabledCount++;
}
if (enabledCount == 0) {
@@ -118,7 +118,7 @@ public final class IndexedDrawCapture {
final int[] types = new int[enabledCount];
final boolean[] normalized = new boolean[enabledCount];
int k = 0;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
final AttribSnapshot.AttribDesc d = snap.get(i);
if (d == null) continue;
locations[k] = i;
@@ -0,0 +1,52 @@
package com.gtnewhorizons.angelica.glsm.recording.commands;
import org.jetbrains.annotations.Nullable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
final class PixelDataSnapshot {
private PixelDataSnapshot() {}
static @Nullable ByteBuffer copy(@Nullable ByteBuffer pixels) {
if (pixels == null) return null;
final int pos = pixels.position();
final ByteBuffer out = memAlloc(pixels.remaining()).order(ByteOrder.nativeOrder());
out.put(pixels);
out.flip();
pixels.position(pos);
return out;
}
static @Nullable ByteBuffer copy(@Nullable IntBuffer pixels) {
if (pixels == null) return null;
final int pos = pixels.position();
final ByteBuffer out = memAlloc(pixels.remaining() * 4).order(ByteOrder.nativeOrder());
out.asIntBuffer().put(pixels);
pixels.position(pos);
return out;
}
static @Nullable ByteBuffer copy(@Nullable FloatBuffer pixels) {
if (pixels == null) return null;
final int pos = pixels.position();
final ByteBuffer out = memAlloc(pixels.remaining() * 4).order(ByteOrder.nativeOrder());
out.asFloatBuffer().put(pixels);
pixels.position(pos);
return out;
}
static @Nullable ByteBuffer copy(@Nullable DoubleBuffer pixels) {
if (pixels == null) return null;
final int pos = pixels.position();
final ByteBuffer out = memAlloc(pixels.remaining() * 8).order(ByteOrder.nativeOrder());
out.asDoubleBuffer().put(pixels);
pixels.position(pos);
return out;
}
}
@@ -2,15 +2,17 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
/**
* Command: glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels)
* Specifies a two-dimensional texture image during display list playback.
@@ -24,92 +26,58 @@ public record TexImage2DCmd(
int border,
int format,
int type,
@Nullable byte[] pixelData
@Nullable ByteBuffer pixels,
@Nullable PixelUnpackState unpack
) implements DisplayListCommand {
/**
* Create command from IntBuffer
*/
public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 4; // 4 bytes per int
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asIntBuffer().put(pixels);
pixels.position(pos); // Restore position
public TexImage2DCmd {
if (pixels == null) {
unpack = null;
}
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, data);
}
/**
* Create command from FloatBuffer
*/
public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 4; // 4 bytes per float
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asFloatBuffer().put(pixels);
pixels.position(pos); // Restore position
}
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, data);
public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) {
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
/**
* Create command from DoubleBuffer
*/
public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 8; // 8 bytes per double
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asDoubleBuffer().put(pixels);
pixels.position(pos); // Restore position
}
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, data);
public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) {
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
/**
* Create command from ByteBuffer
*/
public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = limit - pos;
data = new byte[size];
pixels.get(data);
pixels.position(pos); // Restore position
}
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, data);
public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) {
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) {
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
@Override
public void execute() {
ByteBuffer buffer = null;
if (pixelData != null) {
buffer = ByteBuffer.allocateDirect(pixelData.length).order(ByteOrder.nativeOrder());
buffer.put(pixelData);
buffer.flip();
if (pixels == null) {
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, (ByteBuffer) null);
return;
}
GLStateManager.forcePixelUnpackState(unpack);
try {
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
} finally {
GLStateManager.restorePixelUnpackState(unpack);
}
}
@Override
public void delete() {
if (pixels != null) {
memFree(pixels);
}
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, buffer);
}
@Override
public @NotNull String toString() {
String dataStr = pixelData != null ? (pixelData.length + " bytes") : "null";
return String.format("TexImage2D(target=%s, level=%d, internalformat=%s, %dx%d, border=%d, format=%s, type=%s, data=%s)",
String dataStr = pixels != null ? (pixels.remaining() + " bytes") : "null";
return String.format("TexImage2D(target=%s, level=%d, internalformat=%s, %dx%d, border=%d, format=%s, type=%s, data=%s, unpack=%s)",
GLDebug.getTextureTargetName(target), level, GLDebug.getTextureFormatName(internalformat),
width, height, border, GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr);
width, height, border, GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr, unpack);
}
}
@@ -2,15 +2,17 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
/**
* Command: glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels)
* Updates a portion of an existing two-dimensional texture image during display list playback.
@@ -24,92 +26,58 @@ public record TexSubImage2DCmd(
int height,
int format,
int type,
@Nullable byte[] pixelData
@Nullable ByteBuffer pixels,
@Nullable PixelUnpackState unpack
) implements DisplayListCommand {
/**
* Create command from IntBuffer
*/
public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 4; // 4 bytes per int
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asIntBuffer().put(pixels);
pixels.position(pos); // Restore position
public TexSubImage2DCmd {
if (pixels == null) {
unpack = null;
}
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, data);
}
/**
* Create command from FloatBuffer
*/
public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 4; // 4 bytes per float
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asFloatBuffer().put(pixels);
pixels.position(pos); // Restore position
}
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, data);
public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) {
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
/**
* Create command from DoubleBuffer
*/
public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 8; // 8 bytes per double
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asDoubleBuffer().put(pixels);
pixels.position(pos); // Restore position
}
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, data);
public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) {
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
/**
* Create command from ByteBuffer
*/
public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = limit - pos;
data = new byte[size];
pixels.get(data);
pixels.position(pos); // Restore position
}
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, data);
public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) {
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) {
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
@Override
public void execute() {
ByteBuffer buffer = null;
if (pixelData != null) {
buffer = ByteBuffer.allocateDirect(pixelData.length).order(ByteOrder.nativeOrder());
buffer.put(pixelData);
buffer.flip();
if (pixels == null) {
GLStateManager.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, (ByteBuffer) null);
return;
}
GLStateManager.forcePixelUnpackState(unpack);
try {
GLStateManager.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
} finally {
GLStateManager.restorePixelUnpackState(unpack);
}
}
@Override
public void delete() {
if (pixels != null) {
memFree(pixels);
}
GLStateManager.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, buffer);
}
@Override
public @NotNull String toString() {
String dataStr = pixelData != null ? (pixelData.length + " bytes") : "null";
return String.format("TexSubImage2D(target=%s, level=%d, offset=(%d,%d), %dx%d, format=%s, type=%s, data=%s)",
String dataStr = pixels != null ? (pixels.remaining() + " bytes") : "null";
return String.format("TexSubImage2D(target=%s, level=%d, offset=(%d,%d), %dx%d, format=%s, type=%s, data=%s, unpack=%s)",
GLDebug.getTextureTargetName(target), level, xoffset, yoffset, width, height,
GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr);
GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr, unpack);
}
}
@@ -126,6 +126,7 @@ public class GLSMRedirector {
.add("glFrustum")
.add("glGetBoolean")
.add("glGetFloat")
.add("glGetFloatv", "glGetFloat")
.add("glGetInteger")
.add("glGetLight")
.add("glGetMaterial")
@@ -296,6 +297,7 @@ public class GLSMRedirector {
.add("glStencilOpSeparate")
.add("glUseProgram")
.add("glShaderSource")
.add("nglShaderSource")
.add("glLinkProgram")
.add("glDeleteProgram")
.add("glCreateShader")
@@ -321,6 +323,7 @@ public class GLSMRedirector {
.add("glUniformMatrix2")
.add("glUniformMatrix3")
.add("glUniformMatrix4")
.add("glUniformMatrix4fv", "glUniformMatrix4")
.add("glDeleteShader")
.add("glGetShaderi")
.add("glGetShaderInfoLog")
@@ -342,6 +345,7 @@ public class GLSMRedirector {
.add("glGenVertexArrays")
.add("glBindVertexArray")
.add("glDeleteVertexArrays")
.add("glIsVertexArray")
.add("glBindFramebuffer")
.add("glDeleteFramebuffers")
.add("glGenFramebuffers")
@@ -587,18 +591,16 @@ public class GLSMRedirector {
}
} else {
final Map<String, String> redirects = mNode.owner.startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(mNode.owner);
if (redirects != null) {
final String glsmName = redirects.get(mNode.name);
if (glsmName != null) {
if (LOG_SPAM) {
final String shortOwner = mNode.owner.substring(mNode.owner.lastIndexOf("/") + 1);
LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, glsmName, mNode.desc);
}
mNode.owner = GLStateManager;
mNode.name = glsmName;
changed = true;
redirectInMethod = true;
final String glsmName = redirects != null ? redirects.get(mNode.name) : null;
if (glsmName != null) {
if (LOG_SPAM) {
final String shortOwner = mNode.owner.substring(mNode.owner.lastIndexOf("/") + 1);
LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, glsmName, mNode.desc);
}
mNode.owner = GLStateManager;
mNode.name = glsmName;
changed = true;
redirectInMethod = true;
}
}
// Redirect <init> calls for quadric classes
@@ -617,8 +619,7 @@ public class GLSMRedirector {
for (int i = 0; i < dynNode.bsmArgs.length; i++) {
if (!(dynNode.bsmArgs[i] instanceof Handle handle)) continue;
final Map<String, String> redirects = handle.getOwner().startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(handle.getOwner());
if (redirects == null) continue;
final String glsmName = redirects.get(handle.getName());
final String glsmName = redirects != null ? redirects.get(handle.getName()) : null;
if (glsmName == null) continue;
if (LOG_SPAM) {
final String shortOwner = handle.getOwner().substring(handle.getOwner().lastIndexOf("/") + 1);
@@ -0,0 +1,51 @@
package com.gtnewhorizons.angelica.glsm.states;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* Immutable client-side pixel unpack state (glPixelStore UNPACK_* params)
*/
public record PixelUnpackState(
int alignment,
int rowLength,
int skipRows,
int skipPixels,
int imageHeight,
int skipImages,
boolean swapBytes,
boolean lsbFirst
) {
public static final PixelUnpackState DEFAULT = new PixelUnpackState(4, 0, 0, 0, 0, 0, false, false);
public boolean isDefault() {
return this.equals(DEFAULT);
}
public PixelUnpackState with(int pname, int param) {
return switch (pname) {
case GL11.GL_UNPACK_ALIGNMENT -> (param == 1 || param == 2 || param == 4 || param == 8) ? new PixelUnpackState(param, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
case GL11.GL_UNPACK_ROW_LENGTH -> param >= 0 ? new PixelUnpackState(alignment, param, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
case GL11.GL_UNPACK_SKIP_ROWS -> param >= 0 ? new PixelUnpackState(alignment, rowLength, param, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
case GL11.GL_UNPACK_SKIP_PIXELS -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, param, imageHeight, skipImages, swapBytes, lsbFirst) : this;
case GL12.GL_UNPACK_IMAGE_HEIGHT -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, param, skipImages, swapBytes, lsbFirst) : this;
case GL12.GL_UNPACK_SKIP_IMAGES -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, param, swapBytes, lsbFirst) : this;
case GL11.GL_UNPACK_SWAP_BYTES -> new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, param != 0, lsbFirst);
case GL11.GL_UNPACK_LSB_FIRST -> new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, param != 0);
default -> this;
};
}
public static void applyDiff(PixelUnpackState from, PixelUnpackState to) {
if (from.alignment != to.alignment) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_ALIGNMENT, to.alignment);
if (from.rowLength != to.rowLength) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_ROW_LENGTH, to.rowLength);
if (from.skipRows != to.skipRows) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SKIP_ROWS, to.skipRows);
if (from.skipPixels != to.skipPixels) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, to.skipPixels);
if (from.imageHeight != to.imageHeight) RENDER_BACKEND.pixelStorei(GL12.GL_UNPACK_IMAGE_HEIGHT, to.imageHeight);
if (from.skipImages != to.skipImages) RENDER_BACKEND.pixelStorei(GL12.GL_UNPACK_SKIP_IMAGES, to.skipImages);
if (from.swapBytes != to.swapBytes) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SWAP_BYTES, to.swapBytes ? 1 : 0);
if (from.lsbFirst != to.lsbFirst) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_LSB_FIRST, to.lsbFirst ? 1 : 0);
}
}
@@ -1,198 +0,0 @@
package com.gtnewhorizons.angelica.glsm.states;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
/**
* Per-VAO vertex attribute pointer state tracking (Mesa-style).
*/
public class VertexAttribState {
public static final int MAX_ATTRIBS = 16;
private static final Int2ObjectOpenHashMap<Attrib[]> vaoAttribs = new Int2ObjectOpenHashMap<>();
private static Attrib[] current;
private static final ArrayDeque<Attrib[]> pool = new ArrayDeque<>();
private static int clientSideEnabledCount;
public static void init(int defaultVAO) {
vaoAttribs.clear();
pool.clear();
current = allocAttribArray();
vaoAttribs.put(defaultVAO, current);
clientSideEnabledCount = 0;
}
public static void onBindVertexArray(int newVAO) {
current = vaoAttribs.computeIfAbsent(newVAO, k -> allocAttribArray());
recomputeClientSideCount();
}
public static void onDeleteVertexArray(int vaoId) {
final Attrib[] arr = vaoAttribs.remove(vaoId);
if (arr != null) {
pool.addLast(arr);
}
}
public static void reset() {
vaoAttribs.clear();
pool.clear();
current = null;
clientSideEnabledCount = 0;
}
private static Attrib[] allocAttribArray() {
Attrib[] arr = pool.pollFirst();
if (arr != null) {
for (Attrib a : arr) a.reset();
return arr;
}
arr = new Attrib[MAX_ATTRIBS];
for (int i = 0; i < MAX_ATTRIBS; i++) arr[i] = new Attrib();
return arr;
}
public static void set(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = current[index];
final boolean was = a.isClientSide();
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = offset;
a.vboId = vboId;
a.clientPointer = null;
if (was) clientSideEnabledCount--;
}
public static void set(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = current[index];
final boolean was = a.isClientSide();
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = 0;
a.vboId = vboId;
a.clientPointer = (vboId == 0) ? pointer : null;
final boolean now = a.isClientSide();
if (was != now) clientSideEnabledCount += now ? 1 : -1;
}
public static void setEnabled(int index, boolean enabled) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = current[index];
if (a.clientPointer != null && a.enabled != enabled) {
clientSideEnabledCount += enabled ? 1 : -1;
}
a.enabled = enabled;
}
public static Attrib get(int index) {
return current[index];
}
private static void recomputeClientSideCount() {
int count = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (current[i].isClientSide()) count++;
}
clientSideEnabledCount = count;
}
/**
* Returns true if any currently enabled vertex attribute was registered without a VBO
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
* offsets into a non-existent buffer, causing a native crash at draw time.
*/
public static boolean hasAnyClientSideEnabledAttrib() {
return clientSideEnabledCount > 0;
}
public static class Attrib {
public boolean enabled;
public int size;
public int type;
public boolean normalized;
public int stride;
public long offset;
public int vboId;
public ByteBuffer clientPointer;
public boolean isClientSide() {
return enabled && clientPointer != null;
}
public void reset() {
enabled = false;
size = 0;
type = 0;
normalized = false;
stride = 0;
offset = 0;
vboId = 0;
clientPointer = null;
}
public int effectiveStride() {
return (stride != 0) ? stride : size * typeSizeBytes();
}
public int typeSizeBytes() {
return glTypeSizeBytes(type);
}
public static int glTypeSizeBytes(int glType) {
return switch (glType) {
case GL11.GL_FLOAT, GL11.GL_INT, GL11.GL_UNSIGNED_INT -> 4;
case GL11.GL_DOUBLE -> 8;
case GL11.GL_SHORT, GL11.GL_UNSIGNED_SHORT -> 2;
case GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE -> 1;
default -> 4;
};
}
public float readComponent(ByteBuffer buf, int base, int component) {
return readComponent(type, normalized, buf, base, component);
}
public static float readComponent(int type, boolean normalized, ByteBuffer buf, int base, int component) {
return switch (type) {
case GL11.GL_FLOAT -> buf.getFloat(base + component * 4);
case GL11.GL_DOUBLE -> (float) buf.getDouble(base + component * 8);
case GL11.GL_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? v / (float) Integer.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? (v & 0xFFFFFFFFL) / (float) 0xFFFFFFFFL : (float) (v & 0xFFFFFFFFL);
}
case GL11.GL_SHORT -> {
final short v = buf.getShort(base + component * 2);
yield normalized ? v / (float) Short.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_SHORT -> {
final int v = buf.getShort(base + component * 2) & 0xFFFF;
yield normalized ? v / (float) 0xFFFF : (float) v;
}
case GL11.GL_BYTE -> {
final byte v = buf.get(base + component);
yield normalized ? v / 127.0f : (float) v;
}
case GL11.GL_UNSIGNED_BYTE -> {
final int v = buf.get(base + component) & 0xFF;
yield normalized ? v / 255.0f : (float) v;
}
default -> 0.0f;
};
}
}
}
@@ -7,7 +7,6 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.QuadConverter;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import net.minecraft.client.renderer.Tessellator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -204,14 +203,8 @@ public class TessellatorStreamingDrawer {
firstVertex = 0;
}
GLStateManager.prepareWideLineEmulation(drawMode);
ShaderManager.getInstance().preDraw(flags);
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
GLStateManager.glBindVertexArray(0);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glDisableClientState(GL11.GL_NORMAL_ARRAY);
}
private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
@@ -17,6 +17,7 @@ public class TextureInfo {
@Getter @Setter protected int magFilter = GL11.GL_LINEAR;
@Getter @Setter protected int wrapS = GL11.GL_REPEAT;
@Getter @Setter protected int wrapT = GL11.GL_REPEAT;
@Getter @Setter protected int baseLevel = 0;
@Getter @Setter protected int maxLevel = 1000;
@Getter @Setter protected int minLod = -1000;
@Getter @Setter protected int maxLod = 1000;
@@ -24,6 +25,8 @@ public class TextureInfo {
@Getter @Setter protected float maxAnisotropy = 1.0f;
@Getter @Setter protected boolean generateMipmap = false;
protected TextureInfo(int id) {
this.id = id;
}
@@ -0,0 +1,215 @@
package com.gtnewhorizons.angelica.glsm;
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 java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(GLSMCoreExtension.class)
public class GLSM_DisplayList_PixelStore_Test {
private static final int SRC = 8;
private static final int SUB = 4;
@AfterEach
void resetUnpackState() {
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
setUnpack(0, 0, 0);
}
private static void setUnpack(int rowLength, int skipRows, int skipPixels) {
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, rowLength);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, skipRows);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, skipPixels);
}
private static void setStridedUnpack() {
setUnpack(SRC, 1, 2);
}
/** RGBA source; red channel = y*size+x, alpha = 255. */
private static ByteBuffer makeSource(int size) {
final ByteBuffer buf = ByteBuffer.allocateDirect(size * size * 4).order(ByteOrder.nativeOrder());
for (int i = 0; i < size * size; i++) {
buf.put((byte) i).put((byte) 0).put((byte) 0).put((byte) 255);
}
buf.flip();
return buf;
}
private static int makeTexture(int size, ByteBuffer pixels) {
final int tex = GLStateManager.glGenTextures();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, size, size, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
return tex;
}
private static ByteBuffer readback(int size) {
final ByteBuffer out = ByteBuffer.allocateDirect(size * size * 4).order(ByteOrder.nativeOrder());
GLStateManager.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, out);
return out;
}
private static int red(ByteBuffer pixels, int size, int x, int y) {
return pixels.get((y * size + x) * 4) & 0xFF;
}
private static void assertStridedSubImage(ByteBuffer pixels) {
for (int y = 0; y < SUB; y++) {
for (int x = 0; x < SUB; x++) {
assertEquals((y + 1) * SRC + (x + 2), red(pixels, SUB, x, y), "pixel (" + x + "," + y + ")");
}
}
}
private static void assertTightSubImage(ByteBuffer pixels) {
for (int y = 0; y < SUB; y++) {
for (int x = 0; x < SUB; x++) {
assertEquals(y * SUB + x, red(pixels, SUB, x, y), "pixel (" + x + "," + y + ")");
}
}
}
private static void assertUnpack(int rowLength, int skipRows, int skipPixels) {
assertEquals(rowLength, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH));
assertEquals(skipRows, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
assertEquals(skipPixels, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_PIXELS));
}
@Test
void pixelStoreExecutesImmediatelyAndIsNotRecorded() {
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 7);
assertEquals(7, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH), "must execute immediately during recording");
GLStateManager.glEndList();
assertEquals(7, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH), "must persist after glEndList");
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
GLStateManager.glCallList(list);
assertEquals(0, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH), "must not have been recorded into the list");
GLStateManager.glDeleteLists(list, 1);
}
@Test
void stridedTexSubImage2DCompile() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SRC);
setStridedUnpack();
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
assertEquals(0, red(readback(SUB), SUB, 0, 0), "GL_COMPILE must not execute the upload");
setUnpack(0, 0, 0);
GLStateManager.glCallList(list);
assertStridedSubImage(readback(SUB));
assertUnpack(0, 0, 0);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void stridedTexSubImage2DCompileAndExecute() {
final ByteBuffer zeros = ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder());
final int tex = makeTexture(SUB, zeros);
final ByteBuffer src = makeSource(SRC);
setStridedUnpack();
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
assertStridedSubImage(readback(SUB));
setUnpack(0, 0, 0);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, zeros);
GLStateManager.glCallList(list);
assertStridedSubImage(readback(SUB));
assertUnpack(0, 0, 0);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void stridedTexImage2DCompile() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SRC);
setStridedUnpack();
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
setUnpack(0, 0, 0);
GLStateManager.glCallList(list);
assertStridedSubImage(readback(SUB));
assertUnpack(0, 0, 0);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void defaultRecordedTexSubImage2DReplaysWithDefaults() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SUB);
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
setStridedUnpack();
GLStateManager.glCallList(list);
assertTightSubImage(readback(SUB));
assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT), "live state must survive replay");
assertUnpack(SRC, 1, 2);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void defaultRecordedTexImage2DReplaysWithDefaults() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SUB);
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
setStridedUnpack();
GLStateManager.glCallList(list);
assertTightSubImage(readback(SUB));
assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT), "live state must survive replay");
assertUnpack(SRC, 1, 2);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
}
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.util.GLSMUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -133,7 +133,7 @@ public class GLSM_VAO_UnitTest {
private static void glBindVertexArray(int array) {
if (array == 0) array = GLStateManager.getDefaultVAO();
GLStateManager.boundVAO = array;
VertexAttribState.onBindVertexArray(array);
VAOManager.onBindVertexArrayPre(array);
UniversalVAO.bindVertexArray(array);
}
}
@@ -28,7 +28,7 @@ class VertexAttribDefaultBridgeTest {
@Test
void testGlColor4fSetsVertexAttrib() {
GLStateManager.glColor4f(0.5f, 0.25f, 0.75f, 0.8f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
assertEquals(0.5f, color[0], EPSILON, "color.r");
@@ -40,7 +40,7 @@ class VertexAttribDefaultBridgeTest {
@Test
void testGlNormal3fSetsVertexAttrib() {
GLStateManager.glNormal3f(0.0f, 1.0f, 0.0f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] normal = getVertexAttrib4f(NORMAL_ATTRIB);
assertEquals(0.0f, normal[0], EPSILON, "normal.x");
@@ -51,7 +51,7 @@ class VertexAttribDefaultBridgeTest {
@Test
void testSetLightmapTextureCoordsOnUnit1SetsVertexAttrib() {
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, 240.0f, 128.0f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] lm = getVertexAttrib4f(SECONDARY_UV_ATTRIB);
assertEquals(240.0f, lm[0], EPSILON, "lightmap.s");
@@ -64,7 +64,7 @@ class VertexAttribDefaultBridgeTest {
void testGlTexCoord2fSetsVertexAttrib() {
// glTexCoord2f outside recording → sets default vertex attrib 2 (PRIMARY_UV)
GLStateManager.glTexCoord2f(0.5f, 0.75f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] uv = getVertexAttrib4f(PRIMARY_UV_ATTRIB);
assertEquals(0.5f, uv[0], EPSILON, "texcoord.s");
@@ -77,7 +77,7 @@ class VertexAttribDefaultBridgeTest {
// Second call is a cache hit — changeColor not called.
// Vertex attrib should still hold the correct value from the first call.
GLStateManager.glColor4f(0.3f, 0.6f, 0.9f, 1.0f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
assertEquals(0.3f, color[0], EPSILON, "color.r after dedup");
@@ -89,7 +89,7 @@ class VertexAttribDefaultBridgeTest {
@Test
void testCompileMode_DoesNotSetVertexAttrib() {
GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
int listId = GLStateManager.glGenLists(1);
GLStateManager.glNewList(listId, org.lwjgl.opengl.GL11.GL_COMPILE);
@@ -3,10 +3,10 @@ package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.commands.BatchedIndexedDrawCmd;
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -307,10 +307,10 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
final ByteBuffer colorSlice = clientData.duplicate().order(ByteOrder.nativeOrder());
colorSlice.position(8);
VertexAttribState.set(0, 2, GL11.GL_FLOAT, false, POS_COLOR_STRIDE, clientData, 0);
VertexAttribState.setEnabled(0, true);
VertexAttribState.set(1, 4, GL11.GL_UNSIGNED_BYTE, true, POS_COLOR_STRIDE, colorSlice, 0);
VertexAttribState.setEnabled(1, true);
VAOManager.setAttribute(0, 2, GL11.GL_FLOAT, false, POS_COLOR_STRIDE, clientData);
VAOManager.enableAttribute(0);
VAOManager.setAttribute(1, 4, GL11.GL_UNSIGNED_BYTE, true, POS_COLOR_STRIDE, colorSlice);
VAOManager.enableAttribute(1);
GLStateManager.glBindVertexArray(0);
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
@@ -0,0 +1,61 @@
package com.gtnewhorizons.angelica.glsm.states;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PixelUnpackStateTest {
@Test
void defaults() {
assertTrue(PixelUnpackState.DEFAULT.isDefault());
assertEquals(4, PixelUnpackState.DEFAULT.alignment());
assertEquals(0, PixelUnpackState.DEFAULT.rowLength());
assertFalse(PixelUnpackState.DEFAULT.swapBytes());
assertFalse(PixelUnpackState.DEFAULT.lsbFirst());
}
@Test
void withUpdatesUnpackParams() {
PixelUnpackState s = PixelUnpackState.DEFAULT
.with(GL11.GL_UNPACK_ALIGNMENT, 1)
.with(GL11.GL_UNPACK_ROW_LENGTH, 64)
.with(GL11.GL_UNPACK_SKIP_ROWS, 2)
.with(GL11.GL_UNPACK_SKIP_PIXELS, 3)
.with(GL12.GL_UNPACK_IMAGE_HEIGHT, 32)
.with(GL12.GL_UNPACK_SKIP_IMAGES, 1)
.with(GL11.GL_UNPACK_SWAP_BYTES, 1)
.with(GL11.GL_UNPACK_LSB_FIRST, 1);
assertEquals(new PixelUnpackState(1, 64, 2, 3, 32, 1, true, true), s);
assertFalse(s.isDefault());
}
@Test
void invalidValuesIgnored() {
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 3));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 0));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ROW_LENGTH, -1));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SKIP_ROWS, -5));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL12.GL_UNPACK_IMAGE_HEIGHT, -1));
}
@Test
void booleansAcceptAnyNonzero() {
assertTrue(PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 2).swapBytes());
assertTrue(PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_LSB_FIRST, -1).lsbFirst());
PixelUnpackState on = PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 1);
assertFalse(on.with(GL11.GL_UNPACK_SWAP_BYTES, 0).swapBytes());
}
@Test
void packAndUnknownPnamesIgnored() {
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_PACK_ALIGNMENT, 1));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_PACK_ROW_LENGTH, 16));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(0x1234, 1));
}
}
@@ -23,6 +23,9 @@ import org.lwjgl.opengl.GL44C;
import org.lwjgl.opengl.GL45C;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.opengl.GLDebugMessageCallback;
import org.lwjgl.sdl.SDL_DropEvent;
import org.lwjgl.sdl.SDL_EventFilter;
import org.lwjgl.sdl.SDLEvents;
import org.lwjgl.system.MemoryStack;
import java.nio.ByteBuffer;
@@ -31,6 +34,10 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* LWJGL3 GL implementation of {@link RenderBackend}.
@@ -43,6 +50,10 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
private GLDebugMessageCallback debugCallback;
private boolean debugOutputActive;
private SDL_EventFilter dropEventFilter;
private final ConcurrentLinkedQueue<String> droppedFiles = new ConcurrentLinkedQueue<>();
private volatile boolean watchingDrops;
@Override
public void init() {
caps = GL.getCapabilities();
@@ -50,7 +61,49 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public void shutdown() {
// no-op
stopFileDrop();
}
@Override
public boolean supportsFileDrop() {
return true;
}
@Override
public void startFileDrop() {
if (watchingDrops) return;
dropEventFilter = SDL_EventFilter.create((userData, eventPtr) -> {
if (SDL_DropEvent.ntype(eventPtr) == SDLEvents.SDL_EVENT_DROP_FILE) {
final String path = SDL_DropEvent.ndataString(eventPtr);
if (path != null && !path.isEmpty()) {
droppedFiles.add(path);
}
}
return true;
});
SDLEvents.SDL_AddEventWatch(dropEventFilter, 0L);
watchingDrops = true;
}
@Override
public void stopFileDrop() {
if (!watchingDrops) return;
SDLEvents.SDL_RemoveEventWatch(dropEventFilter, 0L);
dropEventFilter.free();
dropEventFilter = null;
droppedFiles.clear();
watchingDrops = false;
}
@Override
public List<String> pollDroppedFiles() {
if (droppedFiles.isEmpty()) return Collections.emptyList();
final List<String> out = new ArrayList<>();
String p;
while ((p = droppedFiles.poll()) != null) {
out.add(p);
}
return out;
}
@Override
@@ -491,11 +544,6 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
GL11C.glPixelStorei(pname, param);
}
@Override
public void pixelStoref(int pname, float param) {
GL11C.glPixelStoref(pname, param);
}
@Override
public void sampleCoverage(float value, boolean invert) {
GL13C.glSampleCoverage(value, invert);
@@ -1028,6 +1076,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
GL30C.glBindVertexArray(array);
}
@Override
public boolean isVertexArray(int array) {
return GL30C.glIsVertexArray(array);
}
@Override
public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) {
GL20C.glVertexAttribPointer(index, size, type, normalized, stride, pointer);
@@ -1318,11 +1371,14 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public boolean supportsDebugOutput() {
return caps != null && caps.OpenGL43;
return caps != null && (caps.OpenGL43 || caps.GL_KHR_debug);
}
@Override
public int setupDebugOutput(DebugMessageHandler handler) {
if (caps == null || !(caps.OpenGL43 || caps.GL_KHR_debug)) {
return 0;
}
debugCallback = GLDebugMessageCallback.create(
(source, type, id, severity, length, message, userParam) -> {
final String msg = GLDebugMessageCallback.getMessage(length, message);
@@ -1350,6 +1406,9 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public int disableDebugOutput() {
if (caps == null || !(caps.OpenGL43 || caps.GL_KHR_debug)) {
return 0;
}
GL43C.glDebugMessageCallback(null, 0L);
if (debugCallback != null) {
debugCallback.free();
+1 -1
View File
@@ -16,7 +16,7 @@ pluginManagement {
}
plugins {
id("com.gtnewhorizons.gtnhsettingsconvention") version ("2.0.25")
id("com.gtnewhorizons.gtnhsettingsconvention") version ("2.0.26")
}
include("glsm")
@@ -25,6 +25,7 @@ public class DeferredDrawBatcher {
@Getter private static boolean active = false;
private static DeferredBatchTessellator batchTessellator;
private static long entryStateKey;
private DeferredDrawBatcher() {}
@@ -41,13 +42,14 @@ public class DeferredDrawBatcher {
batchTessellator.clearRanges();
batchTessellator.setParentTessellator(Tessellator.instance);
batchTessellator.snapshotDefaultModelview();
entryStateKey = captureStateKey();
TessellatorManager.startCapturingDirect(batchTessellator);
}
/**
* Exit deferred mode and flush all captured batches. Groups entries by state key
* and vertex format, issues one draw per unique group. Restores the GL state that
* was active for the last group so vanilla sees consistent state.
* and vertex format, issues one draw per unique group. Restores the bracket's entry
* state so the trailing vanilla Tessellator.draw() renders with the layer atlas.
*/
public static void exitAndFlush() {
active = false;
@@ -56,6 +58,7 @@ public class DeferredDrawBatcher {
if (!ranges.isEmpty()) {
ranges.sort(Comparator.comparingLong(DeferredBatchTessellator.DrawRange::stateKey));
flushSorted(ranges);
applyStateKey(entryStateKey);
}
TessellatorManager.stopCapturingDirect();
@@ -147,7 +150,8 @@ public class DeferredDrawBatcher {
* - fog state (mode, color, start/end — constant for the frame)
*/
static long captureStateKey() {
final int textureId = GLStateManager.getTextures().getTextureUnitBindings(0).getBinding();
final int activeUnit = GLStateManager.getActiveTextureUnit();
final int textureId = GLStateManager.getTextures().getTextureUnitBindings(activeUnit).getBinding();
final int srcRgb = GLStateManager.getBlendState().getSrcRgb();
final int dstRgb = GLStateManager.getBlendState().getDstRgb();
final boolean blendEnabled = GLStateManager.getBlendMode().isEnabled();
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.compat;
import com.falsepattern.endlessids.config.GeneralConfig;
import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
import cpw.mods.fml.common.Loader;
@@ -14,6 +15,8 @@ public class ModStatus {
public static boolean isBetterCrashesLoaded;
public static boolean isNEIDLoaded;
public static boolean isNEIDMetadataExtended;
public static boolean isEIDLoaded;
public static boolean isMetadataExtended;
public static boolean isLotrLoaded;
public static boolean isChunkAPILoaded;
public static boolean isEIDBiomeLoaded;
@@ -35,9 +38,9 @@ public class ModStatus {
isBetterCrashesLoaded = Loader.isModLoaded("bettercrashes");
isNEIDLoaded = Loader.isModLoaded("neid");
isEIDLoaded = Loader.isModLoaded("endlessids");
isLotrLoaded = Loader.isModLoaded("lotr");
isChunkAPILoaded = Loader.isModLoaded("chunkapi");
isEIDBiomeLoaded = Loader.isModLoaded("endlessids_biome");
isXaerosMinimapLoaded = Loader.isModLoaded("XaeroMinimap");
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
isBattlegearLoaded = Loader.isModLoaded("battlegear2");
@@ -65,5 +68,15 @@ public class ModStatus {
isNEIDMetadataExtended = true;
}
}
boolean eidBlockItemExtended = false;
boolean eidBiomeExtended = false;
if (isEIDLoaded) {
eidBlockItemExtended = GeneralConfig.extendBlockItem;
eidBiomeExtended = GeneralConfig.extendBiome;
}
isEIDBiomeLoaded = eidBiomeExtended || Loader.isModLoaded("endlessids_biome");
isMetadataExtended = isNEIDMetadataExtended || eidBlockItemExtended;
}
}
@@ -227,13 +227,13 @@ public class AngelicaConfig {
@Config.Comment({"Block corners and edges between chunks might have \"cracks\" (various lines/dots) in them.",
"While using \"Compact Vertex Format\" makes the situation even worse.",
"This option fixes it.",
"Requires texture reloading (F3 + T) after changing this option to take effect"})
@Config.DefaultBoolean(true)
public static boolean fixBlockCrack;
"This option fixes it, though may lead to other visual artifacts.",
"Requires game restart after changing this option to take effect"})
@Config.DefaultBoolean(false)
public static boolean blockCrackFix;
@Config.Comment({
"The \"epsilon\" value for the fixBlockCrack option. ",
"The \"epsilon\" value for the blockCrackFix option. ",
"Set this a bit higher if you can still see lines/dots between solid blocks in dark areas.",
"May cause intense flickering (z-fighting) between blocks if the value is too high"
})
@@ -241,7 +241,7 @@ public class AngelicaConfig {
@Config.DefaultDouble(0.001)
public static double blockCrackFixEpsilon;
@Config.Comment("Block classes that have bugs when rendering with the fixBlockCrack can be put here to avoid manipulating them")
@Config.Comment("Block classes that have bugs when rendering with the blockCrackFix can be put here to avoid manipulating them")
@Config.DefaultStringList({
"net.minecraft.block.BlockCauldron",
"net.minecraft.block.BlockStairs"
@@ -249,7 +249,7 @@ public class AngelicaConfig {
public static String[] blockCrackFixBlacklist;
@Config.Comment({"Block classes that have render pass other than 0 but still need to be manipulated.",
"Add a block class here if you see flickering (z-fighting) with fixBlockCrack enabled"
"Add a block class here if you see flickering (z-fighting) with blockCrackFix enabled"
})
@Config.DefaultStringList({
"gregtech.common.blocks.BlockOres",
@@ -310,6 +310,16 @@ public class AngelicaConfig {
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public boolean narrowEars;
@Config.Comment("Narrow Fisk's Superheroes transformer exclusions to allow GL redirection")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public boolean narrowFiskHeroes;
@Config.Comment("Narrow FoamFix transformer exclusions to allow GL redirection in its repackaged Ears")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public boolean narrowFoamFix;
}
@Config.Comment("Renders chunks before neighbors are ready. Improves loading at render distance edges, useful for low render distance servers.")
@@ -186,6 +186,8 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
narrowTransformerConfig("AdvancedLightsabers", AngelicaConfig.transformerCompat.narrowAdvancedLightsabers);
narrowTransformerConfig("Alfheim", AngelicaConfig.transformerCompat.narrowAlfheim);
narrowTransformerConfig("Ears", AngelicaConfig.transformerCompat.narrowEars);
narrowTransformerConfig("FiskHeroes", AngelicaConfig.transformerCompat.narrowFiskHeroes);
narrowTransformerConfig("FoamFix", AngelicaConfig.transformerCompat.narrowFoamFix);
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.IncompatibleModsDisablerTweaker");
if (AngelicaConfig.enableHudCaching) {
@@ -117,7 +117,7 @@ public enum Mixins implements IMixins {
.setPhase(Phase.EARLY)
.addClientMixins("angelica.bugfixes.MixinRenderBlocks_CrackFix")
.addExcludedMod(TargetedMod.FALSETWEAKS)
.setApplyIf(() -> AngelicaConfig.fixBlockCrack)),
.setApplyIf(() -> AngelicaConfig.blockCrackFix)),
ANGELICA_FIX_FLUID_RENDERER_CHECKING_BLOCK_AGAIN(
new MixinBuilder("Fix RenderBlockFluid reading the block type from the world access multiple times")
@@ -14,6 +14,7 @@ import com.gtnewhorizons.angelica.rendering.celeritas.light.VanillaDiffuseProvid
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
import lombok.Getter;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.vertices.ExtendedDataHelper;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
@@ -184,6 +185,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
animatedSprites.add(sprite);
}
final boolean isFluid = blockRenderContext.renderType == ExtendedDataHelper.FLUID_RENDER_TYPE;
// If block used vanilla AO, then we can safely compute our own lighting data and ignore what vanilla
// computed. We suppress vanilla emitting the brightness/color in MixinRenderBlocks.
// Otherwise, we need to use the block's lightmap values as they are as the ISBRH may have
@@ -213,6 +216,17 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
for (int vIdx = 0; vIdx < 4; vIdx++) {
vertices[vIdx].trueNormal = trueNormal;
}
if (useAoCalculation && isFluid) {
quadView.setup(trueNormal, blockX, blockY, blockZ);
final ModelQuadFacing lightFace = quadView.getLightFace();
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
final ModelQuadFacing cullFace = quadView.getCullFace();
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, cullFace, lightFace, shouldApplyDiffuse, true);
for (int vIdx = 0; vIdx < 4; vIdx++) {
vertices[vIdx].light = quadLightData.lm[vIdx];
}
}
}
// Apply dynamic lights after AO calculation so they're not overwritten
@@ -269,7 +269,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
if (contextEncoder != null) {
final byte lightValue = (byte) block.getLightValue();
if (isFluid) {
contextEncoder.prepareToRenderFluid(blockRenderContext, block, lightValue);
contextEncoder.prepareToRenderFluid(blockRenderContext, block, metadata, lightValue);
} else {
int effectiveMeta = metadata;
if (BlockRenderingSettings.INSTANCE.hasSnowyEntries()
@@ -21,6 +21,8 @@ public class AngelicaRenderPassConfiguration {
private record AngelicaPipelineState(int pass, boolean disableAlphaTest) implements TerrainRenderPass.PipelineState {
@Override
public void setup() {
GLStateManager.glDepthMask(true);
if (pass == 0) {
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
}
@@ -7,7 +7,7 @@ public interface ContextAwareChunkVertexEncoder extends ChunkVertexEncoder {
void prepareToRenderBlock(BlockRenderContext ctx, Block block, int metadata, short renderType, byte lightValue);
void prepareToRenderFluid(BlockRenderContext ctx, Block block, byte lightValue);
void prepareToRenderFluid(BlockRenderContext ctx, Block block, int metadata, byte lightValue);
void finishRenderingBlock();
}
@@ -7,11 +7,14 @@ import static com.gtnewhorizons.angelica.rendering.celeritas.iris.IrisExtendedCh
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import lombok.Setter;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.vertices.ExtendedDataHelper;
import net.coderbot.iris.vertices.NormalHelper;
import net.coderbot.iris.vertices.NormI8;
import net.minecraft.block.Block;
import net.minecraft.block.BlockDynamicLiquid;
import net.minecraft.block.BlockStaticLiquid;
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.Material;
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexEncoder;
import org.joml.Vector3f;
@@ -53,20 +56,44 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
public void prepareToRenderBlock(BlockRenderContext ctx, Block block, int metadata, short renderType, byte lightValue) {
this.context = ctx;
Int2IntMap metaMap = blockMetaMatches != null ? blockMetaMatches.get(block) : null;
ctx.blockId = (short) (metaMap != null ? metaMap.get(metadata) : -1);
ctx.blockId = (short) (metaMap != null ? BlockMaterialMapping.resolveId(metaMap, metadata) : -1);
ctx.renderType = renderType;
ctx.lightValue = lightValue;
}
@Override
public void prepareToRenderFluid(BlockRenderContext ctx, Block block, byte lightValue) {
public void prepareToRenderFluid(BlockRenderContext ctx, Block block, int metadata, byte lightValue) {
this.context = ctx;
Int2IntMap metaMap = blockMetaMatches != null ? blockMetaMatches.get(block) : null;
ctx.blockId = (short) (metaMap != null ? metaMap.get(0) : -1);
ctx.blockId = (short) resolveFluidBlockId(block, metadata);
ctx.renderType = ExtendedDataHelper.FLUID_RENDER_TYPE;
ctx.lightValue = lightValue;
}
/**
* Try and prevent material flickering
*/
private int resolveFluidBlockId(Block block, int metadata) {
if (blockMetaMatches == null) return -1;
int id = lookupFluidMeta(block, metadata);
if (id == -1) {
final Block counterpart = liquidCounterpart(block);
if (counterpart != null) id = lookupFluidMeta(counterpart, metadata);
}
return id;
}
private int lookupFluidMeta(Block block, int metadata) {
final Int2IntMap metaMap = blockMetaMatches.get(block);
return metaMap != null ? BlockMaterialMapping.resolveId(metaMap, metadata) : -1;
}
private static Block liquidCounterpart(Block block) {
if (block instanceof BlockStaticLiquid) return Block.getBlockById(Block.getIdFromBlock(block) - 1);
if (block instanceof BlockDynamicLiquid) return Block.getBlockById(Block.getIdFromBlock(block) + 1);
return null;
}
@Override
public void finishRenderingBlock() {
if (context != null) {
@@ -289,9 +289,6 @@ public abstract class TileOverride implements Comparable<TileOverride> {
if (token.isEmpty()) {
// nothing
} else if (token.contains("/")) {
if (!token.endsWith(".png")) {
token += ".png";
}
ResourceLocation resource = TexturePackAPI.parseResourceLocation(properties.getResource(), token);
if (resource != null) {
list.add(resource.toString());
+30 -4
View File
@@ -13,6 +13,7 @@ import cpw.mods.fml.common.gameevent.InputEvent;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import lombok.Getter;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.celeritas.IrisCeleritasShaderProvider;
import net.coderbot.iris.compat.dh.DHCompat;
@@ -72,7 +73,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.zip.ZipError;
import java.util.zip.ZipException;
public class Iris {
@@ -458,6 +458,33 @@ public class Iris {
}
}
/**
* Toggles shader debug mode. When enabled, patched shader sources are dumped to disk (see
* {@link net.coderbot.iris.pipeline.PatchedShaderPrinter}) and logging is more verbose.
*/
public static void setDebug(boolean enable) {
try {
irisConfig.setDebugEnabled(enable);
irisConfig.save();
reload();
} catch (IOException e) {
logger.error("Failed to " + (enable ? "enable" : "disable") + " debug mode", e);
}
logger.info("Debug functionality is " + (enable ? "enabled, logging will be more verbose!" : "disabled."));
}
/**
* Toggles whether unrecognized shader packs may load, exposing the {@code ALLOWS_UNKNOWN_SHADERS} macro.
*/
public static void setAllowUnknownShaders(boolean allow) {
try {
irisConfig.setUnknown(allow);
reload();
} catch (IOException e) {
logger.error("Failed to " + (allow ? "allow" : "disallow") + " unknown shaders", e);
}
}
public static void loadShaderpack() {
if (irisConfig == null) {
if (!initialized) {
@@ -678,8 +705,7 @@ public class Iris {
try (Stream<Path> stream = Files.walk(root)) {
return stream.filter(Files::isDirectory).anyMatch(path -> path.endsWith("shaders"));
}
} catch (ZipError zipError) {
// Java 8 seems to throw a ZipError instead of a subclass of IOException
} catch (ZipException e) {
Iris.logger.warn("The ZIP at " + pack + " is corrupt");
} catch (IOException ignored) {
// ignored, not a valid shader pack.
@@ -923,7 +949,7 @@ public class Iris {
return;
final Int2IntMap metaMap = blockMetaMatches.get(block);
final int blockId = metaMap != null ? metaMap.get(meta) : -1;
final int blockId = metaMap != null ? BlockMaterialMapping.resolveId(metaMap, meta) : -1;
if (TessellatorManager.get() instanceof StateAwareTessellator tess)
tess.angelica$setShaderOverrideBlockId((short) blockId);
@@ -1,5 +1,6 @@
package net.coderbot.iris.block_rendering;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.rendering.celeritas.BlockRenderLayer;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@@ -27,10 +28,12 @@ public class BlockMaterialMapping {
public record BlockIdMaps(Reference2ObjectMap<Block, Int2IntMap> blockMetaMap, NbtConditionalIdMap<Block> tileEntityMap) {}
// Meta-key bits OR'd in at runtime
public static final int SNOWY_META_BIT = 0x10;
public static final int SNOWY_META_BIT = 1 << 16;
public static final int DOUBLE_PLANT_TOP_BIT = 0x8;
public static final int WILDCARD_META_KEY = 1 << 30;
/**
* Creates the standard block meta ID map, the TileEntity NBT-conditional map, and registers
* snowy blocks on {@link BlockRenderingSettings}.
@@ -168,11 +171,17 @@ public class BlockMaterialMapping {
if (metas.isEmpty()) {
for (int meta = 0; meta < 16; meta++) metaMap.putIfAbsent(meta | extraBits, intId);
if (ModStatus.isMetadataExtended) metaMap.putIfAbsent(WILDCARD_META_KEY | extraBits, intId);
} else {
for (int meta : metas) metaMap.putIfAbsent(meta | extraBits, intId);
}
}
public static int resolveId(Int2IntMap metaMap, int metaKey) {
final int id = metaMap.get(metaKey);
return id != -1 ? id : metaMap.get(WILDCARD_META_KEY | (metaKey & SNOWY_META_BIT));
}
/**
* Returns the registered Block for an id, or null if unknown.
* The registry returns Blocks.air (its default) for missing keys, which we coerce to null.
@@ -161,6 +161,7 @@ public class DHCompatInternal {
if (((IRenderTargetExt) Minecraft.getMinecraft().getFramebuffer()).iris$getDepthBufferVersion() != cachedVersion) {
cachedVersion = ((IRenderTargetExt) Minecraft.getMinecraft().getFramebuffer()).iris$getDepthBufferVersion();
createDepthTex(Minecraft.getMinecraft().getFramebuffer().framebufferWidth, Minecraft.getMinecraft().getFramebuffer().framebufferHeight);
storedDepthTex = -1;
}
if (storedDepthTex != depthTex && dhTerrainFramebuffer != null) {
storedDepthTex = depthTex;
@@ -31,7 +31,6 @@ import net.coderbot.iris.uniforms.custom.CustomUniforms;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
@@ -46,12 +45,6 @@ import java.util.Map;
public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgram {
// Uniforms
public final int modelViewUniform;
public final int modelViewInverseUniform;
public final int projectionUniform;
public final int projectionInverseUniform;
public final int normalMatrix3fUniform;
// Fog/Clip Uniforms
private final int id;
private final ProgramUniforms uniforms;
private final CustomUniforms customUniforms;
@@ -62,7 +55,6 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
private final Matrix4f tempModel = new Matrix4f();
private final Matrix4f tempProj = new Matrix4f();
private final Matrix4f tempMat4 = new Matrix4f();
private final Matrix3f tempMat3 = new Matrix3f();
private final int instancedShaderOffsetChunkUniform;
private final int instancedShaderOffsetSubChunkUniform;
@@ -143,12 +135,6 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
GLStateManager.glEnableVertexAttribArray(0);
projectionUniform = tryGetUniformLocation2("iris_ProjectionMatrix");
projectionInverseUniform = tryGetUniformLocation2("iris_ProjectionMatrixInverse");
modelViewUniform = tryGetUniformLocation2("iris_ModelViewMatrix");
modelViewInverseUniform = tryGetUniformLocation2("iris_ModelViewMatrixInverse");
normalMatrix3fUniform = tryGetUniformLocation2("iris_NormalMatrix");
this.instancedShaderOffsetChunkUniform = this.tryGetUniformLocation2("uOffsetChunk");
this.instancedShaderOffsetSubChunkUniform = this.tryGetUniformLocation2("uOffsetSubChunk");
this.instancedShaderCameraChunkPosUniform = this.tryGetUniformLocation2("uCameraPosChunk");
@@ -213,18 +199,6 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
}
}
public void setUniform(int index, Matrix3f matrix) {
if (index == -1) return;
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer buffer = stack.callocFloat(9);
matrix.get(buffer);
buffer.rewind();
GLStateManager.glUniformMatrix3(index, false, buffer);
}
}
// Override ShaderProgram.bind()
public void bind(DhApiRenderParam renderParam) {
GLStateManager.glBindVertexArray(va);
@@ -238,14 +212,8 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
toJOML(tempModel, renderParam.dhModelViewMatrix);
toJOML(tempProj, renderParam.dhProjectionMatrix);
setUniform(modelViewUniform, tempModel);
setUniform(projectionUniform, tempProj);
this.setUniform(this.instancedShaderProjectionModelViewMatrixUniform, tempMat4.set(tempProj).mul(tempModel));
tempModel.invert();
tempProj.invert();
setUniform(modelViewInverseUniform, tempModel);
setUniform(projectionInverseUniform, tempProj);
setUniform(normalMatrix3fUniform, tempModel.transpose3x3(tempMat3));
LodRendererEvents.pushDhMatrices(tempProj, tempModel);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + IrisSamplers.LIGHTMAP_TEXTURE_UNIT);
DynamicTexture lightmapTexture = ((EntityRendererAccessor) Minecraft.getMinecraft().entityRenderer).getLightmapTexture();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture.getGlTextureId());
@@ -259,6 +227,7 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
}
public void unbind() {
LodRendererEvents.popDhMatrices();
GLStateManager.glBindVertexArray(0);
GLStateManager.glUseProgram(0);
ProgramUniforms.clearActiveUniforms();
@@ -3,7 +3,6 @@ package net.coderbot.iris.compat.dh;
import com.google.common.primitives.Ints;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.EntityRendererAccessor;
import com.mitchej123.lwjgl.MemoryStack;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
import net.coderbot.iris.gl.blending.BlendModeOverride;
@@ -26,15 +25,12 @@ import net.coderbot.iris.uniforms.builtin.BuiltinReplacementUniforms;
import net.coderbot.iris.uniforms.custom.CustomUniforms;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL32;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -44,11 +40,6 @@ public class IrisLodRenderProgram {
public final int modelOffsetUniform;
public final int worldYOffsetUniform;
public final int mircoOffsetUniform;
public final int modelViewUniform;
public final int modelViewInverseUniform;
public final int projectionUniform;
public final int projectionInverseUniform;
public final int normalMatrix3fUniform;
// Fog/Clip Uniforms
public final int clipDistanceUniform;
private final int id;
@@ -58,9 +49,6 @@ public class IrisLodRenderProgram {
private final ProgramImages images;
private final BlendModeOverride blend;
private final BufferBlendOverride[] bufferBlendOverrides;
private final Matrix4f tempMat4a = new Matrix4f();
private final Matrix4f tempMat4b = new Matrix4f();
private final Matrix3f tempMat3 = new Matrix3f();
// This will bind AbstractVertexAttribute
private IrisLodRenderProgram(String name, boolean isShadowPass, boolean translucent, BlendModeOverride override, BufferBlendOverride[] bufferBlendOverrides, String vertex, String tessControl, String tessEval, String geometry, String fragment, CustomUniforms customUniforms, DeferredWorldRenderingPipeline pipeline) {
@@ -132,11 +120,6 @@ public class IrisLodRenderProgram {
modelOffsetUniform = tryGetUniformLocation2("modelOffset");
worldYOffsetUniform = tryGetUniformLocation2("worldYOffset");
mircoOffsetUniform = tryGetUniformLocation2("mircoOffset");
projectionUniform = tryGetUniformLocation2("iris_ProjectionMatrix");
projectionInverseUniform = tryGetUniformLocation2("iris_ProjectionMatrixInverse");
modelViewUniform = tryGetUniformLocation2("iris_ModelViewMatrix");
modelViewInverseUniform = tryGetUniformLocation2("iris_ModelViewMatrixInverse");
normalMatrix3fUniform = tryGetUniformLocation2("iris_NormalMatrix");
// Fog/Clip Uniforms
clipDistanceUniform = tryGetUniformLocation2("clipDistance");
@@ -176,30 +159,6 @@ public class IrisLodRenderProgram {
return GLStateManager.glGetUniformLocation(this.id, name);
}
public void setUniform(int index, Matrix4fc matrix) {
if (index == -1 || matrix == null) return;
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer buffer = stack.callocFloat(16);
matrix.get(buffer);
buffer.rewind();
GLStateManager.glUniformMatrix4(index, false, buffer);
}
}
public void setUniform(int index, Matrix3f matrix) {
if (index == -1) return;
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer buffer = stack.callocFloat(9);
matrix.get(buffer);
buffer.rewind();
GLStateManager.glUniformMatrix3(index, false, buffer);
}
}
// Override ShaderProgram.bind()
public void bind() {
GLStateManager.glUseProgram(id);
@@ -227,11 +186,8 @@ public class IrisLodRenderProgram {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + IrisSamplers.LIGHTMAP_TEXTURE_UNIT);
DynamicTexture lightmapTexture = ((EntityRendererAccessor) Minecraft.getMinecraft().entityRenderer).getLightmapTexture();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture.getGlTextureId());
setUniform(modelViewUniform, modelView);
setUniform(modelViewInverseUniform, modelView.invert(tempMat4a));
setUniform(projectionUniform, projection);
setUniform(projectionInverseUniform, projection.invert(tempMat4b));
setUniform(normalMatrix3fUniform, tempMat4a.set(modelView).invert().transpose3x3(tempMat3));
LodRendererEvents.pushDhMatrices(projection, modelView);
setUniform(mircoOffsetUniform, 0.01f); // 0.01 block offset
@@ -33,6 +33,7 @@ import net.coderbot.iris.shadows.ShadowRenderingState;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import net.irisshaders.iris.api.v0.IrisApi;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11;
public class LodRendererEvents {
@@ -46,6 +47,28 @@ public class LodRendererEvents {
private static int textureHeight;
private static final Matrix4f tempMat4 = new Matrix4f();
private static boolean dhMatricesActive = false;
private static final Matrix4f savedModelView = new Matrix4f();
private static final Matrix4f savedProjection = new Matrix4f();
static void pushDhMatrices(Matrix4fc projection, Matrix4fc modelView) {
if (!dhMatricesActive) {
savedModelView.set(GLStateManager.getModelViewMatrix());
savedProjection.set(GLStateManager.getProjectionMatrix());
dhMatricesActive = true;
}
GLStateManager.setModelViewMatrix(modelView);
GLStateManager.setProjectionMatrix(projection);
}
static void popDhMatrices() {
if (dhMatricesActive) {
GLStateManager.setModelViewMatrix(savedModelView);
GLStateManager.setProjectionMatrix(savedProjection);
dhMatricesActive = false;
}
}
// constructor //
@@ -181,6 +204,7 @@ public class LodRendererEvents {
DhApiBeforeRenderCleanupEvent beforeCleanupEvent = new DhApiBeforeRenderCleanupEvent() {
@Override
public void beforeCleanup(DhApiEventParam<DhApiRenderParam> event) {
popDhMatrices();
if (getInstance().shouldOverride) {
if (ShadowRenderingState.areShadowsCurrentlyBeingRendered()) {
getInstance().getShadowShader().unbind();
@@ -38,6 +38,8 @@ public class IrisConfig {
*/
private boolean enableDebugOptions;
private boolean allowUnknownShaders;
private final Path propertiesPath;
public IrisConfig(Path propertiesPath) {
@@ -45,6 +47,7 @@ public class IrisConfig {
enableShaders = true;
disableUpdateMessage = false;
enableDebugOptions = false;
allowUnknownShaders = false;
this.propertiesPath = propertiesPath;
}
@@ -113,6 +116,25 @@ public class IrisConfig {
return enableDebugOptions;
}
/**
* Sets whether debug features are enabled.
*/
public void setDebugEnabled(boolean enabled) {
this.enableDebugOptions = enabled;
}
public boolean shouldAllowUnknownShaders() {
return allowUnknownShaders;
}
/**
* Sets whether unrecognized shader packs may load.
*/
public void setUnknown(boolean allow) throws IOException {
this.allowUnknownShaders = allow;
save();
}
/**
* loads the config file and then populates the string, int, and boolean entries with the parsed entries
*
@@ -132,6 +154,7 @@ public class IrisConfig {
shaderPackName = properties.getProperty("shaderPack");
enableShaders = !"false".equals(properties.getProperty("enableShaders"));
enableDebugOptions = "true".equals(properties.getProperty("enableDebugOptions"));
allowUnknownShaders = "true".equals(properties.getProperty("allowUnknownShaders"));
disableUpdateMessage = "true".equals(properties.getProperty("disableUpdateMessage"));
// TODO: GUI
try {
@@ -159,6 +182,7 @@ public class IrisConfig {
properties.setProperty("shaderPack", getShaderPackName().orElse(""));
properties.setProperty("enableShaders", enableShaders ? "true" : "false");
properties.setProperty("enableDebugOptions", enableDebugOptions ? "true" : "false");
properties.setProperty("allowUnknownShaders", allowUnknownShaders ? "true" : "false");
properties.setProperty("disableUpdateMessage", disableUpdateMessage ? "true" : "false");
properties.setProperty("maxShadowRenderDistance", String.valueOf(IrisVideoSettings.shadowDistance));
// NB: This uses ISO-8859-1 with unicode escapes as the encoding
@@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.Tags;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import cpw.mods.fml.common.Loader;
import net.coderbot.iris.Iris;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.parsing.BiomeCategories;
import net.coderbot.iris.pipeline.HandRenderer;
@@ -91,6 +92,10 @@ public class StandardMacros {
define(standardDefines, "DISTANT_HORIZONS");
}
if (Iris.getIrisConfig().shouldAllowUnknownShaders()) {
define(standardDefines, "ALLOWS_UNKNOWN_SHADERS");
}
define(standardDefines, "DH_BLOCK_UNKNOWN", String.valueOf(0));
define(standardDefines, "DH_BLOCK_LEAVES", String.valueOf(1));
define(standardDefines, "DH_BLOCK_STONE", String.valueOf(2));
@@ -4,10 +4,13 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import cpw.mods.fml.client.config.GuiUtils;
import lombok.Getter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
/**
* Class serving as abstraction and
@@ -111,6 +114,32 @@ public final class GuiUtil {
font.drawStringWithShadow(text, x + 4, y + 4, 0xFFFFFF);
}
public static void drawScrollingText(FontRenderer font, String text, int centerX, int minX, int maxX, int minY, int maxY, int color) {
final int textWidth = font.getStringWidth(text);
final int boxWidth = maxX - minX;
final int textY = ((minY + maxY) - font.FONT_HEIGHT) / 2 + 1;
if (textWidth > boxWidth) {
final int overflow = textWidth - boxWidth;
final double time = System.currentTimeMillis() / 1000.0;
final double period = Math.max(overflow * 0.5, 3.0);
final double t = (Math.sin((Math.PI / 2.0) * Math.cos((Math.PI * 2.0) * time / period)) / 2.0) + 0.5;
final int scroll = (int) (t * overflow);
final Minecraft mc = client();
final int scale = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight).getScaleFactor();
GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
GLStateManager.glScissor(minX * scale, mc.displayHeight - (maxY * scale), boxWidth * scale, (maxY - minY) * scale);
font.drawStringWithShadow(text, minX - scroll, textY, color);
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
} else {
int textX = centerX - (textWidth / 2);
textX = Math.min(textX, maxX - textWidth);
textX = Math.max(textX, minX);
font.drawStringWithShadow(text, textX, textY, color);
}
}
/**
* Shorten a text to a specific length, adding an ellipsis (...)
* to the end if shortened.
@@ -140,13 +169,25 @@ public final class GuiUtil {
* @return the translated text if found, otherwise the default provided
*/
public static String translateOrDefault(String defaultText, String translationDesc, Object ... format) {
final String translated = I18n.format(translationDesc, format);
final String translated = format.length == 0
? translateLenient(translationDesc)
: I18n.format(translationDesc, format);
if(!translated.equals(translationDesc)) {
return translated;
}
return defaultText;
}
private static final String FORMAT_ERROR_PREFIX = "Format error: ";
/**
* Nasty dirty hack, but it works.
*/
public static String translateLenient(String key) {
final String translated = I18n.format(key);
return translated.startsWith(FORMAT_ERROR_PREFIX) ? translated.substring(FORMAT_ERROR_PREFIX.length()) : translated;
}
/**
* Plays the {@code UI_BUTTON_CLICK} sound event as a
* master sound effect.
@@ -155,7 +196,7 @@ public final class GuiUtil {
* or other action.
*/
public static void playButtonClickSound() {
// client().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1));
client().getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F));
}
/**
@@ -21,7 +21,9 @@ public class IrisElementRow {
private final int spacing;
private int x;
private int y;
private int width;
@Getter
private int width;
private int height;
public IrisElementRow(int spacing) {
@@ -19,7 +19,7 @@ public abstract class IrisGuiSlot extends GuiSlot {
super(mc, width, height, top, bottom, slotHeight);
// Set Center Vertically to false
this.field_148163_i = false;
this.headerPadding = 2;
}
@Override
@@ -37,11 +37,15 @@ public abstract class IrisGuiSlot extends GuiSlot {
}
@Override
protected void drawSelectionBox(int x, int y, int mouseX, int mouseY) {
final int oldPadding = this.headerPadding;
this.headerPadding = 2;
super.drawSelectionBox(x, y, mouseX, mouseY);
this.headerPadding = oldPadding;
public int func_148124_c/*getSlotIndexFromScreenCoords*/(int x, int y) {
final int left = this.left + this.width / 2 - this.getListWidth() / 2 + hitLeftInset() + hitXShift();
final int right = this.left + this.width / 2 + this.getListWidth() / 2 - hitRightInset() + hitXShift();
final int relativeY = y - this.top + (int) this.amountScrolled - hitYOffset();
final int index = relativeY / this.slotHeight;
if (y <= this.top || y >= this.bottom) {
return -1;
}
return x < this.getScrollBarX() && x >= left && x <= right && index >= 0 && relativeY >= 0 && index < this.getSize() ? index : -1;
}
@Override
@@ -53,6 +57,23 @@ public abstract class IrisGuiSlot extends GuiSlot {
return false;
}
protected int hitYOffset() {
return 4;
}
protected int hitRightInset() {
return 0;
}
protected int hitLeftInset() {
return 0;
}
protected int hitXShift() {
return 0;
}
public boolean mouseClicked(int mouseX, int mouseY, int mouseButton) {
if (!this.func_148125_i/*enabled*/()) {
return false;
@@ -60,15 +81,15 @@ public abstract class IrisGuiSlot extends GuiSlot {
final int size = this.getSize();
final int scrollBarX = this.getScrollBarX();
final int rightEdge = scrollBarX + 6;
final int elementLeft = this.width / 2 - this.getListWidth() / 2;
final int elementRight = this.width / 2 + this.getListWidth() / 2;
final int relativeY = mouseY - this.top - this.headerPadding + (int) this.amountScrolled - 4;
final int elementLeft = this.width / 2 - this.getListWidth() / 2 + hitLeftInset() + hitXShift();
final int elementRight = this.width / 2 + this.getListWidth() / 2 - hitRightInset() + hitXShift();
final int relativeY = mouseY - this.top + (int) this.amountScrolled - hitYOffset();
boolean handled = false;
final boolean leftMouseDown = Mouse.isButtonDown(0);
final boolean rightMouseDown = Mouse.isButtonDown(1);
if (mouseX <= this.left || mouseX >= this.right || mouseY <= this.top || mouseY >= this.bottom) {
return handled;
return false;
}
if (leftMouseDown && mouseX >= scrollBarX && mouseX <= rightEdge) {
scrolling = true;
@@ -25,7 +25,7 @@ public class ShaderPackOptionList extends IrisGuiSlot {
private final List<BaseEntry> entries = new ArrayList<>();
public ShaderPackOptionList(ShaderPackScreen screen, NavigationController navigation, ShaderPack pack, Minecraft client, int width, int height, int top, int bottom, int left, int right) {
super(client, width, height, top, bottom, 20);
super(client, width, height, top, bottom, 24);
this.navigation = navigation;
this.screen = screen;
@@ -72,7 +72,7 @@ public class ShaderPackOptionList extends IrisGuiSlot {
}
}
if (row.size() > 0) {
if (!row.isEmpty()) {
while (row.size() < columns) {
row.add(AbstractElementWidget.EMPTY);
}
@@ -93,7 +93,12 @@ public class ShaderPackOptionList extends IrisGuiSlot {
}
@Override
public boolean mouseReleased( int mouseX, int mouseY, int button) {
final int relativeY = mouseY - this.top - this.headerPadding + (int) this.amountScrolled - 4;
this.scrolling = false;
if (mouseY <= this.top || mouseY >= this.bottom) {
return false;
}
final int relativeY = mouseY - this.top + (int) this.amountScrolled - hitYOffset();
final int index = relativeY / this.slotHeight;
if (index < 0 || index >= this.entries.size())
@@ -113,11 +118,31 @@ public class ShaderPackOptionList extends IrisGuiSlot {
// noop
}
@Override
protected int hitYOffset() {
return 3;
}
@Override
protected int hitRightInset() {
return 4;
}
@Override
protected int hitLeftInset() {
return -1;
}
@Override
protected int hitXShift() {
return 2;
}
@Override
protected void drawSlot(int index, int x, int y, int i1, Tessellator tessellator, int mouseX, int mouseY) {
final BaseEntry entry = this.entries.get(index);
final boolean isMouseOver = this.func_148124_c/*getSlotIndexFromScreenCoords*/(mouseX, mouseY) == index;
entry.drawEntry(screen, index, x - 2, y + 4, this.getListWidth(), this.slotHeight, tessellator, mouseX, mouseY, isMouseOver);
entry.drawEntry(screen, index, x, y - 2, this.getListWidth(), this.slotHeight, tessellator, mouseX, mouseY, isMouseOver);
}
@@ -4,13 +4,23 @@ import lombok.Getter;
import lombok.Setter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gui.element.shaderselection.BaseEntry;
import net.coderbot.iris.gui.element.shaderselection.DownloadEntry;
import net.coderbot.iris.gui.element.shaderselection.LabelEntry;
import net.coderbot.iris.gui.element.shaderselection.ShaderPackEntry;
import net.coderbot.iris.gui.element.shaderselection.TopButtonRowEntry;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -31,15 +41,70 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
private final List<BaseEntry> entries = new ArrayList<>();
private final WatchService watcher;
private final WatchKey key;
private boolean keyValid;
public ShaderPackSelectionList(ShaderPackScreen screen, Minecraft client, int width, int height, int top, int bottom, int left, int right) {
super(client, width, height, top, bottom, 20);
this.screen = screen;
this.topButtonRow = new TopButtonRowEntry(this, Iris.getIrisConfig().areShadersEnabled());
WatchService watcher1;
WatchKey key1;
try {
watcher1 = FileSystems.getDefault().newWatchService();
key1 = Iris.getShaderpacksDirectory().register(watcher1,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
keyValid = true;
} catch (IOException e) {
Iris.logger.error("Couldn't register shaderpacks directory file watcher!", e);
watcher1 = null;
key1 = null;
keyValid = false;
}
this.watcher = watcher1;
this.key = key1;
refresh();
}
// Poll for new shaderpacks
public void poll() {
if (!keyValid) {
return;
}
boolean changed = false;
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
continue;
}
changed = true;
}
if (changed) {
refresh();
}
keyValid = key.reset();
}
public void close() throws IOException {
if (key != null) {
key.cancel();
}
if (watcher != null) {
watcher.close();
}
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
poll();
super.drawScreen(mouseX, mouseY, partialTicks);
}
public void refresh() {
this.entries.clear();
@@ -68,8 +133,13 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
this.entries.add(topButtonRow);
// Download something man
if (names.isEmpty()) {
this.entries.add(new DownloadEntry(this, "Download Shaders", "https://modrinth.com/shaders"));
}
// Only allow the enable/disable shaders button if the user has added a shader pack. Otherwise, the button will be disabled.
topButtonRow.allowEnableShadersButton = names.size() > 0;
topButtonRow.allowEnableShadersButton = !names.isEmpty();
int index = 0;
@@ -78,6 +148,11 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
addPackEntry(index, name);
}
// Try not to gaslight users in case they swap back and forth from lwjgl2 and lwjgl3
final String footerKey = BackendManager.RENDER_BACKEND.supportsFileDrop()
? "pack.iris.list.label"
: "pack.iris.list.label.lwjgl2";
addLabelEntries(EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format(footerKey));
}
public void addPackEntry(int index, String name) {
@@ -128,6 +203,8 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
return true;
} else if( entry instanceof TopButtonRowEntry topButtonRowEntry) {
return topButtonRowEntry.mouseClicked(mouseX, mouseY, 0);
} else if (entry instanceof DownloadEntry downloadEntry) {
return downloadEntry.click(this.screen);
}
return false;
}
@@ -152,7 +229,7 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
protected void drawSlot(int index, int x, int y, int i1, Tessellator tessellator, int mouseX, int mouseY) {
final BaseEntry entry = this.entries.get(index);
final boolean isMouseOver = this.func_148124_c/*getSlotIndexFromScreenCoords*/(mouseX, mouseY) == index;
entry.drawEntry(screen, index, x - 2, y + 4, this.getListWidth(), tessellator, mouseX, mouseY, isMouseOver);
entry.drawEntry(screen, index, x, y + 4, this.getListWidth(), tessellator, mouseX, mouseY, isMouseOver);
}
}
@@ -14,6 +14,7 @@ public class ElementRowEntry extends BaseEntry {
private int cachedWidth;
private int cachedPosX;
private float cachedSingleWidth;
public ElementRowEntry(NavigationController navigation, List<AbstractElementWidget<?>> widgets) {
super(navigation);
@@ -32,25 +33,43 @@ public class ElementRowEntry extends BaseEntry {
// Width of a single widget
final float singleWidgetWidth = (float) totalWidthWithoutMargins / widgets.size();
this.cachedSingleWidth = singleWidgetWidth;
for (int i = 0; i < widgets.size(); i++) {
final AbstractElementWidget<?> widget = widgets.get(i);
final boolean widgetHovered = isMouseOver && (getHoveredWidget(mouseX) == i);
widget.drawScreen(x + (int) ((singleWidgetWidth + 2) * i), y, (int) singleWidgetWidth, slotHeight + 2, mouseX, mouseY, 0, widgetHovered);
widget.drawScreen(x + (int) ((singleWidgetWidth + 2) * i), y, (int) singleWidgetWidth, slotHeight - 2, mouseX, mouseY, 0, widgetHovered);
screen.setElementHoveredStatus(widget, widgetHovered);
}
}
public int getHoveredWidget(int mouseX) {
final float positionAcrossWidget = ((float) MathHelper.clamp_int(mouseX - cachedPosX, 0, cachedWidth)) / cachedWidth;
final float CONTENT_ANCHOR_OFFSET = 2f;
final float rel = (mouseX - cachedPosX) + CONTENT_ANCHOR_OFFSET;
final float clamped = Math.clamp(rel, 0f, cachedWidth);
final float positionAcrossWidget = clamped / cachedWidth;
return MathHelper.clamp_int((int) Math.floor(widgets.size() * positionAcrossWidget), 0, widgets.size() - 1);
}
private int widgetAt(int mouseX) {
final int i = getHoveredWidget(mouseX);
final int widgetX = this.cachedPosX + (int) ((this.cachedSingleWidth + 2) * i);
if (mouseX < widgetX || mouseX >= widgetX + (int) this.cachedSingleWidth) {
return -1;
}
return i;
}
@Override
public boolean mouseClicked(int mouseX, int mouseY, int button) {
return this.widgets.get(getHoveredWidget(mouseX)).mouseClicked(mouseX, mouseY, button);
final int i = widgetAt(mouseX);
if (i < 0) {
return false;
}
return this.widgets.get(i).mouseClicked(mouseX, mouseY, button);
}
@Override
@@ -7,6 +7,7 @@ import net.coderbot.iris.gui.element.IrisElementRow;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
@@ -60,13 +61,12 @@ public class HeaderEntry extends BaseEntry {
@Override
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int slotWidth, int slotHeight, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
// Draw dividing line
// fill(x - 3, (y + slotHeight) - 2, x + slotWidth, (y + slotHeight) - 1, 0x66BEBEBE);
Gui.drawRect(x - 3, (y + slotHeight) - 3, x + slotWidth, (y + slotHeight) - 2, 0x66BEBEBE);
final int tickDelta = 0;
final FontRenderer font = Minecraft.getMinecraft().fontRenderer;
// Draw header text
this.screen.drawCenteredString(font, text, x + (int) (slotWidth * 0.5), y + 5, 0xFFFFFF);
GuiUtil.drawScrollingText(font, text, x + (int) (slotWidth * 0.5), x + 5,
((x + slotWidth) - 10) - this.utilityButtons.getWidth(), y + 5, y + 15, 0xFFFFFF);
GuiUtil.bindIrisWidgetsTexture();
@@ -0,0 +1,33 @@
package net.coderbot.iris.gui.element.shaderselection;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.element.ShaderPackSelectionList;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.minecraft.client.renderer.Tessellator;
/**
* Show a download button when no shaderpacks exist in the shaderpacks folder.
*/
public class DownloadEntry extends BaseEntry {
private final String label;
private final String url;
public DownloadEntry(ShaderPackSelectionList list, String label, String url) {
super(list);
this.label = label;
this.url = url;
}
public boolean click(ShaderPackScreen screen) {
GuiUtil.playButtonClickSound();
screen.openLinkConfirm(url);
return true;
}
@Override
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int listWidth, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
GuiUtil.bindIrisWidgetsTexture();
GuiUtil.drawButton(x - 2, y - 5, listWidth, 18, isMouseOver, false);
screen.drawCenteredString(label, (x + listWidth / 2) - 2, y, 0xFFFFFF);
}
}
@@ -14,6 +14,6 @@ public class LabelEntry extends BaseEntry {
@Override
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int listWidth, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
screen.drawCenteredString(label, x, y, 0xFFFFFF);
screen.drawCenteredString(label, (x + listWidth / 2) - 2, y, 0xFFFFFF);
}
}
@@ -1,6 +1,7 @@
package net.coderbot.iris.gui.element.shaderselection;
import lombok.Getter;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.element.ShaderPackSelectionList;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.minecraft.client.gui.FontRenderer;
@@ -37,6 +38,8 @@ public class ShaderPackEntry extends BaseEntry {
}
if(isMouseOver) {
GuiUtil.bindIrisWidgetsTexture();
GuiUtil.drawButton(x - 2, y - 6, listWidth, 20, true, false);
name = EnumChatFormatting.BOLD + name;
}
@@ -13,12 +13,10 @@ public class TopButtonRowEntry extends BaseEntry {
private static final String NONE_PRESENT_LABEL = I18n.format("options.iris.shaders.nonePresent"); //.withStyle(ChatFormatting.GRAY);
private static final String SHADERS_DISABLED_LABEL = I18n.format("options.iris.shaders.disabled");
private static final String SHADERS_ENABLED_LABEL = I18n.format("options.iris.shaders.enabled");
private static final int REFRESH_BUTTON_WIDTH = 18;
private final ShaderPackSelectionList shaderPackSelectionList;
private final IrisElementRow buttons = new IrisElementRow();
private final EnableShadersButtonElement enableDisableButton;
private final IrisElementRow.Element refreshPacksButton;
public boolean allowEnableShadersButton = true;
public boolean shadersEnabled;
@@ -38,15 +36,7 @@ public class TopButtonRowEntry extends BaseEntry {
return false;
});
this.refreshPacksButton = new IrisElementRow.IconButtonElement(
GuiUtil.Icon.REFRESH,
button -> {
this.shaderPackSelectionList.refresh();
GuiUtil.playButtonClickSound();
return true;
});
this.buttons.add(this.enableDisableButton, 0).add(this.refreshPacksButton, REFRESH_BUTTON_WIDTH);
this.buttons.add(this.enableDisableButton, 0);
}
public void setShadersEnabled(boolean shadersEnabled) {
@@ -57,7 +47,7 @@ public class TopButtonRowEntry extends BaseEntry {
@Override
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int listWidth, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
this.buttons.setWidth(this.enableDisableButton, (listWidth - 1) - REFRESH_BUTTON_WIDTH);
this.buttons.setWidth(this.enableDisableButton, listWidth);
this.enableDisableButton.centerX = x + (int)(listWidth * 0.5);
this.buttons.drawScreen(x - 2, y - 3, 18, mouseX, mouseY, 0, isMouseOver);
@@ -90,9 +90,9 @@ public abstract class BaseOptionElementWidget<T extends OptionMenuElement> exten
FontRenderer font = Minecraft.getMinecraft().fontRenderer;
// Draw the label
font.drawStringWithShadow(this.trimmedLabel, x + 6, y + 7, 0xFFFFFF);
font.drawStringWithShadow(this.trimmedLabel, x + 6, y + 7, this.isValueModified() ? 0xFFC94A : 0xFFFFFF);
// Draw the value label
font.drawStringWithShadow(this.valueLabel, (x + (width - 2)) - (int)(this.valueSectionWidth * 0.5) - (int)(font.getStringWidth(this.valueLabel) * 0.5), y + 7, 0xFFFFFF);
font.drawStringWithShadow(this.valueLabel, (x + (width - 2)) - (int)(this.valueSectionWidth * 0.5) - (int)(font.getStringWidth(this.valueLabel) * 0.5), y + 7, getValueColor());
}
protected final void renderOptionWithValue(int x, int y, int width, int height, boolean hovered) {
@@ -119,17 +119,15 @@ public abstract class BaseOptionElementWidget<T extends OptionMenuElement> exten
}
protected final String createTrimmedLabel() {
String label = GuiUtil.shortenText(Minecraft.getMinecraft().fontRenderer, this.label, this.maxLabelWidth);
if (this.isValueModified()) {
label = label + " (*)"; //.withStyle(style -> style.withColor(TextColor.fromRgb(0xffc94a)));
}
return label;
return GuiUtil.shortenText(Minecraft.getMinecraft().fontRenderer, this.label, this.maxLabelWidth);
}
protected abstract String createValueLabel();
protected int getValueColor() {
return 0xFFFFFF;
}
public abstract boolean applyNextValue();
public abstract boolean applyPreviousValue();
@@ -147,7 +145,13 @@ public abstract class BaseOptionElementWidget<T extends OptionMenuElement> exten
@Override
public Optional<String> getCommentBody() {
return Optional.ofNullable(getCommentKey()).map(I18n::format);
final String key = getCommentKey();
if (key == null) {
return Optional.empty();
}
final String translated = GuiUtil.translateLenient(key);
// Don't show comment boxes without a translation, otherwise you get something like option.{commentname}.comment
return translated.equals(key) ? Optional.empty() : Optional.of(translated);
}
@Override
@@ -70,6 +70,14 @@ public class BooleanElementWidget extends BaseOptionElementWidget<OptionMenuBool
return this.value ? TEXT_TRUE : TEXT_FALSE;
}
@Override
protected int getValueColor() {
if (this.value == this.defaultValue) {
return 0xFFFFFF;
}
return this.value ? 0x55FF55 : 0xFF5555;
}
@Override
public String getCommentKey() {
return "option." + this.option.getName() + ".comment";
@@ -6,13 +6,10 @@ import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.coderbot.iris.shaderpack.option.menu.OptionMenuLinkElement;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.resources.I18n;
import java.util.Optional;
public class LinkElementWidget extends CommentedElementWidget<OptionMenuLinkElement> {
private static final String ARROW = new String(">");
private final String targetScreenId;
private final String label;
@@ -39,7 +36,7 @@ public class LinkElementWidget extends CommentedElementWidget<OptionMenuLinkElem
final FontRenderer font = Minecraft.getMinecraft().fontRenderer;
final int maxLabelWidth = width - 9;
final int maxLabelWidth = width - 6;
if (font.getStringWidth(this.label) > maxLabelWidth) {
this.isLabelTrimmed = true;
@@ -51,8 +48,7 @@ public class LinkElementWidget extends CommentedElementWidget<OptionMenuLinkElem
int labelWidth = font.getStringWidth(this.trimmedLabel);
font.drawStringWithShadow(this.trimmedLabel, x + (int)(width * 0.5) - (int)(labelWidth * 0.5) - (int)(0.5 * Math.max(labelWidth - (width - 18), 0)), y + 7, 0xFFFFFF);
font.drawString(ARROW, (x + width) - 9, y + 7, 0xFFFFFF);
font.drawStringWithShadow(this.trimmedLabel, x + (int)(width * 0.5) - (int)(labelWidth * 0.5), y + 7, 0xFFFFFF);
if (hovered && this.isLabelTrimmed) {
// To prevent other elements from being drawn on top of the tooltip
@@ -79,7 +75,7 @@ public class LinkElementWidget extends CommentedElementWidget<OptionMenuLinkElem
@Override
public Optional<String> getCommentBody() {
final String translation = "screen." + this.targetScreenId + ".comment";
final String translated = I18n.format(translation);
return Optional.ofNullable(!translated.equals(translation) ? I18n.format(translation) : null);
final String translated = GuiUtil.translateLenient(translation);
return Optional.ofNullable(!translated.equals(translation) ? translated : null);
}
}
@@ -1,6 +1,7 @@
package net.coderbot.iris.gui.element.widget;
import net.coderbot.iris.Iris;
import net.minecraft.util.EnumChatFormatting;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.NavigationController;
import net.coderbot.iris.gui.element.ShaderPackOptionList;
@@ -64,7 +65,7 @@ public final class OptionMenuConstructor {
static {
registerScreen(OptionMenuMainElementScreen.class, screen ->
new ElementWidgetScreenData(Iris.getCurrentPackName() + (Iris.isFallback() ? " (fallback)" : ""), false));
new ElementWidgetScreenData(EnumChatFormatting.BOLD + Iris.getCurrentPackName() + (Iris.isFallback() ? " (fallback)" : ""), false));
registerScreen(OptionMenuSubElementScreen.class, screen ->
new ElementWidgetScreenData(GuiUtil.translateOrDefault(new String(screen.screenId), "screen." + screen.screenId), true));
@@ -59,6 +59,11 @@ public class ProfileElementWidget extends BaseOptionElementWidget<OptionMenuProf
return this.profileLabel;
}
@Override
protected int getValueColor() {
return PROFILE_CUSTOM.equals(this.profileLabel) ? 0xFFFF55 : 0xFFFFFF;
}
@Override
public Optional<String> getCommentTitle() {
return Optional.of(PROFILE_LABEL);
@@ -108,7 +108,7 @@ public class SliderElementWidget extends StringElementWidget {
@Override
public boolean mouseReleased(double mx, double my, int button) {
if (button == 0) {
if (button == 0 && this.mouseDown) {
this.onReleased();
return true;
@@ -60,6 +60,11 @@ public class StringElementWidget extends BaseOptionElementWidget<OptionMenuStrin
return GuiUtil.translateOrDefault(getValue(), "value." + this.option.getName() + "." + getValue());
}
@Override
protected int getValueColor() {
return 0x6688FF;
}
@Override
public String getCommentKey() {
return "option." + this.option.getName() + ".comment";
@@ -2,6 +2,7 @@ package net.coderbot.iris.gui.screen;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.NavigationController;
@@ -16,8 +17,12 @@ import net.coderbot.iris.shaderpack.ShaderPack;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiConfirmOpenLink;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiYesNo;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
@@ -25,12 +30,19 @@ import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@@ -41,8 +53,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
*/
public static final Set<Runnable> TOP_LAYER_RENDER_QUEUE = new HashSet<>();
private static final String SELECT_TITLE = I18n.format("pack.iris.select.title");
private static final String CONFIGURE_TITLE = I18n.format("pack.iris.configure.title");
private static final String SELECT_TITLE = EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format("pack.iris.select.title");
private static final String CONFIGURE_TITLE = EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format("pack.iris.configure.title");
private static final int COMMENT_PANEL_WIDTH = 314;
private final GuiScreen parent;
@@ -72,7 +84,6 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
private boolean guiHidden = false;
private boolean dirty = false;
private float guiButtonHoverTimer = 0.0f;
public ShaderPackScreen(GuiScreen parent) {
this.title = I18n.format("options.iris.shaderPackSelection.title");
@@ -102,6 +113,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
this.initGui();
}
handleDroppedFiles();
if (this.mc.theWorld == null) {
super.drawDefaultBackground();
} else if (!this.guiHidden) {
@@ -122,11 +135,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
hoveredElementCommentTimer = 0;
}
final float previousHoverTimer = this.guiButtonHoverTimer;
super.drawScreen(mouseX, mouseY, delta);
if (previousHoverTimer == this.guiButtonHoverTimer) {
this.guiButtonHoverTimer = 0.0f;
}
if (!this.guiHidden) {
drawCenteredString(this.fontRendererObj, this.title, (int) (this.width * 0.5), 8, 0xFFFFFF);
@@ -143,12 +152,9 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
// Draw the comment panel
if (this.isDisplayingComment()) {
// Determine panel height and position
final int panelHeight = Math.max(50, 18 + (this.hoveredElementCommentBody.size() * 10));
int x = mouseX + 5;
if (x + 314 >= (this.width - 4)) x = this.width - (318);
int y = mouseY + 8;
if (y + panelHeight >= (this.height - 4)) y = this.height - (panelHeight + 4);
final int x = (int) (0.5 * this.width) - 157;
final int y = this.height - (panelHeight + 4);
// Draw panel
GuiUtil.drawPanel(x, y, COMMENT_PANEL_WIDTH, panelHeight);
// Draw text
@@ -181,10 +187,21 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
@Override
public void initGui() {
super.initGui();
BackendManager.RENDER_BACKEND.startFileDrop();
final int bottomCenter = this.width / 2 - 50;
final int topCenter = this.width / 2 - 76;
final boolean inWorld = this.mc.theWorld != null;
if (this.shaderPackList != null) {
try {
this.shaderPackList.close();
} catch (IOException e) {
Iris.logger.error("Failed to close previous shaderpack selection watcher!", e);
}
}
this.shaderPackList = new ShaderPackSelectionList(this, this.mc, this.width, this.height, 32, this.height - 58, 0, this.width);
if (Iris.getCurrentPack().isPresent() && this.navigation != null) {
@@ -339,12 +356,30 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
public void refreshScreenSwitchButton() {
if (this.screenSwitchButton != null) {
this.screenSwitchButton.displayString = optionMenuOpen ? I18n.format("options.iris.shaderPackList") : I18n.format("options.iris.shaderPackSettings");
this.screenSwitchButton.enabled = optionMenuOpen || shaderPackList.getTopButtonRow().shadersEnabled;
// Disable the settings switch when shaders are off or the selected pack exposes no options
this.screenSwitchButton.enabled = optionMenuOpen || (shaderPackList.getTopButtonRow().shadersEnabled
&& Iris.getCurrentPack().map(p -> !p.getMenuContainer().mainScreen.elements.isEmpty()).orElse(true));
}
}
@Override
protected void keyTyped(char typedChar, int keyCode) {
if (GuiScreen.isCtrlKeyDown() && keyCode == Keyboard.KEY_D) {
this.mc.displayGuiScreen(new GuiYesNo((result, id) -> {
Iris.setDebug(result);
this.mc.displayGuiScreen(this);
}, "Shader debug mode toggle",
"Debug mode helps investigate problems and shows shader errors. Would you like to enable it?", 0));
return;
}
if (GuiScreen.isCtrlKeyDown() && keyCode == Keyboard.KEY_G) {
this.mc.displayGuiScreen(new GuiYesNo((result, id) -> {
Iris.setAllowUnknownShaders(result);
this.mc.displayGuiScreen(this);
}, "Unknown shader toggle",
"This allows unknown shaders to load in.", 0));
return;
}
if (keyCode == Keyboard.KEY_ESCAPE) {
if (this.guiHidden) {
this.guiHidden = false;
@@ -359,6 +394,21 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
return;
}
}
if (keyCode == Keyboard.KEY_F1 && this.mc.theWorld != null) {
this.guiHidden = !this.guiHidden;
this.initGui();
return;
}
if (keyCode == Keyboard.KEY_TAB) {
final boolean canOpenOptions = shaderPackList.getTopButtonRow().shadersEnabled
&& Iris.getCurrentPack().map(p -> !p.getMenuContainer().mainScreen.elements.isEmpty()).orElse(false);
if (this.optionMenuOpen || canOpenOptions) {
this.optionMenuOpen = !this.optionMenuOpen;
this.applyChanges();
this.initGui();
}
return;
}
super.keyTyped(typedChar, keyCode);
}
@@ -367,6 +417,84 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
this.notificationDialogTimer = 100;
}
private void handleDroppedFiles() {
final List<String> dropped = BackendManager.RENDER_BACKEND.pollDroppedFiles();
if (dropped.isEmpty()) {
return;
}
final List<Path> paths = dropped.stream().map(Paths::get).collect(Collectors.toList());
if (this.optionMenuOpen) {
onOptionMenuFilesDrop(paths);
} else {
onPackListFilesDrop(paths);
}
}
public void onPackListFilesDrop(List<Path> paths) {
final List<Path> packs = paths.stream().filter(Iris::isValidShaderpack).toList();
for (Path pack : packs) {
final String fileName = pack.getFileName().toString();
try {
Iris.getShaderpacksDirectoryManager().copyPackIntoDirectory(fileName, pack);
} catch (FileAlreadyExistsException e) {
displayNotification(I18n.format("options.iris.shaderPackSelection.copyErrorAlreadyExists", fileName));
this.shaderPackList.refresh();
return;
} catch (IOException e) {
Iris.logger.warn("Error copying dragged shader pack", e);
displayNotification(I18n.format("options.iris.shaderPackSelection.copyError", fileName));
this.shaderPackList.refresh();
return;
}
}
// After copying, refresh the list so the new packs show up
this.shaderPackList.refresh();
if (packs.isEmpty()) {
if (paths.size() == 1) {
displayNotification(I18n.format("options.iris.shaderPackSelection.failedAddSingle", paths.getFirst().getFileName().toString()));
} else {
displayNotification(I18n.format("options.iris.shaderPackSelection.failedAdd"));
}
} else if (packs.size() == 1) {
final String packName = packs.getFirst().getFileName().toString();
displayNotification(I18n.format("options.iris.shaderPackSelection.addedPack", packName));
// Select the freshly-added pack, since the user probably wants to use it
this.shaderPackList.select(packName);
} else {
displayNotification(I18n.format("options.iris.shaderPackSelection.addedPacks", packs.size()));
}
}
public void onOptionMenuFilesDrop(List<Path> paths) {
// Only one settings file should be imported at a time
if (paths.size() != 1) {
displayNotification(I18n.format("options.iris.shaderPackOptions.tooManyFiles"));
return;
}
importPackOptions(paths.getFirst());
}
public void importPackOptions(Path settingFile) {
try (InputStream in = Files.newInputStream(settingFile)) {
final Properties properties = new Properties();
properties.load(in);
Iris.queueShaderPackOptionsFromProperties(properties);
displayNotification(I18n.format("options.iris.shaderPackOptions.importedSettings", settingFile.getFileName().toString()));
if (this.navigation != null) {
this.navigation.refresh();
}
} catch (Exception e) {
Iris.logger.error("Error importing shader settings file \"" + settingFile + "\"", e);
displayNotification(I18n.format("options.iris.shaderPackOptions.failedImport", settingFile.getFileName().toString()));
}
}
public void onClose() {
if (!dropChanges) {
applyChanges();
@@ -377,6 +505,19 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
this.mc.displayGuiScreen(parent);
}
@Override
public void onGuiClosed() {
BackendManager.RENDER_BACKEND.stopFileDrop();
if (this.shaderPackList != null) {
try {
this.shaderPackList.close();
} catch (IOException e) {
Iris.logger.error("Failed to safely close shaderpack selection!", e);
}
}
}
private void dropChangesAndClose() {
dropChanges = true;
onClose();
@@ -419,8 +560,29 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
CompletableFuture.runAsync(() -> openUri(Iris.getShaderpacksDirectoryManager().getDirectoryUri()));
}
public void openLinkConfirm(String url) {
this.mc.displayGuiScreen(new GuiConfirmOpenLink((result, id) -> {
if (result) {
try {
openUri(new URI(url));
} catch (URISyntaxException e) {
Iris.logger.error("Invalid shader download URL: " + url, e);
}
}
this.mc.displayGuiScreen(this);
}, url, 0, true) {
@Override
public void initGui() {
super.initGui();
for (int i = 0; i < this.buttonList.size(); i++) {
this.buttonList.get(i).xPosition = this.width / 2 - 155 + i * 105;
}
}
});
}
private void openUri(URI uri) {
switch (net.minecraft.util.Util.getOSType()) {
switch (Util.getOSType()) {
case OSX -> {
try {
Runtime.getRuntime().exec(new String[] { "/usr/bin/open", uri.toString() });
@@ -452,7 +614,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
try {
final Class<?> aClass = Class.forName("java.awt.Desktop");
final Object getDesktop = aClass.getMethod("getDesktop").invoke((Object) null);
final Object getDesktop = aClass.getMethod("getDesktop").invoke(null);
aClass.getMethod("browse", URI.class).invoke(getDesktop, uri);
} catch (Exception e) {
e.printStackTrace();
@@ -475,17 +637,17 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
this.hoveredElementCommentTitle = ((CommentedElementWidget<?>) widget).getCommentTitle();
Optional<String> commentBody = ((CommentedElementWidget<?>) widget).getCommentBody();
if (!commentBody.isPresent()) {
if (commentBody.isEmpty()) {
this.hoveredElementCommentBody.clear();
} else {
String rawCommentBody = commentBody.get();
// Strip any trailing "."s
// Strip any trailing periods
if (rawCommentBody.endsWith(".")) {
rawCommentBody = rawCommentBody.substring(0, rawCommentBody.length() - 1);
}
// Split comment body into lines by separator ". "
List<String> splitByPeriods = Arrays.stream(rawCommentBody.split("\\. [ ]*")).map(String::new).collect(Collectors.toList());
List<String> splitByPeriods = Arrays.stream(rawCommentBody.split("\\. +")).toList();
// Line wrap
this.hoveredElementCommentBody = new ArrayList<>();
for (String text : splitByPeriods) {
@@ -1597,6 +1597,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
this.customUniforms.update();
// Make sure SSBO is bound
if (ssboHolder != null) {
ssboHolder.setupBuffers();
}
// Get ready for world rendering
prepareRenderTargets();
@@ -1,7 +1,7 @@
package net.coderbot.iris.rendertarget;
import com.gtnewhorizons.angelica.compat.mojang.NativeImage;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.lwjgl.opengl.GL11;
@@ -10,6 +10,11 @@ import java.util.Random;
public class NativeImageBackedNoiseTexture extends DynamicTexture {
public NativeImageBackedNoiseTexture(int size) {
super(create(size));
RenderSystem.texParameteri(getGlTextureId(), GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
RenderSystem.texParameteri(getGlTextureId(), GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
RenderSystem.texParameteri(getGlTextureId(), GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
RenderSystem.texParameteri(getGlTextureId(), GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
}
private static NativeImage create(int size) {
@@ -27,11 +32,4 @@ public class NativeImageBackedNoiseTexture extends DynamicTexture {
return image;
}
@Override
public void updateDynamicTexture() {
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);
super.updateDynamicTexture();
}
}
@@ -0,0 +1,21 @@
package net.coderbot.iris.shaderpack;
import java.util.HashMap;
import java.util.Map;
public class DimensionFlatteningMap {
private static final Map<String, String> MODERN_TO_LEGACY = new HashMap<>();
static {
MODERN_TO_LEGACY.put("minecraft:overworld", "Overworld");
MODERN_TO_LEGACY.put("minecraft:the_nether", "Nether");
MODERN_TO_LEGACY.put("minecraft:the_end", "The End");
}
/**
* Maps a modern dimension ID to its 1.7.10 string name.
*/
public static String toLegacyName(String dimensionName) {
return MODERN_TO_LEGACY.getOrDefault(dimensionName, dimensionName);
}
}
@@ -10,6 +10,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -33,7 +34,7 @@ public class LanguageMap {
stream.filter(path -> !Files.isDirectory(path)).forEach(path -> {
// Also note that OptiFine uses a property scheme for loading language entries to keep parity with other
// OptiFine features
final String currentFileName = path.getFileName().toString();
final String currentFileName = path.getFileName().toString().toLowerCase(Locale.ROOT);
if (!currentFileName.endsWith(".lang")) {
// This file lacks a .lang file extension and should be ignored.
@@ -23,14 +23,12 @@ import net.coderbot.iris.shaderpack.option.ShaderPackOptions;
import net.coderbot.iris.shaderpack.option.menu.OptionMenuContainer;
import net.coderbot.iris.shaderpack.option.values.MutableOptionValues;
import net.coderbot.iris.shaderpack.option.values.OptionValues;
import net.coderbot.iris.shaderpack.parsing.BooleanParser;
import net.coderbot.iris.shaderpack.preprocessor.JcppProcessor;
import net.coderbot.iris.shaderpack.preprocessor.PropertiesPreprocessor;
import net.coderbot.iris.shaderpack.texture.CustomTextureData;
import net.coderbot.iris.shaderpack.texture.TextureFilteringData;
import net.coderbot.iris.shaderpack.texture.TextureStage;
import net.coderbot.iris.uniforms.custom.CustomUniforms;
import net.irisshaders.iris.api.v0.IrisApi;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
@@ -242,12 +240,8 @@ public class ShaderPack {
List<String> invalidFeatureFlags = invalidFlagList.stream().map(FeatureFlags::getHumanReadableName).collect(Collectors.toList());
if (!invalidFeatureFlags.isEmpty()) {
// TODO: GUI
// if (Minecraft.getMinecraft().screen instanceof ShaderPackScreen) {
// Minecraft.getMinecraft().setScreen(new FeatureMissingErrorScreen(Minecraft.getMinecraft().screen, I18n.format("iris.unsupported.pack"), I18n.format("iris.unsupported.pack.description", FeatureFlags.getInvalidStatus(invalidFlagList), invalidFeatureFlags.stream()
// .collect(Collectors.joining(", ", ": ", ".")))));
// }
IrisApi.getInstance().getConfig().setShadersEnabledAndApply(false);
// TODO: GUI?
Iris.logger.warn("Shader pack requires unsupported feature flags, loading anyway: {}", String.join(", ", invalidFeatureFlags));
}
ProfileSet profiles = ProfileSet.fromTree(shaderProperties.getProfiles(), this.shaderPackOptions.getOptionSet());
@@ -258,9 +252,7 @@ public class ShaderPack {
this.profile.current.ifPresent(profile -> disabledPrograms.addAll(profile.disabledPrograms));
// Add programs that are disabled by shader options
shaderProperties.getConditionallyEnabledPrograms().forEach((program, shaderOption) -> {
if ("true".equals(shaderOption)) return;
if ("false".equals(shaderOption) || !this.shaderPackOptions.getOptionValues().getBooleanValueOrDefault(shaderOption)) {
if (!BooleanParser.parse(shaderOption, this.shaderPackOptions.getOptionValues())) {
disabledPrograms.add(program);
}
});
@@ -438,7 +430,8 @@ public class ShaderPack {
folderNames.add(folderName);
for (String dimensionName : dimensionNames) {
dimensionMap.put(dimensionName, folderName);
// Convert modern dimension IDs to 1.7.10 names
dimensionMap.put(DimensionFlatteningMap.toLegacyName(dimensionName), folderName);
}
});
@@ -668,6 +661,7 @@ public class ShaderPack {
*/
public ProgramSet getProgramSet(String dimensionName) {
int dimensionId = Iris.getCurrentDimensionId();
dimensionName = DimensionFlatteningMap.toLegacyName(dimensionName);
// First, try to find an exact match in the dimension map
String folderName = dimensionMap.get(dimensionName);
@@ -383,8 +383,11 @@ public class FlatteningMap {
doublePlant("peony", 5);
// === Water / Lava (IDs 8-11) ===
// In 1.13+, water/lava are single blocks. In 1.7.10, flowing variants are separate.
// Map modern names to both still and flowing for full coverage.
// In 1.13+, water/lava are single blocks. In 1.7.10, flowing variants are separate.
// Map modern names to both still and flowing for full coverage. See IrisExtendedChunkVertexEncoder.java
// for more info on this.
liquidLevels("water", "water", "flowing_water");
liquidLevels("lava", "lava", "flowing_lava");
multi("water",
entry("water"),
entry("flowing_water"));
@@ -972,6 +975,13 @@ public class FlatteningMap {
STATE_MAPPINGS.put(modern + "|" + property + "=" + value, List.of(entries));
}
/** Maps each modern liquid level state to that exact metadata on both the static and flowing IDs */
private static void liquidLevels(String modern, String legacyStatic, String legacyDynamic) {
for (int level = 0; level <= 15; level++) {
state(modern, "level", String.valueOf(level), entryMetas(legacyStatic, level), entryMetas(legacyDynamic, level));
}
}
/** Creates a BlockEntry with no specific metas. */
private static BlockEntry entry(String legacyName) {
return new BlockEntry(new NamespacedId("minecraft", legacyName), Collections.emptySet());
@@ -11,6 +11,9 @@ public interface OptionValues {
Optional<String> getStringValue(String name);
default boolean getBooleanValueOrDefault(String name) {
if ("0".equals(name)) return false;
if ("1".equals(name)) return true;
return getBooleanValue(name).orElseGet(() -> {
if (!getOptionSet().getBooleanOptions().containsKey(name)) {
Iris.logger.warn("Tried to get boolean value for unknown option: " + name + ", defaulting to true!");
@@ -0,0 +1,124 @@
package net.coderbot.iris.shaderpack.parsing;
import net.coderbot.iris.Iris;
import net.coderbot.iris.shaderpack.option.values.OptionValues;
import java.util.EmptyStackException;
import java.util.Stack;
public class BooleanParser {
public static boolean parse(String expression, OptionValues valueLookup) {
try {
StringBuilder option = new StringBuilder();
Stack<Operation> operationStack = new Stack<>();
Stack<Boolean> valueStack = new Stack<>();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
switch (c) {
case '!' -> operationStack.push(Operation.NOT);
case '&' -> {
if (!option.isEmpty()) {
valueStack.push(processValue(option.toString(), valueLookup, operationStack));
option = new StringBuilder();
}
if (operationStack.isEmpty() || !operationStack.peek().equals(Operation.AND)) {
operationStack.push(Operation.OPEN);
}
i++;
operationStack.push(Operation.AND);
}
case '|' -> {
if (!option.isEmpty()) {
valueStack.push(processValue(option.toString(), valueLookup, operationStack));
option = new StringBuilder();
}
if (!operationStack.isEmpty() && operationStack.peek().equals(Operation.AND)) {
evaluate(operationStack, valueStack, true);
}
i++;
operationStack.push(Operation.OR);
}
case '(' -> operationStack.push(Operation.OPEN);
case ')' -> {
if (!option.isEmpty()) {
valueStack.push(processValue(option.toString(), valueLookup, operationStack));
option = new StringBuilder();
}
if (!operationStack.isEmpty() && operationStack.peek().equals(Operation.AND)) {
evaluate(operationStack, valueStack, true);
}
evaluate(operationStack, valueStack, true);
}
case ' ' -> {}
default -> option.append(c);
}
}
if (!option.isEmpty()) {
valueStack.push(processValue(option.toString(), valueLookup, operationStack));
}
evaluate(operationStack, valueStack, false);
boolean result = valueStack.pop();
if (!valueStack.isEmpty() || !operationStack.isEmpty()) {
Iris.logger.warn("Failed to parse boolean expression, stack isn't empty, defaulting to true: '{}'", expression);
return true;
}
return result;
} catch (EmptyStackException e) {
Iris.logger.warn("Failed to parse boolean expression, stack is empty unexpectedly, defaulting to true: '{}'", expression);
return true;
}
}
private static boolean processValue(String value, OptionValues valueLookup, Stack<Operation> operationStack) {
boolean booleanValue = switch (value) {
case "true", "1" -> true;
case "false", "0" -> false;
default -> valueLookup != null && valueLookup.getBooleanValueOrDefault(value);
};
if (!operationStack.isEmpty() && operationStack.peek() == Operation.NOT) {
operationStack.pop();
return !booleanValue;
}
return booleanValue;
}
private static void evaluate(Stack<Operation> operationStack, Stack<Boolean> valueStack, boolean currentBracket) {
boolean value = valueStack.pop();
while (!operationStack.isEmpty() && (!currentBracket || operationStack.peek() != Operation.OPEN)) {
value = operationStack.pop().compute(value, valueStack);
}
if (!operationStack.isEmpty() && operationStack.peek() == Operation.OPEN) {
operationStack.pop();
if (!operationStack.isEmpty() && operationStack.peek() == Operation.NOT) {
value = operationStack.pop().compute(value, valueStack);
}
}
valueStack.push(value);
}
private enum Operation {
AND {
@Override
boolean compute(boolean value, Stack<Boolean> valueStack) {
return valueStack.pop() && value;
}
},
OR {
@Override
boolean compute(boolean value, Stack<Boolean> valueStack) {
return valueStack.pop() || value;
}
},
NOT {
@Override
boolean compute(boolean value, Stack<Boolean> valueStack) {
return !value;
}
},
OPEN;
boolean compute(boolean value, Stack<Boolean> valueStack) {
return value;
}
}
}
@@ -116,6 +116,7 @@ public final class CommonUniforms {
final SmoothedVec2f eyeBrightnessSmooth = new SmoothedVec2f(directives.getEyeBrightnessHalfLife(), directives.getEyeBrightnessHalfLife(), CommonUniforms::getEyeBrightness, updateNotifier);
uniforms
.uniform1f(ONCE, "pi", () -> (float) Math.PI)
.uniform1f(ONCE, "darknessFactor", () -> 0.0F) // This is PER_FRAME in modern, it is an effect added by The Warden. We're just setting to 0 because 1.7.10 doesn't have it.
.uniform1f(ONCE, "darknessLightFactor", () -> 0.0F) // Warden darkness current light factor - 1.7.10 doesn't have it so hardcode to 0
.uniform1b(PER_FRAME, "hideGUI", () -> client.gameSettings.hideGUI)
@@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntFunction;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.block_rendering.NbtConditionalIdMap;
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
@@ -96,7 +97,7 @@ public class ItemMaterialHelper {
if (blockMetaMatches != null) {
Int2IntMap metaMap = blockMetaMatches.get(block);
if (metaMap != null) {
int id = metaMap.get(itemBlock.getMetadata(metadata));
int id = BlockMaterialMapping.resolveId(metaMap, itemBlock.getMetadata(metadata));
if (id != -1) {
return id;
}
@@ -72,6 +72,11 @@ public class VanillaBiomeList {
biomes.add(new BiomeEntry(BiomeGenBase.sky, "END_HIGHLANDS"));
biomes.add(new BiomeEntry(BiomeGenBase.sky, "END_BARRENS"));
// Modern biome variants (1.20+)
biomes.add(new BiomeEntry(BiomeGenBase.jungle, "BIOME_SPARSE_JUNGLE"));
biomes.add(new BiomeEntry(BiomeGenBase.mushroomIsland, "BIOME_MUSHROOM_FIELDS"));
biomes.add(new BiomeEntry(BiomeGenBase.swampland, "BIOME_MANGROVE_SWAMP"));
return Collections.unmodifiableList(biomes);
}
@@ -214,6 +214,8 @@ options.angelica.threadedChunkBuilding.tooltip=If enabled, chunk meshing will be
pack.iris.select.title=Select
pack.iris.configure.title=Configure
pack.iris.list.label=+ Drag and Drop Shader Packs to add
pack.iris.list.label.lwjgl2=Install lwjgl3ify to drag and drop Shader Packs
label.iris.true=On
label.iris.false=Off
@@ -0,0 +1,257 @@
options.button.shader=シェーダー...
options.chunks=%s チャンク
options.entityShadows=エンティティの影
options.droppedItems=%s アイテム
options.droppedItems.max=無制限
sodium.option_impact.low=低
sodium.option_impact.medium=中
sodium.option_impact.high=高
sodium.option_impact.extreme=極高
sodium.option_impact.varies=場合による
sodium.options.pages.quality=品質
sodium.options.pages.performance=パフォーマンス
sodium.options.pages.advanced=詳細
sodium.options.pages.appearance=外観
sodium.options.pages.text=文字エフェクト
sodium.options.view_distance.tooltip=地形をどれだけ遠くまで描画するかを設定します。描画距離が短いほど描画される地形が少なくなり、FPSが向上します。
sodium.options.simulation_distance.tooltip=エンティティが読み込まれ、処理される距離を設定します。演算距離を短くすることで内部サーバーの負荷が減り、FPSが向上します。
sodium.options.brightness.tooltip=ゲーム画面の明るさ(ガンマ)を設定します。
sodium.options.clouds.name=雲
sodium.options.clouds.tooltip=雲をどのように表示するか、無効にするかを設定します。
sodium.options.gui_scale.tooltip=GUIの大きさを設定します。「自動」では最大倍率が適用されます。
sodium.options.fullscreen.tooltip=有効にすると、ゲーム画面は(対応している場合)フルスクリーンで表示されます。
sodium.options.v_sync.tooltip=有効にすると、ゲームのFPSがモニターのリフレッシュレートに同期され、入力遅延が増加する代わりに、より滑らかになります。処理速度が遅いと、パフォーマンスが低下する可能性があります。
sodium.options.fps_limit.tooltip=最大FPSを制限し、マルチタスク時の負荷を軽減できます。垂直同期が有効になっている場合、この設定はモニターのリフレッシュレートよりも低い値でない限り無視されます。
sodium.options.view_bobbing.tooltip=有効にすると、移動時に視点が揺れます。画面酔いの症状が出る場合は、無効にすると症状が軽減される可能性があります。
sodium.options.graphics_quality.tooltip=一部の古い形式の設定を制御します。これはMODとの互換性のために必要です。以降の設定を「デフォルト」にすると、この設定が適用されます。
sodium.options.clouds_quality.tooltip=雲の品質を設定します。
sodium.options.weather_quality.tooltip=雨や雪のエフェクトの品質を設定します。
sodium.options.leaves_quality.name=木の葉の品質
sodium.options.leaves_quality.tooltip=木の葉の品質を設定します。
sodium.options.grass_quality.name=草ブロックの品質
sodium.options.grass_quality.tooltip=草ブロックの側面の品質を設定します。
sodium.options.celeritas_smooth_lighting.name=Celeritasスムースライティング
sodium.options.celeritas_smooth_lighting.tooltip=有効にすると、Celeritasの照明パイプラインがスムースライティングに使用されます。スムースライティングが有効である必要があります。
sodium.options.particle_quality.name=パーティクルの品質
sodium.options.particle_quality.tooltip=画面上に同時に存在できるパーティクルの最大数を設定します。
sodium.options.smooth_lighting.tooltip=ブロックの照明と陰影を滑らかにします。チャンクロードが少し遅くなりますが、FPSには影響しません。
sodium.options.biome_blend.value=%s ブロック
sodium.options.biome_blend.tooltip=ブロックの色情報を収集するバイオームの範囲を設定します。値を大きくすると、チャンクロードが大幅に遅くなる上に、あまり品質が向上しなくなります。
sodium.options.entity_shadows.tooltip=有効にすると、エンティティの下に影が表示されます。
sodium.options.vignette.name=ビネット
sodium.options.vignette.tooltip=有効にすると、ゲーム画面にビネット効果が適用されます。フィルレートが制限されていない限り、FPSに影響が出る可能性は非常に低いでしょう。
sodium.options.mipmap_levels.tooltip=ブロックのテクスチャに使用するミップマップの数を設定します。値が大きいほど遠くのブロックの見た目が良くなりますが、アニメーションが多いとパフォーマンスに悪影響を与える可能性があります。
sodium.options.use_chunk_multidraw.name=チャンクの同時描画
sodium.options.use_chunk_multidraw.tooltip=複数のチャンクを少ない回数で描画できます。CPU負荷を大幅に削減し、GPUの利用効率も向上する可能性があります。一部のグラフィックドライバで問題を引き起こす可能性があるため、不具合が発生する場合は無効にしてください。
sodium.options.use_vertex_objects.name=頂点配列オブジェクト
sodium.options.use_vertex_objects.tooltip=頂点の描画方法に関する情報をグラフィックドライバに移動することでパフォーマンスが向上し、同じオブジェクトの反復描画をより最適化できるようになります。互換性の無いMODを使用している場合を除き、通常はこの機能を無効にする必要はありません。
sodium.options.use_block_face_culling.name=ブロック表面カリング
sodium.options.use_block_face_culling.tooltip=有効にすると、画面に映るブロック面のみが描画対象となります。描画処理の初期段階で多数のブロック面が除外され、メモリ帯域幅とGPUの処理時間を節約できます。一部のリソースパックでは問題が発生する可能性があるため、ブロック面に穴が開く場合は無効にしてください。
sodium.options.use_compact_vertex_format.name=小さな頂点フォーマット
sodium.options.use_compact_vertex_format.tooltip=有効にすると、チャンク描画に小さな頂点フォーマットが使用されます。特に内臓グラフィックスの場合、VRAMの使用量と帯域幅を大幅に削減できますが、位置とテクスチャ座標属性の精度が低下するため、一部のリソースパックでZ-Fightingが発生する可能性があります。
sodium.options.use_fog_occlusion.name=霧による遮蔽
sodium.options.use_fog_occlusion.tooltip=有効にすると、霧に完全に隠れているチャンクは描画されなくなり、パフォーマンスが向上します。霧が強い水中などでより効果が高くなりますが、空と霧の間で見た目の異常が出る可能性があります。
sodium.options.use_entity_culling.name=エンティティカリング
sodium.options.use_entity_culling.tooltip=有効にすると、見える範囲に存在しないと判断されたエンティティが描画されなくなります。地下や壁の背後にあるエンティティの描画をしないことで、パフォーマンスが向上します。
sodium.options.use_particle_culling.name=パーティクルカリング
sodium.options.use_particle_culling.tooltip=有効にすると、見えると判断されたパーティクルのみが描画されるようになります。近くに多数のパーティクルがある場合にFPSが大幅に向上する可能性があります。
sodium.options.animate_only_visible_textures.name=見えるアニメーションのみ描画
sodium.options.animate_only_visible_textures.tooltip=有効にすると、表示可能と判断されたアニメーションのみが描画されます。特にリソースパックの負荷が高い場合、一部のハードウェアでFPSが大幅に向上する可能性があります。一部のアニメーションが動かないなどの問題が発生した場合は、この設定を無効にしてください。
sodium.options.translucency_sorting.name=半透明ブロックの整理
sodium.options.translucency_sorting.tooltip=有効にすると、半透明ブロックは奥から手前へと正しく並べ替えられ、特に複数の半透明ブロックが重なっている場合の品質が向上しますが、代わりに負荷が上昇します。
sodium.options.cpu_render_ahead_limit.name=CPUの先読み処理制限
sodium.options.cpu_render_ahead_limit.tooltip=CPUがGPUの描画完了まで待機できる最大フレーム数を設定します。極端に低い値、または高い値を設定すると、FPSが不安定になる可能性があります。
sodium.options.cpu_render_ahead_limit.value=%s フレーム
sodium.options.allow_direct_memory_access.name=ダイレクトメモリアクセス
sodium.options.allow_direct_memory_access.tooltip=有効にすると、一部の重要なコードパスでのパフォーマンス向上のために、メモリへの直接アクセスが許可されます。チャンクやエンティティの描画でのCPUの余分な負荷が大幅に削減される可能性が高まりますが、一部のバグやクラッシュの診断が難しくなる場合があります。誰かに指示された場合か、設定内容を十分に理解している場合にのみ無効にしてください。
sodium.options.ignore_driver_blacklist.name=ドライバブラックリストの無視
sodium.options.ignore_driver_blacklist.tooltip=有効にすると、ハードウェア/ドライバー構成との既知の非互換性が無視され、問題を引き起こす可能性のある設定を有効にできるようになります。何をしているのかを正確に理解していない限り、このオプションを変更しないでください。変更後に設定画面を保存して閉じ、再度開く必要があります。
sodium.options.enable_memory_tracing.name=メモリトレース
sodium.options.enable_memory_tracing.tooltip=デバッグ機能です。有効にすると、メモリリークが検出された際の診断情報を改善するために、メモリ割り当てと処理の呼び出し履歴が収集されます。
sodium.options.performance_impact_string=パフォーマンスへの影響: %s
sodium.options.use_persistent_mapping.name=永続的なマッピング
sodium.options.use_persistent_mapping.tooltip=有効にすると、チャンクとエンティティの描画を高速化するために、少量のメモリが永続的にマッピングされます。\n\nOpenGL4.4かARB_buffer_storageが必要です。
sodium.options.chunk_memory_allocator.name=チャンクメモリ割り当て
sodium.options.chunk_memory_allocator.tooltip=チャンク描画に使用するメモリ割り当て方法を設定します。\n- 非同期: 最速で、ほとんどの最新のグラフィックドライバで正常に動作します。\n- スワップ: 古いグラフィックドライバ向けの代替法です。メモリ使用量が大幅に増加する可能性があります。
sodium.options.chunk_memory_allocator.async=非同期
sodium.options.chunk_memory_allocator.swap=スワップ
sodium.options.chunk_update_threads.name=チャンク更新スレッド
sodium.options.chunk_update_threads.tooltip=チャンクロードに使用するCPUスレッド数を設定します。スレッド数を増やすと、チャンクロード速度が向上しますが、フレームタイムに悪影響を与える可能性があります。
sodium.options.always_defer_chunk_updates.name=常にチャンク更新を遅らせる
sodium.options.always_defer_chunk_updates.tooltip=有効にすると、重要なチャンクの更新完了を待たずに描画が実行されます。状況によってはFPSが大幅に向上する可能性がありますが、地形描画に著しい視覚的な遅延が発生する場合があります。
sodium.options.use_no_error_context.name=エラーの無いコンテキスト
sodium.options.use_no_error_context.tooltip=有効にすると、エラーチェックが無効になった状態でOpenGLコンテキストが作成されます。パフォーマンスが若干向上する可能性がありますが、OpenGLエラーを適切に処理できずにゲームがクラッシュする可能性も高まります。原因不明の突然のクラッシュが発生する場合は、この設定を無効にしてください。
sodium.options.async_occlusion_mode.name=非同期オクルージョン
sodium.options.async_occlusion_mode.tooltip=有効にすると、レンダリングパスで非同期オクルージョンカリングを使用します。「Only Shadow(デフォルト)」はシャドウパスのみで非同期オクルージョンを実行し、メインの描画リストでは同期を維持します。「Everything」は全てのパスを非同期で実行しますが、カメラの高速移動時に1フレームの可視遅延が発生する可能性があります。「None」は非同期オクルージョンを完全に無効化します。
sodium.options.use_occlusion_culling.name=オクルージョンカリング
sodium.options.use_occlusion_culling.tooltip=有効にすると、他のチャンクの後ろに隠れているチャンクは描画されません。無効にするとパフォーマンスが大幅に低下しますが、デバッグには役立ちます。
sodium.options.threads.value=%s スレッド
sodium.options.default=デフォルト
sodium.options.buttons.undo=戻す
sodium.options.buttons.apply=適用
sodium.options.buttons.donate=JellySquidに一杯のコーヒーを!
options.dynamic_fov=臨場感ある視野角
options.dynamic_fov.tooltip=有効にすると、ゲーム画面の視野角はプレイヤーの速度に応じて変化します。
options.framerate=%s FPS
iris.shaders.toggled=シェーダーを %s に切り替えました!
iris.shaders.disabled=シェーダーを無効化しました!
options.iris.apply=適用
options.iris.refresh=更新
options.iris.openShaderPackFolder=パックフォルダーを開く...
options.iris.shaderPackSettings=シェーダーの設定...
options.iris.shaderPackList=シェーダー一覧...
options.iris.refreshShaderPacks=シェーダーの更新
options.iris.shaderPackSelection=シェーダーパック...
options.iris.shaderPackSelection.title=シェーダーパック
options.iris.shaderPackSelection.addedPack=シェーダー「%s」を追加しました
options.iris.shaderPackSelection.addedPacks=%s個のシェーダーを追加しました。
options.iris.shaderPackSelection.failedAdd=無効なファイルです
options.iris.shaderPackSelection.failedAddSingle=「%s」は無効なシェーダーです。
options.iris.shaderPackSelection.copyError=シェーダー「%s」を追加できませんでした
options.iris.shaderPackSelection.copyErrorAlreadyExists=「%s」は既にパックフォルダーに存在します!
options.iris.shaderPackOptions.tooManyFiles=シェーダー設定ファイルは複数個インポートできません!
options.iris.shaderPackOptions.failedImport=シェーダー設定ファイル「%s」のインポートに失敗しました!
options.iris.shaderPackOptions.importedSettings=シェーダー設定を「%s」からインポートしました。
options.iris.shaders.disabled=シェーダー: OFF
options.iris.shaders.enabled=シェーダー: ON
options.iris.shaders.nonePresent=シェーダー: 未選択
options.iris.back=戻る
options.iris.reset=リセット
options.iris.reset.tooltip=全ての設定をリセットしますか?
options.iris.reset.tooltip.holdShift=Shiftキーを押しながらクリックでリセット
options.iris.importSettings.tooltip=ファイルから設定をインポート
options.iris.exportSettings.tooltip=設定をファイルへエクスポート
options.iris.mustDisableFullscreen=フルスクリーンを解除してください!
options.iris.setToDefault=この設定をリセットしますか?
options.iris.profile=プロファイル
options.iris.profile.custom=カスタム
options.iris.shadowDistance=影の最大描画距離
options.iris.shadowDistance.sodium_tooltip=影の最大描画距離
options.iris.shadowDistance.enabled=影が描画される最大の距離を設定できます。 指定された値より遠くの地形やエンティティには影が描画されません。 最大描画距離を下げることで、パフォーマンスが向上することがあります。
options.iris.shadowDistance.disabled=お使いのシェーダーパックでは既に最大描画距離が設定されているため、ここでは変更できません。
options.iris.gui.hide=GUIを非表示
options.iris.gui.show=GUIを表示
options.dynamic_lights=ダイナミックライト
options.dynamic_lights.tooltip=ダイナミックライトを有効にします。モード:OFF - 無効、FASTEST - 500ミリ秒の遅延、FAST - 250ミリ秒の遅延、FANCY - 50ミリ秒の遅延、REALTIME - 無遅延。
options.dynamic_lights_shader_force=ダイナミックライト - シェーダーに強制適用
options.dynamic_lights_shader_force.tooltip=有効 - シェーダーがONのときでもダイナミックライトを使用します、無効 - シェーダーがONのときはダイナミックライトを使用しません。
options.dynamic_lights_frustum_culling=フラスタムカリング
options.dynamic_lights_frustum_culling.tooltip=画面上の光源があるチャンクのみを更新します。GPU負荷が軽減されます。
options.dynamic_lights_adaptive_ticking=適応型ティック処理
options.dynamic_lights_adaptive_ticking.tooltip=遠方および画面外の光源の更新頻度を下げます。グローバルモード設定と併用できます。
options.dynamic_lights_slow_dist=遅い光源更新の距離
options.dynamic_lights_slow_dist.tooltip=光源の更新頻度が1/5となる距離(単位:ブロック)。推奨値:32ブロック(約2チャンク)。
options.dynamic_lights_slower_dist=より遅い光源更新の距離
options.dynamic_lights_slower_dist.tooltip=光源の更新頻度が1/10となる距離(単位:ブロック)。推奨値:64ブロック(約4チャンク)。
options.dynamic_lights_background_dist=バックグラウンド更新距離
options.dynamic_lights_background_dist.tooltip=光源の更新頻度が1/20(1秒に1回)となる距離(単位:ブロック)。見えない光源にも適用されます。
options.dynamic_lights_cull_timeout=カリングタイムアウト(ティック)
options.dynamic_lights_cull_timeout.tooltip=見えないチャンクが更新を強制されるまでに待機できる最大ティック数。値が小さいほど応答性は向上しますが、GPUの負荷が増加します。
options.dynamiclights.page=ダイナミックライト
options.dynamiclights.entities=エンティティの設定...
options.dynamiclights.entities.title=ダイナミックライト- エンティティ
options.dynamiclights.entities.info=ダイナミックライトを適用するエンティティの種類を設定します。一部の項目はMODとの互換性のために設定されています。
options.dynamiclights.entities.search=検索 (@mod, $on, $off)
options.dynamiclights.entity.tooltip=%sのエンティティのダイナミックライトを設定します。そのエンティティタイプが不要な光を発する場合は、パフォーマンス向上のために無効にしてください。
options.angelica.search.no_results=該当する設定が見つかりません
options.angelica.fontconfig=フォント設定...
options.angelica.fontconfig.title=フォント設定
options.angelica.fontconfig.currentfonts=使用フォント: §e%s§r 代替フォント: §b%s
options.angelica.fontconfig.searchprompt=§7§oフォントを検索...
options.angelica.fontconfig.testareaprompt=§7§oテストエリア
options.angelica.fontconfig.testareainfo=ここに文章を入力して、どのように表示されるかを確認できます。書式設定には「&」を使用してください。
options.angelica.fontconfig.testareaexample=&d&lへロブライン&rは&n此処には&7&m&o居なかった&r
options.angelica.fontconfig.disable_custom_font=カスタムフォントを無効化
options.angelica.fontconfig.enable_custom_font=カスタムフォントを有効化
options.angelica.fontconfig.reset_values=設定をリセット
options.angelica.fontconfig.no_fonts_warning=お使いのシステムではカスタムフォントを利用できません。詳細についてはログをご覧ください。
options.angelica.fontconfig.font_quality=カスタムフォントの品質: %s
options.angelica.fontconfig.shadow_offset=陰影のオフセット: %s
options.angelica.fontconfig.shadow_copies=陰影の数: %s
options.angelica.fontconfig.bold_copies=太字のコピーの数:%s
options.angelica.fontconfig.glyph_scale=文字の大きさ:%s
options.angelica.fontconfig.glyph_aspect=文字の縦横比:%s
options.angelica.fontconfig.whitespace_scale=空白の長さ:%s
options.angelica.fontconfig.glyph_spacing=文字間の長さ:%s
options.angelica.fontconfig.font_aa_strength=アンチエイリアシングの強度:%s
options.angelica.fontconfig.aamode.aa_none=アンチエイリアシング:オフ
options.angelica.fontconfig.aamode.aa_4x=アンチエイリアシング:4x MSAA
options.angelica.fontconfig.aamode.aa_16x=アンチエイリアシング:16x MSAA
options.angelica.fontconfig.custom_font_scale=カスタムフォントの大きさ:%s
options.angelica.fontconfig.shadow_offset_unicode=ユニコード陰影オフセット:%s
options.angelica.fontconfig.font_quality.tooltip=カスタムフォントの描画品質を向上させ、パフォーマンスとメモリ使用量を多少犠牲にする代わりに、ギザギザを軽減します。
options.angelica.fontconfig.shadow_offset.tooltip=フォントの陰影のオフセット。§l太字§rにも適用されます。
options.angelica.fontconfig.shadow_copies.tooltip=文字の陰影の数。すべてをオフセットすることで、擬似的な3D効果を生み出します。
options.angelica.fontconfig.bold_copies.tooltip=太字のコピーの数。細めのカスタムフォントで、太字の見栄えが良くなる場合があります。
options.angelica.fontconfig.glyph_scale.tooltip=各文字の大きさ。極端な値を設定すると、プレイに支障が出る可能性があります。
options.angelica.fontconfig.glyph_aspect.tooltip=各文字の縦横比。
options.angelica.fontconfig.whitespace_scale.tooltip=スペースなどの空白の拡大率。
options.angelica.fontconfig.glyph_spacing.tooltip=各文字の間隔。
options.angelica.fontconfig.font_aa_strength.tooltip=フォントのアンチエイリアシングに使用されるサンプル間隔。値が大きいとフォントがぼやけ、値が小さいとギザギザが発生する可能性があります。お使いの環境に最適な値を見つけてください。
options.angelica.fontconfig.aamode.tooltip=フォントのアンチエイリアシングモード。パフォーマンスを多少犠牲にする代わりに、拡大縮小された文字や、斜体の外観と読みやすさを大幅に向上させることができます。
options.angelica.fontconfig.custom_font_scale.tooltip=カスタムフォントの大きさ変更の値です。カスタムフォントの文字が、通常フォントの文字と比較して小さすぎるか、大きすぎる場合はこの値を調整してください。
options.angelica.fontconfig.shadow_offset_unicode.tooltip=Unicodeフォントにのみ適用される陰影オフセットの値です。Unicodeフォントの一部をMinecraft風のフォントに置き換えるリソースパックを使用している場合は、この値を1.0倍に設定してください。
options.angelica.hudCaching=HUDキャッシング
options.angelica.hudCaching.tooltip=HUDをフレームバッファにキャッシュし、フレーム間で再利用することで描画時の余計な負荷を削減します。起動時にHUDキャッシングを有効にする必要があります(実験的機能)。
options.angelica.droppedItemLimit=ドロップアイテムの描画数制限
options.angelica.droppedItemLimit.tooltip=描画されるドロップアイテムの最大数。値を小さくするとパフォーマンスが向上する可能性があります。
options.angelica.mobSpawnerRenderDistance=モブスポナーの描画距離
options.angelica.mobSpawnerRenderDistance.tooltip=モブスポナー内の回転するモブの描画距離
options.angelica.itemdisplaylistcount=アイテム描画キャッシュの制限
options.angelica.itemdisplaylistcount.tooltip=最適化されたアイテム描画でキャッシュするVBOの最大値。値を大きくするとパフォーマンスが大幅に向上しますが、VRAMの使用量が増える可能性があります。
options.angelica.threadedChunkBuilding=並列チャンク構築
options.angelica.threadedChunkBuilding.tooltip=有効にすると、チャンクメッシュ生成が複数のスレッドで並列実行され、チャンクロード速度が大幅に向上します。カスタムブロックレンダラーを使用するMODでレンダリングの問題が発生する場合は、無効にしてください。有効化には描画の再読み込みが必要です。
pack.iris.select.title=シェーダー選択
pack.iris.configure.title=シェーダー設定
pack.iris.list.label=+ ドラッグアンドドロップでシェーダーを追加
pack.iris.list.label.lwjgl2=ドラッグアンドドロップでシェーダーを追加するにはlwjgl3ifyをインストールしてください
label.iris.true=ON
label.iris.false=OFF
sodium.options.use_render_pass_optimization.name=レンダーパスの最適化
sodium.options.use_render_pass_optimization.tooltip=スプライトの透明度を分析し、スプライトがより負荷の高いレンダリングパスを必要としない場合、レンダリングパスを自動的にダウングレードします(例:半透明から切り抜き、切り抜きから不透明)。これにより、描画呼び出しと余計な描画が削減されます。
options.angelica.aggressiveChunkLoading=積極的チャンクロード
options.angelica.aggressiveChunkLoading.tooltip=隣接するチャンクの準備がまだ整っていなくても、チャンクが読み込まれるとすぐに描画します。描画距離の端での体感的なチャンクロード速度が向上します。隣接するチャンクが読み込まれると、チャンクは自動的に再構築されます。描画距離の短いサーバーで役立ちます。
options.angelica.disablef3=F3画面追加情報の無効化
options.angelica.disablef3.tooltip=Angelicaに関するF3画面の情報の表示を無効にします。
options.angelica.texteffects.rgb_colors=RGBカラー
options.angelica.texteffects.rgb_colors.tooltip=「%s」のHTMLカラーコードを有効にします。
options.angelica.texteffects.gradients=グラデーション
options.angelica.texteffects.gradients.tooltip=滑らかな色の移り変わり
options.angelica.texteffects.rainbow=虹色
options.angelica.texteffects.rainbow.tooltip=虹色のアニメーション
options.angelica.texteffects.wave_text=波打つ文字
options.angelica.texteffects.wave_text.tooltip=波打つ文字のアニメーション
options.angelica.texteffects.wave_amplitude=波の振幅
options.angelica.texteffects.wave_amplitude.tooltip=波打つ文字が跳ね返る距離(デフォルト:2.0)
options.angelica.texteffects.dinnerbone_text=文字の上下反転
options.angelica.texteffects.dinnerbone_text.tooltip=文字の上下反転の設定。
options.angelica.texteffects.drop_shadow=陰影
options.angelica.texteffects.drop_shadow.tooltip=区切りごとの陰影は「%s」、色付きの陰影は
options.angelica.texteffects.ampersand_conversion=アンパサンドの変換
options.angelica.texteffects.ampersand_conversion.tooltip=描画時に「%s」プレフィックスコードをフォーマットコードに変換します。
sodium.options.multidraw_mode.name=同時描画モード
sodium.options.multidraw_mode.tooltip=地形描画の命令の割り当て方法を設定します。§l直接的§r は glMultiDrawElementsBaseVertex を使用します (デフォルト、OpenGL 3.2 以降)。§l間接的§r はGPU間接レンダリング (GL 4.3 以降) を使用し、一部のハードウェアではCPUの余分な負荷を削減できます。§l独立的§r は個別の描画呼び出しをします。
sodium.options.multidraw_mode.direct=直接的
sodium.options.multidraw_mode.indirect=間接的
sodium.options.multidraw_mode.individual=独立的
sodium.options.upload_method.name=ストリーミングアップロード方式
sodium.options.upload_method.tooltip=データがGPUにストリーミングされる方式を変更します。一部の設定は、特定のドライバーでパフォーマンスの向上につながる場合があります。
sodium.options.enable_deferred_batching.name=遅延パーティクル同時処理
sodium.options.enable_deferred_batching.tooltip=パーティクルの描画呼び出しを、レイヤーごとに統合された描画にまとめて実行します。パーティクルが正しくレンダリングされない場合は、この設定を無効にしてください。
@@ -0,0 +1,80 @@
options.button.detail=詳細...
options.button.other=その他...
options.button.particle=パーティクル...
options.button.sky=環境...
options.chunks=%s チャンク
options.framerate=%s FPS
options.graphics.shelledfancy=描画優先(ハイブリッド)
options.graphics.shelledfast=処理優先(ハイブリッド)
options.graphics.smart=スマート
options.graphics.ultra=ウルトラ
options.off=オフ
options.on=オン
options.cloud_height=雲の高さ
options.cloud_height.tooltip=雲の高さを設定します。
options.cloud_scale=雲の大きさ
options.cloud_scale.tooltip=雲の大きさを設定します。
options.gui_background=背景テーマ
options.gui_background.tooltip=設定画面背景のテーマを設定します。
options.downfall_distance=天気のエフェクト
options.downfall_distance.tooltip=天気のエフェクトを設定します。
options.dynamic_fov=臨場感ある視野角
options.dynamic_fov.tooltip=ゲームモードによっては、プレイヤーの移動速度や特定のMODの操作(ツールのズームや武器のスコープなど)に応じて、ゲーム画面の視野角が変化します。
options.horizon=地平線
options.horizon.tooltip=描画チャンクの端を映すかを設定します。
options.terrain_fog=霧
options.terrain_fog.tooltip=霧の有効、無効を設定します。
options.fog_near_distance=霧の距離
options.fog_near_distance.tooltip=霧が発生し始める距離を設定します。(0%%(透明)から描画距離に応じて線形的に増加します)。
options.bobview_mode=画面の揺れ
options.bobview_mode.tooltip=画面の揺れ動作を、手または画面のみを揺らすように変更します。
options.bobview.hand=手のみ
options.bobview.camera=画面のみ
options.fov.all=オン
options.fov.mods=Modによる動作のみ
options.fov.none=オフ
options.mode_cloud_translucency=半透明の雲
options.mode_cloud_translucency.tooltip=半透明の雲の有効、無効を設定します
options.mode_clouds=雲
options.mode_clouds.tooltip=雲の設定
options.mode_dropped_items=ドロップアイテム
options.mode_dropped_items.tooltip=ドロップアイテムの描画品質を設定します。
options.mode_glint_inv=GUIエンチャントエフェクト
options.mode_glint_inv.tooltip=GUI上でのエンチャントエフェクトの有効、無効を設定します。
options.mode_glint_world=ワールドエンチャントエフェクト
options.mode_glint_world.tooltip=ワールド上でのエンチャントエフェクトの有効、無効を設定します。
options.mode_gui_background=背景
options.mode_gui_background.tooltip=設定画面背景の有効、無効を設定します。
options.mode_leaves=葉の品質
options.mode_leaves.tooltip=葉の品質
options.mode_light_flicker=光のちらつき
options.mode_light_flicker.tooltip=光のちらつきの有効、無効を設定します。
options.mode_shadows=エンティティの影
options.mode_shadows.tooltip=エンティティの影の有効、無効を設定します。
options.mode_sky=空
options.mode_sky.tooltip=空の描画の有効、無効を設定します。
options.mode_sun_moon=太陽と月
options.mode_sun_moon.tooltip=太陽と月の描画の有効、無効を設定します。
options.mode_stars=星
options.mode_stars.tooltip=星の描画の有効、無効を設定します。
options.mode_water=水
options.mode_water.tooltip=水の設定をします。
options.mode_vignette=ビネット
options.mode_vignette.tooltip=ビネットの有効、無効を設定します。
options.particles_enc_table=エンチャント台のパーティクル
options.particles_enc_table.tooltip=エンチャント台のパーティクルの設定をします。
options.particles_void=奈落のパーティクル
options.particles_void.tooltip=奈落のパーティクルの有効、無効を設定します。
options.render_distance_clouds=雲までの距離
options.render_distance_clouds.tooltip=最も近い雲までの距離を設定します。
options.title.detail=詳細設定
options.title.other=その他の設定
options.title.particle=パーティクルの設定
options.title.sky=環境の設定
options.title.video=ビデオ設定
options.total_stars=星の密度
options.total_stars.tooltip=星の密度を設定します。
options.void_fog=奈落の霧
options.void_fog.tooltip=奈落の霧の有効、無効を設定します。
options.hurt_shake=被ダメージ時の画面揺れ
options.background.random=ランダム
@@ -25,7 +25,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* 2. During each glint renderItemIn2D call:
* a. Pre-pass: no color/depth writes, GL_LEQUAL depth test, increment the stencil 1→2
* where depth passes. This determines pixel ownership without corrupting the depth buffer (damn potions...)
* b. Color pass: draw only where stencil == 2, GL_ALWAYS depth test, decrement the stencil
* b. Color pass: draw only where stencil == 2, GL_LEQUAL depth test, decrement the stencil
* 2→1 to reset for the next glint pass.
*
* For the shadow pass, all stencil operations are skipped. Shadow FBOs have no stencil attachment.
@@ -127,7 +127,7 @@ public class MixinItemRenderer_EdgeDepth {
GLStateManager.glColorMask(true, true, true, true);
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 2, 0x03);
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_DECR);
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
original.call(tess, minU, minV, maxU, maxV, w, h, thickness);
// Restore state for the next glint renderItemIn2D call
@@ -254,6 +254,6 @@ public class MixinRenderBlocks_CrackFix {
}
@Unique
private boolean angelica$crackFixOff() {
return !AngelicaConfig.fixBlockCrack || angelica$disableCrackFix;
return !AngelicaConfig.blockCrackFix || angelica$disableCrackFix;
}
}
@@ -193,7 +193,7 @@ public abstract class MixinRenderBlocks {
CompactCtmQuadProcessor processor = ctx.compact().getProcessor();
RenderBlockState renderBlockState = ctx.renderBlockState();
CTMUtils.clearCurrentCompact();
if (this.blockAccess == null) {
if (this.blockAccess == null || renderBlockState.getBlockAccess() == null) {
return;
}
@@ -6,15 +6,12 @@ import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.pipeline.HandRenderer;
import net.coderbot.iris.uniforms.ItemIdManager;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.item.ItemStack;
import org.objectweb.asm.Opcodes;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
@@ -44,31 +41,6 @@ public class MixinItemRenderer {
ItemIdManager.setItemId(this.itemToRender);
}
/**
* Force the equip lower/raise animation whenever the held stack isn't an exact match for
* the one being rendered.
*/
@Redirect(
method = "updateEquippedItem",
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemDamage()I", ordinal = 0)
)
private int iris$animateStackSwap(ItemStack incoming) {
if (this.itemToRender != null
&& !ItemStack.areItemStacksEqual(incoming, this.itemToRender)) {
return Integer.MIN_VALUE;
}
return incoming.getItemDamage();
}
@Redirect(
method = "updateEquippedItem",
at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/ItemRenderer;itemToRender:Lnet/minecraft/item/ItemStack;", opcode = Opcodes.PUTFIELD, ordinal = 0)
)
private void iris$commitSlotOnInstantSwap(ItemRenderer self, ItemStack value) {
this.itemToRender = value;
int equippedItemSlot = Minecraft.getMinecraft().thePlayer.inventory.currentItem;
}
/**
* Reset the item ID after rendering first-person items.
*/
@@ -78,7 +78,9 @@ public class MixinLocale {
languageCodes.clear();
// Reverse order due to how minecraft has English and then the primary language in the language definitions list
new LinkedList<>(definitions).descendingIterator().forEachRemaining(languageCodes::add);
new LinkedList<>(definitions).descendingIterator()
// FQN here because due to clashing
.forEachRemaining(code -> languageCodes.add(code.toLowerCase(java.util.Locale.ROOT)));
}
@Inject(method = "checkUnicode", at = @At("HEAD"), cancellable = true)
@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin allows devs to target the ender crystal beams as a separate entity.
* Mixin allows devs to target the ender crystal beams and dragon death rays as separate entities.
* Also sets the special condition "lightning" on the dragon's death beams.
*/
@Mixin(RenderDragon.class)
@@ -28,9 +28,15 @@ public abstract class MixinRenderDragon {
@Unique
private static final NamespacedId END_CRYSTAL_BEAM = new NamespacedId("minecraft", "end_crystal_beam");
@Unique
private static final NamespacedId DRAGON_DEATH_RAY = new NamespacedId("minecraft", "dragon_death_rays");
@Unique
private int angelica$previousEntityId = -1;
@Unique
private int angelica$previousDeathRayEntityId = -1;
@Unique
private boolean angelica$deathBeamsActive = false;
@@ -83,6 +89,13 @@ public abstract class MixinRenderDragon {
GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.LIGHTNING);
angelica$deathBeamsActive = true;
Object2IntFunction<NamespacedId> entityIdMap = BlockRenderingSettings.INSTANCE.getEntityIds();
if (entityIdMap != null) {
angelica$previousDeathRayEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedEntity();
int deathRayId = entityIdMap.applyAsInt(DRAGON_DEATH_RAY);
CapturedRenderingState.INSTANCE.setCurrentEntity(deathRayId);
}
angelica$depthPassReplay++;
GLStateManager.glColorMask(false, false, false, false);
angelica$invokeRenderEquippedItems(dragon, partialTicks);
@@ -118,6 +131,10 @@ public abstract class MixinRenderDragon {
private void angelica$endDeathBeamsLighting(EntityDragon dragon, float partialTicks, CallbackInfo ci) {
if (angelica$depthPassReplay > 0) return;
if (angelica$deathBeamsActive) {
if (angelica$previousDeathRayEntityId != -1) {
CapturedRenderingState.INSTANCE.setCurrentEntity(angelica$previousDeathRayEntityId);
angelica$previousDeathRayEntityId = -1;
}
GbufferPrograms.teardownSpecialRenderCondition();
angelica$deathBeamsActive = false;
}